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 );
InitMaxHealth( 100.f );
InitMana( ( 50.f ) );
InitMana( 50.f );
InitMaxMana( 50.f );
}

View File

@ -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<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 )
{
@ -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;

View File

@ -114,7 +114,7 @@ void gen_UGasaAttributeSet()
{
InitHealth( 100.f );
InitMaxHealth( 100.f );
InitMana(( 50.f ));
InitMana( 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 )
{
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: ( <macro>, ... )
result.append( ast->Macro.ast->Content );
return;
// Could also be: ( <macro> <type <name>, ... )
}
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 );
}
// [[ <Content>
eat( TokType::Attribute_Close );
// [[ <Content> ]]
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__(( <Content>
eat( TokType::Capture_End );
eat( TokType::Capture_End );
// __attribute__(( <Content> ))
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( <Content>
eat( TokType::Capture_End );
// __declspec( <Content> )
len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text;
}
else if ( currtok.is_attribute() )
{
eat( currtok.Type );
// <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> ( ... )
}
// [[ <Content>
eat( TokType::Attribute_Close );
// [[ <Content> ]]
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__(( <Content>
eat( TokType::Capture_End );
eat( TokType::Capture_End );
// __attribute__(( <Content> ))
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( <Content>
eat( TokType::Capture_End );
// __declspec( <Content> )
s32 len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text;
}
else if ( currtok.is_attribute() )
{
eat( currtok.Type );
s32 len = start.Length;
// <Attribute>
}
if ( len > 0 )
@ -8036,6 +8081,24 @@ namespace parser
}
// <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 ] == '~' )
{
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<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 )
{
Context.pop();
return CodeInvalid;
}
// ( <ValueType>
// ( <Macro> <ValueType>
if ( check( TokType::Identifier ) )
{
name = currtok;
eat( TokType::Identifier );
// ( <ValueType> <Name>
if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) )
{
eat( TokType::Operator );
// ( <ValueType> <Name> =
Token value_tok = currtok;
if ( currtok.Type == TokType::Comma )
{
log_failure( "Expected value after assignment operator\n%s.", Context.to_string() );
Context.pop();
return CodeInvalid;
}
while ( left && currtok.Type != TokType::Comma && currtok.Type != TokType::Capture_End )
{
value_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )value_tok.Text;
eat( currtok.Type );
}
value = untyped_str( strip_formatting( value_tok, strip_formatting_dont_preserve_newlines ) );
// ( <ValueType> <Name> = <Expression>
}
// ( <Macro> <ValueType> <Name>
}
// 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 );
// ( <Macro> <ValueType> <Name> =
Token value_tok = currtok;
if ( currtok.Type == TokType::Comma )
{
log_failure( "Expected value after assignment operator\n%s.", Context.to_string() );
Context.pop();
return CodeInvalid;
}
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 ) );
// ( <Macro> <ValueType> <Name> = <Expression>
}
}
else {
macro = parse_simple_preprocess(ETokType::Preprocess_Macro);
// ( <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 );
// ( <ValueType> <Name> = <Expression>,
// ( <Macro> <ValueType> <Name> = <Expression>,
Code type = { nullptr };
Code value = { nullptr };
@ -9417,18 +9506,27 @@ namespace parser
eat( TokType::Varadic_Argument );
result.append( param_varadic );
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 )
{
Context.pop();
return CodeInvalid;
}
// ( <ValueType> <Name> = <Expression>, <ValueType>
// ( <Macro> <ValueType> <Name> = <Expression>, <Macro> <ValueType>
name = { nullptr, 0, TokType::Invalid, false };
@ -9436,37 +9534,56 @@ namespace parser
{
name = currtok;
eat( TokType::Identifier );
// ( <ValueType> <Name> = <Expression>, <ValueType> <Name>
if ( bitfield_is_equal( u32, currtok.Flags, TF_Assign ) )
{
eat( TokType::Operator );
// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> =
Token value_tok = currtok;
if ( currtok.Type == TokType::Comma )
{
log_failure( "Expected value after assignment operator\n%s", Context.to_string() );
Context.pop();
return CodeInvalid;
}
while ( left && currtok.Type != TokType::Comma && currtok.Type != TokType::Capture_End )
{
value_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )value_tok.Text;
eat( currtok.Type );
}
value = untyped_str( strip_formatting( value_tok, strip_formatting_dont_preserve_newlines ) );
// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = <Expression>
}
// ( <Macro> <ValueType> <Name> = <Expression>, <Macro> <ValueType> <Name>
}
// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = <Expression>, ..
}
else {
macro = parse_simple_preprocess(ETokType::Preprocess_Macro);
// ( ..., <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 );
// ( <Macro> <ValueType> <Name> = <Expression>, <Macro> <ValueType> <Name> =
Token value_tok = currtok;
if ( currtok.Type == TokType::Comma )
{
log_failure( "Expected value after assignment operator\n%s", Context.to_string() );
Context.pop();
return CodeInvalid;
}
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 ) );
// ( <Macro> <ValueType> <Name> = <Expression>, <Macro> <ValueType> <Name> = <Expression>
}
// ( <Macro> <ValueType> <Name> = <Expression>, <Macro> <ValueType> <Name> = <Expression>, ..
}
CodeParam param = ( CodeParam )make_code();
@ -9487,7 +9604,7 @@ namespace parser
if ( ! use_template_capture )
eat( TokType::Capture_End );
// ( <ValueType> <Name> = <Expression>, <ValueType> <Name> = <Expression>, .. )
// ( <Macro> <ValueType> <Name> = <Expression>, <Macro> <ValueType> <Name> = <Expression>, .. )
else
{
@ -9498,7 +9615,7 @@ namespace parser
return CodeInvalid;
}
eat( TokType::Operator );
// < <ValueType> <Name> = <Expression>, <ValueType> <Name> = <Expression>, .. >
// < <Macro> <ValueType> <Name> = <Expression>, <Macro> <ValueType> <Name> = <Expression>, .. >
}
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 );
}
// < <Content>
eat( TokType::Operator );
// Due to the >> token, this could have been eaten early...
if (level == 0)
eat( TokType::Operator );
// < <Content> >
// Extend length of name to last token
@ -9955,7 +10076,10 @@ namespace parser
// <Name> ( <Parameters> ) : <InitializerList>
initializer_list = untyped_str( initializer_list_tok );
body = parse_function_body();
// TODO(Ed): Constructors can have post-fix specifiers
body = parse_function_body();
// <Name> ( <Parameters> ) : <InitializerList> { <Body> }
}
else if ( check( TokType::BraceCurly_Open ) )
@ -9978,8 +10102,6 @@ namespace parser
// <Name> ( <Parameters> ); <InlineCmt>
}
// 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
}
// <Name> = <Expression>
// Unreal UMETA macro support
if ( currtok.Type == TokType::Preprocess_Macro )
{
eat( TokType::Preprocess_Macro );
// <Name> = <Expression> <Macro>
}
if ( currtok.Type == TokType::Comma )
{
eat( TokType::Comma );
// <Name> = <Expression>,
// <Name> = <Expression> <Macro>,
}
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();
// <export> template< <Parameters> > <Attributes> <specifiers.
attributes = parse_attributes();
// <export> template< <Parameters> > <Attributes>
// Specifiers Parsing
{
@ -10850,8 +10980,121 @@ namespace parser
// <export> template< <Parameters> > <Attributes> <Specifiers>
}
// 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];
// <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 );
// <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.
*/
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);
// <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 );
// <ModuleFalgs> typedef <UnderlyingType>
if ( check( TokType::Identifier ) )

View File

@ -121,6 +121,20 @@
</Expand>
</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">
<DisplayString>{Name} Type: {Type}</DisplayString>
<Expand>