From 466adc5bd91ef85d499654cd2bbf0154e5c8fdce Mon Sep 17 00:00:00 2001 From: Ed_ Date: Wed, 17 Apr 2024 12:16:22 -0400 Subject: [PATCH] Updates to GasaGen, replace engine's void SBlueprintActionMenu::Construct with new code --- GASATHON.10x | 46 +++ Project/Source/GasaGen/GasaGen.cpp | 8 +- .../GasaGen/GasaGen_ChangeBPActionMenu.cpp | 273 ++++++++++++++++ .../GasaGen/GasaGen_UGasaAttributeSet.cpp | 6 +- .../GasaGen/GasaGen_ue_parse_testing.cpp | 2 +- Project/Source/GasaGen/gen.cpp | 292 +++++++++++------- Project/Source/GasaGen/gen.hpp | 2 +- scripts/.clang-format | 12 +- scripts/gen_pass_gasa.ps1 | 6 +- 9 files changed, 514 insertions(+), 133 deletions(-) create mode 100644 GASATHON.10x create mode 100644 Project/Source/GasaGen/GasaGen_ChangeBPActionMenu.cpp diff --git a/GASATHON.10x b/GASATHON.10x new file mode 100644 index 0000000..2bfdc56 --- /dev/null +++ b/GASATHON.10x @@ -0,0 +1,46 @@ + + + + *.* + *.obj,*.lib,*.pch,*.dll,*.pdb,.vs,Debug,Release,x64,obj,*.user,Intermediate + true + true + true + false + false + false + + + + + + + + + + + + false + + Debug + Release + + + x64 + + + C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include + C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\ATLMFC\include + C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\VS\include + C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt + C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\um + C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\shared + C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\winrt + C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\cppwinrt + C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\include\um + + + + + + diff --git a/Project/Source/GasaGen/GasaGen.cpp b/Project/Source/GasaGen/GasaGen.cpp index f63ce16..5853b66 100644 --- a/Project/Source/GasaGen/GasaGen.cpp +++ b/Project/Source/GasaGen/GasaGen.cpp @@ -11,6 +11,7 @@ using namespace gen; #include "GasaGenCommon.cpp" #include "GasaGen_ue_parse_testing.cpp" #include "GasaGen_UGasaAttributeSet.cpp" +#include "GasaGen_ChangeBPActionMenu.cpp" int gen_main() { @@ -59,6 +60,8 @@ int gen_main() StrC str_DECLARE_EVENT_ThreeParams = txt("DECLARE_EVENT_ThreeParams("); StrC str_USTRUCT = txt("USTRUCT("); StrC str_GENERATED_USTRUCT_BODY = txt("GENERATED_USTRUCT_BODY("); + StrC str_SLATE_BEGIN_ARGS = txt("SLATE_BEGIN_ARGS("); + StrC str_SLATE_END_ARGS = txt("SLATE_END_ARGS("); PreprocessorDefines.append( get_cached_string(str_GENERATED_BODY)); PreprocessorDefines.append( get_cached_string(str_GENERATED_UCLASS_BODY)); @@ -97,11 +100,14 @@ int gen_main() PreprocessorDefines.append( get_cached_string(str_DECLARE_EVENT_ThreeParams)); PreprocessorDefines.append( get_cached_string(str_USTRUCT)); PreprocessorDefines.append( get_cached_string(str_GENERATED_USTRUCT_BODY)); + PreprocessorDefines.append( get_cached_string(str_SLATE_BEGIN_ARGS)); + PreprocessorDefines.append( get_cached_string(str_SLATE_END_ARGS)); - ue_parse_testing(); + // ue_parse_testing(); StrC str_gasa_api = txt("GASA_API"); gen_UGasaAttributeSet(); + swap_SBlueprintActionMenu_Construct(); return 0; } diff --git a/Project/Source/GasaGen/GasaGen_ChangeBPActionMenu.cpp b/Project/Source/GasaGen/GasaGen_ChangeBPActionMenu.cpp new file mode 100644 index 0000000..43ebf2c --- /dev/null +++ b/Project/Source/GasaGen/GasaGen_ChangeBPActionMenu.cpp @@ -0,0 +1,273 @@ +constexpr StrC SBlueprintActionMenu_Construct_Replacement = txt(R"( +void SBlueprintActionMenu::Construct( const FArguments& InArgs, TSharedPtr InEditor ) +{ + bActionExecuted = false; + + this->GraphObj = InArgs._GraphObj; + this->DraggedFromPins = InArgs._DraggedFromPins; + this->NewNodePosition = InArgs._NewNodePosition; + this->OnClosedCallback = InArgs._OnClosedCallback; + this->bAutoExpandActionMenu = InArgs._AutoExpandActionMenu; + this->EditorPtr = InEditor; + this->OnCloseReasonCallback = InArgs._OnCloseReason; + + // Generate the context display; showing the user what they're picking something for + //@TODO: Should probably be somewhere more schema-sensitive than the graph panel! + FSlateColor TypeColor; + FString TypeOfDisplay; + const FSlateBrush* ContextIcon = nullptr; + + if (DraggedFromPins.Num() == 1) + { + UEdGraphPin* OnePin = DraggedFromPins[0]; + + const UEdGraphSchema* Schema = OnePin->GetSchema(); + const UEdGraphSchema_K2* K2Schema = GetDefault(); + + if (!Schema->IsA(UEdGraphSchema_K2::StaticClass()) || !K2Schema->IsExecPin(*OnePin)) + { + // Get the type color and icon + TypeColor = Schema->GetPinTypeColor(OnePin->PinType); + ContextIcon = FAppStyle::GetBrush( OnePin->PinType.IsArray() ? TEXT("Graph.ArrayPin.Connected") : TEXT("Graph.Pin.Connected") ); + } + } + + FBlueprintActionContext MenuContext; + ConstructActionContext(MenuContext); + + TSharedPtr AddImportTargetContent = SNullWidget::NullWidget; + if (GetDefault()->bEnableNamespaceImportingFeatures) + { + SAssignNew(AddImportTargetContent, SBox) + .ToolTipText(LOCTEXT("ImportActionLabelTooltip", "Choose a namespace to import and load additional actions.")) + [ + SNew(SHorizontalBox) + +SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(LOCTEXT("ImportActionButtonLabel", "Import Actions From:")) + ] + +SHorizontalBox::Slot() + .AutoWidth() + .Padding(4.f, 0.f) + [ + SNew(SBlueprintNamespaceEntry) + .AllowTextEntry(false) + .OnNamespaceSelected(this, &SBlueprintActionMenu::OnNamespaceSelectedForImport) + .OnGetNamespacesToExclude(this, &SBlueprintActionMenu::OnGetNamespacesToExcludeFromImportMenu) + .ExcludedNamespaceTooltipText(LOCTEXT("CannotSelectNamespaceForImport", "This namespace has already been imported by this Blueprint.")) + ] + ]; + } + + TSharedPtr TargetContextSubMenuButton; + // @TODO: would be nice if we could use a checkbox style for this, and have a different state for open/closed + SAssignNew(TargetContextSubMenuButton, SComboButton) + .MenuPlacement(MenuPlacement_MenuRight) + .HasDownArrow(false) + .ButtonStyle(FAppStyle::Get(), "BlueprintEditor.ContextMenu.TargetsButton") + .ContentPadding(FMargin(5)) + .MenuContent() + [ + SAssignNew(ContextTargetSubMenu, SBlueprintContextTargetMenu, MenuContext) + .OnTargetMaskChanged(this, &SBlueprintActionMenu::OnContextTargetsChanged) + .CustomTargetContent() + [ + AddImportTargetContent.ToSharedRef() + ] + ]; + + // Build the widget layout + TSharedRef ContentBox = SNew(SBox); + { + TSharedRef VBox = SNew(SVerticalBox); + { + SVerticalBox::FSlot::FSlotArguments + SearchIndicator = SVerticalBox::Slot(); + { + TSharedRef HBox = SNew(SHorizontalBox); + { + SHorizontalBox::FSlot::FSlotArguments + TypePill = SHorizontalBox::Slot(); + { + TSharedRef PillImage = SNew(SImage); + { + SImage::FArguments Args; + Args.Visibility(this, & SBlueprintActionMenu::GetTypeImageVisibility); + PillImage->SetColorAndOpacity(TypeColor); + PillImage->SetImage(ContextIcon); + PillImage->Construct(Args); + } + SHorizontalBox::FSlot* Slot; TypePill.Expose(Slot); + TypePill.AutoWidth(); + TypePill.VAlign(VAlign_Center); + TypePill.Padding( FMargin(0.f, 0.f, (ContextIcon != nullptr) ? 5.f : 0.f, 0.f)); + TypePill.AttachWidget(PillImage); + } + + SHorizontalBox::FSlot::FSlotArguments + SearchContextDescription = SHorizontalBox::Slot(); + { + TSharedRef TextBlock = SNew(STextBlock); + { + STextBlock::FArguments Args; + Args.Text(this, & SBlueprintActionMenu::GetSearchContextDesc); + Args.Font(FAppStyle::GetFontStyle(FName("BlueprintEditor.ActionMenu.ContextDescriptionFont"))); + Args.ToolTip(IDocumentation::Get()->CreateToolTip( + LOCTEXT("BlueprintActionMenuContextTextTooltip", "Describes the current context of the action list"), + NULL, + TEXT("Shared/Editors/BlueprintEditor"), + TEXT("BlueprintActionMenuContextText"))); + Args.AutoWrapText(true); + TextBlock->Construct(Args); + } + SearchContextDescription.FillWidth(1.f); + SearchContextDescription.VAlign(VAlign_Center); + SearchContextDescription.AttachWidget(TextBlock); + } + + SHorizontalBox::FSlot::FSlotArguments + ContextToggle = SHorizontalBox::Slot(); + { + TSharedRef CheckBox = SNew(SCheckBox); + { + SCheckBox::FArguments Args; + Args.OnCheckStateChanged(this, & SBlueprintActionMenu::OnContextToggleChanged); + Args.IsChecked(this, & SBlueprintActionMenu::ContextToggleIsChecked); + Args.ToolTip(IDocumentation::Get()->CreateToolTip( + LOCTEXT("BlueprintActionMenuContextToggleTooltip" + , "Should the list be filtered to only actions that make sense in the current context?"), + NULL, + TEXT("Shared/Editors/BlueprintEditor"), + TEXT("BlueprintActionMenuContextToggle"))); + Args.Content().SlotContent.Widget = SNew(STextBlock) + .Text(LOCTEXT("BlueprintActionMenuContextToggle", "Context Sensitive")); + CheckBox->Construct(Args); + } + ContextToggle.HAlign(HAlign_Right); + ContextToggle.VAlign(VAlign_Center); + ContextToggle.AutoWidth(); + ContextToggle.AttachWidget( CheckBox ); + } + + SHorizontalBox::FSlot::FSlotArguments + ContextButton = SHorizontalBox::Slot(); + { + ContextButton.HAlign(HAlign_Right); + ContextButton.VAlign(VAlign_Center); + ContextButton.AutoWidth(); + ContextButton.Padding( FMargin( 3.f, 0.f, 0.f, 0.f )); + ContextButton.AttachWidget( TargetContextSubMenuButton.ToSharedRef() ); + } + + SHorizontalBox::FArguments Args = SHorizontalBox::FArguments(); + Args.operator+( TypePill ); + Args.operator+( SearchContextDescription ); + Args.operator+( ContextToggle ); + Args.operator+( ContextButton ); + HBox->Construct(Args); + } + + SearchIndicator.AutoHeight(); + SearchIndicator.Padding( FMargin(2, 2, 2, 5)); + SearchIndicator.AttachWidget(HBox); + } + + SVerticalBox::FSlot::FSlotArguments + ActionList = SVerticalBox::Slot(); + { + SAssignNew( GraphActionMenu, SGraphActionMenu); + + SGraphActionMenu::FArguments Args; + Args.OnActionSelected(this, &SBlueprintActionMenu::OnActionSelected); + Args.OnCreateWidgetForAction(SGraphActionMenu::FOnCreateWidgetForAction::CreateSP(this, &SBlueprintActionMenu::OnCreateWidgetForAction)); + Args.OnGetActionList(this, &SBlueprintActionMenu::OnGetActionList); + Args.OnCreateCustomRowExpander_Static(&CreateCustomBlueprintActionExpander); + Args.DraggedFromPins(DraggedFromPins); + Args.GraphObj(GraphObj); + GraphActionMenu->Construct(Args); + + ActionList.AttachWidget( GraphActionMenu.ToSharedRef() ); + } + + SVerticalBox::FSlot::FSlotArguments + ProgressBar = SVerticalBox::Slot(); + { + TSharedRef Box = SNew(SBox); + { + TSharedRef Bar = SNew(SProgressBar); + { + Bar->SetBorderPadding(FVector2D( 0, 0 )); + Bar->SetPercent( TAttribute>::CreateLambda([this]() + { + return ContextMenuBuilder.IsValid() ? ContextMenuBuilder->GetPendingActionsProgress() : 0.0f; + })); + } + Box->SetContent( Bar ); + Box->SetHeightOverride(2); + Box->SetVisibility(TAttribute().CreateLambda([this]() + { + return ContextMenuBuilder.IsValid() && ContextMenuBuilder->GetNumPendingActions() > 0 ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed; + })); + } + ProgressBar.AutoHeight(); + ProgressBar.AttachWidget(Box); + } + + SVerticalBox::FArguments Args; + Args.operator+( SearchIndicator ); + Args.operator+( ActionList ); + Args.operator+( ProgressBar ); + VBox->Construct( Args ); + } + + ContentBox->SetMaxDesiredWidth(500.f); + ContentBox->SetMaxDesiredHeight(650.f); + ContentBox->SetContent( VBox ); + + SBorder::FArguments Args; + Args.BorderImage(FAppStyle::GetBrush("Menu.Background")); + Args.Padding(5.0f); + Args.operator[](ContentBox); + SBorder::Construct(Args); + } +})"); + +void swap_SBlueprintActionMenu_Construct() +{ +#define path_SBlueprintActionMenuCpp \ + R"(C:\projects\Unreal\Surgo\UE\Engine\Source\Editor\Kismet\Private\SBlueprintActionMenu.cpp)" + + FileContents content = file_read_contents( GlobalAllocator, true, path_SBlueprintActionMenuCpp ); + CodeBody parsed_SBlueprintActionMenu = parse_global_body( StrC { content.size, (char const*)content.data }); + + CodeFn signature_to_change = parse_function( code( + void SBlueprintActionMenu::Construct( const FArguments& InArgs, TSharedPtr InEditor ) {} + )); + + CodeBody changed_SBlueprintActionMenu = def_body(ECode::Global_Body); + for ( Code code : parsed_SBlueprintActionMenu ) + { + switch ( code->Type ) + { + using namespace ECode; + case Function: + CodeFn function_def = code.cast(); + log_fmt("%S\n", function_def->Name); + + if ( String::are_equal(function_def->Name, signature_to_change->Name) + && function_def->Params.is_equal(signature_to_change->Params)) + { + code = parse_function( SBlueprintActionMenu_Construct_Replacement ); + } + break; + } + changed_SBlueprintActionMenu.append(code); + } + + Builder SBlueprintActionMenu_Changed = Builder::open(path_SBlueprintActionMenuCpp); + SBlueprintActionMenu_Changed.print(changed_SBlueprintActionMenu); + SBlueprintActionMenu_Changed.write(); +} \ No newline at end of file diff --git a/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp b/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp index 5126e71..4111e69 100644 --- a/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp +++ b/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp @@ -68,8 +68,6 @@ void gen_UGasaAttributeSet() )); body.append( GetLifetimeOfReplicatedProps ); body.append( def_pragma( txt("endregion UObject"))); - - String test = GetLifetimeOfReplicatedProps.to_string(); } GasaAttributeSet = def_class( class_name, body , type_UAttributeSet, AccessSpec::Public @@ -137,11 +135,11 @@ void gen_UGasaAttributeSet() ))); } - GetLifetimeOfReplicatedProps = parse_function( token_fmt( "body", (StrC)(field_lifetimes.to_string()), stringize( + GetLifetimeOfReplicatedProps = parse_function( token_fmt( "field_lifetimes", (StrC)(field_lifetimes.to_string()), stringize( void UGasaAttributeSet::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); - + } ))); } diff --git a/Project/Source/GasaGen/GasaGen_ue_parse_testing.cpp b/Project/Source/GasaGen/GasaGen_ue_parse_testing.cpp index f024e4e..2b39e62 100644 --- a/Project/Source/GasaGen/GasaGen_ue_parse_testing.cpp +++ b/Project/Source/GasaGen/GasaGen_ue_parse_testing.cpp @@ -95,7 +95,7 @@ void ue_parse_testing() { switch ( class_code->Type ) { - case CodeT::Variable: + // case CodeT::Variable: case CodeT::Function: case CodeT::Function_Fwd: if ( class_code->Name ) diff --git a/Project/Source/GasaGen/gen.cpp b/Project/Source/GasaGen/gen.cpp index 6c3f5db..cca08df 100644 --- a/Project/Source/GasaGen/gen.cpp +++ b/Project/Source/GasaGen/gen.cpp @@ -1718,7 +1718,11 @@ String CodeDestructor::to_string() void CodeDestructor::to_string_def( String& result ) { - if ( ast->Specs ) + if ( ast->Name ) + { + result.append_fmt( "%S()", ast->Name ); + } + else if ( ast->Specs ) { if ( ast->Specs.has( ESpecifier::Virtual ) ) result.append_fmt( "virtual ~%S()", ast->Parent->Name ); @@ -2434,7 +2438,7 @@ void CodeStruct::to_string_def( String& result ) { char const* access_level = to_str( ast->ParentAccess ); - result.append_fmt( "%S : %s %S", ast->Name, access_level, ast->ParentType ); + result.append_fmt( "%S : %s %S", ast->Name, access_level, ast->ParentType.to_string() ); CodeType interface = ast->ParentType->Next->cast< CodeType >(); if ( interface ) @@ -7302,6 +7306,7 @@ namespace parser internal CodeFn parse_function_after_name( ModuleFlag mflags, CodeAttributes attributes, CodeSpecifiers specifiers, CodeType ret_type, Token name ); internal Code parse_function_body(); internal Code parse_global_nspace(); + internal Code parse_global_nspace_constructor_destructor( CodeSpecifiers specifiers ); internal Token parse_identifier( bool* possible_member_function = nullptr ); internal CodeInclude parse_include(); internal CodeOperator parse_operator_after_ret_type( ModuleFlag mflags, CodeAttributes attributes, CodeSpecifiers specifiers, CodeType ret_type ); @@ -7340,115 +7345,6 @@ namespace parser constexpr bool strip_formatting_dont_preserve_newlines = false; - internal inline - bool is_constructor_definition() - { - /* - To check if a definition is for a constructor we can go straight to the opening parenthesis for its parameters - From There we work backwards to see if we come across two identifiers with the same name between an member access - :: operator, there can be template parameters on the left of the :: so we ignore those. - Whats important is that its back to back. - - This has multiple possible faults. What we parse using this method may not filter out if something has a "return type" - This is bad since technically you could have a namespace nested into another namespace with the same name. - If this awful pattern is done the only way to distiguish with this coarse parse is to know there is no return type defined. - - TODO(Ed): We could fix this by attempting to parse a type, but we would have to have a way to have it soft fail and rollback. - */ - TokArray tokens = Context.Tokens; - - s32 idx = tokens.Idx; - Token nav = tokens[ idx ]; - for ( ; idx < tokens.Arr.num(); idx++, nav = tokens[ idx ] ) - { - if ( nav.Text[0] == '<' ) - { - // Skip templated expressions as they mey have expressions with the () operators - s32 capture_level = 0; - s32 template_level = 0; - for ( ; idx < tokens.Arr.num(); idx++, nav = tokens[idx] ) - { - if (nav.Text[ 0 ] == '<') - ++ template_level; - - if (nav.Text[ 0 ] == '>') - -- template_level; - if (nav.Type == TokType::Operator && nav.Text[1] == '>') - -- template_level; - - if ( nav.Type == ETokType::Capture_Start) - { - if (template_level != 0 ) - ++ capture_level; - else - break; - } - - if ( template_level != 0 && nav.Type == ETokType::Capture_End) - -- capture_level; - } - } - - if ( nav.Type == TokType::Capture_Start ) - break; - } - - -- idx; - Token tok_right = tokens[idx]; - Token tok_left = NullToken; - - if (tok_right.Type != TokType::Identifier) - { - // We're not dealing with a constructor if there is no identifier right before the opening of a parameter's scope. - return false; - } - - -- idx; - tok_left = tokens[idx]; - // ... - - if ( tok_left.Type != TokType::Access_StaticSymbol ) - return false; - - -- idx; - tok_left = tokens[idx]; - // ... :: - - // We search toward the left until we find the next valid identifier - s32 capture_level = 0; - s32 template_level = 0; - while ( idx != tokens.Idx ) - { - if (tok_left.Text[ 0 ] == '<') - ++ template_level; - - if (tok_left.Text[ 0 ] == '>') - -- template_level; - if (tok_left.Type == TokType::Operator && tok_left.Text[1] == '>') - -- template_level; - - if ( template_level != 0 && tok_left.Type == ETokType::Capture_Start) - ++ capture_level; - - if ( template_level != 0 && tok_left.Type == ETokType::Capture_End) - -- capture_level; - - if ( capture_level == 0 && template_level == 0 && tok_left.Type == TokType::Identifier ) - break; - - -- idx; - tok_left = tokens[idx]; - } - - bool is_same = str_compare( tok_right.Text, tok_left.Text, tok_right.Length ) == 0; - if (tok_left.Type == TokType::Identifier && is_same) - { - // We have found the pattern we desired - // :: ( - return true; - } - } - /* This function was an attempt at stripping formatting from any c++ code. It has edge case failures that prevent it from being used in function bodies. @@ -9012,11 +8908,11 @@ namespace parser case TokType::Type_double : case TokType::Type_int : { + Code constructor_destructor = parse_global_nspace_constructor_destructor( specifiers ); // Possible constructor implemented at global file scope. - if (is_constructor_definition()) + if ( constructor_destructor ) { - member = parse_constructor( specifiers ); - // :: () { ... } + member = constructor_destructor; break; } @@ -9074,6 +8970,134 @@ namespace parser return result; } + internal inline + Code parse_global_nspace_constructor_destructor( CodeSpecifiers specifiers ) + { + Code result = { nullptr }; + + /* + To check if a definition is for a constructor we can go straight to the opening parenthesis for its parameters + From There we work backwards to see if we come across two identifiers with the same name between an member access + :: operator, there can be template parameters on the left of the :: so we ignore those. + Whats important is that its back to back. + + This has multiple possible faults. What we parse using this method may not filter out if something has a "return type" + This is bad since technically you could have a namespace nested into another namespace with the same name. + If this awful pattern is done the only way to distiguish with this coarse parse is to know there is no return type defined. + + TODO(Ed): We could fix this by attempting to parse a type, but we would have to have a way to have it soft fail and rollback. + */ + TokArray tokens = Context.Tokens; + + s32 idx = tokens.Idx; + Token nav = tokens[ idx ]; + for ( ; idx < tokens.Arr.num(); idx++, nav = tokens[ idx ] ) + { + if ( nav.Text[0] == '<' ) + { + // Skip templated expressions as they mey have expressions with the () operators + s32 capture_level = 0; + s32 template_level = 0; + for ( ; idx < tokens.Arr.num(); idx++, nav = tokens[idx] ) + { + if (nav.Text[ 0 ] == '<') + ++ template_level; + + if (nav.Text[ 0 ] == '>') + -- template_level; + if (nav.Type == TokType::Operator && nav.Text[1] == '>') + -- template_level; + + if ( nav.Type == ETokType::Capture_Start) + { + if (template_level != 0 ) + ++ capture_level; + else + break; + } + + if ( template_level != 0 && nav.Type == ETokType::Capture_End) + -- capture_level; + } + } + + if ( nav.Type == TokType::Capture_Start ) + break; + } + + -- idx; + Token tok_right = tokens[idx]; + Token tok_left = NullToken; + + if (tok_right.Type != TokType::Identifier) + { + // We're not dealing with a constructor if there is no identifier right before the opening of a parameter's scope. + return result; + } + + -- idx; + tok_left = tokens[idx]; + // ... + + bool possible_destructor = false; + if ( tok_left.Type == TokType::Operator && tok_left.Text[0] == '~') + { + possible_destructor = true; + -- idx; + tok_left = tokens[idx]; + } + + if ( tok_left.Type != TokType::Access_StaticSymbol ) + return result; + + -- idx; + tok_left = tokens[idx]; + // ... :: + + // We search toward the left until we find the next valid identifier + s32 capture_level = 0; + s32 template_level = 0; + while ( idx != tokens.Idx ) + { + if (tok_left.Text[ 0 ] == '<') + ++ template_level; + + if (tok_left.Text[ 0 ] == '>') + -- template_level; + if (tok_left.Type == TokType::Operator && tok_left.Text[1] == '>') + -- template_level; + + if ( template_level != 0 && tok_left.Type == ETokType::Capture_Start) + ++ capture_level; + + if ( template_level != 0 && tok_left.Type == ETokType::Capture_End) + -- capture_level; + + if ( capture_level == 0 && template_level == 0 && tok_left.Type == TokType::Identifier ) + break; + + -- idx; + tok_left = tokens[idx]; + } + + bool is_same = str_compare( tok_right.Text, tok_left.Text, tok_right.Length ) == 0; + if (tok_left.Type == TokType::Identifier && is_same) + { + // We have found the pattern we desired + if (possible_destructor) + { + // :: ~ ( + result = parse_destructor( specifiers ); + } + else { + // :: ( + result = parse_constructor( specifiers ); + } + } + + return result; + } + // TODO(Ed): I want to eventually change the identifier to its own AST type. // This would allow distinction of the qualifier for a symbol :: // This would also allow @@ -9114,6 +9138,21 @@ namespace parser } } + if ( currtok.Type == TokType::Operator && currtok.Text[0] == '~' ) + { + bool is_destructor = str_compare( Context.Scope->Prev->ProcName, "parse_destructor" ) == 0; + if (is_destructor) + { + name.Length = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )name.Text; + Context.pop(); + return name; + } + + log_failure( "Error, had a ~ operator after %S but not a destructor\n%s", ETokType::to_str( prevtok.Type ), Context.to_string() ); + Context.pop(); + return { nullptr, 0, TokType::Invalid }; + } + if ( currtok.Type != TokType::Identifier ) { log_failure( "Error, expected static symbol identifier, not %s\n%s", ETokType::to_str( currtok.Type ), Context.to_string() ); @@ -10332,7 +10371,7 @@ namespace parser eat( currtok.Type ); } - initializer_list_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )initializer_list_tok.Text; + initializer_list_tok.Length = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )initializer_list_tok.Text; // ( ) : initializer_list = untyped_str( initializer_list_tok ); @@ -10393,6 +10432,9 @@ namespace parser { push_scope(); + bool has_context = Context.Scope && Context.Scope->Prev; + bool is_in_global_nspace = has_context && str_compare( Context.Scope->Prev->ProcName, "parse_global_nspace" ) == 0; + if ( check( TokType::Spec_Virtual ) ) { if ( specifiers ) @@ -10403,6 +10445,8 @@ namespace parser } // + Token prefix_identifier = parse_identifier(); + if ( left && currtok.Text[ 0 ] == '~' ) eat( TokType::Operator ); else @@ -10467,6 +10511,12 @@ namespace parser CodeDestructor result = ( CodeDestructor )make_code(); + if ( prefix_identifier ) + { + prefix_identifier.Length += 1 + identifier.Length; + result->Name = get_cached_string( prefix_identifier ); + } + if ( specifiers ) result->Specs = specifiers; @@ -11256,11 +11306,15 @@ namespace parser bool is_in_global_nspace = has_context && str_compare( Context.Scope->Prev->ProcName, "parse_global_nspace" ) == 0; // Possible constructor implemented at global file scope. - if (is_in_global_nspace && is_constructor_definition()) + if (is_in_global_nspace) { - definition = parse_constructor( specifiers ); - // :: () { ... } - break; + Code constructor_destructor = parse_global_nspace_constructor_destructor( specifiers ); + if ( constructor_destructor ) + { + definition = constructor_destructor; + // :: () { ... } + break; + } } // Possible user Defined operator casts diff --git a/Project/Source/GasaGen/gen.hpp b/Project/Source/GasaGen/gen.hpp index 8613334..95085b1 100644 --- a/Project/Source/GasaGen/gen.hpp +++ b/Project/Source/GasaGen/gen.hpp @@ -2070,7 +2070,7 @@ struct AST_Destructor Code Next; parser::Token* Tok; Code Parent; - char _PAD_NAME_[ sizeof( StringCached ) ]; + StringCached Name; CodeT Type; char _PAD_UNUSED_[ sizeof( ModuleFlag ) + sizeof( u32 ) ]; }; diff --git a/scripts/.clang-format b/scripts/.clang-format index 06805ad..eeda36b 100644 --- a/scripts/.clang-format +++ b/scripts/.clang-format @@ -151,17 +151,17 @@ SpaceBeforeRangeBasedForLoopColon: true SpaceBeforeSquareBrackets: false SpacesBeforeTrailingComments: 4 -SpaceInEmptyBlock: true +SpaceInEmptyBlock: false SpaceInEmptyParentheses: false -SpacesInAngles: true -SpacesInCStyleCastParentheses: true -SpacesInConditionalStatement: true -SpacesInContainerLiterals: true +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false SpacesInLineCommentPrefix: Minimum: 1 Maximum: 20 SpacesInParentheses: true -SpacesInSquareBrackets: true +SpacesInSquareBrackets: false Standard: c++17 diff --git a/scripts/gen_pass_gasa.ps1 b/scripts/gen_pass_gasa.ps1 index 62ded28..1293420 100644 --- a/scripts/gen_pass_gasa.ps1 +++ b/scripts/gen_pass_gasa.ps1 @@ -105,8 +105,12 @@ function run-gengasa $path_AbilitySystem = join-path $path_gasa 'AbilitySystem' $include = @( - 'GasaAttributeSet.h', 'GasaAttributeSet.cpp', 'LETS_SEE.h' + 'GasaAttributeSet.h', 'GasaAttributeSet.cpp' ) format-cpp $path_AbilitySystem $include $null + + $path_KismetPrivate = 'C:\projects\Unreal\Surgo\UE\Engine\Source\Editor\Kismet\Private\' + $include = @( 'SBlueprintActionMenu.cpp' ) + format-cpp $path_KismetPrivate $include $null } run-gengasa