Was able to parse UObject with gencpp!!!
This commit is contained in:
		
							
								
								
									
										4
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | { | ||||||
|  | 	"autoHide.autoHideSideBar": false, | ||||||
|  | 	"autoHide.autoHidePanel": false | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								Project/Binaries/GasaGen_214406991.raddbgi
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Project/Binaries/GasaGen_214406991.raddbgi
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -4,6 +4,7 @@ | |||||||
| GameDefaultMap=/Game/Levels/StartupMap.StartupMap | GameDefaultMap=/Game/Levels/StartupMap.StartupMap | ||||||
| EditorStartupMap=/Game/Levels/StartupMap.StartupMap | EditorStartupMap=/Game/Levels/StartupMap.StartupMap | ||||||
| GlobalDefaultGameMode=/Game/Core/Game/BP_GameMode.BP_GameMode_C | GlobalDefaultGameMode=/Game/Core/Game/BP_GameMode.BP_GameMode_C | ||||||
|  | GameInstanceClass=/Script/Gasa.GasaGameInstance | ||||||
|  |  | ||||||
| [/Script/WindowsTargetPlatform.WindowsTargetSettings] | [/Script/WindowsTargetPlatform.WindowsTargetSettings] | ||||||
| DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 | DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 | ||||||
|   | |||||||
| @@ -21,7 +21,6 @@ public: | |||||||
| 	FGameplayAttributeData MaxMana; | 	FGameplayAttributeData MaxMana; | ||||||
|  |  | ||||||
| 	UGasaAttributeSet(); | 	UGasaAttributeSet(); | ||||||
|  |  | ||||||
| 	UFUNCTION() | 	UFUNCTION() | ||||||
| 	void Client_OnRep_Health( FGameplayAttributeData& PrevHealth ); | 	void Client_OnRep_Health( FGameplayAttributeData& PrevHealth ); | ||||||
| 	UFUNCTION() | 	UFUNCTION() | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ | |||||||
| #include "Camera/CameraComponent.h" | #include "Camera/CameraComponent.h" | ||||||
| #include "GameFramework/SpringArmComponent.h" | #include "GameFramework/SpringArmComponent.h" | ||||||
|  |  | ||||||
|  |  | ||||||
| ACameraMount::ACameraMount() | ACameraMount::ACameraMount() | ||||||
| { | { | ||||||
| 	PrimaryActorTick.bCanEverTick = true; | 	PrimaryActorTick.bCanEverTick = true; | ||||||
|   | |||||||
| @@ -22,4 +22,3 @@ public: | |||||||
| 	void PostInitializeComponents() override; | 	void PostInitializeComponents() override; | ||||||
| #pragma endregion Actor | #pragma endregion Actor | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,4 +5,7 @@ void UGasaGameInstance::Init() | |||||||
| 	Super::Init(); | 	Super::Init(); | ||||||
|  |  | ||||||
| 	DevOptionsCache.CachedDevOptions(); | 	DevOptionsCache.CachedDevOptions(); | ||||||
|  |  | ||||||
|  | 	using namespace Gasa; | ||||||
|  | 	Log(FString::Printf(TEXT("UObject Size:  %d RT: %d"), sizeof(UObject), UObject::StaticClass()->PropertiesSize )); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								Project/Source/Gasa/ScratchMeta.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Project/Source/Gasa/ScratchMeta.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | // Don't keep this included anywhere | ||||||
|  | // Purely for inspection purposes | ||||||
|  |  | ||||||
|  | #include "GasaCommon.h" | ||||||
|  |  | ||||||
|  | void test() | ||||||
|  | { | ||||||
|  | 	UObject::StaticClass()->PropertiesSize | ||||||
|  | } | ||||||
							
								
								
									
										7
									
								
								Project/Source/Gasa/UI/GasaUserWidget.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Project/Source/Gasa/UI/GasaUserWidget.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | #include "GasaUserWidget.h" | ||||||
|  |  | ||||||
|  | UGasaUserWidget::UGasaUserWidget(FObjectInitializer const& ObjectInitializer) | ||||||
|  | 	: UUserWidget(ObjectInitializer) | ||||||
|  | { | ||||||
|  | 	 | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								Project/Source/Gasa/UI/GasaUserWidget.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								Project/Source/Gasa/UI/GasaUserWidget.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "Blueprint/UserWidget.h" | ||||||
|  |  | ||||||
|  | #include "GasaUserWidget.generated.h" | ||||||
|  |  | ||||||
|  | UCLASS(Blueprintable) | ||||||
|  | class GASA_API UGasaUserWidget : public UUserWidget | ||||||
|  | { | ||||||
|  | 	GENERATED_BODY() | ||||||
|  | public: | ||||||
|  |  | ||||||
|  | 	UPROPERTY(BlueprintReadOnly) | ||||||
|  | 	TObjectPtr<UObject> WidgetController; | ||||||
|  |  | ||||||
|  | 	UGasaUserWidget(FObjectInitializer const& ObjectInitializer); | ||||||
|  |  | ||||||
|  | 	UFUNCTION(BlueprintCallable) | ||||||
|  | 	void SetWidgetController(UObject* Controller) | ||||||
|  | 	{ | ||||||
|  | 		WidgetController = Controller; | ||||||
|  | 		OnWidgetControllerSet(); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	UFUNCTION(BlueprintImplementableEvent) | ||||||
|  | 	void OnWidgetControllerSet(); | ||||||
|  | }; | ||||||
							
								
								
									
										9
									
								
								Project/Source/Gasa/UI/ProgressBar.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Project/Source/Gasa/UI/ProgressBar.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "Components/ProgressBar.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | UCLASS() | ||||||
|  | class GASA_API UProgressIndicator : public UProgressBar | ||||||
|  | { | ||||||
|  | 	 | ||||||
|  | }; | ||||||
							
								
								
									
										1
									
								
								Project/Source/Gasa/UI/WidgetController.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Project/Source/Gasa/UI/WidgetController.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | #include "WidgetController.h" | ||||||
							
								
								
									
										23
									
								
								Project/Source/Gasa/UI/WidgetController.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								Project/Source/Gasa/UI/WidgetController.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "GasaCommon.h" | ||||||
|  | #include "WidgetController.generated.h" | ||||||
|  |  | ||||||
|  | UCLASS(BlueprintType) | ||||||
|  | class GASA_API UWdgetController : public UObject | ||||||
|  | { | ||||||
|  | 	GENERATED_BODY() | ||||||
|  | public: | ||||||
|  |  | ||||||
|  | 	UPROPERTY(BlueprintReadOnly, Category="Player") | ||||||
|  | 	TObjectPtr<APlayerController> Controller; | ||||||
|  |  | ||||||
|  | 	UPROPERTY(BlueprintReadOnly, Category="Player") | ||||||
|  | 	TObjectPtr<APlayerState> PlayerState; | ||||||
|  |  | ||||||
|  | 	UPROPERTY(BlueprintReadOnly, Category="Player") | ||||||
|  | 	TObjectPtr<UAbilitySystemComponent> AbilitySystem; | ||||||
|  | 	 | ||||||
|  | 	UPROPERTY(BlueprintReadOnly, Category="Player") | ||||||
|  | 	TObjectPtr<UAttributeSet> Attributes; | ||||||
|  | }; | ||||||
| @@ -5,280 +5,136 @@ | |||||||
| #define GEN_IMPLEMENTATION | #define GEN_IMPLEMENTATION | ||||||
| #include "gen.cpp" | #include "gen.cpp" | ||||||
| #include "gen.builder.cpp" | #include "gen.builder.cpp" | ||||||
|  | #include "gen.scanner.hpp" | ||||||
| using namespace gen; | using namespace gen; | ||||||
|  |  | ||||||
| // Program assumes its working directory is the project | #include "GasaGenCommon.cpp" | ||||||
| #define path_config                              "./Source/Config/" | #include "GasaGen_UGasaAttributeSet.cpp" | ||||||
| #define path_module_gasa                         "./Source/Gasa/" |  | ||||||
| #define path_gasa_ability_system path_module_gasa "AbilitySystem/" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| void def_attribute_properties( CodeBody body, Array<StringCached> properties ) |  | ||||||
| { |  | ||||||
| 	for ( StringCached property : properties ) |  | ||||||
| 	{ |  | ||||||
| 		Code field_uproperty = code_fmt( "property", (StrC)property, stringize( |  | ||||||
| 			UPROPERTY(ReplicatedUsing=Client_OnRep_<property>, EditAnywhere, BlueprintReadWrite, Category="Attributes") |  | ||||||
| 		)); |  | ||||||
|  |  | ||||||
| 		CodeType type_FGameplayAttributeData = def_type( txt("FGameplayAttributeData")); |  | ||||||
|  |  | ||||||
| 		body.append(fmt_newline); |  | ||||||
| 		body.append( field_uproperty ); |  | ||||||
| 		body.append(fmt_newline); |  | ||||||
| 		body.append( def_variable( type_FGameplayAttributeData, StrC(property)) ); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void def_attribute_field_on_reps( CodeBody body, Array<StringCached> fields ) |  | ||||||
| { |  | ||||||
| 	for ( StringCached field : fields ) |  | ||||||
| 	{ |  | ||||||
| 		Code umeta_UFUNCTION = code_str( UFUNCTION() ); |  | ||||||
|  |  | ||||||
| 		body.append(fmt_newline); |  | ||||||
| 		body.append( umeta_UFUNCTION ); |  | ||||||
| 		body.append(fmt_newline); |  | ||||||
| 		body.append( code_fmt( "field", (StrC)field, stringize( |  | ||||||
| 			void Client_OnRep_<field>(FGameplayAttributeData& Prev<field>); |  | ||||||
| 		))); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void def_attribute_field_property_getters( CodeBody body, StrC class_name, Array<StringCached> properties ) |  | ||||||
| { |  | ||||||
| 	for ( String property : properties ) |  | ||||||
| 	{ |  | ||||||
| 		CodeFn generated_get_attribute = parse_function( |  | ||||||
| 			token_fmt( "class_name", class_name, "property", (StrC)property, |  | ||||||
| 			stringize( |  | ||||||
| 				static FGameplayAttribute Get<property>Attribute() |  | ||||||
| 				{ |  | ||||||
| 					static FProperty* Prop = FindFieldChecked<FProperty>(<class_name>::StaticClass(), GET_MEMBER_NAME_CHECKED(<class_name>, <property>)); |  | ||||||
| 					return Prop; |  | ||||||
| 				} |  | ||||||
| 			))); |  | ||||||
| 		body.append( generated_get_attribute ); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void def_attribute_field_value_getters( CodeBody body, Array<StringCached> properties ) |  | ||||||
| { |  | ||||||
| 	for ( String property : properties ) |  | ||||||
| 	{ |  | ||||||
| 	#pragma push_macro(FORCEINLINE) |  | ||||||
| 	#undef FORCEINLINE |  | ||||||
|  |  | ||||||
| 		body.append( code_fmt( "property", (StrC)property, |  | ||||||
| 		stringize( |  | ||||||
| 			FORCEINLINE float Get<property>() const |  | ||||||
| 			{ |  | ||||||
| 				return <property>.GetCurrentValue(); |  | ||||||
| 			} |  | ||||||
| 		))); |  | ||||||
|  |  | ||||||
| 	#pragma pop_macro(FORCEINLINE) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void def_attribute_field_value_setters( CodeBody body, Array<StringCached> properties ) |  | ||||||
| { |  | ||||||
| 	for ( String property : properties ) |  | ||||||
| 	{ |  | ||||||
| 		body.append( code_fmt( "property", (StrC)property, |  | ||||||
| 		stringize( |  | ||||||
| 			FORCEINLINE void Set<property>(float NewVal) |  | ||||||
| 			{ |  | ||||||
| 				UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent(); |  | ||||||
| 				if (ensure(AbilityComp)) |  | ||||||
| 				{ |  | ||||||
| 					AbilityComp->SetNumericAttributeBase(Get<property>Attribute(), NewVal); |  | ||||||
| 				}; |  | ||||||
| 			} |  | ||||||
| 		))); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void def_attribute_field_initers ( CodeBody body, Array<StringCached> properties ) |  | ||||||
| { |  | ||||||
| 	for ( String property : properties ) |  | ||||||
| 	{ |  | ||||||
| 		body.append( code_fmt( "property", (StrC)property, |  | ||||||
| 		stringize( |  | ||||||
| 			FORCEINLINE void Init<property>(float NewVal) |  | ||||||
| 			{ |  | ||||||
| 				<property>.SetBaseValue(NewVal); |  | ||||||
| 				<property>.SetCurrentValue(NewVal); |  | ||||||
| 			} |  | ||||||
| 		))); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void impl_attribute_fields( CodeBody body, StrC class_name, Array<StringCached> properties ) |  | ||||||
| { |  | ||||||
| 	for ( String property : properties ) |  | ||||||
| 	{ |  | ||||||
| 		body.append(fmt_newline); |  | ||||||
|  |  | ||||||
| 		CodeFn field_impl = parse_function( token_fmt( "class_name", class_name, "property", (StrC)property, |  | ||||||
| 		stringize( |  | ||||||
| 			void <class_name>::Client_OnRep_<property>(FGameplayAttributeData& Prev<property>) |  | ||||||
| 			{ |  | ||||||
| 				GAMEPLAYATTRIBUTE_REPNOTIFY(<class_name>, <property>, Prev<property>) |  | ||||||
| 			} |  | ||||||
| 		))); |  | ||||||
|  |  | ||||||
| 		body.append( field_impl ); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int gen_main() | int gen_main() | ||||||
| { | { | ||||||
| 	gen::init(); | 	gen::init(); | ||||||
| 	log_fmt("Generating code for the Gasa module"); | 	log_fmt("Generating code for the Gasa module"); | ||||||
|  |  | ||||||
| 	Code umeta_uclass         = code_str( UCLASS() ); | 	// Initialize Globals | ||||||
| 	Code umeta_generated_body = code_str( GENERATED_BODY() ); | 	umeta_uclass         = code_str( UCLASS() ); | ||||||
| 	Code gasa_api             = code_str( GASA_API ); | 	umeta_generated_body = code_str( GENERATED_BODY() ); | ||||||
|  | 	gasa_api             = code_str( GASA_API ); | ||||||
|  |  | ||||||
| 	CodeType type_UAttributeSet = def_type( txt("UAttributeSet") ); | 	StrC str_generated_body        = txt("GENERATED_BODY("); | ||||||
|  | 	StrC str_generated_uclass_body = txt("GENERATED_UCLASS_BODY("); | ||||||
|  | 	StrC str_property_binding_impl = txt("PROPERTY_BINDING_IMPLEMENTATION"); | ||||||
|  | 	StrC str_uclass                = txt("UCLASS("); | ||||||
|  | 	StrC str_ue_deprecated         = txt("UE_DEPRECATED("); | ||||||
|  | 	StrC str_ufunction             = txt("UFUNCTION("); | ||||||
|  | 	StrC str_uproperty             = txt("UPROPERTY("); | ||||||
|  | 	StrC str_umg_api               = txt("UMG_API"); | ||||||
|  |  | ||||||
| 	CodeComment generation_notice = def_comment(txt("This was generated by GasaGen/GasaGen.cpp")); | 	StrC str_declare_log_category_extern = txt("DECLARE_LOG_CATEGORY_EXTERN("); | ||||||
|  | 	StrC str_enum_class_flags = txt("ENUM_CLASS_FLAGS("); | ||||||
|  | 	StrC str_declare_class = txt("DECLARE_CLASS("); | ||||||
|  | 	StrC str_define_default_object_initializer_constructor_call = txt("DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL("); | ||||||
|  | 	StrC str_core_object_api = txt("COREUOBJECT_API"); | ||||||
|  | 	StrC str_macro_text = txt("TEXT("); | ||||||
|  | 	StrC str_declare_multicast_delegate_one_parameter = txt("DECLARE_MULTICAST_DELEGATE_OneParam("); | ||||||
|  | 	StrC str_declare_multicast_delegate_two_parameter = txt("DECLARE_MULTICAST_DELEGATE_TwoParams("); | ||||||
|  | 	StrC str_declare_multicast_delegate_three_parameter = txt("DECLARE_MULTICAST_DELEGATE_ThreeParams("); | ||||||
|  | 	StrC str_declare_delegate_retval_one_param = txt("DECLARE_DELEGATE_RetVal_OneParam("); | ||||||
|  | 	StrC str_declare_function = txt("DECLARE_FUNCTION("); | ||||||
|  | 	StrC str_result_decl = txt("RESULT_DECL"); | ||||||
|  | 	StrC str_property_binding_implementation = txt("PROPERTY_BINDING_IMPLEMENTATION("); | ||||||
|  | 	StrC str_FORCEINLINE = txt("FORCEINLINE"); | ||||||
|  |  | ||||||
| 	Array<StringCached> attribute_fields = Array<StringCached>::init( GlobalAllocator); | 	PreprocessorDefines.append( get_cached_string(str_generated_body)); | ||||||
| 	attribute_fields.append( get_cached_string(txt("Health"))); | 	PreprocessorDefines.append( get_cached_string(str_generated_uclass_body)); | ||||||
| 	attribute_fields.append( get_cached_string(txt("MaxHealth"))); | 	PreprocessorDefines.append( get_cached_string(str_property_binding_impl)); | ||||||
| 	attribute_fields.append( get_cached_string(txt("Mana"))); | 	PreprocessorDefines.append( get_cached_string(str_ue_deprecated)); | ||||||
| 	attribute_fields.append( get_cached_string(txt("MaxMana"))); | 	PreprocessorDefines.append( get_cached_string(str_uclass)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_ufunction)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_uproperty)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_umg_api)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_declare_log_category_extern)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_enum_class_flags)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_declare_class)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_define_default_object_initializer_constructor_call)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_core_object_api)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_macro_text)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_declare_multicast_delegate_one_parameter)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_declare_multicast_delegate_two_parameter)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_declare_multicast_delegate_three_parameter)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_declare_delegate_retval_one_param)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_declare_function)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_result_decl)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_property_binding_implementation)); | ||||||
|  | 	PreprocessorDefines.append( get_cached_string(str_FORCEINLINE)); | ||||||
|  |  | ||||||
|  | #define path_UProgressBar \ | ||||||
|  | 	"C:/projects/Unreal/Surgo/UE/Engine/Source/Runtime/UMG/Public/Components/ProgressBar.h" | ||||||
|  |  | ||||||
| 	StrC attributeset_name = txt("UGasaAttributeSet"); | 	FileContents content = file_read_contents( GlobalAllocator, true, path_UProgressBar ); | ||||||
|  | 	CodeBody parsed_uprogressbar = parse_global_body( StrC { content.size, (char const*)content.data }); | ||||||
|  |  | ||||||
| 	Builder header = Builder::open( path_gasa_ability_system "GasaAttributeSet.h"); | 	log_fmt("\n\n"); | ||||||
|  | 	for ( Code gcode : parsed_uprogressbar ) | ||||||
| 	{ | 	{ | ||||||
| 		header.print(generation_notice); | 		if ( gcode->Type == CodeT::Class ) | ||||||
| 		header.print(fmt_newline); |  | ||||||
| 		{ | 		{ | ||||||
| 			CodeInclude Include_AttributeSet               = def_include(txt("AttributeSet.h")); | 			log_fmt("Class %S - Definitions:\n", gcode->Name); | ||||||
| 			CodeInclude Include_AbilitySystemComponent     = def_include(txt("AbilitySystemComponent.h")); |  | ||||||
| 			CodeInclude Include_GasaAttributeSet_Generated = def_include(txt("GasaAttributeSet.generated.h")); |  | ||||||
|  |  | ||||||
| 			CodeAttributes attributes = def_attributes( gasa_api->Name); | 			if (gcode->Body->Type != CodeT::Class_Body) | ||||||
|  | 				continue; | ||||||
| 			CodeClass GasaAttributeSet = {}; | 			for ( Code class_code : gcode->Body->cast<CodeBody>() ) | ||||||
| 			{ | 			{ | ||||||
| 				CodeBody body = def_body( CodeT::Class_Body ); | 				switch ( class_code->Type ) | ||||||
| 				{ | 				{ | ||||||
| 					body.append( umeta_generated_body); | 					case CodeT::Variable: | ||||||
| 					body.append( fmt_newline); | 					case CodeT::Function: | ||||||
| 					body.append( access_public ); | 					case CodeT::Function_Fwd: | ||||||
|  | 						if ( class_code->Name ) | ||||||
| 					def_attribute_properties( body, attribute_fields); | 						{ | ||||||
|  | 							log_fmt("%s\n", class_code->Name ); | ||||||
| 					body.append(fmt_newline); | 						} | ||||||
| 					body.append( def_constructor() ); | 					break; | ||||||
|  |  | ||||||
| 					def_attribute_field_on_reps( body, attribute_fields); |  | ||||||
|  |  | ||||||
| 					body.append(fmt_newline); |  | ||||||
|  |  | ||||||
| 					body.append( fmt_newline ); |  | ||||||
| 					body.append( def_pragma(code( region Getters ))); |  | ||||||
| 					def_attribute_field_property_getters( body, attributeset_name, attribute_fields ); |  | ||||||
| 					def_attribute_field_value_getters( body, attribute_fields ); |  | ||||||
| 					body.append( def_pragma(code( endregion Getters ))); |  | ||||||
| 					body.append( fmt_newline ); |  | ||||||
|  |  | ||||||
| 					body.append( def_pragma(code( region Setters ))); |  | ||||||
| 					def_attribute_field_value_setters( body, attribute_fields ); |  | ||||||
| 					def_attribute_field_initers( body, attribute_fields ); |  | ||||||
| 					body.append( def_pragma(code( endregion Setters ))); |  | ||||||
| 					body.append( fmt_newline ); |  | ||||||
|  |  | ||||||
| 					body.append( def_pragma( txt("region UObject"))); |  | ||||||
| 					body.append( parse_function( code( |  | ||||||
| 						void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override; |  | ||||||
| 					))); |  | ||||||
| 					body.append( def_pragma( txt("endregion UObject"))); |  | ||||||
| 				} | 				} | ||||||
| 				GasaAttributeSet = def_class( txt("UGasaAttributeSet"), body |  | ||||||
| 					, type_UAttributeSet |  | ||||||
| 					, AccessSpec::Public |  | ||||||
| 					, attributes |  | ||||||
| 				); |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			CodeNS ns_gasa = parse_namespace( code( |  | ||||||
| 				namespace Gasa |  | ||||||
| 				{ |  | ||||||
| 					inline |  | ||||||
| 					UGasaAttributeSet const* GetAttributeSet( UAbilitySystemComponent* ASC ) |  | ||||||
| 					{ |  | ||||||
| 						return Cast<UGasaAttributeSet>(ASC->GetAttributeSet( UGasaAttributeSet::StaticClass() )); |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			)); |  | ||||||
|  |  | ||||||
| 			header.print( Include_AttributeSet); |  | ||||||
| 			header.print( Include_AbilitySystemComponent); |  | ||||||
| 			header.print( Include_GasaAttributeSet_Generated); |  | ||||||
| 			header.print( fmt_newline); |  | ||||||
| 			header.print(umeta_uclass); |  | ||||||
| 			header.print(GasaAttributeSet); |  | ||||||
| 			header.print(ns_gasa); |  | ||||||
| 		} | 		} | ||||||
| 		header.write(); |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	Builder source = Builder::open( path_gasa_ability_system "GasaAttributeSet.cpp" ); | #define path_UObject \ | ||||||
|  | 	R"(C:\projects\Unreal\Surgo\UE\Engine\Source\Runtime\CoreUObject\Public\UObject\Object.h)" | ||||||
|  |  | ||||||
|  | 	content = file_read_contents( GlobalAllocator, true, path_UObject ); | ||||||
|  | 	CodeBody parsed_uobject = parse_global_body( StrC { content.size, (char const*)content.data }); | ||||||
|  |  | ||||||
|  | 	log_fmt("\n\n"); | ||||||
|  | 	for ( Code gcode : parsed_uobject ) | ||||||
| 	{ | 	{ | ||||||
| 		source.print(generation_notice); | 		if ( gcode->Type == CodeT::Class ) | ||||||
| 		source.print( def_include( txt("GasaAttributeSet.h"))); |  | ||||||
| 		source.print(fmt_newline); |  | ||||||
| 		source.print( def_include( txt("AbilitySystemComponent.h"))); |  | ||||||
| 		source.print( def_include( txt("Net/UnrealNetwork.h"))); |  | ||||||
| 		source.print( def_include( txt("Networking/GasaNetLibrary.h"))); |  | ||||||
| 		{ | 		{ | ||||||
| 			CodeBody body = def_body( CodeT::Global_Body ); | 			log_fmt("Class %S - Definitions:\n", gcode->Name); | ||||||
| 			body.append(fmt_newline); |  | ||||||
| 			body.append(code_str( |  | ||||||
| 				UGasaAttributeSet::UGasaAttributeSet() |  | ||||||
| 				{ |  | ||||||
| 					InitHealth( 100.f ); |  | ||||||
| 					InitMaxHealth( 100.f ); |  | ||||||
| 					InitMana(( 50.f )); |  | ||||||
| 					InitMaxMana( 50.f ); |  | ||||||
| 				} |  | ||||||
| 			)); |  | ||||||
| 			body.append(fmt_newline); |  | ||||||
|  |  | ||||||
| 			impl_attribute_fields( body, attributeset_name, attribute_fields); | 			if (gcode->Body->Type != CodeT::Class_Body) | ||||||
|  | 				continue; | ||||||
| 			CodeFn GetLifetimeOfReplicatedProps; | 			for ( Code class_code : gcode->Body->cast<CodeBody>() ) | ||||||
| 			{ | 			{ | ||||||
| 				CodeBody field_lifetimes = def_body( CodeT::Function_Body); | 				switch ( class_code->Type ) | ||||||
| 				for (StringCached field : attribute_fields) |  | ||||||
| 				{ | 				{ | ||||||
| 					field_lifetimes.append( code_fmt( "field", (StrC)field, stringize( | 					case CodeT::Variable: | ||||||
| 						DOREPLIFETIME_DEFAULT_GAS(UGasaAttributeSet, <field>); | 					case CodeT::Function: | ||||||
| 					))); | 					case CodeT::Function_Fwd: | ||||||
|  | 						if ( class_code->Name ) | ||||||
|  | 						{ | ||||||
|  | 							log_fmt("%s\n", class_code->Name ); | ||||||
|  | 						} | ||||||
|  | 					break; | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				GetLifetimeOfReplicatedProps = parse_function( token_fmt( "body", (StrC)(field_lifetimes.to_string()), stringize( |  | ||||||
| 					void UGasaAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const |  | ||||||
| 					{ |  | ||||||
| 						Super::GetLifetimeReplicatedProps(OutLifetimeProps); |  | ||||||
| 						<body> |  | ||||||
| 					} |  | ||||||
| 				))); |  | ||||||
| 			} | 			} | ||||||
| 			body.append(GetLifetimeOfReplicatedProps); |  | ||||||
|  |  | ||||||
| 			source.print(body); |  | ||||||
| 		} | 		} | ||||||
| 		source.write(); |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// gen::deinit(); | 	StrC str_gasa_api = txt("GASA_API"); | ||||||
|  |  | ||||||
|  | 	gen_UGasaAttributeSet(); | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								Project/Source/GasaGen/GasaGenCommon.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Project/Source/GasaGen/GasaGenCommon.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | // Program assumes its working directory is the project | ||||||
|  | #define path_config                              "./Source/Config/" | ||||||
|  | #define path_module_gasa                         "./Source/Gasa/" | ||||||
|  | #define path_gasa_ability_system path_module_gasa "AbilitySystem/" | ||||||
|  |  | ||||||
|  | #pragma region Globals | ||||||
|  | // These Code objects are created before anything else after gencpp does its initializatioon | ||||||
|  | global Code umeta_uclass; | ||||||
|  | global Code umeta_generated_body; | ||||||
|  | global Code gasa_api; | ||||||
|  | #pragma endregion Globals | ||||||
							
								
								
									
										268
									
								
								Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,268 @@ | |||||||
|  | // Used in the GasaGen.cpp translation unit | ||||||
|  |  | ||||||
|  | void def_attribute_properties            ( CodeBody body, Array<StringCached> properties ); | ||||||
|  | void def_attribute_field_on_reps         ( CodeBody body, Array<StringCached> properties ); | ||||||
|  | void def_attribute_field_property_getters( CodeBody body, StrC class_name, Array<StringCached> properties ); | ||||||
|  | void def_attribute_field_value_getters   ( CodeBody body, Array<StringCached> properties ); | ||||||
|  | void def_attribute_field_value_setters   ( CodeBody body, Array<StringCached> properties ); | ||||||
|  | void def_attribute_field_initers         ( CodeBody body, Array<StringCached> properties ); | ||||||
|  | void impl_attribute_fields               ( CodeBody body, StrC class_name, Array<StringCached> properties ); | ||||||
|  |  | ||||||
|  | void gen_UGasaAttributeSet() | ||||||
|  | { | ||||||
|  | 	CodeType   type_UAttributeSet = def_type( txt("UAttributeSet") ); | ||||||
|  | 	CodeComment generation_notice = def_comment(txt("This was generated by GasaGen/GasaGen.cpp")); | ||||||
|  |  | ||||||
|  | 	Array<StringCached> attribute_fields = Array<StringCached>::init( GlobalAllocator); | ||||||
|  | 	attribute_fields.append( get_cached_string(txt("Health"))); | ||||||
|  | 	attribute_fields.append( get_cached_string(txt("MaxHealth"))); | ||||||
|  | 	attribute_fields.append( get_cached_string(txt("Mana"))); | ||||||
|  | 	attribute_fields.append( get_cached_string(txt("MaxMana"))); | ||||||
|  |  | ||||||
|  | 	StrC class_name = txt("UGasaAttributeSet"); | ||||||
|  |  | ||||||
|  | 	Builder header = Builder::open( path_gasa_ability_system "GasaAttributeSet.h"); | ||||||
|  | 	{ | ||||||
|  | 		header.print(generation_notice); | ||||||
|  | 		header.print(fmt_newline); | ||||||
|  | 		{ | ||||||
|  | 			CodeInclude Include_AttributeSet               = def_include(txt("AttributeSet.h")); | ||||||
|  | 			CodeInclude Include_AbilitySystemComponent     = def_include(txt("AbilitySystemComponent.h")); | ||||||
|  | 			CodeInclude Include_GasaAttributeSet_Generated = def_include(txt("GasaAttributeSet.generated.h")); | ||||||
|  |  | ||||||
|  | 			CodeAttributes api_attribute= def_attributes( gasa_api->Name); | ||||||
|  |  | ||||||
|  | 			CodeClass GasaAttributeSet = {}; | ||||||
|  | 			{ | ||||||
|  | 				CodeBody body = def_body( CodeT::Class_Body ); | ||||||
|  | 				{ | ||||||
|  | 					body.append( umeta_generated_body); | ||||||
|  | 					body.append( fmt_newline); | ||||||
|  | 					body.append( access_public ); | ||||||
|  |  | ||||||
|  | 					def_attribute_properties( body, attribute_fields); | ||||||
|  |  | ||||||
|  | 					body.append(fmt_newline); | ||||||
|  | 					body.append( def_constructor() ); | ||||||
|  |  | ||||||
|  | 					def_attribute_field_on_reps( body, attribute_fields); | ||||||
|  |  | ||||||
|  | 					body.append(fmt_newline); | ||||||
|  |  | ||||||
|  | 					body.append( fmt_newline ); | ||||||
|  | 					body.append( def_pragma(code( region Getters ))); | ||||||
|  | 					def_attribute_field_property_getters( body, class_name, attribute_fields ); | ||||||
|  | 					def_attribute_field_value_getters( body, attribute_fields ); | ||||||
|  | 					body.append( def_pragma(code( endregion Getters ))); | ||||||
|  | 					body.append( fmt_newline ); | ||||||
|  |  | ||||||
|  | 					body.append( def_pragma(code( region Setters ))); | ||||||
|  | 					def_attribute_field_value_setters( body, attribute_fields ); | ||||||
|  | 					def_attribute_field_initers( body, attribute_fields ); | ||||||
|  | 					body.append( def_pragma(code( endregion Setters ))); | ||||||
|  | 					body.append( fmt_newline ); | ||||||
|  |  | ||||||
|  | 					body.append( def_pragma( txt("region UObject"))); | ||||||
|  | 					body.append( parse_function( code( | ||||||
|  | 						void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override; | ||||||
|  | 					))); | ||||||
|  | 					body.append( def_pragma( txt("endregion UObject"))); | ||||||
|  | 				} | ||||||
|  | 				GasaAttributeSet = def_class( class_name, body | ||||||
|  | 					, type_UAttributeSet, AccessSpec::Public | ||||||
|  | 					, api_attribute | ||||||
|  | 				); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			CodeNS ns_gasa = parse_namespace( code( | ||||||
|  | 				namespace Gasa | ||||||
|  | 				{ | ||||||
|  | 					inline | ||||||
|  | 					UGasaAttributeSet const* GetAttributeSet( UAbilitySystemComponent* ASC ) | ||||||
|  | 					{ | ||||||
|  | 						return Cast<UGasaAttributeSet>(ASC->GetAttributeSet( UGasaAttributeSet::StaticClass() )); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			)); | ||||||
|  |  | ||||||
|  | 			header.print( Include_AttributeSet); | ||||||
|  | 			header.print( Include_AbilitySystemComponent); | ||||||
|  | 			header.print( Include_GasaAttributeSet_Generated); | ||||||
|  | 			header.print( fmt_newline); | ||||||
|  | 			header.print(umeta_uclass); | ||||||
|  | 			header.print(GasaAttributeSet); | ||||||
|  | 			header.print(ns_gasa); | ||||||
|  | 		} | ||||||
|  | 		header.write(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	Builder source = Builder::open( path_gasa_ability_system "GasaAttributeSet.cpp" ); | ||||||
|  | 	{ | ||||||
|  | 		source.print(generation_notice); | ||||||
|  | 		header.print(fmt_newline); | ||||||
|  | 		source.print( def_include( txt("GasaAttributeSet.h"))); | ||||||
|  | 		source.print(fmt_newline); | ||||||
|  | 		source.print( def_include( txt("AbilitySystemComponent.h"))); | ||||||
|  | 		source.print( def_include( txt("Net/UnrealNetwork.h"))); | ||||||
|  | 		source.print( def_include( txt("Networking/GasaNetLibrary.h"))); | ||||||
|  | 		{ | ||||||
|  | 			CodeBody body = def_body( CodeT::Global_Body ); | ||||||
|  | 			body.append(fmt_newline); | ||||||
|  |  | ||||||
|  | 			CodeConstructor constructor_for_UGasaAttributeSet = parse_constructor( code( | ||||||
|  | 				UGasaAttributeSet::UGasaAttributeSet() | ||||||
|  | 				{ | ||||||
|  | 					InitHealth( 100.f ); | ||||||
|  | 					InitMaxHealth( 100.f ); | ||||||
|  | 					InitMana(( 50.f )); | ||||||
|  | 					InitMaxMana( 50.f ); | ||||||
|  | 				} | ||||||
|  | 			)); | ||||||
|  |  | ||||||
|  | 			body.append(constructor_for_UGasaAttributeSet ); | ||||||
|  | 			body.append(fmt_newline); | ||||||
|  |  | ||||||
|  | 			impl_attribute_fields( body, class_name, attribute_fields); | ||||||
|  |  | ||||||
|  | 			CodeFn GetLifetimeOfReplicatedProps; | ||||||
|  | 			{ | ||||||
|  | 				CodeBody field_lifetimes = def_body( CodeT::Function_Body); | ||||||
|  | 				for (StringCached field : attribute_fields) | ||||||
|  | 				{ | ||||||
|  | 					field_lifetimes.append( code_fmt( "field", (StrC)field, stringize( | ||||||
|  | 						DOREPLIFETIME_DEFAULT_GAS(UGasaAttributeSet, <field>); | ||||||
|  | 					))); | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				GetLifetimeOfReplicatedProps = parse_function( token_fmt( "body", (StrC)(field_lifetimes.to_string()), stringize( | ||||||
|  | 					void UGasaAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const | ||||||
|  | 					{ | ||||||
|  | 						Super::GetLifetimeReplicatedProps(OutLifetimeProps); | ||||||
|  | 						<body> | ||||||
|  | 					} | ||||||
|  | 				))); | ||||||
|  | 			} | ||||||
|  | 			body.append(GetLifetimeOfReplicatedProps); | ||||||
|  |  | ||||||
|  | 			source.print(body); | ||||||
|  | 		} | ||||||
|  | 		source.write(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void def_attribute_properties( CodeBody body, Array<StringCached> properties ) | ||||||
|  | { | ||||||
|  | 	for ( StringCached property : properties ) | ||||||
|  | 	{ | ||||||
|  | 		Code field_uproperty = code_fmt( "property", (StrC)property, stringize( | ||||||
|  | 			UPROPERTY(ReplicatedUsing=Client_OnRep_<property>, EditAnywhere, BlueprintReadWrite, Category="Attributes") | ||||||
|  | 		)); | ||||||
|  |  | ||||||
|  | 		CodeType type_FGameplayAttributeData = def_type( txt("FGameplayAttributeData")); | ||||||
|  |  | ||||||
|  | 		body.append(fmt_newline); | ||||||
|  | 		body.append( field_uproperty ); | ||||||
|  | 		body.append(fmt_newline); | ||||||
|  | 		body.append( def_variable( type_FGameplayAttributeData, StrC(property)) ); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void def_attribute_field_on_reps( CodeBody body, Array<StringCached> properties ) | ||||||
|  | { | ||||||
|  | 	for ( StringCached property : properties ) | ||||||
|  | 	{ | ||||||
|  | 		Code umeta_UFUNCTION = code_str( UFUNCTION() ); | ||||||
|  |  | ||||||
|  | 		body.append(fmt_newline); | ||||||
|  | 		body.append( umeta_UFUNCTION ); | ||||||
|  | 		body.append(fmt_newline); | ||||||
|  | 		body.append( code_fmt( "property", (StrC)property, stringize( | ||||||
|  | 			void Client_OnRep_<property>(FGameplayAttributeData& Prev<property>); | ||||||
|  | 		))); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void def_attribute_field_property_getters( CodeBody body, StrC class_name, Array<StringCached> properties ) | ||||||
|  | { | ||||||
|  | 	for ( String property : properties ) | ||||||
|  | 	{ | ||||||
|  | 		CodeFn generated_get_attribute = parse_function( | ||||||
|  | 			token_fmt( "class_name", class_name, "property", (StrC)property, | ||||||
|  | 			stringize( | ||||||
|  | 				static FGameplayAttribute Get<property>Attribute() | ||||||
|  | 				{ | ||||||
|  | 					static FProperty* Prop = FindFieldChecked<FProperty>(<class_name>::StaticClass(), GET_MEMBER_NAME_CHECKED(<class_name>, <property>)); | ||||||
|  | 					return Prop; | ||||||
|  | 				} | ||||||
|  | 			))); | ||||||
|  | 		body.append( generated_get_attribute ); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void def_attribute_field_value_getters( CodeBody body, Array<StringCached> properties ) | ||||||
|  | { | ||||||
|  | 	for ( String property : properties ) | ||||||
|  | 	{ | ||||||
|  | #pragma push_macro(FORCEINLINE) | ||||||
|  | #undef FORCEINLINE | ||||||
|  | 		body.append( code_fmt( "property", (StrC)property, | ||||||
|  | 		stringize( | ||||||
|  | 			FORCEINLINE float Get<property>() const | ||||||
|  | 			{ | ||||||
|  | 				return <property>.GetCurrentValue(); | ||||||
|  | 			} | ||||||
|  | 		))); | ||||||
|  | #pragma pop_macro(FORCEINLINE) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void def_attribute_field_value_setters( CodeBody body, Array<StringCached> properties ) | ||||||
|  | { | ||||||
|  | 	for ( String property : properties ) | ||||||
|  | 	{ | ||||||
|  | 		body.append( code_fmt( "property", (StrC)property, | ||||||
|  | 		stringize( | ||||||
|  | 			FORCEINLINE void Set<property>(float NewVal) | ||||||
|  | 			{ | ||||||
|  | 				UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent(); | ||||||
|  | 				if (ensure(AbilityComp)) | ||||||
|  | 				{ | ||||||
|  | 					AbilityComp->SetNumericAttributeBase(Get<property>Attribute(), NewVal); | ||||||
|  | 				}; | ||||||
|  | 			} | ||||||
|  | 		))); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void def_attribute_field_initers ( CodeBody body, Array<StringCached> properties ) | ||||||
|  | { | ||||||
|  | 	for ( String property : properties ) | ||||||
|  | 	{ | ||||||
|  | 		body.append( code_fmt( "property", (StrC)property, | ||||||
|  | 		stringize( | ||||||
|  | 			FORCEINLINE void Init<property>(float NewVal) | ||||||
|  | 			{ | ||||||
|  | 				<property>.SetBaseValue(NewVal); | ||||||
|  | 				<property>.SetCurrentValue(NewVal); | ||||||
|  | 			} | ||||||
|  | 		))); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void impl_attribute_fields( CodeBody body, StrC class_name, Array<StringCached> properties ) | ||||||
|  | { | ||||||
|  | 	for ( String property : properties ) | ||||||
|  | 	{ | ||||||
|  | 		body.append(fmt_newline); | ||||||
|  |  | ||||||
|  | 		CodeFn field_impl = parse_function( token_fmt( "class_name", class_name, "property", (StrC)property, | ||||||
|  | 		stringize( | ||||||
|  | 			void <class_name>::Client_OnRep_<property>(FGameplayAttributeData& Prev<property>) | ||||||
|  | 			{ | ||||||
|  | 				GAMEPLAYATTRIBUTE_REPNOTIFY(<class_name>, <property>, Prev<property>) | ||||||
|  | 			} | ||||||
|  | 		))); | ||||||
|  |  | ||||||
|  | 		body.append( field_impl ); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								Project/Source/GasaGen/Readme.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Project/Source/GasaGen/Readme.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | # GasaGen | ||||||
|  |  | ||||||
|  | This is a single translation unit meta-program for generating code for the Gasa modules. | ||||||
|  | It runs before UHT. | ||||||
|  |  | ||||||
|  | GasaGen.cpp is the effective translation unit. | ||||||
|  | All related code for GasaGen is prefixed with: | ||||||
|  |  | ||||||
|  | ```GasaGen_``` | ||||||
|  |  | ||||||
|  | Anything with the `gen` namespace (case sensitive, including the files), is related to the Gencpp header. Genccp is used. | ||||||
|  | Gencpp is a cpp library to make ergonomic use of stage metaprogramming for C++ in C++. | ||||||
|  |  | ||||||
|  | For how GasaGen is built see [`scripts/gen_pass_gasa.ps1`](../../../scripts/gen_pass_gasa.ps1) | ||||||
| @@ -1564,7 +1564,12 @@ String CodeConstructor::to_string() | |||||||
| void CodeConstructor::to_string_def( String& result ) | void CodeConstructor::to_string_def( String& result ) | ||||||
| { | { | ||||||
| 	AST* ClassStructParent = ast->Parent->Parent; | 	AST* ClassStructParent = ast->Parent->Parent; | ||||||
| 	result.append( ClassStructParent->Name ); | 	if (ClassStructParent) { | ||||||
|  | 		result.append( ClassStructParent->Name ); | ||||||
|  | 	} | ||||||
|  | 	else { | ||||||
|  | 		result.append( ast->Name ); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if ( ast->Params ) | 	if ( ast->Params ) | ||||||
| 		result.append_fmt( "( %S )", ast->Params.to_string() ); | 		result.append_fmt( "( %S )", ast->Params.to_string() ); | ||||||
| @@ -1588,12 +1593,15 @@ void CodeConstructor::to_string_fwd( String& result ) | |||||||
| 	if ( ast->Params ) | 	if ( ast->Params ) | ||||||
| 		result.append_fmt( "( %S )", ast->Params.to_string() ); | 		result.append_fmt( "( %S )", ast->Params.to_string() ); | ||||||
| 	else | 	else | ||||||
| 	{ | 		result.append_fmt("()"); | ||||||
| 		if ( ast->InlineCmt ) |  | ||||||
| 			result.append_fmt( "(); // %S\n", ast->InlineCmt->Content ); | 	if (ast->Body) | ||||||
| 		else | 		result.append_fmt( " = %S", ast->Body.to_string() ); | ||||||
| 			result.append( "();\n" ); |  | ||||||
| 	} | 	if ( ast->InlineCmt ) | ||||||
|  | 		result.append_fmt( "; // %S\n", ast->InlineCmt->Content ); | ||||||
|  | 	else | ||||||
|  | 		result.append( ";" ); | ||||||
| } | } | ||||||
|  |  | ||||||
| String CodeClass::to_string() | String CodeClass::to_string() | ||||||
| @@ -2263,15 +2271,21 @@ String CodeParam::to_string() | |||||||
|  |  | ||||||
| void CodeParam::to_string( String& result ) | void CodeParam::to_string( String& result ) | ||||||
| { | { | ||||||
| 	if ( ast->ValueType.ast == nullptr ) | 	if ( ast->Macro ) | ||||||
| 	{ | 	{ | ||||||
| 		result.append_fmt( "%S", ast->Name ); | 		// Were using the convention that if the value type is a macro we ignore everything else | ||||||
|  | 		// Related to parsing: ( <macro>, ... ) | ||||||
|  | 		result.append( ast->Macro.ast->Content ); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if ( ast->Name ) | 	if ( ast->Name ) | ||||||
| 		result.append_fmt( "%S %S", ast->ValueType.to_string(), ast->Name ); | 	{ | ||||||
|  | 		if ( ast->ValueType.ast == nullptr ) | ||||||
|  | 			result.append_fmt( "%S", ast->Name ); | ||||||
|  | 		else | ||||||
|  | 			result.append_fmt( "%S %S", ast->ValueType.to_string(), ast->Name ); | ||||||
|  | 	} | ||||||
| 	else | 	else | ||||||
| 		result.append_fmt( "%S", ast->ValueType.to_string() ); | 		result.append_fmt( "%S", ast->ValueType.to_string() ); | ||||||
|  |  | ||||||
| @@ -5826,11 +5840,12 @@ namespace parser | |||||||
| 		TF_Preprocess      = bit( 2 ), | 		TF_Preprocess      = bit( 2 ), | ||||||
| 		TF_Preprocess_Cond = bit( 3 ), | 		TF_Preprocess_Cond = bit( 3 ), | ||||||
| 		TF_Attribute       = bit( 6 ), | 		TF_Attribute       = bit( 6 ), | ||||||
| 		TF_AccessSpecifier = bit( 7 ), | 		TF_AccessOperator  = bit( 7 ), | ||||||
| 		TF_Specifier       = bit( 8 ), | 		TF_AccessSpecifier = bit( 8 ), | ||||||
| 		TF_EndDefinition   = bit( 9 ),    // Either ; or } | 		TF_Specifier       = bit( 9 ), | ||||||
| 		TF_Formatting      = bit( 10 ), | 		TF_EndDefinition   = bit( 10 ),    // Either ; or } | ||||||
| 		TF_Literal         = bit( 11 ), | 		TF_Formatting      = bit( 11 ), | ||||||
|  | 		TF_Literal         = bit( 12 ), | ||||||
|  |  | ||||||
| 		TF_Null            = 0, | 		TF_Null            = 0, | ||||||
| 	}; | 	}; | ||||||
| @@ -5854,6 +5869,11 @@ namespace parser | |||||||
| 			return { Length, Text }; | 			return { Length, Text }; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		bool is_access_operator() | ||||||
|  | 		{ | ||||||
|  | 			return bitfield_is_equal( u32, Flags, TF_AccessOperator ); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		bool is_access_specifier() | 		bool is_access_specifier() | ||||||
| 		{ | 		{ | ||||||
| 			return bitfield_is_equal( u32, Flags, TF_AccessSpecifier ); | 			return bitfield_is_equal( u32, Flags, TF_AccessSpecifier ); | ||||||
| @@ -6258,6 +6278,11 @@ namespace parser | |||||||
|  |  | ||||||
| 		TokType type = ETokType::to_type( token ); | 		TokType type = ETokType::to_type( token ); | ||||||
|  |  | ||||||
|  | 		if (type <= TokType::Access_Public && type >= TokType::Access_Private ) | ||||||
|  | 		{ | ||||||
|  | 			token.Flags |= TF_AccessSpecifier; | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		if ( type == ETokType::Decl_Extern_Linkage ) | 		if ( type == ETokType::Decl_Extern_Linkage ) | ||||||
| 		{ | 		{ | ||||||
| 			SkipWhitespace(); | 			SkipWhitespace(); | ||||||
| @@ -6370,7 +6395,7 @@ namespace parser | |||||||
| 				scanner++; | 				scanner++; | ||||||
| 				length++; | 				length++; | ||||||
| 			} | 			} | ||||||
| 			if ( scanner[ 1 ] == '(' ) | 			if ( scanner[ 0 ] == '(' ) | ||||||
| 			{ | 			{ | ||||||
| 				length++; | 				length++; | ||||||
| 			} | 			} | ||||||
| @@ -6439,7 +6464,7 @@ namespace parser | |||||||
| 					token.Text   = scanner; | 					token.Text   = scanner; | ||||||
| 					token.Length = 1; | 					token.Length = 1; | ||||||
| 					token.Type   = TokType::Access_MemberSymbol; | 					token.Type   = TokType::Access_MemberSymbol; | ||||||
| 					token.Flags  = TF_AccessSpecifier; | 					token.Flags  = TF_AccessOperator; | ||||||
|  |  | ||||||
| 					if ( left ) | 					if ( left ) | ||||||
| 					{ | 					{ | ||||||
| @@ -6805,7 +6830,7 @@ namespace parser | |||||||
| 						{ | 						{ | ||||||
| 							token.Length++; | 							token.Length++; | ||||||
| 							//						token.Type = TokType::Access_PointerToMemberSymbol; | 							//						token.Type = TokType::Access_PointerToMemberSymbol; | ||||||
| 							token.Flags |= TF_AccessSpecifier; | 							token.Flags |= TF_AccessOperator; | ||||||
| 							move_forward(); | 							move_forward(); | ||||||
|  |  | ||||||
| 							if ( current == '*' ) | 							if ( current == '*' ) | ||||||
| @@ -7199,6 +7224,7 @@ namespace parser | |||||||
| 	internal CodeBody       parse_class_struct_body( TokType which, Token name = NullToken ); | 	internal CodeBody       parse_class_struct_body( TokType which, Token name = NullToken ); | ||||||
| 	internal Code           parse_class_struct( TokType which, bool inplace_def ); | 	internal Code           parse_class_struct( TokType which, bool inplace_def ); | ||||||
| 	internal CodeDefine     parse_define(); | 	internal CodeDefine     parse_define(); | ||||||
|  | 	internal Code           parse_expression(); | ||||||
| 	internal Code           parse_forward_or_definition( TokType which, bool is_inplace ); | 	internal Code           parse_forward_or_definition( TokType which, bool is_inplace ); | ||||||
| 	internal CodeFn         parse_function_after_name( ModuleFlag mflags, CodeAttributes attributes, CodeSpecifiers specifiers, CodeType ret_type, Token name ); | 	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_function_body(); | ||||||
| @@ -7217,7 +7243,7 @@ namespace parser | |||||||
| 	internal CodeVar            parse_variable_declaration_list(); | 	internal CodeVar            parse_variable_declaration_list(); | ||||||
|  |  | ||||||
| 	internal CodeClass       parse_class( bool inplace_def = false ); | 	internal CodeClass       parse_class( bool inplace_def = false ); | ||||||
| 	internal CodeConstructor parse_constructor(); | 	internal CodeConstructor parse_constructor( CodeSpecifiers specifiers ); | ||||||
| 	internal CodeDestructor  parse_destructor( CodeSpecifiers specifiers = NoCode ); | 	internal CodeDestructor  parse_destructor( CodeSpecifiers specifiers = NoCode ); | ||||||
| 	internal CodeEnum        parse_enum( bool inplace_def = false ); | 	internal CodeEnum        parse_enum( bool inplace_def = false ); | ||||||
| 	internal CodeBody        parse_export_body(); | 	internal CodeBody        parse_export_body(); | ||||||
| @@ -7707,6 +7733,7 @@ namespace parser | |||||||
| 			{ | 			{ | ||||||
| 				access = currtok.to_access_specifier(); | 				access = currtok.to_access_specifier(); | ||||||
| 				// <ModuleFlags> <class/struct> <Attributes> <Name> : <Access Specifier> | 				// <ModuleFlags> <class/struct> <Attributes> <Name> : <Access Specifier> | ||||||
|  | 				eat( currtok.Type ); | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			Token parent_tok = parse_identifier(); | 			Token parent_tok = parse_identifier(); | ||||||
| @@ -7791,6 +7818,13 @@ namespace parser | |||||||
|  |  | ||||||
| 			switch ( currtok_noskip.Type ) | 			switch ( currtok_noskip.Type ) | ||||||
| 			{ | 			{ | ||||||
|  | 				case TokType::Statement_End: | ||||||
|  | 				{ | ||||||
|  | 					// TODO(Ed): Convert this to a general warning procedure | ||||||
|  | 					log_fmt("Dangling end statement found %S\n", currtok_noskip.to_string()); | ||||||
|  | 					eat( TokType::Statement_End ); | ||||||
|  | 					continue; | ||||||
|  | 				} | ||||||
| 				case TokType::NewLine : | 				case TokType::NewLine : | ||||||
| 					member = fmt_newline; | 					member = fmt_newline; | ||||||
| 					eat( TokType::NewLine ); | 					eat( TokType::NewLine ); | ||||||
| @@ -7941,12 +7975,14 @@ namespace parser | |||||||
| 				case TokType::Spec_Consteval : | 				case TokType::Spec_Consteval : | ||||||
| 				case TokType::Spec_Constexpr : | 				case TokType::Spec_Constexpr : | ||||||
| 				case TokType::Spec_Constinit : | 				case TokType::Spec_Constinit : | ||||||
|  | 				case TokType::Spec_Explicit: | ||||||
| 				case TokType::Spec_ForceInline : | 				case TokType::Spec_ForceInline : | ||||||
| 				case TokType::Spec_Inline : | 				case TokType::Spec_Inline : | ||||||
| 				case TokType::Spec_Mutable : | 				case TokType::Spec_Mutable : | ||||||
| 				case TokType::Spec_NeverInline : | 				case TokType::Spec_NeverInline : | ||||||
| 				case TokType::Spec_Static : | 				case TokType::Spec_Static : | ||||||
| 				case TokType::Spec_Volatile : | 				case TokType::Spec_Volatile : | ||||||
|  | 				case TokType::Spec_Virtual: | ||||||
| 				{ | 				{ | ||||||
| 					SpecifierT specs_found[ 16 ] { ESpecifier::NumSpecifiers }; | 					SpecifierT specs_found[ 16 ] { ESpecifier::NumSpecifiers }; | ||||||
| 					s32        NumSpecifiers = 0; | 					s32        NumSpecifiers = 0; | ||||||
| @@ -7955,28 +7991,40 @@ namespace parser | |||||||
| 					{ | 					{ | ||||||
| 						SpecifierT spec = ESpecifier::to_type( currtok ); | 						SpecifierT spec = ESpecifier::to_type( currtok ); | ||||||
|  |  | ||||||
|  | 						b32 ignore_spec = false; | ||||||
|  |  | ||||||
| 						switch ( spec ) | 						switch ( spec ) | ||||||
| 						{ | 						{ | ||||||
| 							case ESpecifier::Constexpr : | 							case ESpecifier::Constexpr : | ||||||
| 							case ESpecifier::Constinit : | 							case ESpecifier::Constinit : | ||||||
|  | 							case ESpecifier::Explicit: | ||||||
| 							case ESpecifier::Inline : | 							case ESpecifier::Inline : | ||||||
| 							case ESpecifier::ForceInline : | 							case ESpecifier::ForceInline : | ||||||
| 							case ESpecifier::Mutable : | 							case ESpecifier::Mutable : | ||||||
| 							case ESpecifier::NeverInline : | 							case ESpecifier::NeverInline : | ||||||
| 							case ESpecifier::Static : | 							case ESpecifier::Static : | ||||||
| 							case ESpecifier::Volatile : | 							case ESpecifier::Volatile : | ||||||
|  | 							case ESpecifier::Virtual: | ||||||
| 								break; | 								break; | ||||||
|  |  | ||||||
| 							case ESpecifier::Consteval : | 							case ESpecifier::Consteval : | ||||||
| 								expects_function = true; | 								expects_function = true; | ||||||
| 								break; | 								break; | ||||||
|  |  | ||||||
|  | 							case ESpecifier::Const : | ||||||
|  | 								ignore_spec = true; | ||||||
|  | 								break; | ||||||
|  |  | ||||||
| 							default : | 							default : | ||||||
| 								log_failure( "Invalid specifier %s for variable\n%s", ESpecifier::to_str( spec ), Context.to_string() ); | 								log_failure( "Invalid specifier %s for variable\n%s", ESpecifier::to_str( spec ), Context.to_string() ); | ||||||
| 								Context.pop(); | 								Context.pop(); | ||||||
| 								return CodeInvalid; | 								return CodeInvalid; | ||||||
| 						} | 						} | ||||||
|  |  | ||||||
|  | 						// Every specifier after would be considered part of the type type signature | ||||||
|  | 						if (ignore_spec) | ||||||
|  | 							break; | ||||||
|  |  | ||||||
| 						specs_found[ NumSpecifiers ] = spec; | 						specs_found[ NumSpecifiers ] = spec; | ||||||
| 						NumSpecifiers++; | 						NumSpecifiers++; | ||||||
| 						eat( currtok.Type ); | 						eat( currtok.Type ); | ||||||
| @@ -8018,7 +8066,7 @@ namespace parser | |||||||
| 					{ | 					{ | ||||||
| 						if ( str_compare( name.Text, currtok.Text, name.Length ) == 0 ) | 						if ( str_compare( name.Text, currtok.Text, name.Length ) == 0 ) | ||||||
| 						{ | 						{ | ||||||
| 							member = parse_constructor(); | 							member = parse_constructor( specifiers ); | ||||||
| 							// <Attributes> <Specifiers> <Name>() | 							// <Attributes> <Specifiers> <Name>() | ||||||
| 							break; | 							break; | ||||||
| 						} | 						} | ||||||
| @@ -8109,12 +8157,38 @@ namespace parser | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		Token tok = tokens[ idx - 1 ]; | 		Token tok = tokens[ idx - 1 ]; | ||||||
|  | 		if ( tok.is_specifier() && is_trailing( ESpecifier::to_type(tok)) ) | ||||||
|  | 		{ | ||||||
|  | 			// <which> <type_identifier>(...) <specifier> ...; | ||||||
|  |  | ||||||
|  | 			s32   spec_idx = idx - 1; | ||||||
|  | 			Token spec     = tokens[spec_idx]; | ||||||
|  | 			while ( spec.is_specifier() && is_trailing( ESpecifier::to_type(spec)) ) | ||||||
|  | 			{ | ||||||
|  | 				-- spec_idx; | ||||||
|  | 				spec = tokens[spec_idx]; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if ( tokens[spec_idx].Type == TokType::Capture_End ) | ||||||
|  | 			{ | ||||||
|  | 				// Forward declaration with trailing specifiers for a procedure | ||||||
|  | 				tok = tokens[spec_idx]; | ||||||
|  |  | ||||||
|  | 				Code result = parse_operator_function_or_variable( false, { nullptr }, { nullptr } ); | ||||||
|  | 				// <Attributes> <Specifiers> <ReturnType/ValueType> <operator <Op>, or Name> ... | ||||||
|  | 				Context.pop(); | ||||||
|  | 				return result; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			log_failure( "Unsupported or bad member definition after %s declaration\n%s", to_str(which), Context.to_string() ); | ||||||
|  | 			Context.pop(); | ||||||
|  | 			return CodeInvalid; | ||||||
|  | 		} | ||||||
| 		if ( tok.Type == TokType::Identifier ) | 		if ( tok.Type == TokType::Identifier ) | ||||||
| 		{ | 		{ | ||||||
| 			tok                 = tokens[ idx - 2 ]; | 			tok                 = tokens[ idx - 2 ]; | ||||||
|  |  | ||||||
| 			bool is_indirection = tok.Type == TokType::Ampersand || tok.Type == TokType::Star; | 			bool is_indirection = tok.Type == TokType::Ampersand || tok.Type == TokType::Star; | ||||||
|  |  | ||||||
| 			bool ok_to_parse    = false; | 			bool ok_to_parse    = false; | ||||||
|  |  | ||||||
| 			if ( tok.Type == TokType::BraceCurly_Close ) | 			if ( tok.Type == TokType::BraceCurly_Close ) | ||||||
| @@ -8130,6 +8204,17 @@ namespace parser | |||||||
| 				// <which> <type_identifier> <identifier>; | 				// <which> <type_identifier> <identifier>; | ||||||
| 				ok_to_parse = true; | 				ok_to_parse = true; | ||||||
| 			} | 			} | ||||||
|  | 			else if ( tok.Type == TokType::Assign_Classifer | ||||||
|  | 				&& tokens[idx - 4].Type == TokType::Decl_Class | ||||||
|  | 				&& tokens[idx - 5].Type == which ) | ||||||
|  | 			{ | ||||||
|  | 				// Its a forward declaration of an enum class | ||||||
|  | 				// <enum> <class> <type_identifier> : <identifier>; | ||||||
|  | 				ok_to_parse = true; | ||||||
|  | 				Code result = parse_enum(); | ||||||
|  | 				Context.pop(); | ||||||
|  | 				return result; | ||||||
|  | 			} | ||||||
| 			else if ( is_indirection ) | 			else if ( is_indirection ) | ||||||
| 			{ | 			{ | ||||||
| 				// Its a indirection type with type ID using struct namespace. | 				// Its a indirection type with type ID using struct namespace. | ||||||
| @@ -8139,7 +8224,7 @@ namespace parser | |||||||
|  |  | ||||||
| 			if ( ! ok_to_parse ) | 			if ( ! ok_to_parse ) | ||||||
| 			{ | 			{ | ||||||
| 				log_failure( "Unsupported or bad member definition after struct declaration\n%s", Context.to_string() ); | 				log_failure( "Unsupported or bad member definition after %s declaration\n%s", to_str(which), Context.to_string() ); | ||||||
| 				Context.pop(); | 				Context.pop(); | ||||||
| 				return CodeInvalid; | 				return CodeInvalid; | ||||||
| 			} | 			} | ||||||
| @@ -8167,7 +8252,7 @@ namespace parser | |||||||
| 		} | 		} | ||||||
| 		else | 		else | ||||||
| 		{ | 		{ | ||||||
| 			log_failure( "Unsupported or bad member definition after struct declaration\n%s", Context.to_string() ); | 			log_failure( "Unsupported or bad member definition after %s declaration\n%S", to_str(which).Ptr, Context.to_string() ); | ||||||
| 			Context.pop(); | 			Context.pop(); | ||||||
| 			return CodeInvalid; | 			return CodeInvalid; | ||||||
| 		} | 		} | ||||||
| @@ -8219,6 +8304,40 @@ namespace parser | |||||||
| 		return define; | 		return define; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	internal inline | ||||||
|  | 	Code parse_assignment_expression() | ||||||
|  | 	{ | ||||||
|  | 		Code expr = { nullptr }; | ||||||
|  |  | ||||||
|  | 		eat( TokType::Operator ); | ||||||
|  | 		// <Attributes> <Specifiers> <ValueType> <Name> = | ||||||
|  |  | ||||||
|  | 		Token expr_tok = currtok; | ||||||
|  |  | ||||||
|  | 		if ( currtok.Type == TokType::Statement_End && currtok.Type != TokType::Comma ) | ||||||
|  | 		{ | ||||||
|  | 			log_failure( "Expected expression after assignment operator\n%s", Context.to_string() ); | ||||||
|  | 			Context.pop(); | ||||||
|  | 			return CodeInvalid; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		s32 level = 0; | ||||||
|  | 		while ( left && currtok.Type != TokType::Statement_End && (currtok.Type != TokType::Comma || level > 0) ) | ||||||
|  | 		{ | ||||||
|  | 			if (currtok.Type == TokType::Capture_Start) | ||||||
|  | 				level++; | ||||||
|  | 			else if (currtok.Type == TokType::Capture_End) | ||||||
|  | 				level--; | ||||||
|  |  | ||||||
|  | 			eat( currtok.Type ); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		expr_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )expr_tok.Text - 1; | ||||||
|  | 		expr            = untyped_str( expr_tok ); | ||||||
|  | 		// = <Expression> | ||||||
|  | 		return expr; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	internal inline Code parse_forward_or_definition( TokType which, bool is_inplace ) | 	internal inline Code parse_forward_or_definition( TokType which, bool is_inplace ) | ||||||
| 	{ | 	{ | ||||||
| 		Code result = CodeInvalid; | 		Code result = CodeInvalid; | ||||||
| @@ -8416,6 +8535,13 @@ namespace parser | |||||||
|  |  | ||||||
| 			switch ( currtok_noskip.Type ) | 			switch ( currtok_noskip.Type ) | ||||||
| 			{ | 			{ | ||||||
|  | 				case TokType::Statement_End: | ||||||
|  | 				{ | ||||||
|  | 					// TODO(Ed): Convert this to a general warning procedure | ||||||
|  | 					log_fmt("Dangling end statement found %S\n", currtok_noskip.to_string()); | ||||||
|  | 					eat( TokType::Statement_End ); | ||||||
|  | 					continue; | ||||||
|  | 				} | ||||||
| 				case TokType::NewLine : | 				case TokType::NewLine : | ||||||
| 					// Empty lines are auto skipped by Tokens.current() | 					// Empty lines are auto skipped by Tokens.current() | ||||||
| 					member = fmt_newline; | 					member = fmt_newline; | ||||||
| @@ -8625,7 +8751,7 @@ namespace parser | |||||||
| 				case TokType::Type_double : | 				case TokType::Type_double : | ||||||
| 				case TokType::Type_int : | 				case TokType::Type_int : | ||||||
| 				{ | 				{ | ||||||
| 					bool found_operator_cast = false; | 					bool found_operator_cast_outside_class_implmentation = false; | ||||||
| 					s32  idx                 = Context.Tokens.Idx; | 					s32  idx                 = Context.Tokens.Idx; | ||||||
|  |  | ||||||
| 					for ( ; idx < Context.Tokens.Arr.num(); idx++ ) | 					for ( ; idx < Context.Tokens.Arr.num(); idx++ ) | ||||||
| @@ -8643,12 +8769,12 @@ namespace parser | |||||||
| 						} | 						} | ||||||
|  |  | ||||||
| 						if ( tok.Type == TokType::Decl_Operator ) | 						if ( tok.Type == TokType::Decl_Operator ) | ||||||
| 							found_operator_cast = true; | 							found_operator_cast_outside_class_implmentation = true; | ||||||
|  |  | ||||||
| 						break; | 						break; | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
| 					if ( found_operator_cast ) | 					if ( found_operator_cast_outside_class_implmentation ) | ||||||
| 					{ | 					{ | ||||||
| 						member = parse_operator_cast(); | 						member = parse_operator_cast(); | ||||||
| 						// <Attributes> <Specifiers> <Name>::operator <Type>() { ... } | 						// <Attributes> <Specifiers> <Name>::operator <Type>() { ... } | ||||||
| @@ -9197,8 +9323,10 @@ namespace parser | |||||||
| 			return { nullptr }; | 			return { nullptr }; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		Code     macro = { nullptr }; | ||||||
| 		CodeType type  = { nullptr }; | 		CodeType type  = { nullptr }; | ||||||
| 		Code     value = { nullptr }; | 		Code     value = { nullptr }; | ||||||
|  | 		Token    name  = NullToken; | ||||||
|  |  | ||||||
| 		if ( check( TokType::Varadic_Argument ) ) | 		if ( check( TokType::Varadic_Argument ) ) | ||||||
| 		{ | 		{ | ||||||
| @@ -9211,50 +9339,61 @@ namespace parser | |||||||
| 			// or < ... > | 			// or < ... > | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		type = parse_type(); | 		// Ex: Unreal has this type of macro:                 vvvvvvvv | ||||||
| 		if ( type == Code::Invalid ) | 		// COREUOBJECT_API void CallFunction( FFrame& Stack, RESULT_DECL, UFunction* Function ); | ||||||
|  | 		// This is so that a 'ValueType for the param is a preprocesor macro to support this nasty macro usage' | ||||||
|  |  | ||||||
|  | 		if ( ! check(TokType::Preprocess_Macro)) | ||||||
| 		{ | 		{ | ||||||
| 			Context.pop(); | 			type = parse_type(); | ||||||
| 			return CodeInvalid; | 			if ( type == Code::Invalid ) | ||||||
| 		} |  | ||||||
| 		// ( <ValueType> |  | ||||||
|  |  | ||||||
| 		Token name = NullToken; |  | ||||||
|  |  | ||||||
| 		if ( check( TokType::Identifier ) ) |  | ||||||
| 		{ |  | ||||||
| 			name = currtok; |  | ||||||
| 			eat( TokType::Identifier ); |  | ||||||
| 			// ( <ValueType> <Name> |  | ||||||
|  |  | ||||||
| 			if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) |  | ||||||
| 			{ | 			{ | ||||||
| 				eat( TokType::Operator ); | 				Context.pop(); | ||||||
| 				// ( <ValueType> <Name> = | 				return CodeInvalid; | ||||||
|  |  | ||||||
| 				Token value_tok = currtok; |  | ||||||
|  |  | ||||||
| 				if ( currtok.Type == TokType::Comma ) |  | ||||||
| 				{ |  | ||||||
| 					log_failure( "Expected value after assignment operator\n%s.", Context.to_string() ); |  | ||||||
| 					Context.pop(); |  | ||||||
| 					return CodeInvalid; |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				while ( left && currtok.Type != TokType::Comma && currtok.Type != TokType::Capture_End ) |  | ||||||
| 				{ |  | ||||||
| 					value_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )value_tok.Text; |  | ||||||
| 					eat( currtok.Type ); |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				value = untyped_str( strip_formatting( value_tok, strip_formatting_dont_preserve_newlines ) ); |  | ||||||
| 				// ( <ValueType> <Name> = <Expression> |  | ||||||
| 			} | 			} | ||||||
|  | 			// ( <ValueType> | ||||||
|  |  | ||||||
|  | 			if ( check( TokType::Identifier ) ) | ||||||
|  | 			{ | ||||||
|  | 				name = currtok; | ||||||
|  | 				eat( TokType::Identifier ); | ||||||
|  | 				// ( <ValueType> <Name> | ||||||
|  |  | ||||||
|  | 				if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) | ||||||
|  | 				{ | ||||||
|  | 					eat( TokType::Operator ); | ||||||
|  | 					// ( <ValueType> <Name> = | ||||||
|  |  | ||||||
|  | 					Token value_tok = currtok; | ||||||
|  |  | ||||||
|  | 					if ( currtok.Type == TokType::Comma ) | ||||||
|  | 					{ | ||||||
|  | 						log_failure( "Expected value after assignment operator\n%s.", Context.to_string() ); | ||||||
|  | 						Context.pop(); | ||||||
|  | 						return CodeInvalid; | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					while ( left && currtok.Type != TokType::Comma && currtok.Type != TokType::Capture_End ) | ||||||
|  | 					{ | ||||||
|  | 						value_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )value_tok.Text; | ||||||
|  | 						eat( currtok.Type ); | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					value = untyped_str( strip_formatting( value_tok, strip_formatting_dont_preserve_newlines ) ); | ||||||
|  | 					// ( <ValueType> <Name> = <Expression> | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		else { | ||||||
|  | 			macro = parse_simple_preprocess(ETokType::Preprocess_Macro); | ||||||
|  | 			// ( <Macro> | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		CodeParam result = ( CodeParam )make_code(); | 		CodeParam result = ( CodeParam )make_code(); | ||||||
| 		result->Type     = Parameters; | 		result->Type     = Parameters; | ||||||
|  |  | ||||||
|  | 		result->Macro = macro; | ||||||
|  |  | ||||||
| 		if ( name.Length > 0 ) | 		if ( name.Length > 0 ) | ||||||
| 			result->Name = get_cached_string( name ); | 			result->Name = get_cached_string( name ); | ||||||
|  |  | ||||||
| @@ -9281,51 +9420,60 @@ namespace parser | |||||||
| 				// ( <ValueType> <Name> = <Expression>, ... | 				// ( <ValueType> <Name> = <Expression>, ... | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			type = parse_type(); | 			if ( ! check(TokType::Preprocess_Macro)) | ||||||
| 			if ( type == Code::Invalid ) |  | ||||||
| 			{ | 			{ | ||||||
| 				Context.pop(); | 				type = parse_type(); | ||||||
| 				return CodeInvalid; | 				if ( type == Code::Invalid ) | ||||||
| 			} |  | ||||||
| 			// ( <ValueType> <Name> = <Expression>, <ValueType> |  | ||||||
|  |  | ||||||
| 			name = { nullptr, 0, TokType::Invalid, false }; |  | ||||||
|  |  | ||||||
| 			if ( check( TokType::Identifier ) ) |  | ||||||
| 			{ |  | ||||||
| 				name = currtok; |  | ||||||
| 				eat( TokType::Identifier ); |  | ||||||
| 				// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> |  | ||||||
|  |  | ||||||
| 				if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) |  | ||||||
| 				{ | 				{ | ||||||
| 					eat( TokType::Operator ); | 					Context.pop(); | ||||||
| 					// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = | 					return CodeInvalid; | ||||||
|  |  | ||||||
| 					Token value_tok = currtok; |  | ||||||
|  |  | ||||||
| 					if ( currtok.Type == TokType::Comma ) |  | ||||||
| 					{ |  | ||||||
| 						log_failure( "Expected value after assignment operator\n%s", Context.to_string() ); |  | ||||||
| 						Context.pop(); |  | ||||||
| 						return CodeInvalid; |  | ||||||
| 					} |  | ||||||
|  |  | ||||||
| 					while ( left && currtok.Type != TokType::Comma && currtok.Type != TokType::Capture_End ) |  | ||||||
| 					{ |  | ||||||
| 						value_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )value_tok.Text; |  | ||||||
| 						eat( currtok.Type ); |  | ||||||
| 					} |  | ||||||
|  |  | ||||||
| 					value = untyped_str( strip_formatting( value_tok, strip_formatting_dont_preserve_newlines ) ); |  | ||||||
| 					// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = <Expression> |  | ||||||
| 				} | 				} | ||||||
|  | 				// ( <ValueType> <Name> = <Expression>, <ValueType> | ||||||
|  |  | ||||||
|  | 				name = { nullptr, 0, TokType::Invalid, false }; | ||||||
|  |  | ||||||
|  | 				if ( check( TokType::Identifier ) ) | ||||||
|  | 				{ | ||||||
|  | 					name = currtok; | ||||||
|  | 					eat( TokType::Identifier ); | ||||||
|  | 					// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> | ||||||
|  |  | ||||||
|  | 					if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) | ||||||
|  | 					{ | ||||||
|  | 						eat( TokType::Operator ); | ||||||
|  | 						// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = | ||||||
|  |  | ||||||
|  | 						Token value_tok = currtok; | ||||||
|  |  | ||||||
|  | 						if ( currtok.Type == TokType::Comma ) | ||||||
|  | 						{ | ||||||
|  | 							log_failure( "Expected value after assignment operator\n%s", Context.to_string() ); | ||||||
|  | 							Context.pop(); | ||||||
|  | 							return CodeInvalid; | ||||||
|  | 						} | ||||||
|  |  | ||||||
|  | 						while ( left && currtok.Type != TokType::Comma && currtok.Type != TokType::Capture_End ) | ||||||
|  | 						{ | ||||||
|  | 							value_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )value_tok.Text; | ||||||
|  | 							eat( currtok.Type ); | ||||||
|  | 						} | ||||||
|  |  | ||||||
|  | 						value = untyped_str( strip_formatting( value_tok, strip_formatting_dont_preserve_newlines ) ); | ||||||
|  | 						// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = <Expression> | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = <Expression>, .. | ||||||
|  | 			} | ||||||
|  | 			else { | ||||||
|  | 				macro = parse_simple_preprocess(ETokType::Preprocess_Macro); | ||||||
|  | 				// ( ..., <Macro> | ||||||
| 			} | 			} | ||||||
| 			// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = <Expression>, .. |  | ||||||
|  |  | ||||||
| 			CodeParam param = ( CodeParam )make_code(); | 			CodeParam param = ( CodeParam )make_code(); | ||||||
| 			param->Type     = Parameters; | 			param->Type     = Parameters; | ||||||
|  |  | ||||||
|  | 			param->Macro = macro; | ||||||
|  |  | ||||||
| 			if ( name.Length > 0 ) | 			if ( name.Length > 0 ) | ||||||
| 				param->Name = get_cached_string( name ); | 				param->Name = get_cached_string( name ); | ||||||
|  |  | ||||||
| @@ -9552,26 +9700,8 @@ namespace parser | |||||||
|  |  | ||||||
| 		if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) | 		if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) | ||||||
| 		{ | 		{ | ||||||
| 			eat( TokType::Operator ); |  | ||||||
| 			// <Attributes> <Specifiers> <ValueType> <Name> = |  | ||||||
|  |  | ||||||
| 			Token expr_tok = currtok; |  | ||||||
|  |  | ||||||
| 			if ( currtok.Type == TokType::Statement_End || currtok.Type != TokType::Comma ) |  | ||||||
| 			{ |  | ||||||
| 				log_failure( "Expected expression after assignment operator\n%s", Context.to_string() ); |  | ||||||
| 				Context.pop(); |  | ||||||
| 				return CodeInvalid; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			while ( left && currtok.Type != TokType::Statement_End || currtok.Type != TokType::Comma ) |  | ||||||
| 			{ |  | ||||||
| 				eat( currtok.Type ); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			expr_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )expr_tok.Text - 1; |  | ||||||
| 			expr            = untyped_str( expr_tok ); |  | ||||||
| 			// <Attributes> <Specifiers> <ValueType> <Name> = <Expression> | 			// <Attributes> <Specifiers> <ValueType> <Name> = <Expression> | ||||||
|  | 			expr = parse_assignment_expression(); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if ( currtok.Type == TokType::BraceCurly_Open ) | 		if ( currtok.Type == TokType::BraceCurly_Open ) | ||||||
| @@ -9789,7 +9919,7 @@ namespace parser | |||||||
| 		return result; | 		return result; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	internal CodeConstructor parse_constructor() | 	internal CodeConstructor parse_constructor( CodeSpecifiers specifiers ) | ||||||
| 	{ | 	{ | ||||||
| 		push_scope(); | 		push_scope(); | ||||||
|  |  | ||||||
| @@ -9808,14 +9938,14 @@ namespace parser | |||||||
| 			eat( TokType::Assign_Classifer ); | 			eat( TokType::Assign_Classifer ); | ||||||
| 			// <Name> ( <Parameters> ) : | 			// <Name> ( <Parameters> ) : | ||||||
|  |  | ||||||
| 			Token initializer_list_tok = NullToken; | 			Token initializer_list_tok = currtok; | ||||||
|  |  | ||||||
| 			s32 level                  = 0; | 			s32 level                  = 0; | ||||||
| 			while ( left && ( currtok.Type != TokType::BraceCurly_Open || level > 0 ) ) | 			while ( left && ( currtok.Type != TokType::BraceCurly_Open || level > 0 ) ) | ||||||
| 			{ | 			{ | ||||||
| 				if ( currtok.Type == TokType::BraceCurly_Open ) | 				if (currtok.Type == TokType::Capture_Start) | ||||||
| 					level++; | 					level++; | ||||||
| 				else if ( currtok.Type == TokType::BraceCurly_Close ) | 				else if ( currtok.Type == TokType::Capture_End ) | ||||||
| 					level--; | 					level--; | ||||||
|  |  | ||||||
| 				eat( currtok.Type ); | 				eat( currtok.Type ); | ||||||
| @@ -9833,6 +9963,10 @@ namespace parser | |||||||
| 			body = parse_function_body(); | 			body = parse_function_body(); | ||||||
| 			// <Name> ( <Parameters> ) { <Body> } | 			// <Name> ( <Parameters> ) { <Body> } | ||||||
| 		} | 		} | ||||||
|  | 		else if ( check( TokType::Operator) && currtok.Text[ 0 ] == '=' ) | ||||||
|  | 		{ | ||||||
|  | 			body = parse_assignment_expression(); | ||||||
|  | 		} | ||||||
| 		else | 		else | ||||||
| 		{ | 		{ | ||||||
| 			Token stmt_end = currtok; | 			Token stmt_end = currtok; | ||||||
| @@ -9844,15 +9978,21 @@ namespace parser | |||||||
| 			// <Name> ( <Parameters> ); <InlineCmt> | 			// <Name> ( <Parameters> ); <InlineCmt> | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		// TODO(Ed): Constructors can have post-fix specifiers | ||||||
|  |  | ||||||
| 		CodeConstructor result = ( CodeConstructor )make_code(); | 		CodeConstructor result = ( CodeConstructor )make_code(); | ||||||
|  |  | ||||||
|  | 		result->Name = get_cached_string(identifier); | ||||||
|  |  | ||||||
|  | 		result->Specs = specifiers; | ||||||
|  |  | ||||||
| 		if ( params ) | 		if ( params ) | ||||||
| 			result->Params = params; | 			result->Params = params; | ||||||
|  |  | ||||||
| 		if ( initializer_list ) | 		if ( initializer_list ) | ||||||
| 			result->InitializerList = initializer_list; | 			result->InitializerList = initializer_list; | ||||||
|  |  | ||||||
| 		if ( body ) | 		if ( body && body->Type == ECode::Function_Body ) | ||||||
| 		{ | 		{ | ||||||
| 			result->Body = body; | 			result->Body = body; | ||||||
| 			result->Type = ECode::Constructor; | 			result->Type = ECode::Constructor; | ||||||
| @@ -10660,54 +10800,58 @@ namespace parser | |||||||
| 			s32        NumSpecifiers = 0; | 			s32        NumSpecifiers = 0; | ||||||
|  |  | ||||||
| 			attributes               = parse_attributes(); | 			attributes               = parse_attributes(); | ||||||
| 			// <export> template< <Parameters> > <Attributes> | 			// <export> template< <Parameters> > <Attributes> <specifiers. | ||||||
|  |  | ||||||
| 			while ( left && currtok.is_specifier() ) | 			// Specifiers Parsing | ||||||
| 			{ | 			{ | ||||||
| 				SpecifierT spec = ESpecifier::to_type( currtok ); | 				while ( left && currtok.is_specifier() ) | ||||||
|  |  | ||||||
| 				switch ( spec ) |  | ||||||
| 				{ | 				{ | ||||||
| 					case ESpecifier::Const : | 					SpecifierT spec = ESpecifier::to_type( currtok ); | ||||||
| 					case ESpecifier::Constexpr : |  | ||||||
| 					case ESpecifier::Constinit : | 					switch ( spec ) | ||||||
| 					case ESpecifier::External_Linkage : | 					{ | ||||||
| 					case ESpecifier::Global : | 						case ESpecifier::Const : | ||||||
| 					case ESpecifier::Inline : | 						case ESpecifier::Constexpr : | ||||||
| 					case ESpecifier::ForceInline : | 						case ESpecifier::Constinit : | ||||||
| 					case ESpecifier::Local_Persist : | 						case ESpecifier::External_Linkage : | ||||||
| 					case ESpecifier::Mutable : | 						case ESpecifier::Global : | ||||||
| 					case ESpecifier::Static : | 						case ESpecifier::Inline : | ||||||
| 					case ESpecifier::Thread_Local : | 						case ESpecifier::ForceInline : | ||||||
| 					case ESpecifier::Volatile : | 						case ESpecifier::Local_Persist : | ||||||
|  | 						case ESpecifier::Mutable : | ||||||
|  | 						case ESpecifier::Static : | ||||||
|  | 						case ESpecifier::Thread_Local : | ||||||
|  | 						case ESpecifier::Volatile : | ||||||
|  | 							break; | ||||||
|  |  | ||||||
|  | 						case ESpecifier::Consteval : | ||||||
|  | 							expects_function = true; | ||||||
|  | 							break; | ||||||
|  |  | ||||||
|  | 						default : | ||||||
|  | 							log_failure( "Invalid specifier %s for variable or function\n%s", ESpecifier::to_str( spec ), Context.to_string() ); | ||||||
|  | 							Context.pop(); | ||||||
|  | 							return CodeInvalid; | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					// Ignore const it will be handled by the type | ||||||
|  | 					if ( spec == ESpecifier::Const ) | ||||||
| 						break; | 						break; | ||||||
|  |  | ||||||
| 					case ESpecifier::Consteval : | 					specs_found[ NumSpecifiers ] = spec; | ||||||
| 						expects_function = true; | 					NumSpecifiers++; | ||||||
| 						break; | 					eat( currtok.Type ); | ||||||
|  |  | ||||||
| 					default : |  | ||||||
| 						log_failure( "Invalid specifier %s for variable or function\n%s", ESpecifier::to_str( spec ), Context.to_string() ); |  | ||||||
| 						Context.pop(); |  | ||||||
| 						return CodeInvalid; |  | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				// Ignore const it will be handled by the type | 				if ( NumSpecifiers ) | ||||||
| 				if ( spec == ESpecifier::Const ) | 				{ | ||||||
| 					continue; | 					specifiers = def_specifiers( NumSpecifiers, specs_found ); | ||||||
|  | 				} | ||||||
| 				specs_found[ NumSpecifiers ] = spec; | 				// <export> template< <Parameters> > <Attributes> <Specifiers> | ||||||
| 				NumSpecifiers++; |  | ||||||
| 				eat( currtok.Type ); |  | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if ( NumSpecifiers ) | 			// TODO(Ed) : Port over user-defined operator cast detection from | ||||||
| 			{ | 			//parse_global_nspace (which is a source defintion version) or parse_class_struct_body (which is an inline class definition) | ||||||
| 				specifiers = def_specifiers( NumSpecifiers, specs_found ); |  | ||||||
| 			} |  | ||||||
| 			// <export> template< <Parameters> > <Attributes> <Specifiers> |  | ||||||
|  |  | ||||||
| 			// TODO(Ed) : Port over operator cast detection from parse_global_nspace or parse_class_struct_body |  | ||||||
|  |  | ||||||
| 			definition = parse_operator_function_or_variable( expects_function, attributes, specifiers ); | 			definition = parse_operator_function_or_variable( expects_function, attributes, specifiers ); | ||||||
| 			// <export> template< <Parameters> > <Attributes> <Specifiers> ... | 			// <export> template< <Parameters> > <Attributes> <Specifiers> ... | ||||||
| @@ -10781,12 +10925,13 @@ namespace parser | |||||||
| 		if ( currtok.Type == TokType::Decl_Class || currtok.Type == TokType::Decl_Enum || currtok.Type == TokType::Decl_Struct | 		if ( currtok.Type == TokType::Decl_Class || currtok.Type == TokType::Decl_Enum || currtok.Type == TokType::Decl_Struct | ||||||
| 		     || currtok.Type == TokType::Decl_Union ) | 		     || currtok.Type == TokType::Decl_Union ) | ||||||
| 		{ | 		{ | ||||||
| 			name = currtok; |  | ||||||
| 			eat( currtok.Type ); | 			eat( currtok.Type ); | ||||||
| 			// <Attributes> <Specifiers> <class, enum, struct, union> | 			// <Attributes> <Specifiers> <class, enum, struct, union> | ||||||
|  |  | ||||||
| 			name.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )name.Text; | 			name = parse_identifier(); | ||||||
| 			eat( TokType::Identifier ); |  | ||||||
|  | 			// name.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )name.Text; | ||||||
|  | 			// eat( TokType::Identifier ); | ||||||
| 			Context.Scope->Name = name; | 			Context.Scope->Name = name; | ||||||
| 			// <Attributes> <Specifiers> <class, enum, struct, union> <Name> | 			// <Attributes> <Specifiers> <class, enum, struct, union> <Name> | ||||||
| 		} | 		} | ||||||
| @@ -10835,6 +10980,12 @@ namespace parser | |||||||
| 			name.Length = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )name.Text; | 			name.Length = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )name.Text; | ||||||
| 			// <Attributes> <Specifiers> <Compound type expression> | 			// <Attributes> <Specifiers> <Compound type expression> | ||||||
| 		} | 		} | ||||||
|  | 		else if ( currtok.Type == TokType::Type_Typename ) | ||||||
|  | 		{ | ||||||
|  | 			name = currtok; | ||||||
|  | 			eat(TokType::Type_Typename); | ||||||
|  | 			// <typename> | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		// The usual Identifier type signature that may have namespace qualifiers | 		// The usual Identifier type signature that may have namespace qualifiers | ||||||
| 		else | 		else | ||||||
| @@ -11685,8 +11836,54 @@ CodeConstructor parse_constructor( StrC def ) | |||||||
| 	if ( toks.Arr == nullptr ) | 	if ( toks.Arr == nullptr ) | ||||||
| 		return CodeInvalid; | 		return CodeInvalid; | ||||||
|  |  | ||||||
|  | 	// TODO(Ed): Constructors can have prefix attributes | ||||||
|  |  | ||||||
|  | 	CodeSpecifiers specifiers; | ||||||
|  | 	SpecifierT     specs_found[ 16 ] { ESpecifier::NumSpecifiers }; | ||||||
|  | 	s32            NumSpecifiers = 0; | ||||||
|  |  | ||||||
|  | 	while ( left && currtok.is_specifier() ) | ||||||
|  | 	{ | ||||||
|  | 		SpecifierT spec = ESpecifier::to_type( currtok ); | ||||||
|  |  | ||||||
|  | 		b32 ignore_spec = false; | ||||||
|  |  | ||||||
|  | 		switch ( spec ) | ||||||
|  | 		{ | ||||||
|  | 			case ESpecifier::Constexpr : | ||||||
|  | 			case ESpecifier::Explicit: | ||||||
|  | 			case ESpecifier::Inline : | ||||||
|  | 			case ESpecifier::ForceInline : | ||||||
|  | 			case ESpecifier::NeverInline : | ||||||
|  | 				break; | ||||||
|  |  | ||||||
|  | 			case ESpecifier::Const : | ||||||
|  | 				ignore_spec = true; | ||||||
|  | 				break; | ||||||
|  |  | ||||||
|  | 			default : | ||||||
|  | 				log_failure( "Invalid specifier %s for variable\n%s", ESpecifier::to_str( spec ), Context.to_string() ); | ||||||
|  | 				Context.pop(); | ||||||
|  | 				return CodeInvalid; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Every specifier after would be considered part of the type type signature | ||||||
|  | 		if (ignore_spec) | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		specs_found[ NumSpecifiers ] = spec; | ||||||
|  | 		NumSpecifiers++; | ||||||
|  | 		eat( currtok.Type ); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if ( NumSpecifiers ) | ||||||
|  | 	{ | ||||||
|  | 		specifiers = def_specifiers( NumSpecifiers, specs_found ); | ||||||
|  | 		// <specifiers> ... | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	Context.Tokens         = toks; | 	Context.Tokens         = toks; | ||||||
| 	CodeConstructor result = parse_constructor(); | 	CodeConstructor result = parse_constructor( specifiers ); | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -11699,6 +11896,9 @@ CodeDestructor parse_destructor( StrC def ) | |||||||
| 	if ( toks.Arr == nullptr ) | 	if ( toks.Arr == nullptr ) | ||||||
| 		return CodeInvalid; | 		return CodeInvalid; | ||||||
|  |  | ||||||
|  | 	// TODO(Ed): Destructors can have prefix attributes | ||||||
|  | 	// TODO(Ed): Destructors can have virtual | ||||||
|  |  | ||||||
| 	Context.Tokens        = toks; | 	Context.Tokens        = toks; | ||||||
| 	CodeDestructor result = parse_destructor(); | 	CodeDestructor result = parse_destructor(); | ||||||
| 	return result; | 	return result; | ||||||
|   | |||||||
| @@ -70,9 +70,9 @@ using LogFailType = sw ( * )( char const*, ... ); | |||||||
| enum class AccessSpec : u32 | enum class AccessSpec : u32 | ||||||
| { | { | ||||||
| 	Default, | 	Default, | ||||||
| 	Public, |  | ||||||
| 	Protected, |  | ||||||
| 	Private, | 	Private, | ||||||
|  | 	Protected, | ||||||
|  | 	Public, | ||||||
|  |  | ||||||
| 	Num_AccessSpec, | 	Num_AccessSpec, | ||||||
| 	Invalid, | 	Invalid, | ||||||
| @@ -82,9 +82,9 @@ inline char const* to_str( AccessSpec type ) | |||||||
| { | { | ||||||
| 	local_persist char const* lookup[ ( u32 )AccessSpec::Num_AccessSpec ] = { | 	local_persist char const* lookup[ ( u32 )AccessSpec::Num_AccessSpec ] = { | ||||||
| 		"", | 		"", | ||||||
| 		"public", |  | ||||||
| 		"protected", |  | ||||||
| 		"private", | 		"private", | ||||||
|  | 		"protected", | ||||||
|  | 		"public", | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	if ( type > AccessSpec::Public ) | 	if ( type > AccessSpec::Public ) | ||||||
| @@ -814,6 +814,7 @@ struct AST | |||||||
|  |  | ||||||
| 			union | 			union | ||||||
| 			{ | 			{ | ||||||
|  | 				AST* Macro;           // Parameter | ||||||
| 				AST* BitfieldSize;    // Variable (Class/Struct Data Member) | 				AST* BitfieldSize;    // Variable (Class/Struct Data Member) | ||||||
| 				AST* Params;          // Constructor, Function, Operator, Template, Typename | 				AST* Params;          // Constructor, Function, Operator, Template, Typename | ||||||
| 			}; | 			}; | ||||||
| @@ -2014,7 +2015,7 @@ struct AST_Constructor | |||||||
| 	Code           Next; | 	Code           Next; | ||||||
| 	parser::Token* Tok; | 	parser::Token* Tok; | ||||||
| 	Code           Parent; | 	Code           Parent; | ||||||
| 	char           _PAD_NAME_[ sizeof( StringCached ) ]; | 	StringCached   Name; | ||||||
| 	CodeT          Type; | 	CodeT          Type; | ||||||
| 	char           _PAD_UNUSED_[ sizeof( ModuleFlag ) + sizeof( u32 ) ]; | 	char           _PAD_UNUSED_[ sizeof( ModuleFlag ) + sizeof( u32 ) ]; | ||||||
| }; | }; | ||||||
| @@ -2616,11 +2617,11 @@ struct AST_Param | |||||||
|  |  | ||||||
| 		struct | 		struct | ||||||
| 		{ | 		{ | ||||||
| 			char     _PAD_PROPERTIES_2_[ sizeof( AST* ) * 3 ]; | 			char     _PAD_PROPERTIES_1_[ sizeof( AST* ) * 3 ]; | ||||||
| 			CodeType ValueType; | 			CodeType ValueType; | ||||||
| 			char     _PAD_PROPERTIES_[ sizeof( AST* ) ]; | 			Code     Macro; | ||||||
| 			Code     Value; | 			Code     Value; | ||||||
| 			char     _PAD_PROPERTIES_3_[ sizeof( AST* ) ]; | 			char     _PAD_PROPERTIES_2_[ sizeof( AST* ) ]; | ||||||
| 		}; | 		}; | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ Implementation design perfs: | |||||||
|   * Lift to more specific or generalized code-pathsonly when necessary |   * Lift to more specific or generalized code-pathsonly when necessary | ||||||
|   * Minimize distinct code-paths |   * Minimize distinct code-paths | ||||||
|   * Use classes as "filters", keep things "mega-structed" |   * Use classes as "filters", keep things "mega-structed" | ||||||
|  |     * Use composition for large sets of entiites (when possible). | ||||||
|   * Never pre-emtively make interfaces or interface-like patterns |   * Never pre-emtively make interfaces or interface-like patterns | ||||||
| * Keep everything data-wise in the runtime unless there is a measurable performance cost. | * Keep everything data-wise in the runtime unless there is a measurable performance cost. | ||||||
| * Some exploratory optimizations for educational purposes. | * Some exploratory optimizations for educational purposes. | ||||||
|   | |||||||
| @@ -105,7 +105,7 @@ function run-gengasa | |||||||
|  |  | ||||||
| 	$path_AbilitySystem = join-path $path_gasa 'AbilitySystem' | 	$path_AbilitySystem = join-path $path_gasa 'AbilitySystem' | ||||||
| 	$include  = @( | 	$include  = @( | ||||||
| 		'GasaAttributeSet.h', 'GasaAttributeSet.cpp' | 		'GasaAttributeSet.h', 'GasaAttributeSet.cpp', 'LETS_SEE.h' | ||||||
| 	) | 	) | ||||||
| 	format-cpp $path_AbilitySystem $include $null | 	format-cpp $path_AbilitySystem $include $null | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user