Was able to parse UObject with gencpp!!!
This commit is contained in:
		| @@ -21,7 +21,6 @@ public: | ||||
| 	FGameplayAttributeData MaxMana; | ||||
|  | ||||
| 	UGasaAttributeSet(); | ||||
|  | ||||
| 	UFUNCTION() | ||||
| 	void Client_OnRep_Health( FGameplayAttributeData& PrevHealth ); | ||||
| 	UFUNCTION() | ||||
|   | ||||
| @@ -3,7 +3,6 @@ | ||||
| #include "Camera/CameraComponent.h" | ||||
| #include "GameFramework/SpringArmComponent.h" | ||||
|  | ||||
|  | ||||
| ACameraMount::ACameraMount() | ||||
| { | ||||
| 	PrimaryActorTick.bCanEverTick = true; | ||||
|   | ||||
| @@ -22,4 +22,3 @@ public: | ||||
| 	void PostInitializeComponents() override; | ||||
| #pragma endregion Actor | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -5,4 +5,7 @@ void UGasaGameInstance::Init() | ||||
| 	Super::Init(); | ||||
|  | ||||
| 	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 | ||||
| #include "gen.cpp" | ||||
| #include "gen.builder.cpp" | ||||
| #include "gen.scanner.hpp" | ||||
| using namespace gen; | ||||
|  | ||||
| // 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/" | ||||
| #include "GasaGenCommon.cpp" | ||||
| #include "GasaGen_UGasaAttributeSet.cpp" | ||||
|  | ||||
|  | ||||
| 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() | ||||
| { | ||||
| 	gen::init(); | ||||
| 	log_fmt("Generating code for the Gasa module"); | ||||
|  | ||||
| 	Code umeta_uclass         = code_str( UCLASS() ); | ||||
| 	Code umeta_generated_body = code_str( GENERATED_BODY() ); | ||||
| 	Code gasa_api             = code_str( GASA_API ); | ||||
| 	// Initialize Globals | ||||
| 	umeta_uclass         = code_str( UCLASS() ); | ||||
| 	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); | ||||
| 	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"))); | ||||
| 	PreprocessorDefines.append( get_cached_string(str_generated_body)); | ||||
| 	PreprocessorDefines.append( get_cached_string(str_generated_uclass_body)); | ||||
| 	PreprocessorDefines.append( get_cached_string(str_property_binding_impl)); | ||||
| 	PreprocessorDefines.append( get_cached_string(str_ue_deprecated)); | ||||
| 	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); | ||||
| 		header.print(fmt_newline); | ||||
| 		if ( gcode->Type == CodeT::Class ) | ||||
| 		{ | ||||
| 			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")); | ||||
| 			log_fmt("Class %S - Definitions:\n", gcode->Name); | ||||
|  | ||||
| 			CodeAttributes attributes = def_attributes( gasa_api->Name); | ||||
|  | ||||
| 			CodeClass GasaAttributeSet = {}; | ||||
| 			if (gcode->Body->Type != CodeT::Class_Body) | ||||
| 				continue; | ||||
| 			for ( Code class_code : gcode->Body->cast<CodeBody>() ) | ||||
| 			{ | ||||
| 				CodeBody body = def_body( CodeT::Class_Body ); | ||||
| 				switch ( class_code->Type ) | ||||
| 				{ | ||||
| 					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, 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"))); | ||||
| 					case CodeT::Variable: | ||||
| 					case CodeT::Function: | ||||
| 					case CodeT::Function_Fwd: | ||||
| 						if ( class_code->Name ) | ||||
| 						{ | ||||
| 							log_fmt("%s\n", class_code->Name ); | ||||
| 						} | ||||
| 					break; | ||||
| 				} | ||||
| 				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); | ||||
| 		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"))); | ||||
| 		if ( gcode->Type == CodeT::Class ) | ||||
| 		{ | ||||
| 			CodeBody body = def_body( CodeT::Global_Body ); | ||||
| 			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); | ||||
| 			log_fmt("Class %S - Definitions:\n", gcode->Name); | ||||
|  | ||||
| 			impl_attribute_fields( body, attributeset_name, attribute_fields); | ||||
|  | ||||
| 			CodeFn GetLifetimeOfReplicatedProps; | ||||
| 			if (gcode->Body->Type != CodeT::Class_Body) | ||||
| 				continue; | ||||
| 			for ( Code class_code : gcode->Body->cast<CodeBody>() ) | ||||
| 			{ | ||||
| 				CodeBody field_lifetimes = def_body( CodeT::Function_Body); | ||||
| 				for (StringCached field : attribute_fields) | ||||
| 				switch ( class_code->Type ) | ||||
| 				{ | ||||
| 					field_lifetimes.append( code_fmt( "field", (StrC)field, stringize( | ||||
| 						DOREPLIFETIME_DEFAULT_GAS(UGasaAttributeSet, <field>); | ||||
| 					))); | ||||
| 					case CodeT::Variable: | ||||
| 					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; | ||||
| } | ||||
|   | ||||
							
								
								
									
										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 ) | ||||
| { | ||||
| 	AST* ClassStructParent = ast->Parent->Parent; | ||||
| 	result.append( ClassStructParent->Name ); | ||||
| 	if (ClassStructParent) { | ||||
| 		result.append( ClassStructParent->Name ); | ||||
| 	} | ||||
| 	else { | ||||
| 		result.append( ast->Name ); | ||||
| 	} | ||||
|  | ||||
| 	if ( ast->Params ) | ||||
| 		result.append_fmt( "( %S )", ast->Params.to_string() ); | ||||
| @@ -1588,12 +1593,15 @@ void CodeConstructor::to_string_fwd( String& result ) | ||||
| 	if ( ast->Params ) | ||||
| 		result.append_fmt( "( %S )", ast->Params.to_string() ); | ||||
| 	else | ||||
| 	{ | ||||
| 		if ( ast->InlineCmt ) | ||||
| 			result.append_fmt( "(); // %S\n", ast->InlineCmt->Content ); | ||||
| 		else | ||||
| 			result.append( "();\n" ); | ||||
| 	} | ||||
| 		result.append_fmt("()"); | ||||
|  | ||||
| 	if (ast->Body) | ||||
| 		result.append_fmt( " = %S", ast->Body.to_string() ); | ||||
|  | ||||
| 	if ( ast->InlineCmt ) | ||||
| 		result.append_fmt( "; // %S\n", ast->InlineCmt->Content ); | ||||
| 	else | ||||
| 		result.append( ";" ); | ||||
| } | ||||
|  | ||||
| String CodeClass::to_string() | ||||
| @@ -2263,15 +2271,21 @@ String CodeParam::to_string() | ||||
|  | ||||
| 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; | ||||
| 	} | ||||
|  | ||||
| 	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 | ||||
| 		result.append_fmt( "%S", ast->ValueType.to_string() ); | ||||
|  | ||||
| @@ -5826,11 +5840,12 @@ namespace parser | ||||
| 		TF_Preprocess      = bit( 2 ), | ||||
| 		TF_Preprocess_Cond = bit( 3 ), | ||||
| 		TF_Attribute       = bit( 6 ), | ||||
| 		TF_AccessSpecifier = bit( 7 ), | ||||
| 		TF_Specifier       = bit( 8 ), | ||||
| 		TF_EndDefinition   = bit( 9 ),    // Either ; or } | ||||
| 		TF_Formatting      = bit( 10 ), | ||||
| 		TF_Literal         = bit( 11 ), | ||||
| 		TF_AccessOperator  = bit( 7 ), | ||||
| 		TF_AccessSpecifier = bit( 8 ), | ||||
| 		TF_Specifier       = bit( 9 ), | ||||
| 		TF_EndDefinition   = bit( 10 ),    // Either ; or } | ||||
| 		TF_Formatting      = bit( 11 ), | ||||
| 		TF_Literal         = bit( 12 ), | ||||
|  | ||||
| 		TF_Null            = 0, | ||||
| 	}; | ||||
| @@ -5854,6 +5869,11 @@ namespace parser | ||||
| 			return { Length, Text }; | ||||
| 		} | ||||
|  | ||||
| 		bool is_access_operator() | ||||
| 		{ | ||||
| 			return bitfield_is_equal( u32, Flags, TF_AccessOperator ); | ||||
| 		} | ||||
|  | ||||
| 		bool is_access_specifier() | ||||
| 		{ | ||||
| 			return bitfield_is_equal( u32, Flags, TF_AccessSpecifier ); | ||||
| @@ -6258,6 +6278,11 @@ namespace parser | ||||
|  | ||||
| 		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 ) | ||||
| 		{ | ||||
| 			SkipWhitespace(); | ||||
| @@ -6370,7 +6395,7 @@ namespace parser | ||||
| 				scanner++; | ||||
| 				length++; | ||||
| 			} | ||||
| 			if ( scanner[ 1 ] == '(' ) | ||||
| 			if ( scanner[ 0 ] == '(' ) | ||||
| 			{ | ||||
| 				length++; | ||||
| 			} | ||||
| @@ -6439,7 +6464,7 @@ namespace parser | ||||
| 					token.Text   = scanner; | ||||
| 					token.Length = 1; | ||||
| 					token.Type   = TokType::Access_MemberSymbol; | ||||
| 					token.Flags  = TF_AccessSpecifier; | ||||
| 					token.Flags  = TF_AccessOperator; | ||||
|  | ||||
| 					if ( left ) | ||||
| 					{ | ||||
| @@ -6805,7 +6830,7 @@ namespace parser | ||||
| 						{ | ||||
| 							token.Length++; | ||||
| 							//						token.Type = TokType::Access_PointerToMemberSymbol; | ||||
| 							token.Flags |= TF_AccessSpecifier; | ||||
| 							token.Flags |= TF_AccessOperator; | ||||
| 							move_forward(); | ||||
|  | ||||
| 							if ( current == '*' ) | ||||
| @@ -7199,6 +7224,7 @@ namespace parser | ||||
| 	internal CodeBody       parse_class_struct_body( TokType which, Token name = NullToken ); | ||||
| 	internal Code           parse_class_struct( TokType which, bool inplace_def ); | ||||
| 	internal CodeDefine     parse_define(); | ||||
| 	internal Code           parse_expression(); | ||||
| 	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 Code           parse_function_body(); | ||||
| @@ -7217,7 +7243,7 @@ namespace parser | ||||
| 	internal CodeVar            parse_variable_declaration_list(); | ||||
|  | ||||
| 	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 CodeEnum        parse_enum( bool inplace_def = false ); | ||||
| 	internal CodeBody        parse_export_body(); | ||||
| @@ -7707,6 +7733,7 @@ namespace parser | ||||
| 			{ | ||||
| 				access = currtok.to_access_specifier(); | ||||
| 				// <ModuleFlags> <class/struct> <Attributes> <Name> : <Access Specifier> | ||||
| 				eat( currtok.Type ); | ||||
| 			} | ||||
|  | ||||
| 			Token parent_tok = parse_identifier(); | ||||
| @@ -7791,6 +7818,13 @@ namespace parser | ||||
|  | ||||
| 			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 : | ||||
| 					member = fmt_newline; | ||||
| 					eat( TokType::NewLine ); | ||||
| @@ -7941,12 +7975,14 @@ namespace parser | ||||
| 				case TokType::Spec_Consteval : | ||||
| 				case TokType::Spec_Constexpr : | ||||
| 				case TokType::Spec_Constinit : | ||||
| 				case TokType::Spec_Explicit: | ||||
| 				case TokType::Spec_ForceInline : | ||||
| 				case TokType::Spec_Inline : | ||||
| 				case TokType::Spec_Mutable : | ||||
| 				case TokType::Spec_NeverInline : | ||||
| 				case TokType::Spec_Static : | ||||
| 				case TokType::Spec_Volatile : | ||||
| 				case TokType::Spec_Virtual: | ||||
| 				{ | ||||
| 					SpecifierT specs_found[ 16 ] { ESpecifier::NumSpecifiers }; | ||||
| 					s32        NumSpecifiers = 0; | ||||
| @@ -7955,28 +7991,40 @@ namespace parser | ||||
| 					{ | ||||
| 						SpecifierT spec = ESpecifier::to_type( currtok ); | ||||
|  | ||||
| 						b32 ignore_spec = false; | ||||
|  | ||||
| 						switch ( spec ) | ||||
| 						{ | ||||
| 							case ESpecifier::Constexpr : | ||||
| 							case ESpecifier::Constinit : | ||||
| 							case ESpecifier::Explicit: | ||||
| 							case ESpecifier::Inline : | ||||
| 							case ESpecifier::ForceInline : | ||||
| 							case ESpecifier::Mutable : | ||||
| 							case ESpecifier::NeverInline : | ||||
| 							case ESpecifier::Static : | ||||
| 							case ESpecifier::Volatile : | ||||
| 							case ESpecifier::Virtual: | ||||
| 								break; | ||||
|  | ||||
| 							case ESpecifier::Consteval : | ||||
| 								expects_function = true; | ||||
| 								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 ); | ||||
| @@ -8018,7 +8066,7 @@ namespace parser | ||||
| 					{ | ||||
| 						if ( str_compare( name.Text, currtok.Text, name.Length ) == 0 ) | ||||
| 						{ | ||||
| 							member = parse_constructor(); | ||||
| 							member = parse_constructor( specifiers ); | ||||
| 							// <Attributes> <Specifiers> <Name>() | ||||
| 							break; | ||||
| 						} | ||||
| @@ -8109,12 +8157,38 @@ namespace parser | ||||
| 		} | ||||
|  | ||||
| 		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 ) | ||||
| 		{ | ||||
| 			tok                 = tokens[ idx - 2 ]; | ||||
|  | ||||
| 			bool is_indirection = tok.Type == TokType::Ampersand || tok.Type == TokType::Star; | ||||
|  | ||||
| 			bool ok_to_parse    = false; | ||||
|  | ||||
| 			if ( tok.Type == TokType::BraceCurly_Close ) | ||||
| @@ -8130,6 +8204,17 @@ namespace parser | ||||
| 				// <which> <type_identifier> <identifier>; | ||||
| 				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 ) | ||||
| 			{ | ||||
| 				// Its a indirection type with type ID using struct namespace. | ||||
| @@ -8139,7 +8224,7 @@ namespace parser | ||||
|  | ||||
| 			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(); | ||||
| 				return CodeInvalid; | ||||
| 			} | ||||
| @@ -8167,7 +8252,7 @@ namespace parser | ||||
| 		} | ||||
| 		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(); | ||||
| 			return CodeInvalid; | ||||
| 		} | ||||
| @@ -8219,6 +8304,40 @@ namespace parser | ||||
| 		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 ) | ||||
| 	{ | ||||
| 		Code result = CodeInvalid; | ||||
| @@ -8416,6 +8535,13 @@ namespace parser | ||||
|  | ||||
| 			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 : | ||||
| 					// Empty lines are auto skipped by Tokens.current() | ||||
| 					member = fmt_newline; | ||||
| @@ -8625,7 +8751,7 @@ namespace parser | ||||
| 				case TokType::Type_double : | ||||
| 				case TokType::Type_int : | ||||
| 				{ | ||||
| 					bool found_operator_cast = false; | ||||
| 					bool found_operator_cast_outside_class_implmentation = false; | ||||
| 					s32  idx                 = Context.Tokens.Idx; | ||||
|  | ||||
| 					for ( ; idx < Context.Tokens.Arr.num(); idx++ ) | ||||
| @@ -8643,12 +8769,12 @@ namespace parser | ||||
| 						} | ||||
|  | ||||
| 						if ( tok.Type == TokType::Decl_Operator ) | ||||
| 							found_operator_cast = true; | ||||
| 							found_operator_cast_outside_class_implmentation = true; | ||||
|  | ||||
| 						break; | ||||
| 					} | ||||
|  | ||||
| 					if ( found_operator_cast ) | ||||
| 					if ( found_operator_cast_outside_class_implmentation ) | ||||
| 					{ | ||||
| 						member = parse_operator_cast(); | ||||
| 						// <Attributes> <Specifiers> <Name>::operator <Type>() { ... } | ||||
| @@ -9197,8 +9323,10 @@ namespace parser | ||||
| 			return { nullptr }; | ||||
| 		} | ||||
|  | ||||
| 		Code     macro = { nullptr }; | ||||
| 		CodeType type  = { nullptr }; | ||||
| 		Code     value = { nullptr }; | ||||
| 		Token    name  = NullToken; | ||||
|  | ||||
| 		if ( check( TokType::Varadic_Argument ) ) | ||||
| 		{ | ||||
| @@ -9211,50 +9339,61 @@ namespace parser | ||||
| 			// or < ... > | ||||
| 		} | ||||
|  | ||||
| 		type = parse_type(); | ||||
| 		if ( type == Code::Invalid ) | ||||
| 		// Ex: Unreal has this type of macro:                 vvvvvvvv | ||||
| 		// 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(); | ||||
| 			return CodeInvalid; | ||||
| 		} | ||||
| 		// ( <ValueType> | ||||
|  | ||||
| 		Token name = NullToken; | ||||
|  | ||||
| 		if ( check( TokType::Identifier ) ) | ||||
| 		{ | ||||
| 			name = currtok; | ||||
| 			eat( TokType::Identifier ); | ||||
| 			// ( <ValueType> <Name> | ||||
|  | ||||
| 			if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) | ||||
| 			type = parse_type(); | ||||
| 			if ( type == Code::Invalid ) | ||||
| 			{ | ||||
| 				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> | ||||
| 				Context.pop(); | ||||
| 				return CodeInvalid; | ||||
| 			} | ||||
| 			// ( <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(); | ||||
| 		result->Type     = Parameters; | ||||
|  | ||||
| 		result->Macro = macro; | ||||
|  | ||||
| 		if ( name.Length > 0 ) | ||||
| 			result->Name = get_cached_string( name ); | ||||
|  | ||||
| @@ -9281,51 +9420,60 @@ namespace parser | ||||
| 				// ( <ValueType> <Name> = <Expression>, ... | ||||
| 			} | ||||
|  | ||||
| 			type = parse_type(); | ||||
| 			if ( type == Code::Invalid ) | ||||
| 			if ( ! check(TokType::Preprocess_Macro)) | ||||
| 			{ | ||||
| 				Context.pop(); | ||||
| 				return CodeInvalid; | ||||
| 			} | ||||
| 			// ( <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 ) ) | ||||
| 				type = parse_type(); | ||||
| 				if ( type == Code::Invalid ) | ||||
| 				{ | ||||
| 					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> | ||||
| 					Context.pop(); | ||||
| 					return CodeInvalid; | ||||
| 				} | ||||
| 				// ( <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(); | ||||
| 			param->Type     = Parameters; | ||||
|  | ||||
| 			param->Macro = macro; | ||||
|  | ||||
| 			if ( name.Length > 0 ) | ||||
| 				param->Name = get_cached_string( name ); | ||||
|  | ||||
| @@ -9552,26 +9700,8 @@ namespace parser | ||||
|  | ||||
| 		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> | ||||
| 			expr = parse_assignment_expression(); | ||||
| 		} | ||||
|  | ||||
| 		if ( currtok.Type == TokType::BraceCurly_Open ) | ||||
| @@ -9789,7 +9919,7 @@ namespace parser | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	internal CodeConstructor parse_constructor() | ||||
| 	internal CodeConstructor parse_constructor( CodeSpecifiers specifiers ) | ||||
| 	{ | ||||
| 		push_scope(); | ||||
|  | ||||
| @@ -9808,14 +9938,14 @@ namespace parser | ||||
| 			eat( TokType::Assign_Classifer ); | ||||
| 			// <Name> ( <Parameters> ) : | ||||
|  | ||||
| 			Token initializer_list_tok = NullToken; | ||||
| 			Token initializer_list_tok = currtok; | ||||
|  | ||||
| 			s32 level                  = 0; | ||||
| 			while ( left && ( currtok.Type != TokType::BraceCurly_Open || level > 0 ) ) | ||||
| 			{ | ||||
| 				if ( currtok.Type == TokType::BraceCurly_Open ) | ||||
| 				if (currtok.Type == TokType::Capture_Start) | ||||
| 					level++; | ||||
| 				else if ( currtok.Type == TokType::BraceCurly_Close ) | ||||
| 				else if ( currtok.Type == TokType::Capture_End ) | ||||
| 					level--; | ||||
|  | ||||
| 				eat( currtok.Type ); | ||||
| @@ -9833,6 +9963,10 @@ namespace parser | ||||
| 			body = parse_function_body(); | ||||
| 			// <Name> ( <Parameters> ) { <Body> } | ||||
| 		} | ||||
| 		else if ( check( TokType::Operator) && currtok.Text[ 0 ] == '=' ) | ||||
| 		{ | ||||
| 			body = parse_assignment_expression(); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			Token stmt_end = currtok; | ||||
| @@ -9844,15 +9978,21 @@ namespace parser | ||||
| 			// <Name> ( <Parameters> ); <InlineCmt> | ||||
| 		} | ||||
|  | ||||
| 		// TODO(Ed): Constructors can have post-fix specifiers | ||||
|  | ||||
| 		CodeConstructor result = ( CodeConstructor )make_code(); | ||||
|  | ||||
| 		result->Name = get_cached_string(identifier); | ||||
|  | ||||
| 		result->Specs = specifiers; | ||||
|  | ||||
| 		if ( params ) | ||||
| 			result->Params = params; | ||||
|  | ||||
| 		if ( initializer_list ) | ||||
| 			result->InitializerList = initializer_list; | ||||
|  | ||||
| 		if ( body ) | ||||
| 		if ( body && body->Type == ECode::Function_Body ) | ||||
| 		{ | ||||
| 			result->Body = body; | ||||
| 			result->Type = ECode::Constructor; | ||||
| @@ -10660,54 +10800,58 @@ namespace parser | ||||
| 			s32        NumSpecifiers = 0; | ||||
|  | ||||
| 			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 ); | ||||
|  | ||||
| 				switch ( spec ) | ||||
| 				while ( left && currtok.is_specifier() ) | ||||
| 				{ | ||||
| 					case ESpecifier::Const : | ||||
| 					case ESpecifier::Constexpr : | ||||
| 					case ESpecifier::Constinit : | ||||
| 					case ESpecifier::External_Linkage : | ||||
| 					case ESpecifier::Global : | ||||
| 					case ESpecifier::Inline : | ||||
| 					case ESpecifier::ForceInline : | ||||
| 					case ESpecifier::Local_Persist : | ||||
| 					case ESpecifier::Mutable : | ||||
| 					case ESpecifier::Static : | ||||
| 					case ESpecifier::Thread_Local : | ||||
| 					case ESpecifier::Volatile : | ||||
| 					SpecifierT spec = ESpecifier::to_type( currtok ); | ||||
|  | ||||
| 					switch ( spec ) | ||||
| 					{ | ||||
| 						case ESpecifier::Const : | ||||
| 						case ESpecifier::Constexpr : | ||||
| 						case ESpecifier::Constinit : | ||||
| 						case ESpecifier::External_Linkage : | ||||
| 						case ESpecifier::Global : | ||||
| 						case ESpecifier::Inline : | ||||
| 						case ESpecifier::ForceInline : | ||||
| 						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; | ||||
|  | ||||
| 					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; | ||||
| 					specs_found[ NumSpecifiers ] = spec; | ||||
| 					NumSpecifiers++; | ||||
| 					eat( currtok.Type ); | ||||
| 				} | ||||
|  | ||||
| 				// Ignore const it will be handled by the type | ||||
| 				if ( spec == ESpecifier::Const ) | ||||
| 					continue; | ||||
|  | ||||
| 				specs_found[ NumSpecifiers ] = spec; | ||||
| 				NumSpecifiers++; | ||||
| 				eat( currtok.Type ); | ||||
| 				if ( NumSpecifiers ) | ||||
| 				{ | ||||
| 					specifiers = def_specifiers( NumSpecifiers, specs_found ); | ||||
| 				} | ||||
| 				// <export> template< <Parameters> > <Attributes> <Specifiers> | ||||
| 			} | ||||
|  | ||||
| 			if ( NumSpecifiers ) | ||||
| 			{ | ||||
| 				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 | ||||
| 			// 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) | ||||
|  | ||||
| 			definition = parse_operator_function_or_variable( expects_function, 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 | ||||
| 		     || currtok.Type == TokType::Decl_Union ) | ||||
| 		{ | ||||
| 			name = currtok; | ||||
| 			eat( currtok.Type ); | ||||
| 			// <Attributes> <Specifiers> <class, enum, struct, union> | ||||
|  | ||||
| 			name.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )name.Text; | ||||
| 			eat( TokType::Identifier ); | ||||
| 			name = parse_identifier(); | ||||
|  | ||||
| 			// name.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )name.Text; | ||||
| 			// eat( TokType::Identifier ); | ||||
| 			Context.Scope->Name = name; | ||||
| 			// <Attributes> <Specifiers> <class, enum, struct, union> <Name> | ||||
| 		} | ||||
| @@ -10835,6 +10980,12 @@ namespace parser | ||||
| 			name.Length = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )name.Text; | ||||
| 			// <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 | ||||
| 		else | ||||
| @@ -11685,8 +11836,54 @@ CodeConstructor parse_constructor( StrC def ) | ||||
| 	if ( toks.Arr == nullptr ) | ||||
| 		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; | ||||
| 	CodeConstructor result = parse_constructor(); | ||||
| 	CodeConstructor result = parse_constructor( specifiers ); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| @@ -11699,6 +11896,9 @@ CodeDestructor parse_destructor( StrC def ) | ||||
| 	if ( toks.Arr == nullptr ) | ||||
| 		return CodeInvalid; | ||||
|  | ||||
| 	// TODO(Ed): Destructors can have prefix attributes | ||||
| 	// TODO(Ed): Destructors can have virtual | ||||
|  | ||||
| 	Context.Tokens        = toks; | ||||
| 	CodeDestructor result = parse_destructor(); | ||||
| 	return result; | ||||
|   | ||||
| @@ -70,9 +70,9 @@ using LogFailType = sw ( * )( char const*, ... ); | ||||
| enum class AccessSpec : u32 | ||||
| { | ||||
| 	Default, | ||||
| 	Public, | ||||
| 	Protected, | ||||
| 	Private, | ||||
| 	Protected, | ||||
| 	Public, | ||||
|  | ||||
| 	Num_AccessSpec, | ||||
| 	Invalid, | ||||
| @@ -82,9 +82,9 @@ inline char const* to_str( AccessSpec type ) | ||||
| { | ||||
| 	local_persist char const* lookup[ ( u32 )AccessSpec::Num_AccessSpec ] = { | ||||
| 		"", | ||||
| 		"public", | ||||
| 		"protected", | ||||
| 		"private", | ||||
| 		"protected", | ||||
| 		"public", | ||||
| 	}; | ||||
|  | ||||
| 	if ( type > AccessSpec::Public ) | ||||
| @@ -814,6 +814,7 @@ struct AST | ||||
|  | ||||
| 			union | ||||
| 			{ | ||||
| 				AST* Macro;           // Parameter | ||||
| 				AST* BitfieldSize;    // Variable (Class/Struct Data Member) | ||||
| 				AST* Params;          // Constructor, Function, Operator, Template, Typename | ||||
| 			}; | ||||
| @@ -2014,7 +2015,7 @@ struct AST_Constructor | ||||
| 	Code           Next; | ||||
| 	parser::Token* Tok; | ||||
| 	Code           Parent; | ||||
| 	char           _PAD_NAME_[ sizeof( StringCached ) ]; | ||||
| 	StringCached   Name; | ||||
| 	CodeT          Type; | ||||
| 	char           _PAD_UNUSED_[ sizeof( ModuleFlag ) + sizeof( u32 ) ]; | ||||
| }; | ||||
| @@ -2616,11 +2617,11 @@ struct AST_Param | ||||
|  | ||||
| 		struct | ||||
| 		{ | ||||
| 			char     _PAD_PROPERTIES_2_[ sizeof( AST* ) * 3 ]; | ||||
| 			char     _PAD_PROPERTIES_1_[ sizeof( AST* ) * 3 ]; | ||||
| 			CodeType ValueType; | ||||
| 			char     _PAD_PROPERTIES_[ sizeof( AST* ) ]; | ||||
| 			Code     Macro; | ||||
| 			Code     Value; | ||||
| 			char     _PAD_PROPERTIES_3_[ sizeof( AST* ) ]; | ||||
| 			char     _PAD_PROPERTIES_2_[ sizeof( AST* ) ]; | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user