Updates to GasaGen, replace engine's void SBlueprintActionMenu::Construct with new code

This commit is contained in:
Edward R. Gonzalez 2024-04-17 12:16:22 -04:00
parent e043d322b1
commit 466adc5bd9
9 changed files with 514 additions and 133 deletions

46
GASATHON.10x Normal file
View File

@ -0,0 +1,46 @@
<?xml version="1.0"?>
<N10X>
<Workspace>
<IncludeFilter>*.*</IncludeFilter>
<ExcludeFilter>*.obj,*.lib,*.pch,*.dll,*.pdb,.vs,Debug,Release,x64,obj,*.user,Intermediate</ExcludeFilter>
<SyncFiles>true</SyncFiles>
<Recursive>true</Recursive>
<ShowEmptyFolders>true</ShowEmptyFolders>
<IncludeFilesWithoutExt>false</IncludeFilesWithoutExt>
<IsVirtual>false</IsVirtual>
<IsFolder>false</IsFolder>
<BuildCommand></BuildCommand>
<RebuildCommand></RebuildCommand>
<BuildFileCommand></BuildFileCommand>
<CleanCommand></CleanCommand>
<BuildWorkingDirectory></BuildWorkingDirectory>
<CancelBuild></CancelBuild>
<RunCommand></RunCommand>
<RunCommandWorkingDirectory></RunCommandWorkingDirectory>
<DebugCommand></DebugCommand>
<ExePathCommand></ExePathCommand>
<DebugSln></DebugSln>
<UseVisualStudioEnvBat>false</UseVisualStudioEnvBat>
<Configurations>
<Configuration>Debug</Configuration>
<Configuration>Release</Configuration>
</Configurations>
<Platforms>
<Platform>x64</Platform>
</Platforms>
<AdditionalIncludePaths>
<AdditionalIncludePath>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\include</AdditionalIncludePath>
<AdditionalIncludePath>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.39.33519\ATLMFC\include</AdditionalIncludePath>
<AdditionalIncludePath>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\VS\include</AdditionalIncludePath>
<AdditionalIncludePath>C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt</AdditionalIncludePath>
<AdditionalIncludePath>C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\um</AdditionalIncludePath>
<AdditionalIncludePath>C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\shared</AdditionalIncludePath>
<AdditionalIncludePath>C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\winrt</AdditionalIncludePath>
<AdditionalIncludePath>C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\cppwinrt</AdditionalIncludePath>
<AdditionalIncludePath>C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\include\um</AdditionalIncludePath>
</AdditionalIncludePaths>
<Defines></Defines>
<ConfigProperties></ConfigProperties>
<Children></Children>
</Workspace>
</N10X>

View File

@ -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;
}

View File

@ -0,0 +1,273 @@
constexpr StrC SBlueprintActionMenu_Construct_Replacement = txt(R"(
void SBlueprintActionMenu::Construct( const FArguments& InArgs, TSharedPtr<FBlueprintEditor> 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<UEdGraphSchema_K2>();
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<SWidget> AddImportTargetContent = SNullWidget::NullWidget;
if (GetDefault<UBlueprintEditorSettings>()->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<SComboButton> 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<SBox> ContentBox = SNew(SBox);
{
TSharedRef<SVerticalBox> VBox = SNew(SVerticalBox);
{
SVerticalBox::FSlot::FSlotArguments
SearchIndicator = SVerticalBox::Slot();
{
TSharedRef<SHorizontalBox> HBox = SNew(SHorizontalBox);
{
SHorizontalBox::FSlot::FSlotArguments
TypePill = SHorizontalBox::Slot();
{
TSharedRef<SImage> 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<STextBlock> 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<SCheckBox> 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<SBox> Box = SNew(SBox);
{
TSharedRef<SProgressBar> Bar = SNew(SProgressBar);
{
Bar->SetBorderPadding(FVector2D( 0, 0 ));
Bar->SetPercent( TAttribute<TOptional<float>>::CreateLambda([this]()
{
return ContextMenuBuilder.IsValid() ? ContextMenuBuilder->GetPendingActionsProgress() : 0.0f;
}));
}
Box->SetContent( Bar );
Box->SetHeightOverride(2);
Box->SetVisibility(TAttribute<EVisibility>().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<FBlueprintEditor> 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<CodeFn>();
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();
}

View File

@ -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<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
<body>
<field_lifetimes>
}
)));
}

View File

@ -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 )

View File

@ -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];
// <Attributes> <Specifiers> ... <Identifier>
if ( tok_left.Type != TokType::Access_StaticSymbol )
return false;
-- idx;
tok_left = tokens[idx];
// <Attributes> <Specifiers> ... :: <Identifier>
// 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
// <Name> :: <Name> (
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 );
// <Attributes> <Specifiers> <Name> :: <Name> <Type> () { ... }
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];
// <Attributes> <Specifiers> ... <Identifier>
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];
// <Attributes> <Specifiers> ... :: <Identifier>
// 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)
{
// <Name> :: ~<Name> (
result = parse_destructor( specifiers );
}
else {
// <Name> :: <Name> (
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 <qualifier>::<nested 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;
// <Name> ( <Parameters> ) : <InitializerList>
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
}
// <Virtual Specifier>
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,12 +11306,16 @@ 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 );
Code constructor_destructor = parse_global_nspace_constructor_destructor( specifiers );
if ( constructor_destructor )
{
definition = constructor_destructor;
// <Attributes> <Specifiers> <Name> :: <Name> <Type> () { ... }
break;
}
}
// Possible user Defined operator casts
if (is_in_global_nspace)

View File

@ -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 ) ];
};

View File

@ -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

View File

@ -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