From 1a60fd54fc675271d793adae83c9d780c6698933 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sun, 4 Oct 2026 13:15:04 -0400 Subject: [PATCH] progress --- base/components/interface.hpp | 25 +++-- base/components/interface.parsing.cpp | 104 ++++++++++++--------- base/components/lexer.cpp | 8 +- base/components/parser.cpp | 4 +- docs/Parsing.md | 6 +- gen_c_library/c_library.cpp | 52 ++++++++++- scripts/Readme.md | 1 + scripts/build.ci.ps1 | 101 +++++++++++++-------- scripts/clean.ps1 | 48 +++++----- test/lexer_failures/test.cpp | 5 +- test/parse_body_messages/test.cpp | 126 ++++++++++++++++++++++++++ 11 files changed, 355 insertions(+), 125 deletions(-) create mode 100644 test/parse_body_messages/test.cpp diff --git a/base/components/interface.hpp b/base/components/interface.hpp index a46337a..9351352 100644 --- a/base/components/interface.hpp +++ b/base/components/interface.hpp @@ -15,6 +15,13 @@ \▓▓▓▓▓▓ \▓▓▓▓▓▓▓\▓▓ \▓▓ \▓▓▓▓▓▓\▓▓ \▓▓ \▓▓▓▓ \▓▓▓▓▓▓▓\▓▓ \▓▓ \▓▓▓▓▓▓▓ \▓▓▓▓▓▓▓ \▓▓▓▓▓▓▓ */ +struct ParseInfo +{ + ParseMessage* messages; + LexedInfo lexed; + Code result; +}; + // Note(Ed): This is subject to heavily change // with upcoming changes to the library's fallback (default) allocations strategy; // and major changes to lexer/parser context usage. @@ -80,6 +87,7 @@ struct Context // TODO(Ed): Active parse context vs a parse result need to be separated conceptually ParseContext parser; + ParseInfo parse_info; // TODO(Ed): Formatting - This will eventually be in a separate struct when in the process of serialization of the builder. s32 temp_serialize_indent; @@ -363,13 +371,6 @@ struct ParseStackNode // TODO(Ed): When an error occurs, the parse stack is not released and instead the scope is left dangling. }; -struct ParseInfo -{ - ParseMessage* messages; - LexedInfo lexed; - Code result; -}; - struct ParseOpts { AllocatorInfo backing_msgs; @@ -388,7 +389,15 @@ GEN_API CodeBody parse_export_body ( Str export_def ); GEN_API CodeExtern parse_extern_link ( Str exten_link_def ); GEN_API CodeFriend parse_friend ( Str friend_def ); GEN_API CodeFn parse_function ( Str fn_def ); -GEN_API CodeBody parse_global_body ( Str body_def ); + +struct Opts_parse_global_body +{ + Context* ctx; + ParseInfo* info; +}; +GEN_API void parse_global_body_base( Context* ctx, Str body_def, ParseInfo* info ); +GEN_API CodeBody parse_global_body( Str body_def, Opts_parse_global_body opts GEN_PARAM_DEFAULT ); + GEN_API CodeNS parse_namespace ( Str namespace_def ); GEN_API CodeOperator parse_operator ( Str operator_def ); GEN_API CodeOpCast parse_operator_cast( Str operator_def ); diff --git a/base/components/interface.parsing.cpp b/base/components/interface.parsing.cpp index 95baae1..09337ca 100644 --- a/base/components/interface.parsing.cpp +++ b/base/components/interface.parsing.cpp @@ -55,7 +55,7 @@ CodeClass parse_class( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -86,7 +86,7 @@ CodeConstructor parse_constructor(Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -163,7 +163,7 @@ CodeDefine parse_define( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -194,7 +194,7 @@ CodeDestructor parse_destructor( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -223,7 +223,7 @@ CodeEnum parse_enum( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -249,7 +249,7 @@ CodeBody parse_export_body( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -275,7 +275,7 @@ CodeExtern parse_extern_link( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -301,7 +301,7 @@ CodeFriend parse_friend( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -327,7 +327,7 @@ CodeFn parse_function( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -340,35 +340,55 @@ done: return result; } -CodeBody parse_global_body( Str def ) +void parse_global_body_base( Context* ctx, Str def, ParseInfo* info ) { - // TODO(Ed): Lift this. - Context* ctx = _ctx; - CodeBody result = InvalidCode; - LexedInfo lexed = struct_zero(LexedInfo); + LexedInfo lexed; + CodeBody body; - ctx->parser = struct_zero(ParseContext); - if (check_parse_args(def) == false) - goto done; + if ( ctx == nullptr || info == nullptr ) + GEN_FATAL( "parse_global_body_base: null context or info" ); - lexed = lex(ctx, def); - ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { - if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) - parser_record_failure(ctx, lexed.messages->content); - else - parser_record_failure(ctx, txt("parse: lex produced no tokens")); - goto done; + info[0] = struct_zero( ParseInfo ); + lexed = struct_zero( LexedInfo ); + body = (CodeBody) make_code(); + body->Type = CT_Global_Body; + + ctx->parser = struct_zero( ParseContext ); + if ( check_parse_args( def ) == false ) { + info->result = cast( Code, body ); + info->messages = ctx->parser.messages; + return; } - { - ParseStackNode scope = NullScope; - parser_push(& ctx->parser, & scope); - result = parse_global_nspace(ctx, CT_Global_Body ); - parser_pop(& ctx->parser); + lexed = lex( ctx, def ); + info->lexed = lexed; + if ( lexed.tokens.num == 0 ) { + info->result = cast( Code, body ); + info->messages = ctx->parser.messages; + return; } -done: - return result; + + ctx->parser.tokens = lexed.tokens; { + ParseStackNode scope = NullScope; + parser_push( & ctx->parser, & scope ); + body = parse_global_nspace( ctx, CT_Global_Body ); + parser_pop( & ctx->parser ); + } + info->result = cast( Code, body ); + info->messages = ctx->parser.messages; +} + +CodeBody parse_global_body( Str def, Opts_parse_global_body opts ) { + Opts_parse_global_body resolved = get_optional( opts ); + Context* ctx = resolved.ctx; + ParseInfo* info; + + if ( ctx == nullptr ) ctx = _ctx; + if ( ctx == nullptr ) GEN_FATAL( "parse_global_body: no context" ); + info = resolved.info; if ( info == nullptr ) info = & ctx->parse_info; + + parse_global_body_base( ctx, def, info ); + return cast( CodeBody, info->result ); } CodeNS parse_namespace( Str def ) @@ -384,7 +404,7 @@ CodeNS parse_namespace( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -410,7 +430,7 @@ CodeOperator parse_operator( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -436,7 +456,7 @@ CodeOpCast parse_operator_cast( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -462,7 +482,7 @@ CodeStruct parse_struct( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -493,7 +513,7 @@ CodeTemplate parse_template( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -519,7 +539,7 @@ CodeTypename parse_type( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -545,7 +565,7 @@ CodeTypedef parse_typedef( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -571,7 +591,7 @@ CodeUnion parse_union( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -597,7 +617,7 @@ CodeUsing parse_using( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else @@ -623,7 +643,7 @@ CodeVar parse_variable( Str def ) lexed = lex(ctx, def); ctx->parser.tokens = lexed.tokens; - if (ctx->parser.tokens.ptr == nullptr) { + if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) { if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr) parser_record_failure(ctx, lexed.messages->content); else diff --git a/base/components/lexer.cpp b/base/components/lexer.cpp index 3c400da..ad82f74 100644 --- a/base/components/lexer.cpp +++ b/base/components/lexer.cpp @@ -694,7 +694,7 @@ LexedInfo lex(Context* lib_ctx, Str content) case Lex_ReturnNull: { info.messages = c.messages; - return info; + goto Lex_Done; } } } @@ -725,7 +725,7 @@ LexedInfo lex(Context* lib_ctx, Str content) lexer_record_failure_fmt(lib_ctx, ctx, "gen::lex: invalid varadic argument, expected '...' got '..%c' (%d, %d)\n%s", (* ctx->scanner), c.line, c.column, context_str ); info.messages = c.messages; - return info; + goto Lex_Done; } } @@ -1282,7 +1282,7 @@ LexedInfo lex(Context* lib_ctx, Str content) StrBuilder context_str = strbuilder_fmt_buf( lib_ctx->Allocator_Temp, "%.*s", min( 100, c.left ), c.scanner ); lexer_record_failure_fmt(lib_ctx, ctx, "Failed to lex token '%c' (%d, %d)\n%s", (* ctx->scanner), c.line, c.column, context_str ); info.messages = c.messages; - return info; + goto Lex_Done; } FoundToken: @@ -1318,8 +1318,8 @@ LexedInfo lex(Context* lib_ctx, Str content) info.messages = c.messages; info.text = content; - info.tokens = struct_init(TokenSlice) { pcast(Token*, c.tokens), scast(s32, array_num(c.tokens)) }; Lex_Done: + info.tokens = struct_init(TokenSlice) { pcast(Token*, c.tokens), scast(s32, array_num(c.tokens)) }; return info; } diff --git a/base/components/parser.cpp b/base/components/parser.cpp index bcb88c4..f39cf3f 100644 --- a/base/components/parser.cpp +++ b/base/components/parser.cpp @@ -1715,9 +1715,9 @@ CodeBody parse_global_nspace(Context* ctx, CodeType which) { case Tok_Comma: { - log_failure("Dangling comma found: %SB\nContext:\n%SB", tok_to_strbuilder(ctx->Allocator_Temp, currtok), parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp)); + parser_record_failure(ctx, txt("Dangling comma")); parser_pop( & ctx->parser); - return InvalidCode; + return result; } break; case Tok_Statement_End: diff --git a/docs/Parsing.md b/docs/Parsing.md index b05bdd7..487d543 100644 --- a/docs/Parsing.md +++ b/docs/Parsing.md @@ -15,6 +15,9 @@ You can think of this parser as *frontend parser* vs a *semantic parser*. Its in User exposed interface: ```cpp +CodeBody parse_global_body ( Str body_def, Opts_parse_global_body opts ); +void parse_global_body_base( Context* ctx, Str body_def, ParseInfo* info ); + CodeClass parse_class ( Str class_def ); CodeConstructor parse_constructor ( Str constructor_def ); CodeDefine parse_define ( Str define_def ); @@ -24,7 +27,6 @@ CodeBody parse_export_body ( Str export_def ); CodeExtern parse_extern_link ( Str exten_link_def ); CodeFriend parse_friend ( Str friend_def ); CodeFn parse_function ( Str fn_def ); -CodeBody parse_global_body ( Str body_def ); CodeNS parse_namespace ( Str namespace_def ); CodeOperator parse_operator ( Str operator_def ); CodeOpCast parse_operator_cast( Str operator_def ); @@ -155,5 +157,3 @@ typedef HashTable(Macro) MacroTable; * typedefs have attributes with the type (`parse_type`) * Parsing attributes can be extended to support user defined macros by defining `GEN_DEFINE_ATTRIBUTE_TOKENS` (see `gen.hpp` for the formatting) * This is useful for example: parsing Unreal `Module_API` macros. - -**The lexer & parser do not gracefully attempt to continue when it comes across incorrect code, and doesn't properly track errors into a listing (yet).** diff --git a/gen_c_library/c_library.cpp b/gen_c_library/c_library.cpp index 34aa724..f4dbc40 100644 --- a/gen_c_library/c_library.cpp +++ b/gen_c_library/c_library.cpp @@ -739,10 +739,10 @@ do \ { // pop the array entry array_token->NumEntries -= 1; - Code next = array_entry->Next; - Code prev = array_entry->Prev; - next->Prev = array_entry->Prev; - prev->Next = next; + Code next = array_entry->Next; + Code prev = array_entry->Prev; + next->Prev = array_entry->Prev; + prev->Next = next; if ( array_token->Front == array_entry ) array_token->Front = next; @@ -1142,6 +1142,27 @@ R"(#define ( code ) _Generic( (code), \ for ( CodeParams opt_param : fn->Params ) if (opt_param->ValueType->Name.starts_with(txt("Opts_"))) { // Convert the definition to use a default struct: https://vxtwitter.com/vkrajacic/status/1749816169736073295 + if ( fn->Name.is_equal( txt("parse_global_body") ) ) + { + Str new_name = txt("parse_global_body_opts"); + char const* tmpl_fn_macro = "#define ( ... ) ( & () { __VA_ARGS__ } )\n"; + Code fn_macro; + + opt_param->ValueType->Specs = def_specifier( Spec_Ptr ); + fn_macro = untyped_str( token_fmt( + "def_name", fn->Name + , "def__name", new_name + , "params", txt("def, ") + , "opts_type", opt_param->ValueType->Name + , tmpl_fn_macro + )); + fn->Name = cache_str( new_name ); + interface.append( fn ); + interface.append( fn_macro ); + handled = true; + break; + } + Str prefix = txt("def_"); Str actual_name = { fn->Name.Ptr + prefix.Len, fn->Name.Len - prefix.Len }; Str new_name = StrBuilder::fmt_buf(_ctx->Allocator_Temp, "def__%S", actual_name ).to_str(); @@ -1409,6 +1430,29 @@ R"(#define ( code ) _Generic( (code), \ Code src_code_serialization = scan_file( path_base "components/code_serialization.cpp" ); Code src_parsing_interface = scan_file( path_base "components/interface.parsing.cpp" ); + { + Str needle = txt("CodeBody parse_global_body( Str def, Opts_parse_global_body opts )"); + Str scanned = src_parsing_interface->Content; + ssize signature_at = -1; + ssize scan_offset; + ssize last_offset = scanned.Len - needle.Len; + for ( scan_offset = 0; scan_offset <= last_offset; ++ scan_offset ) + { + if ( c_str_compare_len( scanned.Ptr + scan_offset, needle.Ptr, needle.Len ) == 0 ) { + signature_at = scan_offset; + break; + } + } + if ( signature_at < 0 ) GEN_FATAL( "c_library: parse_global_body signature missing from scanned interface.parsing.cpp" ); + /*Resolved replacement*/ { + Str replacement = txt("CodeBody parse_global_body_opts( Str def, Opts_parse_global_body* opts )"); + StrBuilder rebuilt = strbuilder_make_reserve( _ctx->Allocator_Temp, scanned.Len + replacement.Len ); + strbuilder_append_str( & rebuilt, Str { scanned.Ptr, signature_at } ); + strbuilder_append_str( & rebuilt, replacement ); + strbuilder_append_str( & rebuilt, Str { scanned.Ptr + signature_at + needle.Len, scanned.Len - signature_at - needle.Len } ); + src_parsing_interface = untyped_str( strbuilder_to_str( rebuilt ) ); + } + } Code src_untyped = scan_file( path_base "components/interface.untyped.cpp" ); Code src_parser_case_macros = scan_file( path_base "components/parser_case_macros.cpp" ); diff --git a/scripts/Readme.md b/scripts/Readme.md index e62cd33..2d68977 100644 --- a/scripts/Readme.md +++ b/scripts/Readme.md @@ -28,6 +28,7 @@ args: test parser_bounds lexer_failures + parse_body_messages clang msvc : By default this project builds with clang, specifying msvc will build with MSVC. debug diff --git a/scripts/build.ci.ps1 b/scripts/build.ci.ps1 index e95f0d0..dcacff4 100644 --- a/scripts/build.ci.ps1 +++ b/scripts/build.ci.ps1 @@ -65,19 +65,20 @@ if ($null -eq $is_linux) { Push-Location $path_root #region Arguments - $vendor = $null - $release = $null -[bool] $verbose = $false -[bool] $base = $false -[bool] $segmented = $false -[bool] $singleheader = $false -[bool] $c_lib = $false -[bool] $c_lib_static = $false -[bool] $c_lib_dyn = $false -[bool] $unreal = $false -[bool] $test = $false -[bool] $parser_bounds = $false -[bool] $lexer_failures = $false + $vendor = $null + $release = $null +[bool] $verbose = $false +[bool] $base = $false +[bool] $segmented = $false +[bool] $singleheader = $false +[bool] $c_lib = $false +[bool] $c_lib_static = $false +[bool] $c_lib_dyn = $false +[bool] $unreal = $false +[bool] $test = $false +[bool] $parser_bounds = $false +[bool] $lexer_failures = $false +[bool] $parse_body_messages = $false [array] $vendors = @( "clang", "msvc" ) @@ -85,20 +86,21 @@ Push-Location $path_root if ( $args ) { $args | ForEach-Object { switch ($_){ - { $_ -in $vendors } { $vendor = $_; break } - "verbose" { $verbose = $true } - "release" { $release = $true } - "debug" { $release = $false } - "base" { $base = $true } - "segmented" { $segmented = $true } - "singleheader" { $singleheader = $true } - "c_lib" { $c_lib = $true } - "c_lib_static" { $c_lib_static = $true } - "c_lib_dyn" { $c_lib_dyn = $true } - "unreal" { $unreal = $true } - "test" { $test = $true } - "parser_bounds" { $parser_bounds = $true } - "lexer_failures" { $lexer_failures = $true } + { $_ -in $vendors } { $vendor = $_; break } + "verbose" { $verbose = $true } + "release" { $release = $true } + "debug" { $release = $false } + "base" { $base = $true } + "segmented" { $segmented = $true } + "singleheader" { $singleheader = $true } + "c_lib" { $c_lib = $true } + "c_lib_static" { $c_lib_static = $true } + "c_lib_dyn" { $c_lib_dyn = $true } + "unreal" { $unreal = $true } + "test" { $test = $true } + "parser_bounds" { $parser_bounds = $true } + "lexer_failures" { $lexer_failures = $true } + "parse_body_messages" { $parse_body_messages = $true } } }} #endregion Arguments @@ -127,16 +129,17 @@ else { $optimize = $true } -$cannot_build = $base -eq $false -$cannot_build = $cannot_build -and $segmented -eq $false -$cannot_build = $cannot_build -and $singleheader -eq $false -$cannot_build = $cannot_build -and $c_lib -eq $false -$cannot_build = $cannot_build -and $c_lib_static -eq $false -$cannot_build = $cannot_build -and $c_lib_dyn -eq $false -$cannot_build = $cannot_build -and $unreal -eq $false -$cannot_build = $cannot_build -and $test -eq $false -$cannot_build = $cannot_build -and $parser_bounds -eq $false -$cannot_build = $cannot_build -and $lexer_failures -eq $false +$cannot_build = $base -eq $false +$cannot_build = $cannot_build -and $segmented -eq $false +$cannot_build = $cannot_build -and $singleheader -eq $false +$cannot_build = $cannot_build -and $c_lib -eq $false +$cannot_build = $cannot_build -and $c_lib_static -eq $false +$cannot_build = $cannot_build -and $c_lib_dyn -eq $false +$cannot_build = $cannot_build -and $unreal -eq $false +$cannot_build = $cannot_build -and $test -eq $false +$cannot_build = $cannot_build -and $parser_bounds -eq $false +$cannot_build = $cannot_build -and $lexer_failures -eq $false +$cannot_build = $cannot_build -and $parse_body_messages -eq $false if ( $cannot_build ) { Stop-stage "No build target specified" } @@ -495,6 +498,30 @@ if ($lexer_failures) $result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable invoke-stageTool -stage "lexer_failures" -compiled $result -executable $executable -path_work $path_probe } + +if ($parse_body_messages) +{ + $path_probe = join-path $path_test parse_body_messages + $path_build = join-path $path_probe build + if ( -not(Test-Path($path_build) )) { + new-item -ItemType Directory -Path $path_build | Out-Null + } + + $includes = @( $path_base ) + $unit = join-path $path_probe "test.cpp" + $executable = join-path $path_build "parse_body_messages.exe" + + $compiler_args = @() + $compiler_args += ( $flag_define + 'GEN_TIME' ) + $compiler_args += $flag_cpp17 + + $linker_args = @( + $flag_link_win_subsystem_console + ) + + $result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable + invoke-stageTool -stage "parse_body_messages" -compiled $result -executable $executable -path_work $path_probe +} #endregion Building pop-location # $path_root diff --git a/scripts/clean.ps1 b/scripts/clean.ps1 index 69515a3..0d32560 100644 --- a/scripts/clean.ps1 +++ b/scripts/clean.ps1 @@ -1,28 +1,29 @@ $misc = Join-Path $PSScriptRoot 'helpers/misc.psm1' Import-Module $misc -$path_root = Get-ScriptRepoRoot -$path_base = Join-Path $path_root base -$path_base_build = Join-Path $path_base build -$path_c_library = Join-Path $path_root gen_c_library -$path_c_library_build = Join-Path $path_c_library build -$path_c_library_gen = Join-Path $path_c_library gen -$path_segmented = Join-Path $path_root gen_segmented -$path_segmented_build = Join-Path $path_segmented build -$path_segmented_gen = Join-Path $path_segmented gen -$path_singleheader = Join-Path $path_root gen_singleheader -$path_singleheader_build = Join-Path $path_singleheader build -$path_singleheader_gen = Join-Path $path_singleheader gen -$path_unreal = Join-Path $path_root gen_unreal_engine -$path_unreal_build = Join-Path $path_unreal build -$path_unreal_gen = Join-Path $path_unreal gen -$path_test = Join-Path $path_root test -$path_test_build = Join-Path $path_test build -$path_test_gen = Join-Path $path_test gen -$path_parser_bounds_build = Join-Path $path_test "parser_bounds\build" -$path_lexer_failures_build = Join-Path $path_test "lexer_failures\build" -$path_x64 = Join-Path $path_root x64 -$path_release = Join-Path $path_root release +$path_root = Get-ScriptRepoRoot +$path_base = Join-Path $path_root base +$path_base_build = Join-Path $path_base build +$path_c_library = Join-Path $path_root gen_c_library +$path_c_library_build = Join-Path $path_c_library build +$path_c_library_gen = Join-Path $path_c_library gen +$path_segmented = Join-Path $path_root gen_segmented +$path_segmented_build = Join-Path $path_segmented build +$path_segmented_gen = Join-Path $path_segmented gen +$path_singleheader = Join-Path $path_root gen_singleheader +$path_singleheader_build = Join-Path $path_singleheader build +$path_singleheader_gen = Join-Path $path_singleheader gen +$path_unreal = Join-Path $path_root gen_unreal_engine +$path_unreal_build = Join-Path $path_unreal build +$path_unreal_gen = Join-Path $path_unreal gen +$path_test = Join-Path $path_root test +$path_test_build = Join-Path $path_test build +$path_test_gen = Join-Path $path_test gen +$path_parser_bounds_build = Join-Path $path_test "parser_bounds\build" +$path_lexer_failures_build = Join-Path $path_test "lexer_failures\build" +$path_parse_body_messages_build = Join-Path $path_test "parse_body_messages\build" +$path_x64 = Join-Path $path_root x64 +$path_release = Join-Path $path_root release if ( Test-Path $path_base_build) { Remove-Item $path_base_build -Recurse -Verbose @@ -66,6 +67,9 @@ if ( Test-Path $path_parser_bounds_build ) { if ( Test-Path $path_lexer_failures_build ) { Remove-Item $path_lexer_failures_build -Recurse -Verbose } +if ( Test-Path $path_parse_body_messages_build ) { + Remove-Item $path_parse_body_messages_build -Recurse -Verbose +} if ( Test-Path $path_x64) { Remove-Item $path_x64 -Recurse -Verbose } diff --git a/test/lexer_failures/test.cpp b/test/lexer_failures/test.cpp index bbf3d12..6309aa2 100644 --- a/test/lexer_failures/test.cpp +++ b/test/lexer_failures/test.cpp @@ -11,8 +11,7 @@ internal void expect(int condition, Str label) { if (condition == false) { log_fmt("FAIL %S\n", label); ++g_failures; } } -internal void expect_public(Str def, Str needle, Str label) -{ +internal void expect_public(Str def, Str needle, Str label) { Context ctx = {}; init(&ctx); CodeVar parsed = parse_variable(def); expect(cast(Code, parsed) == Code_Invalid, label); expect(_ctx->parser.messages != nullptr, label); @@ -31,7 +30,7 @@ internal void expect_public(Str def, Str needle, Str label) internal void expect_lex_fail(Str def, Str needle, Str label) { Context ctx = {}; init(&ctx); LexedInfo lexed = lex(&ctx, def); - expect(lexed.tokens.ptr == nullptr, label); + expect(lexed.tokens.ptr != nullptr, label); expect(lexed.messages != nullptr, label); if (lexed.messages != nullptr) expect(str_contains(lexed.messages->content, needle), label); deinit(&ctx); diff --git a/test/parse_body_messages/test.cpp b/test/parse_body_messages/test.cpp new file mode 100644 index 0000000..7a2050b --- /dev/null +++ b/test/parse_body_messages/test.cpp @@ -0,0 +1,126 @@ +#define GEN_TIME +#define GEN_DEFINE_LIBRARY_CODE_CONSTANTS +#define GEN_ENFORCE_STRONG_CODE_TYPES +#include "gen.cpp" + +using namespace gen; + +global int g_failures = 0; + +internal void expect(int condition, Str label) { + if (condition == false) { log_fmt("FAIL %S\n", label); ++g_failures; } +} + +internal Str body_text(CodeBody body) { + return strbuilder_to_str(code_to_strbuilder(cast(Code, body))); +} + +internal void expect_good_base(Context* ctx, Str label) { + ParseInfo info = {}; + Str def = txt("struct Context { int a; int b; };"); + parse_global_body_base(ctx, def, &info); + CodeBody body = cast(CodeBody, info.result); + Str text = body_text(body); + expect(body.ast != nullptr, label); + expect(str_contains(text, txt("Context")), label); + expect(str_contains(text, txt("a")), label); + expect(str_contains(text, txt("b")), label); + expect(info.messages == nullptr, label); + expect(info.lexed.messages == nullptr, label); + expect(info.lexed.tokens.ptr != nullptr, label); + expect(info.lexed.tokens.num > 0, label); +} + +internal void expect_at(Str label) { + Context ctx = {}; + ParseInfo info = {}; + Str def = txt("struct Context { int a; int b; };\n@"); + init(&ctx); + parse_global_body_base(&ctx, def, &info); + CodeBody body = cast(CodeBody, info.result); + Str text = body_text(body); + expect(body.ast != nullptr, label); + expect(str_contains(text, txt("Context")), label); + expect(info.lexed.messages != nullptr, label); + if (info.lexed.messages != nullptr) + expect(str_contains(info.lexed.messages->content, txt("Failed to lex token")), label); + expect(info.lexed.tokens.ptr != nullptr, label); + expect(info.lexed.tokens.num > 0, label); + expect_good_base(&ctx, txt("at-reuse")); + deinit(&ctx); +} + +internal void expect_comma(Str label) { + Context ctx = {}; + ParseInfo info = {}; + Str def = txt("struct Context { int a; int b; };\n,"); + init(&ctx); + parse_global_body_base(&ctx, def, &info); + CodeBody body = cast(CodeBody, info.result); + Str text = body_text(body); + expect(str_contains(text, txt("Context")), label); + expect(info.messages != nullptr, label); + if (info.messages != nullptr) + expect(str_contains(info.messages->Content, txt("Dangling comma")), label); + expect_good_base(&ctx, txt("comma-reuse")); + deinit(&ctx); +} + +internal void expect_space(Str label) { + Context ctx = {}; + ParseInfo info = {}; + init(&ctx); + parse_global_body_base(&ctx, txt(" \t\n"), &info); + CodeBody body = cast(CodeBody, info.result); + expect(body.ast != nullptr, label); + expect(body->Type == CT_Global_Body, label); + expect(info.lexed.tokens.num == 0, label); + expect(info.lexed.messages != nullptr, label); + if (info.lexed.messages != nullptr) + expect(str_contains(info.lexed.messages->content, txt("no tokens found")), label); + expect_good_base(&ctx, txt("space-reuse")); + deinit(&ctx); +} + +internal void expect_empty(Str label) { + Context ctx = {}; + ParseInfo info = {}; + Str def = { nullptr, 0 }; + init(&ctx); + parse_global_body_base(&ctx, def, &info); + CodeBody body = cast(CodeBody, info.result); + expect(body.ast != nullptr, label); + expect(info.messages != nullptr, label); + if (info.messages != nullptr) + expect(str_contains(info.messages->Content, txt("length must greater than 0")), label); + expect_good_base(&ctx, txt("empty-reuse")); + deinit(&ctx); +} + +internal void expect_wrapper(Str label) { + Context ctx = {}; + CodeBody body; + Str text; + init(&ctx); + body = parse_global_body(txt("struct Context { int a; int b; };")); + text = body_text(body); + expect(str_contains(text, txt("Context")), label); + expect(ctx.parse_info.messages == nullptr, label); + expect(str_contains(body_text(cast(CodeBody, ctx.parse_info.result)), txt("Context")), label); + deinit(&ctx); +} + +int main() +{ + Context ctx = {}; init(& ctx); + expect_good_base(&ctx, txt("good")); + deinit(& ctx); + expect_at(txt("at")); + expect_comma(txt("comma")); + expect_space(txt("space")); + expect_empty(txt("empty")); + expect_wrapper(txt("wrapper")); + if (g_failures != 0) { log_fmt("parse body messages failed: %d\n", g_failures); return 1; } + log_fmt("parse body messages passed\n"); + return 0; +}