From 9423cd0f0acf0fc6d532c386e229e8dc3f3cd8a6 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Tue, 16 Apr 2024 01:20:15 -0400 Subject: [PATCH] Some cleanup of gencpp --- Project/Source/GasaGen/GasaGen.cpp | 10 +- Project/Source/GasaGen/gen.cpp | 217 ++++++++++++++++++----------- 2 files changed, 140 insertions(+), 87 deletions(-) diff --git a/Project/Source/GasaGen/GasaGen.cpp b/Project/Source/GasaGen/GasaGen.cpp index 7b0d8ff..060b500 100644 --- a/Project/Source/GasaGen/GasaGen.cpp +++ b/Project/Source/GasaGen/GasaGen.cpp @@ -125,8 +125,8 @@ int gen_main() case CodeT::Function_Fwd: if ( class_code->Name ) { - // log_fmt("%s\n", class_code->Name ); - log_fmt("%s\n", class_code->to_string() ); + log_fmt("%s\n", class_code->Name ); + // log_fmt("%s\n", class_code->to_string() ); } break; } @@ -176,7 +176,7 @@ int gen_main() #define path_AActor \ R"(C:\projects\Unreal\Surgo\UE\Engine\Source\Runtime\Engine\Classes\GameFramework\Actor.h)" -#if 0 +#if 1 content = file_read_contents( GlobalAllocator, true, path_AActor ); CodeBody parsed_aactor = parse_global_body( StrC { content.size, (char const*)content.data }); @@ -194,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 ); diff --git a/Project/Source/GasaGen/gen.cpp b/Project/Source/GasaGen/gen.cpp index 66efc34..5c3f013 100644 --- a/Project/Source/GasaGen/gen.cpp +++ b/Project/Source/GasaGen/gen.cpp @@ -7294,6 +7294,115 @@ namespace parser constexpr bool strip_formatting_dont_preserve_newlines = false; + internal inline + bool is_constructor_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. + + TODO(Ed): 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; + Token nav = tokens[ idx ]; + for ( ; idx < tokens.Arr.num(); idx++, nav = tokens[ idx ] ) + { + if ( nav.Text[0] == '<' ) + { + // Skip templated expressions as they mey have expressions with the () operators + s32 capture_level = 0; + s32 template_level = 0; + for ( ; idx < tokens.Arr.num(); idx++, nav = tokens[idx] ) + { + if (nav.Text[ 0 ] == '<') + ++ template_level; + + if (nav.Text[ 0 ] == '>') + -- template_level; + if (nav.Type == TokType::Operator && nav.Text[1] == '>') + -- template_level; + + if ( nav.Type == ETokType::Capture_Start) + { + if (template_level != 0 ) + ++ capture_level; + else + break; + } + + if ( template_level != 0 && nav.Type == ETokType::Capture_End) + -- capture_level; + } + } + + if ( nav.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. + return false; + } + + -- idx; + tok_left = tokens[idx]; + // ... + + if ( tok_left.Type != TokType::Access_StaticSymbol ) + return false; + + -- 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 + // :: ( + return true; + } + } + /* This function was an attempt at stripping formatting from any c++ code. It has edge case failures that prevent it from being used in function bodies. @@ -7835,6 +7944,7 @@ namespace parser internal neverinline CodeBody parse_class_struct_body( TokType which, Token name ) { using namespace ECode; + push_scope(); eat( TokType::BraceCurly_Open ); @@ -7856,7 +7966,7 @@ namespace parser bool expects_function = false; - Context.Scope->Start = currtok_noskip; + // Context.Scope->Start = currtok_noskip; if ( currtok_noskip.Type == TokType::Preprocess_Hash ) eat( TokType::Preprocess_Hash ); @@ -8576,6 +8686,8 @@ namespace parser { using namespace ECode; + push_scope(); + if ( which != Namespace_Body && which != Global_Body && which != Export_Body && which != Extern_Linkage_Body ) return CodeInvalid; @@ -8594,7 +8706,7 @@ namespace parser bool expects_function = false; - Context.Scope->Start = currtok_noskip; + // Context.Scope->Start = currtok_noskip; if ( currtok_noskip.Type == TokType::Preprocess_Hash ) eat( TokType::Preprocess_Hash ); @@ -8788,6 +8900,7 @@ namespace parser StrC spec_str = ESpecifier::to_str( spec ); log_failure( "Invalid specifier %.*s for variable\n%s", spec_str.Len, spec_str, Context.to_string() ); + Context.pop(); return CodeInvalid; } @@ -8817,6 +8930,14 @@ namespace parser case TokType::Type_double : case TokType::Type_int : { + // Possible constructor implemented at global file scope. + if (is_constructor_definition()) + { + member = parse_constructor( specifiers ); + // :: () { ... } + break; + } + bool found_operator_cast_outside_class_implmentation = false; s32 idx = Context.Tokens.Idx; @@ -8855,6 +8976,7 @@ namespace parser if ( member == Code::Invalid ) { log_failure( "Failed to parse member\n%s", Context.to_string() ); + Context.pop(); return CodeInvalid; } @@ -8866,6 +8988,7 @@ namespace parser eat( TokType::BraceCurly_Close ); // { } + Context.pop(); return result; } @@ -10980,90 +11103,20 @@ namespace parser // template< > } + + bool has_context = Context.Scope && Context.Scope->Prev; + bool is_in_global_nspace = has_context && str_compare( Context.Scope->Prev->ProcName, "parse_global_nspace" ) == 0; + // Possible constructor implemented at global file scope. + if (is_in_global_nspace && is_constructor_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]; - // ... - - 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; - } + definition = parse_constructor( specifiers ); + // :: () { ... } + break; } - // User Defined operator casts + // Possible user Defined operator casts + if (is_in_global_nspace) { bool found_operator_cast_outside_class_implmentation = false; s32 idx = Context.Tokens.Idx;