diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..014c294 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "autoHide.autoHideSideBar": false, + "autoHide.autoHidePanel": false +} \ No newline at end of file diff --git a/Project/Binaries/GasaGen_214406991.raddbgi b/Project/Binaries/GasaGen_214406991.raddbgi new file mode 100644 index 0000000..437f3c6 Binary files /dev/null and b/Project/Binaries/GasaGen_214406991.raddbgi differ diff --git a/Project/Binaries/Win64/UnrealEditor-Gasa.dll b/Project/Binaries/Win64/UnrealEditor-Gasa.dll index ebec324..a9fa863 100644 Binary files a/Project/Binaries/Win64/UnrealEditor-Gasa.dll and b/Project/Binaries/Win64/UnrealEditor-Gasa.dll differ diff --git a/Project/Binaries/Win64/UnrealEditor-Gasa.pdb b/Project/Binaries/Win64/UnrealEditor-Gasa.pdb index 34c626e..8bf1354 100644 Binary files a/Project/Binaries/Win64/UnrealEditor-Gasa.pdb and b/Project/Binaries/Win64/UnrealEditor-Gasa.pdb differ diff --git a/Project/Config/DefaultEngine.ini b/Project/Config/DefaultEngine.ini index 0159339..4d2482c 100644 --- a/Project/Config/DefaultEngine.ini +++ b/Project/Config/DefaultEngine.ini @@ -4,6 +4,7 @@ GameDefaultMap=/Game/Levels/StartupMap.StartupMap EditorStartupMap=/Game/Levels/StartupMap.StartupMap GlobalDefaultGameMode=/Game/Core/Game/BP_GameMode.BP_GameMode_C +GameInstanceClass=/Script/Gasa.GasaGameInstance [/Script/WindowsTargetPlatform.WindowsTargetSettings] DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 diff --git a/Project/Content/Core/Pickups/BP_HealthPotion.uasset b/Project/Content/Core/Pickups/BP_HealthPotion.uasset index fe6339f..3e65fb1 100644 --- a/Project/Content/Core/Pickups/BP_HealthPotion.uasset +++ b/Project/Content/Core/Pickups/BP_HealthPotion.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1bb45c36adc4aaec849655a9782f02ad29163bce2fbe0f67a677902a0c5281a -size 23238 +oid sha256:6eb40e146413c93c8740e1ea2640a502809be263dc787b932982508109ff5425 +size 24406 diff --git a/Project/Content/Levels/StartupMap.umap b/Project/Content/Levels/StartupMap.umap index 5a7ecc1..5e17ee6 100644 --- a/Project/Content/Levels/StartupMap.umap +++ b/Project/Content/Levels/StartupMap.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a95ffacd2e27ff3e3337e3b09df620e08c64b4b82cafd79309ea7178a8869a0c +oid sha256:383190c33024f7a6f84dd3e9604ced05a82af8810cfebaa575ea7de889018b17 size 71926 diff --git a/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.h b/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.h index e6460e7..a612b0b 100644 --- a/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.h +++ b/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.h @@ -21,7 +21,6 @@ public: FGameplayAttributeData MaxMana; UGasaAttributeSet(); - UFUNCTION() void Client_OnRep_Health( FGameplayAttributeData& PrevHealth ); UFUNCTION() diff --git a/Project/Source/Gasa/Actors/CameraMount.cpp b/Project/Source/Gasa/Actors/CameraMount.cpp index 6987fc3..8bf12d8 100644 --- a/Project/Source/Gasa/Actors/CameraMount.cpp +++ b/Project/Source/Gasa/Actors/CameraMount.cpp @@ -3,7 +3,6 @@ #include "Camera/CameraComponent.h" #include "GameFramework/SpringArmComponent.h" - ACameraMount::ACameraMount() { PrimaryActorTick.bCanEverTick = true; diff --git a/Project/Source/Gasa/Actors/CameraMount.h b/Project/Source/Gasa/Actors/CameraMount.h index c099aeb..60a7b37 100644 --- a/Project/Source/Gasa/Actors/CameraMount.h +++ b/Project/Source/Gasa/Actors/CameraMount.h @@ -22,4 +22,3 @@ public: void PostInitializeComponents() override; #pragma endregion Actor }; - diff --git a/Project/Source/Gasa/Game/GasaGameInstance.cpp b/Project/Source/Gasa/Game/GasaGameInstance.cpp index f6f529a..1b1b707 100644 --- a/Project/Source/Gasa/Game/GasaGameInstance.cpp +++ b/Project/Source/Gasa/Game/GasaGameInstance.cpp @@ -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 )); } diff --git a/Project/Source/Gasa/ScratchMeta.h b/Project/Source/Gasa/ScratchMeta.h new file mode 100644 index 0000000..0f82c4c --- /dev/null +++ b/Project/Source/Gasa/ScratchMeta.h @@ -0,0 +1,9 @@ +// Don't keep this included anywhere +// Purely for inspection purposes + +#include "GasaCommon.h" + +void test() +{ + UObject::StaticClass()->PropertiesSize +} diff --git a/Project/Source/Gasa/UI/GasaUserWidget.cpp b/Project/Source/Gasa/UI/GasaUserWidget.cpp new file mode 100644 index 0000000..3327803 --- /dev/null +++ b/Project/Source/Gasa/UI/GasaUserWidget.cpp @@ -0,0 +1,7 @@ +#include "GasaUserWidget.h" + +UGasaUserWidget::UGasaUserWidget(FObjectInitializer const& ObjectInitializer) + : UUserWidget(ObjectInitializer) +{ + +} diff --git a/Project/Source/Gasa/UI/GasaUserWidget.h b/Project/Source/Gasa/UI/GasaUserWidget.h new file mode 100644 index 0000000..e2158b2 --- /dev/null +++ b/Project/Source/Gasa/UI/GasaUserWidget.h @@ -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 WidgetController; + + UGasaUserWidget(FObjectInitializer const& ObjectInitializer); + + UFUNCTION(BlueprintCallable) + void SetWidgetController(UObject* Controller) + { + WidgetController = Controller; + OnWidgetControllerSet(); + } + + UFUNCTION(BlueprintImplementableEvent) + void OnWidgetControllerSet(); +}; diff --git a/Project/Source/Gasa/UI/ProgressBar.h b/Project/Source/Gasa/UI/ProgressBar.h new file mode 100644 index 0000000..6da5351 --- /dev/null +++ b/Project/Source/Gasa/UI/ProgressBar.h @@ -0,0 +1,9 @@ +#pragma once +#include "Components/ProgressBar.h" + + +UCLASS() +class GASA_API UProgressIndicator : public UProgressBar +{ + +}; \ No newline at end of file diff --git a/Project/Source/Gasa/UI/WidgetController.cpp b/Project/Source/Gasa/UI/WidgetController.cpp new file mode 100644 index 0000000..81b2965 --- /dev/null +++ b/Project/Source/Gasa/UI/WidgetController.cpp @@ -0,0 +1 @@ +#include "WidgetController.h" diff --git a/Project/Source/Gasa/UI/WidgetController.h b/Project/Source/Gasa/UI/WidgetController.h new file mode 100644 index 0000000..399537d --- /dev/null +++ b/Project/Source/Gasa/UI/WidgetController.h @@ -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 Controller; + + UPROPERTY(BlueprintReadOnly, Category="Player") + TObjectPtr PlayerState; + + UPROPERTY(BlueprintReadOnly, Category="Player") + TObjectPtr AbilitySystem; + + UPROPERTY(BlueprintReadOnly, Category="Player") + TObjectPtr Attributes; +}; diff --git a/Project/Source/GasaGen/GasaGen.cpp b/Project/Source/GasaGen/GasaGen.cpp index 8ebda2a..1ab53d3 100644 --- a/Project/Source/GasaGen/GasaGen.cpp +++ b/Project/Source/GasaGen/GasaGen.cpp @@ -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 properties ) -{ - for ( StringCached property : properties ) - { - Code field_uproperty = code_fmt( "property", (StrC)property, stringize( - UPROPERTY(ReplicatedUsing=Client_OnRep_, 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 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_(FGameplayAttributeData& Prev); - ))); - } -} - -void def_attribute_field_property_getters( CodeBody body, StrC class_name, Array properties ) -{ - for ( String property : properties ) - { - CodeFn generated_get_attribute = parse_function( - token_fmt( "class_name", class_name, "property", (StrC)property, - stringize( - static FGameplayAttribute GetAttribute() - { - static FProperty* Prop = FindFieldChecked(::StaticClass(), GET_MEMBER_NAME_CHECKED(, )); - return Prop; - } - ))); - body.append( generated_get_attribute ); - } -} - -void def_attribute_field_value_getters( CodeBody body, Array properties ) -{ - for ( String property : properties ) - { - #pragma push_macro(FORCEINLINE) - #undef FORCEINLINE - - body.append( code_fmt( "property", (StrC)property, - stringize( - FORCEINLINE float Get() const - { - return .GetCurrentValue(); - } - ))); - - #pragma pop_macro(FORCEINLINE) - } -} - -void def_attribute_field_value_setters( CodeBody body, Array properties ) -{ - for ( String property : properties ) - { - body.append( code_fmt( "property", (StrC)property, - stringize( - FORCEINLINE void Set(float NewVal) - { - UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent(); - if (ensure(AbilityComp)) - { - AbilityComp->SetNumericAttributeBase(GetAttribute(), NewVal); - }; - } - ))); - } -} - -void def_attribute_field_initers ( CodeBody body, Array properties ) -{ - for ( String property : properties ) - { - body.append( code_fmt( "property", (StrC)property, - stringize( - FORCEINLINE void Init(float NewVal) - { - .SetBaseValue(NewVal); - .SetCurrentValue(NewVal); - } - ))); - } -} - -void impl_attribute_fields( CodeBody body, StrC class_name, Array 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 ::Client_OnRep_(FGameplayAttributeData& Prev) - { - GAMEPLAYATTRIBUTE_REPNOTIFY(, , Prev) - } - ))); - - 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 attribute_fields = Array::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 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& 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(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 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, ); - ))); + 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& OutLifetimeProps) const - { - Super::GetLifetimeReplicatedProps(OutLifetimeProps); - - } - ))); } - body.append(GetLifetimeOfReplicatedProps); - - source.print(body); } - source.write(); } - // gen::deinit(); + StrC str_gasa_api = txt("GASA_API"); + + gen_UGasaAttributeSet(); return 0; } diff --git a/Project/Source/GasaGen/GasaGenCommon.cpp b/Project/Source/GasaGen/GasaGenCommon.cpp new file mode 100644 index 0000000..691409f --- /dev/null +++ b/Project/Source/GasaGen/GasaGenCommon.cpp @@ -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 diff --git a/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp b/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp new file mode 100644 index 0000000..ef5b58e --- /dev/null +++ b/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp @@ -0,0 +1,268 @@ +// Used in the GasaGen.cpp translation unit + +void def_attribute_properties ( CodeBody body, Array properties ); +void def_attribute_field_on_reps ( CodeBody body, Array properties ); +void def_attribute_field_property_getters( CodeBody body, StrC class_name, Array properties ); +void def_attribute_field_value_getters ( CodeBody body, Array properties ); +void def_attribute_field_value_setters ( CodeBody body, Array properties ); +void def_attribute_field_initers ( CodeBody body, Array properties ); +void impl_attribute_fields ( CodeBody body, StrC class_name, Array 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 attribute_fields = Array::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& 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(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, ); + ))); + } + + GetLifetimeOfReplicatedProps = parse_function( token_fmt( "body", (StrC)(field_lifetimes.to_string()), stringize( + void UGasaAttributeSet::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const + { + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + + } + ))); + } + body.append(GetLifetimeOfReplicatedProps); + + source.print(body); + } + source.write(); + } +} + +void def_attribute_properties( CodeBody body, Array properties ) +{ + for ( StringCached property : properties ) + { + Code field_uproperty = code_fmt( "property", (StrC)property, stringize( + UPROPERTY(ReplicatedUsing=Client_OnRep_, 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 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_(FGameplayAttributeData& Prev); + ))); + } +} + +void def_attribute_field_property_getters( CodeBody body, StrC class_name, Array properties ) +{ + for ( String property : properties ) + { + CodeFn generated_get_attribute = parse_function( + token_fmt( "class_name", class_name, "property", (StrC)property, + stringize( + static FGameplayAttribute GetAttribute() + { + static FProperty* Prop = FindFieldChecked(::StaticClass(), GET_MEMBER_NAME_CHECKED(, )); + return Prop; + } + ))); + body.append( generated_get_attribute ); + } +} + +void def_attribute_field_value_getters( CodeBody body, Array properties ) +{ + for ( String property : properties ) + { +#pragma push_macro(FORCEINLINE) +#undef FORCEINLINE + body.append( code_fmt( "property", (StrC)property, + stringize( + FORCEINLINE float Get() const + { + return .GetCurrentValue(); + } + ))); +#pragma pop_macro(FORCEINLINE) + } +} + +void def_attribute_field_value_setters( CodeBody body, Array properties ) +{ + for ( String property : properties ) + { + body.append( code_fmt( "property", (StrC)property, + stringize( + FORCEINLINE void Set(float NewVal) + { + UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent(); + if (ensure(AbilityComp)) + { + AbilityComp->SetNumericAttributeBase(GetAttribute(), NewVal); + }; + } + ))); + } +} + +void def_attribute_field_initers ( CodeBody body, Array properties ) +{ + for ( String property : properties ) + { + body.append( code_fmt( "property", (StrC)property, + stringize( + FORCEINLINE void Init(float NewVal) + { + .SetBaseValue(NewVal); + .SetCurrentValue(NewVal); + } + ))); + } +} + +void impl_attribute_fields( CodeBody body, StrC class_name, Array 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 ::Client_OnRep_(FGameplayAttributeData& Prev) + { + GAMEPLAYATTRIBUTE_REPNOTIFY(, , Prev) + } + ))); + + body.append( field_impl ); + } +} diff --git a/Project/Source/GasaGen/Readme.md b/Project/Source/GasaGen/Readme.md new file mode 100644 index 0000000..362b2cc --- /dev/null +++ b/Project/Source/GasaGen/Readme.md @@ -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) diff --git a/Project/Source/GasaGen/gen.cpp b/Project/Source/GasaGen/gen.cpp index 5f762e1..6151d8e 100644 --- a/Project/Source/GasaGen/gen.cpp +++ b/Project/Source/GasaGen/gen.cpp @@ -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: ( , ... ) + 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(); // : + 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 ); // () break; } @@ -8109,12 +8157,38 @@ namespace parser } Token tok = tokens[ idx - 1 ]; + if ( tok.is_specifier() && is_trailing( ESpecifier::to_type(tok)) ) + { + // (...) ...; + + 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 } ); + // , 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 // ; 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 + // : ; + 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 ); + // = + + 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 ); + // = + 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(); // ::operator () { ... } @@ -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; - } - // ( - - Token name = NullToken; - - if ( check( TokType::Identifier ) ) - { - name = currtok; - eat( TokType::Identifier ); - // ( - - if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) + type = parse_type(); + if ( type == Code::Invalid ) { - eat( TokType::Operator ); - // ( = - - 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 ) ); - // ( = + Context.pop(); + return CodeInvalid; } + // ( + + if ( check( TokType::Identifier ) ) + { + name = currtok; + eat( TokType::Identifier ); + // ( + + if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) + { + eat( TokType::Operator ); + // ( = + + 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 ) ); + // ( = + } + } + } + else { + macro = parse_simple_preprocess(ETokType::Preprocess_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 // ( = , ... } - type = parse_type(); - if ( type == Code::Invalid ) + if ( ! check(TokType::Preprocess_Macro)) { - Context.pop(); - return CodeInvalid; - } - // ( = , - - name = { nullptr, 0, TokType::Invalid, false }; - - if ( check( TokType::Identifier ) ) - { - name = currtok; - eat( TokType::Identifier ); - // ( = , - - if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) + type = parse_type(); + if ( type == Code::Invalid ) { - eat( TokType::Operator ); - // ( = , = - - 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 ) ); - // ( = , = + Context.pop(); + return CodeInvalid; } + // ( = , + + name = { nullptr, 0, TokType::Invalid, false }; + + if ( check( TokType::Identifier ) ) + { + name = currtok; + eat( TokType::Identifier ); + // ( = , + + if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) + { + eat( TokType::Operator ); + // ( = , = + + 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 ) ); + // ( = , = + } + } + // ( = , = , .. + } + else { + macro = parse_simple_preprocess(ETokType::Preprocess_Macro); + // ( ..., } - // ( = , = , .. 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 ); - // = - - 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 ); // = + 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 ); // ( ) : - 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(); // ( ) { } } + else if ( check( TokType::Operator) && currtok.Text[ 0 ] == '=' ) + { + body = parse_assignment_expression(); + } else { Token stmt_end = currtok; @@ -9844,15 +9978,21 @@ namespace parser // ( ); } + // 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(); - // template< > + // template< > template< > } - if ( NumSpecifiers ) - { - specifiers = def_specifiers( NumSpecifiers, specs_found ); - } - // template< > - - // 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 ); // template< > ... @@ -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 ); // - 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; // } @@ -10835,6 +10980,12 @@ namespace parser name.Length = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )name.Text; // } + else if ( currtok.Type == TokType::Type_Typename ) + { + name = currtok; + eat(TokType::Type_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 ); + // ... + } + 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; diff --git a/Project/Source/GasaGen/gen.hpp b/Project/Source/GasaGen/gen.hpp index ed3613a..3af0b3e 100644 --- a/Project/Source/GasaGen/gen.hpp +++ b/Project/Source/GasaGen/gen.hpp @@ -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* ) ]; }; }; diff --git a/Readme.md b/Readme.md index a6793ba..d54b886 100644 --- a/Readme.md +++ b/Readme.md @@ -10,6 +10,7 @@ Implementation design perfs: * Lift to more specific or generalized code-pathsonly when necessary * Minimize distinct code-paths * 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 * Keep everything data-wise in the runtime unless there is a measurable performance cost. * Some exploratory optimizations for educational purposes. diff --git a/scripts/gen_pass_gasa.ps1 b/scripts/gen_pass_gasa.ps1 index 537ae1b..62ded28 100644 --- a/scripts/gen_pass_gasa.ps1 +++ b/scripts/gen_pass_gasa.ps1 @@ -105,7 +105,7 @@ function run-gengasa $path_AbilitySystem = join-path $path_gasa 'AbilitySystem' $include = @( - 'GasaAttributeSet.h', 'GasaAttributeSet.cpp' + 'GasaAttributeSet.h', 'GasaAttributeSet.cpp', 'LETS_SEE.h' ) format-cpp $path_AbilitySystem $include $null }