From a70f0e10d37a94b68b393c73074309c0b8fd76e2 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Mon, 15 Apr 2024 23:09:46 -0400 Subject: [PATCH] Can parse AActor with gencpp --- .../Gasa/AbilitySystem/GasaAttributeSet.cpp | 2 +- Project/Source/GasaGen/GasaGen.cpp | 136 ++++- .../GasaGen/GasaGen_UGasaAttributeSet.cpp | 2 +- Project/Source/GasaGen/gen.cpp | 565 +++++++++++++----- scripts/gencpp.natvis | 14 + 5 files changed, 529 insertions(+), 190 deletions(-) diff --git a/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.cpp b/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.cpp index 832f52d..bcc6fff 100644 --- a/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.cpp +++ b/Project/Source/Gasa/AbilitySystem/GasaAttributeSet.cpp @@ -9,7 +9,7 @@ UGasaAttributeSet::UGasaAttributeSet() { InitHealth( 100.f ); InitMaxHealth( 100.f ); - InitMana( ( 50.f ) ); + InitMana( 50.f ); InitMaxMana( 50.f ); } diff --git a/Project/Source/GasaGen/GasaGen.cpp b/Project/Source/GasaGen/GasaGen.cpp index 1ab53d3..7b0d8ff 100644 --- a/Project/Source/GasaGen/GasaGen.cpp +++ b/Project/Source/GasaGen/GasaGen.cpp @@ -22,43 +22,55 @@ int gen_main() umeta_generated_body = code_str( GENERATED_BODY() ); gasa_api = code_str( GASA_API ); - 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"); - - 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"); + 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_ufunction = txt("UFUNCTION("); + StrC str_uproperty = txt("UPROPERTY("); + 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_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"); + 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_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_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_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_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)); @@ -68,11 +80,31 @@ int gen_main() PreprocessorDefines.append( get_cached_string(str_result_decl)); PreprocessorDefines.append( get_cached_string(str_property_binding_implementation)); 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 \ "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 }); log_fmt("\n\n"); @@ -93,22 +125,63 @@ int gen_main() case CodeT::Function_Fwd: 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; } } } } +#endif #define path_UObject \ R"(C:\projects\Unreal\Surgo\UE\Engine\Source\Runtime\CoreUObject\Public\UObject\Object.h)" +#if 0 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 ) + { + 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() ) + { + 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 ) { @@ -121,8 +194,8 @@ int gen_main() switch ( class_code->Type ) { case CodeT::Variable: - case CodeT::Function: - case CodeT::Function_Fwd: + // case CodeT::Function: + // case CodeT::Function_Fwd: if ( 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(); return 0; diff --git a/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp b/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp index ef5b58e..3475567 100644 --- a/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp +++ b/Project/Source/GasaGen/GasaGen_UGasaAttributeSet.cpp @@ -114,7 +114,7 @@ void gen_UGasaAttributeSet() { InitHealth( 100.f ); InitMaxHealth( 100.f ); - InitMana(( 50.f )); + InitMana( 50.f ); InitMaxMana( 50.f ); } )); diff --git a/Project/Source/GasaGen/gen.cpp b/Project/Source/GasaGen/gen.cpp index 6151d8e..66efc34 100644 --- a/Project/Source/GasaGen/gen.cpp +++ b/Project/Source/GasaGen/gen.cpp @@ -1588,7 +1588,12 @@ void CodeConstructor::to_string_def( String& result ) void CodeConstructor::to_string_fwd( String& result ) { AST* ClassStructParent = ast->Parent->Parent; - result.append( ClassStructParent->Name ); + if (ClassStructParent) { + result.append( ClassStructParent->Name ); + } + else { + result.append( ast->Name ); + } if ( ast->Params ) result.append_fmt( "( %S )", ast->Params.to_string() ); @@ -2273,24 +2278,24 @@ void CodeParam::to_string( String& result ) { if ( ast->Macro ) { - // Were using the convention that if the value type is a macro we ignore everything else // Related to parsing: ( , ... ) result.append( ast->Macro.ast->Content ); - return; + // Could also be: ( , ... ) } if ( ast->Name ) { if ( ast->ValueType.ast == nullptr ) - result.append_fmt( "%S", ast->Name ); + result.append_fmt( " %S", ast->Name ); else - result.append_fmt( "%S %S", ast->ValueType.to_string(), ast->Name ); + result.append_fmt( " %S %S", ast->ValueType.to_string(), ast->Name ); + } - else - result.append_fmt( "%S", ast->ValueType.to_string() ); + else if ( ast->ValueType ) + result.append_fmt( " %S", ast->ValueType.to_string() ); if ( ast->Value ) - result.append_fmt( "= %S", ast->Value.to_string() ); + result.append_fmt( " = %S", ast->Value.to_string() ); if ( ast->NumEntries - 1 > 0 ) { @@ -5599,7 +5604,14 @@ namespace parser { 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 { @@ -5700,6 +5712,11 @@ namespace parser __Attributes_Start, API_Export, API_Import, + UE_DEPRECATED, + UMG_API, + COREUOBJECT_API, + ENGINE_API, + GASA_API, NumTokens }; @@ -5803,6 +5820,11 @@ namespace parser { sizeof( "__attrib_start__" ), "__attrib_start__" }, { sizeof( "GEN_API_Export_Code" ), "GEN_API_Export_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 ]; } @@ -5982,7 +6004,7 @@ namespace parser } }; - global Arena_64KB defines_map_arena; + global Arena_128KB defines_map_arena; global HashTable< StrC > defines; global Array< Token > Tokens; @@ -6283,6 +6305,11 @@ namespace parser token.Flags |= TF_AccessSpecifier; } + if ( type > TokType::__Attributes_Start ) + { + token.Flags |= TF_Attribute; + } + if ( type == ETokType::Decl_Extern_Linkage ) { SkipWhitespace(); @@ -7174,7 +7201,7 @@ namespace parser { 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 ); } @@ -7256,7 +7283,7 @@ namespace parser internal CodeStruct parse_struct( bool inplace_def = false ); internal CodeVar parse_variable(); 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 CodeUnion parse_union( bool inplace_def = false ); internal CodeUsing parse_using(); @@ -7594,72 +7621,90 @@ namespace parser { push_scope(); - Token start = NullToken; + Token start = currtok; s32 len = 0; - if ( check( TokType::Attribute_Open ) ) + // 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() ) { - eat( TokType::Attribute_Open ); - // [[ + if ( check( 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( TokType::Attribute_Close ); + // [[ ]] + + len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text; + } + else if ( check( TokType::Decl_GNU_Attribute ) ) + { + eat( TokType::Decl_GNU_Attribute ); + eat( TokType::Capture_Start ); + eat( TokType::Capture_Start ); + // __attribute__(( + + while ( left && currtok.Type != TokType::Capture_End ) + { + eat( currtok.Type ); + } + // __attribute__(( + + eat( TokType::Capture_End ); + eat( TokType::Capture_End ); + // __attribute__(( )) + + len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text; + } + else if ( check( TokType::Decl_MSVC_Attribute ) ) + { + eat( TokType::Decl_MSVC_Attribute ); + eat( TokType::Capture_Start ); + // __declspec( + + while ( left && currtok.Type != TokType::Capture_End ) + { + eat( currtok.Type ); + } + // __declspec( + + eat( TokType::Capture_End ); + // __declspec( ) + + len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text; + } + else if ( currtok.is_attribute() ) { eat( currtok.Type ); + // + + // 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; + // ( ... ) } - // [[ - - eat( TokType::Attribute_Close ); - // [[ ]] - - s32 len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text; - } - - else if ( check( TokType::Decl_GNU_Attribute ) ) - { - eat( TokType::Decl_GNU_Attribute ); - eat( TokType::Capture_Start ); - eat( TokType::Capture_Start ); - // __attribute__(( - - start = currtok; - while ( left && currtok.Type != TokType::Capture_End ) - { - eat( currtok.Type ); - } - // __attribute__(( - - eat( TokType::Capture_End ); - eat( TokType::Capture_End ); - // __attribute__(( )) - - s32 len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text; - } - - else if ( check( TokType::Decl_MSVC_Attribute ) ) - { - eat( TokType::Decl_MSVC_Attribute ); - eat( TokType::Capture_Start ); - // __declspec( - - start = currtok; - while ( left && currtok.Type != TokType::Capture_End ) - { - eat( currtok.Type ); - } - // __declspec( - - eat( TokType::Capture_End ); - // __declspec( ) - - s32 len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text; - } - - else if ( currtok.is_attribute() ) - { - eat( currtok.Type ); - s32 len = start.Length; - // } if ( len > 0 ) @@ -8036,6 +8081,24 @@ namespace parser } // + 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 = more_attributes; + } + if ( currtok.Type == TokType::Operator && currtok.Text[ 0 ] == '~' ) { member = parse_destructor( specifiers ); @@ -8451,6 +8514,9 @@ namespace parser result->Type = Function_Fwd; } + if ( attributes ) + result->Attributes = attributes; + if ( specifiers ) result->Specs = specifiers; @@ -9339,54 +9405,77 @@ 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' + #define CheckEndParams() \ + (use_template_capture ? (currtok.Text[ 0 ] != '>') : (currtok.Type != TokType::Capture_End)) - 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 Class, bool bManualAttachment, ... + if ( check(TokType::Preprocess_Macro)) { - type = parse_type(); + macro = parse_simple_preprocess(ETokType::Preprocess_Macro); + // ( + } + if ( currtok.Type != TokType::Comma ) + { + type = parse_type( use_template_capture ); if ( type == Code::Invalid ) { Context.pop(); return CodeInvalid; } - // ( + // ( if ( check( TokType::Identifier ) ) { name = currtok; eat( TokType::Identifier ); - // ( - - if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) - { - eat( TokType::Operator ); - // ( = - - Token value_tok = currtok; - - if ( currtok.Type == TokType::Comma ) - { - log_failure( "Expected value after assignment operator\n%s.", Context.to_string() ); - Context.pop(); - return CodeInvalid; - } - - while ( left && currtok.Type != TokType::Comma && currtok.Type != TokType::Capture_End ) - { - value_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )value_tok.Text; - eat( currtok.Type ); - } - - value = untyped_str( strip_formatting( value_tok, strip_formatting_dont_preserve_newlines ) ); - // ( = - } + // ( + } + + // 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 ); + // ( = + + Token value_tok = currtok; + + if ( currtok.Type == TokType::Comma ) + { + log_failure( "Expected value after assignment operator\n%s.", Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + 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; + eat( currtok.Type ); + } + + value = untyped_str( strip_formatting( value_tok, strip_formatting_dont_preserve_newlines ) ); + // ( = } - } - else { - macro = parse_simple_preprocess(ETokType::Preprocess_Macro); - // ( } CodeParam result = ( CodeParam )make_code(); @@ -9404,10 +9493,10 @@ namespace parser 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 ); - // ( = , + // ( = , Code type = { nullptr }; Code value = { nullptr }; @@ -9417,18 +9506,27 @@ namespace parser eat( TokType::Varadic_Argument ); result.append( param_varadic ); continue; - // ( = , ... + // ( = , ... } - 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 Class, bool bManualAttachment, ... + if ( check(TokType::Preprocess_Macro)) { - type = parse_type(); + macro = parse_simple_preprocess(ETokType::Preprocess_Macro); + // ( + } + if ( currtok.Type != TokType::Comma ) + { + type = parse_type( use_template_capture ); if ( type == Code::Invalid ) { Context.pop(); return CodeInvalid; } - // ( = , + // ( = , name = { nullptr, 0, TokType::Invalid, false }; @@ -9436,37 +9534,56 @@ namespace parser { name = currtok; eat( TokType::Identifier ); - // ( = , - - if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) ) - { - eat( TokType::Operator ); - // ( = , = - - Token value_tok = currtok; - - if ( currtok.Type == TokType::Comma ) - { - log_failure( "Expected value after assignment operator\n%s", Context.to_string() ); - Context.pop(); - return CodeInvalid; - } - - while ( left && currtok.Type != TokType::Comma && currtok.Type != TokType::Capture_End ) - { - value_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )value_tok.Text; - eat( currtok.Type ); - } - - value = untyped_str( strip_formatting( value_tok, strip_formatting_dont_preserve_newlines ) ); - // ( = , = - } + // ( = , } - // ( = , = , .. - } - else { - macro = parse_simple_preprocess(ETokType::Preprocess_Macro); - // ( ..., + + // 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 ); + // ( = , = + + Token value_tok = currtok; + + if ( currtok.Type == TokType::Comma ) + { + log_failure( "Expected value after assignment operator\n%s", Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + 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; + eat( currtok.Type ); + } + + value = untyped_str( strip_formatting( value_tok, strip_formatting_dont_preserve_newlines ) ); + // ( = , = + } + // ( = , = , .. } CodeParam param = ( CodeParam )make_code(); @@ -9487,7 +9604,7 @@ namespace parser if ( ! use_template_capture ) eat( TokType::Capture_End ); - // ( = , = , .. ) + // ( = , = , .. ) else { @@ -9498,7 +9615,7 @@ namespace parser return CodeInvalid; } eat( TokType::Operator ); - // < = , = , .. > + // < = , = , .. > } Context.pop(); @@ -9669,19 +9786,23 @@ namespace parser // < s32 level = 0; - while ( left && ( currtok.Text[ 0 ] != '>' || level > 0 ) ) + while ( left && level >= 0 && ( currtok.Text[ 0 ] != '>' || level > 0 ) ) { if ( currtok.Text[ 0 ] == '<' ) level++; if ( currtok.Text[ 0 ] == '>' ) level--; + if ( currtok.Type == TokType::Operator && currtok.Text[1] == '>') + level--; eat( currtok.Type ); } // < - eat( TokType::Operator ); + // Due to the >> token, this could have been eaten early... + if (level == 0) + eat( TokType::Operator ); // < > // Extend length of name to last token @@ -9955,7 +10076,10 @@ namespace parser // ( ) : initializer_list = untyped_str( initializer_list_tok ); - body = parse_function_body(); + + // TODO(Ed): Constructors can have post-fix specifiers + + body = parse_function_body(); // ( ) : { } } else if ( check( TokType::BraceCurly_Open ) ) @@ -9978,8 +10102,6 @@ namespace parser // ( ); } - // TODO(Ed): Constructors can have post-fix specifiers - CodeConstructor result = ( CodeConstructor )make_code(); result->Name = get_cached_string(identifier); @@ -10243,10 +10365,18 @@ namespace parser } // = + + // Unreal UMETA macro support + if ( currtok.Type == TokType::Preprocess_Macro ) + { + eat( TokType::Preprocess_Macro ); + // = + } + if ( currtok.Type == TokType::Comma ) { eat( TokType::Comma ); - // = , + // = , } entry.Length = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )entry.Text; @@ -10799,8 +10929,8 @@ namespace parser SpecifierT specs_found[ 16 ] { ESpecifier::NumSpecifiers }; s32 NumSpecifiers = 0; - attributes = parse_attributes(); - // template< > template< > // Specifiers Parsing { @@ -10850,8 +10980,121 @@ namespace parser // template< > } - // 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) + // Possible constructor implemented at global file scope. + { + /* + 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]; + // ... + + if ( tok_left.Type != TokType::Access_StaticSymbol ) + break; + + -- idx; + tok_left = tokens[idx]; + // ... :: + + // 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 + // :: ( + + definition = parse_constructor( specifiers ); + // :: () { ... } + 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 ); + // :: operator () { ... } + break; + } + } definition = parse_operator_function_or_variable( expects_function, attributes, specifiers ); // template< > ... @@ -10881,7 +11124,7 @@ namespace parser 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(); @@ -10921,8 +11164,16 @@ namespace parser 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); + // + } + // 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 ) { eat( currtok.Type ); @@ -11429,7 +11680,7 @@ namespace parser } } else - type = parse_type( &is_function ); + type = parse_type( false, &is_function ); // typedef if ( check( TokType::Identifier ) ) diff --git a/scripts/gencpp.natvis b/scripts/gencpp.natvis index 00222f9..7b94b25 100644 --- a/scripts/gencpp.natvis +++ b/scripts/gencpp.natvis @@ -121,6 +121,20 @@ + + {Name} Type: {Type} + + InlineCmt + Specs + InitializerList + Params + Body + Parent + Prev + Next + + + {Name} Type: {Type}