Can parse AActor with gencpp

This commit is contained in:
Edward R. Gonzalez 2024-04-15 23:09:46 -04:00
parent 48d21ddd15
commit 6e3dfb70be
5 changed files with 529 additions and 190 deletions

View File

@ -9,7 +9,7 @@ UGasaAttributeSet::UGasaAttributeSet()
{ {
InitHealth( 100.f ); InitHealth( 100.f );
InitMaxHealth( 100.f ); InitMaxHealth( 100.f );
InitMana( ( 50.f ) ); InitMana( 50.f );
InitMaxMana( 50.f ); InitMaxMana( 50.f );
} }

View File

@ -26,16 +26,12 @@ int gen_main()
StrC str_generated_uclass_body = txt("GENERATED_UCLASS_BODY("); StrC str_generated_uclass_body = txt("GENERATED_UCLASS_BODY(");
StrC str_property_binding_impl = txt("PROPERTY_BINDING_IMPLEMENTATION"); StrC str_property_binding_impl = txt("PROPERTY_BINDING_IMPLEMENTATION");
StrC str_uclass = txt("UCLASS("); StrC str_uclass = txt("UCLASS(");
StrC str_ue_deprecated = txt("UE_DEPRECATED(");
StrC str_ufunction = txt("UFUNCTION("); StrC str_ufunction = txt("UFUNCTION(");
StrC str_uproperty = txt("UPROPERTY("); StrC str_uproperty = txt("UPROPERTY(");
StrC str_umg_api = txt("UMG_API");
StrC str_declare_log_category_extern = txt("DECLARE_LOG_CATEGORY_EXTERN("); StrC str_declare_log_category_extern = txt("DECLARE_LOG_CATEGORY_EXTERN(");
StrC str_enum_class_flags = txt("ENUM_CLASS_FLAGS("); StrC str_enum_class_flags = txt("ENUM_CLASS_FLAGS(");
StrC str_declare_class = txt("DECLARE_CLASS("); StrC str_declare_class = txt("DECLARE_CLASS(");
StrC str_define_default_object_initializer_constructor_call = txt("DEFINE_DEFAULT_OBJECT_INITIALIZER_CONSTRUCTOR_CALL("); 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_macro_text = txt("TEXT(");
StrC str_declare_multicast_delegate_one_parameter = txt("DECLARE_MULTICAST_DELEGATE_OneParam("); 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_two_parameter = txt("DECLARE_MULTICAST_DELEGATE_TwoParams(");
@ -45,20 +41,36 @@ int gen_main()
StrC str_result_decl = txt("RESULT_DECL"); StrC str_result_decl = txt("RESULT_DECL");
StrC str_property_binding_implementation = txt("PROPERTY_BINDING_IMPLEMENTATION("); StrC str_property_binding_implementation = txt("PROPERTY_BINDING_IMPLEMENTATION(");
StrC str_FORCEINLINE = txt("FORCEINLINE"); StrC str_FORCEINLINE = txt("FORCEINLINE");
StrC str_UENUM = txt("UENUM(");
StrC str_UMETA = txt("UMETA(");
StrC str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams = txt("DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams(");
StrC str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_SevenParams = txt("DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_SevenParams(");
StrC str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_NineParams = txt("DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_NineParams(");
StrC str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FourParams = txt("DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FourParams(");
StrC str_DECLARE_DELEGATE_SixParams = txt("DECLARE_DELEGATE_SixParams(");
StrC str_DECLARE_EVENT_TwoParams = txt("DECLARE_EVENT_TwoParams(");
StrC str_DECLARE_DELEGATE_RetVal_ThreeParams = txt("DECLARE_DELEGATE_RetVal_ThreeParams(");
StrC str_PRAGMA_DISABLE_DEPRECATION_WARNINGS = txt("PRAGMA_DISABLE_DEPRECATION_WARNINGS");
StrC str_PRAGMA_ENABLE_DEPRECATION_WARNINGS = txt("PRAGMA_ENABLE_DEPRECATION_WARNINGS");
StrC str_DEFINE_ACTORDESC_TYPE = txt("DEFINE_ACTORDESC_TYPE(");
StrC str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_TwoParams = txt("DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_TwoParams(");
StrC str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_OneParam = txt("DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_OneParam(");
StrC str_UPARAM = txt("UPARAM(");
StrC str_FORCEINLINE_DEBUGGABLE = txt("FORCEINLINE_DEBUGGABLE");
PreprocessorDefines.append( get_cached_string(str_generated_body)); PreprocessorDefines.append( get_cached_string(str_generated_body));
PreprocessorDefines.append( get_cached_string(str_generated_uclass_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_property_binding_impl));
PreprocessorDefines.append( get_cached_string(str_ue_deprecated)); // PreprocessorDefines.append( get_cached_string(str_ue_deprecated));
PreprocessorDefines.append( get_cached_string(str_uclass)); PreprocessorDefines.append( get_cached_string(str_uclass));
PreprocessorDefines.append( get_cached_string(str_ufunction)); PreprocessorDefines.append( get_cached_string(str_ufunction));
PreprocessorDefines.append( get_cached_string(str_uproperty)); PreprocessorDefines.append( get_cached_string(str_uproperty));
PreprocessorDefines.append( get_cached_string(str_umg_api)); // PreprocessorDefines.append( get_cached_string(str_umg_api));
PreprocessorDefines.append( get_cached_string(str_declare_log_category_extern)); 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_enum_class_flags));
PreprocessorDefines.append( get_cached_string(str_declare_class)); 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_define_default_object_initializer_constructor_call));
PreprocessorDefines.append( get_cached_string(str_core_object_api)); // PreprocessorDefines.append( get_cached_string(str_core_object_api));
PreprocessorDefines.append( get_cached_string(str_macro_text)); 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_one_parameter));
PreprocessorDefines.append( get_cached_string(str_declare_multicast_delegate_two_parameter)); PreprocessorDefines.append( get_cached_string(str_declare_multicast_delegate_two_parameter));
@ -68,11 +80,31 @@ int gen_main()
PreprocessorDefines.append( get_cached_string(str_result_decl)); PreprocessorDefines.append( get_cached_string(str_result_decl));
PreprocessorDefines.append( get_cached_string(str_property_binding_implementation)); PreprocessorDefines.append( get_cached_string(str_property_binding_implementation));
PreprocessorDefines.append( get_cached_string(str_FORCEINLINE)); PreprocessorDefines.append( get_cached_string(str_FORCEINLINE));
PreprocessorDefines.append( get_cached_string(str_UENUM));
PreprocessorDefines.append( get_cached_string(str_UMETA));
PreprocessorDefines.append( get_cached_string(str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams));
PreprocessorDefines.append( get_cached_string(str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_SevenParams));
PreprocessorDefines.append( get_cached_string(str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_NineParams));
PreprocessorDefines.append( get_cached_string(str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FourParams));
PreprocessorDefines.append( get_cached_string(str_DECLARE_DELEGATE_SixParams));
PreprocessorDefines.append( get_cached_string(str_DECLARE_EVENT_TwoParams));
PreprocessorDefines.append( get_cached_string(str_DECLARE_DELEGATE_RetVal_ThreeParams));
// PreprocessorDefines.append( get_cached_string(str_ENGINE_API));
PreprocessorDefines.append( get_cached_string(str_PRAGMA_DISABLE_DEPRECATION_WARNINGS));
PreprocessorDefines.append( get_cached_string(str_PRAGMA_ENABLE_DEPRECATION_WARNINGS));
PreprocessorDefines.append( get_cached_string(str_DEFINE_ACTORDESC_TYPE));
PreprocessorDefines.append( get_cached_string(str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_TwoParams));
PreprocessorDefines.append( get_cached_string(str_DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_OneParam));
PreprocessorDefines.append( get_cached_string(str_UPARAM));
PreprocessorDefines.append( get_cached_string(str_FORCEINLINE_DEBUGGABLE));
FileContents content;
#define path_UProgressBar \ #define path_UProgressBar \
"C:/projects/Unreal/Surgo/UE/Engine/Source/Runtime/UMG/Public/Components/ProgressBar.h" "C:/projects/Unreal/Surgo/UE/Engine/Source/Runtime/UMG/Public/Components/ProgressBar.h"
FileContents content = file_read_contents( GlobalAllocator, true, path_UProgressBar ); #if 0
content = file_read_contents( GlobalAllocator, true, path_UProgressBar );
CodeBody parsed_uprogressbar = parse_global_body( StrC { content.size, (char const*)content.data }); CodeBody parsed_uprogressbar = parse_global_body( StrC { content.size, (char const*)content.data });
log_fmt("\n\n"); log_fmt("\n\n");
@ -93,22 +125,63 @@ int gen_main()
case CodeT::Function_Fwd: case CodeT::Function_Fwd:
if ( class_code->Name ) if ( class_code->Name )
{ {
log_fmt("%s\n", class_code->Name ); // log_fmt("%s\n", class_code->Name );
log_fmt("%s\n", class_code->to_string() );
} }
break; break;
} }
} }
} }
} }
#endif
#define path_UObject \ #define path_UObject \
R"(C:\projects\Unreal\Surgo\UE\Engine\Source\Runtime\CoreUObject\Public\UObject\Object.h)" R"(C:\projects\Unreal\Surgo\UE\Engine\Source\Runtime\CoreUObject\Public\UObject\Object.h)"
#if 0
content = file_read_contents( GlobalAllocator, true, path_UObject ); content = file_read_contents( GlobalAllocator, true, path_UObject );
CodeBody parsed_uobject = parse_global_body( StrC { content.size, (char const*)content.data }); CodeBody parsed_uobject = parse_global_body( StrC { content.size, (char const*)content.data });
log_fmt("\n\n"); log_fmt("\n\n");
for ( Code gcode : parsed_uobject ) for ( Code gcode : parsed_uobject )
{
if ( gcode->Type == CodeT::Class )
{
log_fmt("Class %S - Definitions:\n", gcode->Name);
// log_fmt("%s\n", gcode->to_string() );
if (gcode->Body->Type != CodeT::Class_Body)
continue;
for ( Code class_code : gcode->Body->cast<CodeBody>() )
{
switch ( class_code->Type )
{
case CodeT::Constructor:
case CodeT::Constructor_Fwd:
case CodeT::Variable:
case CodeT::Function:
case CodeT::Function_Fwd:
if ( class_code->Name )
{
log_fmt("%s\n", class_code->Name );
// log_fmt("%s\n", class_code->to_string() );
}
break;
}
}
}
}
#endif
#define path_AActor \
R"(C:\projects\Unreal\Surgo\UE\Engine\Source\Runtime\Engine\Classes\GameFramework\Actor.h)"
#if 0
content = file_read_contents( GlobalAllocator, true, path_AActor );
CodeBody parsed_aactor = parse_global_body( StrC { content.size, (char const*)content.data });
log_fmt("\n\n");
for ( Code gcode : parsed_aactor )
{ {
if ( gcode->Type == CodeT::Class ) if ( gcode->Type == CodeT::Class )
{ {
@ -121,8 +194,8 @@ int gen_main()
switch ( class_code->Type ) switch ( class_code->Type )
{ {
case CodeT::Variable: case CodeT::Variable:
case CodeT::Function: // case CodeT::Function:
case CodeT::Function_Fwd: // case CodeT::Function_Fwd:
if ( class_code->Name ) if ( class_code->Name )
{ {
log_fmt("%s\n", class_code->Name ); log_fmt("%s\n", class_code->Name );
@ -132,8 +205,9 @@ int gen_main()
} }
} }
} }
#endif
StrC str_gasa_api = txt("GASA_API"); // StrC str_gasa_api = txt("GASA_API");
gen_UGasaAttributeSet(); gen_UGasaAttributeSet();
return 0; return 0;

View File

@ -114,7 +114,7 @@ void gen_UGasaAttributeSet()
{ {
InitHealth( 100.f ); InitHealth( 100.f );
InitMaxHealth( 100.f ); InitMaxHealth( 100.f );
InitMana(( 50.f )); InitMana( 50.f );
InitMaxMana( 50.f ); InitMaxMana( 50.f );
} }
)); ));

