Updates to GasaGen, replace engine's void SBlueprintActionMenu::Construct with new code
This commit is contained in:
		| @@ -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; | ||||
| } | ||||
|   | ||||
							
								
								
									
										273
									
								
								Project/Source/GasaGen/GasaGen_ChangeBPActionMenu.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								Project/Source/GasaGen/GasaGen_ChangeBPActionMenu.cpp
									
									
									
									
									
										Normal 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(); | ||||
| } | ||||
| @@ -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> | ||||
| 					} | ||||
| 				))); | ||||
| 			} | ||||
|   | ||||
| @@ -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 ) | ||||
|   | ||||
| @@ -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,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 ); | ||||
| 				// <Attributes> <Specifiers> <Name> :: <Name> <Type> () { ... } | ||||
| 				break; | ||||
| 				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 | ||||
|   | ||||
| @@ -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 ) ]; | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user