Was able to parse UObject with gencpp!!!
This commit is contained in:
parent
7b9e277bc1
commit
48d21ddd15
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"autoHide.autoHideSideBar": false,
|
||||
"autoHide.autoHidePanel": false
|
||||
}
|
BIN
Project/Binaries/GasaGen_214406991.raddbgi
Normal file
BIN
Project/Binaries/GasaGen_214406991.raddbgi
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -4,6 +4,7 @@
|
||||
GameDefaultMap=/Game/Levels/StartupMap.StartupMap
|
||||
EditorStartupMap=/Game/Levels/StartupMap.StartupMap
|
||||
GlobalDefaultGameMode=/Game/Core/Game/BP_GameMode.BP_GameMode_C
|
||||
GameInstanceClass=/Script/Gasa.GasaGameInstance
|
||||
|
||||
[/Script/WindowsTargetPlatform.WindowsTargetSettings]
|
||||
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
|
||||
|
BIN
Project/Content/Core/Pickups/BP_HealthPotion.uasset
(Stored with Git LFS)
BIN
Project/Content/Core/Pickups/BP_HealthPotion.uasset
(Stored with Git LFS)
Binary file not shown.
BIN
Project/Content/Levels/StartupMap.umap
(Stored with Git LFS)
BIN
Project/Content/Levels/StartupMap.umap
(Stored with Git LFS)
Binary file not shown.
@ -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 );
|
||||
}
|
||||
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() ));
|
||||
break;
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
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()
|
||||
log_fmt("Class %S - Definitions:\n", gcode->Name);
|
||||
|
||||
if (gcode->Body->Type != CodeT::Class_Body)
|
||||
continue;
|
||||
for ( Code class_code : gcode->Body->cast<CodeBody>() )
|
||||
{
|
||||
InitHealth( 100.f );
|
||||
InitMaxHealth( 100.f );
|
||||
InitMana(( 50.f ));
|
||||
InitMaxMana( 50.f );
|
||||
switch ( class_code->Type )
|
||||
{
|
||||
case CodeT::Variable:
|
||||
case CodeT::Function:
|
||||
case CodeT::Function_Fwd:
|
||||
if ( class_code->Name )
|
||||
{
|
||||
log_fmt("%s\n", class_code->Name );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
body.append(fmt_newline);
|
||||
|
||||
impl_attribute_fields( body, attributeset_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);
|
||||
StrC str_gasa_api = txt("GASA_API");
|
||||
|
||||
source.print(body);
|
||||
}
|
||||
source.write();
|
||||
}
|
||||
|
||||
// gen::deinit();
|
||||
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;
|
||||
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
|
||||
{
|
||||
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 );
|
||||
result.append_fmt( "; // %S\n", ast->InlineCmt->Content );
|
||||
else
|
||||
result.append( "();\n" );
|
||||
}
|
||||
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 )
|
||||
{
|
||||
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,6 +9339,12 @@ namespace parser
|
||||
// or < ... >
|
||||
}
|
||||
|
||||
// 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))
|
||||
{
|
||||
type = parse_type();
|
||||
if ( type == Code::Invalid )
|
||||
{
|
||||
@ -9219,8 +9353,6 @@ namespace parser
|
||||
}
|
||||
// ( <ValueType>
|
||||
|
||||
Token name = NullToken;
|
||||
|
||||
if ( check( TokType::Identifier ) )
|
||||
{
|
||||
name = currtok;
|
||||
@ -9251,10 +9383,17 @@ namespace parser
|
||||
// ( <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,6 +9420,8 @@ namespace parser
|
||||
// ( <ValueType> <Name> = <Expression>, ...
|
||||
}
|
||||
|
||||
if ( ! check(TokType::Preprocess_Macro))
|
||||
{
|
||||
type = parse_type();
|
||||
if ( type == Code::Invalid )
|
||||
{
|
||||
@ -9322,10 +9463,17 @@ namespace parser
|
||||
}
|
||||
}
|
||||
// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = <Expression>, ..
|
||||
}
|
||||
else {
|
||||
macro = parse_simple_preprocess(ETokType::Preprocess_Macro);
|
||||
// ( ..., <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 );
|
||||
// <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,8 +10800,10 @@ namespace parser
|
||||
s32 NumSpecifiers = 0;
|
||||
|
||||
attributes = parse_attributes();
|
||||
// <export> template< <Parameters> > <Attributes>
|
||||
// <export> template< <Parameters> > <Attributes> <specifiers.
|
||||
|
||||
// Specifiers Parsing
|
||||
{
|
||||
while ( left && currtok.is_specifier() )
|
||||
{
|
||||
SpecifierT spec = ESpecifier::to_type( currtok );
|
||||
@ -10694,7 +10836,7 @@ namespace parser
|
||||
|
||||
// Ignore const it will be handled by the type
|
||||
if ( spec == ESpecifier::Const )
|
||||
continue;
|
||||
break;
|
||||
|
||||
specs_found[ NumSpecifiers ] = spec;
|
||||
NumSpecifiers++;
|
||||
@ -10706,8 +10848,10 @@ namespace parser
|
||||
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* ) ];
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user