View File

@ -1588,7 +1588,12 @@ void CodeConstructor::to_string_def( String& result )
void CodeConstructor::to_string_fwd( String& result ) void CodeConstructor::to_string_fwd( String& result )
{ {
AST* ClassStructParent = ast->Parent->Parent; AST* ClassStructParent = ast->Parent->Parent;
if (ClassStructParent) {
result.append( ClassStructParent->Name ); result.append( ClassStructParent->Name );
}
else {
result.append( ast->Name );
}
if ( ast->Params ) if ( ast->Params )
result.append_fmt( "( %S )", ast->Params.to_string() ); result.append_fmt( "( %S )", ast->Params.to_string() );
@ -2273,24 +2278,24 @@ void CodeParam::to_string( String& result )
{ {
if ( ast->Macro ) if ( ast->Macro )
{ {
// Were using the convention that if the value type is a macro we ignore everything else
// Related to parsing: ( <macro>, ... ) // Related to parsing: ( <macro>, ... )
result.append( ast->Macro.ast->Content ); result.append( ast->Macro.ast->Content );
return; // Could also be: ( <macro> <type <name>, ... )
} }
if ( ast->Name ) if ( ast->Name )
{ {
if ( ast->ValueType.ast == nullptr ) if ( ast->ValueType.ast == nullptr )
result.append_fmt( "%S", ast->Name ); result.append_fmt( " %S", ast->Name );
else else
result.append_fmt( "%S %S", ast->ValueType.to_string(), ast->Name ); result.append_fmt( " %S %S", ast->ValueType.to_string(), ast->Name );
} }
else else if ( ast->ValueType )
result.append_fmt( "%S", ast->ValueType.to_string() ); result.append_fmt( " %S", ast->ValueType.to_string() );
if ( ast->Value ) if ( ast->Value )
result.append_fmt( "= %S", ast->Value.to_string() ); result.append_fmt( " = %S", ast->Value.to_string() );
if ( ast->NumEntries - 1 > 0 ) if ( ast->NumEntries - 1 > 0 )
{ {
@ -5599,7 +5604,14 @@ namespace parser
{ {
namespace ETokType namespace ETokType
{ {
#define GEN_DEFINE_ATTRIBUTE_TOKENS Entry( API_Export, GEN_API_Export_Code ) Entry( API_Import, GEN_API_Import_Code ) #define GEN_DEFINE_ATTRIBUTE_TOKENS \
Entry( API_Export, "GEN_API_Export_Code" ) \
Entry( API_Import, "GEN_API_Import_Code" ) \
Entry( UE_DEPRECATED, "UE_DEPRECATED(" ) \
Entry( UMG_API, "UMG_API" ) \
Entry( COREUOBJECT_API, "COREUOBJECT_API" ) \
Entry( ENGINE_API, "ENGINE_API" ) \
Entry( GASA_API, "GASA_API" )
enum Type : u32 enum Type : u32
{ {
@ -5700,6 +5712,11 @@ namespace parser
__Attributes_Start, __Attributes_Start,
API_Export, API_Export,
API_Import, API_Import,
UE_DEPRECATED,
UMG_API,
COREUOBJECT_API,
ENGINE_API,
GASA_API,
NumTokens NumTokens
}; };
@ -5803,6 +5820,11 @@ namespace parser
{ sizeof( "__attrib_start__" ), "__attrib_start__" }, { sizeof( "__attrib_start__" ), "__attrib_start__" },
{ sizeof( "GEN_API_Export_Code" ), "GEN_API_Export_Code" }, { sizeof( "GEN_API_Export_Code" ), "GEN_API_Export_Code" },
{ sizeof( "GEN_API_Import_Code" ), "GEN_API_Import_Code" }, { sizeof( "GEN_API_Import_Code" ), "GEN_API_Import_Code" },
{ sizeof( "UE_DEPRECATED" ), "UE_DEPRECATED" },
{ sizeof( "UMG_API" ), "UMG_API" },
{ sizeof( "COREUOBJECT_API" ), "COREUOBJECT_API" },
{ sizeof( "ENGINE_API" ), "ENGINE_API" },
{ sizeof( "GASA_API" ), "GASA_API" },
}; };
return lookup[ type ]; return lookup[ type ];
} }
@ -5982,7 +6004,7 @@ namespace parser
} }
}; };
global Arena_64KB defines_map_arena; global Arena_128KB defines_map_arena;
global HashTable< StrC > defines; global HashTable< StrC > defines;
global Array< Token > Tokens; global Array< Token > Tokens;
@ -6283,6 +6305,11 @@ namespace parser
token.Flags |= TF_AccessSpecifier; token.Flags |= TF_AccessSpecifier;
} }
if ( type > TokType::__Attributes_Start )
{
token.Flags |= TF_Attribute;
}
if ( type == ETokType::Decl_Extern_Linkage ) if ( type == ETokType::Decl_Extern_Linkage )
{ {
SkipWhitespace(); SkipWhitespace();
@ -7174,7 +7201,7 @@ namespace parser
{ {
Tokens = Array< Token >::init_reserve( LexArena, ( LexAllocator_Size - sizeof( Array< Token >::Header ) ) / sizeof( Token ) ); Tokens = Array< Token >::init_reserve( LexArena, ( LexAllocator_Size - sizeof( Array< Token >::Header ) ) / sizeof( Token ) );
defines_map_arena = Arena_64KB::init(); defines_map_arena = Arena_128KB::init();
defines = HashTable< StrC >::init( defines_map_arena ); defines = HashTable< StrC >::init( defines_map_arena );
} }
@ -7256,7 +7283,7 @@ namespace parser
internal CodeStruct parse_struct( bool inplace_def = false ); internal CodeStruct parse_struct( bool inplace_def = false );
internal CodeVar parse_variable(); internal CodeVar parse_variable();
internal CodeTemplate parse_template(); internal CodeTemplate parse_template();
internal CodeType parse_type( bool* is_function = nullptr ); internal CodeType parse_type( bool from_template = false, bool* is_function = nullptr );
internal CodeTypedef parse_typedef(); internal CodeTypedef parse_typedef();
internal CodeUnion parse_union( bool inplace_def = false ); internal CodeUnion parse_union( bool inplace_def = false );
internal CodeUsing parse_using(); internal CodeUsing parse_using();
@ -7594,15 +7621,18 @@ namespace parser
{ {
push_scope(); push_scope();
Token start = NullToken; Token start = currtok;
s32 len = 0; s32 len = 0;
// There can be more than one attribute. If there is flatten them to a single string.
// TODO(Ed): Support keeping an linked list of attributes similar to parameters
while ( left && currtok.is_attribute() )
{
if ( check( TokType::Attribute_Open ) ) if ( check( TokType::Attribute_Open ) )
{ {
eat( TokType::Attribute_Open ); eat( TokType::Attribute_Open );
// [[ // [[
start = currtok;
while ( left && currtok.Type != TokType::Attribute_Close ) while ( left && currtok.Type != TokType::Attribute_Close )
{ {
eat( currtok.Type ); eat( currtok.Type );
@ -7612,9 +7642,8 @@ namespace parser
eat( TokType::Attribute_Close ); eat( TokType::Attribute_Close );
// [[ <Content> ]] // [[ <Content> ]]
s32 len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text; len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text;
} }
else if ( check( TokType::Decl_GNU_Attribute ) ) else if ( check( TokType::Decl_GNU_Attribute ) )
{ {
eat( TokType::Decl_GNU_Attribute ); eat( TokType::Decl_GNU_Attribute );
@ -7622,7 +7651,6 @@ namespace parser
eat( TokType::Capture_Start ); eat( TokType::Capture_Start );
// __attribute__(( // __attribute__((
start = currtok;
while ( left && currtok.Type != TokType::Capture_End ) while ( left && currtok.Type != TokType::Capture_End )
{ {
eat( currtok.Type ); eat( currtok.Type );
@ -7633,16 +7661,14 @@ namespace parser
eat( TokType::Capture_End ); eat( TokType::Capture_End );
// __attribute__(( <Content> )) // __attribute__(( <Content> ))
s32 len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text; len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text;
} }
else if ( check( TokType::Decl_MSVC_Attribute ) ) else if ( check( TokType::Decl_MSVC_Attribute ) )
{ {
eat( TokType::Decl_MSVC_Attribute ); eat( TokType::Decl_MSVC_Attribute );
eat( TokType::Capture_Start ); eat( TokType::Capture_Start );
// __declspec( // __declspec(
start = currtok;
while ( left && currtok.Type != TokType::Capture_End ) while ( left && currtok.Type != TokType::Capture_End )
{ {
eat( currtok.Type ); eat( currtok.Type );
@ -7652,14 +7678,33 @@ namespace parser
eat( TokType::Capture_End ); eat( TokType::Capture_End );
// __declspec( <Content> ) // __declspec( <Content> )
s32 len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text; len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text;
} }
else if ( currtok.is_attribute() ) else if ( currtok.is_attribute() )
{ {
eat( currtok.Type ); eat( currtok.Type );
s32 len = start.Length;
// <Attribute> // <Attribute>
// If its a macro based attribute, this could be a functional macro such as Unreal's UE_DEPRECATED(...)
if ( check( TokType::Capture_Start))
{
eat( TokType::Capture_Start );
s32 level = 0;
while (left && currtok.Type != TokType::Capture_End && level == 0)
{
if (currtok.Type == TokType::Capture_Start)
++ level;
if (currtok.Type == TokType::Capture_End)
--level;
eat(currtok.Type);
}
eat(TokType::Capture_End);
}
len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text;
// <Attribute> ( ... )
}
} }
if ( len > 0 ) if ( len > 0 )
@ -8036,6 +8081,24 @@ namespace parser
} }
// <Attributes> <Specifiers> // <Attributes> <Specifiers>
if ( currtok.is_attribute() )
{
// Unfortuantely Unreal has code where there is attirbutes before specifiers
CodeAttributes more_attributes = parse_attributes();
if ( attributes )
{
String fused = String::make_reserve( GlobalAllocator, attributes->Content.length() + more_attributes->Content.length() );
fused.append_fmt( "%S %S", attributes->Content, more_attributes->Content );
attributes->Name = get_cached_string(fused);
attributes->Content = attributes->Name;
// <Attributes> <Specifiers> <Attributes>
}
attributes = more_attributes;
}
if ( currtok.Type == TokType::Operator && currtok.Text[ 0 ] == '~' ) if ( currtok.Type == TokType::Operator && currtok.Text[ 0 ] == '~' )
{ {
member = parse_destructor( specifiers ); member = parse_destructor( specifiers );
@ -8451,6 +8514,9 @@ namespace parser
result->Type = Function_Fwd; result->Type = Function_Fwd;
} }
if ( attributes )
result->Attributes = attributes;
if ( specifiers ) if ( specifiers )
result->Specs = specifiers; result->Specs = specifiers;
@ -9339,30 +9405,42 @@ namespace parser
// or < ... > // or < ... >
} }
// Ex: Unreal has this type of macro: vvvvvvvv #define CheckEndParams() \
// COREUOBJECT_API void CallFunction( FFrame& Stack, RESULT_DECL, UFunction* Function ); (use_template_capture ? (currtok.Text[ 0 ] != '>') : (currtok.Type != TokType::Capture_End))
// This is so that a 'ValueType for the param is a preprocesor macro to support this nasty macro usage'
if ( ! check(TokType::Preprocess_Macro)) // Ex: Unreal has this type of macro: vvvvvvvvv
// COREUOBJECT_API void CallFunction( FFrame& Stack, RESULT_DECL, UFunction* Function );
// and: vvvv
// AddComponentByClass(UPARAM(meta = (AllowAbstract = "false")) TSubclassOf<UActorComponent> Class, bool bManualAttachment, ...
if ( check(TokType::Preprocess_Macro))
{ {
type = parse_type(); macro = parse_simple_preprocess(ETokType::Preprocess_Macro);
// ( <Macro>
}
if ( currtok.Type != TokType::Comma )
{
type = parse_type( use_template_capture );
if ( type == Code::Invalid ) if ( type == Code::Invalid )
{ {
Context.pop(); Context.pop();
return CodeInvalid; return CodeInvalid;
} }
// ( <ValueType> // ( <Macro> <ValueType>
if ( check( TokType::Identifier ) ) if ( check( TokType::Identifier ) )
{ {
name = currtok; name = currtok;
eat( TokType::Identifier ); eat( TokType::Identifier );
// ( <ValueType> <Name> // ( <Macro> <ValueType> <Name>
}
if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) // In template captures you can have a typename have direct assignment without a name
// typename = typename ...
// Which would result in a static value type from a struct expansion (traditionally)
if ( ( name.Text || use_template_capture ) && bitfield_is_equal( u32, currtok.Flags, TF_Assign ) )
{ {
eat( TokType::Operator ); eat( TokType::Operator );
// ( <ValueType> <Name> = // ( <Macro> <ValueType> <Name> =
Token value_tok = currtok; Token value_tok = currtok;
@ -9373,21 +9451,32 @@ namespace parser
return CodeInvalid; return CodeInvalid;
} }
while ( left && currtok.Type != TokType::Comma && currtok.Type != TokType::Capture_End ) s32 capture_level = 0;
s32 template_level = 0;
while ( left && (currtok.Type != TokType::Comma) && template_level >= 0 && CheckEndParams() || capture_level > 0 || template_level > 0 )
{ {
if (currtok.Text[ 0 ] == '<')
++ template_level;
if (currtok.Text[ 0 ] == '>')
-- template_level;
if (currtok.Type == TokType::Operator && currtok.Text[1] == '>')
-- template_level;
if ( currtok.Type == ETokType::Capture_Start)
++ capture_level;
if ( currtok.Type == ETokType::Capture_End)
-- capture_level;
value_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )value_tok.Text; value_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )value_tok.Text;
eat( currtok.Type ); eat( currtok.Type );
} }
value = untyped_str( strip_formatting( value_tok, strip_formatting_dont_preserve_newlines ) ); value = untyped_str( strip_formatting( value_tok, strip_formatting_dont_preserve_newlines ) );
// ( <ValueType> <Name> = <Expression> // ( <Macro> <ValueType> <Name> = <Expression>
} }
} }
}
else {
macro = parse_simple_preprocess(ETokType::Preprocess_Macro);
// ( <Macro>
}
CodeParam result = ( CodeParam )make_code(); CodeParam result = ( CodeParam )make_code();
result->Type = Parameters; result->Type = Parameters;
@ -9404,10 +9493,10 @@ namespace parser
result->NumEntries++; result->NumEntries++;
while ( left && use_template_capture ? currtok.Type != TokType::Operator && currtok.Text[ 0 ] != '>' : currtok.Type != TokType::Capture_End ) while ( check(TokType::Comma) )
{ {
eat( TokType::Comma ); eat( TokType::Comma );
// ( <ValueType> <Name> = <Expression>, // ( <Macro> <ValueType> <Name> = <Expression>,
Code type = { nullptr }; Code type = { nullptr };
Code value = { nullptr }; Code value = { nullptr };
@ -9417,18 +9506,27 @@ namespace parser
eat( TokType::Varadic_Argument ); eat( TokType::Varadic_Argument );
result.append( param_varadic ); result.append( param_varadic );
continue; continue;
// ( <ValueType> <Name> = <Expression>, ... // ( <Macro> <ValueType> <Name> = <Expression>, ...
} }
if ( ! check(TokType::Preprocess_Macro)) // Ex: Unreal has this type of macro: vvvvvvvvv
// COREUOBJECT_API void CallFunction( FFrame& Stack, RESULT_DECL, UFunction* Function );
// and: vvvv
// AddComponentByClass(UPARAM(meta = (AllowAbstract = "false")) TSubclassOf<UActorComponent> Class, bool bManualAttachment, ...
if ( check(TokType::Preprocess_Macro))
{ {
type = parse_type(); macro = parse_simple_preprocess(ETokType::Preprocess_Macro);
// ( <Macro>
}
if ( currtok.Type != TokType::Comma )
{
type = parse_type( use_template_capture );
if ( type == Code::Invalid ) if ( type == Code::Invalid )
{ {
Context.pop(); Context.pop();
return CodeInvalid; return CodeInvalid;
} }
// ( <ValueType> <Name> = <Expression>, <ValueType> // ( <Macro> <ValueType> <Name> = <Expression>, <Macro> <ValueType>
name = { nullptr, 0, TokType::Invalid, false }; name = { nullptr, 0, TokType::Invalid, false };
@ -9436,12 +9534,16 @@ namespace parser
{ {
name = currtok; name = currtok;
eat( TokType::Identifier ); eat( TokType::Identifier );
// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> // ( <Macro> <ValueType> <Name> = <Expression>, <Macro> <ValueType> <Name>
}
if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) // In template captures you can have a typename have direct assignment without a name
// typename = typename ...
// Which would result in a static value type from a struct expansion (traditionally)
if ( ( name.Text || use_template_capture ) && bitfield_is_equal( u32, currtok.Flags, TF_Assign ) )
{ {
eat( TokType::Operator ); eat( TokType::Operator );
// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = // ( <Macro> <ValueType> <Name> = <Expression>, <Macro> <ValueType> <Name> =
Token value_tok = currtok; Token value_tok = currtok;
@ -9452,21 +9554,36 @@ namespace parser
return CodeInvalid; return CodeInvalid;
} }
while ( left && currtok.Type != TokType::Comma && currtok.Type != TokType::Capture_End ) s32 capture_level = 0;
s32 template_level = 0;
while ( left
&& currtok.Type != TokType::Comma
&& template_level >= 0
&& CheckEndParams()
|| capture_level > 0 || template_level > 0 )
{ {
if (currtok.Text[ 0 ] == '<')
++ template_level;
if (currtok.Text[ 0 ] == '>')
-- template_level;
if (currtok.Type == TokType::Operator && currtok.Text[1] == '>')
-- template_level;
if ( currtok.Type == ETokType::Capture_Start)
++ capture_level;
if ( currtok.Type == ETokType::Capture_End)
-- capture_level;
value_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )value_tok.Text; value_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )value_tok.Text;
eat( currtok.Type ); eat( currtok.Type );
} }
value = untyped_str( strip_formatting( value_tok, strip_formatting_dont_preserve_newlines ) ); value = untyped_str( strip_formatting( value_tok, strip_formatting_dont_preserve_newlines ) );
// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = <Expression> // ( <Macro> <ValueType> <Name> = <Expression>, <Macro> <ValueType> <Name> = <Expression>
} }
} // ( <Macro> <ValueType> <Name> = <Expression>, <Macro> <ValueType> <Name> = <Expression>, ..
// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = <Expression>, ..
}
else {
macro = parse_simple_preprocess(ETokType::Preprocess_Macro);
// ( ..., <Macro>
} }
CodeParam param = ( CodeParam )make_code(); CodeParam param = ( CodeParam )make_code();
@ -9487,7 +9604,7 @@ namespace parser
if ( ! use_template_capture ) if ( ! use_template_capture )
eat( TokType::Capture_End ); eat( TokType::Capture_End );
// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = <Expression>, .. ) // ( <Macro> <ValueType> <Name> = <Expression>, <Macro> <ValueType> <Name> = <Expression>, .. )
else else
{ {
@ -9498,7 +9615,7 @@ namespace parser
return CodeInvalid; return CodeInvalid;
} }
eat( TokType::Operator ); eat( TokType::Operator );
// < <ValueType> <Name> = <Expression>, <ValueType> <Name> = <Expression>, .. > // < <Macro> <ValueType> <Name> = <Expression>, <Macro> <ValueType> <Name> = <Expression>, .. >
} }
Context.pop(); Context.pop();
@ -9669,18 +9786,22 @@ namespace parser
// < // <
s32 level = 0; s32 level = 0;
while ( left && ( currtok.Text[ 0 ] != '>' || level > 0 ) ) while ( left && level >= 0 && ( currtok.Text[ 0 ] != '>' || level > 0 ) )
{ {
if ( currtok.Text[ 0 ] == '<' ) if ( currtok.Text[ 0 ] == '<' )
level++; level++;
if ( currtok.Text[ 0 ] == '>' ) if ( currtok.Text[ 0 ] == '>' )
level--; level--;
if ( currtok.Type == TokType::Operator && currtok.Text[1] == '>')
level--;
eat( currtok.Type ); eat( currtok.Type );
} }
// < <Content> // < <Content>
// Due to the >> token, this could have been eaten early...
if (level == 0)
eat( TokType::Operator ); eat( TokType::Operator );
// < <Content> > // < <Content> >
@ -9955,6 +10076,9 @@ namespace parser
// <Name> ( <Parameters> ) : <InitializerList> // <Name> ( <Parameters> ) : <InitializerList>
initializer_list = untyped_str( initializer_list_tok ); initializer_list = untyped_str( initializer_list_tok );
// TODO(Ed): Constructors can have post-fix specifiers
body = parse_function_body(); body = parse_function_body();
// <Name> ( <Parameters> ) : <InitializerList> { <Body> } // <Name> ( <Parameters> ) : <InitializerList> { <Body> }
} }
@ -9978,8 +10102,6 @@ namespace parser
// <Name> ( <Parameters> ); <InlineCmt> // <Name> ( <Parameters> ); <InlineCmt>
} }
// TODO(Ed): Constructors can have post-fix specifiers
CodeConstructor result = ( CodeConstructor )make_code(); CodeConstructor result = ( CodeConstructor )make_code();
result->Name = get_cached_string(identifier); result->Name = get_cached_string(identifier);
@ -10243,10 +10365,18 @@ namespace parser
} }
// <Name> = <Expression> // <Name> = <Expression>
// Unreal UMETA macro support
if ( currtok.Type == TokType::Preprocess_Macro )
{
eat( TokType::Preprocess_Macro );
// <Name> = <Expression> <Macro>
}
if ( currtok.Type == TokType::Comma ) if ( currtok.Type == TokType::Comma )
{ {
eat( TokType::Comma ); eat( TokType::Comma );
// <Name> = <Expression>, // <Name> = <Expression> <Macro>,
} }
entry.Length = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )entry.Text; entry.Length = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )entry.Text;
@ -10800,7 +10930,7 @@ namespace parser
s32 NumSpecifiers = 0; s32 NumSpecifiers = 0;
attributes = parse_attributes(); attributes = parse_attributes();
// <export> template< <Parameters> > <Attributes> <specifiers. // <export> template< <Parameters> > <Attributes>
// Specifiers Parsing // Specifiers Parsing
{ {
@ -10850,8 +10980,121 @@ namespace parser
// <export> template< <Parameters> > <Attributes> <Specifiers> // <export> template< <Parameters> > <Attributes> <Specifiers>
} }
// TODO(Ed) : Port over user-defined operator cast detection from // Possible constructor implemented at global file scope.
//parse_global_nspace (which is a source defintion version) or parse_class_struct_body (which is an inline class definition) {
/*
To check if a definition is for a constructor we can go straight to the opening parenthesis for its parameters
From There we work backwards to see if we come across two identifiers with the same name between an member access
:: operator, there can be template parameters on the left of the :: so we ignore those.
Whats important is that its back to back.
This has multiple possible faults. What we parse using this method may not filter out if something has a "return type"
This is bad since technically you could have a namespace nested into another namespace with the same name.
If this awful pattern is done the only way to distiguish with this coarse parse is to know there is no return type defined.
We could fix this by attempting to parse a type, but we would have to have a way to have it soft fail and rollback.
*/
TokArray tokens = Context.Tokens;
s32 idx = tokens.Idx;
s32 level = 0;
for ( ; idx < tokens.Arr.num(); idx++ )
{
if ( level == 0 && tokens[ idx ].Type == TokType::Capture_Start )
break;
}
-- idx;
Token tok_right = tokens[idx];
Token tok_left = NullToken;
if (tok_right.Type != TokType::Identifier)
{
// We're not dealing with a constructor if there is no identifier right before the opening of a parameter's scope.
break;
}
-- idx;
tok_left = tokens[idx];
// <Attributes> <Specifiers> ... <Identifier>
if ( tok_left.Type != TokType::Access_StaticSymbol )
break;
-- idx;
tok_left = tokens[idx];
// <Attributes> <Specifiers> ... :: <Identifier>
// We search toward the left until we find the next valid identifier
s32 capture_level = 0;
s32 template_level = 0;
while ( idx != tokens.Idx )
{
if (tok_left.Text[ 0 ] == '<')
++ template_level;
if (tok_left.Text[ 0 ] == '>')
-- template_level;
if (tok_left.Type == TokType::Operator && tok_left.Text[1] == '>')
-- template_level;
if ( template_level != 0 && tok_left.Type == ETokType::Capture_Start)
++ capture_level;
if ( template_level != 0 && tok_left.Type == ETokType::Capture_End)
-- capture_level;
if ( capture_level == 0 && template_level == 0 && tok_left.Type == TokType::Identifier )
break;
-- idx;
tok_left = tokens[idx];
}
bool is_same = str_compare( tok_right.Text, tok_left.Text, tok_right.Length ) == 0;
if (tok_left.Type == TokType::Identifier && is_same)
{
// We have found the pattern we desired
// <Name> :: <Name> (
definition = parse_constructor( specifiers );
// <Attributes> <Specifiers> <Name> :: <Name> <Type> () { ... }
break;
}
}
// User Defined operator casts
{
bool found_operator_cast_outside_class_implmentation = false;
s32 idx = Context.Tokens.Idx;
for ( ; idx < Context.Tokens.Arr.num(); idx++ )
{
Token tok = Context.Tokens[ idx ];
if ( tok.Type == TokType::Identifier )
{
idx++;
tok = Context.Tokens[ idx ];
if ( tok.Type == TokType::Access_StaticSymbol )
continue;
break;
}
if ( tok.Type == TokType::Decl_Operator )
found_operator_cast_outside_class_implmentation = true;
break;
}
if ( found_operator_cast_outside_class_implmentation )
{
definition = parse_operator_cast( specifiers );
// <Attributes> <Specifiers> <Name> :: operator <Type> () { ... }
break;
}
}
definition = parse_operator_function_or_variable( expects_function, attributes, specifiers ); definition = parse_operator_function_or_variable( expects_function, attributes, specifiers );
// <export> template< <Parameters> > <Attributes> <Specifiers> ... // <export> template< <Parameters> > <Attributes> <Specifiers> ...
@ -10881,7 +11124,7 @@ namespace parser
The excess whitespace cannot be stripped however, because there is no semantic awareness within the first capture group. The excess whitespace cannot be stripped however, because there is no semantic awareness within the first capture group.
*/ */
internal CodeType parse_type( bool* typedef_is_function ) internal CodeType parse_type( bool from_template, bool* typedef_is_function )
{ {
push_scope(); push_scope();
@ -10921,8 +11164,16 @@ namespace parser
return CodeInvalid; return CodeInvalid;
} }
if ( from_template && currtok.Type == TokType::Decl_Class )
{
// If a value's type is being parsed from a template, class can be used instead of typename.
name = currtok;
eat(TokType::Decl_Class);
// <class>
}
// All kinds of nonsense can makeup a type signature, first we check for a in-place definition of a class, enum, struct, or union // All kinds of nonsense can makeup a type signature, first we check for a in-place definition of a class, enum, struct, or union
if ( currtok.Type == TokType::Decl_Class || currtok.Type == TokType::Decl_Enum || currtok.Type == TokType::Decl_Struct else if ( currtok.Type == TokType::Decl_Class || currtok.Type == TokType::Decl_Enum || currtok.Type == TokType::Decl_Struct
|| currtok.Type == TokType::Decl_Union ) || currtok.Type == TokType::Decl_Union )
{ {
eat( currtok.Type ); eat( currtok.Type );
@ -11429,7 +11680,7 @@ namespace parser
} }
} }
else else
type = parse_type( &is_function ); type = parse_type( false, &is_function );
// <ModuleFalgs> typedef <UnderlyingType> // <ModuleFalgs> typedef <UnderlyingType>
if ( check( TokType::Identifier ) ) if ( check( TokType::Identifier ) )

View File

@ -121,6 +121,20 @@
</Expand> </Expand>
</Type> </Type>
<Type Name="gen::AST_Constructor">
<DisplayString>{Name} Type: {Type}</DisplayString>
<Expand>
<Item Name="InlineCmt">InlineCmt</Item>
<Item Name="Specs">Specs</Item>
<Item Name="InitializerList">InitializerList</Item>
<Item Name="Params">Params</Item>
<Item Name="Body">Body</Item>
<Item Name="Parent">Parent</Item>
<Item Name="Prev">Prev</Item>
<Item Name="Next">Next</Item>
</Expand>
</Type>
<Type Name="gen::AST_Class"> <Type Name="gen::AST_Class">
<DisplayString>{Name} Type: {Type}</DisplayString> <DisplayString>{Name} Type: {Type}</DisplayString>
<Expand> <Expand>