From e50a444796fff679499f14bf441e890742f564a6 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sun, 10 May 2026 15:38:16 -0400 Subject: [PATCH] test(gencpp): add full gencpp/base samples and comprehensive test suite - Copied 58 files from C:\projects\gencpp\base\ to tests/assets/gencpp_samples - Added test_gencpp_full_suite.py that validates: - Skeleton generation for all .hpp files - Code outline generation - get_definition for key symbols - AST masking with aggregation - All 25 tests pass --- .../gencpp_samples/auxiliary/builder.cpp | 61 + .../gencpp_samples/auxiliary/builder.hpp | 71 + .../gencpp_samples/auxiliary/gen_template.hpp | 35 + .../gencpp_samples/auxiliary/scanner.cpp | 148 + .../gencpp_samples/auxiliary/scanner.hpp | 46 + tests/assets/gencpp_samples/base.cpp | 74 + .../assets/gencpp_samples/components/ast.cpp | 1303 ++++ .../assets/gencpp_samples/components/ast.hpp | 457 ++ .../components/ast_case_macros.cpp | 80 + .../gencpp_samples/components/ast_types.hpp | 1170 ++++ .../components/code_serialization.cpp | 1349 ++++ .../gencpp_samples/components/code_types.hpp | 1176 ++++ .../gencpp_samples/components/constants.hpp | 85 + .../gencpp_samples/components/header_end.hpp | 0 .../components/header_start.hpp | 32 + .../gencpp_samples/components/inlines.hpp | 753 +++ .../gencpp_samples/components/interface.cpp | 542 ++ .../gencpp_samples/components/interface.hpp | 490 ++ .../components/interface.parsing.cpp | 473 ++ .../components/interface.untyped.cpp | 191 + .../components/interface.upfront.cpp | 2138 ++++++ .../gencpp_samples/components/lexer.cpp | 1295 ++++ .../gencpp_samples/components/parser.cpp | 5784 +++++++++++++++++ .../components/parser_case_macros.cpp | 112 + .../components/parser_types.hpp | 272 + .../gencpp_samples/components/src_start.cpp | 11 + .../gencpp_samples/components/static_data.cpp | 92 + .../gencpp_samples/components/types.hpp | 181 + .../dependencies/basic_types.hpp | 142 + .../dependencies/containers.hpp | 826 +++ .../gencpp_samples/dependencies/debug.cpp | 46 + .../gencpp_samples/dependencies/debug.hpp | 71 + .../dependencies/filesystem.cpp | 659 ++ .../dependencies/filesystem.hpp | 386 ++ .../gencpp_samples/dependencies/hashing.cpp | 90 + .../gencpp_samples/dependencies/hashing.hpp | 11 + .../gencpp_samples/dependencies/macros.hpp | 343 + .../gencpp_samples/dependencies/memory.cpp | 522 ++ .../gencpp_samples/dependencies/memory.hpp | 668 ++ .../gencpp_samples/dependencies/parsing.cpp | 1116 ++++ .../gencpp_samples/dependencies/parsing.hpp | 433 ++ .../gencpp_samples/dependencies/platform.hpp | 158 + .../gencpp_samples/dependencies/printing.cpp | 600 ++ .../gencpp_samples/dependencies/printing.hpp | 29 + .../gencpp_samples/dependencies/src_start.cpp | 85 + .../dependencies/string_ops.cpp | 214 + .../dependencies/string_ops.hpp | 287 + .../gencpp_samples/dependencies/strings.cpp | 177 + .../gencpp_samples/dependencies/strings.hpp | 627 ++ .../gencpp_samples/dependencies/timing.cpp | 167 + .../gencpp_samples/dependencies/timing.hpp | 19 + tests/assets/gencpp_samples/gen.cpp | 46 + tests/assets/gencpp_samples/gen.dep.cpp | 18 + tests/assets/gencpp_samples/gen.dep.hpp | 21 + tests/assets/gencpp_samples/gen.hpp | 39 + tests/test_gencpp_full_suite.py | 96 + 56 files changed, 26317 insertions(+) create mode 100644 tests/assets/gencpp_samples/auxiliary/builder.cpp create mode 100644 tests/assets/gencpp_samples/auxiliary/builder.hpp create mode 100644 tests/assets/gencpp_samples/auxiliary/gen_template.hpp create mode 100644 tests/assets/gencpp_samples/auxiliary/scanner.cpp create mode 100644 tests/assets/gencpp_samples/auxiliary/scanner.hpp create mode 100644 tests/assets/gencpp_samples/base.cpp create mode 100644 tests/assets/gencpp_samples/components/ast.cpp create mode 100644 tests/assets/gencpp_samples/components/ast.hpp create mode 100644 tests/assets/gencpp_samples/components/ast_case_macros.cpp create mode 100644 tests/assets/gencpp_samples/components/ast_types.hpp create mode 100644 tests/assets/gencpp_samples/components/code_serialization.cpp create mode 100644 tests/assets/gencpp_samples/components/code_types.hpp create mode 100644 tests/assets/gencpp_samples/components/constants.hpp create mode 100644 tests/assets/gencpp_samples/components/header_end.hpp create mode 100644 tests/assets/gencpp_samples/components/header_start.hpp create mode 100644 tests/assets/gencpp_samples/components/inlines.hpp create mode 100644 tests/assets/gencpp_samples/components/interface.cpp create mode 100644 tests/assets/gencpp_samples/components/interface.hpp create mode 100644 tests/assets/gencpp_samples/components/interface.parsing.cpp create mode 100644 tests/assets/gencpp_samples/components/interface.untyped.cpp create mode 100644 tests/assets/gencpp_samples/components/interface.upfront.cpp create mode 100644 tests/assets/gencpp_samples/components/lexer.cpp create mode 100644 tests/assets/gencpp_samples/components/parser.cpp create mode 100644 tests/assets/gencpp_samples/components/parser_case_macros.cpp create mode 100644 tests/assets/gencpp_samples/components/parser_types.hpp create mode 100644 tests/assets/gencpp_samples/components/src_start.cpp create mode 100644 tests/assets/gencpp_samples/components/static_data.cpp create mode 100644 tests/assets/gencpp_samples/components/types.hpp create mode 100644 tests/assets/gencpp_samples/dependencies/basic_types.hpp create mode 100644 tests/assets/gencpp_samples/dependencies/containers.hpp create mode 100644 tests/assets/gencpp_samples/dependencies/debug.cpp create mode 100644 tests/assets/gencpp_samples/dependencies/debug.hpp create mode 100644 tests/assets/gencpp_samples/dependencies/filesystem.cpp create mode 100644 tests/assets/gencpp_samples/dependencies/filesystem.hpp create mode 100644 tests/assets/gencpp_samples/dependencies/hashing.cpp create mode 100644 tests/assets/gencpp_samples/dependencies/hashing.hpp create mode 100644 tests/assets/gencpp_samples/dependencies/macros.hpp create mode 100644 tests/assets/gencpp_samples/dependencies/memory.cpp create mode 100644 tests/assets/gencpp_samples/dependencies/memory.hpp create mode 100644 tests/assets/gencpp_samples/dependencies/parsing.cpp create mode 100644 tests/assets/gencpp_samples/dependencies/parsing.hpp create mode 100644 tests/assets/gencpp_samples/dependencies/platform.hpp create mode 100644 tests/assets/gencpp_samples/dependencies/printing.cpp create mode 100644 tests/assets/gencpp_samples/dependencies/printing.hpp create mode 100644 tests/assets/gencpp_samples/dependencies/src_start.cpp create mode 100644 tests/assets/gencpp_samples/dependencies/string_ops.cpp create mode 100644 tests/assets/gencpp_samples/dependencies/string_ops.hpp create mode 100644 tests/assets/gencpp_samples/dependencies/strings.cpp create mode 100644 tests/assets/gencpp_samples/dependencies/strings.hpp create mode 100644 tests/assets/gencpp_samples/dependencies/timing.cpp create mode 100644 tests/assets/gencpp_samples/dependencies/timing.hpp create mode 100644 tests/assets/gencpp_samples/gen.cpp create mode 100644 tests/assets/gencpp_samples/gen.dep.cpp create mode 100644 tests/assets/gencpp_samples/gen.dep.hpp create mode 100644 tests/assets/gencpp_samples/gen.hpp create mode 100644 tests/test_gencpp_full_suite.py diff --git a/tests/assets/gencpp_samples/auxiliary/builder.cpp b/tests/assets/gencpp_samples/auxiliary/builder.cpp new file mode 100644 index 0000000..391f520 --- /dev/null +++ b/tests/assets/gencpp_samples/auxiliary/builder.cpp @@ -0,0 +1,61 @@ +#ifdef INTELLISENSE_DIRECTIVES +# include "builder.hpp" +#endif + +#pragma region Builder + +Builder builder_open( char const* path ) +{ + Builder result; + + FileError error = file_open_mode( & result.File, EFileMode_WRITE, path ); + if ( error != EFileError_NONE ) + { + log_failure( "gen::File::open - Could not open file: %s", path); + return result; + } + + Context* ctx = get_context(); + GEN_ASSERT_NOT_NULL(ctx); + result.Buffer = strbuilder_make_reserve( ctx->Allocator_Temp, ctx->InitSize_BuilderBuffer ); + + // log_fmt("$Builder - Opened file: %s\n", result.File.filename ); + return result; +} + +void builder_pad_lines( Builder* builder, s32 num ) +{ + strbuilder_append_str( & builder->Buffer, txt("\n") ); +} + +void builder_print( Builder* builder, Code code ) +{ + StrBuilder str = code_to_strbuilder(code); + // const ssize len = str.length(); + // log_fmt( "%s - print: %.*s\n", File.filename, len > 80 ? 80 : len, str.Data ); + strbuilder_append_string( & builder->Buffer, str ); +} + +void builder_print_fmt_va( Builder* builder, char const* fmt, va_list va ) +{ + ssize res; + char buf[ GEN_PRINTF_MAXLEN ] = { 0 }; + + res = c_str_fmt_va( buf, count_of( buf ) - 1, fmt, va ) - 1; + + strbuilder_append_c_str_len( (StrBuilder*) & (builder->Buffer), (char const*)buf, res); +} + +void builder_write(Builder* builder) +{ + b32 result = file_write( & builder->File, builder->Buffer, strbuilder_length(builder->Buffer) ); + + if ( result == false ) + log_failure("gen::File::write - Failed to write to file: %s\n", file_name( & builder->File ) ); + + log_fmt( "Generated: %s\n", builder->File.filename ); + file_close( & builder->File ); + strbuilder_free(& builder->Buffer); +} + +#pragma endregion Builder diff --git a/tests/assets/gencpp_samples/auxiliary/builder.hpp b/tests/assets/gencpp_samples/auxiliary/builder.hpp new file mode 100644 index 0000000..f8c18c7 --- /dev/null +++ b/tests/assets/gencpp_samples/auxiliary/builder.hpp @@ -0,0 +1,71 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "helpers/push_ignores.inline.hpp" +# include "components/header_start.hpp" +# include "components/types.hpp" +# include "components/gen/ecodetypes.hpp" +# include "components/gen/eoperator.hpp" +# include "components/gen/especifier.hpp" +# include "components/ast.hpp" +# include "components/code_types.hpp" +# include "components/ast_types.hpp" +# include "components/interface.hpp" +# include "components/inlines.hpp" +# include "components/gen/ast_inlines.hpp" +# include "components/header_end.hpp" +using namespace gen; +#endif + +#pragma region Builder + +struct Builder; +typedef struct Builder Builder; + +GEN_API Builder builder_open ( char const* path ); +GEN_API void builder_pad_lines ( Builder* builder, s32 num ); +GEN_API void builder_print ( Builder* builder, Code code ); +GEN_API void builder_print_fmt_va( Builder* builder, char const* fmt, va_list va ); +GEN_API void builder_write ( Builder* builder ); + +forceinline void builder_print_fmt ( Builder* builder, char const* fmt, ... ) { + va_list va; + va_start( va, fmt ); + builder_print_fmt_va( builder, fmt, va ); + va_end( va ); +} + +struct Builder +{ + FileInfo File; + StrBuilder Buffer; + +#if GEN_COMPILER_CPP && ! GEN_C_LIKE_CPP + forceinline static Builder open( char const* path ) { return builder_open(path); } + + forceinline void pad_lines( s32 num ) { return builder_pad_lines(this, num); } + + forceinline void print( Code code ) { return builder_print(this, code); } + forceinline void print_fmt( char const* fmt, ... ) { + va_list va; + va_start( va, fmt ); + builder_print_fmt_va( this, fmt, va ); + va_end( va ); + } + + forceinline void write() { return builder_write(this); } +#endif +}; + +#if GEN_COMPILER_CPP && ! GEN_C_LIKE_CPP +forceinline void builder_pad_lines( Builder& builder, s32 num ) { return builder_pad_lines(& builder, num); } +forceinline void builder_print ( Builder& builder, Code code ) { return builder_print(& builder, code); } +forceinline void builder_write ( Builder& builder ) { return builder_write(& builder ); } +forceinline void builder_print_fmt( Builder& builder, char const* fmt, ...) { + va_list va; + va_start( va, fmt ); + builder_print_fmt_va( & builder, fmt, va ); + va_end( va ); +} +#endif + +#pragma endregion Builder diff --git a/tests/assets/gencpp_samples/auxiliary/gen_template.hpp b/tests/assets/gencpp_samples/auxiliary/gen_template.hpp new file mode 100644 index 0000000..04b5734 --- /dev/null +++ b/tests/assets/gencpp_samples/auxiliary/gen_template.hpp @@ -0,0 +1,35 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "helpers/push_ignores.inline.hpp" +# include "components/header_start.hpp" +# include "components/types.hpp" +# include "components/gen/ecode.hpp" +# include "components/gen/eoperator.hpp" +# include "components/gen/especifier.hpp" +# include "components/ast.hpp" +# include "components/code_types.hpp" +# include "components/ast_types.hpp" +# include "components/interface.hpp" +# include "components/inlines.hpp" +# include "components/gen/ast_inlines.hpp" +# include "components/header_end.hpp" +#endif + +/* + Explicitly generates a resolved definition of a cpp template definition. + + TODO(Ed): Needs implementing for the C-library variant. + TODO(Ed): We need a non syntax subst implemtnation for Strings for this to work. It must subst keywords directly based on template parameter names. + + This is only meant to be used on relatively trivial templates, where the type or numeric is mostly a 'duck' type. + It cannot parse complex template parameters. + + The varadic args should correspond 1:1 with the type of objects the generator expects from the template's parameters.alignas. +*/ + +CodeOperator gen_operator_template( CodeTemplate template, ... ); +CodeFn gen_func_template( CodeTemplate template, ... ); +Code gen_class_struct_template( CodeTemplate template, ... ); + +Code gen_template( CodeTemplate template, ... ); +Code gen_template( Str template, Str instantiation ); diff --git a/tests/assets/gencpp_samples/auxiliary/scanner.cpp b/tests/assets/gencpp_samples/auxiliary/scanner.cpp new file mode 100644 index 0000000..de0fd7b --- /dev/null +++ b/tests/assets/gencpp_samples/auxiliary/scanner.cpp @@ -0,0 +1,148 @@ +#ifdef INTELLISENSE_DIRECTIVES +# include "scanner.hpp" +#endif + +#pragma region Scanner + +Code scan_file( char const* path ) +{ + FileInfo file; + + FileError error = file_open_mode( & file, EFileMode_READ, path ); + if ( error != EFileError_NONE ) + { + GEN_FATAL( "scan_file: Could not open: %s", path ); + } + + ssize fsize = file_size( & file ); + if ( fsize <= 0 ) + { + GEN_FATAL("scan_file: %s is empty", path ); + } + + StrBuilder str = strbuilder_make_reserve( get_context()->Allocator_Temp, fsize ); + file_read( & file, str, fsize ); + strbuilder_get_header(str)->Length = fsize; + + // Skip INTELLISENSE_DIRECTIVES preprocessor blocks + // Its designed so that the directive should be the first thing in the file. + // Anything that comes before it will also be omitted. + { + #define current (*scanner) + #define matched 0 + #define move_fwd() do { ++ scanner; -- left; } while (0) + const Str directive_start = txt( "ifdef" ); + const Str directive_end = txt( "endif" ); + const Str def_intellisense = txt("INTELLISENSE_DIRECTIVES" ); + + bool found_directive = false; + char const* scanner = (char const*)str; + s32 left = fsize; + while ( left ) + { + // Processing directive. + if ( current == '#' ) + { + move_fwd(); + while ( left && char_is_space( current ) ) + move_fwd(); + + if ( ! found_directive ) + { + if ( left && c_str_compare_len( scanner, directive_start.Ptr, directive_start.Len ) == matched ) + { + scanner += directive_start.Len; + left -= directive_start.Len; + + while ( left && char_is_space( current ) ) + move_fwd(); + + if ( left && c_str_compare_len( scanner, def_intellisense.Ptr, def_intellisense.Len ) == matched ) + { + scanner += def_intellisense.Len; + left -= def_intellisense.Len; + + found_directive = true; + } + } + + // Skip to end of line + while ( left && current != '\r' && current != '\n' ) + move_fwd(); + move_fwd(); + + if ( left && current == '\n' ) + move_fwd(); + + continue; + } + + if ( left && c_str_compare_len( scanner, directive_end.Ptr, directive_end.Len ) == matched ) + { + scanner += directive_end.Len; + left -= directive_end.Len; + + // Skip to end of line + while ( left && current != '\r' && current != '\n' ) + move_fwd(); + move_fwd(); + + if ( left && current == '\n' ) + move_fwd(); + + // sptr skip_size = fsize - left; + if ( (scanner + 2) >= ( (char const*) str + fsize ) ) + { + mem_move( str, scanner, left ); + strbuilder_get_header(str)->Length = left; + break; + } + + mem_move( str, scanner, left ); + strbuilder_get_header(str)->Length = left; + + break; + } + } + + move_fwd(); + } + #undef move_fwd + #undef matched + #undef current + } + + file_close( & file ); + return untyped_str( strbuilder_to_str(str) ); +} + +CodeBody parse_file( const char* path ) { + FileContents file = file_read_contents( get_context()->Allocator_Temp, true, path ); + Str content = { (char const*)file.data, file.size }; + CodeBody code = parse_global_body( content ); + log_fmt("\nParsed: %s\n", path); + return code; +} + +CSV_Column parse_csv_one_column(AllocatorInfo allocator, char const* path) { + FileContents content = file_read_contents( allocator, file_zero_terminate, path ); + Arena csv_arena = arena_init_from_memory(content.data, content.size); + + CSV_Column result; + csv_parse( & result.ADT, rcast(char*, content.data), allocator, false ); + result.Content = result.ADT.nodes[0].nodes; + return result; +} + +CSV_Columns2 parse_csv_two_columns(AllocatorInfo allocator, char const* path) { + FileContents content = file_read_contents( allocator, file_zero_terminate, path ); + Arena csv_arena = arena_init_from_memory(content.data, content.size); + + CSV_Columns2 result; + csv_parse( & result.ADT, rcast(char*, content.data), allocator, false ); + result.Col_1 = result.ADT.nodes[0].nodes; + result.Col_2 = result.ADT.nodes[1].nodes; + return result; +} + +#pragma endregion Scanner diff --git a/tests/assets/gencpp_samples/auxiliary/scanner.hpp b/tests/assets/gencpp_samples/auxiliary/scanner.hpp new file mode 100644 index 0000000..fbeaf11 --- /dev/null +++ b/tests/assets/gencpp_samples/auxiliary/scanner.hpp @@ -0,0 +1,46 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "helpers/push_ignores.inline.hpp" +# include "components/header_start.hpp" +# include "components/types.hpp" +# include "components/gen/ecodetypes.hpp" +# include "components/gen/eoperator.hpp" +# include "components/gen/especifier.hpp" +# include "components/ast.hpp" +# include "components/code_types.hpp" +# include "components/ast_types.hpp" +# include "components/interface.hpp" +# include "components/inlines.hpp" +# include "components/gen/ast_inlines.hpp" +# include "components/header_end.hpp" +#endif + +#pragma region Scanner + +// This is a simple file reader that reads the entire file into memory. +// It has an extra option to skip the first few lines for undesired includes. +// This is done so that includes can be kept in dependency and component files so that intellisense works. +GEN_API Code scan_file( char const* path ); + +GEN_API CodeBody parse_file( const char* path ); + +// The follow is basic support for light csv parsing (use it as an example) +// Make something robust if its more serious. + +typedef struct CSV_Column CSV_Column; +struct CSV_Column { + CSV_Object ADT; + Array(ADT_Node) Content; +}; + +typedef struct CSV_Columns2 CSV_Columns2; +struct CSV_Columns2 { + CSV_Object ADT; + Array(ADT_Node) Col_1; + Array(ADT_Node) Col_2; +}; + +GEN_API CSV_Column parse_csv_one_column (AllocatorInfo allocator, char const* path); +GEN_API CSV_Columns2 parse_csv_two_columns(AllocatorInfo allocator, char const* path); + +#pragma endregion Scanner diff --git a/tests/assets/gencpp_samples/base.cpp b/tests/assets/gencpp_samples/base.cpp new file mode 100644 index 0000000..81433e4 --- /dev/null +++ b/tests/assets/gencpp_samples/base.cpp @@ -0,0 +1,74 @@ +#define GEN_DEFINE_LIBRARY_CODE_CONSTANTS +#define GEN_ENFORCE_STRONG_CODE_TYPES +#define GEN_C_LIKE_CPP 1 +#include "gen.cpp" +#include "helpers/push_ignores.inline.hpp" + +#include + +GEN_NS_BEGIN +#include "helpers/base_codegen.hpp" +#include "helpers/misc.hpp" +GEN_NS_END + +using namespace gen; + +constexpr char const* path_format_style = "../scripts/.clang-format"; +constexpr char const* scratch_file = "build/scratch.hpp"; + +Code format( Code code ) { + return code_refactor_and_format(code, scratch_file, nullptr, path_format_style ); +} + +constexpr char const* generation_notice = +"// This file was generated automatially by gencpp's bootstrap.cpp " +"(See: https://github.com/Ed94/gencpp)\n\n"; + +int gen_main() +{ + gen::Context ctx {}; + gen::init( & ctx); + + CodeBody gen_component_header = def_global_body( args( + def_preprocess_cond( PreprocessCond_IfDef, txt("INTELLISENSE_DIRECTIVES") ), + pragma_once, + def_include(txt("components/types.hpp")), + preprocess_endif, + fmt_newline, + untyped_str( to_str_from_c_str(generation_notice) ) + )); + + CodeBody ecode = gen_ecode ( "enums/ECodeTypes.csv" ); + CodeBody eoperator = gen_eoperator ( "enums/EOperator.csv" ); + CodeBody especifier = gen_especifier( "enums/ESpecifier.csv" ); + CodeBody etoktype = gen_etoktype ( "enums/ETokType.csv", "enums/AttributeTokens.csv" ); + CodeBody ast_inlines = gen_ast_inlines(); + + Builder header_ecode = builder_open( "components/gen/ecodetypes.hpp" ); + builder_print( & header_ecode, gen_component_header ); + builder_print( & header_ecode, format(ecode) ); + builder_write( & header_ecode); + + Builder header_eoperator = builder_open( "components/gen/eoperator.hpp" ); + builder_print( & header_eoperator, gen_component_header ); + builder_print( & header_eoperator, format(eoperator) ); + builder_write( & header_eoperator ); + + Builder header_especifier = builder_open( "components/gen/especifier.hpp" ); + builder_print( & header_especifier, gen_component_header ); + builder_print( & header_especifier, format(especifier) ); + builder_write( & header_especifier); + + Builder header_etoktype = builder_open( "components/gen/etoktype.hpp" ); + builder_print( & header_etoktype, gen_component_header ); + builder_print( & header_etoktype, format(etoktype) ); + builder_write( & header_etoktype); + + Builder header_ast_inlines = builder_open( "components/gen/ast_inlines.hpp" ); + builder_print( & header_ast_inlines, gen_component_header ); + builder_print( & header_ast_inlines, format(ast_inlines) ); + builder_write( & header_ast_inlines); + + gen::deinit(& ctx); + return 0; +} diff --git a/tests/assets/gencpp_samples/components/ast.cpp b/tests/assets/gencpp_samples/components/ast.cpp new file mode 100644 index 0000000..d8042b5 --- /dev/null +++ b/tests/assets/gencpp_samples/components/ast.cpp @@ -0,0 +1,1303 @@ +#ifdef INTELLISENSE_DIRECTIVES +#pragma once +#include "static_data.cpp" +#endif + +// This serializes all the data-members in a "debug" format, where each member is printed with its associated value. +Str code_debug_str(Code self) +{ + GEN_ASSERT(self != nullptr); + StrBuilder result_stack = strbuilder_make_reserve( _ctx->Allocator_Temp, kilobytes(1) ); + StrBuilder* result = & result_stack; + + if ( self->Parent ) + strbuilder_append_fmt( result, "\n\tParent : %S %S", code_type_str(self->Parent), self->Name.Len ? self->Name : txt("Null") ); + else + strbuilder_append_fmt( result, "\n\tParent : %S", txt("Null") ); + + strbuilder_append_fmt( result, "\n\tName : %S", self->Name.Len ? self->Name : txt("Null") ); + strbuilder_append_fmt( result, "\n\tType : %S", code_type_str(self) ); + strbuilder_append_fmt( result, "\n\tModule Flags : %S", module_flag_to_str( self->ModuleFlags ) ); + + switch ( self->Type ) + { + case CT_Invalid: + case CT_NewLine: + case CT_Access_Private: + case CT_Access_Protected: + case CT_Access_Public: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + break; + + case CT_Untyped: + case CT_Execution: + case CT_Comment: + case CT_PlatformAttributes: + case CT_Preprocess_Include: + case CT_Preprocess_Pragma: + case CT_Preprocess_If: + case CT_Preprocess_ElIf: + case CT_Preprocess_Else: + case CT_Preprocess_IfDef: + case CT_Preprocess_IfNotDef: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tContent: %S", self->Content ); + break; + + case CT_Preprocess_Define: + // TODO(ED): Needs implementaton + log_failure("code_debug_str: NOT IMPLEMENTED for CT_Preprocess_Define"); + break; + + case CT_Class: + case CT_Struct: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tAttributes : %S", self->Attributes ? strbuilder_to_str( code_to_strbuilder(self->Attributes) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tParentAccess: %S", self->ParentType ? access_spec_to_str( self->ParentAccess ) : txt("No Parent") ); + strbuilder_append_fmt( result, "\n\tParentType : %S", self->ParentType ? code_type_str(self->ParentType) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tBody : %S", self->Body ? code_debug_str(self->Body) : txt("Null") ); + break; + + case CT_Class_Fwd: + case CT_Struct_Fwd: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tAttributes : %S", self->Attributes ? strbuilder_to_str( code_to_strbuilder(self->Attributes) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tParentAccess: %S", self->ParentType ? access_spec_to_str( self->ParentAccess ) : txt("No Parent") ); + strbuilder_append_fmt( result, "\n\tParentType : %S", self->ParentType ? code_type_str(self->ParentType) : txt("Null") ); + break; + + case CT_Constructor: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tSpecs : %S", self->Specs ? strbuilder_to_str( code_to_strbuilder(self->Specs) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tInitializerList: %S", self->InitializerList ? strbuilder_to_str( code_to_strbuilder(self->InitializerList) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tParams : %S", self->Params ? strbuilder_to_str( code_to_strbuilder(self->Params) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tBody : %S", self->Body ? code_debug_str(self->Body) : txt("Null") ); + break; + + case CT_Constructor_Fwd: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tSpecs : %S", self->Specs ? strbuilder_to_str( code_to_strbuilder(self->Specs) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tInitializerList: %S", self->InitializerList ? strbuilder_to_str( code_to_strbuilder(self->InitializerList) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tParams : %S", self->Params ? strbuilder_to_str( code_to_strbuilder(self->Params) ) : txt("Null") ); + break; + + case CT_Destructor: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tSpecs : %S", self->Specs ? strbuilder_to_str( code_to_strbuilder(self->Specs) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tBody : %S", self->Body ? code_debug_str(self->Body) : txt("Null") ); + break; + + case CT_Destructor_Fwd: + break; + + case CT_Enum: + case CT_Enum_Class: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tAttributes : %S", self->Attributes ? strbuilder_to_str( code_to_strbuilder(self->Attributes) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tUnderlying Type : %S", self->UnderlyingType ? strbuilder_to_str( code_to_strbuilder(self->UnderlyingType)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tBody : %S", self->Body ? code_debug_str(self->Body) : txt("Null") ); + break; + + case CT_Enum_Fwd: + case CT_Enum_Class_Fwd: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tAttributes : %S", self->Attributes ? strbuilder_to_str( code_to_strbuilder(self->Attributes) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tUnderlying Type : %S", self->UnderlyingType ? strbuilder_to_str( code_to_strbuilder(self->UnderlyingType)) : txt("Null") ); + break; + + case CT_Extern_Linkage: + case CT_Namespace: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tBody: %S", self->Body ? code_debug_str(self->Body) : txt("Null") ); + break; + + case CT_Friend: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tDeclaration: %S", self->Declaration ? strbuilder_to_str( code_to_strbuilder(self->Declaration)) : txt("Null") ); + break; + + case CT_Function: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tAttributes: %S", self->Attributes ? strbuilder_to_str( code_to_strbuilder(self->Attributes) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tSpecs : %S", self->Specs ? strbuilder_to_str( code_to_strbuilder(self->Specs)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tReturnType: %S", self->ReturnType ? strbuilder_to_str( code_to_strbuilder(self->ReturnType)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tParams : %S", self->Params ? strbuilder_to_str( code_to_strbuilder(self->Params)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tBody : %S", self->Body ? code_debug_str(self->Body) : txt("Null") ); + break; + + case CT_Function_Fwd: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tAttributes: %S", self->Attributes ? strbuilder_to_str( code_to_strbuilder(self->Attributes) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tSpecs : %S", self->Specs ? strbuilder_to_str( code_to_strbuilder(self->Specs)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tReturnType: %S", self->ReturnType ? strbuilder_to_str( code_to_strbuilder(self->ReturnType)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tParams : %S", self->Params ? strbuilder_to_str( code_to_strbuilder(self->Params)) : txt("Null") ); + break; + + case CT_Module: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + break; + + case CT_Operator: + case CT_Operator_Member: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tAttributes: %S", self->Attributes ? strbuilder_to_str( code_to_strbuilder(self->Attributes) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tSpecs : %S", self->Specs ? strbuilder_to_str( code_to_strbuilder(self->Specs)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tReturnType: %S", self->ReturnType ? strbuilder_to_str( code_to_strbuilder(self->ReturnType)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tParams : %S", self->Params ? strbuilder_to_str( code_to_strbuilder(self->Params)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tBody : %S", self->Body ? code_debug_str(self->Body) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tOp : %S", operator_to_str( self->Op ) ); + break; + + case CT_Operator_Fwd: + case CT_Operator_Member_Fwd: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tAttributes: %S", self->Attributes ? strbuilder_to_str( code_to_strbuilder(self->Attributes) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tSpecs : %S", self->Specs ? strbuilder_to_str( code_to_strbuilder(self->Specs) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tReturnType: %S", self->ReturnType ? strbuilder_to_str( code_to_strbuilder(self->ReturnType) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tParams : %S", self->Params ? strbuilder_to_str( code_to_strbuilder(self->Params) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tOp : %S", operator_to_str( self->Op ) ); + break; + + case CT_Operator_Cast: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tSpecs : %S", self->Specs ? strbuilder_to_str( code_to_strbuilder(self->Specs)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tValueType : %S", self->ValueType ? strbuilder_to_str( code_to_strbuilder(self->ValueType)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tBody : %S", self->Body ? code_debug_str(self->Body) : txt("Null") ); + break; + + case CT_Operator_Cast_Fwd: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tSpecs : %S", self->Specs ? strbuilder_to_str( code_to_strbuilder(self->Specs)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tValueType : %S", self->ValueType ? strbuilder_to_str( code_to_strbuilder(self->ValueType)) : txt("Null") ); + break; + + case CT_Parameters: + strbuilder_append_fmt( result, "\n\tNumEntries: %d", self->NumEntries ); + strbuilder_append_fmt( result, "\n\tLast : %S", self->Last->Name ); + strbuilder_append_fmt( result, "\n\tNext : %S", self->Next->Name ); + strbuilder_append_fmt( result, "\n\tValueType : %S", self->ValueType ? strbuilder_to_str( code_to_strbuilder(self->ValueType)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tValue : %S", self->Value ? strbuilder_to_str( code_to_strbuilder(self->Value)) : txt("Null") ); + break; + + case CT_Parameters_Define: + // TODO(ED): Needs implementaton + log_failure("code_debug_str: NOT IMPLEMENTED for CT_Parameters_Define"); + break; + + case CT_Specifiers: + { + strbuilder_append_fmt( result, "\n\tNumEntries: %d", self->NumEntries ); + strbuilder_append_str( result, txt("\n\tArrSpecs: ") ); + + s32 idx = 0; + s32 left = self->NumEntries; + while ( left-- ) + { + Str spec = spec_to_str( self->ArrSpecs[idx] ); + strbuilder_append_fmt( result, "%.*s, ", spec.Len, spec.Ptr ); + idx++; + } + strbuilder_append_fmt( result, "\n\tNextSpecs: %S", self->NextSpecs ? code_debug_str(self->NextSpecs) : txt("Null") ); + } + break; + + case CT_Template: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tParams : %S", self->Params ? strbuilder_to_str( code_to_strbuilder(self->Params)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tDeclaration: %S", self->Declaration ? strbuilder_to_str( code_to_strbuilder(self->Declaration)) : txt("Null") ); + break; + + case CT_Typedef: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tUnderlyingType: %S", self->UnderlyingType ? strbuilder_to_str( code_to_strbuilder(self->UnderlyingType)) : txt("Null") ); + break; + + case CT_Typename: + strbuilder_append_fmt( result, "\n\tAttributes : %S", self->Attributes ? strbuilder_to_str( code_to_strbuilder(self->Attributes) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tSpecs : %S", self->Specs ? strbuilder_to_str( code_to_strbuilder(self->Specs)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tReturnType : %S", self->ReturnType ? strbuilder_to_str( code_to_strbuilder(self->ReturnType)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tParams : %S", self->Params ? strbuilder_to_str( code_to_strbuilder(self->Params)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tArrExpr : %S", self->ArrExpr ? strbuilder_to_str( code_to_strbuilder(self->ArrExpr)) : txt("Null") ); + break; + + case CT_Union: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tAttributes: %S", self->Attributes ? strbuilder_to_str( code_to_strbuilder(self->Attributes) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tBody : %S", self->Body ? code_debug_str(self->Body) : txt("Null") ); + break; + + case CT_Using: + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tAttributes : %S", self->Attributes ? strbuilder_to_str( code_to_strbuilder(self->Attributes) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tUnderlyingType: %S", self->UnderlyingType ? strbuilder_to_str( code_to_strbuilder(self->UnderlyingType)) : txt("Null") ); + break; + + case CT_Variable: + + if ( self->Parent && self->Parent->Type == CT_Variable ) + { + // Its a NextVar + strbuilder_append_fmt( result, "\n\tSpecs : %S", self->Specs ? strbuilder_to_str( code_to_strbuilder(self->Specs)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tValue : %S", self->Value ? strbuilder_to_str( code_to_strbuilder(self->Value)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tBitfieldSize: %S", self->BitfieldSize ? strbuilder_to_str( code_to_strbuilder(self->BitfieldSize)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tNextVar : %S", self->NextVar ? code_debug_str(self->NextVar) : txt("Null") ); + break; + } + + if ( self->Prev ) + strbuilder_append_fmt( result, "\n\tPrev: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + if ( self->Next ) + strbuilder_append_fmt( result, "\n\tNext: %S %S", code_type_str(self->Prev), self->Prev->Name.Len ? self->Prev->Name : txt("Null") ); + + strbuilder_append_fmt( result, "\n\tInlineCmt : %S", self->InlineCmt ? self->InlineCmt->Content : txt("Null") ); + strbuilder_append_fmt( result, "\n\tAttributes : %S", self->Attributes ? strbuilder_to_str( code_to_strbuilder(self->Attributes) ) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tSpecs : %S", self->Specs ? strbuilder_to_str( code_to_strbuilder(self->Specs)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tValueType : %S", self->ValueType ? strbuilder_to_str( code_to_strbuilder(self->ValueType)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tBitfieldSize: %S", self->BitfieldSize ? strbuilder_to_str( code_to_strbuilder(self->BitfieldSize)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tValue : %S", self->Value ? strbuilder_to_str( code_to_strbuilder(self->Value)) : txt("Null") ); + strbuilder_append_fmt( result, "\n\tNextVar : %S", self->NextVar ? code_debug_str(self->NextVar) : txt("Null") ); + break; + } + + return strbuilder_to_str( * result ); +} + +Code code_duplicate(Code self) +{ + Code result = make_code(); + + void* mem_result = rcast(void*, cast(AST*, result)); + void* mem_self = rcast(void*, cast(AST*, self)); + mem_copy( mem_result, mem_self, sizeof( AST ) ); + + result->Parent = NullCode; + return result; +} + +StrBuilder code_to_strbuilder(Code self) +{ + StrBuilder result = strbuilder_make_str( _ctx->Allocator_Temp, txt("") ); + code_to_strbuilder_ref( self, & result ); + return result; +} + +void code_to_strbuilder_ref( Code self, StrBuilder* result ) +{ + GEN_ASSERT(self != nullptr); + local_persist thread_local + char SerializationLevel = 0; + + switch ( self->Type ) + { + case CT_Invalid: + #ifdef GEN_DONT_ALLOW_INVALID_CODE + log_failure("Attempted to serialize invalid code! - %S", Parent ? Parent->code_debug_str() : Name ); + #else + strbuilder_append_fmt( result, "Invalid Code!" ); + #endif + break; + + case CT_NewLine: + strbuilder_append_str( result, txt("\n")); + break; + + case CT_Untyped: + case CT_Execution: + case CT_Comment: + case CT_PlatformAttributes: + strbuilder_append_str( result, self->Content ); + break; + + case CT_Access_Private: + case CT_Access_Protected: + case CT_Access_Public: + strbuilder_append_str( result, self->Name ); + break; + + case CT_Class: + class_to_strbuilder_def(cast(CodeClass, self), result ); + break; + + case CT_Class_Fwd: + class_to_strbuilder_fwd(cast(CodeClass, self), result ); + break; + + case CT_Constructor: + constructor_to_strbuilder_def(cast(CodeConstructor, self), result ); + break; + + case CT_Constructor_Fwd: + constructor_to_strbuilder_fwd(cast(CodeConstructor, self), result ); + break; + + case CT_Destructor: + destructor_to_strbuilder_def(cast(CodeDestructor, self), result ); + break; + + case CT_Destructor_Fwd: + destructor_to_strbuilder_fwd(cast(CodeDestructor, self), result ); + break; + + case CT_Enum: + enum_to_strbuilder_def(cast(CodeEnum, self), result ); + break; + + case CT_Enum_Fwd: + enum_to_strbuilder_fwd(cast(CodeEnum, self), result ); + break; + + case CT_Enum_Class: + enum_to_strbuilder_class_def(cast(CodeEnum, self), result ); + break; + + case CT_Enum_Class_Fwd: + enum_to_strbuilder_class_fwd(cast(CodeEnum, self), result ); + break; + + case CT_Export_Body: + body_to_strbuilder_export(cast(CodeBody, self), result ); + break; + + case CT_Extern_Linkage: + extern_to_strbuilder(cast(CodeExtern, self), result ); + break; + + case CT_Friend: + friend_to_strbuilder_ref(cast(CodeFriend, self), result ); + break; + + case CT_Function: + fn_to_strbuilder_def(cast(CodeFn, self), result ); + break; + + case CT_Function_Fwd: + fn_to_strbuilder_fwd(cast(CodeFn, self), result ); + break; + + case CT_Module: + module_to_strbuilder_ref(cast(CodeModule, self), result ); + break; + + case CT_Namespace: + namespace_to_strbuilder_ref(cast(CodeNS, self), result ); + break; + + case CT_Operator: + case CT_Operator_Member: + code_op_to_strbuilder_def(cast(CodeOperator, self), result ); + break; + + case CT_Operator_Fwd: + case CT_Operator_Member_Fwd: + code_op_to_strbuilder_fwd(cast(CodeOperator, self), result ); + break; + + case CT_Operator_Cast: + opcast_to_strbuilder_def(cast(CodeOpCast, self), result ); + break; + + case CT_Operator_Cast_Fwd: + opcast_to_strbuilder_fwd(cast(CodeOpCast, self), result ); + break; + + case CT_Parameters: + params_to_strbuilder_ref(cast(CodeParams, self), result ); + break; + + case CT_Parameters_Define: + define_params_to_strbuilder_ref(cast(CodeDefineParams, self), result); + break; + + case CT_Preprocess_Define: + define_to_strbuilder_ref(cast(CodeDefine, self), result ); + break; + + case CT_Preprocess_If: + preprocess_to_strbuilder_if(cast(CodePreprocessCond, self), result ); + break; + + case CT_Preprocess_IfDef: + preprocess_to_strbuilder_ifdef(cast(CodePreprocessCond, self), result ); + break; + + case CT_Preprocess_IfNotDef: + preprocess_to_strbuilder_ifndef(cast(CodePreprocessCond, self), result ); + break; + + case CT_Preprocess_Include: + include_to_strbuilder_ref(cast(CodeInclude, self), result ); + break; + + case CT_Preprocess_ElIf: + preprocess_to_strbuilder_elif(cast(CodePreprocessCond, self), result ); + break; + + case CT_Preprocess_Else: + preprocess_to_strbuilder_else(cast(CodePreprocessCond, self), result ); + break; + + case CT_Preprocess_EndIf: + preprocess_to_strbuilder_endif(cast(CodePreprocessCond, self), result ); + break; + + case CT_Preprocess_Pragma: + pragma_to_strbuilder_ref(cast(CodePragma, self), result ); + break; + + case CT_Specifiers: + specifiers_to_strbuilder_ref(cast(CodeSpecifiers, self), result ); + break; + + case CT_Struct: + struct_to_strbuilder_def(cast(CodeStruct, self), result ); + break; + + case CT_Struct_Fwd: + struct_to_strbuilder_fwd(cast(CodeStruct, self), result ); + break; + + case CT_Template: + template_to_strbuilder_ref(cast(CodeTemplate, self), result ); + break; + + case CT_Typedef: + typedef_to_strbuilder_ref(cast(CodeTypedef, self), result ); + break; + + case CT_Typename: + typename_to_strbuilder_ref(cast(CodeTypename, self), result ); + break; + + case CT_Union: + union_to_strbuilder_def( cast(CodeUnion, self), result ); + break; + + case CT_Union_Fwd: + union_to_strbuilder_fwd( cast(CodeUnion, self), result ); + break; + + case CT_Using: + using_to_strbuilder_ref(cast(CodeUsing, self), result ); + break; + + case CT_Using_Namespace: + using_to_strbuilder_ns(cast(CodeUsing, self), result ); + break; + + case CT_Variable: + var_to_strbuilder_ref(cast(CodeVar, self), result ); + break; + + case CT_Enum_Body: + case CT_Class_Body: + case CT_Extern_Linkage_Body: + case CT_Function_Body: + case CT_Global_Body: + case CT_Namespace_Body: + case CT_Struct_Body: + case CT_Union_Body: + body_to_strbuilder_ref( cast(CodeBody, self), result ); + break; + } +} + +bool code_is_equal( Code self, Code other ) +{ +/* + AST values are either some u32 value, a cached string, or a pointer to another AST. + + u32 values are compared by value. + Cached strings are compared by pointer. + AST nodes are compared with AST::is_equal. +*/ + if ( other == nullptr ) + { + log_fmt( "AST::is_equal: other is null\nAST: %S", code_debug_str(self) ); + return false; + } + + if ( self->Type != other->Type ) + { + log_fmt("AST::is_equal: Type check failure with other\nAST: %S\nOther: %S" + , code_debug_str(self) + , code_debug_str(other) + ); + + return false; + } + + switch ( self->Type ) + { + #define check_member_val( val ) \ + if ( self->val != other->val ) \ + { \ + log_fmt("\nAST::is_equal: Member - " #val " failed\n" \ + "AST : %S\n" \ + "Other: %S\n" \ + , code_debug_str(self) \ + ,code_debug_str(other) \ + ); \ + \ + return false; \ + } + + #define check_member_str( str ) \ + if ( ! str_are_equal( self->str, other->str ) ) \ + { \ + log_fmt("\nAST::is_equal: Member string - "#str " failed\n" \ + "AST : %S\n" \ + "Other: %S\n" \ + , code_debug_str(self) \ + ,code_debug_str(other) \ + ); \ + \ + return false; \ + } + + #define check_member_content( content ) \ + if ( ! str_are_equal( self->content, other->content )) \ + { \ + log_fmt("\nAST::is_equal: Member content - "#content " failed\n" \ + "AST : %S\n" \ + "Other: %S\n" \ + , code_debug_str(self) \ + , code_debug_str(other) \ + ); \ + \ + log_fmt("Content cannot be trusted to be unique with this check " \ + "so it must be verified by eye for now\n" \ + "AST Content:\n%S\n" \ + "Other Content:\n%S\n" \ + , str_visualize_whitespace(self->content, _ctx->Allocator_Temp) \ + , str_visualize_whitespace(other->content, _ctx->Allocator_Temp) \ + ); \ + } + + #define check_member_ast( ast ) \ + if ( self->ast ) \ + { \ + if ( other->ast == nullptr ) \ + { \ + log_fmt("\nAST::is_equal: Failed for member " #ast " other equivalent param is null\n" \ + "AST : %S\n" \ + "Other: %S\n" \ + "For ast member: %S\n" \ + , code_debug_str(self) \ + , code_debug_str(other) \ + , code_debug_str(self->ast) \ + ); \ + \ + return false; \ + } \ + \ + if ( ! code_is_equal(self->ast, other->ast ) ) \ + { \ + log_fmt( "\nAST::is_equal: Failed for " #ast"\n" \ + "AST : %S\n" \ + "Other: %S\n" \ + "For ast member: %S\n" \ + "other's ast member: %S\n" \ + , code_debug_str(self) \ + , code_debug_str(other) \ + , code_debug_str(self->ast) \ + , code_debug_str(other->ast) \ + ); \ + \ + return false; \ + } \ + } + + case CT_NewLine: + case CT_Access_Public: + case CT_Access_Protected: + case CT_Access_Private: + case CT_Preprocess_Else: + case CT_Preprocess_EndIf: + return true; + + + // Comments are not validated. + case CT_Comment: + return true; + + case CT_Execution: + case CT_PlatformAttributes: + case CT_Untyped: + { + check_member_content( Content ); + return true; + } + + case CT_Class_Fwd: + case CT_Struct_Fwd: + { + check_member_str( Name ); + check_member_ast( ParentType ); + check_member_val( ParentAccess ); + check_member_ast( Attributes ); + + return true; + } + + case CT_Class: + case CT_Struct: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( ParentType ); + check_member_val( ParentAccess ); + check_member_ast( Attributes ); + check_member_ast( Body ); + + return true; + } + + case CT_Constructor: + { + check_member_ast( InitializerList ); + check_member_ast( Params ); + check_member_ast( Body ); + + return true; + } + + case CT_Constructor_Fwd: + { + check_member_ast( InitializerList ); + check_member_ast( Params ); + + return true; + } + + case CT_Destructor: + { + check_member_ast( Specs ); + check_member_ast( Body ); + + return true; + } + + case CT_Destructor_Fwd: + { + check_member_ast( Specs ); + + return true; + } + + case CT_Enum: + case CT_Enum_Class: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( Attributes ); + check_member_ast( UnderlyingType ); + check_member_ast( Body ); + check_member_ast( UnderlyingTypeMacro ); + + return true; + } + + case CT_Enum_Fwd: + case CT_Enum_Class_Fwd: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( Attributes ); + check_member_ast( UnderlyingType ); + check_member_ast( UnderlyingTypeMacro ); + + return true; + } + + case CT_Extern_Linkage: + { + check_member_str( Name ); + check_member_ast( Body ); + + return true; + } + + case CT_Friend: + { + check_member_str( Name ); + check_member_ast( Declaration ); + + return true; + } + + case CT_Function: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( ReturnType ); + check_member_ast( Attributes ); + check_member_ast( Specs ); + check_member_ast( Params ); + check_member_ast( Body ); + + return true; + } + + case CT_Function_Fwd: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( ReturnType ); + check_member_ast( Attributes ); + check_member_ast( Specs ); + check_member_ast( Params ); + + return true; + } + + case CT_Module: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + + return true; + } + + case CT_Namespace: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( Body ); + + return true; + } + + case CT_Operator: + case CT_Operator_Member: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( ReturnType ); + check_member_ast( Attributes ); + check_member_ast( Specs ); + check_member_ast( Params ); + check_member_ast( Body ); + + return true; + } + + case CT_Operator_Fwd: + case CT_Operator_Member_Fwd: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( ReturnType ); + check_member_ast( Attributes ); + check_member_ast( Specs ); + check_member_ast( Params ); + + return true; + } + + case CT_Operator_Cast: + { + check_member_str( Name ); + check_member_ast( Specs ); + check_member_ast( ValueType ); + check_member_ast( Body ); + + return true; + } + + case CT_Operator_Cast_Fwd: + { + check_member_str( Name ); + check_member_ast( Specs ); + check_member_ast( ValueType ); + + return true; + } + + case CT_Parameters: + { + if ( self->NumEntries > 1 ) + { + Code curr = self; + Code curr_other = other; + while ( curr != nullptr ) + { + if ( curr ) + { + if ( curr_other == nullptr ) + { + log_fmt("\nAST::is_equal: Failed for parameter, other equivalent param is null\n" + "AST : %S\n" + "Other: %S\n" + "For ast member: %S\n" + , code_debug_str(curr) + ); + + return false; + } + + if ( str_are_equal(curr->Name, curr_other->Name) ) + { + log_fmt( "\nAST::is_equal: Failed for parameter name check\n" + "AST : %S\n" + "Other: %S\n" + "For ast member: %S\n" + "other's ast member: %S\n" + , code_debug_str(self) + , code_debug_str(other) + , code_debug_str(curr) + , code_debug_str(curr_other) + ); + return false; + } + + if ( curr->ValueType && ! code_is_equal(curr->ValueType, curr_other->ValueType) ) + { + log_fmt( "\nAST::is_equal: Failed for parameter value type check\n" + "AST : %S\n" + "Other: %S\n" + "For ast member: %S\n" + "other's ast member: %S\n" + , code_debug_str(self) + , code_debug_str(other) + , code_debug_str(curr) + , code_debug_str(curr_other) + ); + return false; + } + + if ( curr->Value && ! code_is_equal(curr->Value, curr_other->Value) ) + { + log_fmt( "\nAST::is_equal: Failed for parameter value check\n" + "AST : %S\n" + "Other: %S\n" + "For ast member: %S\n" + "other's ast member: %S\n" + , code_debug_str(self) + , code_debug_str(other) + , code_debug_str(curr) + , code_debug_str(curr_other) + ); + return false; + } + } + + curr = curr->Next; + curr_other = curr_other->Next; + } + + check_member_val( NumEntries ); + + return true; + } + + check_member_str( Name ); + check_member_ast( ValueType ); + check_member_ast( Value ); + check_member_ast( ArrExpr ); + + return true; + } + + case CT_Parameters_Define: + { + // TODO(ED): Needs implementaton + log_failure("code_is_equal: NOT IMPLEMENTED for CT_Parameters_Define"); + return false; + } + + case CT_Preprocess_Define: + { + check_member_str( Name ); + check_member_content( Body->Content ); + return true; + } + + case CT_Preprocess_If: + case CT_Preprocess_IfDef: + case CT_Preprocess_IfNotDef: + case CT_Preprocess_ElIf: + { + check_member_content( Content ); + + return true; + } + + case CT_Preprocess_Include: + case CT_Preprocess_Pragma: + { + check_member_content( Content ); + + return true; + } + + case CT_Specifiers: + { + check_member_val( NumEntries ); + check_member_str( Name ); + for ( s32 idx = 0; idx < self->NumEntries; ++idx ) + { + check_member_val( ArrSpecs[ idx ] ); + } + return true; + } + + case CT_Template: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( Params ); + check_member_ast( Declaration ); + + return true; + } + + case CT_Typedef: + { + check_member_val( IsFunction ); + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( Specs ); + check_member_ast( UnderlyingType ); + + return true; + } + case CT_Typename: + { + check_member_val( IsParamPack ); + check_member_str( Name ); + check_member_ast( Specs ); + check_member_ast( ArrExpr ); + + return true; + } + + case CT_Union: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( Attributes ); + check_member_ast( Body ); + + return true; + } + + case CT_Union_Fwd: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( Attributes ); + } + + case CT_Using: + case CT_Using_Namespace: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( UnderlyingType ); + check_member_ast( Attributes ); + + return true; + } + + case CT_Variable: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( ValueType ); + check_member_ast( BitfieldSize ); + check_member_ast( Value ); + check_member_ast( Attributes ); + check_member_ast( Specs ); + check_member_ast( NextVar ); + + return true; + } + + case CT_Class_Body: + case CT_Enum_Body: + case CT_Export_Body: + case CT_Global_Body: + case CT_Namespace_Body: + case CT_Struct_Body: + case CT_Union_Body: + { + check_member_ast( Front ); + check_member_ast( Back ); + + Code curr = self->Front; + Code curr_other = other->Front; + while ( curr != nullptr ) + { + if ( curr_other == nullptr ) + { + log_fmt("\nAST::is_equal: Failed for body, other equivalent param is null\n" + "AST : %S\n" + "Other: %S\n" + , code_debug_str(curr) + , code_debug_str(other) + ); + + return false; + } + + if ( ! code_is_equal( curr, curr_other ) ) + { + log_fmt( "\nAST::is_equal: Failed for body\n" + "AST : %S\n" + "Other: %S\n" + "For ast member: %S\n" + "other's ast member: %S\n" + , code_debug_str(self) + , code_debug_str(other) + , code_debug_str(curr) + , code_debug_str(curr_other) + ); + + return false; + } + + curr = curr->Next; + curr_other = curr_other->Next; + } + + check_member_val( NumEntries ); + + return true; + } + + #undef check_member_val + #undef check_member_str + #undef check_member_ast + } + + return true; +} + +bool code_validate_body(Code self) +{ + switch ( self->Type ) + { + case CT_Class_Body: + { + CodeBody body = cast(CodeBody, self); + for (Code code_entry = begin_CodeBody(body); code_entry != end_CodeBody(body); next_CodeBody(body, code_entry)) switch (code_entry->Type) + { + GEN_AST_BODY_CLASS_UNALLOWED_TYPES_CASES: + log_failure("AST::validate_body: Invalid entry in body %S", code_debug_str(code_entry)); + return false; + + default: + continue; + } + } + break; + case CT_Enum_Body: + { + CodeBody body = cast(CodeBody, self); + for ( Code entry = begin_CodeBody(body); entry != end_CodeBody(body); next_CodeBody(body, entry) ) + { + if ( entry->Type != CT_Untyped ) + { + log_failure( "AST::validate_body: Invalid entry in enum body (needs to be untyped or comment) %S", code_debug_str(entry) ); + return false; + } + } + } + break; + case CT_Export_Body: + { + CodeBody body = cast(CodeBody, self); + for (Code code_entry = begin_CodeBody(body); code_entry != end_CodeBody(body); next_CodeBody(body, code_entry)) switch (code_entry->Type) + { + GEN_AST_BODY_EXPORT_UNALLOWED_TYPES_CASES: + log_failure("AST::validate_body: Invalid entry in body %S", code_debug_str(code_entry)); + return false; + + default: + continue; + } + } + break; + case CT_Extern_Linkage: + { + CodeBody body = cast(CodeBody, self); + for (Code code_entry = begin_CodeBody(body); code_entry != end_CodeBody(body); next_CodeBody(body, code_entry)) switch (code_entry->Type) + { + GEN_AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES_CASES: + log_failure("AST::validate_body: Invalid entry in body %S", code_debug_str(code_entry)); + return false; + + default: + continue; + } + } + break; + case CT_Function_Body: + { + CodeBody body = cast(CodeBody, self); + for (Code code_entry = begin_CodeBody(body); code_entry != end_CodeBody(body); next_CodeBody(body, code_entry)) switch (code_entry->Type) + { + GEN_AST_BODY_FUNCTION_UNALLOWED_TYPES_CASES: + log_failure("AST::validate_body: Invalid entry in body %S", code_debug_str(code_entry)); + return false; + + default: + continue; + } + } + break; + case CT_Global_Body: + { + CodeBody body = cast(CodeBody, self); + for ( Code entry = begin_CodeBody(body); entry != end_CodeBody(body); next_CodeBody(body, entry) )switch (entry->Type) + { + GEN_AST_BODY_GLOBAL_UNALLOWED_TYPES_CASES: + log_failure("AST::validate_body: Invalid entry in body %S", code_debug_str(entry)); + return false; + } + } + break; + case CT_Namespace_Body: + { + CodeBody body = cast(CodeBody, self); + for ( Code entry = begin_CodeBody(body); entry != end_CodeBody(body); next_CodeBody(body, entry) ) switch (entry->Type) + { + GEN_AST_BODY_NAMESPACE_UNALLOWED_TYPES_CASES: + log_failure("AST::validate_body: Invalid entry in body %S", code_debug_str(entry)); + return false; + } + } + break; + case CT_Struct_Body: + { + CodeBody body = cast(CodeBody, self); + for ( Code entry = begin_CodeBody(body); entry != end_CodeBody(body); next_CodeBody(body, entry) ) switch (entry->Type) + { + GEN_AST_BODY_STRUCT_UNALLOWED_TYPES_CASES: + log_failure("AST::validate_body: Invalid entry in body %S", code_debug_str(entry)); + return false; + } + } + break; + case CT_Union_Body: + { + CodeBody body = cast(CodeBody, self); + for ( Code entry = begin_CodeBody(body); entry != end_CodeBody(body); next_CodeBody(body, entry) ) + { + if ( entry->Type != CT_Untyped ) + { + log_failure( "AST::validate_body: Invalid entry in union body (needs to be untyped or comment) %S", code_debug_str(entry) ); + return false; + } + } + } + break; + + default: + log_failure( "AST::validate_body: Invalid this AST does not have a body %S", code_debug_str(self) ); + return false; + } + return false; +} diff --git a/tests/assets/gencpp_samples/components/ast.hpp b/tests/assets/gencpp_samples/components/ast.hpp new file mode 100644 index 0000000..ae2513e --- /dev/null +++ b/tests/assets/gencpp_samples/components/ast.hpp @@ -0,0 +1,457 @@ +#ifdef INTELLISENSE_DIRECTIVES +#pragma once +#include "parser_types.hpp" +#endif + +/* + ______ ______ ________ __ __ ______ __ + / \ / \| \ | \ | \ / \ | \ +| ▓▓▓▓▓▓\ ▓▓▓▓▓▓\\▓▓▓▓▓▓▓▓ | ▓▓\ | ▓▓ | ▓▓▓▓▓▓\ ______ ____| ▓▓ ______ +| ▓▓__| ▓▓ ▓▓___\▓▓ | ▓▓ | ▓▓▓\| ▓▓ | ▓▓ \▓▓/ \ / ▓▓/ \ +| ▓▓ ▓▓\▓▓ \ | ▓▓ | ▓▓▓▓\ ▓▓ | ▓▓ | ▓▓▓▓▓▓\ ▓▓▓▓▓▓▓ ▓▓▓▓▓▓\ +| ▓▓▓▓▓▓▓▓_\▓▓▓▓▓▓\ | ▓▓ | ▓▓\▓▓ ▓▓ | ▓▓ __| ▓▓ | ▓▓ ▓▓ | ▓▓ ▓▓ ▓▓ +| ▓▓ | ▓▓ \__| ▓▓ | ▓▓ | ▓▓ \▓▓▓▓ | ▓▓__/ \ ▓▓__/ ▓▓ ▓▓__| ▓▓ ▓▓▓▓▓▓▓▓ +| ▓▓ | ▓▓\▓▓ ▓▓ | ▓▓ | ▓▓ \▓▓▓ \▓▓ ▓▓\▓▓ ▓▓\▓▓ ▓▓\▓▓ \ + \▓▓ \▓▓ \▓▓▓▓▓▓ \▓▓ \▓▓ \▓▓ \▓▓▓▓▓▓ \▓▓▓▓▓▓ \▓▓▓▓▓▓▓ \▓▓▓▓▓▓▓ +*/ + +struct AST; +struct AST_Body; +struct AST_Attributes; +struct AST_Comment; +struct AST_Constructor; +// struct AST_BaseClass; +struct AST_Class; +struct AST_Define; +struct AST_DefineParams; +struct AST_Destructor; +struct AST_Enum; +struct AST_Exec; +struct AST_Extern; +struct AST_Include; +struct AST_Friend; +struct AST_Fn; +struct AST_Module; +struct AST_NS; +struct AST_Operator; +struct AST_OpCast; +struct AST_Params; +struct AST_Pragma; +struct AST_PreprocessCond; +struct AST_Specifiers; + +#ifdef GEN_EXECUTION_EXPRESSION_SUPPORT +struct AST_Expr; +struct AST_Expr_Assign; +struct AST_Expr_Alignof; +struct AST_Expr_Binary; +struct AST_Expr_CStyleCast; +struct AST_Expr_FunctionalCast; +struct AST_Expr_CppCast; +struct AST_Expr_ProcCall; +struct AST_Expr_Decltype; +struct AST_Expr_Comma; // TODO(Ed) : This is a binary op not sure if it needs its own AST... +struct AST_Expr_AMS; // Access Member Symbol +struct AST_Expr_Sizeof; +struct AST_Expr_Subscript; +struct AST_Expr_Ternary; +struct AST_Expr_UnaryPrefix; +struct AST_Expr_UnaryPostfix; +struct AST_Expr_Element; + +struct AST_Stmt; +struct AST_Stmt_Break; +struct AST_Stmt_Case; +struct AST_Stmt_Continue; +struct AST_Stmt_Decl; +struct AST_Stmt_Do; +struct AST_Stmt_Expr; // TODO(Ed) : Is this distinction needed? (Should it be a flag instead?) +struct AST_Stmt_Else; +struct AST_Stmt_If; +struct AST_Stmt_For; +struct AST_Stmt_Goto; +struct AST_Stmt_Label; +struct AST_Stmt_Switch; +struct AST_Stmt_While; +#endif + +struct AST_Struct; +struct AST_Template; +struct AST_Typename; +struct AST_Typedef; +struct AST_Union; +struct AST_Using; +struct AST_Var; + +#if GEN_COMPILER_C +typedef AST* Code; +#else +struct Code; +#endif + +#if GEN_COMPILER_C +typedef AST_Body* CodeBody; +typedef AST_Attributes* CodeAttributes; +typedef AST_Comment* CodeComment; +typedef AST_Class* CodeClass; +typedef AST_Constructor* CodeConstructor; +typedef AST_Define* CodeDefine; +typedef AST_DefineParams* CodeDefineParams; +typedef AST_Destructor* CodeDestructor; +typedef AST_Enum* CodeEnum; +typedef AST_Exec* CodeExec; +typedef AST_Extern* CodeExtern; +typedef AST_Include* CodeInclude; +typedef AST_Friend* CodeFriend; +typedef AST_Fn* CodeFn; +typedef AST_Module* CodeModule; +typedef AST_NS* CodeNS; +typedef AST_Operator* CodeOperator; +typedef AST_OpCast* CodeOpCast; +typedef AST_Params* CodeParams; +typedef AST_PreprocessCond* CodePreprocessCond; +typedef AST_Pragma* CodePragma; +typedef AST_Specifiers* CodeSpecifiers; +#else +struct CodeBody; +struct CodeAttributes; +struct CodeComment; +struct CodeClass; +struct CodeConstructor; +struct CodeDefine; +struct CodeDefineParams; +struct CodeDestructor; +struct CodeEnum; +struct CodeExec; +struct CodeExtern; +struct CodeInclude; +struct CodeFriend; +struct CodeFn; +struct CodeModule; +struct CodeNS; +struct CodeOperator; +struct CodeOpCast; +struct CodeParams; +struct CodePreprocessCond; +struct CodePragma; +struct CodeSpecifiers; +#endif + +#ifdef GEN_EXECUTION_EXPRESSION_SUPPORT + +#if GEN_COMPILER_C +typedef AST_Expr* CodeExpr; +typedef AST_Expr_Assign* CodeExpr_Assign; +typedef AST_Expr_Alignof* CodeExpr_Alignof; +typedef AST_Expr_Binary* CodeExpr_Binary; +typedef AST_Expr_CStyleCast* CodeExpr_CStyleCast; +typedef AST_Expr_FunctionalCast* CodeExpr_FunctionalCast; +typedef AST_Expr_CppCast* CodeExpr_CppCast; +typedef AST_Expr_Element* CodeExpr_Element; +typedef AST_Expr_ProcCall* CodeExpr_ProcCall; +typedef AST_Expr_Decltype* CodeExpr_Decltype; +typedef AST_Expr_Comma* CodeExpr_Comma; +typedef AST_Expr_AMS* CodeExpr_AMS; // Access Member Symbol +typedef AST_Expr_Sizeof* CodeExpr_Sizeof; +typedef AST_Expr_Subscript* CodeExpr_Subscript; +typedef AST_Expr_Ternary* CodeExpr_Ternary; +typedef AST_Expr_UnaryPrefix* CodeExpr_UnaryPrefix; +typedef AST_Expr_UnaryPostfix* CodeExpr_UnaryPostfix; +#else +struct CodeExpr; +struct CodeExpr_Assign; +struct CodeExpr_Alignof; +struct CodeExpr_Binary; +struct CodeExpr_CStyleCast; +struct CodeExpr_FunctionalCast; +struct CodeExpr_CppCast; +struct CodeExpr_Element; +struct CodeExpr_ProcCall; +struct CodeExpr_Decltype; +struct CodeExpr_Comma; +struct CodeExpr_AMS; // Access Member Symbol +struct CodeExpr_Sizeof; +struct CodeExpr_Subscript; +struct CodeExpr_Ternary; +struct CodeExpr_UnaryPrefix; +struct CodeExpr_UnaryPostfix; +#endif + +#if GEN_COMPILER_C +typedef AST_Stmt* CodeStmt; +typedef AST_Stmt_Break* CodeStmt_Break; +typedef AST_Stmt_Case* CodeStmt_Case; +typedef AST_Stmt_Continue* CodeStmt_Continue; +typedef AST_Stmt_Decl* CodeStmt_Decl; +typedef AST_Stmt_Do* CodeStmt_Do; +typedef AST_Stmt_Expr* CodeStmt_Expr; +typedef AST_Stmt_Else* CodeStmt_Else; +typedef AST_Stmt_If* CodeStmt_If; +typedef AST_Stmt_For* CodeStmt_For; +typedef AST_Stmt_Goto* CodeStmt_Goto; +typedef AST_Stmt_Label* CodeStmt_Label; +typedef AST_Stmt_Lambda* CodeStmt_Lambda; +typedef AST_Stmt_Switch* CodeStmt_Switch; +typedef AST_Stmt_While* CodeStmt_While; +#else +struct CodeStmt; +struct CodeStmt_Break; +struct CodeStmt_Case; +struct CodeStmt_Continue; +struct CodeStmt_Decl; +struct CodeStmt_Do; +struct CodeStmt_Expr; +struct CodeStmt_Else; +struct CodeStmt_If; +struct CodeStmt_For; +struct CodeStmt_Goto; +struct CodeStmt_Label; +struct CodeStmt_Lambda; +struct CodeStmt_Switch; +struct CodeStmt_While; +#endif + +// GEN_EXECUTION_EXPRESSION_SUPPORT +#endif + +#if GEN_COMPILER_C +typedef AST_Struct* CodeStruct; +typedef AST_Template* CodeTemplate; +typedef AST_Typename* CodeTypename; +typedef AST_Typedef* CodeTypedef; +typedef AST_Union* CodeUnion; +typedef AST_Using* CodeUsing; +typedef AST_Var* CodeVar; +#else +struct CodeStruct; +struct CodeTemplate; +struct CodeTypename; +struct CodeTypedef; +struct CodeUnion; +struct CodeUsing; +struct CodeVar; +#endif + +#if GEN_COMPILER_CPP +template< class Type> forceinline Type tmpl_cast( Code self ) { return * rcast( Type*, & self ); } +#endif + +#pragma region Code C-Interface + + void code_append (Code code, Code other ); +GEN_API Str code_debug_str (Code code); +GEN_API Code code_duplicate (Code code); + Code* code_entry (Code code, u32 idx ); + bool code_has_entries (Code code); + bool code_is_body (Code code); +GEN_API bool code_is_equal (Code code, Code other); + bool code_is_valid (Code code); + void code_set_global (Code code); +GEN_API StrBuilder code_to_strbuilder (Code self ); +GEN_API void code_to_strbuilder_ref(Code self, StrBuilder* result ); + Str code_type_str (Code self ); +GEN_API bool code_validate_body (Code self ); + +#pragma endregion Code C-Interface + +#if GEN_COMPILER_CPP +/* + AST* wrapper + - Not constantly have to append the '*' as this is written often.. + - Allows for implicit conversion to any of the ASTs (raw or filtered). +*/ +struct Code +{ + AST* ast; + +# define Using_Code( Typename ) \ + forceinline Str debug_str() { return code_debug_str(* this); } \ + forceinline Code duplicate() { return code_duplicate(* this); } \ + forceinline bool is_equal( Code other ) { return code_is_equal(* this, other); } \ + forceinline bool is_body() { return code_is_body(* this); } \ + forceinline bool is_valid() { return code_is_valid(* this); } \ + forceinline void set_global() { return code_set_global(* this); } + +# define Using_CodeOps( Typename ) \ + forceinline Typename& operator = ( Code other ); \ + forceinline bool operator ==( Code other ) { return (AST*)ast == other.ast; } \ + forceinline bool operator !=( Code other ) { return (AST*)ast != other.ast; } \ + forceinline bool operator ==(std::nullptr_t) const { return ast == nullptr; } \ + forceinline bool operator !=(std::nullptr_t) const { return ast != nullptr; } \ + operator bool(); + +#if ! GEN_C_LIKE_CPP + Using_Code( Code ); + forceinline void append(Code other) { return code_append(* this, other); } + forceinline Code* entry(u32 idx) { return code_entry(* this, idx); } + forceinline bool has_entries() { return code_has_entries(* this); } + forceinline StrBuilder to_strbuilder() { return code_to_strbuilder(* this); } + forceinline void to_strbuilder(StrBuilder& result) { return code_to_strbuilder_ref(* this, & result); } + forceinline Str type_str() { return code_type_str(* this); } + forceinline bool validate_body() { return code_validate_body(*this); } +#endif + + Using_CodeOps( Code ); + forceinline Code operator *() { return * this; } // Required to support for-range iteration. + forceinline AST* operator ->() { return ast; } + + Code& operator ++(); + +#ifdef GEN_ENFORCE_STRONG_CODE_TYPES +# define operator explicit operator +#endif + operator CodeBody() const; + operator CodeAttributes() const; + // operator CodeBaseClass() const; + operator CodeComment() const; + operator CodeClass() const; + operator CodeConstructor() const; + operator CodeDefine() const; + operator CodeDefineParams() const; + operator CodeDestructor() const; + operator CodeExec() const; + operator CodeEnum() const; + operator CodeExtern() const; + operator CodeInclude() const; + operator CodeFriend() const; + operator CodeFn() const; + operator CodeModule() const; + operator CodeNS() const; + operator CodeOperator() const; + operator CodeOpCast() const; + operator CodeParams() const; + operator CodePragma() const; + operator CodePreprocessCond() const; + operator CodeSpecifiers() const; + operator CodeStruct() const; + operator CodeTemplate() const; + operator CodeTypename() const; + operator CodeTypedef() const; + operator CodeUnion() const; + operator CodeUsing() const; + operator CodeVar() const; + #undef operator +}; +#endif + +#pragma region Statics +// Used to identify ASTs that should always be duplicated. (Global constant ASTs) +GEN_API extern Code Code_Global; + +// Used to identify invalid generated code. +GEN_API extern Code Code_Invalid; +#pragma endregion Statics + +struct Code_POD +{ + AST* ast; +}; +static_assert( sizeof(Code) == sizeof(Code_POD), "ERROR: Code is not POD" ); + +// Desired width of the AST data structure. +constexpr int const AST_POD_Size = 128; + +constexpr static +int AST_ArrSpecs_Cap = +( + AST_POD_Size + - sizeof(Code) + - sizeof(StrCached) + - sizeof(Code) * 2 + - sizeof(Token*) + - sizeof(Code) + - sizeof(CodeType) + - sizeof(ModuleFlag) + - sizeof(u32) +) +/ sizeof(Specifier) - 1; + +/* + Simple AST POD with functionality to seralize into C++ syntax. + TODO(Ed): Eventually haven't a transparent AST like this will longer be viable once statements & expressions are in (most likely....) +*/ +struct AST +{ + union { + struct + { + 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; // Class, Destructor, Function, Operator, Struct, Typename, Variable + union { + Code InitializerList; // Constructor + Code ParentType; // Class, Struct, ParentType->Next has a possible list of interfaces. + Code ReturnType; // Function, Operator, Typename + Code UnderlyingType; // Enum, Typedef + Code ValueType; // Parameter, Variable + }; + union { + Code Macro; // Parameter + Code BitfieldSize; // Variable (Class/Struct Data Member) + Code Params; // Constructor, Define, Function, Operator, Template, Typename + Code UnderlyingTypeMacro; // Enum + }; + union { + Code ArrExpr; // Typename + Code Body; // Class, Constructor, Define, Destructor, Enum, Friend, Function, Namespace, Struct, Union + Code Declaration; // Friend, Template + Code Value; // Parameter, Variable + }; + union { + Code NextVar; // Variable + Code SuffixSpecs; // Typename, Function (Thanks Unreal) + Code PostNameMacro; // Only used with parameters for specifically UE_REQUIRES (Thanks Unreal) + }; + }; + StrCached Content; // Attributes, Comment, Execution, Include + TokenSlice ContentToks; // TODO(Ed): Use a token slice for content + struct { + Specifier ArrSpecs[AST_ArrSpecs_Cap]; // Specifiers + Code NextSpecs; // Specifiers; If ArrSpecs is full, then NextSpecs is used. + }; + }; + StrCached Name; + union { + Code Prev; + Code Front; + Code Last; + }; + union { + Code Next; + Code Back; + }; + Token* Token; // Reference to starting token, only available if it was derived from parsing. // TODO(Ed): Change this to a token slice. + Code Parent; + CodeType Type; +// CodeFlag CodeFlags; + ModuleFlag ModuleFlags; + union { + b32 IsFunction; // Used by typedef to not serialize the name field. + struct { + b16 IsParamPack; // Used by typename to know if type should be considered a parameter pack. + ETypenameTag TypeTag; // Used by typename to keep track of explicitly declared tags for the identifier (enum, struct, union) + }; + Operator Op; + AccessSpec ParentAccess; + s32 NumEntries; + s32 VarParenthesizedInit; // Used by variables to know that initialization is using a constructor expression instead of an assignment expression. + }; +}; +static_assert( sizeof(AST) == AST_POD_Size, "ERROR: AST is not size of AST_POD_Size" ); + +#if GEN_COMPILER_CPP +// Uses an implicitly overloaded cast from the AST to the desired code type. +// Necessary if the user wants GEN_ENFORCE_STRONG_CODE_TYPES +struct InvalidCode_ImplictCaster; +#define InvalidCode (InvalidCode_ImplictCaster{}) +#else +#define InvalidCode (void*){ (void*)Code_Invalid } +#endif + +#if GEN_COMPILER_CPP +struct NullCode_ImplicitCaster; +// Used when the its desired when omission is allowed in a definition. +#define NullCode (NullCode_ImplicitCaster{}) +#else +#define NullCode nullptr +#endif diff --git a/tests/assets/gencpp_samples/components/ast_case_macros.cpp b/tests/assets/gencpp_samples/components/ast_case_macros.cpp new file mode 100644 index 0000000..f7f66c2 --- /dev/null +++ b/tests/assets/gencpp_samples/components/ast_case_macros.cpp @@ -0,0 +1,80 @@ +// These macros are used in the swtich cases within ast.cpp, inteface.upfront.cpp, parser.cpp + +# define GEN_AST_BODY_CLASS_UNALLOWED_TYPES_CASES \ + case CT_PlatformAttributes: \ + case CT_Class_Body: \ + case CT_Enum_Body: \ + case CT_Extern_Linkage: \ + case CT_Function_Body: \ + case CT_Function_Fwd: \ + case CT_Global_Body: \ + case CT_Namespace: \ + case CT_Namespace_Body: \ + case CT_Operator: \ + case CT_Operator_Fwd: \ + case CT_Parameters: \ + case CT_Specifiers: \ + case CT_Struct_Body: \ + case CT_Typename +# define GEN_AST_BODY_STRUCT_UNALLOWED_TYPES_CASES GEN_AST_BODY_CLASS_UNALLOWED_TYPES_CASES + +# define GEN_AST_BODY_FUNCTION_UNALLOWED_TYPES_CASES \ + case CT_Access_Public: \ + case CT_Access_Protected: \ + case CT_Access_Private: \ + case CT_PlatformAttributes: \ + case CT_Class_Body: \ + case CT_Enum_Body: \ + case CT_Extern_Linkage: \ + case CT_Friend: \ + case CT_Function_Body: \ + case CT_Function_Fwd: \ + case CT_Global_Body: \ + case CT_Namespace: \ + case CT_Namespace_Body: \ + case CT_Operator: \ + case CT_Operator_Fwd: \ + case CT_Operator_Member: \ + case CT_Operator_Member_Fwd: \ + case CT_Parameters: \ + case CT_Specifiers: \ + case CT_Struct_Body: \ + case CT_Typename + +# define GEN_AST_BODY_GLOBAL_UNALLOWED_TYPES_CASES \ + case CT_Access_Public: \ + case CT_Access_Protected: \ + case CT_Access_Private: \ + case CT_PlatformAttributes: \ + case CT_Class_Body: \ + case CT_Enum_Body: \ + case CT_Execution: \ + case CT_Friend: \ + case CT_Function_Body: \ + case CT_Namespace_Body: \ + case CT_Operator_Member: \ + case CT_Operator_Member_Fwd: \ + case CT_Parameters: \ + case CT_Specifiers: \ + case CT_Struct_Body: \ + case CT_Typename +# define GEN_AST_BODY_EXPORT_UNALLOWED_TYPES_CASES GEN_AST_BODY_GLOBAL_UNALLOWED_TYPES_CASES +# define GEN_AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES_CASES GEN_AST_BODY_GLOBAL_UNALLOWED_TYPES_CASES + +# define GEN_AST_BODY_NAMESPACE_UNALLOWED_TYPES_CASES \ + case CT_Access_Public: \ + case CT_Access_Protected: \ + case CT_Access_Private: \ + case CT_PlatformAttributes: \ + case CT_Class_Body: \ + case CT_Enum_Body: \ + case CT_Execution: \ + case CT_Friend: \ + case CT_Function_Body: \ + case CT_Namespace_Body: \ + case CT_Operator_Member: \ + case CT_Operator_Member_Fwd: \ + case CT_Parameters: \ + case CT_Specifiers: \ + case CT_Struct_Body: \ + case CT_Typename diff --git a/tests/assets/gencpp_samples/components/ast_types.hpp b/tests/assets/gencpp_samples/components/ast_types.hpp new file mode 100644 index 0000000..27bfae1 --- /dev/null +++ b/tests/assets/gencpp_samples/components/ast_types.hpp @@ -0,0 +1,1170 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "code_types.hpp" +#endif + +#pragma region AST Types + +/* + ______ ______ ________ ________ + / \ / \| \ | \ +| ▓▓▓▓▓▓\ ▓▓▓▓▓▓\\▓▓▓▓▓▓▓▓ \▓▓▓▓▓▓▓▓__ __ ______ ______ _______ +| ▓▓__| ▓▓ ▓▓___\▓▓ | ▓▓ | ▓▓ | \ | \/ \ / \ / \ +| ▓▓ ▓▓\▓▓ \ | ▓▓ | ▓▓ | ▓▓ | ▓▓ ▓▓▓▓▓▓\ ▓▓▓▓▓▓\ ▓▓▓▓▓▓▓ +| ▓▓▓▓▓▓▓▓_\▓▓▓▓▓▓\ | ▓▓ | ▓▓ | ▓▓ | ▓▓ ▓▓ | ▓▓ ▓▓ ▓▓\▓▓ \ +| ▓▓ | ▓▓ \__| ▓▓ | ▓▓ | ▓▓ | ▓▓__/ ▓▓ ▓▓__/ ▓▓ ▓▓▓▓▓▓▓▓_\▓▓▓▓▓▓\ +| ▓▓ | ▓▓\▓▓ ▓▓ | ▓▓ | ▓▓ \▓▓ ▓▓ ▓▓ ▓▓\▓▓ \ ▓▓ + \▓▓ \▓▓ \▓▓▓▓▓▓ \▓▓ \▓▓ _\▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓ \▓▓▓▓▓▓▓\▓▓▓▓▓▓▓ + | \__| ▓▓ ▓▓ + \▓▓ ▓▓ ▓▓ + \▓▓▓▓▓▓ \▓▓ +*/ + +/* + Show only relevant members of the AST for its type. + AST* fields are replaced with Code types. + - Guards assignemnts to AST* fields to ensure the AST is duplicated if assigned to another parent. +*/ + +struct AST_Body +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + Code Front; + Code Back; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; + s32 NumEntries; +}; +static_assert( sizeof(AST_Body) == sizeof(AST), "ERROR: AST_Body is not the same size as AST"); + +// TODO(Ed): Support chaining attributes (Use parameter linkage pattern) +struct AST_Attributes +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + StrCached Content; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Attributes) == sizeof(AST), "ERROR: AST_Attributes is not the same size as AST"); + +#if 0 +struct AST_BaseClass +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_BaseClass) == sizeof(AST), "ERROR: AST_BaseClass is not the same size as AST"); +#endif + +struct AST_Comment +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + StrCached Content; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Comment) == sizeof(AST), "ERROR: AST_Comment is not the same size as AST"); + +struct AST_Class +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; // Only supported by forward declarations + CodeAttributes Attributes; + CodeSpecifiers Specs; // Support for final + CodeTypename ParentType; + char _PAD_PARAMS_[ sizeof(AST*) ]; + CodeBody Body; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + StrCached Name; + CodeTypename Prev; + CodeTypename Next; + Token* Tok; + Code Parent; + CodeType Type; + ModuleFlag ModuleFlags; + AccessSpec ParentAccess; +}; +static_assert( sizeof(AST_Class) == sizeof(AST), "ERROR: AST_Class is not the same size as AST"); + +struct AST_Constructor +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; // Only supported by forward declarations + char _PAD_PROPERTIES_ [ sizeof(AST*) * 1 ]; + CodeSpecifiers Specs; + Code InitializerList; + CodeParams Params; + Code Body; + char _PAD_PROPERTIES_2_ [ sizeof(AST*) * 2 ]; + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Constructor) == sizeof(AST), "ERROR: AST_Constructor is not the same size as AST"); + +struct AST_Define +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + char _PAD_PROPERTIES_ [ sizeof(AST*) * 4 ]; + CodeDefineParams Params; + Code Body; // Should be completely serialized for now to a: StrCached Content. + char _PAD_PROPERTIES_2_ [ sizeof(AST*) * 1 ]; + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Define) == sizeof(AST), "ERROR: AST_Define is not the same size as AST"); + +struct AST_DefineParams +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeDefineParams Last; + CodeDefineParams Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; + s32 NumEntries; +}; +static_assert( sizeof(AST_DefineParams) == sizeof(AST), "ERROR: AST_DefineParams is not the same size as AST"); + +struct AST_Destructor +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + char _PAD_PROPERTIES_ [ sizeof(AST*) * 1 ]; + CodeSpecifiers Specs; + char _PAD_PROPERTIES_2_ [ sizeof(AST*) * 2 ]; + Code Body; + char _PAD_PROPERTIES_3_ [ sizeof(AST*) ]; + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Destructor) == sizeof(AST), "ERROR: AST_Destructor is not the same size as AST"); + +struct AST_Enum +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + CodeAttributes Attributes; + char _PAD_SPEC_ [ sizeof(AST*) ]; + CodeTypename UnderlyingType; + Code UnderlyingTypeMacro; + CodeBody Body; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Enum) == sizeof(AST), "ERROR: AST_Enum is not the same size as AST"); + +struct AST_Exec +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + StrCached Content; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Exec) == sizeof(AST), "ERROR: AST_Exec is not the same size as AST"); + +#ifdef GEN_EXECUTION_EXPRESSION_SUPPORT +struct AST_Expr +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr) == sizeof(AST), "ERROR: AST_Expr is not the same size as AST"); + +struct AST_Expr_Assign +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Assign) == sizeof(AST), "ERROR: AST_Expr_Assign is not the same size as AST"); + +struct AST_Expr_Alignof +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Alignof) == sizeof(AST), "ERROR: AST_Expr_Alignof is not the same size as AST"); + +struct AST_Expr_Binary +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Binary) == sizeof(AST), "ERROR: AST_Expr_Binary is not the same size as AST"); + +struct AST_Expr_CStyleCast +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_CStyleCast) == sizeof(AST), "ERROR: AST_Expr_CStyleCast is not the same size as AST"); + +struct AST_Expr_FunctionalCast +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_FunctionalCast) == sizeof(AST), "ERROR: AST_Expr_FunctionalCast is not the same size as AST"); + +struct AST_Expr_CppCast +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_CppCast) == sizeof(AST), "ERROR: AST_Expr_CppCast is not the same size as AST"); + +struct AST_Expr_ProcCall +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_ProcCall) == sizeof(AST), "ERROR: AST_Expr_Identifier is not the same size as AST"); + +struct AST_Expr_Decltype +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Decltype) == sizeof(AST), "ERROR: AST_Expr_Decltype is not the same size as AST"); + +struct AST_Expr_Comma +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Comma) == sizeof(AST), "ERROR: AST_Expr_Comma is not the same size as AST"); + +struct AST_Expr_AMS +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_AMS) == sizeof(AST), "ERROR: AST_Expr_AMS is not the same size as AST"); + +struct AST_Expr_Sizeof +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Sizeof) == sizeof(AST), "ERROR: AST_Expr_Sizeof is not the same size as AST"); + +struct AST_Expr_Subscript +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Subscript) == sizeof(AST), "ERROR: AST_Expr_Subscript is not the same size as AST"); + +struct AST_Expr_Ternary +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Ternary) == sizeof(AST), "ERROR: AST_Expr_Ternary is not the same size as AST"); + +struct AST_Expr_UnaryPrefix +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_UnaryPrefix) == sizeof(AST), "ERROR: AST_Expr_UnaryPrefix is not the same size as AST"); + +struct AST_Expr_UnaryPostfix +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_UnaryPostfix) == sizeof(AST), "ERROR: AST_Expr_UnaryPostfix is not the same size as AST"); + +struct AST_Expr_Element +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Element) == sizeof(AST), "ERROR: AST_Expr_Element is not the same size as AST"); +#endif + +struct AST_Extern +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + char _PAD_PROPERTIES_[ sizeof(AST*) * 5 ]; + CodeBody Body; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Extern) == sizeof(AST), "ERROR: AST_Extern is not the same size as AST"); + +struct AST_Include +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + StrCached Content; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Include) == sizeof(AST), "ERROR: AST_Include is not the same size as AST"); + +struct AST_Friend +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + char _PAD_PROPERTIES_[ sizeof(AST*) * 4 ]; + Code Declaration; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Friend) == sizeof(AST), "ERROR: AST_Friend is not the same size as AST"); + +struct AST_Fn +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + CodeAttributes Attributes; + CodeSpecifiers Specs; + CodeTypename ReturnType; + CodeParams Params; + CodeBody Body; + Code SuffixSpecs; // Thanks Unreal + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Fn) == sizeof(AST), "ERROR: AST_Fn is not the same size as AST"); + +struct AST_Module +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Module) == sizeof(AST), "ERROR: AST_Module is not the same size as AST"); + +struct AST_NS +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct { + char _PAD_PROPERTIES_[ sizeof(AST*) * 5 ]; + CodeBody Body; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_NS) == sizeof(AST), "ERROR: AST_NS is not the same size as AST"); + +struct AST_Operator +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + CodeAttributes Attributes; + CodeSpecifiers Specs; + CodeTypename ReturnType; + CodeParams Params; + CodeBody Body; + char _PAD_PROPERTIES_ [ sizeof(AST*) ]; + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + ModuleFlag ModuleFlags; + Operator Op; +}; +static_assert( sizeof(AST_Operator) == sizeof(AST), "ERROR: AST_Operator is not the same size as AST"); + +struct AST_OpCast +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + char _PAD_PROPERTIES_[ sizeof(AST*) ]; + CodeSpecifiers Specs; + CodeTypename ValueType; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + CodeBody Body; + char _PAD_PROPERTIES_3_[ sizeof(AST*) ]; + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_OpCast) == sizeof(AST), "ERROR: AST_OpCast is not the same size as AST"); + +struct AST_Params +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + // TODO(Ed): Support attributes for parameters (Some prefix macros can be converted to that...) + char _PAD_PROPERTIES_2_[ sizeof(AST*) * 3 ]; + CodeTypename ValueType; + Code Macro; + Code Value; + Code PostNameMacro; // Thanks Unreal + // char _PAD_PROPERTIES_3_[sizeof( AST* )]; + }; + }; + StrCached Name; + CodeParams Last; + CodeParams Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; + s32 NumEntries; +}; +static_assert( sizeof(AST_Params) == sizeof(AST), "ERROR: AST_Params is not the same size as AST"); + +struct AST_Pragma +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + StrCached Content; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Pragma) == sizeof(AST), "ERROR: AST_Pragma is not the same size as AST"); + +struct AST_PreprocessCond +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + StrCached Content; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_PreprocessCond) == sizeof(AST), "ERROR: AST_PreprocessCond is not the same size as AST"); + +struct AST_Specifiers +{ + Specifier ArrSpecs[ AST_ArrSpecs_Cap ]; + CodeSpecifiers NextSpecs; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; + s32 NumEntries; +}; +static_assert( sizeof(AST_Specifiers) == sizeof(AST), "ERROR: AST_Specifier is not the same size as AST"); + +#ifdef GEN_EXECUTION_EXPRESSION_SUPPORT +struct AST_Stmt +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt) == sizeof(AST), "ERROR: AST_Stmt is not the same size as AST"); + +struct AST_Stmt_Break +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Break) == sizeof(AST), "ERROR: AST_Stmt_Break is not the same size as AST"); + +struct AST_Stmt_Case +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Case) == sizeof(AST), "ERROR: AST_Stmt_Case is not the same size as AST"); + +struct AST_Stmt_Continue +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Continue) == sizeof(AST), "ERROR: AST_Stmt_Continue is not the same size as AST"); + +struct AST_Stmt_Decl +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Decl) == sizeof(AST), "ERROR: AST_Stmt_Decl is not the same size as AST"); + +struct AST_Stmt_Do +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Do) == sizeof(AST), "ERROR: AST_Stmt_Do is not the same size as AST"); + +struct AST_Stmt_Expr +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Expr) == sizeof(AST), "ERROR: AST_Stmt_Expr is not the same size as AST"); + +struct AST_Stmt_Else +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Else) == sizeof(AST), "ERROR: AST_Stmt_Else is not the same size as AST"); + +struct AST_Stmt_If +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_If) == sizeof(AST), "ERROR: AST_Stmt_If is not the same size as AST"); + +struct AST_Stmt_For +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_For) == sizeof(AST), "ERROR: AST_Stmt_For is not the same size as AST"); + +struct AST_Stmt_Goto +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Goto) == sizeof(AST), "ERROR: AST_Stmt_Goto is not the same size as AST"); + +struct AST_Stmt_Label +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Label) == sizeof(AST), "ERROR: AST_Stmt_Label is not the same size as AST"); + +struct AST_Stmt_Switch +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Switch) == sizeof(AST), "ERROR: AST_Stmt_Switch is not the same size as AST"); + +struct AST_Stmt_While +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + }; + StrCached Name; + CodeExpr Prev; + CodeExpr Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_While) == sizeof(AST), "ERROR: AST_Stmt_While is not the same size as AST"); +#endif + +struct AST_Struct +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + CodeAttributes Attributes; + CodeSpecifiers Specs; // Support for final + CodeTypename ParentType; + char _PAD_PARAMS_[ sizeof(AST*) ]; + CodeBody Body; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + StrCached Name; + CodeTypename Prev; + CodeTypename Next; + Token* Tok; + Code Parent; + CodeType Type; + ModuleFlag ModuleFlags; + AccessSpec ParentAccess; +}; +static_assert( sizeof(AST_Struct) == sizeof(AST), "ERROR: AST_Struct is not the same size as AST"); + +struct AST_Template +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + char _PAD_PROPERTIES_[ sizeof(AST*) * 4 ]; + CodeParams Params; + Code Declaration; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Template) == sizeof(AST), "ERROR: AST_Template is not the same size as AST"); + +#if 0 +// WIP... The type ast is going to become more advanced and lead to a major change to AST design. +struct AST_Type +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + char _PAD_INLINE_CMT_[ sizeof(AST*) ]; + CodeAttributes Attributes; + CodeSpecifiers Specs; + Code QualifierID; + // CodeTypename ReturnType; // Only used for function signatures + // CodeParams Params; // Only used for function signatures + Code ArrExpr; + // CodeSpecifiers SpecsFuncSuffix; // Only used for function signatures + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; + b32 IsParamPack; +}; +static_assert( sizeof(AST_Type) == sizeof(AST), "ERROR: AST_Type is not the same size as AST"); +#endif + +struct AST_Typename +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + char _PAD_INLINE_CMT_[ sizeof(AST*) ]; + CodeAttributes Attributes; + CodeSpecifiers Specs; + CodeTypename ReturnType; // Only used for function signatures + CodeParams Params; // Only used for function signatures + Code ArrExpr; + CodeSpecifiers SpecsFuncSuffix; // Only used for function signatures + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; + struct { + b16 IsParamPack; // Used by typename to know if type should be considered a parameter pack. + ETypenameTag TypeTag; // Used by typename to keep track of explicitly declared tags for the identifier (enum, struct, union) + }; +}; +static_assert( sizeof(AST_Typename) == sizeof(AST), "ERROR: AST_Type is not the same size as AST"); + +struct AST_Typedef +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + char _PAD_PROPERTIES_[ sizeof(AST*) * 2 ]; + Code UnderlyingType; + char _PAD_PROPERTIES_2_[ sizeof(AST*) * 3 ]; + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + ModuleFlag ModuleFlags; + b32 IsFunction; +}; +static_assert( sizeof(AST_Typedef) == sizeof(AST), "ERROR: AST_Typedef is not the same size as AST"); + +struct AST_Union +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + char _PAD_INLINE_CMT_[ sizeof(AST*) ]; + CodeAttributes Attributes; + char _PAD_PROPERTIES_[ sizeof(AST*) * 3 ]; + CodeBody Body; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Union) == sizeof(AST), "ERROR: AST_Union is not the same size as AST"); + +struct AST_Using +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + CodeAttributes Attributes; + char _PAD_SPECS_ [ sizeof(AST*) ]; + CodeTypename UnderlyingType; + char _PAD_PROPERTIES_[ sizeof(AST*) * 3 ]; + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Using) == sizeof(AST), "ERROR: AST_Using is not the same size as AST"); + +struct AST_Var +{ + union { + char _PAD_[ sizeof(Specifier) * AST_ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + CodeAttributes Attributes; + CodeSpecifiers Specs; + CodeTypename ValueType; + Code BitfieldSize; + Code Value; + CodeVar NextVar; + }; + }; + StrCached Name; + Code Prev; + Code Next; + Token* Tok; + Code Parent; + CodeType Type; + ModuleFlag ModuleFlags; + s32 VarParenthesizedInit; +}; +static_assert( sizeof(AST_Var) == sizeof(AST), "ERROR: AST_Var is not the same size as AST"); + +#pragma endregion AST Types diff --git a/tests/assets/gencpp_samples/components/code_serialization.cpp b/tests/assets/gencpp_samples/components/code_serialization.cpp new file mode 100644 index 0000000..39dacb5 --- /dev/null +++ b/tests/assets/gencpp_samples/components/code_serialization.cpp @@ -0,0 +1,1349 @@ +#ifdef INTELLISENSE_DIRECTIVES +#pragma once +#include "ast.cpp" +#endif + +StrBuilder body_to_strbuilder(CodeBody body) +{ + GEN_ASSERT(body); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 128 ); + switch ( body->Type ) + { + case CT_Untyped: + case CT_Execution: + strbuilder_append_str( & result, cast(Code, body)->Content ); + break; + + case CT_Enum_Body: + case CT_Class_Body: + case CT_Extern_Linkage_Body: + case CT_Function_Body: + case CT_Global_Body: + case CT_Namespace_Body: + case CT_Struct_Body: + case CT_Union_Body: + body_to_strbuilder_ref( body, & result ); + break; + + case CT_Export_Body: + body_to_strbuilder_export( body, & result ); + break; + } + return result; +} + +void body_to_strbuilder_export( CodeBody body, StrBuilder* result ) +{ + GEN_ASSERT(body != nullptr); + GEN_ASSERT(result != nullptr); + strbuilder_append_fmt( result, "export\n{\n" ); + + Code curr = body->Front; + s32 left = body->NumEntries; + while ( left-- ) + { + code_to_strbuilder_ref(curr, result); + // strbuilder_append_fmt( result, "%SB", code_to_strbuilder(curr) ); + curr = curr->Next; + } + + strbuilder_append_fmt( result, "};\n" ); +} + +StrBuilder constructor_to_strbuilder(CodeConstructor self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 128 ); + switch (self->Type) + { + case CT_Constructor: + constructor_to_strbuilder_def( self, & result ); + break; + case CT_Constructor_Fwd: + constructor_to_strbuilder_fwd( self, & result ); + break; + } + return result; +} + +void constructor_to_strbuilder_def(CodeConstructor self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + Code ClassStructParent = self->Parent->Parent; + if (ClassStructParent) { + strbuilder_append_str( result, ClassStructParent->Name ); + } + else { + strbuilder_append_str( result, self->Name ); + } + + if ( self->Params ) + strbuilder_append_fmt( result, "( %SB )", params_to_strbuilder(self->Params) ); + else + strbuilder_append_str( result, txt("()") ); + + if ( self->InitializerList ) + strbuilder_append_fmt( result, " : %SB", code_to_strbuilder(self->InitializerList) ); + + if ( self->InlineCmt ) + strbuilder_append_fmt( result, " // %S", self->InlineCmt->Content ); + + strbuilder_append_fmt( result, "\n{\n%SB\n}\n", code_to_strbuilder(self->Body) ); +} + +void constructor_to_strbuilder_fwd(CodeConstructor self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + Code ClassStructParent = self->Parent->Parent; + if (ClassStructParent) { + strbuilder_append_str( result, ClassStructParent->Name ); + } + else { + strbuilder_append_str( result, self->Name ); + } + + if ( self->Params ) + strbuilder_append_fmt( result, "( %SB )", params_to_strbuilder(self->Params) ); + else + strbuilder_append_fmt( result, "()"); + + if (self->Body) + strbuilder_append_fmt( result, " = %SB", code_to_strbuilder(self->Body) ); + + if ( self->InlineCmt ) + strbuilder_append_fmt( result, "; // %S\n", self->InlineCmt->Content ); + else + strbuilder_append_str( result, txt(";\n") ); +} + +StrBuilder class_to_strbuilder( CodeClass self ) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 512 ); + switch ( self->Type ) + { + case CT_Class: + class_to_strbuilder_def(self, & result ); + break; + case CT_Class_Fwd: + class_to_strbuilder_fwd(self, & result ); + break; + } + return result; +} + +void class_to_strbuilder_def( CodeClass self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + strbuilder_append_str( result, txt("class ") ); + + if ( self->Attributes ) + { + 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 %SB", self->Name, access_level, typename_to_strbuilder(self->ParentType) ); + + CodeTypename interface = cast(CodeTypename, self->ParentType->Next); + if ( interface ) + strbuilder_append_str( result, txt("\n") ); + + while ( interface ) + { + strbuilder_append_fmt( result, ", public %SB", typename_to_strbuilder(interface) ); + interface = interface->Next ? cast(CodeTypename, interface->Next) : NullCode; + } + } + + if ( self->InlineCmt ) + { + strbuilder_append_fmt( result, " // %S", self->InlineCmt->Content ); + } + + strbuilder_append_fmt( result, "\n{\n%SB\n}", body_to_strbuilder(self->Body) ); + + if ( self->Parent == nullptr || ( self->Parent->Type != CT_Typedef && self->Parent->Type != CT_Variable ) ) + strbuilder_append_str( result, txt(";\n") ); +} + +void class_to_strbuilder_fwd( CodeClass self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + if ( self->Attributes ) + strbuilder_append_fmt( result, "class %SB %S", attributes_to_strbuilder(self->Attributes), self->Name ); + + else strbuilder_append_fmt( result, "class %S", self->Name ); + + // Check if it can have an end-statement + if ( self->Parent == nullptr || ( self->Parent->Type != CT_Typedef && self->Parent->Type != CT_Variable ) ) + { + if ( self->InlineCmt ) + strbuilder_append_fmt( result, "; // %S\n", self->InlineCmt->Content ); + else + strbuilder_append_str( result, txt(";\n") ); + } +} + +void define_to_strbuilder_ref(CodeDefine define, StrBuilder* result ) +{ + GEN_ASSERT(define); + GEN_ASSERT(define->Body); + GEN_ASSERT(define->Body->Content.Ptr && define->Body->Content.Len > 0); + if (define->Params) { + StrBuilder params_builder = define_params_to_strbuilder(define->Params); + strbuilder_append_fmt( result, "#define %S(%S) %S", define->Name, strbuilder_to_str(params_builder), define->Body->Content ); + } + else { + strbuilder_append_fmt( result, "#define %S %S", define->Name, define->Body->Content ); + } +} + +void define_params_to_strbuilder_ref(CodeDefineParams self, StrBuilder* result) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( self->Name.Ptr && self->Name.Len ) + { + strbuilder_append_fmt( result, " %S", self->Name ); + } + if ( self->NumEntries - 1 > 0 ) + { + for ( CodeDefineParams param = begin_CodeDefineParams(self->Next); param != end_CodeDefineParams(self->Next); param = next_CodeDefineParams(self->Next, param) ) + { + strbuilder_append_fmt( result, ", %SB", define_params_to_strbuilder(param) ); + } + } +} + +StrBuilder destructor_to_strbuilder(CodeDestructor self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 128 ); + switch ( self->Type ) + { + case CT_Destructor: + destructor_to_strbuilder_def( self, & result ); + break; + case CT_Destructor_Fwd: + destructor_to_strbuilder_fwd( self, & result ); + break; + } + return result; +} + +void destructor_to_strbuilder_def(CodeDestructor self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( self->Name.Len ) + { + strbuilder_append_fmt( result, "%S()", self->Name ); + } + else if ( self->Specs ) + { + if ( specifiers_has(self->Specs, Spec_Virtual ) ) + strbuilder_append_fmt( result, "virtual ~%S()", self->Parent->Name ); + else + strbuilder_append_fmt( result, "~%S()", self->Parent->Name ); + } + else + strbuilder_append_fmt( result, "~%S()", self->Parent->Name ); + + strbuilder_append_fmt( result, "\n{\n%SB\n}\n", code_to_strbuilder(self->Body) ); +} + +void destructor_to_strbuilder_fwd(CodeDestructor self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( self->Specs ) + { + if ( specifiers_has(self->Specs, Spec_Virtual ) ) + strbuilder_append_fmt( result, "virtual ~%S();\n", self->Parent->Name ); + else + strbuilder_append_fmt( result, "~%S()", self->Parent->Name ); + + if ( specifiers_has(self->Specs, Spec_Pure ) ) + strbuilder_append_str( result, txt(" = 0;") ); + else if (self->Body) + strbuilder_append_fmt( result, " = %SB;", code_to_strbuilder(self->Body) ); + } + else + strbuilder_append_fmt( result, "~%S();", self->Parent->Name ); + + if ( self->InlineCmt ) + strbuilder_append_fmt( result, " %S", self->InlineCmt->Content ); + else + strbuilder_append_str( result, txt("\n")); +} + +StrBuilder enum_to_strbuilder(CodeEnum self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 512 ); + switch ( self->Type ) + { + case CT_Enum: + enum_to_strbuilder_def(self, & result ); + break; + case CT_Enum_Fwd: + enum_to_strbuilder_fwd(self, & result ); + break; + case CT_Enum_Class: + enum_to_strbuilder_class_def(self, & result ); + break; + case CT_Enum_Class_Fwd: + enum_to_strbuilder_class_fwd(self, & result ); + break; + } + return result; +} + +void enum_to_strbuilder_def(CodeEnum self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + if ( self->Attributes || self->UnderlyingType || self->UnderlyingTypeMacro ) + { + strbuilder_append_str( result, txt("enum ") ); + + if ( self->Attributes ) + strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); + + if ( self->UnderlyingType ) + strbuilder_append_fmt( result, "%S : %SB\n{\n%SB\n}" + , self->Name + , typename_to_strbuilder(self->UnderlyingType) + , body_to_strbuilder(self->Body) + ); + else if ( self->UnderlyingTypeMacro ) + strbuilder_append_fmt( result, "%S %SB\n{\n%SB\n}" + , self->Name + , code_to_strbuilder(self->UnderlyingTypeMacro) + , body_to_strbuilder(self->Body) + ); + + else strbuilder_append_fmt( result, "%S\n{\n%SB\n}", self->Name, body_to_strbuilder(self->Body) ); + } + else strbuilder_append_fmt( result, "enum %S\n{\n%SB\n}", self->Name, body_to_strbuilder(self->Body) ); + + if ( self->Parent == nullptr || ( self->Parent->Type != CT_Typedef && self->Parent->Type != CT_Variable ) ) + strbuilder_append_str( result, txt(";\n")); +} + +void enum_to_strbuilder_fwd(CodeEnum self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + if ( self->Attributes ) + strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); + + if ( self->UnderlyingType ) + strbuilder_append_fmt( result, "enum %S : %SB", self->Name, typename_to_strbuilder(self->UnderlyingType) ); + else if (self->UnderlyingTypeMacro) + { + log_fmt("IDENTIFIED A UNDERLYING ENUM MACRO"); + strbuilder_append_fmt( result, "enum %S %SB", self->Name, code_to_strbuilder(self->UnderlyingTypeMacro) ); + } + else + strbuilder_append_fmt( result, "enum %S", self->Name ); + + if ( self->Parent == nullptr || ( self->Parent->Type != CT_Typedef && self->Parent->Type != CT_Variable ) ) + { + if ( self->InlineCmt ) + strbuilder_append_fmt( result, "; %S", self->InlineCmt->Content ); + else + strbuilder_append_str( result, txt(";\n")); + } +} + +void enum_to_strbuilder_class_def(CodeEnum self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + if ( self->Attributes || self->UnderlyingType ) + { + strbuilder_append_str( result, txt("enum class ") ); + + if ( self->Attributes ) + { + strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); + } + + if ( self->UnderlyingType ) + { + strbuilder_append_fmt( result, "%S : %SB\n{\n%SB\n}", self->Name, typename_to_strbuilder(self->UnderlyingType), body_to_strbuilder(self->Body) ); + } + else + { + strbuilder_append_fmt( result, "%S\n{\n%SB\n}", self->Name, body_to_strbuilder(self->Body) ); + } + } + else + { + strbuilder_append_fmt( result, "enum %S\n{\n%SB\n}", self->Name, body_to_strbuilder(self->Body) ); + } + + if ( self->Parent == nullptr || ( self->Parent->Type != CT_Typedef && self->Parent->Type != CT_Variable ) ) + strbuilder_append_str( result, txt(";\n")); +} + +void enum_to_strbuilder_class_fwd(CodeEnum self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + strbuilder_append_str( result, txt("enum class ") ); + + if ( self->Attributes ) + strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); + + strbuilder_append_fmt( result, "%S : %SB", self->Name, typename_to_strbuilder(self->UnderlyingType) ); + + if ( self->Parent == nullptr || ( self->Parent->Type != CT_Typedef && self->Parent->Type != CT_Variable ) ) + { + if ( self->InlineCmt ) + strbuilder_append_fmt( result, "; %S", self->InlineCmt->Content ); + else + strbuilder_append_str( result, txt(";\n")); + } +} + +StrBuilder fn_to_strbuilder(CodeFn self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 512 ); + switch ( self->Type ) + { + case CT_Function: + fn_to_strbuilder_def(self, & result ); + break; + case CT_Function_Fwd: + fn_to_strbuilder_fwd(self, & result ); + break; + } + return result; +} + +void fn_to_strbuilder_def(CodeFn self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export") ); + + if ( self->Attributes ) + strbuilder_append_fmt( result, " %SB ", attributes_to_strbuilder(self->Attributes) ); + + bool prefix_specs = false; + if ( self->Specs ) + { + for ( Specifier* spec = begin_CodeSpecifiers(self->Specs); spec != end_CodeSpecifiers(self->Specs); spec = next_CodeSpecifiers(self->Specs, spec) ) + { + if ( ! spec_is_trailing( * spec ) ) + { + Str spec_str = spec_to_str( * spec ); + strbuilder_append_fmt( result, " %.*s", spec_str.Len, spec_str.Ptr ); + + prefix_specs = true; + } + } + } + + if ( self->Attributes || prefix_specs ) + strbuilder_append_str( result, txt("\n") ); + + if ( self->ReturnType ) + strbuilder_append_fmt( result, "%SB %S(", typename_to_strbuilder(self->ReturnType), self->Name ); + + else + strbuilder_append_fmt( result, "%S(", self->Name ); + + if ( self->Params ) + strbuilder_append_fmt( result, "%SB)", params_to_strbuilder(self->Params) ); + + else + strbuilder_append_str( result, txt(")") ); + + if ( self->Specs ) + { + for ( Specifier* spec = begin_CodeSpecifiers(self->Specs); spec != end_CodeSpecifiers(self->Specs); spec = next_CodeSpecifiers(self->Specs, spec) ) + { + if ( spec_is_trailing( * spec ) ) + { + Str spec_str = spec_to_str( * spec ); + strbuilder_append_fmt( result, " %.*s", spec_str.Len, spec_str.Ptr ); + } + } + } + + // This is bodged in for now SOLEY for Unreal's PURE_VIRTUAL functional macro + if ( self->SuffixSpecs ) + strbuilder_append_fmt( result, " %SB", code_to_strbuilder(self->SuffixSpecs) ); + + strbuilder_append_fmt( result, "\n{\n%SB\n}\n", body_to_strbuilder(self->Body) ); +} + +void fn_to_strbuilder_fwd(CodeFn self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + if ( self->Attributes ) + strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); + + b32 prefix_specs = false; + if ( self->Specs ) + { + for ( Specifier* spec = begin_CodeSpecifiers(self->Specs); spec != end_CodeSpecifiers(self->Specs); spec = next_CodeSpecifiers(self->Specs, spec) ) + { + if ( ! spec_is_trailing( * spec ) || ! ( * spec != Spec_Pure) ) + { + Str spec_str = spec_to_str( * spec ); + strbuilder_append_fmt( result, " %.*s", spec_str.Len, spec_str.Ptr ); + + prefix_specs = true; + } + } + } + + if ( self->Attributes || prefix_specs ) + { + strbuilder_append_str( result, txt("\n") ); + } + + if ( self->ReturnType ) + strbuilder_append_fmt( result, "%SB %S(", typename_to_strbuilder(self->ReturnType), self->Name ); + + else + strbuilder_append_fmt( result, "%S(", self->Name ); + + if ( self->Params ) + strbuilder_append_fmt( result, "%SB)", params_to_strbuilder(self->Params) ); + + else + strbuilder_append_str( result, txt(")") ); + + if ( self->Specs ) + { + for ( Specifier* spec = begin_CodeSpecifiers(self->Specs); spec != end_CodeSpecifiers(self->Specs); spec = next_CodeSpecifiers(self->Specs, spec) ) + { + if ( spec_is_trailing( * spec ) ) + { + Str spec_str = spec_to_str( * spec ); + strbuilder_append_fmt( result, " %.*s", spec_str.Len, spec_str.Ptr ); + } + } + + if ( specifiers_has(self->Specs, Spec_Pure ) ) + strbuilder_append_str( result, txt(" = 0") ); + else if ( specifiers_has(self->Specs, Spec_Delete ) ) + 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 ) + strbuilder_append_fmt( result, " %SB", code_to_strbuilder(self->SuffixSpecs) ); + + if (self->Body) + strbuilder_append_fmt( result, " = %SB;", body_to_strbuilder(self->Body) ); + + if ( self->InlineCmt ) + strbuilder_append_fmt( result, "; %S", self->InlineCmt->Content ); + else + strbuilder_append_str( result, txt(";\n") ); +} + +void module_to_strbuilder_ref(CodeModule self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if (((scast(u32, ModuleFlag_Export) & scast(u32, self->ModuleFlags)) == scast(u32, ModuleFlag_Export))) + strbuilder_append_str( result, txt("export ")); + + if (((scast(u32, ModuleFlag_Import) & scast(u32, self->ModuleFlags)) == scast(u32, ModuleFlag_Import))) + strbuilder_append_str( result, txt("import ")); + + strbuilder_append_fmt( result, "%S;\n", self->Name ); +} + +StrBuilder code_op_to_strbuilder(CodeOperator self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 512 ); + switch ( self->Type ) + { + case CT_Operator: + case CT_Operator_Member: + code_op_to_strbuilder_def( self, & result ); + break; + case CT_Operator_Fwd: + case CT_Operator_Member_Fwd: + code_op_to_strbuilder_fwd( self, & result ); + break; + } + return result; +} + +void code_op_to_strbuilder_def(CodeOperator self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + if ( self->Attributes ) + strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); + + if ( self->Attributes ) + strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); + + if ( self->Specs ) + { + for ( Specifier* spec = begin_CodeSpecifiers(self->Specs); spec != end_CodeSpecifiers(self->Specs); spec = next_CodeSpecifiers(self->Specs, spec) ) + { + if ( ! spec_is_trailing( * spec ) ) + { + Str spec_str = spec_to_str( * spec ); + strbuilder_append_fmt( result, " %.*s", spec_str.Len, spec_str.Ptr ); + } + } + } + + if ( self->Attributes || self->Specs ) + { + strbuilder_append_str( result, txt("\n") ); + } + + if ( self->ReturnType ) + strbuilder_append_fmt( result, "%SB %S (", typename_to_strbuilder(self->ReturnType), self->Name ); + + if ( self->Params ) + strbuilder_append_fmt( result, "%SB)", params_to_strbuilder(self->Params) ); + + else + strbuilder_append_str( result, txt(")") ); + + if ( self->Specs ) + { + for ( Specifier* spec = begin_CodeSpecifiers(self->Specs); spec != end_CodeSpecifiers(self->Specs); spec = next_CodeSpecifiers(self->Specs, spec) ) + { + if ( spec_is_trailing( * spec ) ) + { + Str spec_str = spec_to_str( * spec ); + strbuilder_append_fmt( result, " %.*s", spec_str.Len, spec_str.Ptr ); + } + } + } + + strbuilder_append_fmt( result, "\n{\n%SB\n}\n" + , body_to_strbuilder(self->Body) + ); +} + +void code_op_to_strbuilder_fwd(CodeOperator self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + if ( self->Attributes ) + strbuilder_append_fmt( result, "%SB\n", attributes_to_strbuilder(self->Attributes) ); + + if ( self->Specs ) + { + for ( Specifier* spec = begin_CodeSpecifiers(self->Specs); spec != end_CodeSpecifiers(self->Specs); spec = next_CodeSpecifiers(self->Specs, spec) ) + { + if ( ! spec_is_trailing( * spec ) ) + { + Str spec_str = spec_to_str( * spec ); + strbuilder_append_fmt( result, " %.*s", spec_str.Len, spec_str.Ptr ); + } + } + } + + if ( self->Attributes || self->Specs ) + { + strbuilder_append_str( result, txt("\n") ); + } + + strbuilder_append_fmt( result, "%SB %S (", typename_to_strbuilder(self->ReturnType), self->Name ); + + if ( self->Params ) + strbuilder_append_fmt( result, "%SB)", params_to_strbuilder(self->Params) ); + + else + strbuilder_append_fmt( result, ")" ); + + if ( self->Specs ) + { + for ( Specifier* spec = begin_CodeSpecifiers(self->Specs); spec != end_CodeSpecifiers(self->Specs); spec = next_CodeSpecifiers(self->Specs, spec) ) + { + if ( spec_is_trailing( * spec ) ) + { + Str spec_str = spec_to_str( * spec ); + strbuilder_append_fmt( result, " %.*s", spec_str.Len, spec_str.Ptr ); + } + } + } + + if ( self->InlineCmt ) + strbuilder_append_fmt( result, "; %S", self->InlineCmt->Content ); + else + strbuilder_append_str( result, txt(";\n") ); +} + +StrBuilder opcast_to_strbuilder(CodeOpCast self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 128 ); + switch ( self->Type ) + { + case CT_Operator_Cast: + opcast_to_strbuilder_def(self, & result ); + break; + case CT_Operator_Cast_Fwd: + opcast_to_strbuilder_fwd(self, & result ); + break; + } + return result; +} + +void opcast_to_strbuilder_def(CodeOpCast self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( self->Specs ) + { + for ( Specifier* spec = begin_CodeSpecifiers(self->Specs); spec != end_CodeSpecifiers(self->Specs); spec = next_CodeSpecifiers(self->Specs, spec) ) + { + if ( ! spec_is_trailing( * spec ) ) + { + Str spec_str = spec_to_str( * spec ); + strbuilder_append_fmt( result, "%*s ", spec_str.Len, spec_str.Ptr ); + } + } + + if ( self->Name.Ptr && self->Name.Len ) + strbuilder_append_fmt( result, "%S operator %SB()", self->Name, typename_to_strbuilder(self->ValueType) ); + else + strbuilder_append_fmt( result, "operator %SB()", typename_to_strbuilder(self->ValueType) ); + + for ( Specifier* spec = begin_CodeSpecifiers(self->Specs); spec != end_CodeSpecifiers(self->Specs); spec = next_CodeSpecifiers(self->Specs, spec) ) + { + if ( spec_is_trailing( * spec ) ) + { + Str spec_str = spec_to_str( * spec ); + strbuilder_append_fmt( result, " %.*s", spec_str.Len, spec_str.Ptr ); + } + } + + strbuilder_append_fmt( result, "\n{\n%SB\n}\n", body_to_strbuilder(self->Body) ); + return; + } + + if ( self->Name.Ptr && self->Name.Len ) + strbuilder_append_fmt( result, "%S operator %SB()\n{\n%SB\n}\n", self->Name, typename_to_strbuilder(self->ValueType), body_to_strbuilder(self->Body) ); + else + strbuilder_append_fmt( result, "operator %SB()\n{\n%SB\n}\n", typename_to_strbuilder(self->ValueType), body_to_strbuilder(self->Body) ); +} + +void opcast_to_strbuilder_fwd(CodeOpCast self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( self->Specs ) + { + for ( Specifier* spec = begin_CodeSpecifiers(self->Specs); spec != end_CodeSpecifiers(self->Specs); spec = next_CodeSpecifiers(self->Specs, spec) ) + { + if ( ! spec_is_trailing( * spec ) ) + { + Str spec_str = spec_to_str( * spec ); + strbuilder_append_fmt( result, "%*s ", spec_str.Len, spec_str.Ptr ); + } + } + + strbuilder_append_fmt( result, "operator %SB()", typename_to_strbuilder(self->ValueType) ); + + for ( Specifier* spec = begin_CodeSpecifiers(self->Specs); spec != end_CodeSpecifiers(self->Specs); spec = next_CodeSpecifiers(self->Specs, spec) ) + { + if ( spec_is_trailing( * spec ) ) + { + Str spec_str = spec_to_str( * spec ); + strbuilder_append_fmt( result, " %*s", spec_str.Len, spec_str.Ptr ); + } + } + + if ( self->InlineCmt ) + strbuilder_append_fmt( result, "; %S", self->InlineCmt->Content ); + else + strbuilder_append_str( result, txt(";\n") ); + return; + } + + if ( self->InlineCmt ) + strbuilder_append_fmt( result, "operator %SB(); %SB", typename_to_strbuilder(self->ValueType) ); + else + strbuilder_append_fmt( result, "operator %SB();\n", typename_to_strbuilder(self->ValueType) ); +} + +void params_to_strbuilder_ref( CodeParams self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( self->Macro ) + { + // Related to parsing: ( , ... ) + strbuilder_append_str( result, self->Macro->Content ); + // Could also be: ( , ... ) + } + + if ( self->Name.Ptr && self->Name.Len ) + { + if ( self->ValueType == nullptr ) + strbuilder_append_fmt( result, " %S", self->Name ); + else + strbuilder_append_fmt( result, " %SB %S", typename_to_strbuilder(self->ValueType), self->Name ); + + } + else if ( self->ValueType ) + strbuilder_append_fmt( result, " %SB", typename_to_strbuilder(self->ValueType) ); + + if ( self->PostNameMacro ) + { + strbuilder_append_fmt( result, " %SB", code_to_strbuilder(self->PostNameMacro) ); + } + + if ( self->Value ) + strbuilder_append_fmt( result, " = %SB", code_to_strbuilder(self->Value) ); + + if ( self->NumEntries - 1 > 0 ) + { + for ( CodeParams param = begin_CodeParams(self->Next); param != end_CodeParams(self->Next); param = next_CodeParams(self->Next, param) ) + { + strbuilder_append_fmt( result, ", %SB", params_to_strbuilder(param) ); + } + } +} + +StrBuilder preprocess_to_strbuilder(CodePreprocessCond self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 256 ); + switch ( self->Type ) + { + case CT_Preprocess_If: + preprocess_to_strbuilder_if( self, & result ); + break; + case CT_Preprocess_IfDef: + preprocess_to_strbuilder_ifdef( self, & result ); + break; + case CT_Preprocess_IfNotDef: + preprocess_to_strbuilder_ifndef( self, & result ); + break; + case CT_Preprocess_ElIf: + preprocess_to_strbuilder_elif( self, & result ); + break; + case CT_Preprocess_Else: + preprocess_to_strbuilder_else( self, & result ); + break; + case CT_Preprocess_EndIf: + preprocess_to_strbuilder_endif( self, & result ); + break; + } + return result; +} + +void specifiers_to_strbuilder_ref( CodeSpecifiers self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + s32 idx = 0; + s32 left = self->NumEntries; + while ( left -- ) + { + Specifier spec = self->ArrSpecs[idx]; + Str spec_str = spec_to_str( spec ); + if ( idx > 0 ) switch (spec) { + case Spec_Ptr: + case Spec_Ref: + case Spec_RValue: + break; + + default: + strbuilder_append_str(result, txt(" ")); + break; + } + strbuilder_append_fmt( result, "%S", spec_str ); + idx++; + } + strbuilder_append_str(result, txt(" ")); +} + +StrBuilder struct_to_strbuilder(CodeStruct self) +{ + GEN_ASSERT(self); + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 512 ); + switch ( self->Type ) + { + case CT_Struct: + struct_to_strbuilder_def( self, & result ); + break; + case CT_Struct_Fwd: + struct_to_strbuilder_fwd( self, & result ); + break; + } + return result; +} + +void struct_to_strbuilder_def( CodeStruct self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + strbuilder_append_str( result, txt("struct ") ); + + if ( self->Attributes ) + { + 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 %SB", access_level, typename_to_strbuilder(self->ParentType) ); + + CodeTypename interface = cast(CodeTypename, self->ParentType->Next); + if ( interface ) + strbuilder_append_str( result, txt("\n") ); + + while ( interface ) + { + strbuilder_append_fmt( result, ", %SB", typename_to_strbuilder(interface) ); + interface = interface->Next ? cast( CodeTypename, interface->Next) : NullCode; + } + } + + if ( self->InlineCmt ) + { + strbuilder_append_fmt( result, " // %S", self->InlineCmt->Content ); + } + + strbuilder_append_fmt( result, "\n{\n%SB\n}", body_to_strbuilder(self->Body) ); + + if ( self->Parent == nullptr || ( self->Parent->Type != CT_Typedef && self->Parent->Type != CT_Variable ) ) + strbuilder_append_str( result, txt(";\n")); +} + +void struct_to_strbuilder_fwd( CodeStruct self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + if ( self->Attributes ) + strbuilder_append_fmt( result, "struct %SB %S", attributes_to_strbuilder(self->Attributes), self->Name ); + + else strbuilder_append_fmt( result, "struct %S", self->Name ); + + if ( self->Parent == nullptr || ( self->Parent->Type != CT_Typedef && self->Parent->Type != CT_Variable ) ) + { + if ( self->InlineCmt ) + strbuilder_append_fmt( result, "; %S", self->InlineCmt->Content ); + else + strbuilder_append_str( result, txt( ";\n") ); + } +} + +void template_to_strbuilder_ref(CodeTemplate self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + if ( self->Params ) + strbuilder_append_fmt( result, "template< %SB >\n%SB", params_to_strbuilder(self->Params), code_to_strbuilder(self->Declaration) ); + else + strbuilder_append_fmt( result, "template<>\n%SB", code_to_strbuilder(self->Declaration) ); +} + +void typedef_to_strbuilder_ref(CodeTypedef self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + strbuilder_append_str( result, txt("typedef ")); + + // Determines if the typedef is a function typename + if ( self->UnderlyingType->ReturnType ) + strbuilder_append_string( result, code_to_strbuilder(self->UnderlyingType) ); + else + strbuilder_append_fmt( result, "%SB %S", code_to_strbuilder(self->UnderlyingType), self->Name ); + + if ( self->UnderlyingType->Type == CT_Typename && self->UnderlyingType->ArrExpr ) + { + strbuilder_append_fmt( result, "[ %SB ];", code_to_strbuilder(self->UnderlyingType->ArrExpr) ); + + Code next_arr_expr = self->UnderlyingType->ArrExpr->Next; + while ( next_arr_expr ) + { + strbuilder_append_fmt( result, "[ %SB ];", code_to_strbuilder(next_arr_expr) ); + next_arr_expr = next_arr_expr->Next; + } + } + else + { + strbuilder_append_str( result, txt(";") ); + } + + if ( self->InlineCmt ) + strbuilder_append_fmt( result, " %S", self->InlineCmt->Content); + else + strbuilder_append_str( result, txt("\n")); +} + +void typename_to_strbuilder_ref(CodeTypename self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + #if defined(GEN_USE_NEW_TYPENAME_PARSING) + if ( self->ReturnType && self->Params ) + { + if ( self->Attributes ) + strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); + else + { + if ( self->Specs ) + strbuilder_append_fmt( result, "%SB ( %S ) ( %SB ) %SB", typename_to_strbuilder(self->ReturnType), self->Name, params_to_strbuilder(self->Params), specifiers_to_strbuilder(self->Specs) ); + else + strbuilder_append_fmt( result, "%SB ( %S ) ( %SB )", typename_to_strbuilder(self->ReturnType), self->Name, params_to_strbuilder(self->Params) ); + } + + break; + } + #else + if ( self->ReturnType && self->Params ) + { + if ( self->Attributes ) + strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); + else + { + if ( self->Specs ) + strbuilder_append_fmt( result, "%SB %S ( %SB ) %SB", typename_to_strbuilder(self->ReturnType), self->Name, params_to_strbuilder(self->Params), specifiers_to_strbuilder(self->Specs) ); + else + strbuilder_append_fmt( result, "%SB %S ( %SB )", typename_to_strbuilder(self->ReturnType), self->Name, params_to_strbuilder(self->Params) ); + } + + return; + } + #endif + + if ( self->Attributes ) + strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); + + switch ( self->TypeTag ) + { + case Tag_Class : strbuilder_append_str( result, txt("class ")); break; + case Tag_Enum : strbuilder_append_str( result, txt("enum ")); break; + case Tag_Struct : strbuilder_append_str( result, txt("struct ")); break; + case Tag_Union : strbuilder_append_str( result, txt("union ")); break; + default: + break; + } + + if ( self->Specs ) + strbuilder_append_fmt( result, "%S %SB", self->Name, specifiers_to_strbuilder(self->Specs) ); + else + strbuilder_append_fmt( result, "%S", self->Name ); + + if ( self->IsParamPack ) + strbuilder_append_str( result, txt("...")); +} + +StrBuilder union_to_strbuilder(CodeUnion self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 512 ); + switch ( self->Type ) + { + case CT_Union: + union_to_strbuilder_def( self, & result ); + break; + case CT_Union_Fwd: + union_to_strbuilder_fwd( self, & result ); + break; + } + return result; +} + +void union_to_strbuilder_def(CodeUnion self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + strbuilder_append_str( result, txt("union ") ); + + if ( self->Attributes ) + strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); + + if ( self->Name.Len ) + { + strbuilder_append_fmt( result, "%S\n{\n%SB\n}" + , self->Name + , body_to_strbuilder(self->Body) + ); + } + else + { + // Anonymous union + strbuilder_append_fmt( result, "\n{\n%SB\n}" + , body_to_strbuilder(self->Body) + ); + } + + if ( self->Parent == nullptr || ( self->Parent->Type != CT_Typedef && self->Parent->Type != CT_Variable ) ) + strbuilder_append_str( result, txt(";\n")); +} + +void union_to_strbuilder_fwd(CodeUnion self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + strbuilder_append_str( result, txt("union ") ); + + if ( self->Attributes ) + strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); + + if ( self->Name.Len ) + { + strbuilder_append_fmt( result, "%S", self->Name); + } + + if ( self->Parent == nullptr || ( self->Parent->Type != CT_Typedef && self->Parent->Type != CT_Variable ) ) + strbuilder_append_str( result, txt(";\n")); +} + +void using_to_strbuilder_ref(CodeUsing self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + if ( self->Attributes ) + strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); + + if ( self->UnderlyingType ) + { + strbuilder_append_fmt( result, "using %S = %SB", self->Name, typename_to_strbuilder(self->UnderlyingType) ); + + if ( self->UnderlyingType->ArrExpr ) + { + strbuilder_append_fmt( result, "[ %SB ]", code_to_strbuilder(self->UnderlyingType->ArrExpr) ); + + Code next_arr_expr = self->UnderlyingType->ArrExpr->Next; + while ( next_arr_expr ) + { + strbuilder_append_fmt( result, "[ %SB ]", code_to_strbuilder(next_arr_expr) ); + next_arr_expr = next_arr_expr->Next; + } + } + + strbuilder_append_str( result, txt(";") ); + } + else + strbuilder_append_fmt( result, "using %S;", self->Name ); + + if ( self->InlineCmt ) + strbuilder_append_fmt( result, " %S\n", self->InlineCmt->Content ); + else + strbuilder_append_str( result, txt("\n")); +} + +neverinline +void var_to_strbuilder_ref(CodeVar self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( self->Parent && self->Parent->Type == CT_Variable ) + { + // Its a comma-separated variable ( a NextVar ) + + if ( self->Specs ) + strbuilder_append_fmt( result, "%SB ", specifiers_to_strbuilder(self->Specs) ); + + strbuilder_append_str( result, self->Name ); + + if ( self->ValueType && self->ValueType->ArrExpr ) + { + strbuilder_append_fmt( result, "[ %SB ]", code_to_strbuilder(self->ValueType->ArrExpr) ); + + Code next_arr_expr = self->ValueType->ArrExpr->Next; + while ( next_arr_expr ) + { + strbuilder_append_fmt( result, "[ %SB ]", code_to_strbuilder(next_arr_expr) ); + next_arr_expr = next_arr_expr->Next; + } + } + + if ( self->Value ) + { + if ( self->VarParenthesizedInit ) + strbuilder_append_fmt( result, "( %SB ", code_to_strbuilder(self->Value) ); + else + strbuilder_append_fmt( result, " = %SB", code_to_strbuilder(self->Value) ); + } + + // Keep the chain going... + if ( self->NextVar ) + strbuilder_append_fmt( result, ", %SB", var_to_strbuilder(self->NextVar) ); + + if ( self->VarParenthesizedInit ) + strbuilder_append_str( result, txt(" )")); + + return; + } + + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + if ( self->Attributes || self->Specs ) + { + if ( self->Attributes ) + strbuilder_append_fmt( result, "%SB ", attributes_to_strbuilder(self->Attributes) ); + + if ( self->Specs ) + strbuilder_append_fmt( result, "%SB", specifiers_to_strbuilder(self->Specs) ); + + strbuilder_append_fmt( result, "%SB %S", typename_to_strbuilder(self->ValueType), self->Name ); + + if ( self->ValueType && self->ValueType->ArrExpr ) + { + strbuilder_append_fmt( result, "[ %SB ]", code_to_strbuilder(self->ValueType->ArrExpr) ); + + Code next_arr_expr = self->ValueType->ArrExpr->Next; + while ( next_arr_expr ) + { + strbuilder_append_fmt( result, "[ %SB ]", code_to_strbuilder(next_arr_expr) ); + next_arr_expr = next_arr_expr->Next; + } + } + + if ( self->BitfieldSize ) + strbuilder_append_fmt( result, " : %SB", code_to_strbuilder(self->BitfieldSize) ); + + if ( self->Value ) + { + if ( self->VarParenthesizedInit ) + strbuilder_append_fmt( result, "( %SB ", code_to_strbuilder(self->Value) ); + else + strbuilder_append_fmt( result, " = %SB", code_to_strbuilder(self->Value) ); + } + + if ( self->NextVar ) + strbuilder_append_fmt( result, ", %SB", var_to_strbuilder(self->NextVar) ); + + if ( self->VarParenthesizedInit ) + strbuilder_append_str( result, txt(" )")); + + if ( self->InlineCmt ) + strbuilder_append_fmt( result, "; %S", self->InlineCmt->Content); + else + strbuilder_append_str( result, txt(";\n") ); + + return; + } + + if ( self->BitfieldSize ) + strbuilder_append_fmt( result, "%SB %S : %SB", typename_to_strbuilder(self->ValueType), self->Name, code_to_strbuilder(self->BitfieldSize) ); + + else if ( self->ValueType && self->ValueType->ArrExpr ) + { + strbuilder_append_fmt( result, "%SB %S[ %SB ]", typename_to_strbuilder(self->ValueType), self->Name, code_to_strbuilder(self->ValueType->ArrExpr) ); + + Code next_arr_expr = self->ValueType->ArrExpr->Next; + while ( next_arr_expr ) + { + strbuilder_append_fmt( result, "[ %SB ]", code_to_strbuilder(next_arr_expr) ); + next_arr_expr = next_arr_expr->Next; + } + } + + else + strbuilder_append_fmt( result, "%SB %S", typename_to_strbuilder(self->ValueType), self->Name ); + + if ( self->Value ) + { + if ( self->VarParenthesizedInit ) + strbuilder_append_fmt( result, "( %SB ", code_to_strbuilder(self->Value) ); + else + strbuilder_append_fmt( result, " = %SB", code_to_strbuilder(self->Value) ); + } + + if ( self->NextVar ) + strbuilder_append_fmt( result, ", %SB", var_to_strbuilder( self->NextVar) ); + + if ( self->VarParenthesizedInit ) + strbuilder_append_str( result, txt(" )")); + + strbuilder_append_str( result, txt(";") ); + + if ( self->InlineCmt ) + strbuilder_append_fmt( result, " %S", self->InlineCmt->Content); + else + strbuilder_append_str( result, txt("\n")); +} diff --git a/tests/assets/gencpp_samples/components/code_types.hpp b/tests/assets/gencpp_samples/components/code_types.hpp new file mode 100644 index 0000000..59b8a77 --- /dev/null +++ b/tests/assets/gencpp_samples/components/code_types.hpp @@ -0,0 +1,1176 @@ +#ifdef INTELLISENSE_DIRECTIVES +#pragma once +#include "ast.hpp" +#endif + +/* + ______ __ ______ __ ______ + / \ | \ | \ | \ / \ +| ▓▓▓▓▓▓\ ______ ____| ▓▓ ______ \▓▓▓▓▓▓_______ _| ▓▓_ ______ ______ | ▓▓▓▓▓▓\ ______ _______ ______ +| ▓▓ \▓▓/ \ / ▓▓/ \ | ▓▓ | \| ▓▓ \ / \ / \| ▓▓_ \▓▓| \ / \/ \ +| ▓▓ | ▓▓▓▓▓▓\ ▓▓▓▓▓▓▓ ▓▓▓▓▓▓\ | ▓▓ | ▓▓▓▓▓▓▓\\▓▓▓▓▓▓ | ▓▓▓▓▓▓\ ▓▓▓▓▓▓\ ▓▓ \ \▓▓▓▓▓▓\ ▓▓▓▓▓▓▓ ▓▓▓▓▓▓\ +| ▓▓ __| ▓▓ | ▓▓ ▓▓ | ▓▓ ▓▓ ▓▓ | ▓▓ | ▓▓ | ▓▓ | ▓▓ __| ▓▓ ▓▓ ▓▓ \▓▓ ▓▓▓▓ / ▓▓ ▓▓ | ▓▓ ▓▓ +| ▓▓__/ \ ▓▓__/ ▓▓ ▓▓__| ▓▓ ▓▓▓▓▓▓▓▓ _| ▓▓_| ▓▓ | ▓▓ | ▓▓| \ ▓▓▓▓▓▓▓▓ ▓▓ | ▓▓ | ▓▓▓▓▓▓▓ ▓▓_____| ▓▓▓▓▓▓▓▓ + \▓▓ ▓▓\▓▓ ▓▓\▓▓ ▓▓\▓▓ \ | ▓▓ \ ▓▓ | ▓▓ \▓▓ ▓▓\▓▓ \ ▓▓ | ▓▓ \▓▓ ▓▓\▓▓ \\▓▓ \ + \▓▓▓▓▓▓ \▓▓▓▓▓▓ \▓▓▓▓▓▓▓ \▓▓▓▓▓▓▓ \▓▓▓▓▓▓\▓▓ \▓▓ \▓▓▓▓ \▓▓▓▓▓▓▓\▓▓ \▓▓ \▓▓▓▓▓▓▓ \▓▓▓▓▓▓▓ \▓▓▓▓▓▓▓ +*/ + +#pragma region Code Type C-Interface + +GEN_API void body_append ( CodeBody body, Code other ); +GEN_API void body_append_body ( CodeBody body, CodeBody other ); +GEN_API StrBuilder body_to_strbuilder ( CodeBody body ); + void body_to_strbuilder_ref ( CodeBody body, StrBuilder* result ); +GEN_API void body_to_strbuilder_export( CodeBody body, StrBuilder* result ); + +Code begin_CodeBody( CodeBody body); +Code end_CodeBody ( CodeBody body ); +Code next_CodeBody ( CodeBody body, Code entry_iter ); + + void class_add_interface ( CodeClass self, CodeTypename interface ); +GEN_API StrBuilder class_to_strbuilder ( CodeClass self ); +GEN_API void class_to_strbuilder_def( CodeClass self, StrBuilder* result ); +GEN_API void class_to_strbuilder_fwd( CodeClass self, StrBuilder* result ); + + void define_params_append (CodeDefineParams appendee, CodeDefineParams other ); + CodeDefineParams define_params_get (CodeDefineParams params, s32 idx); + bool define_params_has_entries (CodeDefineParams params ); + StrBuilder define_params_to_strbuilder (CodeDefineParams params ); +GEN_API void define_params_to_strbuilder_ref(CodeDefineParams params, StrBuilder* result ); + +CodeDefineParams begin_CodeDefineParams(CodeDefineParams params); +CodeDefineParams end_CodeDefineParams (CodeDefineParams params); +CodeDefineParams next_CodeDefineParams (CodeDefineParams params, CodeDefineParams entry_iter); + + void params_append (CodeParams appendee, CodeParams other ); + CodeParams params_get (CodeParams params, s32 idx); + bool params_has_entries (CodeParams params ); + StrBuilder params_to_strbuilder (CodeParams params ); +GEN_API void params_to_strbuilder_ref(CodeParams params, StrBuilder* result ); + +CodeParams begin_CodeParams(CodeParams params); +CodeParams end_CodeParams (CodeParams params); +CodeParams next_CodeParams (CodeParams params, CodeParams entry_iter); + + bool specifiers_append (CodeSpecifiers specifiers, Specifier spec); + bool specifiers_has (CodeSpecifiers specifiers, Specifier spec); + s32 specifiers_index_of (CodeSpecifiers specifiers, Specifier spec); + s32 specifiers_remove (CodeSpecifiers specifiers, Specifier to_remove ); + StrBuilder specifiers_to_strbuilder (CodeSpecifiers specifiers); +GEN_API void specifiers_to_strbuilder_ref(CodeSpecifiers specifiers, StrBuilder* result); + +Specifier* begin_CodeSpecifiers(CodeSpecifiers specifiers); +Specifier* end_CodeSpecifiers (CodeSpecifiers specifiers); +Specifier* next_CodeSpecifiers (CodeSpecifiers specifiers, Specifier* spec_iter); + + void struct_add_interface (CodeStruct self, CodeTypename interface); +GEN_API StrBuilder struct_to_strbuilder (CodeStruct self); +GEN_API void struct_to_strbuilder_fwd(CodeStruct self, StrBuilder* result); +GEN_API void struct_to_strbuilder_def(CodeStruct self, StrBuilder* result); + + StrBuilder attributes_to_strbuilder (CodeAttributes attributes); + void attributes_to_strbuilder_ref(CodeAttributes attributes, StrBuilder* result); + + StrBuilder comment_to_strbuilder (CodeComment comment ); + void comment_to_strbuilder_ref(CodeComment comment, StrBuilder* result ); + +GEN_API StrBuilder constructor_to_strbuilder (CodeConstructor constructor); +GEN_API void constructor_to_strbuilder_def(CodeConstructor constructor, StrBuilder* result ); +GEN_API void constructor_to_strbuilder_fwd(CodeConstructor constructor, StrBuilder* result ); + +GEN_API StrBuilder define_to_strbuilder (CodeDefine self); +GEN_API void define_to_strbuilder_ref(CodeDefine self, StrBuilder* result); + +GEN_API StrBuilder destructor_to_strbuilder (CodeDestructor destructor); +GEN_API void destructor_to_strbuilder_fwd(CodeDestructor destructor, StrBuilder* result ); +GEN_API void destructor_to_strbuilder_def(CodeDestructor destructor, StrBuilder* result ); + +GEN_API StrBuilder enum_to_strbuilder (CodeEnum self); +GEN_API void enum_to_strbuilder_def (CodeEnum self, StrBuilder* result ); +GEN_API void enum_to_strbuilder_fwd (CodeEnum self, StrBuilder* result ); +GEN_API void enum_to_strbuilder_class_def(CodeEnum self, StrBuilder* result ); +GEN_API void enum_to_strbuilder_class_fwd(CodeEnum self, StrBuilder* result ); + + StrBuilder exec_to_strbuilder (CodeExec exec); + void exec_to_strbuilder_ref(CodeExec exec, StrBuilder* result); + + void extern_to_strbuilder(CodeExtern self, StrBuilder* result); + + StrBuilder include_to_strbuilder (CodeInclude self); + void include_to_strbuilder_ref(CodeInclude self, StrBuilder* result); + + StrBuilder friend_to_strbuilder (CodeFriend self); + void friend_to_strbuilder_ref(CodeFriend self, StrBuilder* result); + +GEN_API StrBuilder fn_to_strbuilder (CodeFn self); +GEN_API void fn_to_strbuilder_def(CodeFn self, StrBuilder* result); +GEN_API void fn_to_strbuilder_fwd(CodeFn self, StrBuilder* result); + + StrBuilder module_to_strbuilder (CodeModule self); +GEN_API void module_to_strbuilder_ref(CodeModule self, StrBuilder* result); + + StrBuilder namespace_to_strbuilder (CodeNS self); + void namespace_to_strbuilder_ref(CodeNS self, StrBuilder* result); + +GEN_API StrBuilder code_op_to_strbuilder (CodeOperator self); +GEN_API void code_op_to_strbuilder_fwd(CodeOperator self, StrBuilder* result ); +GEN_API void code_op_to_strbuilder_def(CodeOperator self, StrBuilder* result ); + +GEN_API StrBuilder opcast_to_strbuilder (CodeOpCast op_cast ); +GEN_API void opcast_to_strbuilder_def(CodeOpCast op_cast, StrBuilder* result ); +GEN_API void opcast_to_strbuilder_fwd(CodeOpCast op_cast, StrBuilder* result ); + + StrBuilder pragma_to_strbuilder (CodePragma self); + void pragma_to_strbuilder_ref(CodePragma self, StrBuilder* result); + +GEN_API StrBuilder preprocess_to_strbuilder (CodePreprocessCond cond); + void preprocess_to_strbuilder_if (CodePreprocessCond cond, StrBuilder* result ); + void preprocess_to_strbuilder_ifdef (CodePreprocessCond cond, StrBuilder* result ); + void preprocess_to_strbuilder_ifndef(CodePreprocessCond cond, StrBuilder* result ); + void preprocess_to_strbuilder_elif (CodePreprocessCond cond, StrBuilder* result ); + void preprocess_to_strbuilder_else (CodePreprocessCond cond, StrBuilder* result ); + void preprocess_to_strbuilder_endif (CodePreprocessCond cond, StrBuilder* result ); + + StrBuilder template_to_strbuilder (CodeTemplate self); +GEN_API void template_to_strbuilder_ref(CodeTemplate self, StrBuilder* result); + + StrBuilder typedef_to_strbuilder (CodeTypedef self); +GEN_API void typedef_to_strbuilder_ref(CodeTypedef self, StrBuilder* result ); + + StrBuilder typename_to_strbuilder (CodeTypename self); +GEN_API void typename_to_strbuilder_ref(CodeTypename self, StrBuilder* result); + +GEN_API StrBuilder union_to_strbuilder (CodeUnion self); +GEN_API void union_to_strbuilder_def(CodeUnion self, StrBuilder* result); +GEN_API void union_to_strbuilder_fwd(CodeUnion self, StrBuilder* result); + + StrBuilder using_to_strbuilder (CodeUsing op_cast ); +GEN_API void using_to_strbuilder_ref(CodeUsing op_cast, StrBuilder* result ); + void using_to_strbuilder_ns (CodeUsing op_cast, StrBuilder* result ); + + StrBuilder var_to_strbuilder (CodeVar self); +GEN_API void var_to_strbuilder_ref(CodeVar self, StrBuilder* result); + +// TODO(Ed): Move C-Interface inlines here... + +#pragma endregion Code Type C-Interface + +#if GEN_COMPILER_CPP +#pragma region Code Types C++ + +// These structs are not used at all by the C vairant. +static_assert( GEN_COMPILER_CPP, "This should not be compiled with the C-library" ); + +#define Verify_POD(Type) static_assert(size_of(Code##Type) == size_of(AST_##Type), "ERROR: Code##Type is not a POD") + +struct CodeBody +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeBody ); + forceinline void append( Code other ) { return body_append( *this, other ); } + forceinline void append( CodeBody body ) { return body_append(*this, body); } + forceinline bool has_entries() { return code_has_entries(* this); } + forceinline StrBuilder to_strbuilder() { return body_to_strbuilder(* this); } + forceinline void to_strbuilder( StrBuilder& result ) { return body_to_strbuilder_ref(* this, & result ); } + forceinline void to_strbuilder_export( StrBuilder& result ) { return body_to_strbuilder_export(* this, & result); } + +#endif + forceinline Code begin() { return begin_CodeBody(* this); } + forceinline Code end() { return end_CodeBody(* this); } + Using_CodeOps( CodeBody ); + forceinline operator Code() { return * rcast( Code*, this ); } + forceinline AST_Body* operator->() { return ast; } + AST_Body* ast; +}; + +struct CodeClass +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeClass ); + forceinline void add_interface( CodeType interface ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder_def( StrBuilder& result ); + forceinline void to_strbuilder_fwd( StrBuilder& result ); +#endif + Using_CodeOps( CodeClass ); + forceinline operator Code() { return * rcast( Code*, this ); } + forceinline AST_Class* operator->() { + GEN_ASSERT(ast); + return ast; + } + AST_Class* ast; +}; + +struct CodeParams +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeParams ); + forceinline void append( CodeParams other ) { return params_append(* this, other); } + forceinline CodeParams get( s32 idx ) { return params_get( * this, idx); } + forceinline bool has_entries() { return params_has_entries(* this); } + forceinline StrBuilder to_strbuilder() { return params_to_strbuilder(* this); } + forceinline void to_strbuilder( StrBuilder& result ) { return params_to_strbuilder_ref(*this, & result); } +#endif + Using_CodeOps( CodeParams ); + forceinline CodeParams begin() { return begin_CodeParams(* this); } + forceinline CodeParams end() { return end_CodeParams(* this); } + forceinline operator Code() { return { (AST*)ast }; } + forceinline CodeParams operator *() { return * this; } // Required to support for-range iteration. + forceinline AST_Params* operator->() { + GEN_ASSERT(ast); + return ast; + } + CodeParams& operator++(); + AST_Params* ast; +}; + +struct CodeDefineParams +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeDefineParams ); + forceinline void append( CodeDefineParams other ) { return params_append( cast(CodeParams, * this), cast(CodeParams, other)); } + forceinline CodeDefineParams get( s32 idx ) { return (CodeDefineParams) (Code) params_get( cast(CodeParams, * this), idx); } + forceinline bool has_entries() { return params_has_entries( cast(CodeParams, * this)); } + forceinline StrBuilder to_strbuilder() { return define_params_to_strbuilder(* this); } + forceinline void to_strbuilder( StrBuilder& result ) { return define_params_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps( CodeDefineParams ); + forceinline CodeDefineParams begin() { return (CodeDefineParams) (Code) begin_CodeParams( cast(CodeParams, * this)); } + forceinline CodeDefineParams end() { return (CodeDefineParams) (Code) end_CodeParams( cast(CodeParams, * this)); } + forceinline operator Code() { return { (AST*)ast }; } + forceinline CodeDefineParams operator *() { return * this; } // Required to support for-range iteration. + forceinline AST_DefineParams* operator->() { + GEN_ASSERT(ast); + return ast; + } + forceinline CodeDefineParams& operator++(); + AST_DefineParams* ast; +}; + +struct CodeSpecifiers +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeSpecifiers ); + bool append( Specifier spec ) { return specifiers_append(* this, spec); } + bool has( Specifier spec ) { return specifiers_has(* this, spec); } + s32 index_of( Specifier spec ) { return specifiers_index_of(* this, spec); } + s32 remove( Specifier to_remove ) { return specifiers_remove(* this, to_remove); } + StrBuilder to_strbuilder() { return specifiers_to_strbuilder(* this ); } + void to_strbuilder( StrBuilder& result ) { return specifiers_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps(CodeSpecifiers); + forceinline operator Code() { return { (AST*) ast }; } + forceinline Code operator *() { return * this; } // Required to support for-range iteration. + forceinline AST_Specifiers* operator->() { + GEN_ASSERT(ast); + return ast; + } + AST_Specifiers* ast; +}; + +struct CodeAttributes +{ +#if ! GEN_C_LIKE_CPP + Using_Code(CodeAttributes); + forceinline StrBuilder to_strbuilder() { return attributes_to_strbuilder(* this); } + forceinline void to_strbuilder(StrBuilder& result) { return attributes_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps(CodeAttributes); + operator Code(); + AST_Attributes *operator->(); + AST_Attributes *ast; +}; + +// Define_CodeType( BaseClass ); + +struct CodeComment +{ +#if ! GEN_C_LIKE_CPP + Using_Code(CodeComment); + forceinline StrBuilder to_strbuilder() { return comment_to_strbuilder (* this); } + forceinline void to_strbuilder(StrBuilder& result) { return comment_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps(CodeComment); + operator Code(); + AST_Comment *operator->(); + AST_Comment *ast; +}; + +struct CodeConstructor +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeConstructor ); + forceinline StrBuilder to_strbuilder() { return constructor_to_strbuilder(* this); } + forceinline void to_strbuilder_def( StrBuilder& result ) { return constructor_to_strbuilder_def(* this, & result); } + forceinline void to_strbuilder_fwd( StrBuilder& result ) { return constructor_to_strbuilder_fwd(* this, & result); } +#endif + Using_CodeOps(CodeConstructor); + operator Code(); + AST_Constructor* operator->(); + AST_Constructor* ast; +}; + +struct CodeDefine +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeDefine ); + forceinline StrBuilder to_strbuilder() { return define_to_strbuilder(* this); } + forceinline void to_strbuilder( StrBuilder& result ) { return define_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps(CodeDefine); + operator Code(); + AST_Define* operator->(); + AST_Define* ast; +}; + +struct CodeDestructor +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeDestructor ); + forceinline StrBuilder to_strbuilder() { return destructor_to_strbuilder(* this); } + forceinline void to_strbuilder_def( StrBuilder& result ) { return destructor_to_strbuilder_def(* this, & result); } + forceinline void to_strbuilder_fwd( StrBuilder& result ) { return destructor_to_strbuilder_fwd(* this, & result); } +#endif + Using_CodeOps(CodeDestructor); + operator Code(); + AST_Destructor* operator->(); + AST_Destructor* ast; +}; + +struct CodeEnum +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeEnum ); + forceinline StrBuilder to_strbuilder() { return enum_to_strbuilder(* this); } + forceinline void to_strbuilder_def( StrBuilder& result ) { return enum_to_strbuilder_def(* this, & result); } + forceinline void to_strbuilder_fwd( StrBuilder& result ) { return enum_to_strbuilder_fwd(* this, & result); } + forceinline void to_strbuilder_class_def( StrBuilder& result ) { return enum_to_strbuilder_class_def(* this, & result); } + forceinline void to_strbuilder_class_fwd( StrBuilder& result ) { return enum_to_strbuilder_class_fwd(* this, & result); } +#endif + Using_CodeOps(CodeEnum); + operator Code(); + AST_Enum* operator->(); + AST_Enum* ast; +}; + +struct CodeExec +{ +#if ! GEN_C_LIKE_CPP + Using_Code(CodeExec); + forceinline StrBuilder to_strbuilder() { return exec_to_strbuilder(* this); } + forceinline void to_strbuilder(StrBuilder& result) { return exec_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps(CodeExec); + operator Code(); + AST_Exec *operator->(); + AST_Exec *ast; +}; + +#ifdef GEN_EXECUTION_EXPRESSION_SUPPORT +struct CodeExpr +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr* operator->(); + AST_Expr* ast; +}; + +struct CodeExpr_Assign +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_Assign ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_Assign* operator->(); + AST_Expr_Assign* ast; +}; + +struct CodeExpr_Alignof +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_Alignof ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_Alignof* operator->(); + AST_Expr_Alignof* ast; +}; + +struct CodeExpr_Binary +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_Binary ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_Binary* operator->(); + AST_Expr_Binary* ast; +}; + +struct CodeExpr_CStyleCast +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_CStyleCast ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_CStyleCast* operator->(); + AST_Expr_CStyleCast* ast; +}; + +struct CodeExpr_FunctionalCast +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_FunctionalCast ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_FunctionalCast* operator->(); + AST_Expr_FunctionalCast* ast; +}; + +struct CodeExpr_CppCast +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_CppCast ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_CppCast* operator->(); + AST_Expr_CppCast* ast; +}; + +struct CodeExpr_Element +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_Element ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_Element* operator->(); + AST_Expr_Element* ast; +}; + +struct CodeExpr_ProcCall +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_ProcCall ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_ProcCall* operator->(); + AST_Expr_ProcCall* ast; +}; + +struct CodeExpr_Decltype +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_Decltype ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_Decltype* operator->(); + AST_Expr_Decltype* ast; +}; + +struct CodeExpr_Comma +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_Comma ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_Comma* operator->(); + AST_Expr_Comma* ast; +}; + +struct CodeExpr_AMS +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_AMS ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_AMS* operator->(); + AST_Expr_AMS* ast; +}; + +struct CodeExpr_Sizeof +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_Sizeof ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_Sizeof* operator->(); + AST_Expr_Sizeof* ast; +}; + +struct CodeExpr_Subscript +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_Subscript ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_Subscript* operator->(); + AST_Expr_Subscript* ast; +}; + +struct CodeExpr_Ternary +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_Ternary ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_Ternary* operator->(); + AST_Expr_Ternary* ast; +}; + +struct CodeExpr_UnaryPrefix +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_UnaryPrefix ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Expr_UnaryPrefix* operator->(); + AST_Expr_UnaryPrefix* ast; +}; + +struct CodeExpr_UnaryPostfix +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExpr_UnaryPostfix ); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + AST* raw(); + operator Code(); + AST_Expr_UnaryPostfix* operator->(); + AST_Expr_UnaryPostfix* ast; +}; +#endif + +struct CodeExtern +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeExtern ); + forceinline void to_strbuilder( StrBuilder& result ) { return extern_to_strbuilder(* this, & result); } +#endif + Using_CodeOps(CodeExtern); + operator Code(); + AST_Extern* operator->(); + AST_Extern* ast; +}; + +struct CodeInclude +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeInclude ); + forceinline StrBuilder to_strbuilder() { return include_to_strbuilder(* this); } + forceinline void to_strbuilder( StrBuilder& result ) { return include_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps(CodeInclude); + operator Code(); + AST_Include* operator->(); + AST_Include* ast; +}; + +struct CodeFriend +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeFriend ); + forceinline StrBuilder to_strbuilder() { return friend_to_strbuilder(* this); } + forceinline void to_strbuilder( StrBuilder& result ) { return friend_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps(CodeFriend); + operator Code(); + AST_Friend* operator->(); + AST_Friend* ast; +}; + +struct CodeFn +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeFn ); + forceinline StrBuilder to_strbuilder() { return fn_to_strbuilder(* this); } + forceinline void to_strbuilder_def( StrBuilder& result ) { return fn_to_strbuilder_def(* this, & result); } + forceinline void to_strbuilder_fwd( StrBuilder& result ) { return fn_to_strbuilder_fwd(* this, & result); } +#endif + Using_CodeOps(CodeFn); + operator Code(); + AST_Fn* operator->(); + AST_Fn* ast; +}; + +struct CodeModule +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeModule ); + forceinline StrBuilder to_strbuilder() { return module_to_strbuilder(* this); } + forceinline void to_strbuilder( StrBuilder& result ) { return module_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps(CodeModule); + operator Code(); + AST_Module* operator->(); + AST_Module* ast; +}; + +struct CodeNS +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeNS ); + forceinline StrBuilder to_strbuilder() { return namespace_to_strbuilder(* this); } + forceinline void to_strbuilder( StrBuilder& result ) { return namespace_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps(CodeNS); + operator Code(); + AST_NS* operator->(); + AST_NS* ast; +}; + +struct CodeOperator +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeOperator ); + forceinline StrBuilder to_strbuilder() { return code_op_to_strbuilder(* this); } + forceinline void to_strbuilder_def( StrBuilder& result ) { return code_op_to_strbuilder_def(* this, & result); } + forceinline void to_strbuilder_fwd( StrBuilder& result ) { return code_op_to_strbuilder_fwd(* this, & result); } +#endif + Using_CodeOps(CodeOperator); + operator Code(); + AST_Operator* operator->(); + AST_Operator* ast; +}; + +struct CodeOpCast +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeOpCast ); + forceinline StrBuilder to_strbuilder() { return opcast_to_strbuilder(* this); } + forceinline void to_strbuilder_def( StrBuilder& result ) { return opcast_to_strbuilder_def(* this, & result); } + forceinline void to_strbuilder_fwd( StrBuilder& result ) { return opcast_to_strbuilder_fwd(* this, & result); } +#endif + Using_CodeOps(CodeOpCast); + operator Code(); + AST_OpCast* operator->(); + AST_OpCast* ast; +}; + +struct CodePragma +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodePragma ); + forceinline StrBuilder to_strbuilder() { return pragma_to_strbuilder(* this); } + forceinline void to_strbuilder( StrBuilder& result ) { return pragma_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps( CodePragma ); + operator Code(); + AST_Pragma* operator->(); + AST_Pragma* ast; +}; + +struct CodePreprocessCond +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodePreprocessCond ); + forceinline StrBuilder to_strbuilder() { return preprocess_to_strbuilder(* this); } + forceinline void to_strbuilder_if( StrBuilder& result ) { return preprocess_to_strbuilder_if(* this, & result); } + forceinline void to_strbuilder_ifdef( StrBuilder& result ) { return preprocess_to_strbuilder_ifdef(* this, & result); } + forceinline void to_strbuilder_ifndef( StrBuilder& result ) { return preprocess_to_strbuilder_ifndef(* this, & result); } + forceinline void to_strbuilder_elif( StrBuilder& result ) { return preprocess_to_strbuilder_elif(* this, & result); } + forceinline void to_strbuilder_else( StrBuilder& result ) { return preprocess_to_strbuilder_else(* this, & result); } + forceinline void to_strbuilder_endif( StrBuilder& result ) { return preprocess_to_strbuilder_endif(* this, & result); } +#endif + Using_CodeOps( CodePreprocessCond ); + operator Code(); + AST_PreprocessCond* operator->(); + AST_PreprocessCond* ast; +}; + +#ifdef GEN_EXECUTION_EXPRESSION_SUPPORT +struct CodeStmt +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStmt ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Stmt* operator->(); + AST_Stmt* ast; +}; + +struct CodeStmt_Break +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStmt_Break ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Stmt_Break* operator->(); + AST_Stmt_Break* ast; +}; + +struct CodeStmt_Case +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStmt_Case ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Stmt_Case* operator->(); + AST_Stmt_Case* ast; +}; + +struct CodeStmt_Continue +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStmt_Continue ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Stmt_Continue* operator->(); + AST_Stmt_Continue* ast; +}; + +struct CodeStmt_Decl +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStmt_Decl ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Stmt_Decl* operator->(); + AST_Stmt_Decl* ast; +}; + +struct CodeStmt_Do +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStmt_Do ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Stmt_Do* operator->(); + AST_Stmt_Do* ast; +}; + +struct CodeStmt_Expr +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStmt_Expr ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Stmt_Expr* operator->(); + AST_Stmt_Expr* ast; +}; + +struct CodeStmt_Else +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStmt_Else ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Stmt_Else* operator->(); + AST_Stmt_Else* ast; +}; + +struct CodeStmt_If +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStmt_If ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Stmt_If* operator->(); + AST_Stmt_If* ast; +}; + +struct CodeStmt_For +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStmt_For ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Stmt_For* operator->(); + AST_Stmt_For* ast; +}; + +struct CodeStmt_Goto +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStmt_Goto ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Stmt_Goto* operator->(); + AST_Stmt_Goto* ast; +}; + +struct CodeStmt_Label +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStmt_Label ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Stmt_Label* operator->(); + AST_Stmt_Label* ast; +}; + +struct CodeStmt_Switch +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStmt_Switch ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Stmt_Switch* operator->(); + AST_Stmt_Switch* ast; +}; + +struct CodeStmt_While +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStmt_While ); + forceinline StrBuilder to_strbuilder(); + forceinline void to_strbuilder( StrBuilder& result ); +#endif + operator Code(); + AST_Stmt_While* operator->(); + AST_Stmt_While* ast; +}; +#endif + +struct CodeTemplate +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeTemplate ); + forceinline StrBuilder to_strbuilder() { return template_to_strbuilder(* this); } + forceinline void to_strbuilder( StrBuilder& result ) { return template_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps( CodeTemplate ); + operator Code(); + AST_Template* operator->(); + AST_Template* ast; +}; + +struct CodeTypename +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeTypename ); + forceinline StrBuilder to_strbuilder() { return typename_to_strbuilder(* this); } + forceinline void to_strbuilder( StrBuilder& result ) { return typename_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps( CodeTypename ); + operator Code(); + AST_Typename* operator->(); + AST_Typename* ast; +}; + +struct CodeTypedef +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeTypedef ); + forceinline StrBuilder to_strbuilder() { return typedef_to_strbuilder(* this); } + forceinline void to_strbuilder( StrBuilder& result ) { return typedef_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps( CodeTypedef ); + operator Code(); + AST_Typedef* operator->(); + AST_Typedef* ast; +}; + +struct CodeUnion +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeUnion ); + forceinline StrBuilder to_strbuilder() { return union_to_strbuilder(* this); } + forceinline void to_strbuilder_def( StrBuilder& result ) { return union_to_strbuilder_def(* this, & result); } + forceinline void to_strbuilder_fwd( StrBuilder& result ) { return union_to_strbuilder_fwd(* this, & result); } +#endif + Using_CodeOps(CodeUnion); + operator Code(); + AST_Union* operator->(); + AST_Union* ast; +}; + +struct CodeUsing +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeUsing ); + forceinline StrBuilder to_strbuilder() { return using_to_strbuilder(* this); } + forceinline void to_strbuilder( StrBuilder& result ) { return using_to_strbuilder_ref(* this, & result); } + forceinline void to_strbuilder_ns( StrBuilder& result ) { return using_to_strbuilder_ns(* this, & result); } +#endif + Using_CodeOps(CodeUsing); + operator Code(); + AST_Using* operator->(); + AST_Using* ast; +}; + +struct CodeVar +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeVar ); + forceinline StrBuilder to_strbuilder() { return var_to_strbuilder(* this); } + forceinline void to_strbuilder( StrBuilder& result ) { return var_to_strbuilder_ref(* this, & result); } +#endif + Using_CodeOps(CodeVar); + operator Code(); + AST_Var* operator->(); + AST_Var* ast; +}; + +struct CodeStruct +{ +#if ! GEN_C_LIKE_CPP + Using_Code( CodeStruct ); + forceinline void add_interface( CodeTypename interface ) { return struct_add_interface(* this, interface); } + forceinline StrBuilder to_strbuilder() { return struct_to_strbuilder(* this); } + forceinline void to_strbuilder_fwd( StrBuilder& result ) { return struct_to_strbuilder_fwd(* this, & result); } + forceinline void to_strbuilder_def( StrBuilder& result ) { return struct_to_strbuilder_def(* this, & result); } +#endif + Using_CodeOps( CodeStruct ); + forceinline operator Code() { return * rcast( Code*, this ); } + forceinline AST_Struct* operator->() { + GEN_ASSERT(ast); + return ast; + } + AST_Struct* ast; +}; + +#undef Define_CodeType +#undef Using_Code +#undef Using_CodeOps + +#undef Verify_POD + +struct InvalidCode_ImplictCaster +{ + // operator CodeBaseClass() const; + operator Code () const { return Code_Invalid; } + operator CodeBody () const { return cast(CodeBody, Code_Invalid); } + operator CodeAttributes () const { return cast(CodeAttributes, Code_Invalid); } + operator CodeComment () const { return cast(CodeComment, Code_Invalid); } + operator CodeClass () const { return cast(CodeClass, Code_Invalid); } + operator CodeConstructor () const { return cast(CodeConstructor, Code_Invalid); } + operator CodeDefine () const { return cast(CodeDefine, Code_Invalid); } + operator CodeDefineParams () const { return cast(CodeDefineParams, Code_Invalid); } + operator CodeDestructor () const { return cast(CodeDestructor, Code_Invalid); } + operator CodeExec () const { return cast(CodeExec, Code_Invalid); } + operator CodeEnum () const { return cast(CodeEnum, Code_Invalid); } + operator CodeExtern () const { return cast(CodeExtern, Code_Invalid); } + operator CodeInclude () const { return cast(CodeInclude, Code_Invalid); } + operator CodeFriend () const { return cast(CodeFriend, Code_Invalid); } + operator CodeFn () const { return cast(CodeFn, Code_Invalid); } + operator CodeModule () const { return cast(CodeModule, Code_Invalid); } + operator CodeNS () const { return cast(CodeNS, Code_Invalid); } + operator CodeOperator () const { return cast(CodeOperator, Code_Invalid); } + operator CodeOpCast () const { return cast(CodeOpCast, Code_Invalid); } + operator CodeParams () const { return cast(CodeParams, Code_Invalid); } + operator CodePragma () const { return cast(CodePragma, Code_Invalid); } + operator CodePreprocessCond() const { return cast(CodePreprocessCond, Code_Invalid); } + operator CodeSpecifiers () const { return cast(CodeSpecifiers, Code_Invalid); } + operator CodeStruct () const { return cast(CodeStruct, Code_Invalid); } + operator CodeTemplate () const { return cast(CodeTemplate, Code_Invalid); } + operator CodeTypename () const { return cast(CodeTypename, Code_Invalid); } + operator CodeTypedef () const { return cast(CodeTypedef, Code_Invalid); } + operator CodeUnion () const { return cast(CodeUnion, Code_Invalid); } + operator CodeUsing () const { return cast(CodeUsing, Code_Invalid); } + operator CodeVar () const { return cast(CodeVar, Code_Invalid); } +}; + +struct NullCode_ImplicitCaster +{ + operator Code () const { return {nullptr}; } + operator CodeBody () const { return {(AST_Body*) nullptr}; } + operator CodeAttributes () const { return {(AST_Attributes*)nullptr}; } + operator CodeComment () const { return {nullptr}; } + operator CodeClass () const { return {nullptr}; } + operator CodeConstructor () const { return {nullptr}; } + operator CodeDefine () const { return {nullptr}; } + operator CodeDefineParams () const { return {nullptr}; } + operator CodeDestructor () const { return {nullptr}; } + operator CodeExec () const { return {nullptr}; } + operator CodeEnum () const { return {nullptr}; } + operator CodeExtern () const { return {nullptr}; } + operator CodeInclude () const { return {nullptr}; } + operator CodeFriend () const { return {nullptr}; } + operator CodeFn () const { return {nullptr}; } + operator CodeModule () const { return {nullptr}; } + operator CodeNS () const { return {nullptr}; } + operator CodeOperator () const { return {nullptr}; } + operator CodeOpCast () const { return {nullptr}; } + operator CodeParams () const { return {nullptr}; } + operator CodePragma () const { return {nullptr}; } + operator CodePreprocessCond() const { return {nullptr}; } + operator CodeSpecifiers () const { return {nullptr}; } + operator CodeStruct () const { return {nullptr}; } + operator CodeTemplate () const { return {nullptr}; } + operator CodeTypename () const { return CodeTypename{(AST_Typename*)nullptr}; } + operator CodeTypedef () const { return {nullptr}; } + operator CodeUnion () const { return {nullptr}; } + operator CodeUsing () const { return {nullptr}; } + operator CodeVar () const { return {nullptr}; } +}; + +forceinline Code begin( CodeBody body) { return begin_CodeBody(body); } +forceinline Code end ( CodeBody body ) { return end_CodeBody(body); } +forceinline Code next ( CodeBody body, Code entry_iter ) { return next_CodeBody(body, entry_iter); } + +forceinline CodeParams begin(CodeParams params) { return begin_CodeParams(params); } +forceinline CodeParams end (CodeParams params) { return end_CodeParams(params); } +forceinline CodeParams next (CodeParams params, CodeParams entry_iter) { return next_CodeParams(params, entry_iter); } + +forceinline Specifier* begin(CodeSpecifiers specifiers) { return begin_CodeSpecifiers(specifiers); } +forceinline Specifier* end (CodeSpecifiers specifiers) { return end_CodeSpecifiers(specifiers); } +forceinline Specifier* next (CodeSpecifiers specifiers, Specifier& spec_iter) { return next_CodeSpecifiers(specifiers, & spec_iter); } + +#if ! GEN_C_LIKE_CPP +GEN_OPTIMIZE_MAPPINGS_BEGIN + +forceinline void append ( CodeBody body, Code other ) { return body_append(body, other); } +forceinline void append ( CodeBody body, CodeBody other ) { return body_append_body(body, other); } +forceinline StrBuilder to_strbuilder ( CodeBody body ) { return body_to_strbuilder(body); } +forceinline void to_strbuilder ( CodeBody body, StrBuilder& result ) { return body_to_strbuilder_ref(body, & result); } +forceinline void to_strbuilder_export( CodeBody body, StrBuilder& result ) { return body_to_strbuilder_export(body, & result); } + +forceinline void add_interface ( CodeClass self, CodeTypename interface ) { return class_add_interface(self, interface); } +forceinline StrBuilder to_strbuilder ( CodeClass self ) { return class_to_strbuilder(self); } +forceinline void to_strbuilder_def( CodeClass self, StrBuilder& result ) { return class_to_strbuilder_def(self, & result); } +forceinline void to_strbuilder_fwd( CodeClass self, StrBuilder& result ) { return class_to_strbuilder_fwd(self, & result); } + +forceinline void append (CodeDefineParams appendee, CodeDefineParams other ) { params_append(cast(CodeParams, appendee), cast(CodeParams, other)); } +forceinline CodeDefineParams get (CodeDefineParams params, s32 idx) { return (CodeDefineParams) (Code) params_get(cast(CodeParams, params), idx); } +forceinline bool has_entries (CodeDefineParams params ) { return params_has_entries(cast(CodeParams, params)); } +forceinline StrBuilder to_strbuilder(CodeDefineParams params ) { return define_params_to_strbuilder(params); } +forceinline void to_strbuilder(CodeDefineParams params, StrBuilder& result ) { return define_params_to_strbuilder_ref(params, & result); } + +forceinline void append (CodeParams appendee, CodeParams other ) { return params_append(appendee, other); } +forceinline CodeParams get (CodeParams params, s32 idx) { return params_get(params, idx); } +forceinline bool has_entries (CodeParams params ) { return params_has_entries(params); } +forceinline StrBuilder to_strbuilder(CodeParams params ) { return params_to_strbuilder(params); } +forceinline void to_strbuilder(CodeParams params, StrBuilder& result ) { return params_to_strbuilder_ref(params, & result); } + +forceinline bool append (CodeSpecifiers specifiers, Specifier spec) { return specifiers_append(specifiers, spec); } +forceinline bool has (CodeSpecifiers specifiers, Specifier spec) { return specifiers_has(specifiers, spec); } +forceinline s32 index_of (CodeSpecifiers specifiers, Specifier spec) { return specifiers_index_of(specifiers, spec); } +forceinline s32 remove (CodeSpecifiers specifiers, Specifier to_remove ) { return specifiers_remove(specifiers, to_remove); } +forceinline StrBuilder to_strbuilder (CodeSpecifiers specifiers) { return specifiers_to_strbuilder(specifiers); } +forceinline void to_strbuilder (CodeSpecifiers specifiers, StrBuilder& result) { return specifiers_to_strbuilder_ref(specifiers, & result); } + +forceinline void add_interface (CodeStruct self, CodeTypename interface) { return struct_add_interface(self, interface); } +forceinline StrBuilder to_strbuilder (CodeStruct self) { return struct_to_strbuilder(self); } +forceinline void to_strbuilder_fwd(CodeStruct self, StrBuilder& result) { return struct_to_strbuilder_fwd(self, & result); } +forceinline void to_strbuilder_def(CodeStruct self, StrBuilder& result) { return struct_to_strbuilder_def(self, & result); } + +forceinline StrBuilder to_strbuilder(CodeAttributes attributes) { return attributes_to_strbuilder(attributes); } +forceinline void to_strbuilder(CodeAttributes attributes, StrBuilder& result) { return attributes_to_strbuilder_ref(attributes, & result); } + +forceinline StrBuilder to_strbuilder(CodeComment comment ) { return comment_to_strbuilder(comment); } +forceinline void to_strbuilder(CodeComment comment, StrBuilder& result ) { return comment_to_strbuilder_ref(comment, & result); } + +forceinline StrBuilder to_strbuilder (CodeConstructor constructor) { return constructor_to_strbuilder(constructor); } +forceinline void to_strbuilder_def(CodeConstructor constructor, StrBuilder& result ) { return constructor_to_strbuilder_def(constructor, & result); } +forceinline void to_strbuilder_fwd(CodeConstructor constructor, StrBuilder& result ) { return constructor_to_strbuilder_fwd(constructor, & result); } + +forceinline StrBuilder to_strbuilder(CodeDefine self) { return define_to_strbuilder(self); } +forceinline void to_strbuilder(CodeDefine self, StrBuilder& result) { return define_to_strbuilder_ref(self, & result); } + +forceinline StrBuilder to_strbuilder (CodeDestructor destructor) { return destructor_to_strbuilder(destructor); } +forceinline void to_strbuilder_def(CodeDestructor destructor, StrBuilder& result ) { return destructor_to_strbuilder_def(destructor, & result); } +forceinline void to_strbuilder_fwd(CodeDestructor destructor, StrBuilder& result ) { return destructor_to_strbuilder_fwd(destructor, & result); } + +forceinline StrBuilder to_strbuilder (CodeEnum self) { return enum_to_strbuilder(self); } +forceinline void to_strbuilder_def (CodeEnum self, StrBuilder& result ) { return enum_to_strbuilder_def(self, & result); } +forceinline void to_strbuilder_fwd (CodeEnum self, StrBuilder& result ) { return enum_to_strbuilder_fwd(self, & result); } +forceinline void to_strbuilder_class_def(CodeEnum self, StrBuilder& result ) { return enum_to_strbuilder_class_def(self, & result); } +forceinline void to_strbuilder_class_fwd(CodeEnum self, StrBuilder& result ) { return enum_to_strbuilder_class_fwd(self, & result); } + +forceinline StrBuilder to_strbuilder(CodeExec exec) { return exec_to_strbuilder(exec); } +forceinline void to_strbuilder(CodeExec exec, StrBuilder& result) { return exec_to_strbuilder_ref(exec, & result); } + +forceinline void to_strbuilder(CodeExtern self, StrBuilder& result) { return extern_to_strbuilder(self, & result); } + +forceinline StrBuilder to_strbuilder(CodeInclude self) { return include_to_strbuilder(self); } +forceinline void to_strbuilder(CodeInclude self, StrBuilder& result) { return include_to_strbuilder_ref(self, & result); } + +forceinline StrBuilder to_strbuilder(CodeFriend self) { return friend_to_strbuilder(self); } +forceinline void to_strbuilder(CodeFriend self, StrBuilder& result) { return friend_to_strbuilder_ref(self, & result); } + +forceinline StrBuilder to_strbuilder (CodeFn self) { return fn_to_strbuilder(self); } +forceinline void to_strbuilder_def(CodeFn self, StrBuilder& result) { return fn_to_strbuilder_def(self, & result); } +forceinline void to_strbuilder_fwd(CodeFn self, StrBuilder& result) { return fn_to_strbuilder_fwd(self, & result); } + +forceinline StrBuilder to_strbuilder(CodeModule self) { return module_to_strbuilder(self); } +forceinline void to_strbuilder(CodeModule self, StrBuilder& result) { return module_to_strbuilder_ref(self, & result); } + +forceinline StrBuilder to_strbuilder(CodeNS self) { return namespace_to_strbuilder(self); } +forceinline void to_strbuilder(CodeNS self, StrBuilder& result) { return namespace_to_strbuilder_ref(self, & result); } + +forceinline StrBuilder to_strbuilder (CodeOperator self) { return code_op_to_strbuilder(self); } +forceinline void to_strbuilder_fwd(CodeOperator self, StrBuilder& result ) { return code_op_to_strbuilder_fwd(self, & result); } +forceinline void to_strbuilder_def(CodeOperator self, StrBuilder& result ) { return code_op_to_strbuilder_def(self, & result); } + +forceinline StrBuilder to_strbuilder (CodeOpCast op_cast ) { return opcast_to_strbuilder(op_cast); } +forceinline void to_strbuilder_def(CodeOpCast op_cast, StrBuilder& result ) { return opcast_to_strbuilder_def(op_cast, & result); } +forceinline void to_strbuilder_fwd(CodeOpCast op_cast, StrBuilder& result ) { return opcast_to_strbuilder_fwd(op_cast, & result); } + +forceinline StrBuilder to_strbuilder(CodePragma self) { return pragma_to_strbuilder(self); } +forceinline void to_strbuilder(CodePragma self, StrBuilder& result) { return pragma_to_strbuilder_ref(self, & result); } + +forceinline StrBuilder to_strbuilder (CodePreprocessCond cond) { return preprocess_to_strbuilder(cond); } +forceinline void to_strbuilder_if (CodePreprocessCond cond, StrBuilder& result ) { return preprocess_to_strbuilder_if(cond, & result); } +forceinline void to_strbuilder_ifdef (CodePreprocessCond cond, StrBuilder& result ) { return preprocess_to_strbuilder_ifdef(cond, & result); } +forceinline void to_strbuilder_ifndef(CodePreprocessCond cond, StrBuilder& result ) { return preprocess_to_strbuilder_ifndef(cond, & result); } +forceinline void to_strbuilder_elif (CodePreprocessCond cond, StrBuilder& result ) { return preprocess_to_strbuilder_elif(cond, & result); } +forceinline void to_strbuilder_else (CodePreprocessCond cond, StrBuilder& result ) { return preprocess_to_strbuilder_else(cond, & result); } +forceinline void to_strbuilder_endif (CodePreprocessCond cond, StrBuilder& result ) { return preprocess_to_strbuilder_endif(cond, & result); } + +forceinline StrBuilder to_strbuilder(CodeTemplate self) { return template_to_strbuilder(self); } +forceinline void to_strbuilder(CodeTemplate self, StrBuilder& result) { return template_to_strbuilder_ref(self, & result); } + +forceinline StrBuilder to_strbuilder(CodeTypename self) { return typename_to_strbuilder(self); } +forceinline void to_strbuilder(CodeTypename self, StrBuilder& result) { return typename_to_strbuilder_ref(self, & result); } + +forceinline StrBuilder to_strbuilder(CodeTypedef self) { return typedef_to_strbuilder(self); } +forceinline void to_strbuilder(CodeTypedef self, StrBuilder& result ) { return typedef_to_strbuilder_ref(self, & result); } + +forceinline StrBuilder to_strbuilder (CodeUnion self) { return union_to_strbuilder(self); } +forceinline void to_strbuilder_def(CodeUnion self, StrBuilder& result) { return union_to_strbuilder_def(self, & result); } +forceinline void to_strbuilder_fwd(CodeUnion self, StrBuilder& result) { return union_to_strbuilder_fwd(self, & result); } + +forceinline StrBuilder to_strbuilder (CodeUsing op_cast ) { return using_to_strbuilder(op_cast); } +forceinline void to_strbuilder (CodeUsing op_cast, StrBuilder& result ) { return using_to_strbuilder_ref(op_cast, & result); } +forceinline void to_strbuilder_ns(CodeUsing op_cast, StrBuilder& result ) { return using_to_strbuilder_ns(op_cast, & result); } + +forceinline StrBuilder to_strbuilder(CodeVar self) { return var_to_strbuilder(self); } +forceinline void to_strbuilder(CodeVar self, StrBuilder& result) { return var_to_strbuilder_ref(self, & result); } + +GEN_OPITMIZE_MAPPINGS_END +#endif //if GEN_C_LIKE_CPP + +#pragma endregion Code Types C++ +#endif //if GEN_COMPILER_CPP diff --git a/tests/assets/gencpp_samples/components/constants.hpp b/tests/assets/gencpp_samples/components/constants.hpp new file mode 100644 index 0000000..0b4cd62 --- /dev/null +++ b/tests/assets/gencpp_samples/components/constants.hpp @@ -0,0 +1,85 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "interface.hpp" +#endif + +#pragma region Constants +// Predefined typename codes. Are set to readonly and are setup during gen::init() + +GEN_API extern Macro enum_underlying_macro; + +GEN_API extern Code access_public; +GEN_API extern Code access_protected; +GEN_API extern Code access_private; + +GEN_API extern CodeAttributes attrib_api_export; +GEN_API extern CodeAttributes attrib_api_import; + +GEN_API extern Code module_global_fragment; +GEN_API extern Code module_private_fragment; + +GEN_API extern Code fmt_newline; + +GEN_API extern CodePragma pragma_once; + +GEN_API extern CodeParams param_varadic; + +GEN_API extern CodePreprocessCond preprocess_else; +GEN_API extern CodePreprocessCond preprocess_endif; + +GEN_API extern CodeSpecifiers spec_const; +GEN_API extern CodeSpecifiers spec_consteval; +GEN_API extern CodeSpecifiers spec_constexpr; +GEN_API extern CodeSpecifiers spec_constinit; +GEN_API extern CodeSpecifiers spec_extern_linkage; +GEN_API extern CodeSpecifiers spec_final; +GEN_API extern CodeSpecifiers spec_forceinline; +GEN_API extern CodeSpecifiers spec_global; +GEN_API extern CodeSpecifiers spec_inline; +GEN_API extern CodeSpecifiers spec_internal_linkage; +GEN_API extern CodeSpecifiers spec_local_persist; +GEN_API extern CodeSpecifiers spec_mutable; +GEN_API extern CodeSpecifiers spec_neverinline; +GEN_API extern CodeSpecifiers spec_noexcept; +GEN_API extern CodeSpecifiers spec_override; +GEN_API extern CodeSpecifiers spec_ptr; +GEN_API extern CodeSpecifiers spec_pure; +GEN_API extern CodeSpecifiers spec_ref; +GEN_API extern CodeSpecifiers spec_register; +GEN_API extern CodeSpecifiers spec_rvalue; +GEN_API extern CodeSpecifiers spec_static_member; +GEN_API extern CodeSpecifiers spec_thread_local; +GEN_API extern CodeSpecifiers spec_virtual; +GEN_API extern CodeSpecifiers spec_volatile; + +GEN_API extern CodeTypename t_empty; // Used with varaidc parameters. (Exposing just in case its useful for another circumstance) +GEN_API extern CodeTypename t_auto; +GEN_API extern CodeTypename t_void; +GEN_API extern CodeTypename t_int; +GEN_API extern CodeTypename t_bool; +GEN_API extern CodeTypename t_char; +GEN_API extern CodeTypename t_wchar_t; +GEN_API extern CodeTypename t_class; +GEN_API extern CodeTypename t_typename; + +#ifdef GEN_DEFINE_LIBRARY_CODE_CONSTANTS + GEN_API extern CodeTypename t_b32; + + GEN_API extern CodeTypename t_s8; + GEN_API extern CodeTypename t_s16; + GEN_API extern CodeTypename t_s32; + GEN_API extern CodeTypename t_s64; + + GEN_API extern CodeTypename t_u8; + GEN_API extern CodeTypename t_u16; + GEN_API extern CodeTypename t_u32; + GEN_API extern CodeTypename t_u64; + + GEN_API extern CodeTypename t_ssize; + GEN_API extern CodeTypename t_usize; + + GEN_API extern CodeTypename t_f32; + GEN_API extern CodeTypename t_f64; +#endif + +#pragma endregion Constants diff --git a/tests/assets/gencpp_samples/components/header_end.hpp b/tests/assets/gencpp_samples/components/header_end.hpp new file mode 100644 index 0000000..e69de29 diff --git a/tests/assets/gencpp_samples/components/header_start.hpp b/tests/assets/gencpp_samples/components/header_start.hpp new file mode 100644 index 0000000..30a2b50 --- /dev/null +++ b/tests/assets/gencpp_samples/components/header_start.hpp @@ -0,0 +1,32 @@ +#pragma once + +/* + gencpp: An attempt at "simple" staged metaprogramming for c/c++. + + See Readme.md for more information from the project repository. + + Public Address: + https://github.com/Ed94/gencpp --------------------------------------------------------------. + | _____ _____ _ _ | + | / ____) / ____} | | | | + | | / ___ ___ _ __ ___ _ __ _ __ | {___ | |__ _ _, __ _, ___ __| | | + | | |{_ |/ _ \ '_ \ / __} '_ l| '_ l `\___ \| __/ _` |/ _` |/ _ \/ _` | | + | | l__j | ___/ | | | {__; |+l } |+l | ____) | l| (_| | {_| | ___/ (_| | | + | \_____|\___}_l |_|\___} ,__/| ,__/ (_____/ \__\__/_|\__, |\___}\__,_l | + | | | | | __} | | + | l_l l_l {___/ | + ! ----------------------------------------------------------------------- VERSION: v0.25-Alpha | + ! ============================================================================================ | + ! WARNING: THIS IS AN ALPHA VERSION OF THE LIBRARY, USE AT YOUR OWN DISCRETION | + ! NEVER DO CODE GENERATION WITHOUT AT LEAST HAVING CONTENT IN A CODEBASE UNDER VERSION CONTROL | + ! ============================================================================================ / +*/ +#if ! defined(GEN_DONT_ENFORCE_GEN_TIME_GUARD) && ! defined(GEN_TIME) +# error Gen.hpp : GEN_TIME not defined +#endif + +//! If its desired to roll your own dependencies, define GEN_ROLL_OWN_DEPENDENCIES before including this file. +// Dependencies are derived from the c-zpl library: https://github.com/zpl-c/zpl +#ifndef GEN_ROLL_OWN_DEPENDENCIES +# include "gen.dep.hpp" +#endif diff --git a/tests/assets/gencpp_samples/components/inlines.hpp b/tests/assets/gencpp_samples/components/inlines.hpp new file mode 100644 index 0000000..4489516 --- /dev/null +++ b/tests/assets/gencpp_samples/components/inlines.hpp @@ -0,0 +1,753 @@ +#ifdef INTELLISENSE_DIRECTIVES +#pragma once +#include "constants.hpp" +#endif + +#pragma region Serialization +inline +StrBuilder attributes_to_strbuilder(CodeAttributes attributes) { + GEN_ASSERT(attributes); + char* raw = ccast(char*, str_duplicate( attributes->Content, get_context()->Allocator_Temp ).Ptr); + StrBuilder result = { raw }; + return result; +} + +inline +void attributes_to_strbuilder_ref(CodeAttributes attributes, StrBuilder* result) { + GEN_ASSERT(attributes); + GEN_ASSERT(result); + strbuilder_append_str(result, attributes->Content); +} + +inline +StrBuilder comment_to_strbuilder(CodeComment comment) { + GEN_ASSERT(comment); + char* raw = ccast(char*, str_duplicate( comment->Content, get_context()->Allocator_Temp ).Ptr); + StrBuilder result = { raw }; + return result; +} + +inline +void body_to_strbuilder_ref( CodeBody body, StrBuilder* result ) +{ + GEN_ASSERT(body != nullptr); + GEN_ASSERT(result != nullptr); + Code curr = body->Front; + s32 left = body->NumEntries; + while ( left -- ) + { + code_to_strbuilder_ref(curr, result); + // strbuilder_append_fmt( result, "%SB", code_to_strbuilder(curr) ); + curr = curr->Next; + } +} + +inline +void comment_to_strbuilder_ref(CodeComment comment, StrBuilder* result) { + GEN_ASSERT(comment); + GEN_ASSERT(result); + strbuilder_append_str(result, comment->Content); +} + +inline +StrBuilder define_to_strbuilder(CodeDefine define) +{ + GEN_ASSERT(define); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 512 ); + define_to_strbuilder_ref(define, & result); + return result; +} + +inline +StrBuilder define_params_to_strbuilder(CodeDefineParams params) +{ + GEN_ASSERT(params); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 128 ); + define_params_to_strbuilder_ref( params, & result ); + return result; +} + +inline +StrBuilder exec_to_strbuilder(CodeExec exec) +{ + GEN_ASSERT(exec); + char* raw = ccast(char*, str_duplicate( exec->Content, _ctx->Allocator_Temp ).Ptr); + StrBuilder result = { raw }; + return result; +} + +inline +void exec_to_strbuilder_ref(CodeExec exec, StrBuilder* result) { + GEN_ASSERT(exec); + GEN_ASSERT(result); + strbuilder_append_str(result, exec->Content); +} + +inline +void extern_to_strbuilder(CodeExtern self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( self->Body ) + strbuilder_append_fmt( result, "extern \"%S\"\n{\n%SB\n}\n", self->Name, body_to_strbuilder(self->Body) ); + else + strbuilder_append_fmt( result, "extern \"%S\"\n{}\n", self->Name ); +} + +inline +StrBuilder friend_to_strbuilder(CodeFriend self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 256 ); + friend_to_strbuilder_ref( self, & result ); + return result; +} + +inline +void friend_to_strbuilder_ref(CodeFriend self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + strbuilder_append_fmt( result, "friend %SB", code_to_strbuilder(self->Declaration) ); + + if ( self->Declaration->Type != CT_Function && self->Declaration->Type != CT_Operator && (* result)[ strbuilder_length(* result) - 1 ] != ';' ) + { + strbuilder_append_str( result, txt(";") ); + } + + if ( self->InlineCmt ) + strbuilder_append_fmt( result, " %S", self->InlineCmt->Content ); + else + strbuilder_append_str( result, txt("\n")); +} + +inline +StrBuilder include_to_strbuilder(CodeInclude include) +{ + GEN_ASSERT(include); + return strbuilder_fmt_buf( _ctx->Allocator_Temp, "#include %S\n", include->Content ); +} + +inline +void include_to_strbuilder_ref( CodeInclude include, StrBuilder* result ) +{ + GEN_ASSERT(include); + GEN_ASSERT(result); + strbuilder_append_fmt( result, "#include %S\n", include->Content ); +} + +inline +StrBuilder module_to_strbuilder(CodeModule self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 64 ); + module_to_strbuilder_ref( self, & result ); + return result; +} + +inline +StrBuilder namespace_to_strbuilder(CodeNS self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 512 ); + namespace_to_strbuilder_ref( self, & result ); + return result; +} + +inline +void namespace_to_strbuilder_ref(CodeNS self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( bitfield_is_set( u32, self->ModuleFlags, ModuleFlag_Export )) + strbuilder_append_str( result, txt("export ") ); + + strbuilder_append_fmt( result, "namespace %S\n{\n%SB\n}\n", self->Name, body_to_strbuilder(self->Body) ); +} + +inline +StrBuilder params_to_strbuilder(CodeParams self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 128 ); + params_to_strbuilder_ref( self, & result ); + return result; +} + +inline +StrBuilder pragma_to_strbuilder(CodePragma self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 256 ); + pragma_to_strbuilder_ref( self, & result ); + return result; +} + +inline +void pragma_to_strbuilder_ref(CodePragma self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + strbuilder_append_fmt( result, "#pragma %S\n", self->Content ); +} + +inline +void preprocess_to_strbuilder_if(CodePreprocessCond cond, StrBuilder* result ) +{ + GEN_ASSERT(cond); + GEN_ASSERT(result); + strbuilder_append_fmt( result, "#if %S", cond->Content ); +} + +inline +void preprocess_to_strbuilder_ifdef(CodePreprocessCond cond, StrBuilder* result ) +{ + GEN_ASSERT(cond); + GEN_ASSERT(result); + strbuilder_append_fmt( result, "#ifdef %S\n", cond->Content ); +} + +inline +void preprocess_to_strbuilder_ifndef(CodePreprocessCond cond, StrBuilder* result ) +{ + GEN_ASSERT(cond); + GEN_ASSERT(result); + strbuilder_append_fmt( result, "#ifndef %S", cond->Content ); +} + +inline +void preprocess_to_strbuilder_elif(CodePreprocessCond cond, StrBuilder* result ) +{ + GEN_ASSERT(cond); + GEN_ASSERT(result); + strbuilder_append_fmt( result, "#elif %S\n", cond->Content ); +} + +inline +void preprocess_to_strbuilder_else(CodePreprocessCond cond, StrBuilder* result ) +{ + GEN_ASSERT(cond); + GEN_ASSERT(result); + strbuilder_append_str( result, txt("#else\n") ); +} + +inline +void preprocess_to_strbuilder_endif(CodePreprocessCond cond, StrBuilder* result ) +{ + GEN_ASSERT(cond); + GEN_ASSERT(result); + strbuilder_append_str( result, txt("#endif\n") ); +} + +inline +StrBuilder specifiers_to_strbuilder(CodeSpecifiers self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 64 ); + specifiers_to_strbuilder_ref( self, & result ); + return result; +} + +inline +StrBuilder template_to_strbuilder(CodeTemplate self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 1024 ); + template_to_strbuilder_ref( self, & result ); + return result; +} + +inline +StrBuilder typedef_to_strbuilder(CodeTypedef self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 128 ); + typedef_to_strbuilder_ref( self, & result ); + return result; +} + +inline +StrBuilder typename_to_strbuilder(CodeTypename self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_str( _ctx->Allocator_Temp, txt("") ); + typename_to_strbuilder_ref( self, & result ); + return result; +} + +inline +StrBuilder using_to_strbuilder(CodeUsing self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( _ctx->Allocator_Temp, 128 ); + switch ( self->Type ) + { + case CT_Using: + using_to_strbuilder_ref( self, & result ); + break; + case CT_Using_Namespace: + using_to_strbuilder_ns( self, & result ); + break; + } + return result; +} + +inline +void using_to_strbuilder_ns(CodeUsing self, StrBuilder* result ) +{ + GEN_ASSERT(self); + GEN_ASSERT(result); + if ( self->InlineCmt ) + strbuilder_append_fmt( result, "using namespace $S; %S", self->Name, self->InlineCmt->Content ); + else + strbuilder_append_fmt( result, "using namespace %S;\n", self->Name ); +} + +inline +StrBuilder var_to_strbuilder(CodeVar self) +{ + GEN_ASSERT(self); + StrBuilder result = strbuilder_make_reserve( get_context()->Allocator_Temp, 256 ); + var_to_strbuilder_ref( self, & result ); + return result; +} +#pragma endregion Serialization + +#pragma region Code +inline +void code_append( Code self, Code other ) +{ + GEN_ASSERT(self); + GEN_ASSERT(other); + GEN_ASSERT_MSG(self != other, "Attempted to recursively append Code AST to itself."); + + if ( other->Parent != nullptr ) + other = code_duplicate(other); + + other->Parent = self; + + if ( self->Front == nullptr ) + { + self->Front = other; + self->Back = other; + + self->NumEntries++; + return; + } + + Code + Current = self->Back; + Current->Next = other; + other->Prev = Current; + self->Back = other; + self->NumEntries++; +} +inline +bool code_is_body(Code self) +{ + GEN_ASSERT(self); + switch (self->Type) + { + case CT_Enum_Body: + case CT_Class_Body: + case CT_Union_Body: + case CT_Export_Body: + case CT_Global_Body: + case CT_Struct_Body: + case CT_Function_Body: + case CT_Namespace_Body: + case CT_Extern_Linkage_Body: + return true; + } + return false; +} +inline +Code* code_entry( Code self, u32 idx ) +{ + GEN_ASSERT(self != nullptr); + Code* current = & self->Front; + while ( idx >= 0 && current != nullptr ) + { + if ( idx == 0 ) + return rcast( Code*, current); + + current = & ( * current )->Next; + idx--; + } + + return rcast( Code*, current); +} +forceinline +bool code_is_valid(Code self) +{ + GEN_ASSERT(self); + return self != nullptr && self->Type != CT_Invalid; +} +forceinline +bool code_has_entries(Code self) +{ + GEN_ASSERT(self); + return self->NumEntries > 0; +} +forceinline +void code_set_global(Code self) +{ + if ( self == nullptr ) + { + log_failure("Code::set_global: Cannot set code as global, AST is null!"); + return; + } + + self->Parent = Code_Global; +} +#if GEN_COMPILER_CPP +forceinline +Code& Code::operator ++() +{ + if ( ast ) + ast = ast->Next.ast; + + return * this; +} +#endif +forceinline +Str code_type_str(Code self) +{ + GEN_ASSERT(self != nullptr); + return codetype_to_str( self->Type ); +} +#pragma endregion Code + +#pragma region CodeBody +inline +void body_append( CodeBody self, Code other ) +{ + GEN_ASSERT(self); + GEN_ASSERT(other); + + if (code_is_body(other)) { + body_append_body( self, cast(CodeBody, other) ); + return; + } + + code_append( cast(Code, self), other ); +} +inline +void body_append_body( CodeBody self, CodeBody body ) +{ + GEN_ASSERT(self); + GEN_ASSERT(body); + GEN_ASSERT_MSG(self != body, "Attempted to append body to itself."); + + for ( Code entry = begin_CodeBody(body); entry != end_CodeBody(body); entry = next_CodeBody(body, entry) ) { + body_append( self, entry ); + } +} +inline +Code begin_CodeBody( CodeBody body) { + GEN_ASSERT(body); + if ( body != nullptr ) + return body->Front; + + return NullCode; +} +forceinline +Code end_CodeBody(CodeBody body ){ + GEN_ASSERT(body); + return body->Back->Next; +} +inline +Code next_CodeBody(CodeBody body, Code entry) { + GEN_ASSERT(body); + GEN_ASSERT(entry); + return entry->Next; +} +#pragma endregion CodeBody + +#pragma region CodeClass +inline +void class_add_interface( CodeClass self, CodeTypename type ) +{ + GEN_ASSERT(self); + GEN_ASSERT(type); + CodeTypename possible_slot = self->ParentType; + if ( possible_slot != nullptr ) + { + // Were adding an interface to parent type, so we need to make sure the parent type is public. + self->ParentAccess = AccessSpec_Public; + // If your planning on adding a proper parent, + // then you'll need to move this over to ParentType->next and update ParentAccess accordingly. + } + + while ( possible_slot->Next != nullptr ) + { + possible_slot = cast(CodeTypename, possible_slot->Next); + } + + possible_slot->Next = cast(Code, type); +} +#pragma endregion CodeClass + +#pragma region CodeParams +inline +void params_append( CodeParams appendee, CodeParams other ) +{ + GEN_ASSERT(appendee); + GEN_ASSERT(other); + GEN_ASSERT_MSG(appendee != other, "Attempted to append parameter to itself."); + Code self = cast(Code, appendee); + Code entry = cast(Code, other); + + if ( entry->Parent != nullptr ) + entry = code_duplicate( entry ); + + entry->Parent = self; + + if ( self->Last == nullptr ) + { + self->Last = entry; + self->Next = entry; + self->NumEntries++; + return; + } + + self->Last->Next = entry; + self->Last = entry; + self->NumEntries++; +} +inline +CodeParams params_get(CodeParams self, s32 idx ) +{ + GEN_ASSERT(self); + CodeParams param = self; + do + { + if ( ++ param != nullptr ) + return NullCode; + + param = cast(CodeParams, cast(Code, param)->Next); + } + while ( --idx ); + + return param; +} +forceinline +bool params_has_entries(CodeParams self) +{ + GEN_ASSERT(self); + return self->NumEntries > 0; +} +#if GEN_COMPILER_CPP +forceinline +CodeParams& CodeParams::operator ++() +{ + * this = ast->Next; + return * this; +} +#endif +forceinline +CodeParams begin_CodeParams(CodeParams params) +{ + if ( params != nullptr ) + return params; + + return NullCode; +} +forceinline +CodeParams end_CodeParams(CodeParams params) +{ + // return { (AST_Params*) rcast( AST*, ast)->Last }; + return NullCode; +} +forceinline +CodeParams next_CodeParams(CodeParams params, CodeParams param_iter) +{ + GEN_ASSERT(param_iter); + return param_iter->Next; +} +#pragma endregion CodeParams + +#pragma region CodeDefineParams +forceinline void define_params_append (CodeDefineParams appendee, CodeDefineParams other ) { params_append( cast(CodeParams, appendee), cast(CodeParams, other) ); } +forceinline CodeDefineParams define_params_get (CodeDefineParams self, s32 idx ) { return (CodeDefineParams) (Code) params_get( cast(CodeParams, self), idx); } +forceinline bool define_params_has_entries(CodeDefineParams self) { return params_has_entries( cast(CodeParams, self)); } + +forceinline CodeDefineParams begin_CodeDefineParams(CodeDefineParams params) { return (CodeDefineParams) (Code) begin_CodeParams( cast(CodeParams, (Code)params)); } +forceinline CodeDefineParams end_CodeDefineParams (CodeDefineParams params) { return (CodeDefineParams) (Code) end_CodeParams ( cast(CodeParams, (Code)params)); } +forceinline CodeDefineParams next_CodeDefineParams (CodeDefineParams params, CodeDefineParams entry_iter) { return (CodeDefineParams) (Code) next_CodeParams ( cast(CodeParams, (Code)params), cast(CodeParams, (Code)entry_iter)); } + +#if GEN_COMPILER_CPP +forceinline +CodeDefineParams& CodeDefineParams::operator ++() +{ + * this = ast->Next; + return * this; +} +#endif +#pragma endregion CodeDefineParams + +#pragma region CodeSpecifiers +inline +bool specifiers_append(CodeSpecifiers self, Specifier spec ) +{ + if ( self == nullptr ) + { + log_failure("CodeSpecifiers: Attempted to append to a null specifiers AST!"); + return false; + } + if ( self->NumEntries == AST_ArrSpecs_Cap ) + { + log_failure("CodeSpecifiers: Attempted to append over %d specifiers to a specifiers AST!", AST_ArrSpecs_Cap ); + return false; + } + + self->ArrSpecs[ self->NumEntries ] = spec; + self->NumEntries++; + return true; +} +inline +bool specifiers_has(CodeSpecifiers self, Specifier spec) +{ + GEN_ASSERT(self != nullptr); + for ( s32 idx = 0; idx < self->NumEntries; idx++ ) { + if ( self->ArrSpecs[ idx ] == spec ) + return true; + } + return false; +} +inline +s32 specifiers_index_of(CodeSpecifiers self, Specifier spec) +{ + GEN_ASSERT(self != nullptr); + for ( s32 idx = 0; idx < self->NumEntries; idx++ ) { + if ( self->ArrSpecs[ idx ] == spec ) + return idx; + } + return -1; +} +inline +s32 specifiers_remove( CodeSpecifiers self, Specifier to_remove ) +{ + if ( self == nullptr ) + { + log_failure("CodeSpecifiers: Attempted to append to a null specifiers AST!"); + return -1; + } + if ( self->NumEntries == AST_ArrSpecs_Cap ) + { + log_failure("CodeSpecifiers: Attempted to append over %d specifiers to a specifiers AST!", AST_ArrSpecs_Cap ); + return -1; + } + + s32 result = -1; + + s32 curr = 0; + s32 next = 0; + for(; next < self->NumEntries; ++ curr, ++ next) + { + Specifier spec = self->ArrSpecs[next]; + if (spec == to_remove) + { + result = next; + + next ++; + if (next >= self->NumEntries) + break; + + spec = self->ArrSpecs[next]; + } + + self->ArrSpecs[ curr ] = spec; + } + + if (result > -1) { + self->NumEntries --; + } + return result; +} +forceinline +Specifier* begin_CodeSpecifiers(CodeSpecifiers self) +{ + if ( self != nullptr ) + return & self->ArrSpecs[0]; + + return nullptr; +} +forceinline +Specifier* end_CodeSpecifiers(CodeSpecifiers self) +{ + return self->ArrSpecs + self->NumEntries; +} +forceinline +Specifier* next_CodeSpecifiers(CodeSpecifiers self, Specifier* spec_iter) +{ + return spec_iter + 1; +} +#pragma endregion CodeSpecifiers + +#pragma region CodeStruct +inline +void struct_add_interface(CodeStruct self, CodeTypename type ) +{ + CodeTypename possible_slot = self->ParentType; + if ( possible_slot != nullptr ) + { + // Were adding an interface to parent type, so we need to make sure the parent type is public. + self->ParentAccess = AccessSpec_Public; + // If your planning on adding a proper parent, + // then you'll need to move this over to ParentType->next and update ParentAccess accordingly. + } + + while ( possible_slot->Next != nullptr ) + { + possible_slot = cast(CodeTypename, possible_slot->Next); + } + + possible_slot->Next = cast(Code, type); +} +#pragma endregion Code + +#pragma region Interface +inline +CodeBody def_body( CodeType type ) +{ + switch ( type ) + { + case CT_Class_Body: + case CT_Enum_Body: + case CT_Export_Body: + case CT_Extern_Linkage: + case CT_Function_Body: + case CT_Global_Body: + case CT_Namespace_Body: + case CT_Struct_Body: + case CT_Union_Body: + break; + + default: + log_failure( "def_body: Invalid type %s", codetype_to_str(type).Ptr ); + return (CodeBody)Code_Invalid; + } + + Code + result = make_code(); + result->Type = type; + return (CodeBody)result; +} + +inline +Str token_fmt_impl( ssize num, ... ) +{ + local_persist thread_local + char buf[GEN_PRINTF_MAXLEN] = { 0 }; + mem_set( buf, 0, GEN_PRINTF_MAXLEN ); + + va_list va; + va_start(va, num ); + ssize result = token_fmt_va(buf, GEN_PRINTF_MAXLEN, num, va); + va_end(va); + + Str str = { buf, result }; + return str; +} +#pragma endregion Interface diff --git a/tests/assets/gencpp_samples/components/interface.cpp b/tests/assets/gencpp_samples/components/interface.cpp new file mode 100644 index 0000000..6415e51 --- /dev/null +++ b/tests/assets/gencpp_samples/components/interface.cpp @@ -0,0 +1,542 @@ +#ifdef INTELLISENSE_DIRECTIVES +#pragma once +#include "code_serialization.cpp" +#endif + +internal void parser_init(Context* ctx); +internal void parser_deinit(Context* ctx); + +internal +void* fallback_allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ) +{ + GEN_ASSERT(_ctx); + GEN_ASSERT(_ctx->Fallback_AllocatorBuckets); + Arena* last = array_back(_ctx->Fallback_AllocatorBuckets); + + switch ( type ) + { + case EAllocation_ALLOC: + { + if ( ( last->TotalUsed + size ) > last->TotalSize ) + { + Arena bucket = arena_init_from_allocator( heap(), _ctx->InitSize_Fallback_Allocator_Bucket_Size ); + + if ( bucket.PhysicalStart == nullptr ) + GEN_FATAL( "Failed to create bucket for Fallback_AllocatorBuckets"); + + if ( ! array_append( _ctx->Fallback_AllocatorBuckets, bucket ) ) + GEN_FATAL( "Failed to append bucket to Fallback_AllocatorBuckets"); + + last = array_back(_ctx->Fallback_AllocatorBuckets); + } + + return alloc_align( arena_allocator_info(last), size, alignment ); + } + case EAllocation_FREE: + { + // Doesn't recycle. + } + break; + case EAllocation_FREE_ALL: + { + // Memory::cleanup instead. + } + break; + case EAllocation_RESIZE: + { + if ( last->TotalUsed + size > last->TotalSize ) + { + Arena bucket = arena_init_from_allocator( heap(), _ctx->InitSize_Fallback_Allocator_Bucket_Size ); + + if ( bucket.PhysicalStart == nullptr ) + GEN_FATAL( "Failed to create bucket for Fallback_AllocatorBuckets"); + + if ( ! array_append( _ctx->Fallback_AllocatorBuckets, bucket ) ) + GEN_FATAL( "Failed to append bucket to Fallback_AllocatorBuckets"); + + last = array_back( _ctx->Fallback_AllocatorBuckets); + } + + void* result = alloc_align( last->Backing, size, alignment ); + + if ( result != nullptr && old_memory != nullptr ) + { + mem_copy( result, old_memory, old_size ); + } + + return result; + } + } + + return nullptr; +} + +internal +void fallback_logger(LogEntry entry) +{ + GEN_ASSERT(entry.msg.Len > 0); + GEN_ASSERT(entry.msg.Ptr); + log_fmt("%S: %S", loglevel_to_str(entry.level), entry.msg); +} + +internal +void define_constants() +{ + // We only initalize these if there is no base context. + if ( context_counter > 0 ) + return; + + Code_Global = make_code(); + Code_Global->Name = cache_str( txt("Global Code") ); + Code_Global->Content = Code_Global->Name; + + Code_Invalid = make_code(); + code_set_global(Code_Invalid); + + t_empty = (CodeTypename) make_code(); + t_empty->Type = CT_Typename; + t_empty->Name = cache_str( txt("") ); + code_set_global(cast(Code, t_empty)); + + access_private = make_code(); + access_private->Type = CT_Access_Private; + access_private->Name = cache_str( txt("private:\n") ); + code_set_global(cast(Code, access_private)); + + access_protected = make_code(); + access_protected->Type = CT_Access_Protected; + access_protected->Name = cache_str( txt("protected:\n") ); + code_set_global(access_protected); + + access_public = make_code(); + access_public->Type = CT_Access_Public; + access_public->Name = cache_str( txt("public:\n") ); + code_set_global(access_public); + + Str api_export_str = code(GEN_API_Export_Code); + attrib_api_export = def_attributes( api_export_str ); + code_set_global(cast(Code, attrib_api_export)); + + Str api_import_str = code(GEN_API_Import_Code); + attrib_api_import = def_attributes( api_import_str ); + code_set_global(cast(Code, attrib_api_import)); + + module_global_fragment = make_code(); + module_global_fragment->Type = CT_Untyped; + module_global_fragment->Name = cache_str( txt("module;") ); + module_global_fragment->Content = module_global_fragment->Name; + code_set_global(cast(Code, module_global_fragment)); + + module_private_fragment = make_code(); + module_private_fragment->Type = CT_Untyped; + module_private_fragment->Name = cache_str( txt("module : private;") ); + module_private_fragment->Content = module_private_fragment->Name; + code_set_global(cast(Code, module_private_fragment)); + + fmt_newline = make_code(); + fmt_newline->Type = CT_NewLine; + code_set_global((Code)fmt_newline); + + pragma_once = (CodePragma) make_code(); + pragma_once->Type = CT_Preprocess_Pragma; + pragma_once->Name = cache_str( txt("once") ); + pragma_once->Content = pragma_once->Name; + code_set_global((Code)pragma_once); + + param_varadic = (CodeParams) make_code(); + param_varadic->Type = CT_Parameters; + param_varadic->Name = cache_str( txt("...") ); + param_varadic->ValueType = t_empty; + code_set_global((Code)param_varadic); + + preprocess_else = (CodePreprocessCond) make_code(); + preprocess_else->Type = CT_Preprocess_Else; + code_set_global((Code)preprocess_else); + + preprocess_endif = (CodePreprocessCond) make_code(); + preprocess_endif->Type = CT_Preprocess_EndIf; + code_set_global((Code)preprocess_endif); + + Str auto_str = txt("auto"); t_auto = def_type( auto_str ); code_set_global( t_auto ); + Str void_str = txt("void"); t_void = def_type( void_str ); code_set_global( t_void ); + Str int_str = txt("int"); t_int = def_type( int_str ); code_set_global( t_int ); + Str bool_str = txt("bool"); t_bool = def_type( bool_str ); code_set_global( t_bool ); + Str char_str = txt("char"); t_char = def_type( char_str ); code_set_global( t_char ); + Str wchar_str = txt("wchar_t"); t_wchar_t = def_type( wchar_str ); code_set_global( t_wchar_t ); + Str class_str = txt("class"); t_class = def_type( class_str ); code_set_global( t_class ); + Str typename_str = txt("typename"); t_typename = def_type( typename_str ); code_set_global( t_typename ); + +#ifdef GEN_DEFINE_LIBRARY_CODE_CONSTANTS + t_b32 = def_type( name(b32) ); code_set_global( t_b32 ); + + Str s8_str = txt("s8"); t_s8 = def_type( s8_str ); code_set_global( t_s8 ); + Str s16_str = txt("s16"); t_s16 = def_type( s16_str ); code_set_global( t_s16 ); + Str s32_str = txt("s32"); t_s32 = def_type( s32_str ); code_set_global( t_s32 ); + Str s64_str = txt("s64"); t_s64 = def_type( s64_str ); code_set_global( t_s64 ); + + Str u8_str = txt("u8"); t_u8 = def_type( u8_str ); code_set_global( t_u8 ); + Str u16_str = txt("u16"); t_u16 = def_type( u16_str ); code_set_global( t_u16 ); + Str u32_str = txt("u32"); t_u32 = def_type( u32_str ); code_set_global( t_u32 ); + Str u64_str = txt("u64"); t_u64 = def_type( u64_str ); code_set_global( t_u64 ); + + Str ssize_str = txt("ssize"); t_ssize = def_type( ssize_str ); code_set_global( t_ssize ); + Str usize_str = txt("usize"); t_usize = def_type( usize_str ); code_set_global( t_usize ); + + Str f32_str = txt("f32"); t_f32 = def_type( f32_str ); code_set_global( t_f32 ); + Str f64_str = txt("f64"); t_f64 = def_type( f64_str ); code_set_global( t_f64 ); +#endif + + spec_const = def_specifier( Spec_Const); code_set_global( cast(Code, spec_const )); + spec_consteval = def_specifier( Spec_Consteval); code_set_global( cast(Code, spec_consteval )); + spec_constexpr = def_specifier( Spec_Constexpr); code_set_global( cast(Code, spec_constexpr )); + spec_constinit = def_specifier( Spec_Constinit); code_set_global( cast(Code, spec_constinit )); + spec_extern_linkage = def_specifier( Spec_External_Linkage); code_set_global( cast(Code, spec_extern_linkage )); + spec_final = def_specifier( Spec_Final); code_set_global( cast(Code, spec_final )); + spec_forceinline = def_specifier( Spec_ForceInline); code_set_global( cast(Code, spec_forceinline )); + spec_global = def_specifier( Spec_Global); code_set_global( cast(Code, spec_global )); + spec_inline = def_specifier( Spec_Inline); code_set_global( cast(Code, spec_inline )); + spec_internal_linkage = def_specifier( Spec_Internal_Linkage); code_set_global( cast(Code, spec_internal_linkage )); + spec_local_persist = def_specifier( Spec_Local_Persist); code_set_global( cast(Code, spec_local_persist )); + spec_mutable = def_specifier( Spec_Mutable); code_set_global( cast(Code, spec_mutable )); + spec_neverinline = def_specifier( Spec_NeverInline); code_set_global( cast(Code, spec_neverinline )); + spec_noexcept = def_specifier( Spec_NoExceptions); code_set_global( cast(Code, spec_noexcept )); + spec_override = def_specifier( Spec_Override); code_set_global( cast(Code, spec_override )); + spec_ptr = def_specifier( Spec_Ptr); code_set_global( cast(Code, spec_ptr )); + spec_pure = def_specifier( Spec_Pure); code_set_global( cast(Code, spec_pure )); + spec_ref = def_specifier( Spec_Ref); code_set_global( cast(Code, spec_ref )); + spec_register = def_specifier( Spec_Register); code_set_global( cast(Code, spec_register )); + spec_rvalue = def_specifier( Spec_RValue); code_set_global( cast(Code, spec_rvalue )); + spec_static_member = def_specifier( Spec_Static); code_set_global( cast(Code, spec_static_member )); + spec_thread_local = def_specifier( Spec_Thread_Local); code_set_global( cast(Code, spec_thread_local )); + spec_virtual = def_specifier( Spec_Virtual); code_set_global( cast(Code, spec_virtual )); + spec_volatile = def_specifier( Spec_Volatile); code_set_global( cast(Code, spec_volatile )); + + spec_local_persist = def_specifiers( 1, Spec_Local_Persist ); + code_set_global(cast(Code, spec_local_persist)); + + if (enum_underlying_macro.Name.Len == 0) { + enum_underlying_macro.Name = txt("enum_underlying"); + enum_underlying_macro.Type = MT_Expression; + enum_underlying_macro.Flags = MF_Functional; + } + register_macro(enum_underlying_macro); +} + +void init(Context* ctx) +{ + do_once() { + context_counter = 0; + } + AllocatorInfo fallback_allocator = { & fallback_allocator_proc, nullptr }; + + b32 using_fallback_allocator = false; + if (ctx->Allocator_DyanmicContainers.Proc == nullptr) { + ctx->Allocator_DyanmicContainers = fallback_allocator; + using_fallback_allocator = true; + } + if (ctx->Allocator_Pool.Proc == nullptr ) { + ctx->Allocator_Pool = fallback_allocator; + using_fallback_allocator = true; + } + if (ctx->Allocator_StrCache.Proc == nullptr) { + ctx->Allocator_StrCache = fallback_allocator; + using_fallback_allocator = true; + } + if (ctx->Allocator_Temp.Proc == nullptr) { + ctx->Allocator_Temp = fallback_allocator; + using_fallback_allocator = true; + } + // Setup fallback allocator + if (using_fallback_allocator) + { + ctx->Fallback_AllocatorBuckets = array_init_reserve(Arena, heap(), 128 ); + if ( ctx->Fallback_AllocatorBuckets == nullptr ) + GEN_FATAL( "Failed to reserve memory for Fallback_AllocatorBuckets"); + + Arena bucket = arena_init_from_allocator( heap(), ctx->InitSize_Fallback_Allocator_Bucket_Size ); + if ( bucket.PhysicalStart == nullptr ) + GEN_FATAL( "Failed to create first bucket for Fallback_AllocatorBuckets"); + + array_append( ctx->Fallback_AllocatorBuckets, bucket ); + } + + if (ctx->Max_CommentLineLength == 0) { + ctx->Max_CommentLineLength = 1024; + } + if (ctx->Max_StrCacheLength == 0) { + ctx->Max_StrCacheLength = kilobytes(512); + } + + if (ctx->InitSize_BuilderBuffer == 0) { + ctx->InitSize_BuilderBuffer = megabytes(2); + } + if (ctx->InitSize_CodePoolsArray == 0) { + ctx->InitSize_CodePoolsArray = 16; + } + if (ctx->InitSize_StringArenasArray == 0) { + ctx->InitSize_StringArenasArray = 16; + } + if (ctx->CodePool_NumBlocks == 0) { + ctx->CodePool_NumBlocks = kilobytes(16); + } + + if (ctx->InitSize_LexerTokens == 0 ) { + ctx->InitSize_LexerTokens = kilobytes(64); + } + if (ctx->SizePer_StringArena == 0) { + ctx->SizePer_StringArena = megabytes(1); + } + + if (ctx->InitSize_Fallback_Allocator_Bucket_Size == 0) { + ctx->InitSize_Fallback_Allocator_Bucket_Size = megabytes(8); + } + + if (ctx->InitSize_StrCacheTable == 0) + { + ctx->InitSize_StrCacheTable = kilobytes(8); + } + if (ctx->InitSize_MacrosTable == 0) + { + ctx->InitSize_MacrosTable = kilobytes(8); + } + + if (ctx->Logger == nullptr) { + ctx->Logger = & fallback_logger; + } + + // Override the current context (user has to put it back if unwanted). + _ctx = ctx; + + // Setup the arrays + { + ctx->CodePools = array_init_reserve(Pool, ctx->Allocator_DyanmicContainers, ctx->InitSize_CodePoolsArray ); + if ( ctx->CodePools == nullptr ) + GEN_FATAL( "gen::init: Failed to initialize the CodePools array" ); + + ctx->StringArenas = array_init_reserve(Arena, ctx->Allocator_DyanmicContainers, ctx->InitSize_StringArenasArray ); + if ( ctx->StringArenas == nullptr ) + GEN_FATAL( "gen::init: Failed to initialize the StringArenas array" ); + } + // Setup the code pool and code entries arena. + { + Pool code_pool = pool_init( ctx->Allocator_Pool, ctx->CodePool_NumBlocks, size_of(AST) ); + if ( code_pool.PhysicalStart == nullptr ) + GEN_FATAL( "gen::init: Failed to initialize the code pool" ); + array_append( ctx->CodePools, code_pool ); + + // TODO(Ed): Eventually the string arenas needs to be phased out for a dedicated string slab allocator + Arena strbuilder_arena = arena_init_from_allocator( ctx->Allocator_StrCache, ctx->SizePer_StringArena ); + if ( strbuilder_arena.PhysicalStart == nullptr ) + GEN_FATAL( "gen::init: Failed to initialize the string arena" ); + array_append( ctx->StringArenas, strbuilder_arena ); + } + // Setup the hash tables + { + ctx->StrCache = hashtable_init_reserve(StrCached, ctx->Allocator_DyanmicContainers, ctx->InitSize_StrCacheTable); + if ( ctx->StrCache.Entries == nullptr ) + GEN_FATAL( "gen::init: Failed to initialize the StringCache"); + + ctx->Macros = hashtable_init_reserve(Macro, ctx->Allocator_DyanmicContainers, ctx->InitSize_MacrosTable); + if (ctx->Macros.Hashes == nullptr || ctx->Macros.Entries == nullptr) { + GEN_FATAL( "gen::init: Failed to initialize the PreprocessMacros table" ); + } + } + + define_constants(); + parser_init(ctx); + + ++ context_counter; +} + +void deinit(Context* ctx) +{ + GEN_ASSERT(context_counter); + GEN_ASSERT_MSG(context_counter > 0, "Attempted to deinit a context that for some reason wan't accounted for!"); + usize index = 0; + usize left = array_num(ctx->CodePools); + do + { + Pool* code_pool = & ctx->CodePools[index]; + pool_free(code_pool); + index++; + } + while ( left--, left ); + + index = 0; + left = array_num(ctx->StringArenas); + do + { + Arena* strbuilder_arena = & ctx->StringArenas[index]; + arena_free(strbuilder_arena); + index++; + } + while ( left--, left ); + + hashtable_destroy(ctx->StrCache); + + array_free( ctx->CodePools); + array_free( ctx->StringArenas); + + hashtable_destroy(ctx->Macros); + + left = array_num( ctx->Fallback_AllocatorBuckets); + if (left) + { + index = 0; + do + { + Arena* bucket = & ctx->Fallback_AllocatorBuckets[ index ]; + arena_free(bucket); + index++; + } + while ( left--, left ); + array_free( ctx->Fallback_AllocatorBuckets); + } + parser_deinit(ctx); + + if (_ctx == ctx) + _ctx = nullptr; + -- context_counter; + + Context wipe = {}; + * ctx = wipe; +} + +Context* get_context() { + return _ctx; +} + +void reset(Context* ctx) +{ + s32 index = 0; + s32 left = array_num(ctx->CodePools); + do + { + Pool* code_pool = & ctx->CodePools[index]; + pool_clear(code_pool); + index++; + } + while ( left--, left ); + + index = 0; + left = array_num(ctx->StringArenas); + do + { + Arena* strbuilder_arena = & ctx->StringArenas[index]; + strbuilder_arena->TotalUsed = 0;; + index++; + } + while ( left--, left ); + + hashtable_clear(ctx->StrCache); + hashtable_clear(ctx->Macros); + define_constants(); +} + +void set_context(Context* new_ctx) { + GEN_ASSERT(new_ctx); + _ctx = new_ctx; +} + +AllocatorInfo get_cached_str_allocator( s32 str_length ) +{ + Arena* last = array_back(_ctx->StringArenas); + usize size_req = str_length + sizeof(StrBuilderHeader) + sizeof(char*); + if ( last->TotalUsed + scast(ssize, size_req) > last->TotalSize ) + { + Arena new_arena = arena_init_from_allocator( _ctx->Allocator_StrCache, _ctx->SizePer_StringArena ); + if ( ! array_append( _ctx->StringArenas, new_arena ) ) + GEN_FATAL( "gen::get_cached_str_allocator: Failed to allocate a new string arena" ); + + last = array_back( _ctx->StringArenas); + } + return arena_allocator_info(last); +} + +// Will either make or retrive a code string. +StrCached cache_str( Str str ) +{ + if (str.Len > _ctx->Max_StrCacheLength) { + // Do not cache the string, just shove into the arena and and return it. + Str result = strbuilder_to_str( strbuilder_make_str( get_cached_str_allocator( str.Len ), str )); + return result; + } + u64 key = crc32( str.Ptr, str.Len ); { + StrCached* result = hashtable_get( _ctx->StrCache, key ); + if ( result ) + return * result; + } + Str result = strbuilder_to_str( strbuilder_make_str( get_cached_str_allocator( str.Len ), str )); + hashtable_set( _ctx->StrCache, key, result ); + return result; +} + +// Used internally to retireve a Code object form the CodePool. +Code make_code() +{ + Pool* allocator = array_back( _ctx->CodePools); + if ( allocator->FreeList == nullptr ) + { + Pool code_pool = pool_init( _ctx->Allocator_Pool, _ctx->CodePool_NumBlocks, sizeof(AST) ); + + if ( code_pool.PhysicalStart == nullptr ) + GEN_FATAL( "gen::make_code: Failed to allocate a new code pool - CodePool allcoator returned nullptr." ); + + if ( ! array_append( _ctx->CodePools, code_pool ) ) + GEN_FATAL( "gen::make_code: Failed to allocate a new code pool - CodePools failed to append new pool." ); + + allocator = array_back( _ctx->CodePools); + } + Code result = { rcast( AST*, alloc( pool_allocator_info(allocator), sizeof(AST) )) }; + mem_set( rcast(void*, cast(AST*, result)), 0, sizeof(AST) ); + return result; +} + +Macro* lookup_macro( Str name ) { + u32 key = crc32( name.Ptr, name.Len ); + return hashtable_get( _ctx->Macros, key ); +} + +void register_macro( Macro macro ) { + GEN_ASSERT_NOT_NULL(macro.Name.Ptr); + GEN_ASSERT(macro.Name.Len > 0); + u32 key = crc32( macro.Name.Ptr, macro.Name.Len ); + macro.Name = cache_str(macro.Name); + hashtable_set( _ctx->Macros, key, macro ); +} + +void register_macros( s32 num, ... ) +{ + GEN_ASSERT(num > 0); + va_list va; + va_start(va, num); + do + { + Macro macro = va_arg(va, Macro); + GEN_ASSERT_NOT_NULL(macro.Name.Ptr); + GEN_ASSERT(macro.Name.Len > 0); + macro.Name = cache_str(macro.Name); + + u32 key = crc32( macro.Name.Ptr, macro.Name.Len ); + hashtable_set( _ctx->Macros, key, macro ); + } + while (num--, num > 0); + va_end(va); +} + +void register_macros_arr( s32 num, Macro* macros ) +{ + GEN_ASSERT(num > 0); + do + { + Macro macro = * macros; + GEN_ASSERT_NOT_NULL(macro.Name.Ptr); + GEN_ASSERT(macro.Name.Len > 0); + macro.Name = cache_str(macro.Name); + + u32 key = crc32( macro.Name.Ptr, macro.Name.Len ); + hashtable_set( _ctx->Macros, key, macro ); + ++ macros; + } + while (num--, num > 0); +} diff --git a/tests/assets/gencpp_samples/components/interface.hpp b/tests/assets/gencpp_samples/components/interface.hpp new file mode 100644 index 0000000..a46337a --- /dev/null +++ b/tests/assets/gencpp_samples/components/interface.hpp @@ -0,0 +1,490 @@ +#ifdef INTELLISENSE_DIRECTIVES +#pragma once +#include "ast_types.hpp" +#endif + +#pragma region Gen Interface +/* + / \ | \ | \ / \ +| ▓▓▓▓▓▓\ ______ _______ \▓▓▓▓▓▓_______ _| ▓▓_ ______ ______ | ▓▓▓▓▓▓\ ______ _______ ______ +| ▓▓ __\▓▓/ \| \ | ▓▓ | \| ▓▓ \ / \ / \| ▓▓_ \▓▓| \ / \/ \ +| ▓▓| \ ▓▓▓▓▓▓\ ▓▓▓▓▓▓▓\ | ▓▓ | ▓▓▓▓▓▓▓\\▓▓▓▓▓▓ | ▓▓▓▓▓▓\ ▓▓▓▓▓▓\ ▓▓ \ \▓▓▓▓▓▓\ ▓▓▓▓▓▓▓ ▓▓▓▓▓▓\ +| ▓▓ \▓▓▓▓ ▓▓ ▓▓ ▓▓ | ▓▓ | ▓▓ | ▓▓ | ▓▓ | ▓▓ __| ▓▓ ▓▓ ▓▓ \▓▓ ▓▓▓▓ / ▓▓ ▓▓ | ▓▓ ▓▓ +| ▓▓__| ▓▓ ▓▓▓▓▓▓▓▓ ▓▓ | ▓▓ _| ▓▓_| ▓▓ | ▓▓ | ▓▓| \ ▓▓▓▓▓▓▓▓ ▓▓ | ▓▓ | ▓▓▓▓▓▓▓ ▓▓_____| ▓▓▓▓▓▓▓▓ + \▓▓ ▓▓\▓▓ \ ▓▓ | ▓▓ | ▓▓ \ ▓▓ | ▓▓ \▓▓ ▓▓\▓▓ \ ▓▓ | ▓▓ \▓▓ ▓▓\▓▓ \\▓▓ \ + \▓▓▓▓▓▓ \▓▓▓▓▓▓▓\▓▓ \▓▓ \▓▓▓▓▓▓\▓▓ \▓▓ \▓▓▓▓ \▓▓▓▓▓▓▓\▓▓ \▓▓ \▓▓▓▓▓▓▓ \▓▓▓▓▓▓▓ \▓▓▓▓▓▓▓ +*/ + +// 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. +struct Context +{ +// User Configuration + +// Persistent Data Allocation + AllocatorInfo Allocator_DyanmicContainers; // By default will use a genral slab allocator (TODO(Ed): Currently does not) + AllocatorInfo Allocator_Pool; // By default will use the growing vmem reserve (TODO(Ed): Currently does not) + AllocatorInfo Allocator_StrCache; // By default will use a dedicated slab allocator (TODO(Ed): Currently does not) + +// Temporary Allocation + AllocatorInfo Allocator_Temp; + + // LoggerCallaback* log_callback; // TODO(Ed): Impl user logger callback as an option. + +// Initalization config + u32 Max_CommentLineLength; // Used by def_comment + u32 Max_StrCacheLength; // Any cached string longer than this is always allocated again. + + u32 InitSize_BuilderBuffer; + u32 InitSize_CodePoolsArray; + u32 InitSize_StringArenasArray; + + u32 CodePool_NumBlocks; + + // TODO(Ed): Review these... (No longer needed if using the proper allocation strategy) + u32 InitSize_LexerTokens; + u32 SizePer_StringArena; + + u32 InitSize_StrCacheTable; + u32 InitSize_MacrosTable; + +// TODO(Ed): Symbol Table + // Keep track of all resolved symbols (naemspaced identifiers) + +// Logging + + LoggerProc* Logger; + +// Parser + + // Used by the lexer to persistently treat all these identifiers as preprocessor defines. + // Populate with strings via gen::cache_str. + // Functional defines must have format: id( ;at minimum to indicate that the define is only valid with arguments. + MacroTable Macros; + +// Backend + + // The fallback allocator is utilized if any fo the three above allocators is not specified by the user. + u32 InitSize_Fallback_Allocator_Bucket_Size; + Array(Arena) Fallback_AllocatorBuckets; + + StringTable token_fmt_map; + + // Array(Token) LexerTokens; + + Array(Pool) CodePools; + Array(Arena) StringArenas; + + StringTable StrCache; + + // TODO(Ed): Active parse context vs a parse result need to be separated conceptually + ParseContext parser; + + // TODO(Ed): Formatting - This will eventually be in a separate struct when in the process of serialization of the builder. + s32 temp_serialize_indent; +}; + +// TODO(Ed): Eventually this library should opt out of an implicit context for baseline implementation +// This would automatically make it viable for multi-threaded purposes among other things +// An implicit context interface will be provided instead as wrapper procedures as convience. +GEN_API extern Context* _ctx; + +// TODO(Ed): Swap all usage of this with logger_fmt (then rename logger_fmt to log_fmt) +inline +ssize log_fmt(char const* fmt, ...) +{ + ssize res; + va_list va; + + va_start(va, fmt); + res = c_str_fmt_out_va(fmt, va); + va_end(va); + + return res; +} + +inline +void logger_fmt(Context* ctx, LogLevel level, char const* fmt, ...) +{ + local_persist thread_local + PrintF_Buffer buf = struct_zero_init(); + + va_list va; + va_start(va, fmt); + ssize res = c_str_fmt_va(buf, GEN_PRINTF_MAXLEN, fmt, va) -1; + va_end(va); + + StrBuilder msg = strbuilder_make_length(ctx->Allocator_Temp, buf, res); + + LogEntry entry = { strbuilder_to_str(msg), level }; + ctx->Logger(entry); +} + +// Initialize the library. There first ctx initialized must exist for lifetime of other contextes that come after as its the one that +GEN_API void init(Context* ctx); + +// Currently manually free's the arenas, code for checking for leaks. +// However on Windows at least, it doesn't need to occur as the OS will clean up after the process. +GEN_API void deinit(Context* ctx); + +// Retrieves the active context (not usually needed, but here in case...) +GEN_API Context* get_context(); + +// Clears the allocations, but doesn't free the memory, then calls init() again. +// Ease of use. +GEN_API void reset(Context* ctx); + +GEN_API void set_context(Context* ctx); + +// Mostly intended for the parser +GEN_API Macro* lookup_macro( Str Name ); + +// Alternative way to add a preprocess define entry for the lexer & parser to utilize +// if the user doesn't want to use def_define +// Macros are tracked by name so if the name already exists the entry will be overwritten. +GEN_API void register_macro( Macro macro ); + +// Ease of use batch registration +GEN_API void register_macros( s32 num, ... ); +GEN_API void register_macros_arr( s32 num, Macro* macros ); + +#if GEN_COMPILER_CPP +forceinline void register_macros( s32 num, Macro* macros ) { return register_macros_arr(num, macros); } +#endif + +// Used internally to retrive or make string allocations. +// Strings are stored in a series of string arenas of fixed size (SizePer_StringArena) +GEN_API StrCached cache_str( Str str ); + +/* + This provides a fresh Code AST. + The gen interface use this as their method from getting a new AST object from the CodePool. + Use this if you want to make your own API for formatting the supported Code Types. +*/ +GEN_API Code make_code(); + +// Set these before calling gen's init() procedure. + +#pragma region Upfront + +GEN_API CodeAttributes def_attributes( Str content ); +GEN_API CodeComment def_comment ( Str content ); + +struct Opts_def_struct { + CodeBody body; + CodeTypename parent; + AccessSpec parent_access; + 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 ); + +struct Opts_def_constructor { + CodeParams params; + Code initializer_list; + Code body; +}; +GEN_API CodeConstructor def_constructor( Opts_def_constructor opts GEN_PARAM_DEFAULT ); + +struct Opts_def_define { + CodeDefineParams params; + Str content; + MacroFlags flags; + b32 dont_register_to_preprocess_macros; +}; +GEN_API CodeDefine def_define( Str name, MacroType type, Opts_def_define opts GEN_PARAM_DEFAULT ); + +struct Opts_def_destructor { + Code body; + CodeSpecifiers specifiers; +}; +GEN_API CodeDestructor def_destructor( Opts_def_destructor opts GEN_PARAM_DEFAULT ); + +struct Opts_def_enum { + CodeBody body; + CodeTypename type; + EnumT specifier; + CodeAttributes attributes; + ModuleFlag mflags; + Code type_macro; +}; +GEN_API CodeEnum def_enum( Str name, Opts_def_enum opts GEN_PARAM_DEFAULT ); + +GEN_API CodeExec def_execution ( Str content ); +GEN_API CodeExtern def_extern_link( Str name, CodeBody body ); +GEN_API CodeFriend def_friend ( Code code ); + +struct Opts_def_function { + CodeParams params; + CodeTypename ret_type; + CodeBody body; + CodeSpecifiers specs; + CodeAttributes attrs; + ModuleFlag mflags; +}; +GEN_API CodeFn def_function( Str name, Opts_def_function opts GEN_PARAM_DEFAULT ); + +struct Opts_def_include { b32 foreign; }; +struct Opts_def_module { ModuleFlag mflags; }; +struct Opts_def_namespace { ModuleFlag mflags; }; +GEN_API CodeInclude def_include ( Str content, Opts_def_include opts GEN_PARAM_DEFAULT ); +GEN_API CodeModule def_module ( Str name, Opts_def_module opts GEN_PARAM_DEFAULT ); +GEN_API CodeNS def_namespace( Str name, CodeBody body, Opts_def_namespace opts GEN_PARAM_DEFAULT ); + +struct Opts_def_operator { + CodeParams params; + CodeTypename ret_type; + CodeBody body; + CodeSpecifiers specifiers; + CodeAttributes attributes; + ModuleFlag mflags; +}; +GEN_API CodeOperator def_operator( Operator op, Str nspace, Opts_def_operator opts GEN_PARAM_DEFAULT ); + +struct Opts_def_operator_cast { + CodeBody body; + CodeSpecifiers specs; +}; +GEN_API CodeOpCast def_operator_cast( CodeTypename type, Opts_def_operator_cast opts GEN_PARAM_DEFAULT ); + +struct Opts_def_param { Code value; }; +GEN_API CodeParams def_param ( CodeTypename type, Str name, Opts_def_param opts GEN_PARAM_DEFAULT ); +GEN_API CodePragma def_pragma( Str directive ); + +GEN_API CodePreprocessCond def_preprocess_cond( EPreprocessCond type, Str content ); + +GEN_API CodeSpecifiers def_specifier( Specifier specifier ); + +GEN_API CodeStruct def_struct( Str name, Opts_def_struct opts GEN_PARAM_DEFAULT ); + +struct Opts_def_template { ModuleFlag mflags; }; +GEN_API CodeTemplate def_template( CodeParams params, Code definition, Opts_def_template opts GEN_PARAM_DEFAULT ); + +struct Opts_def_type { + ETypenameTag type_tag; + Code array_expr; + CodeSpecifiers specifiers; + CodeAttributes attributes; +}; +GEN_API CodeTypename def_type( Str name, Opts_def_type opts GEN_PARAM_DEFAULT ); + +struct Opts_def_typedef { + CodeAttributes attributes; + ModuleFlag mflags; +}; +GEN_API CodeTypedef def_typedef( Str name, Code type, Opts_def_typedef opts GEN_PARAM_DEFAULT ); + +struct Opts_def_union { + CodeAttributes attributes; + ModuleFlag mflags; +}; +GEN_API CodeUnion def_union( Str name, CodeBody body, Opts_def_union opts GEN_PARAM_DEFAULT ); + +struct Opts_def_using { + CodeAttributes attributes; + ModuleFlag mflags; +}; +GEN_API CodeUsing def_using( Str name, CodeTypename type, Opts_def_using opts GEN_PARAM_DEFAULT ); + +GEN_API CodeUsing def_using_namespace( Str name ); + +struct Opts_def_variable +{ + Code value; + CodeSpecifiers specifiers; + CodeAttributes attributes; + ModuleFlag mflags; +}; +GEN_API CodeVar def_variable( CodeTypename type, Str name, Opts_def_variable opts GEN_PARAM_DEFAULT ); + +// Constructs an empty body. Use AST::validate_body() to check if the body is was has valid entries. +CodeBody def_body( CodeType type ); + +// There are two options for defining a struct body, either varadically provided with the args macro to auto-deduce the arg num, +/// or provide as an array of Code objects. + +GEN_API CodeBody def_class_body ( s32 num, ... ); +GEN_API CodeBody def_class_body_arr ( s32 num, Code* codes ); +GEN_API CodeDefineParams def_define_params ( s32 num, ... ); +GEN_API CodeDefineParams def_define_params_arr ( s32 num, CodeDefineParams* codes ); +GEN_API CodeBody def_enum_body ( s32 num, ... ); +GEN_API CodeBody def_enum_body_arr ( s32 num, Code* codes ); +GEN_API CodeBody def_export_body ( s32 num, ... ); +GEN_API CodeBody def_export_body_arr ( s32 num, Code* codes); +GEN_API CodeBody def_extern_link_body ( s32 num, ... ); +GEN_API CodeBody def_extern_link_body_arr ( s32 num, Code* codes ); +GEN_API CodeBody def_function_body ( s32 num, ... ); +GEN_API CodeBody def_function_body_arr ( s32 num, Code* codes ); +GEN_API CodeBody def_global_body ( s32 num, ... ); +GEN_API CodeBody def_global_body_arr ( s32 num, Code* codes ); +GEN_API CodeBody def_namespace_body ( s32 num, ... ); +GEN_API CodeBody def_namespace_body_arr ( s32 num, Code* codes ); +GEN_API CodeParams def_params ( s32 num, ... ); +GEN_API CodeParams def_params_arr ( s32 num, CodeParams* params ); +GEN_API CodeSpecifiers def_specifiers ( s32 num, ... ); +GEN_API CodeSpecifiers def_specifiers_arr ( s32 num, Specifier* specs ); +GEN_API CodeBody def_struct_body ( s32 num, ... ); +GEN_API CodeBody def_struct_body_arr ( s32 num, Code* codes ); +GEN_API CodeBody def_union_body ( s32 num, ... ); +GEN_API CodeBody def_union_body_arr ( s32 num, Code* codes ); + +#if GEN_COMPILER_CPP +forceinline CodeBody def_class_body ( s32 num, Code* codes ) { return def_class_body_arr(num, codes); } +forceinline CodeDefineParams def_define_params ( s32 num, CodeDefineParams* codes ) { return def_define_params_arr(num, codes); } +forceinline CodeBody def_enum_body ( s32 num, Code* codes ) { return def_enum_body_arr(num, codes); } +forceinline CodeBody def_export_body ( s32 num, Code* codes) { return def_export_body_arr(num, codes); } +forceinline CodeBody def_extern_link_body( s32 num, Code* codes ) { return def_extern_link_body_arr(num, codes); } +forceinline CodeBody def_function_body ( s32 num, Code* codes ) { return def_function_body_arr(num, codes); } +forceinline CodeBody def_global_body ( s32 num, Code* codes ) { return def_global_body_arr(num, codes); } +forceinline CodeBody def_namespace_body ( s32 num, Code* codes ) { return def_namespace_body_arr(num, codes); } +forceinline CodeParams def_params ( s32 num, CodeParams* params ) { return def_params_arr(num, params); } +forceinline CodeSpecifiers def_specifiers ( s32 num, Specifier* specs ) { return def_specifiers_arr(num, specs); } +forceinline CodeBody def_struct_body ( s32 num, Code* codes ) { return def_struct_body_arr(num, codes); } +forceinline CodeBody def_union_body ( s32 num, Code* codes ) { return def_union_body_arr(num, codes); } +#endif + +#pragma endregion Upfront + +#pragma region Parsing + +struct ParseStackNode +{ + ParseStackNode* prev; + + TokenSlice tokens; + Token* start; + Str name; // The name of the AST node (if parsed) + Str proc_name; // The name of the procedure + Code code_rel; // Relevant AST node + // 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; + AllocatorInfo backing_tokens; + AllocatorInfo backing_ast; +}; + +ParseInfo wip_parse_str( LexedInfo lexed, ParseOpts* opts GEN_PARAM_DEFAULT ); + +GEN_API CodeClass parse_class ( Str class_def ); +GEN_API CodeConstructor parse_constructor ( Str constructor_def ); +GEN_API CodeDefine parse_define ( Str define_def ); +GEN_API CodeDestructor parse_destructor ( Str destructor_def ); +GEN_API CodeEnum parse_enum ( Str enum_def ); +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 ); +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 ); +GEN_API CodeStruct parse_struct ( Str struct_def ); +GEN_API CodeTemplate parse_template ( Str template_def ); +GEN_API CodeTypename parse_type ( Str type_def ); +GEN_API CodeTypedef parse_typedef ( Str typedef_def ); +GEN_API CodeUnion parse_union ( Str union_def ); +GEN_API CodeUsing parse_using ( Str using_def ); +GEN_API CodeVar parse_variable ( Str var_def ); + +#pragma endregion Parsing + +#pragma region Untyped text + +GEN_API ssize token_fmt_va( char* buf, usize buf_size, s32 num_tokens, va_list va ); +//! Do not use directly. Use the token_fmt macro instead. +Str token_fmt_impl( ssize, ... ); + +GEN_API Code untyped_str ( Str content); +GEN_API Code untyped_fmt ( char const* fmt, ... ); +GEN_API Code untyped_token_fmt( s32 num_tokens, char const* fmt, ... ); +GEN_API Code untyped_toks ( TokenSlice tokens ); + +#pragma endregion Untyped text + +#pragma region Macros + +#ifndef gen_main +#define gen_main main +#endif + +#ifndef name +// Convienence for defining any name used with the gen api. +// Lets you provide the length and string literal to the functions without the need for the DSL. +# if GEN_COMPILER_C +# define name( Id_ ) (Str){ stringize(Id_), sizeof(stringize( Id_ )) - 1 } +# else +# define name( Id_ ) Str { stringize(Id_), sizeof(stringize( Id_ )) - 1 } +# endif +#endif + +#ifndef code +// Same as name just used to indicate intention of literal for code instead of names. +# if GEN_COMPILER_C +# define code( ... ) (Str){ stringize( __VA_ARGS__ ), sizeof(stringize(__VA_ARGS__)) - 1 } +# else +# define code( ... ) Str { stringize( __VA_ARGS__ ), sizeof(stringize(__VA_ARGS__)) - 1 } +# endif +#endif + +#ifndef args +// Provides the number of arguments while passing args inplace. +#define args( ... ) num_args( __VA_ARGS__ ), __VA_ARGS__ +#endif + +#ifndef code_str +// Just wrappers over common untyped code definition constructions. +#define code_str( ... ) GEN_NS untyped_str( code( __VA_ARGS__ ) ) +#endif + +#ifndef code_fmt +#define code_fmt( ... ) GEN_NS untyped_str( token_fmt( __VA_ARGS__ ) ) +#endif + +#ifndef parse_fmt +#define parse_fmt( type, ... ) GEN_NS parse_##type( token_fmt( __VA_ARGS__ ) ) +#endif + +#ifndef token_fmt +/* +Takes a format string (char const*) and a list of tokens (Str) and returns a Str of the formatted string. +Tokens are provided in '<'identifier'>' format where '<' '>' are just angle brackets (you can change it in token_fmt_va) +--------------------------------------------------------- + Example - A string with: + typedef ; + Will have a token_fmt arguments populated with: + "type", str_for_type, + "name", str_for_name, + and: + stringize( typedef ; ) +----------------------------------------------------------- +So the full call for this example would be: + token_fmt( + "type", str_for_type + , "name", str_for_name + , stringize( + typedef + )); +!---------------------------------------------------------- +! Note: token_fmt_va is whitespace sensitive for the tokens. +! This can be alleviated by skipping whitespace between brackets but it was choosen to not have that implementation by default. +*/ +#define token_fmt( ... ) GEN_NS token_fmt_impl( (num_args( __VA_ARGS__ ) + 1) / 2, __VA_ARGS__ ) +#endif + +#pragma endregion Macros + +#pragma endregion Gen Interface diff --git a/tests/assets/gencpp_samples/components/interface.parsing.cpp b/tests/assets/gencpp_samples/components/interface.parsing.cpp new file mode 100644 index 0000000..51755f3 --- /dev/null +++ b/tests/assets/gencpp_samples/components/interface.parsing.cpp @@ -0,0 +1,473 @@ +#ifdef INTELLISENSE_DIRECTIVES +#pragma once +#include "gen/etoktype.hpp" +#include "interface.upfront.cpp" +#include "lexer.cpp" +#include "parser.cpp" +#endif + +// Publically Exposed Interface + +ParseInfo wip_parse_str(LexedInfo lexed, ParseOpts* opts) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + if (lexed.tokens.num == 0 && lexed.tokens.ptr == nullptr) { + check_parse_args(lexed.text); + lexed = lex(ctx, lexed.text); + } + ParseInfo info = struct_zero(ParseInfo); + info.lexed = lexed; + + // TODO(Ed): ParseInfo should be set to the parser context. + + ctx->parser = struct_zero(ParseContext); + ctx->parser.tokens = lexed.tokens; + + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope); + + CodeBody result = parse_global_nspace(ctx,CT_Global_Body); + + parser_pop(& ctx->parser); + return info; +} + +CodeClass parse_class( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope); + CodeClass result = (CodeClass) parse_class_struct( ctx, Tok_Decl_Class, parser_not_inplace_def ); + parser_pop(& ctx->parser); + return result; +} + +CodeConstructor parse_constructor(Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope); + + // TODO(Ed): Constructors can have prefix attributes + + CodeSpecifiers specifiers = NullCode; + Specifier specs_found[ 16 ] = { Spec_NumSpecifiers }; + s32 NumSpecifiers = 0; + + while ( left && tok_is_specifier(currtok) ) + { + Specifier spec = str_to_specifier( currtok.Text ); + + b32 ignore_spec = false; + + switch ( spec ) + { + case Spec_Constexpr : + case Spec_Explicit: + case Spec_Inline : + case Spec_ForceInline : + case Spec_NeverInline : + break; + + case Spec_Const : + ignore_spec = true; + break; + + default : + log_failure( "Invalid specifier %s for variable\n%S", spec_to_str( spec ), parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + parser_pop(& ctx->parser); + return InvalidCode; + } + + // Every specifier after would be considered part of the type type signature + if (ignore_spec) + break; + + specs_found[ NumSpecifiers ] = spec; + NumSpecifiers++; + eat( currtok.Type ); + } + + if ( NumSpecifiers ) { + specifiers = def_specifiers_arr( NumSpecifiers, specs_found ); + // ... + } + + CodeConstructor result = parser_parse_constructor(ctx, specifiers); + parser_pop(& ctx->parser); + return result; +} + +CodeDefine parse_define( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope); + CodeDefine result = parser_parse_define(ctx); + parser_pop(& ctx->parser); + return result; +} + +CodeDestructor parse_destructor( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + // TODO(Ed): Destructors can have prefix attributes + // TODO(Ed): Destructors can have virtual + + CodeDestructor result = parser_parse_destructor(ctx, NullCode); + return result; +} + +CodeEnum parse_enum( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) { + return InvalidCode; + } + + return parser_parse_enum(ctx, parser_not_inplace_def); +} + +CodeBody parse_export_body( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + return parser_parse_export_body(ctx); +} + +CodeExtern parse_extern_link( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + return parser_parse_extern_link(ctx); +} + +CodeFriend parse_friend( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + return parser_parse_friend(ctx); +} + +CodeFn parse_function( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + return (CodeFn) parser_parse_function(ctx); +} + +CodeBody parse_global_body( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope); + CodeBody result = parse_global_nspace(ctx, CT_Global_Body ); + parser_pop(& ctx->parser); + return result; +} + +CodeNS parse_namespace( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + return parser_parse_namespace(ctx); +} + +CodeOperator parse_operator( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + return (CodeOperator) parser_parse_operator(ctx); +} + +CodeOpCast parse_operator_cast( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + return parser_parse_operator_cast(ctx, NullCode); +} + +CodeStruct parse_struct( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope); + CodeStruct result = (CodeStruct) parse_class_struct( ctx, Tok_Decl_Struct, parser_not_inplace_def ); + parser_pop(& ctx->parser); + return result; +} + +CodeTemplate parse_template( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + return parser_parse_template(ctx); +} + +CodeTypename parse_type( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + return parser_parse_type( ctx, parser_not_from_template, nullptr); +} + +CodeTypedef parse_typedef( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + return parser_parse_typedef(ctx); +} + +CodeUnion parse_union( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + return parser_parse_union(ctx, parser_not_inplace_def); +} + +CodeUsing parse_using( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + return parser_parse_using(ctx); +} + +CodeVar parse_variable( Str def ) +{ + // TODO(Ed): Lift this. + Context* ctx = _ctx; + + check_parse_args( def ); + + ctx->parser = struct_zero(ParseContext); + + LexedInfo lexed = lex(ctx, def); + ctx->parser.tokens = lexed.tokens; + if ( ctx->parser.tokens.ptr == nullptr ) + return InvalidCode; + + return parser_parse_variable(ctx); +} + +// Undef helper macros +#undef check_parse_args +#undef currtok_noskip +#undef currtok +#undef peektok +#undef prevtok +#undef nexttok +#undef nexttok_noskip +#undef eat +#undef left +#undef check +#undef push_scope +#undef NullScope +#undef def_assign + +// Here for C Variant +#undef lex_dont_skip_formatting +#undef lex_skip_formatting + +#undef parser_inplace_def +#undef parser_not_inplace_def +#undef parser_dont_consume_braces +#undef parser_consume_braces +#undef parser_not_from_template +#undef parser_use_parenthesis +#undef parser_strip_formatting_dont_preserve_newlines diff --git a/tests/assets/gencpp_samples/components/interface.untyped.cpp b/tests/assets/gencpp_samples/components/interface.untyped.cpp new file mode 100644 index 0000000..14cfc4e --- /dev/null +++ b/tests/assets/gencpp_samples/components/interface.untyped.cpp @@ -0,0 +1,191 @@ +#ifdef INTELLISENSE_DIRECTIVES +#pragma once +#include "interface.parsing.cpp" +#endif + +ssize token_fmt_va( char* buf, usize buf_size, s32 num_tokens, va_list va ) +{ + char const* buf_begin = buf; + ssize remaining = buf_size; + + if (_ctx->token_fmt_map.Hashes == nullptr) { + _ctx->token_fmt_map = hashtable_init(Str, _ctx->Allocator_DyanmicContainers ); + } + // Populate token pairs + { + s32 left = num_tokens - 1; + + while ( left-- ) + { + char const* token = va_arg( va, char const* ); + Str value = va_arg( va, Str ); + + u32 key = crc32( token, c_str_len(token) ); + hashtable_set( _ctx->token_fmt_map, key, value ); + } + } + + char const* fmt = va_arg( va, char const* ); + char current = *fmt; + + while ( current ) + { + ssize len = 0; + + while ( current && current != '<' && remaining ) + { + * buf = * fmt; + buf++; + fmt++; + remaining--; + + current = * fmt; + } + + if ( current == '<' ) + { + char const* scanner = fmt + 1; + + s32 tok_len = 0; + + while ( *scanner != '>' ) + { + tok_len++; + scanner++; + } + + char const* token = fmt + 1; + + u32 key = crc32( token, tok_len ); + Str* value = hashtable_get(_ctx->token_fmt_map, key ); + + if ( value ) + { + ssize left = value->Len; + char const* str = value->Ptr; + + while ( left-- ) + { + * buf = * str; + buf++; + str++; + remaining--; + } + + scanner++; + fmt = scanner; + current = * fmt; + continue; + } + + * buf = * fmt; + buf++; + fmt++; + remaining--; + + current = * fmt; + } + } + hashtable_clear(_ctx->token_fmt_map); + ssize result = buf_size - remaining; + return result; +} + +Code untyped_str( Str content ) +{ + if ( content.Len == 0 ) + { + log_failure( "untyped_str: empty string" ); + return InvalidCode; + } + + Code + result = make_code(); + result->Name = cache_str( content ); + result->Type = CT_Untyped; + result->Content = result->Name; + + if ( result->Name.Len == 0 ) + { + log_failure( "untyped_str: could not cache string" ); + return InvalidCode; + } + + return result; +} + +Code untyped_fmt( char const* fmt, ...) +{ + if ( fmt == nullptr ) + { + log_failure( "untyped_fmt: null format string" ); + return InvalidCode; + } + + local_persist thread_local + char buf[GEN_PRINTF_MAXLEN] = { 0 }; + + va_list va; + va_start(va, fmt); + ssize length = c_str_fmt_va(buf, GEN_PRINTF_MAXLEN, fmt, va); + va_end(va); + Str content = { buf, length }; + + Code + result = make_code(); + result->Type = CT_Untyped; + result->Content = cache_str( content ); + + if ( result->Name.Len == 0 ) + { + log_failure( "untyped_fmt: could not cache string" ); + return InvalidCode; + } + + return result; +} + +Code untyped_token_fmt( s32 num_tokens, char const* fmt, ... ) +{ + if ( num_tokens == 0 ) + { + log_failure( "untyped_token_fmt: zero tokens" ); + return InvalidCode; + } + + local_persist thread_local + char buf[GEN_PRINTF_MAXLEN] = { 0 }; + + va_list va; + va_start(va, fmt); + ssize length = token_fmt_va(buf, GEN_PRINTF_MAXLEN, num_tokens, va); + va_end(va); + + Str buf_str = { buf, length }; + + Code + result = make_code(); + result->Type = CT_Untyped; + result->Content = cache_str( buf_str ); + + if ( result->Name.Len == 0 ) + { + log_failure( "untyped_fmt: could not cache string" ); + return InvalidCode; + } + + return result; +} + +Code untyped_toks( TokenSlice tokens ) +{ + if ( tokens.num == 0 ) { + log_failure( "untyped_toks: empty token slice" ); + return InvalidCode; + } + Code + result = make_code(); + result->Type = CT_Untyped; + result->ContentToks = tokens; + return result; +} diff --git a/tests/assets/gencpp_samples/components/interface.upfront.cpp b/tests/assets/gencpp_samples/components/interface.upfront.cpp new file mode 100644 index 0000000..b30864b --- /dev/null +++ b/tests/assets/gencpp_samples/components/interface.upfront.cpp @@ -0,0 +1,2138 @@ +#ifdef INTELLISENSE_DIRECTIVES +#pragma once +#include "interface.cpp" +#endif + +#pragma region Upfront + +enum OpValidateResult : u32 +{ + OpValResult_Fail, + OpValResult_Global, + OpValResult_Member +}; + +internal neverinline +OpValidateResult operator__validate( Operator op, CodeParams params_code, CodeTypename ret_type, CodeSpecifiers specifier ) +{ + if ( op == Op_Invalid ) + { + log_failure("gen::def_operator: op cannot be invalid"); + return OpValResult_Fail; + } + +#pragma region Helper Macros +# define check_params() \ + if ( ! params_code ) \ + { \ + log_failure("gen::def_operator: params is null and operator %S requires it", operator_to_str(op)); \ + return OpValResult_Fail; \ + } \ + if ( params_code->Type != CT_Parameters ) \ + { \ + log_failure("gen::def_operator: params is not of Parameters type - %S", code_debug_str( cast(Code, params_code))); \ + return OpValResult_Fail; \ + } + +# define check_param_eq_ret() \ + if ( ! is_member_symbol && ! code_is_equal(cast(Code, params_code->ValueType), cast(Code, ret_type)) ) \ + { \ + log_failure("gen::def_operator: operator %S requires first parameter to equal return type\n" \ + "param types: %S\n" \ + "return type: %S", \ + operator_to_str(op), \ + code_debug_str(cast(Code, params_code)), \ + code_debug_str(cast(Code, ret_type)) \ + ); \ + return OpValResult_Fail; \ + } +#pragma endregion Helper Macros + + if ( ! ret_type ) + { + log_failure("gen::def_operator: ret_type is null but is required by operator %S", operator_to_str(op)); + } + + if ( ret_type->Type != CT_Typename ) + { + log_failure("gen::def_operator: operator %S - ret_type is not of typename type - %S", + operator_to_str(op), + code_debug_str(cast(Code, ret_type)) + ); + return OpValResult_Fail; + } + + bool is_member_symbol = false; + + switch ( op ) + { +# define specs( ... ) num_args( __VA_ARGS__ ), __VA_ARGS__ + case Op_Assign: + check_params(); + + if ( params_code->NumEntries > 1 ) + { + log_failure("gen::def_operator: " + "operator %S does not support non-member definition (more than one parameter provided) - %S", + operator_to_str(op), + code_debug_str(cast(Code, params_code)) + ); + return OpValResult_Fail; + } + + is_member_symbol = true; + break; + + case Op_Assign_Add: + case Op_Assign_Subtract: + case Op_Assign_Multiply: + case Op_Assign_Divide: + case Op_Assign_Modulo: + case Op_Assign_BAnd: + case Op_Assign_BOr: + case Op_Assign_BXOr: + case Op_Assign_LShift: + case Op_Assign_RShift: + check_params(); + + if ( params_code->NumEntries == 1 ) + is_member_symbol = true; + + else + check_param_eq_ret(); + + if (params_code->NumEntries > 2 ) + { + log_failure("gen::def_operator: operator %S may not be defined with more than two parametes - param count; %d\n%S" + , operator_to_str(op) + , params_code->NumEntries + , code_debug_str(cast(Code, params_code)) + ); + return OpValResult_Fail; + } + break; + + case Op_Increment: + case Op_Decrement: + // If its not set, it just means its a prefix member op. + if ( params_code ) + { + if ( params_code->Type != CT_Parameters ) + { + log_failure("gen::def_operator: operator %S params code provided is not of Parameters type - %S" + , operator_to_str(op) + , code_debug_str(cast(Code, params_code)) + ); + return OpValResult_Fail; + } + + switch ( params_code->NumEntries ) + { + case 1: + if ( code_is_equal((Code)params_code->ValueType, (Code)t_int ) ) + is_member_symbol = true; + + else + check_param_eq_ret(); + break; + + case 2: + check_param_eq_ret(); + + if ( ! code_is_equal((Code)params_get(params_code, 1), (Code)t_int ) ) + { + log_failure("gen::def_operator: " + "operator %S requires second parameter of non-member definition to be int for post-decrement", + operator_to_str(op) + ); + return OpValResult_Fail; + } + break; + + default: + log_failure("gen::def_operator: operator %S recieved unexpected number of parameters recived %d instead of 0-2" + , operator_to_str(op) + , params_code->NumEntries + ); + return OpValResult_Fail; + } + } + break; + + case Op_Unary_Plus: + case Op_Unary_Minus: + if ( ! params_code ) + is_member_symbol = true; + + else + { + if ( params_code->Type != CT_Parameters ) + { + log_failure("gen::def_operator: params is not of Parameters type - %S", code_debug_str((Code)params_code)); + return OpValResult_Fail; + } + + if ( code_is_equal((Code)params_code->ValueType, (Code)ret_type ) ) + { + log_failure("gen::def_operator: " + "operator %S is non-member symbol yet first paramter does not equal return type\n" + "param type: %S\n" + "return type: %S\n" + , code_debug_str((Code)params_code) + , code_debug_str((Code)ret_type) + ); + return OpValResult_Fail; + } + + if ( params_code->NumEntries > 1 ) + { + log_failure("gen::def_operator: operator %S may not have more than one parameter - param count: %d" + , operator_to_str(op) + , params_code->NumEntries + ); + return OpValResult_Fail; + } + } + break; + + case Op_BNot: + { + // Some compilers let you do this... + #if 0 + if ( ! ret_type.is_equal( t_bool) ) + { + log_failure( "gen::def_operator: operator %S return type is not a boolean - %S", operator_to_str(op) code_debug_str(params_code) ); + return OpValidateResult::Fail; + } + #endif + + if ( ! params_code ) + is_member_symbol = true; + + else + { + if ( params_code->Type != CT_Parameters ) + { + log_failure( "gen::def_operator: operator %S - params is not of Parameters type - %S", operator_to_str(op), code_debug_str((Code)params_code) ); + return OpValResult_Fail; + } + + if ( params_code->NumEntries > 1 ) + { + log_failure( + "gen::def_operator: operator %S may not have more than one parameter - param count: %d", + operator_to_str( op ), + params_code->NumEntries + ); + return OpValResult_Fail; + } + } + break; + } + + case Op_Add: + case Op_Subtract: + case Op_Multiply: + case Op_Divide: + case Op_Modulo: + case Op_BAnd: + case Op_BOr: + case Op_BXOr: + case Op_LShift: + case Op_RShift: + check_params(); + + switch ( params_code->NumEntries ) + { + case 1: + is_member_symbol = true; + break; + + case 2: + // This is allowed for arithemtic operators + // if ( ! code_is_equal((Code)params_code->ValueType, (Code)ret_type ) ) + // { + // log_failure("gen::def_operator: " + // "operator %S is non-member symbol yet first paramter does not equal return type\n" + // "param type: %S\n" + // "return type: %S\n" + // , code_debug_str((Code)params_code) + // , code_debug_str((Code)ret_type) + // ); + // return OpValResult_Fail; + // } + break; + + default: + log_failure("gen::def_operator: operator %S recieved unexpected number of paramters recived %d instead of 0-2" + , operator_to_str(op) + , params_code->NumEntries + ); + return OpValResult_Fail; + } + break; + + case Op_UnaryNot: + if ( ! params_code ) + is_member_symbol = true; + + else + { + if ( params_code->Type != CT_Parameters ) + { + log_failure("gen::def_operator: operator %S - params is not of Parameters type - %S", operator_to_str(op), code_debug_str((Code)params_code)); + return OpValResult_Fail; + } + + if ( params_code->NumEntries != 1 ) + { + log_failure("gen::def_operator: operator %S recieved unexpected number of paramters recived %d instead of 0-1" + , operator_to_str(op) + , params_code->NumEntries + ); + return OpValResult_Fail; + } + } + + if ( ! code_is_equal((Code)ret_type, (Code)t_bool )) + { + log_failure("gen::def_operator: operator %S return type must be of type bool - %S" + , operator_to_str(op) + , code_debug_str((Code)ret_type) + ); + return OpValResult_Fail; + } + break; + + case Op_LAnd: + case Op_LOr: + case Op_LEqual: + case Op_LNot: + case Op_Lesser: + case Op_Greater: + case Op_LesserEqual: + case Op_GreaterEqual: + check_params(); + + switch ( params_code->NumEntries ) + { + case 1: + is_member_symbol = true; + break; + + case 2: + break; + + default: + log_failure("gen::def_operator: operator %S recieved unexpected number of paramters recived %d instead of 1-2" + , operator_to_str(op) + , params_code->NumEntries + ); + return OpValResult_Fail; + } + break; + + case Op_Indirection: + case Op_AddressOf: + case Op_MemberOfPointer: + if ( params_code && params_code->NumEntries > 1) + { + log_failure("gen::def_operator: operator %S recieved unexpected number of paramters recived %d instead of 0-1" + , operator_to_str(op) + , params_code->NumEntries + ); + return OpValResult_Fail; + } + else + { + is_member_symbol = true; + } + break; + + case Op_PtrToMemOfPtr: + if ( params_code ) + { + log_failure("gen::def_operator: operator %S expects no paramters - %S", operator_to_str(op), code_debug_str((Code)params_code)); + return OpValResult_Fail; + } + break; + + case Op_Subscript: + case Op_FunctionCall: + case Op_Comma: + check_params(); + break; + + case Op_New: + case Op_Delete: + // This library doesn't support validating new and delete yet. + break; +# undef specs + } + + return is_member_symbol ? OpValResult_Member : OpValResult_Global; +# undef check_params +# undef check_ret_type +# undef check_param_eq_ret +} + +forceinline +bool name__check( char const* context, Str name ) +{ + if ( name.Len <= 0 ) { + log_failure( "gen::%s: Invalid name length provided - %d", name.Len ); + return false; + } + if ( name.Ptr == nullptr ) { + log_failure( "gen::%s: name is null" ); + return false; + } + return true; +} +#define name_check( context, name ) name__check( #context, name ) + +forceinline +bool null__check( char const* context, char const* code_id, Code code ) { + if ( code == nullptr ) { + log_failure( "gen::%s: %s provided is null", context, code_id ); + return false; + } + return true; +} +#define null_check( context, code ) null__check( #context, #code, cast(Code, code) ) + +/* +The implementation of the upfront constructors involves doing three things: +* Validate the arguments given to construct the intended type of AST is valid. +* Construct said AST type. +* Lock the AST (set to readonly) and return the valid object. + +If any of the validation fails, it triggers a call to log_failure with as much info the give the user so that they can hopefully +identify the issue without having to debug too much (at least they can debug though...) + +The largest of the functions is related to operator overload definitions. +The library validates a good protion of their form and thus the argument processing for is quite a bit. +*/ +CodeAttributes def_attributes( Str content ) +{ + if ( content.Len <= 0 || content.Ptr == nullptr ) { + log_failure( "gen::def_attributes: Invalid attributes provided" ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + Code + result = make_code(); + result->Type = CT_PlatformAttributes; + result->Name = cache_str( content ); + result->Content = result->Name; + return (CodeAttributes) result; +} + +CodeComment def_comment( Str content ) +{ + if ( content.Len <= 0 || content.Ptr == nullptr ) + { + log_failure( "gen::def_comment: Invalid comment provided:" ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + + StrBuilder cmt_formatted = strbuilder_make_reserve( _ctx->Allocator_Temp, kilobytes(1) ); + char const* end = content.Ptr + content.Len; + char const* scanner = content.Ptr; + s32 curr = 0; + do + { + char const* next = scanner; + s32 length = 0; + while ( next != end && scanner[ length ] != '\n' ) + { + next = scanner + length; + length++; + } + length++; + + strbuilder_append_fmt(& cmt_formatted, "//%.*s", length, scanner ); + scanner += length; + } + while ( scanner <= end ); + + if ( * strbuilder_back(cmt_formatted) != '\n' ) + strbuilder_append_str( & cmt_formatted, txt("\n") ); + + Str name = strbuilder_to_str(cmt_formatted); + + Code + result = make_code(); + result->Type = CT_Comment; + result->Name = cache_str( name ); + result->Content = result->Name; + + strbuilder_free(& cmt_formatted); + + return (CodeComment) result; +} + +CodeConstructor def_constructor( Opts_def_constructor opt ) +{ + Opts_def_constructor p = get_optional(opt); + + if ( p.params && p.params->Type != CT_Parameters ) { + log_failure("gen::def_constructor: params must be of Parameters type - %s", code_debug_str((Code)p.params)); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + + CodeConstructor result = (CodeConstructor) make_code(); + if ( p.params ) { + result->Params = p.params; + } + if ( p.initializer_list ) { + result->InitializerList = p.initializer_list; + } + if ( p.body ) + { + switch ( p.body->Type ) { + case CT_Function_Body: + case CT_Untyped: + break; + + default: + log_failure("gen::def_constructor: body must be either of Function_Body or Untyped type - %s", code_debug_str(p.body)); + return InvalidCode; + } + + result->Type = CT_Constructor; + result->Body = p.body; + } + else + { + result->Type = CT_Constructor_Fwd; + } + return result; +} + +CodeClass def_class( Str name, Opts_def_struct opt ) +{ + Opts_def_struct p = get_optional(opt); + + if ( ! name_check( def_class, name ) ) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.attributes && p.attributes->Type != CT_PlatformAttributes ) { + log_failure( "gen::def_class: attributes was not a 'PlatformAttributes' type: %s", code_debug_str(p.attributes) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.parent && ( p.parent->Type != CT_Class && p.parent->Type != CT_Struct && p.parent->Type != CT_Typename && p.parent->Type != CT_Untyped ) ) { + log_failure( "gen::def_class: parent provided is not type 'Class', 'Struct', 'Typeanme', or 'Untyped': %s", code_debug_str(p.parent) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + + CodeClass + result = (CodeClass) make_code(); + 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 ) + { + switch ( p.body->Type ) + { + case CT_Class_Body: + case CT_Untyped: + break; + + default: + log_failure("gen::def_class: body must be either of Class_Body or Untyped type - %s", code_debug_str(p.body)); + return InvalidCode; + } + + result->Type = CT_Class; + result->Body = p.body; + result->Body->Parent = cast(Code, result); + } + else { + result->Type = CT_Class_Fwd; + } + for (s32 idx = 0; idx < p.num_interfaces; idx++ ) { + class_add_interface(result, p.interfaces[idx] ); + } + return result; +} + +CodeDefine def_define( Str name, MacroType type, Opts_def_define opt ) +{ + Opts_def_define p = get_optional(opt); + + if ( ! name_check( def_define, name ) ) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + CodeDefine + result = (CodeDefine) make_code(); + result->Type = CT_Preprocess_Define; + result->Name = cache_str( name ); + result->Params = p.params; + if ( p.content.Len <= 0 || p.content.Ptr == nullptr ) + result->Body = untyped_str( txt("\n") ); + else + result->Body = untyped_str( strbuilder_to_str(strbuilder_fmt_buf(_ctx->Allocator_Temp, "%S\n", p.content)) ); + + b32 register_define = ! p.dont_register_to_preprocess_macros; + if ( register_define ) { + Macro macro_entry = { result->Name, type, p.flags }; + register_macro(macro_entry); + } + return result; +} + +CodeDestructor def_destructor( Opts_def_destructor opt ) +{ + Opts_def_destructor p = get_optional(opt); + + if ( p.specifiers && p.specifiers->Type != CT_Specifiers ) { + log_failure( "gen::def_destructor: specifiers was not a 'Specifiers' type: %s", code_debug_str(p.specifiers) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + + CodeDestructor + result = (CodeDestructor) make_code(); + result->Specs = p.specifiers; + if ( p.body ) + { + switch ( p.body->Type ) + { + case CT_Function_Body: + case CT_Untyped: + break; + + default: + log_failure("gen::def_destructor: body must be either of Function_Body or Untyped type - %s", code_debug_str(p.body)); + return InvalidCode; + } + + result->Type = CT_Destructor; + result->Body = p.body; + } + else + { + result->Type = CT_Destructor_Fwd; + } + return result; +} + +CodeEnum def_enum( Str name, Opts_def_enum opt ) +{ + Opts_def_enum p = get_optional(opt); + + if ( ! name_check( def_enum, name ) ) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.type && p.type->Type != CT_Typename ) { + log_failure( "gen::def_enum: enum underlying type provided was not of type Typename: %s", code_debug_str(p.type) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.attributes && p.attributes->Type != CT_PlatformAttributes ) { + log_failure( "gen::def_enum: attributes was not a 'PlatformAttributes' type: %s", code_debug_str(p.attributes) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + + CodeEnum + result = (CodeEnum) make_code(); + result->Name = cache_str( name ); + result->ModuleFlags = p.mflags; + if ( p.body ) + { + switch ( p.body->Type ) + { + case CT_Enum_Body: + case CT_Untyped: + break; + + default: + log_failure( "gen::def_enum: body must be of Enum_Body or Untyped type %s", code_debug_str(p.body)); + return InvalidCode; + } + + result->Type = p.specifier == EnumDecl_Class ? + CT_Enum_Class : CT_Enum; + + result->Body = p.body; + } + else + { + result->Type = p.specifier == EnumDecl_Class ? + CT_Enum_Class_Fwd : CT_Enum_Fwd; + } + result->Attributes = p.attributes; + + if ( p.type ) { + result->UnderlyingType = p.type; + } + else if ( p.type_macro ) { + result->UnderlyingTypeMacro = p.type_macro; + } + else if ( result->Type != CT_Enum_Class_Fwd && result->Type != CT_Enum_Fwd ) + { + log_failure( "gen::def_enum: enum forward declaration must have an underlying type" ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + return result; +} + +CodeExec def_execution( Str content ) +{ + if ( content.Len <= 0 || content.Ptr == nullptr ) { + log_failure( "gen::def_execution: Invalid execution provided" ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + CodeExec + result = (CodeExec) make_code(); + result->Type = CT_Execution; + result->Content = cache_str( content ); + return result; +} + +CodeExtern def_extern_link( Str name, CodeBody body ) +{ + if ( ! name_check(def_extern_link, name) || ! null_check(def_extern_link, body) ) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( body->Type != CT_Extern_Linkage_Body && body->Type != CT_Untyped ) { + log_failure("gen::def_extern_linkage: body is not of extern_linkage or untyped type %s", code_debug_str(body)); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + CodeExtern + result = (CodeExtern)make_code(); + result->Type = CT_Extern_Linkage; + result->Name = cache_str( name ); + result->Body = body; + return result; +} + +CodeFriend def_friend( Code declaration ) +{ + if ( ! null_check( def_friend, declaration ) ) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + switch ( declaration->Type ) + { + case CT_Class_Fwd: + case CT_Function_Fwd: + case CT_Operator_Fwd: + case CT_Struct_Fwd: + case CT_Class: + case CT_Function: + case CT_Operator: + case CT_Struct: + break; + + default: + log_failure("gen::def_friend: requires declartion to have class, function, operator, or struct - %s", code_debug_str(declaration)); + return InvalidCode; + } + CodeFriend + result = (CodeFriend) make_code(); + result->Type = CT_Friend; + result->Declaration = declaration; + return result; +} + +CodeFn def_function( Str name, Opts_def_function opt ) +{ + Opts_def_function p = get_optional(opt); + + if ( ! name_check( def_function, name )) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.params && p.params->Type != CT_Parameters ) { + log_failure( "gen::def_function: params was not a `Parameters` type: %s", code_debug_str(p.params) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.ret_type && p.ret_type->Type != CT_Typename ) { + log_failure( "gen::def_function: ret_type was not a Typename: %s", code_debug_str(p.ret_type) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.specs && p.specs-> Type != CT_Specifiers ) { + log_failure( "gen::def_function: specifiers was not a `Specifiers` type: %s", code_debug_str(p.specs) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.attrs && p.attrs->Type != CT_PlatformAttributes ) { + log_failure( "gen::def_function: attributes was not a `PlatformAttributes` type: %s", code_debug_str(p.attrs) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + + CodeFn + result = (CodeFn) make_code(); + result->Name = cache_str( name ); + result->ModuleFlags = p.mflags; + if ( p.body ) + { + switch ( p.body->Type ) + { + case CT_Function_Body: + case CT_Execution: + case CT_Untyped: + break; + + default: + { + log_failure("gen::def_function: body must be either of Function_Body, Execution, or Untyped type. %s", code_debug_str(p.body)); + return InvalidCode; + } + } + result->Type = CT_Function; + result->Body = p.body; + } + else + { + result->Type = CT_Function_Fwd; + } + result->Attributes = p.attrs; + result->Specs = p.specs; + result->Params = p.params; + result->ReturnType = p.ret_type ? p.ret_type : t_void; + return result; +} + +CodeInclude def_include( Str path, Opts_def_include opt ) +{ + Opts_def_include p = get_optional(opt); + + if ( path.Len <= 0 || path.Ptr == nullptr ) { + log_failure( "gen::def_include: Invalid path provided - %d" ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + StrBuilder content = p.foreign ? + strbuilder_fmt_buf( _ctx->Allocator_Temp, "<%.*s>", path.Len, path.Ptr ) + : strbuilder_fmt_buf( _ctx->Allocator_Temp, "\"%.*s\"", path.Len, path.Ptr ); + + CodeInclude + result = (CodeInclude) make_code(); + result->Type = CT_Preprocess_Include; + result->Name = cache_str( strbuilder_to_str(content) ); + result->Content = result->Name; + return result; +} + +CodeModule def_module( Str name, Opts_def_module opt ) +{ + Opts_def_module p = get_optional(opt); + + if ( ! name_check( def_module, name )) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + CodeModule + result = (CodeModule) make_code(); + result->Type = CT_Module; + result->Name = cache_str( name ); + result->ModuleFlags = p.mflags; + return result; +} + +CodeNS def_namespace( Str name, CodeBody body, Opts_def_namespace opt ) +{ + Opts_def_namespace p = get_optional(opt); + + if ( ! name_check( def_namespace, name )) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( ! null_check( def_namespace, body)) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( body && body->Type != CT_Namespace_Body && body->Type != CT_Untyped ) { + log_failure("gen::def_namespace: body is not of namespace or untyped type %s", code_debug_str(body)); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + CodeNS + result = (CodeNS) make_code(); + result->Type = CT_Namespace; + result->Name = cache_str( name ); + result->ModuleFlags = p.mflags; + result->Body = body; + return result; +} + +CodeOperator def_operator( Operator op, Str nspace, Opts_def_operator opt ) +{ + Opts_def_operator p = get_optional(opt); + + if ( p.attributes && p.attributes->Type != CT_PlatformAttributes ) { + log_failure( "gen::def_operator: PlatformAttributes was provided but its not of attributes type: %s", code_debug_str(p.attributes) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.specifiers && p.specifiers->Type != CT_Specifiers ) { + log_failure( "gen::def_operator: Specifiers was provided but its not of specifiers type: %s", code_debug_str(p.specifiers) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + + OpValidateResult check_result = operator__validate( op, p.params, p.ret_type, p.specifiers ); + if ( check_result == OpValResult_Fail ) { + return InvalidCode; + } + + char const* name = nullptr; + + Str op_str = operator_to_str( op ); + if ( nspace.Len > 0 ) + name = c_str_fmt_buf( "%.*soperator %.*s", nspace.Len, nspace.Ptr, op_str.Len, op_str.Ptr ); + else + name = c_str_fmt_buf( "operator %.*s", op_str.Len, op_str.Ptr ); + + Str name_resolved = { name, c_str_len(name) }; + + CodeOperator + result = (CodeOperator) make_code(); + result->Name = cache_str( name_resolved ); + result->ModuleFlags = p.mflags; + result->Op = op; + if ( p.body ) + { + switch ( p.body->Type ) + { + case CT_Function_Body: + case CT_Execution: + case CT_Untyped: + break; + + default: + { + log_failure("gen::def_operator: body must be either of Function_Body, Execution, or Untyped type. %s", code_debug_str(p.body)); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + } + + result->Type = check_result == OpValResult_Global ? + CT_Operator : CT_Operator_Member; + + result->Body = p.body; + } + else + { + result->Type = check_result == OpValResult_Global ? + CT_Operator_Fwd : CT_Operator_Member_Fwd; + } + result->Attributes = p.attributes; + result->Specs = p.specifiers; + result->ReturnType = p.ret_type; + result->Params = p.params; + return result; +} + +CodeOpCast def_operator_cast( CodeTypename type, Opts_def_operator_cast opt ) +{ + Opts_def_operator_cast p = get_optional(opt); + + if ( ! null_check( def_operator_cast, type )) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( type->Type != CT_Typename ) { + log_failure( "gen::def_operator_cast: type is not a typename - %s", code_debug_str(type) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + + CodeOpCast result = (CodeOpCast) make_code(); + if (p.body) + { + result->Type = CT_Operator_Cast; + + if ( p.body->Type != CT_Function_Body && p.body->Type != CT_Execution ) { + log_failure( "gen::def_operator_cast: body is not of function body or execution type - %s", code_debug_str(p.body) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + result->Body = p.body; + } + else + { + result->Type = CT_Operator_Cast_Fwd; + } + result->Specs = p.specs; + result->ValueType = type; + return result; +} + +CodeParams def_param( CodeTypename type, Str name, Opts_def_param opt ) +{ + Opts_def_param p = get_optional(opt); + + if ( ! name_check( def_param, name ) || ! null_check( def_param, type ) ) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( type->Type != CT_Typename ) { + log_failure( "gen::def_param: type is not a typename - %s", code_debug_str(type) ); + return InvalidCode; + } + if ( p.value && p.value->Type != CT_Untyped ) { + log_failure( "gen::def_param: value is not untyped - %s", code_debug_str(p.value) ); + return InvalidCode; + } + CodeParams + result = (CodeParams) make_code(); + result->Type = CT_Parameters; + result->Name = cache_str( name ); + result->ValueType = type; + result->Value = p.value; + result->NumEntries++; + return result; +} + +CodePragma def_pragma( Str directive ) +{ + if ( directive.Len <= 0 || directive.Ptr == nullptr ) { + log_failure( "gen::def_comment: Invalid comment provided:" ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + CodePragma + result = (CodePragma) make_code(); + result->Type = CT_Preprocess_Pragma; + result->Content = cache_str( directive ); + return result; +} + +CodePreprocessCond def_preprocess_cond( EPreprocessCond type, Str expr ) +{ + if ( expr.Len <= 0 || expr.Ptr == nullptr ) { + log_failure( "gen::def_comment: Invalid comment provided:" ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + CodePreprocessCond + result = (CodePreprocessCond) make_code(); + result->Content = cache_str( expr ); + switch (type) + { + case PreprocessCond_If: + result->Type = CT_Preprocess_If; + break; + case PreprocessCond_IfDef: + result->Type = CT_Preprocess_IfDef; + break; + case PreprocessCond_IfNotDef: + result->Type = CT_Preprocess_IfNotDef; + break; + case PreprocessCond_ElIf: + result->Type = CT_Preprocess_ElIf; + break; + } + return result; +} + +CodeSpecifiers def_specifier( Specifier spec ) +{ + CodeSpecifiers + result = (CodeSpecifiers) make_code(); + result->Type = CT_Specifiers; + specifiers_append(result, spec ); + return result; +} + +CodeStruct def_struct( Str name, Opts_def_struct opt ) +{ + Opts_def_struct p = get_optional(opt); + + if ( p.attributes && p.attributes->Type != CT_PlatformAttributes ) { + log_failure( "gen::def_struct: attributes was not a `PlatformAttributes` type - %s", code_debug_str(cast(Code, p.attributes)) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.parent && p.parent->Type != CT_Typename ) { + log_failure( "gen::def_struct: parent was not a `Struct` type - %s", code_debug_str(p.parent) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.body && p.body->Type != CT_Struct_Body ) { + log_failure( "gen::def_struct: body was not a Struct_Body type - %s", code_debug_str(p.body) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + + CodeStruct + result = (CodeStruct) make_code(); + result->ModuleFlags = p.mflags; + if ( name.Len ) + result->Name = cache_str( name ); + + if ( p.body ) { + result->Type = CT_Struct; + result->Body = p.body; + } + else { + result->Type = CT_Struct_Fwd; + } + result->Attributes = p.attributes; + result->Specs = p.specifiers; + result->ParentAccess = p.parent_access; + result->ParentType = p.parent; + + for (s32 idx = 0; idx < p.num_interfaces; idx++ ) { + struct_add_interface(result, p.interfaces[idx] ); + } + return result; +} + +CodeTemplate def_template( CodeParams params, Code declaration, Opts_def_template opt ) +{ + Opts_def_template p = get_optional(opt); + + if ( ! null_check( def_template, declaration ) ) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( params && params->Type != CT_Parameters ){ + log_failure( "gen::def_template: params is not of parameters type - %s", code_debug_str(params) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + switch (declaration->Type ) + { + case CT_Class: + case CT_Function: + case CT_Struct: + case CT_Variable: + case CT_Using: + break; + + default: + log_failure( "gen::def_template: declaration is not of class, function, struct, variable, or using type - %s", code_debug_str(declaration) ); + } + CodeTemplate + result = (CodeTemplate) make_code(); + result->Type = CT_Template; + result->ModuleFlags = p.mflags; + result->Params = params; + result->Declaration = declaration; + return result; +} + +CodeTypename def_type( Str name, Opts_def_type opt ) +{ + Opts_def_type p = get_optional(opt); + + if ( ! name_check( def_type, name )) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + Code array_expr = p.array_expr; + CodeSpecifiers specifiers = p.specifiers; + CodeAttributes attributes = p.attributes; + if ( p.attributes && p.attributes->Type != CT_PlatformAttributes ) { + log_failure( "gen::def_type: attributes is not of attributes type - %s", code_debug_str((Code)p.attributes) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.specifiers && p.specifiers->Type != CT_Specifiers ) { + log_failure( "gen::def_type: specifiers is not of specifiers type - %s", code_debug_str((Code)p.specifiers) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.array_expr && p.array_expr->Type != CT_Untyped ) { + log_failure( "gen::def_type: arrayexpr is not of untyped type - %s", code_debug_str((Code)p.array_expr) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + CodeTypename + result = (CodeTypename) make_code(); + result->Name = cache_str( name ); + result->Type = CT_Typename; + result->Attributes = p.attributes; + result->Specs = p.specifiers; + result->ArrExpr = p.array_expr; + result->TypeTag = p.type_tag; + return result; +} + +CodeTypedef def_typedef( Str name, Code type, Opts_def_typedef opt ) +{ + Opts_def_typedef p = get_optional(opt); + + if ( ! null_check( def_typedef, type ) ) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + switch ( type->Type ) + { + case CT_Class: + case CT_Class_Fwd: + case CT_Enum: + case CT_Enum_Fwd: + case CT_Enum_Class: + case CT_Enum_Class_Fwd: + case CT_Function_Fwd: + case CT_Struct: + case CT_Struct_Fwd: + case CT_Union: + case CT_Typename: + break; + default: + log_failure( "gen::def_typedef: type was not a Class, Enum, Function Forward, Struct, Typename, or Union - %s", code_debug_str((Code)type) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.attributes && p.attributes->Type != CT_PlatformAttributes ) { + log_failure( "gen::def_typedef: attributes was not a PlatformAttributes - %s", code_debug_str((Code)p.attributes) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + + // Registering the type. + CodeTypename registered_type = def_type( name ); + if ( ! registered_type ) { + log_failure( "gen::def_typedef: failed to register type" ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + + CodeTypedef + result = (CodeTypedef) make_code(); + result->Type = CT_Typedef; + result->ModuleFlags = p.mflags; + result->UnderlyingType = type; + + if ( name.Len <= 0 ) + { + if (type->Type != CT_Untyped) { + log_failure( "gen::def_typedef: name was empty and type was not untyped (indicating its a function typedef) - %s", code_debug_str(type) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + result->Name = cache_str( type->Name ); + result->IsFunction = true; + } + else + { + result->Name = cache_str( name ); + result->IsFunction = false; + } + return result; +} + +CodeUnion def_union( Str name, CodeBody body, Opts_def_union opt ) +{ + Opts_def_union p = get_optional(opt); + + if ( ! null_check( def_union, body ) ) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( body->Type != CT_Union_Body ) { + log_failure( "gen::def_union: body was not a Union_Body type - %s", code_debug_str(body) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.attributes && p.attributes->Type != CT_PlatformAttributes ) { + log_failure( "gen::def_union: attributes was not a PlatformAttributes type - %s", code_debug_str(p.attributes) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + CodeUnion + result = (CodeUnion) make_code(); + result->ModuleFlags = p.mflags; + result->Type = CT_Union; + result->Body = body; + result->Attributes = p.attributes; + if ( name.Ptr ) + result->Name = cache_str( name ); + return result; +} + +CodeUsing def_using( Str name, CodeTypename type, Opts_def_using opt ) +{ + Opts_def_using p = get_optional(opt); + + if ( ! name_check( def_using, name ) || null_check( def_using, type ) ) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + + CodeTypename register_type = def_type( name ); + if ( ! register_type ) { + log_failure( "gen::def_using: failed to register type" ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.attributes && p.attributes->Type != CT_PlatformAttributes ) { + log_failure( "gen::def_using: attributes was not a PlatformAttributes type - %s", code_debug_str(p.attributes) ); + GEN_DEBUG_TRAP(); + return InvalidCode; + } + CodeUsing + result = (CodeUsing) make_code(); + result->Name = cache_str( name ); + result->ModuleFlags = p.mflags; + result->Type = CT_Using; + result->UnderlyingType = type; + result->Attributes = p.attributes; + return result; +} + +CodeUsing def_using_namespace( Str name ) +{ + if ( ! name_check( def_using_namespace, name ) ) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + CodeUsing + result = (CodeUsing) make_code(); + result->Name = cache_str( name ); + result->Type = CT_Using_Namespace; + return result; +} + +CodeVar def_variable( CodeTypename type, Str name, Opts_def_variable opt ) +{ + Opts_def_variable p = get_optional(opt); + + if ( ! name_check( def_variable, name ) || ! null_check( def_variable, type ) ) { + GEN_DEBUG_TRAP(); + return InvalidCode; + } + if ( p.attributes && p.attributes->Type != CT_PlatformAttributes ) + { + log_failure( "gen::def_variable: attributes was not a `PlatformAttributes` type - %s", code_debug_str(p.attributes) ); + return InvalidCode; + } + if ( p.specifiers && p.specifiers->Type != CT_Specifiers ) + { + log_failure( "gen::def_variable: specifiers was not a `Specifiers` type - %s", code_debug_str(p.specifiers) ); + return InvalidCode; + } + if ( type->Type != CT_Typename ) + { + log_failure( "gen::def_variable: type was not a Typename - %s", code_debug_str(type) ); + return InvalidCode; + } + if ( p.value && p.value->Type != CT_Untyped ) + { + log_failure( "gen::def_variable: value was not a `Untyped` type - %s", code_debug_str(p.value) ); + return InvalidCode; + } + CodeVar + result = (CodeVar) make_code(); + result->Name = cache_str( name ); + result->Type = CT_Variable; + result->ModuleFlags = p.mflags; + result->ValueType = type; + result->Attributes = p.attributes; + result->Specs = p.specifiers; + result->Value = p.value; + return result; +} + +#pragma region Helper Macros for def_**_body functions +#define def_body_start( Name_ ) \ +if ( num <= 0 ) \ +{ \ + log_failure("gen::" stringize(Name_) ": num cannot be zero or negative"); \ + return InvalidCode; \ +} + +#define def_body_code_array_start( Name_ ) \ +if ( num <= 0 ) \ +{ \ + log_failure("gen::" stringize(Name_) ": num cannot be zero or negative"); \ + return InvalidCode; \ +} \ + \ +if ( codes == nullptr ) \ +{ \ + log_failure("gen::" stringize(Name_)" : Provided a null array of codes"); \ + return InvalidCode; \ +} + +#pragma endregion Helper Macros for def_**_body functions + +CodeBody def_class_body( s32 num, ... ) +{ + def_body_start( def_class_body ); + + CodeBody result = ( CodeBody )make_code(); + result->Type = CT_Class_Body; + + va_list va; + va_start( va, num ); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + if ( ! entry) { + log_failure("gen::" + "def_class_body" + ": Provided an null entry"); + return InvalidCode; + } + switch (entry->Type) + { + GEN_AST_BODY_CLASS_UNALLOWED_TYPES_CASES: + log_failure("gen::" "def_class_body" ": Entry type is not allowed: %s", code_debug_str(entry)); + return InvalidCode; + + default: + break; + } + body_append(result, entry); + } + while (num--, num > 0); + va_end(va); + + return result; +} + +CodeBody def_class_body_arr( s32 num, Code* codes ) +{ + def_body_code_array_start( def_class_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Function_Body; + do + { + Code entry = *codes; + codes++; + if ( ! entry) { + log_failure("gen::" "def_class_body" ": Provided an null entry"); + return InvalidCode; + } + switch (entry->Type) + { + GEN_AST_BODY_CLASS_UNALLOWED_TYPES_CASES: + log_failure("gen::" "def_class_body" ": Entry type is not allowed: %s", code_debug_str(entry)); + return InvalidCode; + + default: + break; + } + body_append(result, entry); + } + while (num--, num > 0); + + return result; +} + +CodeDefineParams def_define_params( s32 num, ... ) +{ + def_body_start( def_define_params ); + + va_list va; + va_start(va, num); + + Code_POD pod = va_arg(va, Code_POD); + CodeDefineParams param = pcast( CodeDefineParams, pod ); + + null_check( def_define_params, param ); + if ( param->Type != CT_Parameters_Define ) { + log_failure( "gen::def_define_params: param %d is not a parameter for a preprocessor define", num - num + 1 ); + return InvalidCode; + } + + CodeDefineParams result = (CodeDefineParams) code_duplicate(param); + while ( -- num ) + { + pod = va_arg(va, Code_POD); + param = pcast( CodeDefineParams, pod ); + if ( param->Type != CT_Parameters_Define ) { + log_failure( "gen::def_define_params: param %d is not a parameter for a preprocessor define", num - num + 1 ); + return InvalidCode; + } + define_params_append(result, param ); + } + va_end(va); + + return result; +} + +CodeDefineParams def_define_params_arr( s32 num, CodeDefineParams* codes ) +{ + def_body_code_array_start( def_define_params ); + +# define check_current(current) \ + if ( current == nullptr ) { \ + log_failure("gen::def_define_params: Provide a null code in codes array"); \ + return InvalidCode; \ + } \ + if (current->Type != CT_Parameters_Define ) { \ + log_failure("gen::def_define_params: Code in coes array is not of paramter for preprocessor define type - %s", code_debug_str(current) ); \ + return InvalidCode; \ + } + CodeDefineParams current = (CodeDefineParams)code_duplicate(* codes); + check_current(current); + + CodeDefineParams + result = (CodeDefineParams) make_code(); + result->Name = current->Name; + result->Type = current->Type; + while( codes++, current = * codes, num--, num > 0 ) { + check_current(current); + define_params_append(result, current ); + } +# undef check_current + + return result; +} + +CodeBody def_enum_body( s32 num, ... ) +{ + def_body_start( def_enum_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Enum_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + if ( ! entry ) { + log_failure("gen::def_enum_body: Provided a null entry"); + return InvalidCode; + } + if ( entry->Type != CT_Untyped && entry->Type != CT_Comment ) { + log_failure("gen::def_enum_body: Entry type is not allowed - %s. Must be of untyped or comment type.", code_debug_str(entry) ); + return InvalidCode; + } + body_append(result, entry ); + } + while ( num--, num > 0 ); + va_end(va); + + return (CodeBody) result; +} + +CodeBody def_enum_body_arr( s32 num, Code* codes ) +{ + def_body_code_array_start( def_enum_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Enum_Body; + do + { + Code entry = *codes; + if ( ! entry ) { + log_failure("gen::def_enum_body: Provided a null entry"); + return InvalidCode; + } + if ( entry->Type != CT_Untyped && entry->Type != CT_Comment ) { + log_failure("gen::def_enum_body: Entry type is not allowed: %s", code_debug_str(entry) ); + return InvalidCode; + } + body_append(result, entry ); + } + while ( codes++, num--, num > 0 ); + + return result; +} + +CodeBody def_export_body( s32 num, ... ) +{ + def_body_start( def_export_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Export_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + if ( ! entry) + { + log_failure("gen::" "def_export_body" ": Provided an null entry"); + return InvalidCode; + } + switch (entry->Type) + { + GEN_AST_BODY_EXPORT_UNALLOWED_TYPES_CASES: + log_failure("gen::" "def_export_body" ": Entry type is not allowed: %s", code_debug_str(entry)); + return InvalidCode; + + default: + break; + } + body_append(result, entry); + } + while (num--, num > 0); + va_end(va); + + return result; +} + +CodeBody def_export_body_arr( s32 num, Code* codes ) +{ + def_body_code_array_start( def_export_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Export_Body; + do + { + Code entry = *codes; + codes++; + if ( ! entry) { + log_failure("gen::" "def_export_body" ": Provided an null entry"); + return InvalidCode; + } + switch (entry->Type) + { + GEN_AST_BODY_EXPORT_UNALLOWED_TYPES_CASES: + log_failure("gen::" "def_export_body" ": Entry type is not allowed: %s", code_debug_str(entry)); + return InvalidCode; + + default: + break; + } + body_append(result, entry); + } + while (num--, num > 0); + + return result; +} + +CodeBody def_extern_link_body( s32 num, ... ) +{ + def_body_start( def_extern_linkage_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Extern_Linkage_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + if ( ! entry) { + log_failure("gen::" "def_extern_linkage_body" ": Provided an null entry"); + return InvalidCode; + } + switch (entry->Type) + { + GEN_AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES_CASES: + log_failure("gen::" "def_extern_linkage_body" ": Entry type is not allowed: %s", code_debug_str(entry)); + return InvalidCode; + + default: + break; + } + body_append(result, entry); + } + while (num--, num > 0); + va_end(va); + + return result; +} + +CodeBody def_extern_link_body_arr( s32 num, Code* codes ) +{ + def_body_code_array_start( def_extern_linkage_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Extern_Linkage_Body; + do + { + Code entry = *codes; + codes++; + if (!entry) + { + log_failure("gen::" "def_extern_linkage_body" ": Provided an null entry"); + return InvalidCode; + } + switch (entry->Type) + { + GEN_AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES_CASES: + log_failure("gen::" "def_extern_linkage_body" ": Entry type is not allowed: %s", code_debug_str(entry)); + return InvalidCode; + + default: + break; + } + body_append(result, entry); + } + while (num--, num > 0); + + return result; +} + +CodeBody def_function_body( s32 num, ... ) +{ + def_body_start( def_function_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Function_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + if ( ! entry) { + log_failure("gen::" stringize(def_function_body) ": Provided an null entry"); + return InvalidCode; + } + switch (entry->Type) + { + GEN_AST_BODY_FUNCTION_UNALLOWED_TYPES_CASES: + log_failure("gen::" stringize(def_function_body) ": Entry type is not allowed: %s", code_debug_str(entry)); + return InvalidCode; + + default: + break; + } + body_append(result, entry); + } + while (num--, num > 0); + va_end(va); + + return result; +} + +CodeBody def_function_body_arr( s32 num, Code* codes ) +{ + def_body_code_array_start( def_function_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Function_Body; + do + { + Code entry = *codes; + codes++; + if (!entry) { + log_failure("gen::" "def_function_body" ": Provided an null entry"); + return InvalidCode; + } + switch (entry->Type) + { + GEN_AST_BODY_FUNCTION_UNALLOWED_TYPES_CASES: + log_failure("gen::" "def_function_body" ": Entry type is not allowed: %s", code_debug_str(entry)); + return InvalidCode; + + default: + break; + } + body_append(result, entry); + } + while (num--, num > 0); + + return result; +} + +CodeBody def_global_body( s32 num, ... ) +{ + def_body_start( def_global_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Global_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + if ( ! entry) { + log_failure("gen::" "def_global_body" ": Provided an null entry"); + return InvalidCode; + } + switch (entry->Type) + { + case CT_Global_Body: + // result.body_append( entry.code_cast() ) ; + body_append_body( result, cast(CodeBody, entry) ); + continue; + + GEN_AST_BODY_GLOBAL_UNALLOWED_TYPES_CASES: + log_failure("gen::" "def_global_body" ": Entry type is not allowed: %s", code_debug_str(entry)); + return InvalidCode; + + default: + break; + } + body_append(result, entry); + } + while (num--, num > 0); + va_end(va); + + return result; +} + +CodeBody def_global_body_arr( s32 num, Code* codes ) +{ + def_body_code_array_start( def_global_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Global_Body; + do + { + Code entry = *codes; + codes++; + if ( ! entry) { + log_failure("gen::" "def_global_body" ": Provided an null entry"); + return InvalidCode; + } + switch (entry->Type) + { + case CT_Global_Body: + body_append_body(result, cast(CodeBody, entry) ); + continue; + + GEN_AST_BODY_GLOBAL_UNALLOWED_TYPES_CASES: + log_failure("gen::" "def_global_body" ": Entry type is not allowed: %s", code_debug_str(entry)); + return InvalidCode; + + default: + break; + } + + body_append(result, entry); + } + while (num--, num > 0); + + return result; +} + +CodeBody def_namespace_body( s32 num, ... ) +{ + def_body_start( def_namespace_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Namespace_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + if ( ! entry) { + log_failure("gen::" "def_namespace_body" ": Provided an null entry"); + return InvalidCode; + } + switch (entry->Type) + { + GEN_AST_BODY_NAMESPACE_UNALLOWED_TYPES_CASES: + log_failure("gen::" "def_namespace_body" ": Entry type is not allowed: %s", code_debug_str(entry)); + return InvalidCode; + + default: + break; + } + body_append(result, entry); + } + while (num--, num > 0); + va_end(va); + + return result; +} + +CodeBody def_namespace_body_arr( s32 num, Code* codes ) +{ + def_body_code_array_start( def_namespace_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Global_Body; + do + { + Code entry = *codes; + codes++; + if ( ! entry) { + log_failure("gen::" "def_namespace_body" ": Provided an null entry"); + return InvalidCode; + } + switch (entry->Type) + { + GEN_AST_BODY_NAMESPACE_UNALLOWED_TYPES_CASES: + log_failure("gen::" "def_namespace_body" ": Entry type is not allowed: %s", code_debug_str(entry) ); + return InvalidCode; + + default: break; + } + body_append(result, entry); + } + while (num--, num > 0); + + return result; +} + +CodeParams def_params( s32 num, ... ) +{ + def_body_start( def_params ); + + va_list va; + va_start(va, num); + + Code_POD pod = va_arg(va, Code_POD); + CodeParams param = pcast( CodeParams, pod ); + + null_check( def_params, param ); + if ( param->Type != CT_Parameters ) { + log_failure( "gen::def_params: param %d is not a Parameters", num - num + 1 ); + return InvalidCode; + } + + CodeParams result = (CodeParams) code_duplicate(param); + while ( -- num ) + { + pod = va_arg(va, Code_POD); + param = pcast( CodeParams, pod ); + if ( param->Type != CT_Parameters ) { + log_failure( "gen::def_params: param %d is not a Parameters", num - num + 1 ); + return InvalidCode; + } + params_append(result, param ); + } + va_end(va); + + return result; +} + +CodeParams def_params_arr( s32 num, CodeParams* codes ) +{ + def_body_code_array_start( def_params ); + +# define check_current(current) \ + if ( current == nullptr ) { \ + log_failure("gen::def_params: Provide a null code in codes array"); \ + return InvalidCode; \ + } \ + if (current->Type != CT_Parameters ) { \ + log_failure("gen::def_params: Code in coes array is not of paramter type - %s", code_debug_str(current) ); \ + return InvalidCode; \ + } + CodeParams current = (CodeParams)code_duplicate(* codes); + check_current(current); + + CodeParams + result = (CodeParams) make_code(); + result->Name = current->Name; + result->Type = current->Type; + result->ValueType = current->ValueType; + while( codes++, current = * codes, num--, num > 0 ) { + check_current(current); + params_append(result, current ); + } +# undef check_current + + return result; +} + +CodeSpecifiers def_specifiers( s32 num, ... ) +{ + if ( num <= 0 ) { + log_failure("gen::def_specifiers: num cannot be zero or less"); + return InvalidCode; + } + if ( num > AST_ArrSpecs_Cap ) { + log_failure("gen::def_specifiers: num of speciifers to define AST larger than AST specicifier capacity - %d", num); + return InvalidCode; + } + CodeSpecifiers + result = (CodeSpecifiers) make_code(); + result->Type = CT_Specifiers; + + va_list va; + va_start(va, num); + do { + Specifier type = (Specifier)va_arg(va, int); + specifiers_append(result, type ); + } + while ( --num, num ); + va_end(va); + + return result; +} + +CodeSpecifiers def_specifiers_arr( s32 num, Specifier* specs ) +{ + if ( num <= 0 ) { + log_failure("gen::def_specifiers: num cannot be zero or less"); + return InvalidCode; + } + if ( num > AST_ArrSpecs_Cap ) { + log_failure("gen::def_specifiers: num of speciifers to define AST larger than AST specicifier capacity - %d", num); + return InvalidCode; + } + CodeSpecifiers + result = (CodeSpecifiers) make_code(); + result->Type = CT_Specifiers; + + s32 idx = 0; + do { + specifiers_append(result, specs[idx] ); + idx++; + } + while ( --num, num ); + + return result; +} + +CodeBody def_struct_body( s32 num, ... ) +{ + def_body_start( def_struct_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Struct_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + if ( ! entry) { + log_failure("gen::" "def_struct_body" ": Provided an null entry"); + return InvalidCode; + } + switch (entry->Type) + { + GEN_AST_BODY_STRUCT_UNALLOWED_TYPES_CASES: + log_failure("gen::" "def_struct_body" ": Entry type is not allowed: %s", code_debug_str(entry)); + return InvalidCode; + + default: + break; + } + body_append(result, entry); + } + while (num--, num > 0); + va_end(va); + + return result; +} + +CodeBody def_struct_body_arr( s32 num, Code* codes ) +{ + def_body_code_array_start( def_struct_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Struct_Body; + do + { + Code entry = *codes; + codes++; + if ( ! entry) { + log_failure("gen::" "def_struct_body" ": Provided an null entry"); + return InvalidCode; + } + switch (entry->Type) + { + GEN_AST_BODY_STRUCT_UNALLOWED_TYPES_CASES: + log_failure("gen::" "def_struct_body" ": Entry type is not allowed: %s", code_debug_str(entry) ); + return InvalidCode; + + default: + break; + } + body_append(result, entry); + } + while (num--, num > 0); + + return result; +} + +CodeBody def_union_body( s32 num, ... ) +{ + def_body_start( def_union_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Union_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast( Code, pod ); + if ( ! entry ) { + log_failure("gen::def_union_body: Provided a null entry"); + return InvalidCode; + } + if ( entry->Type != CT_Untyped && entry->Type != CT_Comment ) { + log_failure("gen::def_union_body: Entry type is not allowed - %s. Must be of untyped or comment type.", code_debug_str(entry) ); + return InvalidCode; + } + body_append(result, entry ); + } + while ( num--, num > 0 ); + va_end(va); + + return result; +} + +CodeBody def_union_body_arr( s32 num, Code* codes ) +{ + def_body_code_array_start( def_union_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Union_Body; + do + { + Code entry = *codes; + if ( ! entry ) { + log_failure("gen::def_union_body: Provided a null entry"); + return InvalidCode; + } + if ( entry->Type != CT_Untyped && entry->Type != CT_Comment ) { + log_failure("gen::def_union_body: Entry type is not allowed: %s", code_debug_str(entry) ); + return InvalidCode; + } + body_append(result, entry ); + } + while ( codes++, num--, num > 0 ); + + return (CodeBody) result; +} + +# undef name_check +# undef null_check +# undef def_body_start +# undef def_body_code_array_start + +#pragma endregion Upfront diff --git a/tests/assets/gencpp_samples/components/lexer.cpp b/tests/assets/gencpp_samples/components/lexer.cpp new file mode 100644 index 0000000..6c74241 --- /dev/null +++ b/tests/assets/gencpp_samples/components/lexer.cpp @@ -0,0 +1,1295 @@ +#ifdef INTELLISENSE_DIRECTIVES +#pragma once +#include "interface.upfront.cpp" +#include "gen/etoktype.hpp" +#endif + +StrBuilder tok_to_strbuilder(AllocatorInfo ainfo, Token tok) +{ + StrBuilder result = strbuilder_make_reserve( ainfo, kilobytes(4) ); + Str type_str = toktype_to_str( tok.Type ); + + strbuilder_append_fmt( & result, "Line: %d Column: %d, Type: %.*s Content: %.*s" + , tok.Line, tok.Column + , type_str.Len, type_str.Ptr + , tok.Text.Len, tok.Text.Ptr + ); + return result; +} + +bool lex__eat(Context* ctx, ParseContext* self, TokType type ); + +Token* lex_current(ParseContext* self, bool skip_formatting ) +{ + if ( skip_formatting ) + { + while ( self->tokens.ptr[self->token_id].Type == Tok_NewLine || self->tokens.ptr[self->token_id].Type == Tok_Comment ) + self->token_id++; + } + return & self->tokens.ptr[self->token_id]; +} + +Token* lex_peek(ParseContext const* self, bool skip_formatting) +{ + s32 idx = self->token_id; + if ( skip_formatting ) + { + while ( self->tokens.ptr[idx].Type == Tok_NewLine ) + idx++; + + return & self->tokens.ptr[idx]; + } + return & self->tokens.ptr[idx]; +} + +Token* lex_previous(ParseContext const* self, bool skip_formatting) +{ + s32 idx = self->token_id; + if ( skip_formatting ) + { + while ( self->tokens.ptr[idx].Type == Tok_NewLine ) + idx --; + + return & self->tokens.ptr[idx]; + } + return & self->tokens.ptr[idx - 1]; +} + +Token* lex_next(ParseContext const* self, bool skip_formatting) +{ + s32 idx = self->token_id; + if ( skip_formatting ) + { + while ( self->tokens.ptr[idx].Type == Tok_NewLine ) + idx++; + + return & self->tokens.ptr[idx + 1]; + } + return & self->tokens.ptr[idx + 1]; +} + +enum +{ + Lex_Continue, + Lex_ReturnNull, +}; + +forceinline +void lexer_move_forward( LexContext* ctx ) +{ + if ( * ctx->scanner == '\n' ) { + ctx->line += 1; + ctx->column = 1; + } + else { + ++ ctx->column; + } + -- ctx->left; + ++ ctx->scanner; +} +#define move_forward() lexer_move_forward(ctx) + +forceinline +void lexer_skip_whitespace( LexContext* ctx ) +{ + while ( ctx->left && char_is_space( * ctx->scanner ) ) + move_forward(); +} +#define skip_whitespace() lexer_skip_whitespace(ctx) + +forceinline +void lexer_end_line( LexContext* ctx ) +{ + while ( ctx->left && (* ctx->scanner) == ' ' ) + move_forward(); + + if ( ctx->left && (* ctx->scanner) == '\r' ) { + move_forward(); + move_forward(); + } + else if ( ctx->left && (* ctx->scanner) == '\n' ) + move_forward(); +} +#define end_line() lexer_end_line(ctx) + +// TODO(Ed): We need to to attempt to recover from a lex failure? +s32 lex_preprocessor_define( LexContext* ctx ) +{ + Token name = { { ctx->scanner, 1 }, Tok_Identifier, ctx->line, ctx->column, TF_Preprocess }; + move_forward(); + + while ( ctx->left && ( char_is_alphanumeric((* ctx->scanner)) || (* ctx->scanner) == '_' ) ) { + move_forward(); + name.Text.Len++; + } + + Specifier spec = str_to_specifier( name.Text ); + TokType attrib = str_to_toktype( name.Text ); + b32 not_specifier = spec == Spec_Invalid; + b32 not_attribute = attrib <= Tok___Attributes_Start; + + Macro macro = { name.Text, MT_Expression, (MacroFlags)0 }; + Macro* registered_macro = lookup_macro(name.Text); + + if ( registered_macro == nullptr && not_specifier && not_attribute ) { + log_fmt("Warning: '%S' was not registered before the lexer processed its #define directive, it will be registered as a expression macro\n" + , name.Text + ); + // GEN_DEBUG_TRAP(); + } + array_append(ctx->tokens, name); + + if ( ctx->left && (* ctx->scanner) == '(' ) + { + if (registered_macro && ! macro_is_functional(* registered_macro)) { + log_fmt("Warning: %S registered macro is not flagged as functional yet the definition detects opening parenthesis '(' for arguments\n" + , name.Text + ); + // GEN_DEBUG_TRAP(); + } + else { + macro.Flags |= MF_Functional; + } + + Token opening_paren = { { ctx->scanner, 1 }, Tok_Paren_Open, ctx->line, ctx->column, TF_Preprocess }; + array_append(ctx->tokens, opening_paren); + move_forward(); + + Token last_parameter = {}; + // We need to tokenize the define's arguments now: + while( ctx->left && * ctx->scanner != ')') + { + skip_whitespace(); + + Str possible_varadic = { ctx->scanner, 3 }; + if ( ctx->left > 3 && str_are_equal( txt("..."), possible_varadic ) ) { + Token parameter = { { ctx->scanner, 3 }, Tok_Preprocess_Define_Param, ctx->line, ctx->column, TF_Preprocess }; + move_forward(); + move_forward(); + move_forward(); + + array_append(ctx->tokens, parameter); + skip_whitespace(); + last_parameter = parameter; + + while ( (* ctx->scanner) == '\\' ) { + move_forward(); + skip_whitespace(); + } + if (* ctx->scanner != ')' ) + { + log_failure("lex_preprocessor_define(%d, %d): Expected a ')' after '...' (varaidc macro param) %S\n" + , ctx->line + , ctx->column + , name.Text + ); + return Lex_ReturnNull; + } + break; + } + else if ( (* ctx->scanner) == '\\' ) { + move_forward(); + skip_whitespace(); + continue; + } + else if ( char_is_alpha( (* ctx->scanner) ) || (* ctx->scanner) == '_' ) + { + Token parameter = { { ctx->scanner, 1 }, Tok_Preprocess_Define_Param, ctx->line, ctx->column, TF_Preprocess }; + move_forward(); + + while ( ctx->left && ( char_is_alphanumeric((* ctx->scanner)) || (* ctx->scanner) == '_' ) ) + { + move_forward(); + parameter.Text.Len++; + } + array_append(ctx->tokens, parameter); + skip_whitespace(); + last_parameter = parameter; + } + else { + log_failure("lex_preprocessor_define(%d, %d): Expected a '_' or alpha character for a parameter name for %S\n" + , ctx->line + , ctx->column + , name.Text + ); + return Lex_ReturnNull; + } + + if (* ctx->scanner == ')' ) + break; + + // There should be a comma + if ( * ctx->scanner != ',' ) { + log_failure("lex_preprocessor_define(%d, %d): Expected a comma after parameter %S for %S\n" + , ctx->line + , ctx->column + , last_parameter.Text + , name.Text + ); + return Lex_ReturnNull; + } + Token comma = { { ctx->scanner, 1 }, Tok_Comma, ctx->line, ctx->column, TF_Preprocess }; + array_append(ctx->tokens, comma); + move_forward(); + } + + if ( * ctx->scanner != ')' ) { + log_failure("lex_preprocessor_define(%d, %d): Expected a ')' after last_parameter %S for %S (ran out of characters...)\n" + , ctx->line + , ctx->column + , last_parameter.Text + , name.Text + ); + return Lex_ReturnNull; + } + Token closing_paren = { { ctx->scanner, 1 }, Tok_Paren_Close, ctx->line, ctx->column, TF_Preprocess }; + array_append(ctx->tokens, closing_paren); + move_forward(); + } + else if ( registered_macro && macro_is_functional( * registered_macro) ) { + if (registered_macro && ! macro_is_functional(* registered_macro)) { + log_fmt("Warning: %S registered macro is flagged as functional yet the definition detects no opening parenthesis '(' for arguments\n" + , name.Text + ); + GEN_DEBUG_TRAP(); + } + } + + if ( registered_macro == nullptr ) { + register_macro(macro); + } + + // Define's content handled by lex_preprocessor_directive (the original caller of this) + return Lex_Continue; +} + +// TODO(Ed): We need to to attempt to recover from a lex failure? +s32 lex_preprocessor_directive( LexContext* ctx ) +{ + char const* hash = ctx->scanner; + Token hash_tok = { { hash, 1 }, Tok_Preprocess_Hash, ctx->line, ctx->column, TF_Preprocess }; + array_append(ctx->tokens, hash_tok); + + move_forward(); + skip_whitespace(); + + ctx->token.Text.Ptr = ctx->scanner; + while (ctx->left && ! char_is_space((* ctx->scanner)) ) + { + move_forward(); + ctx->token.Text.Len++; + } + + ctx->token.Type = str_to_toktype( ctx->token.Text ); + + bool is_preprocessor = ctx->token.Type >= Tok_Preprocess_Define && ctx->token.Type <= Tok_Preprocess_Pragma; + if ( ! is_preprocessor ) + { + ctx->token.Type = Tok_Preprocess_Unsupported; + + // Its an unsupported directive, skip it + s32 within_string = false; + s32 within_char = false; + while ( ctx->left ) + { + if ( * ctx->scanner == '"' && ! within_char ) + within_string ^= true; + + if ( * ctx->scanner == '\'' && ! within_string ) + within_char ^= true; + + if ( * ctx->scanner == '\\' && ! within_string && ! within_char ) + { + move_forward(); + ctx->token.Text.Len++; + + if ( (* ctx->scanner) == '\r' ) + { + move_forward(); + ctx->token.Text.Len++; + } + + if ( (* ctx->scanner) == '\n' ) + { + move_forward(); + ctx->token.Text.Len++; + continue; + } + else + { + log_failure( "gen::Parser::lex: Invalid escape sequence '\\%c' (%d, %d)" + " in preprocessor directive (%d, %d)\n%.100s" + , (* ctx->scanner), ctx->line, ctx->column + , ctx->token.Line, ctx->token.Column, ctx->token.Text ); + break; + } + } + + if ( (* ctx->scanner) == '\r' ) + { + move_forward(); + ctx->token.Text.Len++; + } + + if ( (* ctx->scanner) == '\n' ) + { + move_forward(); + ctx->token.Text.Len++; + break; + } + + move_forward(); + ctx->token.Text.Len++; + } + + ctx->token.Text.Len = ctx->token.Text.Len + ctx->token.Text.Ptr - hash; + ctx->token.Text.Ptr = hash; + array_append(ctx->tokens, ctx->token); + return Lex_Continue; // Skip found token, its all handled here. + } + + if ( ctx->token.Type == Tok_Preprocess_Else || ctx->token.Type == Tok_Preprocess_EndIf ) + { + ctx->token.Flags |= TF_Preprocess_Cond; + array_append(ctx->tokens, ctx->token); + end_line(); + return Lex_Continue; + } + else if ( ctx->token.Type >= Tok_Preprocess_If && ctx->token.Type <= Tok_Preprocess_ElIf ) + { + ctx->token.Flags |= TF_Preprocess_Cond; + } + + array_append(ctx->tokens, ctx->token); + + skip_whitespace(); + + if ( ctx->token.Type == Tok_Preprocess_Define ) + { + u32 result = lex_preprocessor_define(ctx); // handles: #define ( ) - define's content handled later on within this scope. + if (result != Lex_Continue) + return Lex_ReturnNull; + } + + Token preprocess_content = { { ctx->scanner, 0 }, Tok_Preprocess_Content, ctx->line, ctx->column, TF_Preprocess }; + + if ( ctx->token.Type == Tok_Preprocess_Include ) + { + preprocess_content.Type = Tok_String; + + if ( (* ctx->scanner) != '"' && (* ctx->scanner) != '<' ) + { + StrBuilder directive_str = strbuilder_fmt_buf( ctx->allocator_temp, "%.*s", min( 80, ctx->left + preprocess_content.Text.Len ), ctx->token.Text.Ptr ); + + log_failure( "gen::Parser::lex: Expected '\"' or '<' after #include, not '%c' (%d, %d)\n%s" + , (* ctx->scanner) + , preprocess_content.Line + , preprocess_content.Column + , (char*) directive_str + ); + return Lex_ReturnNull; + } + move_forward(); + preprocess_content.Text.Len++; + + while ( ctx->left && (* ctx->scanner) != '"' && (* ctx->scanner) != '>' ) + { + move_forward(); + preprocess_content.Text.Len++; + } + + move_forward(); + preprocess_content.Text.Len++; + + if ( (* ctx->scanner) == '\r' && ctx->scanner[1] == '\n' ) + { + move_forward(); + move_forward(); + } + else if ( (* ctx->scanner) == '\n' ) + { + move_forward(); + } + + array_append(ctx->tokens, preprocess_content); + return Lex_Continue; // Skip found token, its all handled here. + } + + s32 within_string = false; + s32 within_char = false; + + // Consume preprocess content + while ( ctx->left ) + { + if ( (* ctx->scanner) == '"' && ! within_char ) + within_string ^= true; + + if ( (* ctx->scanner) == '\'' && ! within_string ) + within_char ^= true; + + if ( (* ctx->scanner) == '\\' && ! within_string && ! within_char ) + { + move_forward(); + preprocess_content.Text.Len++; + + if ( (* ctx->scanner) == '\r' ) + { + move_forward(); + preprocess_content.Text.Len++; + } + + if ( (* ctx->scanner) == '\n' ) + { + move_forward(); + preprocess_content.Text.Len++; + continue; + } + else + { + StrBuilder directive_str = strbuilder_make_length( ctx->allocator_temp, ctx->token.Text.Ptr, ctx->token.Text.Len ); + StrBuilder content_str = strbuilder_fmt_buf( ctx->allocator_temp, "%.*s", min( 400, ctx->left + preprocess_content.Text.Len ), preprocess_content.Text.Ptr ); + + log_failure( "gen::Parser::lex: Invalid escape sequence '\\%c' (%d, %d)" + " in preprocessor directive '%s' (%d, %d)\n%s" + , (* ctx->scanner), ctx->line, ctx->column + , directive_str, preprocess_content.Line, preprocess_content.Column + , content_str ); + return Lex_ReturnNull; + break; + } + } + + if ( (* ctx->scanner) == '\r' ) + { + break; + //move_forward(); + } + + if ( (* ctx->scanner) == '\n' ) + { + //move_forward(); + break; + } + + move_forward(); + preprocess_content.Text.Len++; + } + + array_append(ctx->tokens, preprocess_content); + return Lex_Continue; // Skip found token, its all handled here. +} + +void lex_found_token( LexContext* ctx ) +{ + if ( ctx->token.Type != Tok_Invalid ) { + array_append(ctx->tokens, ctx->token); + return; + } + + TokType type = str_to_toktype( ctx->token.Text ); + + if (type == Tok_Preprocess_Define || type == Tok_Preprocess_Include) { + ctx->token.Flags |= TF_Identifier; + } + + if (type <= Tok_Access_Public && type >= Tok_Access_Private ) { + ctx->token.Flags |= TF_AccessSpecifier; + } + if ( type > Tok___Attributes_Start ) { + ctx->token.Flags |= TF_Attribute; + } + if ( type == Tok_Decl_Extern_Linkage ) + { + skip_whitespace(); + + if ( (* ctx->scanner) != '"' ) { + type = Tok_Spec_Extern; + ctx->token.Flags |= TF_Specifier; + } + + ctx->token.Type = type; + array_append(ctx->tokens, ctx->token); + return; + } + if ( ( type <= Tok_Star && type >= Tok_Spec_Alignas) + || type == Tok_Ampersand + || type == Tok_Ampersand_DBL ) + { + ctx->token.Type = type; + ctx->token.Flags |= TF_Specifier; + array_append(ctx->tokens, ctx->token); + return; + } + if ( type != Tok_Invalid ) + { + ctx->token.Type = type; + array_append(ctx->tokens, ctx->token); + return; + } + + Macro* macro = lookup_macro( ctx->token.Text ); + b32 has_args = ctx->left && (* ctx->scanner) == '('; + b32 resolved_to_macro = false; + if (macro) { + ctx->token.Type = macrotype_to_toktype(macro->Type); + b32 is_functional = macro_is_functional(* macro); + resolved_to_macro = has_args ? is_functional : ! is_functional; + if ( ! resolved_to_macro && GEN_BUILD_DEBUG ) { + log_fmt("Info(%d, %d): %S identified as a macro but usage here does not resolve to one (interpreting as identifier)\n" + , ctx->token.Line + , ctx->token.Line + , macro->Name + ); + } + } + if ( resolved_to_macro ) + { + // TODO(Ed): When we introduce a macro AST (and expression support), we'll properly lex this section. + // Want to ignore any arguments the define may have as they can be execution expressions. + if ( has_args ) { + ctx->token.Flags |= TF_Macro_Functional; + } + if ( bitfield_is_set(MacroFlags, macro->Flags, MF_Allow_As_Attribute) ) { + ctx->token.Flags |= TF_Attribute; + } + if ( bitfield_is_set(MacroFlags, macro->Flags, MF_Allow_As_Specifier ) ) { + ctx->token.Flags |= TF_Specifier; + } + } + else + { + ctx->token.Type = Tok_Identifier; + } + + array_append(ctx->tokens, ctx->token); +} + +// TODO(Ed): We should dynamically allocate the lexer's array in Allocator_DyanmicContainers. + +// TODO(Ed): We need to to attempt to recover from a lex failure? + +neverinline +LexedInfo lex(Context* lib_ctx, Str content) +{ + LexedInfo info = struct_zero(LexedInfo); + + LexContext c = struct_zero(LexContext); LexContext* ctx = & c; + c.allocator_temp = lib_ctx->Allocator_Temp; + c.content = content; + c.left = content.Len; + c.scanner = content.Ptr; + c.line = 1; + c.column = 1; + c.tokens = array_init_reserve(Token, lib_ctx->Allocator_DyanmicContainers, lib_ctx->InitSize_LexerTokens ); + + // TODO(Ed): Re-implement to new constraints: + // 1. Ability to continue on error + // 2. Return a lexed info. + + skip_whitespace(); + if ( c.left <= 0 ) { + log_failure( "gen::lex: no tokens found (only whitespace provided)" ); + return info; + } + + b32 preprocess_args = true; + + while (c.left ) + { + c.token = struct_init(Token) { { c.scanner, 0 }, Tok_Invalid, c.line, c.column, TF_Null }; + + bool is_define = false; + + if ( c.column == 1 ) + { + if ( (* ctx->scanner) == '\r') + { + move_forward(); + c.token.Text.Len = 1; + } + + if ( (* ctx->scanner) == '\n' ) + { + move_forward(); + + c.token.Type = Tok_NewLine; + c.token.Text.Len++; + + array_append(c.tokens, c.token); + continue; + } + } + + c.token.Text.Len = 0; + + skip_whitespace(); + if ( c.left <= 0 ) + break; + + switch ( (* ctx->scanner) ) + { + case '#': + { + s32 result = lex_preprocessor_directive( ctx ); + switch ( result ) + { + case Lex_Continue: + { + //TokType last_type = Tokens[array_get_header(Tokens)->Num - 2].Type; + //if ( last_type == Tok_Preprocess_Pragma ) + { + { + Token thanks_c = { { c.scanner, 0 }, Tok_Invalid, c.line, c.column, TF_Null }; + c.token = thanks_c; + } + if ( (* ctx->scanner) == '\r') + { + move_forward(); + c.token.Text.Len = 1; + } + + if ( (* ctx->scanner) == '\n' ) + { + c.token.Type = Tok_NewLine; + c.token.Text.Len++; + move_forward(); + + array_append(c.tokens, c.token); + } + } + continue; + } + + case Lex_ReturnNull: + { + return info; + } + } + } + case '.': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Access_MemberSymbol; + c.token.Flags = TF_AccessOperator; + + if (c.left) { + move_forward(); + } + + if ( (* ctx->scanner) == '.' ) + { + move_forward(); + if( (* ctx->scanner) == '.' ) + { + c.token.Text.Len = 3; + c.token.Type = Tok_Varadic_Argument; + c.token.Flags = TF_Null; + move_forward(); + } + else + { + StrBuilder context_str = strbuilder_fmt_buf( lib_ctx->Allocator_Temp, "%s", c.scanner, min( 100, c.left ) ); + + log_failure( "gen::lex: invalid varadic argument, expected '...' got '..%c' (%d, %d)\n%s", (* ctx->scanner), c.line, c.column, context_str ); + } + } + + goto FoundToken; + } + case '&' : + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Ampersand; + c.token.Flags |= TF_Operator; + c.token.Flags |= TF_Specifier; + + if (c.left) + move_forward(); + + if ( (* ctx->scanner) == '&' ) // && + { + c.token.Text.Len = 2; + c.token.Type = Tok_Ampersand_DBL; + + if (c.left) + move_forward(); + } + + goto FoundToken; + } + case ':': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Assign_Classifer; + // Can be either a classifier (ParentType, Bitfield width), or ternary else + // token.Type = Tok_Colon; + + if (c.left) + move_forward(); + + if ( (* ctx->scanner) == ':' ) + { + move_forward(); + c.token.Type = Tok_Access_StaticSymbol; + c.token.Text.Len++; + } + goto FoundToken; + } + case '{': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_BraceCurly_Open; + + if (c.left) + move_forward(); + goto FoundToken; + } + case '}': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_BraceCurly_Close; + c.token.Flags = TF_EndDefinition; + + if (c.left) + move_forward(); + + end_line(); + goto FoundToken; + } + case '[': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_BraceSquare_Open; + if ( c.left ) + { + move_forward(); + + if ( (* ctx->scanner) == ']' ) + { + c.token.Text.Len = 2; + c.token.Type = Tok_Operator; + move_forward(); + } + } + goto FoundToken; + } + case ']': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_BraceSquare_Close; + + if (c.left) + move_forward(); + goto FoundToken; + } + case '(': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Paren_Open; + + if (c.left) + move_forward(); + goto FoundToken; + } + case ')': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Paren_Close; + + if (c.left) + move_forward(); + goto FoundToken; + } + case '\'': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Char; + c.token.Flags = TF_Literal; + + move_forward(); + + if ( c.left && (* ctx->scanner) == '\\' ) + { + move_forward(); + c.token.Text.Len++; + + if ( (* ctx->scanner) == '\'' ) + { + move_forward(); + c.token.Text.Len++; + } + } + + while ( c.left && (* ctx->scanner) != '\'' ) + { + move_forward(); + c.token.Text.Len++; + } + + if ( c.left ) + { + move_forward(); + c.token.Text.Len++; + } + goto FoundToken; + } + case ',': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Comma; + c.token.Flags = TF_Operator; + + if (c.left) + move_forward(); + goto FoundToken; + } + case '*': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Star; + c.token.Flags |= TF_Specifier; + c.token.Flags |= TF_Operator; + + if (c.left) + move_forward(); + + if ( (* ctx->scanner) == '=' ) + { + c.token.Text.Len++; + c.token.Flags |= TF_Assign; + // c.token.Type = Tok_Assign_Multiply; + + if ( c.left ) + move_forward(); + } + + goto FoundToken; + } + case ';': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Statement_End; + c.token.Flags = TF_EndDefinition; + + if (c.left) + move_forward(); + + end_line(); + goto FoundToken; + } + case '"': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_String; + c.token.Flags |= TF_Literal; + + move_forward(); + while ( c.left ) + { + if ( (* ctx->scanner) == '"' ) + { + move_forward(); + break; + } + + if ( (* ctx->scanner) == '\\' ) + { + move_forward(); + c.token.Text.Len++; + + if ( c.left ) + { + move_forward(); + c.token.Text.Len++; + } + continue; + } + + move_forward(); + c.token.Text.Len++; + } + goto FoundToken; + } + case '?': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Operator; + // c.token.Type = Tok_Ternary; + c.token.Flags = TF_Operator; + + if (c.left) + move_forward(); + + goto FoundToken; + } + case '=': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Operator; + // c.token.Type = Tok_Assign; + c.token.Flags = TF_Operator; + c.token.Flags |= TF_Assign; + + if (c.left) + move_forward(); + + if ( (* ctx->scanner) == '=' ) + { + c.token.Text.Len++; + c.token.Flags = TF_Operator; + + if (c.left) + move_forward(); + } + + goto FoundToken; + } + case '+': + { + // c.token.Type = Tok_Add + + } + case '%': + { + // c.token.Type = Tok_Modulo; + + } + case '^': + { + // c.token.Type = Tok_B_XOr; + } + case '~': + { + // c.token.Type = Tok_Unary_Not; + + } + case '!': + { + // c.token.Type = Tok_L_Not; + } + case '<': + { + // c.token.Type = Tok_Lesser; + + } + case '>': + { + // c.token.Type = Tok_Greater; + + } + case '|': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Operator; + c.token.Flags = TF_Operator; + // token.Type = Tok_L_Or; + + if (c.left) + move_forward(); + + if ( (* ctx->scanner) == '=' ) + { + c.token.Text.Len++; + c.token.Flags |= TF_Assign; + // token.Flags |= TokFlags::Assignment; + // token.Type = Tok_Assign_L_Or; + + if (c.left) + move_forward(); + } + else while ( c.left && (* ctx->scanner) == *(c.scanner - 1) && c.token.Text.Len < 3 ) + { + c.token.Text.Len++; + + if (c.left) + move_forward(); + } + goto FoundToken; + } + + // Dash is unfortunately a bit more complicated... + case '-': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Operator; + // token.Type = Tok_Subtract; + c.token.Flags = TF_Operator; + if ( c.left ) + { + move_forward(); + + if ( (* ctx->scanner) == '>' ) + { + c.token.Text.Len++; +// token.Type = Tok_Access_PointerToMemberSymbol; + c.token.Flags |= TF_AccessOperator; + move_forward(); + + if ( (* ctx->scanner) == '*' ) + { +// token.Type = Tok_Access_PointerToMemberOfPointerSymbol; + c.token.Text.Len++; + move_forward(); + } + } + else if ( (* ctx->scanner) == '=' ) + { + c.token.Text.Len++; + // token.Type = Tok_Assign_Subtract; + c.token.Flags |= TF_Assign; + + if (c.left) + move_forward(); + } + else while ( c.left && (* ctx->scanner) == *(c.scanner - 1) && c.token.Text.Len < 3 ) + { + c.token.Text.Len++; + + if (c.left) + move_forward(); + } + } + goto FoundToken; + } + case '/': + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Operator; + // token.Type = Tok_Divide; + c.token.Flags = TF_Operator; + move_forward(); + + if ( c.left ) + { + if ( (* ctx->scanner) == '=' ) + { + // token.Type = TokeType::Assign_Divide; + move_forward(); + c.token.Text.Len++; + c.token.Flags = TF_Assign; + } + else if ( (* ctx->scanner) == '/' ) + { + c.token.Type = Tok_Comment; + c.token.Text.Len = 2; + c.token.Flags = TF_Null; + move_forward(); + + while ( c.left && (* ctx->scanner) != '\n' && (* ctx->scanner) != '\r' ) + { + move_forward(); + c.token.Text.Len++; + } + + if ( (* ctx->scanner) == '\r' ) + { + move_forward(); + c.token.Text.Len++; + } + if ( (* ctx->scanner) == '\n' ) + { + move_forward(); + c.token.Text.Len++; + } + array_append(c.tokens, c.token); + continue; + } + else if ( (* ctx->scanner) == '*' ) + { + c.token.Type = Tok_Comment; + c.token.Text.Len = 2; + c.token.Flags = TF_Null; + move_forward(); + + bool star = (* ctx->scanner) == '*'; + bool slash = c.scanner[1] == '/'; + bool at_end = star && slash; + while ( c.left && ! at_end ) + { + move_forward(); + c.token.Text.Len++; + + star = (* ctx->scanner) == '*'; + slash = c.scanner[1] == '/'; + at_end = star && slash; + } + c.token.Text.Len += 2; + move_forward(); + move_forward(); + + if ( (* ctx->scanner) == '\r' ) + { + move_forward(); + c.token.Text.Len++; + } + if ( (* ctx->scanner) == '\n' ) + { + move_forward(); + c.token.Text.Len++; + } + array_append(c.tokens, c.token); + // end_line(); + continue; + } + } + goto FoundToken; + } + } + + if ( char_is_alpha( (* ctx->scanner) ) || (* ctx->scanner) == '_' ) + { + Str text = { c.scanner, 1 }; + c.token.Text = text; + move_forward(); + + while ( c.left && ( char_is_alphanumeric((* ctx->scanner)) || (* ctx->scanner) == '_' ) ) { + move_forward(); + c.token.Text.Len++; + } + + goto FoundToken; + } + else if ( char_is_digit((* ctx->scanner)) ) + { + // This is a very brute force lex, no checks are done for validity of literal. + + Str text = { c.scanner, 1 }; + c.token.Text = text; + c.token.Type = Tok_Number; + c.token.Flags = TF_Literal; + move_forward(); + + if (c.left + && ( (* ctx->scanner) == 'x' || (* ctx->scanner) == 'X' + || (* ctx->scanner) == 'b' || (* ctx->scanner) == 'B' + || (* ctx->scanner) == 'o' || (* ctx->scanner) == 'O' ) + ) + { + move_forward(); + c.token.Text.Len++; + + while ( c.left && char_is_hex_digit((* ctx->scanner)) ) { + move_forward(); + c.token.Text.Len++; + } + + goto FoundToken; + } + + while ( c.left && char_is_digit((* ctx->scanner)) ) { + move_forward(); + c.token.Text.Len++; + } + + if ( c.left && (* ctx->scanner) == '.' ) + { + move_forward(); + c.token.Text.Len++; + + while ( c.left && char_is_digit((* ctx->scanner)) ) { + move_forward(); + c.token.Text.Len++; + } + + // Handle number literal suffixes in a botched way + if (c.left && ( + (* ctx->scanner) == 'l' || (* ctx->scanner) == 'L' || // long/long long + (* ctx->scanner) == 'u' || (* ctx->scanner) == 'U' || // unsigned + (* ctx->scanner) == 'f' || (* ctx->scanner) == 'F' || // float + (* ctx->scanner) == 'i' || (* ctx->scanner) == 'I' || // imaginary + (* ctx->scanner) == 'z' || (* ctx->scanner) == 'Z')) // complex + { + char prev = (* ctx->scanner); + move_forward(); + c.token.Text.Len++; + + // Handle 'll'/'LL' as a special case when we just processed an 'l'/'L' + if (c.left && (prev == 'l' || prev == 'L') && ((* ctx->scanner) == 'l' || (* ctx->scanner) == 'L')) { + move_forward(); + c.token.Text.Len++; + } + } + } + + goto FoundToken; + } + else + { + s32 start = max( 0, array_num(c.tokens) - 100 ); + log_fmt("\n%d\n", start); + for ( s32 idx = start; idx < array_num(c.tokens); idx++ ) + { + log_fmt( "Token %d Type: %s : %.*s\n" + , idx + , toktype_to_str( c.tokens[ idx ].Type ).Ptr + , c.tokens[ idx ].Text.Len, c.tokens[ idx ].Text.Ptr + ); + } + + StrBuilder context_str = strbuilder_fmt_buf( _ctx->Allocator_Temp, "%.*s", min( 100, c.left ), c.scanner ); + log_failure( "Failed to lex token '%c' (%d, %d)\n%s", (* ctx->scanner), c.line, c.column, context_str ); + + // Skip to next whitespace since we can't know if anything else is valid until then. + while ( c.left && ! char_is_space( (* ctx->scanner) ) ) { + move_forward(); + } + } + + FoundToken: + { + lex_found_token( ctx ); + TokType last_type = array_back(c.tokens)->Type; + if ( last_type == Tok_Preprocess_Macro_Stmt || last_type == Tok_Preprocess_Macro_Expr ) + { + Token thanks_c = { { c.scanner, 0 }, Tok_Invalid, c.line, c.column, TF_Null }; + c.token = thanks_c; + if ( (* ctx->scanner) == '\r') { + move_forward(); + c.token.Text.Len = 1; + } + if ( (* ctx->scanner) == '\n' ) + { + c.token.Type = Tok_NewLine; + c.token.Text.Len++; + move_forward(); + + array_append(c.tokens, c.token); + continue; + } + } + } + } + + if ( array_num(c.tokens) == 0 ) { + log_failure( "Failed to lex any tokens" ); + return info; + } + + info.messages = c.messages; + info.text = content; + info.tokens = struct_init(TokenSlice) { pcast(Token*, c.tokens), scast(s32, array_num(c.tokens)) }; + return info; +} + +#undef move_forward +#undef skip_whitespace +#undef end_line diff --git a/tests/assets/gencpp_samples/components/parser.cpp b/tests/assets/gencpp_samples/components/parser.cpp new file mode 100644 index 0000000..254353e --- /dev/null +++ b/tests/assets/gencpp_samples/components/parser.cpp @@ -0,0 +1,5784 @@ +#ifdef INTELLISENSE_DIRECTIVES +#pragma once +#include "gen/etoktype.hpp" +#include "parser_case_macros.cpp" +#include "interface.upfront.cpp" +#include "lexer.cpp" +#endif + +// TODO(Ed) : Rename ETok_Capture_Start, ETok_Capture_End to Open_Parenthesis adn Close_Parenthesis + +constexpr bool lex_dont_skip_formatting = false; +constexpr bool lex_skip_formatting = true; + +void parser_push( ParseContext* ctx, ParseStackNode* node ) +{ + node->prev = ctx->scope; + ctx->scope = node; + +#if 0 && GEN_BUILD_DEBUG + log_fmt("\tEntering parser: %.*s\n", Scope->ProcName.Len, Scope->ProcName.Ptr ); +#endif +} + +void parser_pop(ParseContext* ctx) +{ +#if 0 && GEN_BUILD_DEBUG + log_fmt("\tPopping parser: %.*s\n", Scope->ProcName.Len, Scope->ProcName.Ptr ); +#endif + ctx->scope = ctx->scope->prev; +} + +StrBuilder parser_to_strbuilder(ParseContext const* ctx, AllocatorInfo temp) +{ + StrBuilder result = strbuilder_make_reserve( temp, kilobytes(4) ); + + Token scope_start = * ctx->scope->start; + Token last_valid = (ctx->token_id >= ctx->tokens.num) ? ctx->tokens.ptr[ctx->tokens.num -1] : (* lex_peek(ctx, true)); + + sptr length = scope_start.Text.Len; + char const* current = scope_start.Text.Ptr + length; + while ( current <= ctx->tokens.ptr[ctx->tokens.num - 1].Text.Ptr && (* current) != '\n' && length < 74 ) + { + current++; + length++; + } + + Str scope_str = { scope_start.Text.Ptr, length }; + StrBuilder line = strbuilder_make_str( temp, scope_str ); + strbuilder_append_fmt( & result, "\tScope : %s\n", line ); + strbuilder_free(& line); + + sptr dist = (sptr)last_valid.Text.Ptr - (sptr)scope_start.Text.Ptr + 2; + sptr length_from_err = dist; + + Str err_str = { last_valid.Text.Ptr, length_from_err }; + StrBuilder line_from_err = strbuilder_make_str( temp, err_str ); + + if ( length_from_err < 100 ) + strbuilder_append_fmt(& result, "\t(%d, %d):%*c\n", last_valid.Line, last_valid.Column, length_from_err, '^' ); + else + strbuilder_append_fmt(& result, "\t(%d, %d)\n", last_valid.Line, last_valid.Column ); + + ParseStackNode* curr_scope = ctx->scope; + s32 level = 0; + do + { + if ( curr_scope->name.Ptr ) { + strbuilder_append_fmt(& result, "\t%d: %S, AST Name: %S\n", level, curr_scope->proc_name, curr_scope->name ); + } + else { + strbuilder_append_fmt(& result, "\t%d: %S\n", level, curr_scope->proc_name ); + } + + curr_scope = curr_scope->prev; + level++; + } + while ( curr_scope ); + return result; +} + +bool lex__eat(Context* ctx, ParseContext* parser, TokType type) +{ + if ( parser->tokens.num - parser->token_id <= 0 ) { + log_failure( "No tokens left.\n%SB", parser_to_strbuilder(parser, ctx->Allocator_Temp) ); + return false; + } + + Token at_idx = parser->tokens.ptr[ parser->token_id ]; + + if ( ( at_idx.Type == Tok_NewLine && type != Tok_NewLine ) + || ( at_idx.Type == Tok_Comment && type != Tok_Comment ) ) + { + parser->token_id ++; + } + + b32 not_accepted = at_idx.Type != type; + b32 is_identifier = at_idx.Type == Tok_Identifier; + if ( not_accepted ) + { + Macro* macro = lookup_macro(at_idx.Text); + b32 accept_as_identifier = macro && bitfield_is_set(MacroFlags, macro->Flags, MF_Allow_As_Identifier ); + not_accepted = type == Tok_Identifier && accept_as_identifier ? false : true; + } + if ( not_accepted ) + { + Token tok = * lex_current( parser, lex_skip_formatting ); + log_failure( "Parse Error, TokArray::eat, Expected: ' %S ' not ' %S ' (%d, %d)`\n%SB" + , toktype_to_str(type) + , at_idx.Text + , tok.Line + , tok.Column + , parser_to_strbuilder(parser, ctx->Allocator_Temp) + ); + GEN_DEBUG_TRAP(); + return false; + } + +#if 0 && GEN_BUILD_DEBUG + log_fmt("Ate: %SB\n", self->Arr[Idx].to_strbuilder() ); +#endif + + parser->token_id ++; + return true; +} + +internal +void parser_init(Context* ctx) +{ +} + +internal +void parser_deinit(Context* ctx) +{ +} + +#pragma region Helper Macros + +#define check_parse_args( def ) _check_parse_args(& ctx->parser, def, stringize(_func_) ) +bool _check_parse_args(ParseContext* parser, Str def, char const* func_name ) +{ + if ( def.Len <= 0 ) + { + log_failure( c_str_fmt_buf("gen::%s: length must greater than 0", func_name) ); + parser_pop(parser); + return false; + } + if ( def.Ptr == nullptr ) + { + log_failure( c_str_fmt_buf("gen::%s: def was null", func_name) ); + parser_pop(parser); + return false; + } + return true; +} + +# define currtok_noskip (* lex_current( & ctx->parser, lex_dont_skip_formatting )) +# define currtok (* lex_current( & ctx->parser, lex_skip_formatting )) +# define peektok (* lex_peek(& ctx->parser, lex_skip_formatting)) +# define prevtok (* lex_previous( & ctx->parser, lex_dont_skip_formatting)) +# define nexttok (* lex_next( & ctx->parser, lex_skip_formatting )) +# define nexttok_noskip (* lex_next( & ctx->parser, lex_dont_skip_formatting)) +# define eat( Type_ ) lex__eat(ctx, & ctx->parser, Type_ ) +# define left ( ctx->parser.tokens.num - ctx->parser.token_id ) + +#if GEN_COMPILER_CPP +# define def_assign( ... ) { __VA_ARGS__ } +#else +# define def_assign( ... ) __VA_ARGS__ +#endif + +#ifdef check +#define CHECK_WAS_DEFINED +#pragma push_macro("check") +#undef check +#endif + +# define check_noskip( Type_ ) ( left && currtok_noskip.Type == Type_ ) +# define check( Type_ ) ( left && currtok.Type == Type_ ) + +#if GEN_COMPILER_CPP +# define NullScope { nullptr, {nullptr, 0}, lex_current( & ctx->parser, lex_dont_skip_formatting ), Str{nullptr, 0}, txt( __func__ ), { nullptr} } +#else +# define NullScope (ParseStackNode){ nullptr, {nullptr, 0}, lex_current( & ctx->parser, lex_dont_skip_formatting ), (Str){nullptr, 0}, txt( __func__ ), { nullptr} } +#endif + +#pragma endregion Helper Macros + +// Procedure Forwards ( Entire parser internal parser interface ) + +internal Code parse_array_decl (Context* ctx); +internal CodeAttributes parse_attributes (Context* ctx); +internal CodeComment parse_comment (Context* ctx); +internal Code parse_complicated_definition (Context* ctx, TokType which); +internal CodeBody parse_class_struct_body (Context* ctx, TokType which, Token name); +internal Code parse_class_struct (Context* ctx, TokType which, bool inplace_def); +internal Code parse_expression (Context* ctx); +internal Code parse_forward_or_definition (Context* ctx, TokType which, bool is_inplace); +internal CodeFn parse_function_after_name (Context* ctx, ModuleFlag mflags, CodeAttributes attributes, CodeSpecifiers specifiers, CodeTypename ret_type, Token name); +internal Code parse_function_body (Context* ctx); +internal CodeBody parse_global_nspace (Context* ctx, CodeType which); +internal Code parse_global_nspace_constructor_destructor(Context* ctx, CodeSpecifiers specifiers); +internal Token parse_identifier (Context* ctx, bool* possible_member_function); +internal CodeInclude parse_include (Context* ctx); +internal Code parse_macro_as_definiton (Context* ctx, CodeAttributes attributes, CodeSpecifiers specifiers); +internal CodeOperator parse_operator_after_ret_type (Context* ctx, ModuleFlag mflags, CodeAttributes attributes, CodeSpecifiers specifiers, CodeTypename ret_type); +internal Code parse_operator_function_or_variable(Context* ctx, bool expects_function, CodeAttributes attributes, CodeSpecifiers specifiers); +internal CodePragma parse_pragma (Context* ctx); +internal CodeParams parse_params (Context* ctx, bool use_template_capture); +internal CodePreprocessCond parse_preprocess_cond (Context* ctx); +internal Code parse_simple_preprocess (Context* ctx, TokType which); +internal Code parse_static_assert (Context* ctx); +internal void parse_template_args (Context* ctx, Token* token ); +internal CodeVar parse_variable_after_name (Context* ctx, ModuleFlag mflags, CodeAttributes attributes, CodeSpecifiers specifiers, CodeTypename type, Str name); +internal CodeVar parse_variable_declaration_list (Context* ctx); + +internal CodeClass parser_parse_class (Context* ctx, bool inplace_def ); +internal CodeConstructor parser_parse_constructor (Context* ctx, CodeSpecifiers specifiers ); +internal CodeDefine parser_parse_define (Context* ctx); +internal CodeDestructor parser_parse_destructor (Context* ctx, CodeSpecifiers specifiers ); +internal CodeEnum parser_parse_enum (Context* ctx, bool inplace_def ); +internal CodeBody parser_parse_export_body (Context* ctx); +internal CodeBody parser_parse_extern_link_body(Context* ctx); +internal CodeExtern parser_parse_extern_link (Context* ctx); +internal CodeFriend parser_parse_friend (Context* ctx); +internal CodeFn parser_parse_function (Context* ctx); +internal CodeNS parser_parse_namespace (Context* ctx); +internal CodeOpCast parser_parse_operator_cast (Context* ctx, CodeSpecifiers specifiers ); +internal CodeStruct parser_parse_struct (Context* ctx, bool inplace_def ); +internal CodeVar parser_parse_variable (Context* ctx); +internal CodeTemplate parser_parse_template (Context* ctx); +internal CodeTypename parser_parse_type (Context* ctx, bool from_template, bool* is_function ); +internal CodeTypedef parser_parse_typedef (Context* ctx); +internal CodeUnion parser_parse_union (Context* ctx, bool inplace_def ); +internal CodeUsing parser_parse_using (Context* ctx); + +constexpr bool parser_inplace_def = true; +constexpr bool parser_not_inplace_def = false; +constexpr bool parser_dont_consume_braces = true; +constexpr bool parser_consume_braces = false; +constexpr bool parser_not_from_template = false; + +constexpr bool parser_use_parenthesis = false; + +// Internal parsing functions + +constexpr bool parser_strip_formatting_dont_preserve_newlines = false; +/* + 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. +*/ +internal +StrBuilder parser_strip_formatting(Context* ctx, Str raw_text, bool preserve_newlines ) +{ + StrBuilder content = strbuilder_make_reserve( ctx->Allocator_Temp, raw_text.Len ); + + if ( raw_text.Len == 0 ) + return content; + +#define cut_length ( scanner - raw_text.Ptr - last_cut ) +#define cut_ptr ( raw_text.Ptr + last_cut ) +#define pos ( rcast( sptr, scanner ) - rcast( sptr, raw_text.Ptr ) ) +#define move_fwd() do { scanner++; tokleft--; } while(0) + + s32 tokleft = raw_text.Len; + sptr last_cut = 0; + char const* scanner = raw_text.Ptr; + + if ( scanner[0] == ' ' ) { + move_fwd(); + last_cut = 1; + } + + bool within_string = false; + bool within_char = false; + bool must_keep_newline = false; + while ( tokleft ) + { + // Skip over the content of string literals + if ( scanner[0] == '"' ) + { + move_fwd(); + + while ( tokleft && ( scanner[0] != '"' || *( scanner - 1 ) == '\\' ) ) + { + if ( scanner[0] == '\\' && tokleft > 1 ) + { + scanner += 2; + tokleft -= 2; + } + else + { + move_fwd(); + } + } + + // Skip the closing " + if ( tokleft ) + move_fwd(); + + strbuilder_append_c_str_len( & content, cut_ptr, cut_length ); + last_cut = rcast(sptr, scanner ) - rcast( sptr, raw_text.Ptr ); + continue; + } + + // Skip over the content of character literals + if ( scanner[0] == '\'' ) + { + move_fwd(); + + while ( tokleft + && ( scanner[0] != '\'' + || ( *(scanner -1 ) == '\\' ) + ) ) + { + move_fwd(); + } + + // Skip the closing ' + if ( tokleft ) + move_fwd(); + + strbuilder_append_c_str_len( & content, cut_ptr, cut_length ); + last_cut = rcast( sptr, scanner ) - rcast( sptr, raw_text.Ptr ); + continue; + } + + // Block comments + if ( tokleft > 1 && scanner[0] == '/' && scanner[1] == '*' ) + { + while ( tokleft > 1 && !(scanner[0] == '*' && scanner[1] == '/') ) + move_fwd(); + + scanner += 2; + tokleft -= 2; + + strbuilder_append_c_str_len( & content, cut_ptr, cut_length ); + last_cut = rcast( sptr, scanner ) - rcast( sptr, raw_text.Ptr ); + continue; + } + + // Line comments + if ( tokleft > 1 && scanner[0] == '/' && scanner[1] == '/' ) + { + must_keep_newline = true; + + scanner += 2; + tokleft -= 2; + + while ( tokleft && scanner[ 0 ] != '\n' ) + move_fwd(); + + if (tokleft) + move_fwd(); + + strbuilder_append_c_str_len( & content, cut_ptr, cut_length ); + last_cut = rcast( sptr, scanner ) - rcast( sptr, raw_text.Ptr ); + continue; + } + + // Tabs + if (scanner[0] == '\t') + { + if (pos > last_cut) + strbuilder_append_c_str_len( & content, cut_ptr, cut_length); + + if ( * strbuilder_back( content ) != ' ' ) + strbuilder_append_char( & content, ' ' ); + + move_fwd(); + last_cut = rcast( sptr, scanner) - rcast( sptr, raw_text.Ptr); + continue; + } + + if ( tokleft > 1 && scanner[0] == '\r' && scanner[1] == '\n' ) + { + if ( must_keep_newline || preserve_newlines ) + { + must_keep_newline = false; + + scanner += 2; + tokleft -= 2; + + strbuilder_append_c_str_len( & content, cut_ptr, cut_length ); + last_cut = rcast( sptr, scanner ) - rcast( sptr, raw_text.Ptr ); + continue; + } + + if ( pos > last_cut ) + strbuilder_append_c_str_len( & content, cut_ptr, cut_length ); + + // Replace with a space + if ( * strbuilder_back( content ) != ' ' ) + strbuilder_append_char( & content, ' ' ); + + scanner += 2; + tokleft -= 2; + + last_cut = rcast( sptr, scanner ) - rcast( sptr, raw_text.Ptr ); + continue; + } + + if ( scanner[0] == '\n' ) + { + if ( must_keep_newline || preserve_newlines ) + { + must_keep_newline = false; + + move_fwd(); + + strbuilder_append_c_str_len( & content, cut_ptr, cut_length ); + last_cut = rcast( sptr, scanner ) - rcast( sptr, raw_text.Ptr ); + continue; + } + + if ( pos > last_cut ) + strbuilder_append_c_str_len( & content, cut_ptr, cut_length ); + + // Replace with a space + if ( * strbuilder_back( content ) != ' ' ) + strbuilder_append_char( & content, ' ' ); + + move_fwd(); + + last_cut = rcast( sptr, scanner ) - rcast( sptr, raw_text.Ptr ); + continue; + } + + // Escaped newlines + if ( scanner[0] == '\\' ) + { + strbuilder_append_c_str_len( & content, cut_ptr, cut_length ); + + s32 amount_to_skip = 1; + if ( tokleft > 1 && scanner[1] == '\n' ) + { + amount_to_skip = 2; + } + else if ( tokleft > 2 && scanner[1] == '\r' && scanner[2] == '\n' ) + { + amount_to_skip = 3; + } + + if ( amount_to_skip > 1 && pos == last_cut ) + { + scanner += amount_to_skip; + tokleft -= amount_to_skip; + } + else + move_fwd(); + + last_cut = rcast( sptr, scanner ) - rcast( sptr, raw_text.Ptr ); + continue; + } + + // Consectuive spaces + if ( tokleft > 1 && char_is_space( scanner[0] ) && char_is_space( scanner[ 1 ] ) ) + { + strbuilder_append_c_str_len( & content, cut_ptr, cut_length ); + do + { + move_fwd(); + } + while ( tokleft && char_is_space( scanner[0] ) ); + + last_cut = rcast( sptr, scanner ) - rcast( sptr, raw_text.Ptr ); + + // Preserve only 1 space of formattting + char* last = strbuilder_back(content); + if ( last == nullptr || * last != ' ' ) + strbuilder_append_char( & content, ' ' ); + + continue; + } + + move_fwd(); + } + + if ( last_cut < raw_text.Len ) { + strbuilder_append_c_str_len( & content, cut_ptr, raw_text.Len - last_cut ); + } + +#undef cut_ptr +#undef cut_length +#undef pos +#undef move_fwd + + return content; +} + +StrBuilder parser_strip_formatting_2(TokenSlice tokens) +{ + // TODO(Ed): Use this to produce strings for validation purposes. We shouldn't serialize down from tokens once we start storing token slices for content. + StrBuilder result = struct_zero(StrBuilder); + return result; +} + +internal +Code parse_array_decl(Context* ctx) +{ + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope ); + + if ( check( Tok_Operator ) && currtok.Text.Ptr[0] == '[' && currtok.Text.Ptr[1] == ']' ) + { + Code array_expr = untyped_str( txt(" ") ); + eat( Tok_Operator ); + // [] + + parser_pop(& ctx->parser); + return array_expr; + } + + if ( check( Tok_BraceSquare_Open ) ) + { + eat( Tok_BraceSquare_Open ); + // [ + + if ( left == 0 ) + { + log_failure( "Error, unexpected end of array declaration ( '[]' scope started )\n%SB", parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + parser_pop(& ctx->parser); + return InvalidCode; + } + + if ( currtok.Type == Tok_BraceSquare_Close ) + { + log_failure( "Error, empty array expression in definition\n%SB", parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + parser_pop(& ctx->parser); + return InvalidCode; + } + + TokenSlice tokens = { & currtok, 1 }; + Token untyped_tok = currtok; + + while ( left && currtok.Type != Tok_BraceSquare_Close ) + { + eat( currtok.Type ); + ++ tokens.num; + } + + // untyped_tok.Text.Len = ( (sptr)prevtok.Text.Ptr + prevtok.Text.Len ) - (sptr)untyped_tok.Text.Ptr; + untyped_tok.Text = token_range_to_str(untyped_tok, prevtok); + + Code array_expr = untyped_str( untyped_tok.Text ); + // Code array_expr = untyped_toks( tokens ); // TODO(Ed): Use token slice instead of untyped strings. + // [ + + if ( left == 0 ) + { + log_failure( "Error, unexpected end of array declaration, expected ]\n%SB", parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + parser_pop(& ctx->parser); + return InvalidCode; + } + + if ( currtok.Type != Tok_BraceSquare_Close ) + { + log_failure( "%s: Error, expected ] in array declaration, not %S\n%SB", toktype_to_str( currtok.Type ), parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + parser_pop(& ctx->parser); + return InvalidCode; + } + + eat( Tok_BraceSquare_Close ); + // [ ] + + // Its a multi-dimensional array + if ( check( Tok_BraceSquare_Open )) + { + Code adjacent_arr_expr = parse_array_decl(ctx); + // [ ][ ]... + + array_expr->Next = adjacent_arr_expr; + } + + parser_pop(& ctx->parser); + return array_expr; + } + + parser_pop(& ctx->parser); + return NullCode; +} + +internal inline +CodeAttributes parse_attributes(Context* ctx) +{ + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope); + + Token start = currtok; + s32 len = 0; + + // There can be more than one attribute. If there is flatten them to a single string. + // TODO(Ed): Support chaining attributes (Use parameter linkage pattern) + while ( left && tok_is_attribute(currtok) ) + { + if ( check( Tok_Attribute_Open ) ) + { + eat( Tok_Attribute_Open ); + // [[ + + while ( left && currtok.Type != Tok_Attribute_Close ) + { + eat( currtok.Type ); + } + // [[ + + eat( Tok_Attribute_Close ); + // [[ ]] + + len = ( ( sptr )prevtok.Text.Ptr + prevtok.Text.Len ) - ( sptr )start.Text.Ptr; + } + else if ( check( Tok_Decl_GNU_Attribute ) ) + { + eat( Tok_Decl_GNU_Attribute ); + eat( Tok_Paren_Open ); + eat( Tok_Paren_Open ); + // __attribute__(( + + while ( left && currtok.Type != Tok_Paren_Close ) + { + eat( currtok.Type ); + } + // __attribute__(( + + eat( Tok_Paren_Close ); + eat( Tok_Paren_Close ); + // __attribute__(( )) + + len = ( ( sptr )prevtok.Text.Ptr + prevtok.Text.Len ) - ( sptr )start.Text.Ptr; + } + else if ( check( Tok_Decl_MSVC_Attribute ) ) + { + eat( Tok_Decl_MSVC_Attribute ); + eat( Tok_Paren_Open ); + // __declspec( + + while ( left && currtok.Type != Tok_Paren_Close ) + { + eat( currtok.Type ); + } + // __declspec( + + eat( Tok_Paren_Close ); + // __declspec( ) + + len = ( ( sptr )prevtok.Text.Ptr + prevtok.Text.Len ) - ( sptr )start.Text.Ptr; + } + else if ( tok_is_attribute(currtok) ) + { + eat( currtok.Type ); + // + + // If its a macro based attribute, this could be a functional macro such as Unreal's UE_DEPRECATED(...) + if ( check( Tok_Paren_Open)) + { + eat( Tok_Paren_Open ); + + s32 level = 0; + while (left && currtok.Type != Tok_Paren_Close && level == 0) + { + if (currtok.Type == Tok_Paren_Open) + ++ level; + if (currtok.Type == Tok_Paren_Close) + --level; + eat(currtok.Type); + } + eat(Tok_Paren_Close); + } + + len = ( ( sptr )prevtok.Text.Ptr + prevtok.Text.Len ) - ( sptr )start.Text.Ptr; + // ( ... ) + } + } + + if ( len > 0 ) + { + Str attribute_txt = { start.Text.Ptr, len }; + parser_pop(& ctx->parser); + + StrBuilder name_stripped = parser_strip_formatting(ctx, attribute_txt, parser_strip_formatting_dont_preserve_newlines ); + + Code result = make_code(); + result->Type = CT_PlatformAttributes; + result->Name = cache_str( strbuilder_to_str(name_stripped) ); + result->Content = result->Name; + // result->Token = + return ( CodeAttributes )result; + } + + parser_pop(& ctx->parser); + return NullCode; +} + +internal +Code parse_class_struct(Context* ctx, TokType which, bool inplace_def) +{ + if ( which != Tok_Decl_Class && which != Tok_Decl_Struct ) { + log_failure( "Error, expected class or struct, not %S\n%SB", toktype_to_str( which ), parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + return InvalidCode; + } + + Token name = NullToken; + + AccessSpec access = AccessSpec_Default; + CodeTypename parent = { nullptr }; + CodeBody body = { nullptr }; + CodeAttributes attributes = { nullptr }; + ModuleFlag mflags = ModuleFlag_None; + + Code result = InvalidCode; + + if ( check(Tok_Module_Export) ) { + mflags = ModuleFlag_Export; + eat( Tok_Module_Export ); + } + // + + eat( which ); + // + + attributes = parse_attributes(ctx); + // + + if ( check( Tok_Identifier ) ) { + name = parse_identifier(ctx, nullptr); + ctx->parser.scope->name = name.Text; + } + // + + 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 = {nullptr}; + + // TODO(Ed) : Make an AST_DerivedType, we'll store any arbitary derived type into there as a linear linked list of them. + 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(ctx, nullptr); + parent = def_type( parent_tok.Text ); + // : + + if (check(Tok_Comma)) + { + Arena arena = arena_init_from_memory( interface_arr_mem, kilobytes(4) ); + interfaces = array_init_reserve(CodeTypename, arena_allocator_info(& arena), 4 ); + do + { + eat( Tok_Comma ); + // : , + + if ( tok_is_access_specifier(currtok) ) { + eat(currtok.Type); + } + Token interface_tok = parse_identifier(ctx, nullptr); + + array_append( interfaces, def_type( interface_tok.Text ) ); + // : , ... + } + while ( check(Tok_Comma) ); + } + } + + if ( check( Tok_BraceCurly_Open ) ) { + body = parse_class_struct_body( ctx, 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(ctx); + // : , ... { }; + } + + s32 num_interfaces = scast(s32, interfaces ? array_num(interfaces) : 0); + + if ( which == Tok_Decl_Class ) + result = cast(Code, def_class( name.Text, def_assign( body, parent, access, attributes, interfaces, num_interfaces, specifiers, mflags ) )); + + else + result = cast(Code, def_struct( name.Text, def_assign( body, (CodeTypename)parent, access, attributes, interfaces, num_interfaces, specifiers, mflags ) )); + + if ( inline_cmt ) + result->InlineCmt = cast(Code, inline_cmt); + + if (interfaces) + array_free(interfaces); + return result; +} + +internal neverinline +CodeBody parse_class_struct_body(Context* ctx, TokType which, Token name) +{ + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope ); + + eat( Tok_BraceCurly_Open ); + // { + + CodeBody + result = (CodeBody) make_code(); + + if ( which == Tok_Decl_Class ) + result->Type = CT_Class_Body; + else + result->Type = CT_Struct_Body; + + while ( left && currtok_noskip.Type != Tok_BraceCurly_Close ) + { + Code member = Code_Invalid; + CodeAttributes attributes = { nullptr }; + CodeSpecifiers specifiers = { nullptr }; + + bool expects_function = false; + + // ctx->parser.Scope->Start = currtok_noskip; + + if ( currtok_noskip.Type == Tok_Preprocess_Hash ) + eat( Tok_Preprocess_Hash ); + + switch ( currtok_noskip.Type ) + { + case Tok_Statement_End: { + // TODO(Ed): Convert this to a general warning procedure + log_fmt("Dangling end statement found %SB\n", tok_to_strbuilder(ctx->Allocator_Temp, currtok_noskip)); + eat( Tok_Statement_End ); + continue; + } + case Tok_NewLine: { + member = fmt_newline; + eat( Tok_NewLine ); + break; + } + case Tok_Comment: { + member = cast(Code, parse_comment(ctx)); + break; + } + case Tok_Access_Public: { + member = access_public; + eat( Tok_Access_Public ); + eat( Tok_Assign_Classifer ); + // public: + break; + } + case Tok_Access_Protected: { + member = access_protected; + eat( Tok_Access_Protected ); + eat( Tok_Assign_Classifer ); + // protected: + break; + } + case Tok_Access_Private: { + member = access_private; + eat( Tok_Access_Private ); + eat( Tok_Assign_Classifer ); + // private: + break; + } + case Tok_Decl_Class: { + member = parse_complicated_definition(ctx, Tok_Decl_Class ); + // class + break; + } + case Tok_Decl_Enum: { + member = parse_complicated_definition(ctx, Tok_Decl_Enum ); + // enum + break; + } + case Tok_Decl_Friend: { + member = cast(Code, parser_parse_friend(ctx)); + // friend + break; + } + case Tok_Decl_Operator: { + member = cast(Code, parser_parse_operator_cast(ctx, NullCode)); + // operator () + break; + } + case Tok_Decl_Struct: { + member = parse_complicated_definition(ctx, Tok_Decl_Struct ); + // struct + break; + } + case Tok_Decl_Template: { + member = cast(Code, parser_parse_template(ctx)); + // template< ... > + break; + } + case Tok_Decl_Typedef: { + member = cast(Code, parser_parse_typedef(ctx)); + // typedef + break; + } + case Tok_Decl_Union: { + member = parse_complicated_definition(ctx, Tok_Decl_Union ); + // union + break; + } + case Tok_Decl_Using: { + member = cast(Code, parser_parse_using(ctx)); + // using + break; + } + case Tok_Operator: + { + //if ( currtok.Text[0] != '~' ) + //{ + // log_failure( "Operator token found in global body but not destructor unary negation\n%s", to_strbuilder(ctx->parser) ); + // return InvalidCode; + //} + + member = cast(Code, parser_parse_destructor(ctx, NullCode)); + // ~() + break; + } + case Tok_Preprocess_Define: { + member = cast(Code, parser_parse_define(ctx)); + // #define + break; + } + case Tok_Preprocess_Include: + { + member = cast(Code, parse_include(ctx)); + // #include + break; + } + + case Tok_Preprocess_If: + case Tok_Preprocess_IfDef: + case Tok_Preprocess_IfNotDef: + case Tok_Preprocess_ElIf: + member = cast(Code, parse_preprocess_cond(ctx)); + // # + break; + + case Tok_Preprocess_Else: { + member = cast(Code, preprocess_else); + eat( Tok_Preprocess_Else ); + // #else + break; + } + case Tok_Preprocess_EndIf: { + member = cast(Code, preprocess_endif); + eat( Tok_Preprocess_EndIf ); + // #endif + break; + } + + case Tok_Preprocess_Macro_Stmt: { + member = cast(Code, parse_simple_preprocess(ctx, Tok_Preprocess_Macro_Stmt )); + break; + } + + // case Tok_Preprocess_Macro: + // // + // macro_found = true; + // goto Preprocess_Macro_Bare_In_Body; + // break; + + case Tok_Preprocess_Pragma: { + member = cast(Code, parse_pragma(ctx)); + // #pragma + break; + } + + case Tok_Preprocess_Unsupported: { + member = cast(Code, parse_simple_preprocess(ctx, Tok_Preprocess_Unsupported)); + // # + break; + } + + case Tok_StaticAssert: { + member = parse_static_assert(ctx); + // static_assert + 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, ctx->Allocator_Temp)); + return InvalidCode; + } + } + //! Fallthrough intended + case Tok_Attribute_Open: + case Tok_Decl_GNU_Attribute: + case Tok_Decl_MSVC_Attribute: + #define Entry( attribute, str ) case attribute: + GEN_DEFINE_ATTRIBUTE_TOKENS + #undef Entry + { + attributes = parse_attributes(ctx); + // + } + //! Fallthrough intended + GEN_PARSER_CLASS_STRUCT_BODY_ALLOWED_MEMBER_TOK_SPECIFIER_CASES: + { + Specifier specs_found[16] = { Spec_NumSpecifiers }; + s32 NumSpecifiers = 0; + + while ( left && tok_is_specifier(currtok) ) + { + Specifier spec = str_to_specifier( currtok.Text ); + + b32 ignore_spec = false; + + switch ( spec ) + { + GEN_PARSER_CLASS_STRUCT_BODY_ALLOWED_MEMBER_SPECIFIER_CASES: + break; + + case Spec_Consteval: + expects_function = true; + break; + + case Spec_Const : + ignore_spec = true; + break; + + default: + log_failure( "Invalid specifier %S for class/struct member\n%S", spec_to_str(spec), strbuilder_to_str( parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp)) ); + parser_pop(& ctx->parser); + return InvalidCode; + } + + // Every specifier after would be considered part of the type type signature + if (ignore_spec) + break; + + specs_found[NumSpecifiers] = spec; + NumSpecifiers++; + eat( currtok.Type ); + } + + if ( NumSpecifiers ) + { + specifiers = def_specifiers_arr( NumSpecifiers, specs_found ); + } + // + + if ( tok_is_attribute(currtok) ) + { + // Unfortuantely Unreal has code where there is attirbutes before specifiers + CodeAttributes more_attributes = parse_attributes(ctx); + + if ( attributes ) + { + StrBuilder fused = strbuilder_make_reserve( ctx->Allocator_Temp, attributes->Content.Len + more_attributes->Content.Len ); + strbuilder_append_fmt( & fused, "%SB %SB", attributes->Content, more_attributes->Content ); + + Str attrib_name = strbuilder_to_str(fused); + attributes->Name = cache_str( attrib_name ); + attributes->Content = attributes->Name; + // + } + + attributes = more_attributes; + } + + if ( currtok.Type == Tok_Operator && currtok.Text.Ptr[0] == '~' ) + { + member = cast(Code, parser_parse_destructor(ctx, specifiers )); + // ~() + break; + } + + if ( currtok.Type == Tok_Decl_Operator ) + { + member = cast(Code, parser_parse_operator_cast(ctx, specifiers )); + // operator () + break; + } + } + //! Fallthrough intentional + case Tok_Identifier: + case Tok_Preprocess_Macro_Typename: + case Tok_Spec_Const: + case Tok_Type_Unsigned: + case Tok_Type_Signed: + case Tok_Type_Short: + case Tok_Type_Long: + case Tok_Type_bool: + case Tok_Type_char: + case Tok_Type_int: + case Tok_Type_double: + { + if ( nexttok.Type == Tok_Paren_Open && name.Text.Len && currtok.Type == Tok_Identifier ) + { + if ( c_str_compare_len( name.Text.Ptr, currtok.Text.Ptr, name.Text.Len ) == 0 ) + { + member = cast(Code, parser_parse_constructor(ctx, specifiers )); + // () + break; + } + } + + member = parse_operator_function_or_variable(ctx, expects_function, attributes, specifiers ); + // operator ... + // or + // ... + } + break; + + default: + Token untyped_tok = currtok; + while ( left && currtok.Type != Tok_BraceCurly_Close ) + { + untyped_tok.Text.Len = ( (sptr)currtok.Text.Ptr + currtok.Text.Len ) - (sptr)untyped_tok.Text.Ptr; + eat( currtok.Type ); + } + member = untyped_str( untyped_tok.Text ); + // Something unknown + break; + } + + if ( member == Code_Invalid ) + { + log_failure( "Failed to parse member\n%SB", parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + parser_pop(& ctx->parser); + return InvalidCode; + } + body_append(result, member ); + } + + eat( Tok_BraceCurly_Close ); + // { } + parser_pop(& ctx->parser); + return result; +} + +internal +CodeComment parse_comment(Context* ctx) +{ + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope ); + + CodeComment + result = (CodeComment) make_code(); + result->Type = CT_Comment; + result->Content = cache_str( currtok_noskip.Text ); + // result->Token = currtok_noskip; + eat( Tok_Comment ); + + parser_pop(& ctx->parser); + return result; +} + +internal +Code parse_complicated_definition(Context* ctx, TokType which) +{ + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope ); + + b32 is_inplace = false; + b32 is_fn_def = false; + + TokenSlice tokens = ctx->parser.tokens; + + s32 idx = ctx->parser.token_id; + s32 level = 0; + b32 had_def = false; + b32 had_paren = false; + for ( ; idx < tokens.num; idx++ ) + { + if ( tokens.ptr[ idx ].Type == Tok_BraceCurly_Open ) + level++; + + if ( tokens.ptr[ idx ].Type == Tok_BraceCurly_Close ) { + level--; + had_def = level == 0; + } + + b32 found_fn_def = had_def && had_paren; + + if ( level == 0 && (tokens.ptr[ 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(ctx, false, NullCode, NullCode); + // (...) ... { ... } + parser_pop(& ctx->parser); + return result; + } + + if ( ( idx - 2 ) == ctx->parser.token_id ) + { + // It's a forward declaration only + Code result = parse_forward_or_definition(ctx, which, is_inplace ); + // ; + parser_pop(& ctx->parser); + return result; + } + + Token tok = tokens.ptr[ idx - 1 ]; + if ( tok_is_specifier(tok) && spec_is_trailing( str_to_specifier( tok.Text)) ) + { + // (...) ...; + + s32 spec_idx = idx - 1; + Token spec = tokens.ptr[spec_idx]; + while ( tok_is_specifier(spec) && spec_is_trailing( str_to_specifier( spec.Text)) ) + { + -- spec_idx; + spec = tokens.ptr[spec_idx]; + } + + if ( tokens.ptr[spec_idx].Type == Tok_Paren_Close ) + { + // Forward declaration with trailing specifiers for a procedure + tok = tokens.ptr[spec_idx]; + + Code result = parse_operator_function_or_variable(ctx, false, NullCode, NullCode); + // , or Name> ... + parser_pop(& ctx->parser); + return result; + } + + log_failure( "Unsupported or bad member definition after %S declaration\n%SB", toktype_to_str(which), parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + parser_pop(& ctx->parser); + return InvalidCode; + } + if ( tok.Type == Tok_Identifier ) + { + tok = tokens.ptr[ idx - 2 ]; + bool is_indirection = tok.Type == Tok_Ampersand || tok.Type == Tok_Star; + bool ok_to_parse = false; + + if ( tok.Type == Tok_BraceCurly_Close ) + { + // Its an inplace definition + // { ... } ; + ok_to_parse = true; + is_inplace = true; + + CodeTypename type = cast(CodeTypename, parse_forward_or_definition(ctx, which, is_inplace)); + + // Should be a name right after the type. + Token name = parse_identifier(ctx, nullptr); + ctx->parser.scope->name = name.Text; + + CodeVar result = parse_variable_after_name(ctx, ModuleFlag_None, NullCode, NullCode, type, name.Text); + parser_pop(& ctx->parser); + return (Code) result; + } + else if ( tok.Type == Tok_Identifier && tokens.ptr[ idx - 3 ].Type == which ) + { + // Its a variable with type ID using namespace. + // ; + ok_to_parse = true; + } + else if ( tok.Type == Tok_Assign_Classifer + && ( ( tokens.ptr[idx - 5].Type == which && tokens.ptr[idx - 4].Type == Tok_Decl_Class ) + || ( tokens.ptr[idx - 4].Type == which)) + ) + { + // Its a forward declaration of an enum + // : ; + // : ; + ok_to_parse = true; + Code result = cast(Code, parser_parse_enum(ctx, ! parser_inplace_def)); + parser_pop(& ctx->parser); + return result; + } + else if ( is_indirection ) + { + // Its a indirection type with type ID using struct namespace. + // * ; + ok_to_parse = true; + } + + if ( ! ok_to_parse ) + { + log_failure( "Unsupported or bad member definition after %S declaration\n%SB", toktype_to_str(which), parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + parser_pop(& ctx->parser); + return InvalidCode; + } + + Code result = parse_operator_function_or_variable(ctx, false, NullCode, NullCode ); + // , or Name> ... + parser_pop(& ctx->parser); + return result; + } + else if ( tok.Type >= Tok_Type_Unsigned && tok.Type <= Tok_Type_MS_W64 ) + { + tok = tokens.ptr[ idx - 2 ]; + + if ( tok.Type != Tok_Assign_Classifer + || ( ( tokens.ptr[idx - 5].Type != which && tokens.ptr[idx - 4].Type != Tok_Decl_Class ) + && ( tokens.ptr[idx - 4].Type != which)) + ) + { + log_failure( "Unsupported or bad member definition after %S declaration\n%SB", toktype_to_str(which), parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + parser_pop(& ctx->parser); + return InvalidCode; + } + + // Its a forward declaration of an enum class + // : ; + // : ; + Code result = cast(Code, parser_parse_enum(ctx, ! parser_inplace_def)); + parser_pop(& ctx->parser); + return result; + } + else if ( tok.Type == Tok_BraceCurly_Close ) + { + // Its a definition + Code result = parse_forward_or_definition(ctx, which, is_inplace ); + // { ... }; + parser_pop(& ctx->parser); + return result; + } + else if ( tok.Type == Tok_BraceSquare_Close ) + { + // Its an array definition + Code result = parse_operator_function_or_variable(ctx, false, NullCode, NullCode ); + // [ ... ]; + parser_pop(& ctx->parser); + return result; + } + else + { + log_failure( "Unsupported or bad member definition after %S declaration\n%SB", toktype_to_str(which).Ptr, parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + parser_pop(& ctx->parser); + return InvalidCode; + } +} + +internal inline +Code parse_assignment_expression(Context* ctx) +{ + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope ); + + Code expr = { nullptr }; + + eat( Tok_Operator ); + // = + + Token expr_tok = currtok; + + if ( currtok.Type == Tok_Statement_End && currtok.Type != Tok_Comma ) + { + log_failure( "Expected expression after assignment operator\n%SB", parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + parser_pop(& ctx->parser); + return InvalidCode; + } + + s32 level = 0; + while ( left && currtok.Type != Tok_Statement_End && (currtok.Type != Tok_Comma || level > 0) ) + { + if (currtok.Type == Tok_BraceCurly_Open ) + level++; + if (currtok.Type == Tok_BraceCurly_Close ) + level--; + if (currtok.Type == Tok_Paren_Open) + level++; + else if (currtok.Type == Tok_Paren_Close) + level--; + + eat( currtok.Type ); + } + + if (left) { + expr_tok.Text.Len = ( ( sptr )currtok.Text.Ptr + currtok.Text.Len ) - ( sptr )expr_tok.Text.Ptr - 1; + } + expr = untyped_str( expr_tok.Text ); + // = + + parser_pop(& ctx->parser); + return expr; +} + +internal inline +Code parse_forward_or_definition(Context* ctx, TokType which, bool is_inplace ) +{ + Code result = InvalidCode; + + switch ( which ) + { + case Tok_Decl_Class: + result = cast(Code, parser_parse_class(ctx, is_inplace )); + return result; + + case Tok_Decl_Enum: + result = cast(Code, parser_parse_enum(ctx, is_inplace )); + return result; + + case Tok_Decl_Struct: + result = cast(Code, parser_parse_struct(ctx, is_inplace )); + return result; + + case Tok_Decl_Union: + result = cast(Code, parser_parse_union(ctx, is_inplace )); + return result; + + default: + log_failure( "Error, wrong token type given to parse_complicated_definition " + "(only supports class, enum, struct, union) \n%SB" + , parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + + return InvalidCode; + } +} + +// Function parsing is handled in multiple places because its initial signature is shared with variable parsing +internal inline +CodeFn parse_function_after_name(Context* ctx + , ModuleFlag mflags + , CodeAttributes attributes + , CodeSpecifiers specifiers + , CodeTypename ret_type + , Token name +) +{ + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope ); + + CodeParams params = parse_params(ctx, parser_use_parenthesis); + // ( ) + + Code suffix_specs = NullCode; + + // TODO(Ed), Review old comment : These have to be kept separate from the return type's specifiers. + while ( left && tok_is_specifier(currtok) ) + { + // For Unreal's PURE_VIRTUAL Support + Macro* macro = lookup_macro( currtok.Text ); + if (macro && tok_is_specifier(currtok)) + { + suffix_specs = parse_simple_preprocess(ctx, Tok_Preprocess_Macro_Expr); + continue; + } + if ( specifiers == nullptr ) + { + specifiers = def_specifier( str_to_specifier( currtok.Text) ); + eat( currtok.Type ); + continue; + } + + specifiers_append(specifiers, str_to_specifier( currtok.Text) ); + eat( currtok.Type ); + } + // ( ) + + // Check for trailing specifiers... + CodeAttributes post_rt_attributes = parse_attributes(ctx); + 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; + } + } + // ( ) + + CodeBody body = NullCode; + CodeComment inline_cmt = NullCode; + if ( check( Tok_BraceCurly_Open ) ) + { + body = cast(CodeBody, parse_function_body(ctx)); + if ( cast(Code, body) == Code_Invalid ) + { + parser_pop(& ctx->parser); + return InvalidCode; + } + // ( ) { } + } + else if ( check(Tok_Operator) && currtok.Text.Ptr[0] == '=' ) + { + eat(Tok_Operator); + 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); + // ( ) = 0 + } + Token stmt_end = currtok; + eat( Tok_Statement_End ); + + if ( currtok_noskip.Type == Tok_Comment && currtok_noskip.Line == stmt_end.Line ) + inline_cmt = parse_comment(ctx); + // ( ) < = 0 or delete > ; + } + + + if (body == nullptr) + { + Token stmt_end = currtok; + eat( Tok_Statement_End ); + // ( ) < = 0 or delete > ; + + if ( currtok_noskip.Type == Tok_Comment && currtok_noskip.Line == stmt_end.Line ) + inline_cmt = parse_comment(ctx); + // ( ) < = 0 or delete > ; + } + + StrBuilder + name_stripped = strbuilder_make_str( ctx->Allocator_Temp, name.Text ); + strbuilder_strip_space(name_stripped); + + CodeFn + result = (CodeFn) make_code(); + result->Name = cache_str( strbuilder_to_str(name_stripped) ); + result->ModuleFlags = mflags; + + if ( body ) + { + switch ( body->Type ) + { + case CT_Function_Body: + case CT_Untyped: + break; + + default: + { + log_failure("Body must be either of Function_Body or Untyped type, %S\n%SB", code_debug_str(body), parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp)); + parser_pop(& ctx->parser); + return InvalidCode; + } + } + + result->Type = CT_Function; + result->Body = body; + } + else + { + result->Type = CT_Function_Fwd; + } + + if ( attributes ) + result->Attributes = attributes; + + if ( specifiers ) + result->Specs = specifiers; + + if ( suffix_specs ) + result->SuffixSpecs = suffix_specs; + + result->ReturnType = ret_type; + + if ( params ) + result->Params = params; + + if ( inline_cmt ) + result->InlineCmt = inline_cmt; + + parser_pop(& ctx->parser); + return result; +} + +internal +Code parse_function_body(Context* ctx) +{ + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope ); + + eat( Tok_BraceCurly_Open ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = CT_Function_Body; + + // TODO : Support actual parsing of function body + Token start = currtok_noskip; + + s32 level = 0; + while ( left && ( currtok_noskip.Type != Tok_BraceCurly_Close || level > 0 ) ) + { + if ( currtok_noskip.Type == Tok_BraceCurly_Open ) + level++; + + else if ( currtok_noskip.Type == Tok_BraceCurly_Close && level > 0 ) + level--; + + eat( currtok_noskip.Type ); + } + + Token past = prevtok; + + s32 len = ( (sptr)prevtok.Text.Ptr + prevtok.Text.Len ) - (sptr)start.Text.Ptr; + + if ( len > 0 ) + { + Str str = { start.Text.Ptr, len }; + body_append( result, cast(Code, def_execution( str )) ); + } + + eat( Tok_BraceCurly_Close ); + + parser_pop(& ctx->parser); + return cast(Code, result); +} + +internal neverinline +CodeBody parse_global_nspace(Context* ctx, CodeType which) +{ + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope ); + + if ( which != CT_Namespace_Body && which != CT_Global_Body && which != CT_Export_Body && which != CT_Extern_Linkage_Body ) + return InvalidCode; + + if ( which != CT_Global_Body ) + eat( Tok_BraceCurly_Open ); + // { + + CodeBody + result = (CodeBody) make_code(); + result->Type = which; + + while ( left && currtok_noskip.Type != Tok_BraceCurly_Close ) + { + Code member = Code_Invalid; + CodeAttributes attributes = { nullptr }; + CodeSpecifiers specifiers = { nullptr }; + + bool expects_function = false; + + // ctx->parser.Scope->Start = currtok_noskip; + + if ( currtok_noskip.Type == Tok_Preprocess_Hash ) + eat( Tok_Preprocess_Hash ); + + b32 macro_found = false; + + switch ( currtok_noskip.Type ) + { + 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_pop( & ctx->parser); + return InvalidCode; + } + break; + case Tok_Statement_End: + { + // TODO(Ed): Convert this to a general warning procedure + log_fmt("Dangling end statement found %SB\n", tok_to_strbuilder(ctx->Allocator_Temp, currtok_noskip)); + eat( Tok_Statement_End ); + continue; + } + case Tok_NewLine: + // Empty lines are auto skipped by Tokens.current() + member = fmt_newline; + eat( Tok_NewLine ); + break; + + case Tok_Comment: + member = cast(Code, parse_comment(ctx)); + break; + + case Tok_Decl_Class: + member = parse_complicated_definition(ctx, Tok_Decl_Class ); + // class + break; + + case Tok_Decl_Enum: + member = parse_complicated_definition(ctx, Tok_Decl_Enum ); + // enum + break; + + case Tok_Decl_Extern_Linkage: + if ( which == CT_Extern_Linkage_Body ) + log_failure( "Nested extern linkage\n%SB", parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + + member = cast(Code, parser_parse_extern_link(ctx)); + // extern "..." { ... } + break; + + case Tok_Decl_Namespace: + member = cast(Code, parser_parse_namespace(ctx)); + // namespace { ... } + break; + + case Tok_Decl_Struct: + member = parse_complicated_definition(ctx, Tok_Decl_Struct ); + // struct ... + break; + + case Tok_Decl_Template: + member = cast(Code, parser_parse_template(ctx)); + // template<...> ... + break; + + case Tok_Decl_Typedef: + member = cast(Code, parser_parse_typedef(ctx)); + // typedef ... + break; + + case Tok_Decl_Union: + member = parse_complicated_definition(ctx, Tok_Decl_Union ); + // union ... + break; + + case Tok_Decl_Using: + member = cast(Code, parser_parse_using(ctx)); + // using ... + break; + + case Tok_Preprocess_Define: + member = cast(Code, parser_parse_define(ctx)); + // #define ... + break; + + case Tok_Preprocess_Include: + member = cast(Code, parse_include(ctx)); + // #include ... + break; + + case Tok_Preprocess_If: + case Tok_Preprocess_IfDef: + case Tok_Preprocess_IfNotDef: + case Tok_Preprocess_ElIf: + member = cast(Code, parse_preprocess_cond(ctx)); + // # ... + break; + + case Tok_Preprocess_Else: + member = cast(Code, preprocess_else); + eat( Tok_Preprocess_Else ); + // #else + break; + + case Tok_Preprocess_EndIf: + member = cast(Code, preprocess_endif); + eat( Tok_Preprocess_EndIf ); + // #endif + break; + + case Tok_Preprocess_Macro_Stmt: { + member = cast(Code, parse_simple_preprocess(ctx, Tok_Preprocess_Macro_Stmt )); + break; + } + + case Tok_Preprocess_Pragma: { + member = cast(Code, parse_pragma(ctx)); + // #pragma ... + } + break; + + case Tok_Preprocess_Unsupported: { + member = cast(Code, parse_simple_preprocess(ctx, Tok_Preprocess_Unsupported )); + // # ... + } + break; + + case Tok_StaticAssert: { + member = cast(Code, parse_static_assert(ctx)); + // static_assert( , ... ); + } + break; + + case Tok_Module_Export: { + if ( which == CT_Export_Body ) + log_failure( "Nested export declaration\n%SB", parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + + member = cast(Code, parser_parse_export_body(ctx)); + // export { ... } + } + break; + + case Tok_Module_Import: { + // import ... + 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%SB", parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp)); + return InvalidCode; + } + } + //! Fallthrough intentional + case Tok_Attribute_Open: + case Tok_Decl_GNU_Attribute: + case Tok_Decl_MSVC_Attribute: + #define Entry( attribute, str ) case attribute: + GEN_DEFINE_ATTRIBUTE_TOKENS + #undef Entry + { + attributes = parse_attributes(ctx); + // + } + //! Fallthrough intentional + GEN_PARSER_CLASS_GLOBAL_NSPACE_ALLOWED_MEMBER_TOK_SPECIFIER_CASES: + { + Specifier specs_found[16] = { Spec_NumSpecifiers }; + s32 NumSpecifiers = 0; + + while ( left && tok_is_specifier(currtok) ) + { + Specifier spec = str_to_specifier( currtok.Text ); + + bool ignore_spec = false; + + switch ( spec ) + { + GEN_PARSER_CLASS_GLOBAL_NSPACE_ALLOWED_MEMBER_SPECIFIER_CASES: + break; + + case Spec_Consteval: + expects_function = true; + break; + + case Spec_Const: + ignore_spec = true; + break; + + default: + Str spec_str = spec_to_str(spec); + + log_failure( "Invalid specifier %S for variable\n%S", spec_str, strbuilder_to_str( parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp)) ); + parser_pop(& ctx->parser); + return InvalidCode; + } + + if (ignore_spec) + break; + + specs_found[NumSpecifiers] = spec; + NumSpecifiers++; + eat( currtok.Type ); + } + + if ( NumSpecifiers ) + { + specifiers = def_specifiers_arr( NumSpecifiers, specs_found ); + } + // + } + //! Fallthrough intentional + case Tok_Identifier: + case Tok_Preprocess_Macro_Typename: + case Tok_Spec_Const: + case Tok_Type_Long: + case Tok_Type_Short: + case Tok_Type_Signed: + case Tok_Type_Unsigned: + case Tok_Type_bool: + case Tok_Type_char: + case Tok_Type_double: + case Tok_Type_int: + { + // This s only in a scope so that Preprocess_Macro_Bare_In_Body works without microsoft extension warnings + { + Code constructor_destructor = parse_global_nspace_constructor_destructor( ctx, specifiers ); + // Possible constructor implemented at global file scope. + if ( constructor_destructor ) + { + member = constructor_destructor; + break; + } + + bool found_operator_cast_outside_class_implmentation = false; + s32 idx = ctx->parser.token_id; + + for ( ; idx < ctx->parser.tokens.num; idx++ ) + { + Token tok = ctx->parser.tokens.ptr[ idx ]; + + if ( tok.Type == Tok_Identifier ) + { + idx++; + tok = ctx->parser.tokens.ptr[ idx ]; + if ( tok.Type == Tok_Access_StaticSymbol ) + continue; + + break; + } + + if ( tok.Type == Tok_Decl_Operator ) + found_operator_cast_outside_class_implmentation = true; + + break; + } + + if ( found_operator_cast_outside_class_implmentation ) + { + member = cast(Code, parser_parse_operator_cast(ctx, specifiers )); + // ::operator () { ... } + break; + } + } + + member = parse_operator_function_or_variable(ctx, expects_function, attributes, specifiers ); + // ... + } + } + + if ( member == Code_Invalid ) + { + log_failure( "Failed to parse member\nToken: %SB\nContext:\n%SB", tok_to_strbuilder(ctx->Allocator_Temp, currtok_noskip), parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) ); + parser_pop(& ctx->parser); + return InvalidCode; + } + + // log_fmt("Global Body Member: %s", member->debug_str()); + body_append(result, member ); + } + + if ( which != CT_Global_Body ) + eat( Tok_BraceCurly_Close ); + // { } + + parser_pop(& ctx->parser); + return result; +} + +internal inline +Code parse_global_nspace_constructor_destructor(Context* ctx, CodeSpecifiers specifiers) +{ + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope ); + + Code result = { nullptr }; + + /* + 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. + */ + TokenSlice tokens = ctx->parser.tokens; + + s32 idx = ctx->parser.token_id; + Token nav = tokens.ptr[ idx ]; + for ( ; idx < tokens.num; idx++, nav = tokens.ptr[ idx ] ) + { + if ( nav.Text.Ptr[0] == '<' ) + { + // Skip templated expressions as they mey have expressions with the () operators + s32 capture_level = 0; + s32 template_level = 0; + for ( ; idx < tokens.num; idx++, nav = tokens.ptr[idx] ) + { + if (nav.Text.Ptr[ 0 ] == '<') + ++ template_level; + + if (nav.Text.Ptr[ 0 ] == '>') + -- template_level; + if (nav.Type == Tok_Operator && nav.Text.Ptr[1] == '>') + -- template_level; + + if ( nav.Type == Tok_Paren_Open) + { + if (template_level != 0 ) + ++ capture_level; + else + break; + } + + if ( template_level != 0 && nav.Type == Tok_Paren_Close) + -- capture_level; + } + } + + if ( nav.Type == Tok_Paren_Open ) + break; + } + + -- idx; + Token tok_right = tokens.ptr[idx]; + Token tok_left = NullToken; + + if (tok_right.Type != Tok_Identifier) + { + parser_pop(& ctx->parser); + // We're not dealing with a constructor if there is no identifier right before the opening of a parameter's scope. + return result; + } + + -- idx; + tok_left = tokens.ptr[idx]; + // ... + + bool possible_destructor = false; + if ( tok_left.Type == Tok_Operator && tok_left.Text.Ptr[0] == '~') + { + possible_destructor = true; + -- idx; + tok_left = tokens.ptr[idx]; + } + + if ( tok_left.Type != Tok_Access_StaticSymbol ) { + parser_pop(& ctx->parser); + return result; + } + + -- idx; + tok_left = tokens.ptr[idx]; + // ... :: + + // We search toward the left until we find the next valid identifier + s32 capture_level = 0; + s32 template_level = 0; + while ( idx != ctx->parser.token_id ) + { + if (tok_left.Text.Ptr[ 0 ] == '<') + ++ template_level; + + if (tok_left.Text.Ptr[ 0 ] == '>') + -- template_level; + if (tok_left.Type == Tok_Operator && tok_left.Text.Ptr[1] == '>') + -- template_level; + + if ( template_level != 0 && tok_left.Type == Tok_Paren_Open) + ++ capture_level; + + if ( template_level != 0 && tok_left.Type == Tok_Paren_Close) + -- capture_level; + + if ( capture_level == 0 && template_level == 0 && tok_left.Type == Tok_Identifier ) + break; + + -- idx; + tok_left = tokens.ptr[idx]; + } + + bool is_same = c_str_compare_len( tok_right.Text.Ptr, tok_left.Text.Ptr, tok_right.Text.Len ) == 0; + if (tok_left.Type == Tok_Identifier && is_same) + { + // We have found the pattern we desired + if (possible_destructor) + { + // :: ~ ( + result = cast(Code, parser_parse_destructor(ctx, specifiers )); + } + else { + // :: ( + result = cast(Code, parser_parse_constructor(ctx, specifiers )); + } + } + + parser_pop(& ctx->parser); + return result; +} + +// TODO(Ed): I want to eventually change the identifier to its own AST type. +// This would allow distinction of the qualifier for a symbol :: +// This would also allow +internal +Token parse_identifier(Context* ctx, bool* possible_member_function) +{ + ParseStackNode scope = NullScope; + parser_push(& ctx->parser, & scope ); + + Token name = currtok; + ctx->parser.scope->name = name.Text; + + Macro* macro = lookup_macro(currtok.Text); + b32 accept_as_identifier = macro && bitfield_is_set(MacroFlags, macro->Flags, MF_Allow_As_Identifier ); + b32 is_decarator = macro && bitfield_is_set(MacroFlags, macro->Flags, MF_Identifier_Decorator ); + + // Typename can be: '::' + // If that is the case first option will be Tok_Access_StaticSymbol below + if (check(Tok_Identifier) || accept_as_identifier) + { + if (is_decarator) { + Code name_macro = parse_simple_preprocess(ctx, currtok.Type); + name.Text.Len = ( ( sptr )prevtok.Text.Ptr + prevtok.Text.Len ) - ( sptr )name.Text.Ptr; + } + else { + eat(Tok_Identifier); + } + } + // + + parse_template_args(ctx, & name); + //