From 16bc66c80e72a6297d29b142a9dba2c8337f71e8 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Thu, 30 Jan 2025 13:25:56 -0500 Subject: [PATCH] Backporting changes done on UnrealGencpp repo Commits: ec77e8b - Fixes while parsing EditorEngine.h 5017429 - parse_complicated_definition fix when parsing Controller.h aac0dd5 - Add IRISCORE_API 049b59c - Support for attributes retated to an operator or function between the return type and the identifier/op (Thanks World.h...) 97d7e6d - Fix for attributes after name in using statements 9f204e7 - Support for final specifier on class & struct definitions f0698cc - Added support for Spec_Delete (= delete on functions and operators) [Part 3] 1f6650a - Added support for Spec_Delete (= delete on functions and operators) [Part 2] 06ac8da - Added support for Spec_Delete (= delete on functions and operators) --- base/components/ast.hpp | 2 +- base/components/ast_types.hpp | 4 +- base/components/code_serialization.cpp | 32 +++-- base/components/gen/especifier.hpp | 3 + base/components/interface.hpp | 1 + base/components/interface.upfront.cpp | 2 + base/components/lexer.cpp | 2 - base/components/parser.cpp | 162 +++++++++++++++++++------ base/enums/ESpecifier.csv | 1 + base/helpers/base_codegen.hpp | 1 + 10 files changed, 156 insertions(+), 54 deletions(-) diff --git a/base/components/ast.hpp b/base/components/ast.hpp index a7b83dd..f81353a 100644 --- a/base/components/ast.hpp +++ b/base/components/ast.hpp @@ -378,7 +378,7 @@ struct AST { Code InlineCmt; // Class, Constructor, Destructor, Enum, Friend, Functon, Operator, OpCast, Struct, Typedef, Using, Variable Code Attributes; // Class, Enum, Function, Struct, Typedef, Union, Using, Variable // TODO(Ed): Parameters can have attributes - Code Specs; // Destructor, Function, Operator, Typename, Variable + Code Specs; // Class, Destructor, Function, Operator, Struct, Typename, Variable union { Code InitializerList; // Constructor Code ParentType; // Class, Struct, ParentType->Next has a possible list of interfaces. diff --git a/base/components/ast_types.hpp b/base/components/ast_types.hpp index 0c33006..54a5763 100644 --- a/base/components/ast_types.hpp +++ b/base/components/ast_types.hpp @@ -100,7 +100,7 @@ struct AST_Class { CodeComment InlineCmt; // Only supported by forward declarations CodeAttributes Attributes; - char _PAD_SPECS_ [ sizeof(AST*) ]; + CodeSpecifiers Specs; // Support for final CodeTypename ParentType; char _PAD_PARAMS_[ sizeof(AST*) ]; CodeBody Body; @@ -970,7 +970,7 @@ struct AST_Struct { CodeComment InlineCmt; CodeAttributes Attributes; - char _PAD_SPECS_ [ sizeof(AST*) ]; + CodeSpecifiers Specs; // Support for final CodeTypename ParentType; char _PAD_PARAMS_[ sizeof(AST*) ]; CodeBody Body; diff --git a/base/components/code_serialization.cpp b/base/components/code_serialization.cpp index 67c086c..f739202 100644 --- a/base/components/code_serialization.cpp +++ b/base/components/code_serialization.cpp @@ -186,10 +186,16 @@ void class_to_strbuilder_def( CodeClass self, StrBuilder* result ) strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); } + if ( self->Name.Len ) + strbuilder_append_str( result, self->Name ); + + if (self->Specs && specifiers_has(self->Specs, Spec_Final) > -1) + strbuilder_append_str(result, txt(" final")); + if ( self->ParentType ) { Str access_level = access_spec_to_str( self->ParentAccess ); - strbuilder_append_fmt( result, "%S : %S %SB", self->Name, access_level, typename_to_strbuilder(self->ParentType) ); + strbuilder_append_fmt( result, " : %S %SB", self->Name, access_level, typename_to_strbuilder(self->ParentType) ); CodeTypename interface = cast(CodeTypename, self->ParentType->Next); if ( interface ) @@ -201,10 +207,6 @@ void class_to_strbuilder_def( CodeClass self, StrBuilder* result ) interface = interface->Next ? cast(CodeTypename, interface->Next) : NullCode; } } - else if ( self->Name.Len ) - { - strbuilder_append_str( result, self->Name ); - } if ( self->InlineCmt ) { @@ -646,10 +648,12 @@ void fn_to_strbuilder_fwd(CodeFn self, StrBuilder* result ) strbuilder_append_fmt( result, " %.*s", spec_str.Len, spec_str.Ptr ); } } - } - if ( self->Specs && specifiers_has(self->Specs, Spec_Pure ) >= 0 ) - strbuilder_append_str( result, txt(" = 0;") ); + if ( specifiers_has(self->Specs, Spec_Pure ) >= 0 ) + strbuilder_append_str( result, txt(" = 0") ); + else if ( specifiers_has(self->Specs, Spec_Delete ) >= 0 ) + strbuilder_append_str( result, txt(" = delete") ); + } // This is bodged in for now SOLEY for Unreal's PURE_VIRTUAL functional macro (I kept it open ended for other jank) if ( self->SuffixSpecs ) @@ -1096,11 +1100,17 @@ void struct_to_strbuilder_def( CodeStruct self, StrBuilder* result ) strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); } + if ( self->Name.Len ) + strbuilder_append_str( result, self->Name ); + + if (self->Specs && specifiers_has(self->Specs, Spec_Final)) + strbuilder_append_str( result, txt(" final")); + if ( self->ParentType ) { Str access_level = access_spec_to_str( self->ParentAccess ); - strbuilder_append_fmt( result, "%S : %S %SB", self->Name, access_level, typename_to_strbuilder(self->ParentType) ); + strbuilder_append_fmt( result, " : %S %SB", access_level, typename_to_strbuilder(self->ParentType) ); CodeTypename interface = cast(CodeTypename, self->ParentType->Next); if ( interface ) @@ -1112,10 +1122,6 @@ void struct_to_strbuilder_def( CodeStruct self, StrBuilder* result ) interface = interface->Next ? cast( CodeTypename, interface->Next) : NullCode; } } - else if ( self->Name.Len ) - { - strbuilder_append_str( result, self->Name ); - } if ( self->InlineCmt ) { diff --git a/base/components/gen/especifier.hpp b/base/components/gen/especifier.hpp index 3fd9a4a..201b895 100644 --- a/base/components/gen/especifier.hpp +++ b/base/components/gen/especifier.hpp @@ -33,6 +33,7 @@ enum Specifier : u32 Spec_NoExceptions, Spec_Override, Spec_Pure, + Spec_Delete, Spec_Volatile, Spec_NumSpecifiers, Spec_UnderlyingType = 0xffffffffu @@ -67,6 +68,7 @@ inline Str spec_to_str(Specifier type) { "noexcept", sizeof("noexcept") - 1 }, { "override", sizeof("override") - 1 }, { "= 0", sizeof("= 0") - 1 }, + { "= delete", sizeof("= delete") - 1 }, { "volatile", sizeof("volatile") - 1 }, }; return lookup[type]; @@ -81,6 +83,7 @@ inline bool spec_is_trailing(Specifier specifier) case Spec_NoExceptions: case Spec_Override: case Spec_Pure: + case Spec_Delete: case Spec_Volatile: return true; default: diff --git a/base/components/interface.hpp b/base/components/interface.hpp index a697201..bd5fbba 100644 --- a/base/components/interface.hpp +++ b/base/components/interface.hpp @@ -156,6 +156,7 @@ struct Opts_def_struct { CodeAttributes attributes; CodeTypename* interfaces; s32 num_interfaces; + CodeSpecifiers specifiers; // Only used for final specifier for now. ModuleFlag mflags; }; GEN_API CodeClass def_class( Str name, Opts_def_struct opts GEN_PARAM_DEFAULT ); diff --git a/base/components/interface.upfront.cpp b/base/components/interface.upfront.cpp index 4f7cba5..80fbeda 100644 --- a/base/components/interface.upfront.cpp +++ b/base/components/interface.upfront.cpp @@ -532,6 +532,7 @@ CodeClass def_class( Str name, Opts_def_struct p ) result->Name = cache_str( name ); result->ModuleFlags = p.mflags; result->Attributes = p.attributes; + result->Specs = p.specifiers; result->ParentAccess = p.parent_access; result->ParentType = p.parent; if ( p.body ) @@ -1065,6 +1066,7 @@ CodeStruct def_struct( Str name, Opts_def_struct p ) result->Type = CT_Struct_Fwd; } result->Attributes = p.attributes; + result->Specs = p.specifiers; result->ParentAccess = p.parent_access; result->ParentType = p.parent; diff --git a/base/components/lexer.cpp b/base/components/lexer.cpp index ec6aa7c..a1b762a 100644 --- a/base/components/lexer.cpp +++ b/base/components/lexer.cpp @@ -264,7 +264,6 @@ s32 lex_preprocessor_define( LexContext* ctx ) } // TODO(Ed): We need to to attempt to recover from a lex failure? -forceinline s32 lex_preprocessor_directive( LexContext* ctx ) { char const* hash = ctx->scanner; @@ -480,7 +479,6 @@ s32 lex_preprocessor_directive( LexContext* ctx ) return Lex_Continue; // Skip found token, its all handled here. } -forceinline void lex_found_token( LexContext* ctx ) { if ( ctx->token.Type != Tok_Invalid ) { diff --git a/base/components/parser.cpp b/base/components/parser.cpp index a41267c..a1905a4 100644 --- a/base/components/parser.cpp +++ b/base/components/parser.cpp @@ -717,6 +717,13 @@ Code parse_class_struct( TokType which, bool inplace_def ) } // + CodeSpecifiers specifiers = NullCode; + if ( check(Tok_Spec_Final)) { + specifiers = def_specifier(Spec_Final); + eat(Tok_Spec_Final); + } + // + local_persist char interface_arr_mem[ kilobytes(4) ] = {0}; Array(CodeTypename) interfaces; { @@ -728,22 +735,22 @@ Code parse_class_struct( TokType which, bool inplace_def ) if ( check( Tok_Assign_Classifer ) ) { eat( Tok_Assign_Classifer ); - // : + // : if ( tok_is_access_specifier(currtok) ) { access = tok_to_access_specifier(currtok); - // : + // : eat( currtok.Type ); } Token parent_tok = parse_identifier(nullptr); parent = def_type( parent_tok.Text ); - // : + // : while ( check(Tok_Comma) ) { eat( Tok_Comma ); - // : , + // : , if ( tok_is_access_specifier(currtok) ) { eat(currtok.Type); @@ -751,32 +758,32 @@ Code parse_class_struct( TokType which, bool inplace_def ) Token interface_tok = parse_identifier(nullptr); array_append( interfaces, def_type( interface_tok.Text ) ); - // : , ... + // : , ... } } if ( check( Tok_BraceCurly_Open ) ) { body = parse_class_struct_body( which, name ); } - // : , ... { } + // : , ... { } CodeComment inline_cmt = NullCode; if ( ! inplace_def ) { Token stmt_end = currtok; eat( Tok_Statement_End ); - // : , ... { }; + // : , ... { }; if ( currtok_noskip.Type == Tok_Comment && currtok_noskip.Line == stmt_end.Line ) inline_cmt = parse_comment(); - // : , ... { }; + // : , ... { }; } if ( which == Tok_Decl_Class ) - result = cast(Code, def_class( name.Text, def_assign( body, parent, access, attributes, interfaces, scast(s32, array_num(interfaces)), mflags ) )); + result = cast(Code, def_class( name.Text, def_assign( body, parent, access, attributes, interfaces, scast(s32, array_num(interfaces)), specifiers, mflags ) )); else - result = cast(Code, def_struct( name.Text, def_assign( body, (CodeTypename)parent, access, attributes, interfaces, scast(s32, array_num(interfaces)), mflags ) )); + result = cast(Code, def_struct( name.Text, def_assign( body, (CodeTypename)parent, access, attributes, interfaces, scast(s32, array_num(interfaces)), specifiers, mflags ) )); if ( inline_cmt ) result->InlineCmt = cast(Code, inline_cmt); @@ -947,10 +954,6 @@ CodeBody parse_class_struct_body( TokType which, Token name ) member = cast(Code, parse_simple_preprocess( Tok_Preprocess_Macro_Stmt )); break; } - case Tok_Preprocess_Macro_Expr: { - log_failure("Unbounded macro expression residing in class/struct body\n%S", parser_to_strbuilder(_ctx->parser)); - return InvalidCode; - } // case Tok_Preprocess_Macro: // // @@ -976,6 +979,15 @@ CodeBody parse_class_struct_body( TokType which, Token name ) break; } + case Tok_Preprocess_Macro_Expr: + { + if ( ! tok_is_attribute(currtok)) + { + log_failure("Unbounded macro expression residing in class/struct body\n%S", parser_to_strbuilder(_ctx->parser)); + return InvalidCode; + } + } + //! Fallthrough intended case Tok_Attribute_Open: case Tok_Decl_GNU_Attribute: case Tok_Decl_MSVC_Attribute: @@ -1143,27 +1155,44 @@ Code parse_complicated_definition( TokType which ) { push_scope(); - bool is_inplace = false; + b32 is_inplace = false; + b32 is_fn_def = false; TokArray tokens = _ctx->parser.Tokens; s32 idx = tokens.Idx; s32 level = 0; + b32 had_def = false; + b32 had_paren = false; for ( ; idx < array_num(tokens.Arr); idx++ ) { if ( tokens.Arr[ idx ].Type == Tok_BraceCurly_Open ) level++; - if ( tokens.Arr[ idx ].Type == Tok_BraceCurly_Close ) + if ( tokens.Arr[ idx ].Type == Tok_BraceCurly_Close ) { level--; + had_def = level == 0; + } - if ( level == 0 && tokens.Arr[ idx ].Type == Tok_Statement_End ) + b32 found_fn_def = had_def && had_paren; + + if ( level == 0 && (tokens.Arr[ idx ].Type == Tok_Statement_End || found_fn_def) ) break; } + is_fn_def = had_def && had_paren; + if (is_fn_def) + { + // Function definition with on return type + Code result = parse_operator_function_or_variable(false, NullCode, NullCode); + // (...) ... { ... } + parser_pop(& _ctx->parser); + return result; + } + if ( ( idx - 2 ) == tokens.Idx ) { - // Its a forward declaration only + // It's a forward declaration only Code result = parse_forward_or_definition( which, is_inplace ); // ; parser_pop(& _ctx->parser); @@ -1427,13 +1456,27 @@ CodeFn parse_function_after_name( else if ( check(Tok_Operator) && currtok.Text.Ptr[0] == '=' ) { eat(Tok_Operator); - specifiers_append(specifiers, Spec_Pure ); + if ( specifiers == nullptr ) + { + specifiers = (CodeSpecifiers) make_code(); + specifiers->Type = CT_Specifiers; + } + if ( str_are_equal(nexttok.Text, txt("delete"))) + { + specifiers_append(specifiers, Spec_Delete); + eat(currtok.Type); + // ( ) = delete + } + else + { + specifiers_append(specifiers, Spec_Pure ); - eat( Tok_Number); + eat( Tok_Number); + // ( ) = 0 + } Token stmt_end = currtok; eat( Tok_Statement_End ); - // ( ) = 0; - + if ( currtok_noskip.Type == Tok_Comment && currtok_noskip.Line == stmt_end.Line ) inline_cmt = parse_comment(); // ( ) ; @@ -1684,10 +1727,6 @@ CodeBody parse_global_nspace( CodeType which ) member = cast(Code, parse_simple_preprocess( Tok_Preprocess_Macro_Stmt )); break; } - case Tok_Preprocess_Macro_Expr: { - log_failure("Unbounded macro expression residing in class/struct body\n%S", parser_to_strbuilder(_ctx->parser)); - return InvalidCode; - } case Tok_Preprocess_Pragma: { member = cast(Code, parse_pragma()); @@ -1721,6 +1760,16 @@ CodeBody parse_global_nspace( CodeType which ) log_failure( "gen::%s: This function is not implemented" ); return InvalidCode; } + break; + + case Tok_Preprocess_Macro_Expr: + { + if (tok_is_attribute(currtok)) + { + log_failure("Unbounded macro expression residing in class/struct body\n%S", parser_to_strbuilder(_ctx->parser)); + return InvalidCode; + } + } //! Fallthrough intentional case Tok_Attribute_Open: case Tok_Decl_GNU_Attribute: @@ -1995,7 +2044,11 @@ Token parse_identifier( bool* possible_member_function ) Token name = currtok; _ctx->parser.Scope->Name = name.Text; - eat( Tok_Identifier ); + + // Typename can be: '::' + // If that is the case first option will be Tok_Access_StaticSymbol below + if (check(Tok_Identifier)) + eat( Tok_Identifier ); // parse_template_args( & name ); @@ -2415,6 +2468,25 @@ CodeOperator parse_operator_after_ret_type( eat( currtok.Type ); } // operator ( ) + + // TODO(Ed): Add proper "delete" and "new" awareness + // We're dealing with either a "= delete" or operator deletion + if (check(Tok_Operator) && currtok.Text.Ptr[0] == '=') + { + eat(currtok.Type); + if ( ! str_are_equal(currtok.Text, txt("delete"))) + { + log_failure("Expected delete after = in operator forward instead found \"%S\"\n%SB", currtok.Text, parser_to_strbuilder(_ctx->parser)); + parser_pop(& _ctx->parser); + return InvalidCode; + } + if (specifiers == nullptr) + specifiers = def_specifier( Spec_Delete ); + else + specifiers_append(specifiers, Spec_Delete); + eat(currtok.Type); + // operator ( ) = delete + } // Parse Body CodeBody body = { nullptr }; @@ -2466,6 +2538,24 @@ Code parse_operator_function_or_variable( bool expects_function, CodeAttributes CodeTypename type = parser_parse_type( parser_not_from_template, nullptr ); // + // Thanks Unreal + CodeAttributes post_rt_attributes = parse_attributes(); + if (post_rt_attributes) + { + if (attributes) + { + StrBuilder merged = strbuilder_fmt_buf(_ctx->Allocator_Temp, "%S %S", attributes->Content, post_rt_attributes->Content); + attributes->Content = cache_str(strbuilder_to_str(merged)); + } + else + { + attributes = post_rt_attributes; + } + // + // CONVERTED TO: + // + } + if ( type == InvalidCode ) { parser_pop(& _ctx->parser); return InvalidCode; @@ -2713,10 +2803,8 @@ CodeParams parse_params( bool use_template_capture ) // ( } - // 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.Ptr || use_template_capture ) && bitfield_is_set( u32, currtok.Flags, TF_Assign ) ) + // C++ allows typename = expression... so anything goes.... + if ( bitfield_is_set( u32, currtok.Flags, TF_Assign ) ) { eat( Tok_Operator ); // ( = @@ -2824,10 +2912,8 @@ CodeParams parse_params( bool use_template_capture ) // ( = , } - // 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.Ptr || use_template_capture ) && bitfield_is_set( u32, currtok.Flags, TF_Assign ) ) + /// C++ allows typename = expression... so anything goes.... + if ( bitfield_is_set( u32, currtok.Flags, TF_Assign ) ) { eat( Tok_Operator ); // ( = , = @@ -3451,6 +3537,7 @@ CodeConstructor parser_parse_constructor( CodeSpecifiers specifiers ) body = cast(CodeBody, parse_function_body()); // ( ) { } } + // TODO(Ed): Add support for detecting constructor deletion else if ( check( Tok_Operator) && currtok.Text.Ptr[ 0 ] == '=' ) { body = cast(CodeBody, parse_assignment_expression()); @@ -5438,7 +5525,10 @@ CodeUsing parser_parse_using() if ( ! is_namespace ) { - if ( bitfield_is_set( u32, currtok.Flags, TF_Assign ) ) + attributes = parse_attributes(); + // using + + if ( bitfield_is_set( u32, currtok.Flags, TF_Assign )) { attributes = parse_attributes(); // using diff --git a/base/enums/ESpecifier.csv b/base/enums/ESpecifier.csv index a396d37..da98b35 100644 --- a/base/enums/ESpecifier.csv +++ b/base/enums/ESpecifier.csv @@ -24,4 +24,5 @@ Final, final NoExceptions, noexcept Override, override Pure, = 0 +Delete, = delete Volatile, volatile diff --git a/base/helpers/base_codegen.hpp b/base/helpers/base_codegen.hpp index a1d104c..07ba92e 100644 --- a/base/helpers/base_codegen.hpp +++ b/base/helpers/base_codegen.hpp @@ -220,6 +220,7 @@ CodeBody gen_especifier( char const* path, bool use_c_definition = false ) case Spec_NoExceptions: case Spec_Override: case Spec_Pure: + case Spec_Delete: case Spec_Volatile: return true;