From a4cb0c12ef935581500fe159653fb8383d3af176 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Wed, 5 Apr 2023 03:03:56 -0400 Subject: [PATCH] WIP: Design is almost done, impl this weekend. --- .vscode/settings.json | 3 +- Readme.md | 44 +- project/Bloat.cpp | 2 + project/Bloat.hpp | 11 +- project/gen.cpp | 411 +++++++++++------- project/gen.hpp | 574 +++++++++++++------------- project/gen.singleheader.c99.cpp | 543 ++++++++++++++++++++++++ project/gen.singleheader.c99.data | 55 +++ project/gen.singleheader.c99.refactor | 34 ++ project/gen.singleheader.cpp | 0 singleheader/genc.refactor | 4 + test/Array.hpp | 58 ++- test/c99/meson.build | 22 + test/c99/table.h | 79 ++++ test/c99/test.c99.c | 40 ++ test/math.hpp | 6 +- test/test.singleheader.cpp | 0 thirdparty/zpl.h | 2 +- 18 files changed, 1414 insertions(+), 474 deletions(-) create mode 100644 project/gen.singleheader.c99.cpp create mode 100644 project/gen.singleheader.c99.data create mode 100644 project/gen.singleheader.c99.refactor create mode 100644 project/gen.singleheader.cpp create mode 100644 singleheader/genc.refactor create mode 100644 test/c99/meson.build create mode 100644 test/c99/table.h create mode 100644 test/c99/test.c99.c create mode 100644 test/test.singleheader.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 7d111f3..40870c8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,6 +7,7 @@ "utility": "cpp", "xtr1common": "cpp", "xutility": "cpp", - "initializer_list": "cpp" + "initializer_list": "cpp", + "table.h": "c" } } \ No newline at end of file diff --git a/Readme.md b/Readme.md index 29b5e69..b3e1847 100644 --- a/Readme.md +++ b/Readme.md @@ -2,7 +2,7 @@ An attempt at simple staged metaprogramming for c/c++. -This library is intended for small-to midsize projects. +This library is intended for small-to midsized projects. ### TOC @@ -174,20 +174,27 @@ The construction will fail and return InvalidCode otherwise. Interface : -* def_forward_decl * def_class +* def_class_body +* def_class_fwd +* def_enum +* def_enum_class +* def_enum_body * def_global_body -* def_proc -* def_proc_body * def_namespace * def_namespace_body +* def_operator +* def_operator_fwd * def_param * def_params -* def_operator +* def_proc +* def_proc_body +* def_proc_fwd * def_specifier * def_specifiers * def_struct * def_struct_body +* def_struct_fwd * def_variable * def_type * def_using @@ -206,19 +213,20 @@ Code ASTs may be explictly validated at anytime using Code's check() member func Interface : -* make_forward_decl * make_class +* make_enum +* make_enum_class +* make_fwd * make_global_body -* make_proc * make_namespace -* make_params * make_operator +* make_params +* make_proc * make_specifiers * make_struct * make_variable * make_type * make_using -* make_using_namespace ### Parse construction @@ -226,19 +234,29 @@ A string provided to the API is parsed for the intended language construct. Interface : -* parse_forward_decl * parse_class -* parse_glboal_body -* parse_proc +* parse_classes +* parse_class_fwd +* parse_classes_fwd +* parse_enum +* parse_enums +* parse_global_body * parse_namespace +* parse_namespaces * parse_params +* parse_proc +* parse_procs * parse_operator +* parse_operators * parse_specifiers * parse_struct +* parse_strucs * parse_variable +* parse_variables * parse_type +* parse_types * parse_using -* parse_using +* parse_usings The parse API treats any execution scope definitions with no validation and are turned into untyped Code ASTs. This includes the assignmetn of variables; due to the library not yet supporting c/c++ expression parsing. diff --git a/project/Bloat.cpp b/project/Bloat.cpp index 6edb9e3..5bfaf2e 100644 --- a/project/Bloat.cpp +++ b/project/Bloat.cpp @@ -124,5 +124,7 @@ sw token_fmt_va( char* buf, uw buf_size, char const* fmt, s32 num_tokens, va_lis } } + tokmap_clear( & tok_map ); + return result; } diff --git a/project/Bloat.hpp b/project/Bloat.hpp index bc0ba7c..4d5d967 100644 --- a/project/Bloat.hpp +++ b/project/Bloat.hpp @@ -73,17 +73,17 @@ #endif #define bit( Value_ ) ( 1 << Value_ ) -#define bitfield_is_equal( Field_, Mask_ ) ( ( Mask_ & Field_ ) == Mask_ ) +#define bitfield_is_equal( Field_, Mask_ ) ( ( (Mask_) & (Field_) ) == (Mask_) ) #define ct constexpr #define forceinline ZPL_ALWAYS_INLINE #define print_nl( _) zpl_printf("\n") -#define ccast( Type_, Value_ ) * const_cast< Type_* >( & Value_ ) +#define ccast( Type_, Value_ ) * const_cast< Type_* >( & (Value_) ) #define scast( Type_, Value_ ) static_cast< Type_ >( Value_ ) #define rcast( Type_, Value_ ) reinterpret_cast< Type_ >( Value_ ) -#define pcast( Type_, Value_ ) ( * (Type_*)( & Value_ ) ) +#define pcast( Type_, Value_ ) ( * (Type_*)( & (Value_) ) ) #define txt_impl( Value_ ) #Value_ #define txt( Value_ ) txt_impl( Value_ ) -#define txt_with_length( Value_ ) txt_impl( Value_ ), sizeof( txt_impl( Value_) ) +#define txt_with_length( Value_ ) txt_impl( Value_ ), sizeof( txt_impl( Value_ ) ) #define do_once() \ do \ @@ -131,6 +131,8 @@ namespace Memory void cleanup(); } +sw token_fmt_va( char* buf, uw buf_size, char const* fmt, s32 num_tokens, va_list va ); + inline char const* token_fmt( char const* fmt, sw num_tokens, ... ) { @@ -185,3 +187,4 @@ sw fatal(char const *fmt, ...) return -1; #endif } + diff --git a/project/gen.cpp b/project/gen.cpp index 65d151f..2b0dc7a 100644 --- a/project/gen.cpp +++ b/project/gen.cpp @@ -4,40 +4,57 @@ #ifdef gen_time namespace gen { + ZPL_TABLE_DEFINE( StringTable, str_tbl_, string ); + ZPL_TABLE_DEFINE( TypeTable, type_tbl_ , Code ); + namespace StaticData { static array(CodePOD) CodePool = nullptr; + + static array(arena) StringArenas = nullptr; + + static StringTable StringMap; + static TypeTable TypeMap; + + static sw InitSize_CodePool = megabytes(64); + static sw InitSize_StringArena = megabytes(32); + static sw InitSize_StringTable = megabytes(4); + static sw InitSize_TypeTable = megabytes(4); + + static allocator Allocator_CodePool = zpl_heap(); + static allocator Allocator_StringArena = zpl_heap(); + static allocator Allocator_StringTable = zpl_heap(); + static allocator Allocator_TypeTable = zpl_heap(); } #pragma region CONSTANTS # ifdef GEN_DEFINE_LIBRARY_CODE_CONSTANTS - const Code t_void; + Code t_void; - const Code t_bool; - const Code t_char; - const Code t_char_wide; + Code t_bool; + Code t_char; + Code t_char_wide; - const Code t_s8; - const Code t_s16; - const Code t_s32; - const Code t_s64; + Code t_s8; + Code t_s16; + Code t_s32; + Code t_s64; - const Code t_u8; - const Code t_u16; - const Code t_u32; - const Code t_u64; + Code t_u8; + Code t_u16; + Code t_u32; + Code t_u64; - const Code t_sw; - const Code t_uw; + Code t_sw; + Code t_uw; - const Code t_f32; - const Code t_f64; - - const Code spec_constexpr; - const Code spec_inline; + Code t_f32; + Code t_f64; # endif -#pragma endregion CONSTANTS + Code spec_constexpr; + Code spec_inline; +#pragma endregion CONSTANTS /* Used internally to retireve a Code object form the CodePool. @@ -66,10 +83,9 @@ namespace gen t_bool_write = ccast( Code, t_void ); t_bool_write = def_type( txt(void) ); - # define def_constant_code_type( Type_ ) \ - Code& \ - t_##Type_##_write = ccast( Code, t_##Type_ ); \ - t_##Type_##_write = def_type( txt(Type_) ) \ + # define def_constant_code_type( Type_ ) \ + Code& \ + t_##Type_ = def_type( txt(Type_) ) def_constant_code_type( bool ); def_constant_code_type( char ); @@ -109,80 +125,92 @@ namespace gen #endif } - Code decl_type( Code type, char const* name, Code specifiers ) + void clear_code_pool() { - using namespace ECode; + array_clear( StaticData::CodePool ); - if ( type->Type != Specifiers ) - { - log_failure( "gen::decl_type: type is not a Typename"); - return InvalidCode; - } + sw size = array_capacity( StaticData::CodePool ); - if ( type->Type != Typename ) - { - log_failure( "gen::decl_type: specifiers is not a 'Specfiers' type"); - return InvalidCode; - } - - Code - result = make_code(); - result->Type = Decl_Type; - result->Name = string_make( g_allocator, name ); - - array_init( result->Entries, g_allocator ); - result->add( specifiers ); - result->add( type ); - result.lock(); - - return result; + zpl_memset( StaticData::CodePool, 0, size ); } - Code decl_proc( char const* name - , Code specifiers - , Code params - , Code ret_type - ) + allocator get_string_allocator( s32 str_length ) { - using namespace ECode; + using namespace StaticData; - if ( specifiers->Type != Specifiers ) + if ( StringArenas->total_allocated + str_length > StringArenas->total_size ) { - log_failure( "gen::decl_fn: specifiers was not a `Specifiers` type" ); - return InvalidCode; + arena new_arena; + arena_init_from_allocator( & new_arena, Allocator_StringArena, InitSize_StringArena ); + + array_append( StringArenas, new_arena ); + + return arena_allocator( StringArenas ); } - if ( params->Type != Parameters ) - { - log_failure( "gen::decl_fn: params was not a `Parameters` type" ); - return InvalidCode; - } - - if ( ret_type->Type != Typename ) - { - log_failure( "gen::decl_fn: ret_type was not a Typename" ); - return InvalidCode; - } - - Code - result = make_code(); - result->Type = Decl_Function; - result->Name = string_make( g_allocator, name ); - - array_init( result->Entries, g_allocator ); - - if ( specifiers ) - result->add( specifiers ); - - result->add( ret_type ); - - if ( params ) - result->add( params ); - - result.lock(); - return result; + return arena_allocator( StringArenas ); } + // Will either make or retrive a code string. + string code_string( char const* cstr, s32 length ) + { + s32 hash_length = length > kilobytes(1) ? kilobytes(1) : length; + + u32 key = crc32( cstr, hash_length ); + + string* result = str_tbl_get( & StaticData::StringMap, key ); + + if ( result ) + { + return * result; + } + + str_tbl_set( & StaticData::StringMap, key, * result ); + + return * result; + } + + void set_init_reserve_code_pool( sw size ) + { + StaticData::InitSize_CodePool = size; + } + + void set_init_reserve_string_arena( sw size ) + { + StaticData::InitSize_StringArena = size; + } + + void set_init_reserve_string_table( sw size ) + { + StaticData::InitSize_StringTable = size; + } + + void set_init_reserve_type_table( sw size ) + { + StaticData::InitSize_TypeTable = size; + } + + void set_allocator_code_pool( allocator pool_allocator ) + { + StaticData::Allocator_CodePool = pool_allocator; + } + + void set_allocator_string_arena( allocator string_allocator ) + { + StaticData::Allocator_StringArena = string_allocator; + } + + void set_allocator_string_table( allocator string_allocator ) + { + StaticData::Allocator_StringArena = string_allocator; + } + + void set_allocator_type_table( allocator type_reg_allocator ) + { + StaticData::Allocator_TypeTable = type_reg_allocator; + } + +# pragma region Upfront Constructors Code def_params( s32 num, ... ) { using namespace ECode; @@ -271,7 +299,7 @@ namespace gen switch ( body->Type ) { - case Function_Body: + case Proc_Body: case Untyped: break; @@ -285,7 +313,7 @@ namespace gen Code result = make_code(); result->Name = string_make( g_allocator, name ); - result->Type = Function; + result->Type = Proc; array_init( result->Entries, g_allocator ); @@ -331,8 +359,7 @@ namespace gen switch ( entry->Type ) { - case Decl_Function: - case Decl_Type: + case Proc_Forward: case Namespace: case Namespace_Body: case Parameters: @@ -387,8 +414,7 @@ namespace gen switch ( entry->Type ) { - case Decl_Function: - case Decl_Type: + case Proc_Forward: case Namespace: case Namespace_Body: case Parameters: @@ -630,7 +656,7 @@ namespace gen return result; } - Code def_type( char const* name ) + Code def_type( char const* name, Code specifiers ) { Code result = make_code(); @@ -654,57 +680,9 @@ namespace gen return result; } +# pragma endregion Upfront Constructors - Code untyped_str(char const* fmt) - { - Code - result = make_code(); - result->Name = string_make( g_allocator, fmt ); - result->Type = ECode::Untyped; - - return result; - } - - Code untyped_fmt(char const* fmt, ...) - { - local_persist thread_local - char buf[ZPL_PRINTF_MAXLEN] = { 0 }; - - va_list va; - va_start(va, fmt); - zpl_snprintf_va(buf, ZPL_PRINTF_MAXLEN, fmt, va); - va_end(va); - - Code - result = make_code(); - result->Name = string_make( g_allocator, fmt ); - result->Type = ECode::Untyped; - result->Content = string_make( g_allocator, buf ); - - return result; - } - - Code untyped_token_fmt( char const* fmt, s32 num_tokens, ... ) - { - local_persist thread_local - char buf[ZPL_PRINTF_MAXLEN] = { 0 }; - - va_list va; - va_start(va, fmt); - token_fmt_va(buf, ZPL_PRINTF_MAXLEN, fmt, num_tokens, va); - va_end(va); - - Code - result = make_code(); - result->Name = string_make( g_allocator, fmt ); - result->Type = ECode::Untyped; - result->Content = string_make( g_allocator, buf ); - - result.lock(); - - return result; - } - +# pragma region Incremetnal Constructors Code make_proc( char const* name , Code specifiers , Code params @@ -734,7 +712,7 @@ namespace gen Code result = make_code(); result->Name = string_make( g_allocator, name ); - result->Type = Function; + result->Type = Proc; array_init( result->Entries, g_allocator ); @@ -788,12 +766,12 @@ namespace gen return result; } - Code make_unit( char const* name ) + Code make_global_body( char const* name = "", s32 num = 0, ... ) { Code result = make_code(); - result->Type = ECode::Unit; - result->Name = string_make( g_allocator, name ); + result->Type = ECode::Global_Body; + result->Name = string_make( g_allocator, ""); array_init( result->Entries, g_allocator ); @@ -802,7 +780,9 @@ namespace gen return result; } +# pragma endregion Incremetnal Constructions +# pragma region Parsing Constructors Code parse_proc( char const* def, s32 length ) { if ( def == nullptr ) @@ -856,7 +836,7 @@ namespace gen while ( left && char_is_space( * scanner ) ) \ { \ left--; \ - scanner++ ; + scanner++ ; \ } #define Get @@ -947,7 +927,7 @@ namespace gen Code result = make_code(); result->Name = string_make( g_allocator, name ); - result->Type = ECode::Function; + result->Type = ECode::Proc; array_init( result->Entries, g_allocator ); @@ -985,8 +965,124 @@ namespace gen } +# pragma endregion Parsing Constructors +# pragma region Untyped Constructors + Code untyped_str(char const* fmt) + { + Code + result = make_code(); + result->Name = string_make( g_allocator, fmt ); + result->Type = ECode::Untyped; + result->Content = result->Name; + return result; + } + + Code untyped_fmt(char const* fmt, ...) + { + local_persist thread_local + char buf[ZPL_PRINTF_MAXLEN] = { 0 }; + + va_list va; + va_start(va, fmt); + zpl_snprintf_va(buf, ZPL_PRINTF_MAXLEN, fmt, va); + va_end(va); + + Code + result = make_code(); + result->Name = string_make( g_allocator, fmt ); + result->Type = ECode::Untyped; + result->Content = string_make( g_allocator, buf ); + + return result; + } + + Code untyped_token_fmt( char const* fmt, s32 num_tokens, ... ) + { + local_persist thread_local + char buf[ZPL_PRINTF_MAXLEN] = { 0 }; + + va_list va; + va_start(va, fmt); + token_fmt_va(buf, ZPL_PRINTF_MAXLEN, fmt, num_tokens, va); + va_end(va); + + Code + result = make_code(); + result->Name = string_make( g_allocator, fmt ); + result->Type = ECode::Untyped; + result->Content = string_make( g_allocator, buf ); + + result.lock(); + + return result; + } +# pragma endregion Untyped Constructors + +# pragma region AST + bool AST::add( AST* other ) + { + switch ( Type ) + { + using namespace ECode; + + case Untyped: + break; + + case Global_Body: + break; + + case Proc: + break; + + case Proc_Body: + break; + + case Proc_Forward: + break; + + case Namespace: + break; + + case Namespace_Body: + break; + + case Parameters: + break; + + case Specifiers: + break; + + case Struct: + break; + + case Struct_Body: + break; + + case Variable: + break; + + case Typedef: + break; + + case Typename: + break; + + case Using: + break; + } + + array_append( Entries, other ); + + other->Parent = this; + return true; + } + + bool AST::check() + { + + } string AST::to_string() const { @@ -1007,7 +1103,7 @@ namespace gen result = string_append_length( result, Content, string_length(Content) ); break; - case Decl_Function: + case Proc_Forward: { u32 index = 0; u32 left = array_count( Entries ); @@ -1040,14 +1136,7 @@ namespace gen } break; - case Decl_Type: - if ( Entries[0]->Type == Specifiers ) - result = string_append_fmt( result, "%s\n", Entries[0]->to_string()); - - result = string_append_fmt( result, "%s %s;\n", Entries[1]->to_string(), Name ); - break; - - case Function: + case Proc: { u32 index = 0; u32 left = array_count( Entries ); @@ -1080,7 +1169,7 @@ namespace gen } break; - case Function_Body: + case Proc_Body: fatal("NOT SUPPORTED YET"); break; @@ -1138,9 +1227,10 @@ namespace gen return result; } +# pragma endregion AST - +# pragma region Builder void Builder::print( Code code ) { Buffer = string_append_fmt( Buffer, "%s\n\n", code->to_string() ); @@ -1171,5 +1261,6 @@ namespace gen // file_seek( & File, 0 ); file_close( & File ); } +# pragma endregion Builder } #endif diff --git a/project/gen.hpp b/project/gen.hpp index 280accb..3df7c32 100644 --- a/project/gen.hpp +++ b/project/gen.hpp @@ -13,17 +13,14 @@ * Expression validation : Execution expressions are defined using the untyped string API. There is no parse API for validating expression (possibly will add in the future) * Complete file parser DSL : This isn't like the unreal header tool. - Code injection to file or based off a file contents is not supported by the api. - However nothing is stopping you using the library for that purpose. + Code injection to file or based off a file contents is not supported by the api. However nothing is stopping you using the library for that purpose. * Modern c++ (STL library) features - As mentioned in [Usage](#Usage), the user is provided Code objects by calling the interface procedures to generate them or find existing matches. - The AST is managed by the library and provided the user via its interface prodedures. Notes: - * The allocator definitions used are exposed to the user incase they want to dictate memory usage* + * The allocator definitions used are exposed to the user incase they want to dictate memory usage * ASTs are wrapped for the user in a Code struct which essentially a warpper for a AST* type. * Both AST and Code have member symbols but their data layout is enforced to be POD types. @@ -58,22 +55,30 @@ Interface : - * def_forward_decl * def_class + * def_class_body + * def_class_fwd + * def_enum + * def_enum_class + * def_enum_body * def_global_body - * def_proc - * def_proc_body * def_namespace * def_namespace_body + * def_operator + * def_operator_fwd * def_param * def_params - * def_operator + * def_proc + * def_proc_body + * def_proc_fwd * def_specifier * def_specifiers * def_struct * def_struct_body + * def_struct_fwd * def_variable * def_type + * def_typedef * def_using * def_using_namespace @@ -82,27 +87,27 @@ A Code ast is provided but only completed upfront if all components are provided. Components are then added using the AST API for adding ASTs: - * code.add( AST* ) // Adds AST with validation. - * code.add_entry( AST* ) // Adds AST entry without validation. - * code.add_content( AST* ) // Adds AST string content without validation. + * code.add( AST* ) // Adds AST with validation. + * code.add_entry( AST* ) // Adds AST entry without validation. Code ASTs may be explictly validated at anytime using Code's check() member function. Interface : - * make_forward_decl * make_class + * make_enum + * make_enum_class * make_global_body - * make_proc * make_namespace - * make_params * make_operator + * make_params + * make_proc * make_specifiers * make_struct * make_variable * make_type + * make_typedef * make_using - * make_using_namespace ### Parse construction @@ -110,23 +115,33 @@ Interface : - * parse_forward_decl * parse_class - * parse_glboal_body - * parse_proc + * parse_classes + * parse_enum + * parse_enums + * parse_global_body * parse_namespace - * parse_params + * parse_namespaces * parse_operator - * parse_specifiers + * parse_operators + * parse_proc + * parse_procs * parse_struct + * parse_strucs * parse_variable + * parse_variables * parse_type + * parse_typedef + * parse_typedefs * parse_using - * parse_using + * parse_usings The parse API treats any execution scope definitions with no validation and are turned into untyped Code ASTs. This includes the assignmetn of variables; due to the library not yet supporting c/c++ expression parsing. + The plural variants provide an array of codes, its up to the user to add them to a body AST + (they are not auto-added to a body) + ### Untyped constructions Code ASTs are constructed using unvalidated strings. @@ -158,7 +173,7 @@ // #define GEN_DEFINE_DSL #define GEN_DEFINE_LIBRARY_CODE_CONSTANTS // #define GEN_BAN_CPP_TEMPLATES -// #define GEN_USE_FATAL +#define GEN_USE_FATAL #ifdef gen_time namespace gen @@ -179,41 +194,52 @@ namespace gen { enum Type : u8 { - Invalid, // Used only with improperly created Code nodes - Untyped, // User provided raw string - Decl_Function, // ( ) - Decl_Type, // ; - Function, // ( ) - Function_Body, // { } - Namespace, // Define a namespace - Namespace_Body, // { } - Parameters, // ... - Specifiers, // Used with functions, structs, variables - Struct, // struct - Struct_Body, // { } - Variable, // - Typedef, // typedef - Typename, // Typename, used with other types - Using, // using = - Unit, // Represents a file. + Invalid, + Untyped, + Access_Public, + Access_Private, + Access_Protected, + Class, + Enum, + Enum_Body, + Global_Body, + Namespace, + Namespace_Body, + Parameters, + Proc, + Proc_Body, + Proc_Forward, + Specifiers, + Struct, + Struct_Body, + Variable, + Typedef, + Typename, + Using, Num_Types }; - inline + local_persist char const* str( Type type ) { static char const* lookup[Num_Types] = { "Invalid", "Untyped", - "Decl_Function", - "Decl_Type", - "Function", - "Function_Body", + "Access_Public", + "Access_Private", + "Access_Protected", + "Class", + "Enum", + "Enum_Body", + "Global_Body", "Namespace", "Namespace_Body", "Parameters", + "Proc", + "Proc_Body", + "Proc_Forward", "Specifiers", "Struct", "Struct_Body", @@ -221,7 +247,6 @@ namespace gen "Typedef", "Typename", "Using", - "Unit", }; return lookup[ type ]; @@ -245,7 +270,7 @@ namespace gen inline char const* str( Type op ) { - static + local_persist char const* lookup[ Num_Ops ] = { "+", "-", @@ -262,43 +287,48 @@ namespace gen { enum Type : u8 { - Attribute, // [ Parent = this; } + forceinline + AST* body() + { + return Entries[0]; + } + + forceinline + bool check(); + forceinline bool has_entries() const { static bool lookup[ ECode::Num_Types] = { false, // Invalid false, // Untyped - true, // Decl_Function - true, // Decl_Type - true, // Function + false, + false, + false, + true, // Global_Body true, // Parameters + true, // Proc + true, // Proc_Body + true, // Proc_Forward false, // Specifies true, // Struct true, // Struct_Body true, // Variable true, // Typedef true, // Typename - true, // Using }; return lookup[Type]; @@ -394,7 +439,7 @@ namespace gen string to_string() const; - #pragma endregion Member API + #pragma endregion Member Procedures #define Using_Code_POD \ CodeT Type; \ @@ -434,20 +479,32 @@ namespace gen Code body() { if ( ast->Type == ECode::Invalid ) - fatal("Code::body: Type is invalid, cannot get"); + { + log_failure("Code::body: Type is invalid, cannot get"); + return InvalidCode; + } if ( ast->Entries == nullptr || array_count(ast->Entries) == 0 ) - fatal("Code::body: Entries of ast not properly setup."); + { + log_failure("Code::body: Entries of ast not properly setup."); + return InvalidCode; + } - return pcast( Code, ast->Entries[0]); + #ifdef GEN_ENFORCE_READONLY_AST + if ( ast->Readonly ) + { + log_failure("Attempted to a body AST from a readonly AST!"); + return InvalidCode; + } + #endif + + return * (Code*)( ast->body() ); } forceinline void lock() { - #ifdef GEN_ENFORCE_READONLY_AST ast->Readonly = true; - #endif } forceinline @@ -468,6 +525,20 @@ namespace gen Code& operator =( Code other ) { + if ( ast == nullptr ) + { + log_failure("Attempt to set with a null AST!"); + return *this; + } + + #ifdef GEN_ENFORCE_READONLY_AST + if ( ast->Readonly ) + { + log_failure("Attempted to set a readonly AST!"); + return *this; + } + #endif + ast = other.ast; return *this; @@ -476,12 +547,18 @@ namespace gen forceinline AST* operator ->() { - #ifdef GEN_ENFORCE_READONLY_AST if ( ast == nullptr ) - fatal("Attempt to dereference a nullptr!"); + { + log_failure("Attempt to dereference a nullptr!"); + return nullptr; + } + #ifdef GEN_ENFORCE_READONLY_AST if ( ast->Readonly ) - fatal("Attempted to access a member from a readonly ast!"); + { + log_failure("Attempted to access a member from a readonly AST!"); + return nullptr; + } #endif return ast; @@ -495,6 +572,10 @@ namespace gen // Used internally for the most part to identify invaidly generated code. extern const Code InvalidCode; + /* + */ + ZPL_TABLE_DECLARE( ZPL_EXTERN, StringTable, str_tbl_, string ); + /* Type registy: Used to store Typename ASTs. Types are registered by their string literal value. @@ -502,47 +583,56 @@ namespace gen Strings made with the Typename ASTs are stored in thier own arena allocator. TODO: Implement and replace usage of def_type. */ - ZPL_TABLE_DECLARE( ZPL_EXTERN, TypeRegistry, type_reg_, Code ); + ZPL_TABLE_DECLARE( ZPL_EXTERN, TypeTable, type_tbl_, Code ); +#pragma endregion Data Structures -#pragma region gen API +#pragma region Gen Interface /* Initialize the library. This currently just initializes the CodePool. */ void init(); - #pragma region Upfront - /* - Foward Declare a type: - ; - */ - Code def_fwd_type( Code type, char const* name, Code specifiers = UnusedCode ); + // Use this only if you know you generated the code you needed to a file + // And rather get rid of current code asts instead of growing the pool memory. + void clear_code_pool(); - /* - Foward Declare a function: - ( ); - */ - Code def_fwd_proc( char const* name + // Set these before calling gen's init() procedure. + + void set_init_reserve_code_pool ( sw size ); + void set_init_reserve_string_arena( sw size ); + void set_init_reserve_string_table( sw size ); + void set_init_reserve_type_table ( sw size ); + + void set_allocator_code_pool ( allocator pool_allocator ); + void set_allocator_string_arena( allocator string_allocator ); + void set_allocator_string_table( allocator string_allocator ); + void set_allocator_type_table ( allocator type_reg_allocator ); + +# pragma region Upfront + Code def_class ( char const* name, Code body, Code parent = UnusedCode, Code specifiers = UnusedCode ); + Code def_class_body( s32 num, ... ); + Code def_class_fwd ( char const* name ); + + Code def_enum( char const* name, Code type, Code body ); + + Code def_global_body( s32 num, ... ); + + Code def_namespace ( char const* name, Code body ); + Code def_namespace_body( s32 num, ... ); + + Code def_operator( OperatorT op , Code specifiers , Code params , Code ret_type + , Code body ); + Code def_operator_fwd( OperatorT op, Code specifiers, Code params, Code ret_type ); - /* - Define an expression: - < c/c++ expression > + Code def_param( Code type, char const* name ); + Code def_params( s32 num, ... ); + Code def_params( s32 num, Code* params ); - TODO: Evalute if you want to validiate at the execution layer during gen_time (dosen't seem necessary) - */ - // Code def_expression( Code value ); - - /* - Define a function: - ( ) - { - - } - */ Code def_proc( char const* name , Code specifiers , Code params @@ -550,197 +640,101 @@ namespace gen , Code body ); - /* - Define a fucntion body: - { - - - ... - } - - There will be an empty line separation between entires - */ Code def_proc_body( s32 num, ... ); Code def_proc_body( s32 num, Code* codes ); - /* - Define a namespace; - namespace - { - - } - */ - Code def_namespace( char const* name, Code body ); + Code def_proc_fwd( char const* name + , Code specifiers + , Code params + , Code ret_type + ); - /* - Define a namespace body: - { - - - ... - } - - There will be an empty line separation between entires - */ - Code def_namespace_body( s32 num, ... ); - - /* - Define a set of parameters for a function: - , ... - */ - Code def_params( s32 num, ... ); - Code def_params( s32 num, char const** params ); - - /* - Define an operator definition. - */ - Code def_operator( OperatorT op, Code specifiers, Code params, Code ret_type, Code body ); - - /* - Define a set of specifiers for a function, struct, type, or varaible - - Note: - If alignas is specified the procedure expects the next argument to be the alignment value. - If attribute is specified the procedure expects the next argument to be its content as a string. - */ + Code def_specifier( SpecifierT* specifier ); Code def_specifiers( s32 num , ... ); Code def_specifiers( s32 num, SpecifierT* specs ); - /* - Define a struct: - struct : - { - - } - */ Code def_struct( char const* name, Code body, Code parent = UnusedCode, Code specifiers = UnusedCode ); - /* - Define a struct's body: - { - - - ... - } - - There will be an empty line separation between entires - */ Code def_struct_body( s32 num, ... ); Code def_struct_body( s32 num, Code* codes ); - /* - Define a variable: - = ; - */ + Code def_sturct_fwd(); + Code def_variable( Code type, char const* name, Code value = UnusedCode, Code specifiers = UnusedCode ); - /* - Define a typename AST value. - Useless by itself, its intended to be used in conjunction with other Code. + Code def_type( char const* name, Code specifiers = UnusedCode ); - Planned - Not yet Implemented: - Typename Codes are not held in the CodePool, instead they are stored in a - type registry (hastable where the key is a crc hash of the name string). - - If a key exists the existing code value will be provided. - */ - Code def_type( char const* name ); - - /* - Define a using typedef: - using = ; - */ - Code def_using( char const* name, Code type ); - - /* - Define a using namespace: - using namespace ; - - Can only be used in either a - */ + Code def_using ( char const* name, Code type ); Code def_using_namespace( char const* name ); - #pragma endregion Upfront +# pragma endregion Upfront - #pragma region Incremental - /* - Provides an incomplete procedure AST but sets the intended type. - Any adds will be type checked. +# pragma region Incremental + Code make_class( char const* name, Code parent = UnusedCode, Code specifiers = UnusedCode ); + + Code make_enum ( char const* name, Code type = UnusedCode, Code body = UnusedCode ); + Code make_enum_class( char const* name, Code type = UnusedCode, Code body = UnusedCode ); + + Code make_global_body( char const* name = "", s32 num = 0, ... ); + + Code make_namespace( char const* name ); + + Code make_operator( OperatorT op + , Code specifiers = UnusedCode + , Code params = UnusedCode + , Code ret_type = UnusedCode + , Code body = UnusedCode + ); + + Code make_params( s32 num, ... ); - Body is automatically made. Use body() to retrieve. - */ Code make_proc( char const* name , Code specifiers = UnusedCode , Code params = UnusedCode , Code ret_type = UnusedCode ); - - /* - Provides an incomplete struct AST but sets the intended type. - Any adds will be type checked. - Body is automatically made. Use body() to retrieve. - */ + Code make_specifiers( s32 num , ... ); + Code make_specifiers( s32 num, SpecifierT* specs ); + Code make_struct( char const* name, Code parent = UnusedCode, Code specifiers = UnusedCode ); -/* - Creates a unit file. + Code make_variable( char const* name, Code type = UnusedCode, Code value = UnusedCode, Code specifiers = UnusedCode ); - These represent an encapsulation of a generated file - Used this if you need to pass around a group of Code entires at file scope level. + Code make_type( char const* name, Code specifiers = UnusedCode ); - The name provided is the name of the file. -*/ -Code make_file_body( char const* name ); - #pragma endregion Incremental + Code make_using( char const* name, Code type = UnusedCode, Code specifiers = UnusedCode ); +# pragma endregion Incremental - /* - */ - Code parse_variable( char const* var_def, s32 length ); +# pragma region Parsing + Code parse_class( char const* class_def, s32 length ); + s32 parse_classes( char const* class_defs, s32 length, Code* out_classes ); - /* - */ - Code parse_using( char const* using_def, s32 length ); + Code parse_enum( char const* enum_def, s32 length); + s32 parse_enums( char const* enum_defs, s32 length, Code* out_enums ); - /* + Code parse_global_body( char const* body_def, s32 length ); - */ Code parse_operator( char const* operator_def, s32 length ); - /* - Define a procedure by parsing a string. - - Note: This parser only supports the language features the library supports - Any other features used and the lex or parse operation will fail. - - This is not a full-on c/c++ parser, it literally only grabs - what it needs to reconstruct the Code AST for seralization in the - builder, nothing else. - */ Code parse_proc( char const* proc_def, s32 length ); + s32 parse_procs( char const* proc_defs, s32 length, Code* out_procs ); - /* - Define a struct by parsing a string. - - Note: This parser only supports the language features the library supports - Any other features used and the lex or parse operation will fail. - - This is not a full-on c/c++ parser, it literally only grabs - what it needs to reconstruct the Code AST for seralization in the - builder, nothing else. - */ Code parse_struct( char const* struct_def, s32 length ); + s32 parse_structs( char const* struct_defs, s32 length, Code* out_struct_codes ); - /* - - */ - s32 parse_vars( char const* vars_def, s32 length, Code* out_vars_codes ); + Code parse_variable( char const* var_def, s32 length ); + s32 parse_variables( char const* vars_def, s32 length, Code* out_var_codes ); - /* + Code parse_type( char const* type_def, s32 length ); - */ - s32 parse_usings( char const* usings_def, s32 length, Code* out_usings_codes ); + Code parse_typedef( char const* typedef_def, s32 length ); + s32 parse_typedef( char const* typedef_def, s32 length, Code* out_typedef_codes ); - #pragma region Untyped text + Code parse_using ( char const* using_def, s32 length ); + s32 parse_usings( char const* usings_def, s32 length, Code* out_using_codes ); +# pragma endregion Parsing + +# pragma region Untyped text /* Define an untyped code string. @@ -779,7 +773,7 @@ Code make_file_body( char const* name ); Consider this an a preprocessor define. */ Code untyped_token_fmt( char const* fmt, s32 num_tokens, ... ); - #pragma endregion Untyped text +# pragma endregion Untyped text /* Used to generate the files. @@ -806,7 +800,7 @@ Code make_file_body( char const* name ); bool open( char const* path ); void write(); }; -#pragma endregion gen API +#pragma endregion Gen Interface } #pragma region MACROS @@ -839,7 +833,7 @@ Code make_file_body( char const* name ); # define using_type( Name_, Type_ ) Code Name_ = gen::def_using( #Name_, t_##Type_ ) # define variable( Type_, Name_, ... ) Code Name_ = gen::def_variable( t_##Type_, #Name_, __VA_ARGS__ ) -# define untyped( Value_ ) gen::untyped_str( txt(Value_) ) +# define untyped( Value_ ) gen::untyped_str( txt_with_length(Value_) ) # define code_token( Fmt_, ... ) gen::untyped_token_fmt( Fmt_, VA_NARGS( __VA_ARGS__), __VA_ARGS__ ) # define code_fmt( Fmt_, ... ) gen::untyped_fmt( Fmt_, __VA_ARGS__ ) # define specifiers( ... ) gen::def_specifiers( VA_NARGS( __VA_ARGS__ ), __VA_ARGS__ ) @@ -849,19 +843,24 @@ Code make_file_body( char const* name ); # define var( Type_, Name_, ... ) gen::def_variable( Type_, #Name_, __VA_ARGS__ ) # define make( Type_, Name_, ... ) Code Name_ = make_##Type_( #Name_, __VA_ARGS__ ); +# define enum( Name_, Type_, Body_ ) gen::def_enum( #Name_, t_##Type_, Body_ ) # define proc( Name_, Specifiers_, RetType_, Parameters_, Body_ ) Name_ = gen::def_proc( #Name_, Specifiers_, Parameters_, RetType_, Body_ ) # define proc_body( ... ) gen::def_proc_body( VA_NARGS( __VA_ARS__ ), __VA_ARGS__ ) # define params( ... ) gen::def_params( VA_NARGS( __VA_ARGS__ ) / 2, __VA_ARGS__ ) # define struct( Name_, Parent_, Specifiers_, Body_ ) Name_ = gen::def_struct( #Name_, Body_, Parent_, Specifiers_ ) # define struct_body( ... ) gen::def_struct_body( VA_NARGS( __VA_ARGS__ ), __VA_ARGS__ ) -# define add_var( Type_, Name_, ... ) add( gen::def_variable( t_##Type_, #Name_, __VA_ARGS__ ) ) -# define add_untyped( Value_ ) add( gen::untyped_str( txt( Value ) ) ) -# define add_ret_type( ... ) -# define add_params( ... ) +# define add_var( Value_ ) add( gen::parse_variable( txt_with_length(Value_)) ) +# define add_untyped( Value_ ) add( gen::untyped_str( txt_with_length( Value_ ) ) ) +# define add_type( Value_ ) add( gen::parse_type( txt_with_length(Value_)) ) +# define add_params( Value_ ) add( gen::parse_params( txt_with_length(Value_) )) +# define enum_code( Def_ ) gen::parse_enum( txt( Def_ ), sizeof( txt( Def_ )) ) +# define global_code( Def_ ) gen::parse_global_body( txt_with_length( Def_ )) +# define namespace_code( Def_ ) gen::parse_namespace( txt(Def_), sizeof( txt(Def_)) ) # define proc_code( Def_ ) gen::parse_proc( txt( Def_ ), sizeof( txt( Def_ )) ) # define struct_code( Def_ ) gen::parse_struct( txt( Def_ ), sizeof( txt( Def_ )) ) +# define variable_code( Def_ ) gen::parse_variable( txt_with_length( Def_ ) ) #endif #pragma endregion MACROS @@ -873,31 +872,38 @@ namespace gen // These are not set until gen::init is called. // This just preloads a bunch of Code types into the code pool. - extern const Code t_void; + extern Code t_void; - extern const Code t_bool; - extern const Code t_char; - extern const Code t_wchar_t; + extern Code t_bool; + extern Code t_char; + extern Code t_wchar_t; - extern const Code t_s8; - extern const Code t_s16; - extern const Code t_s32; - extern const Code t_s64; + extern Code t_s8; + extern Code t_s16; + extern Code t_s32; + extern Code t_s64; - extern const Code t_u8; - extern const Code t_u16; - extern const Code t_u32; - extern const Code t_u64; + extern Code t_u8; + extern Code t_u16; + extern Code t_u32; + extern Code t_u64; - extern const Code t_sw; - extern const Code t_uw; + extern Code t_sw; + extern Code t_uw; - extern const Code t_f32; - extern const Code t_f64; + extern Code t_f32; + extern Code t_f64; - extern const Code spec_constexpr; - extern const Code spec_inline; + extern Code spec_constexpr; + extern Code spec_inline; } #endif + +namespace gen +{ + extern Code access_public; + extern Code access_protected; + extern Code access_private; +} #pragma endregion CONSTANTS #endif diff --git a/project/gen.singleheader.c99.cpp b/project/gen.singleheader.c99.cpp new file mode 100644 index 0000000..8a5a85e --- /dev/null +++ b/project/gen.singleheader.c99.cpp @@ -0,0 +1,543 @@ +/* + Will generate a c99 compliant version of the gen library + + Note: This is done this way to test usage of library. +*/ + +#define GEN_DEFINE_DSL +#include "gen.cpp" + +using namespace gen; + +ct char const* Header_Comment = +R"(/* + genc: A simple staged metaprogramming library for C99. + + This library is intended for small-to midsize projects. + + AST type checking supports only a small subset of c++. + See the 'ECode' namespace and 'gen API' region to see what is supported. + + ### *WHAT IS NOT PROVIDED* + + * Macro or template generation : This library is to avoid those, adding support for them adds unnecessary complexity. + If you desire define them outside the gen_time scopes. + * Expression validation : Execution expressions are defined using the untyped string API. + There is no parse API for validating expression (possibly will add in the future) + * Complete file parser DSL : This isn't like the unreal header tool. + Code injection to file or based off a file contents is not supported by the api. However nothing is stopping you using the library for that purpose. + * Modern c++ (STL library) features + + The AST is managed by the library and provided the user via its interface prodedures. + + Notes: + + * The allocator definitions used are exposed to the user incase they want to dictate memory usage + * ASTs are wrapped for the user in a Code struct which essentially a warpper for a AST* type. + * Both AST and Code have member symbols but their data layout is enforced to be POD types. + + Data layout of AST struct: + + genc_CodeT Type; + bool Readonly; + genc_AST* Parent; + genc_string Name ; + genc_string Comment; + union { + array(genc_AST*) Entries; + genc_string Content; + }; + + *`CodeT` is a typedef for `ECode::Type` which is the type of the enum.* + + ASTs can be set to readonly by calling Code's lock() member function. + Adding comments is always available even if the AST is set to readonly. + + ### There are four sets of interfaces for Code AST generation the library provides + + * Upfront + * Incremental + * Parsing + * Untyped + + ### Upfront Construction + + All component ASTs must be previously constructed, and provided on creation of the code AST. + The construction will fail and return InvalidCode otherwise. + + Interface : + + * genc_def_class + * genc_def_class_body + * genc_def_class_fwd + * genc_def_enum + * genc_def_enum_class + * genc_def_enum_body + * genc_def_global_body + * genc_def_namespace + * genc_def_namespace_body + * genc_def_operator + * genc_def_param + * genc_def_params + * genc_def_proc + * genc_def_proc_body + * genc_def_proc_fwd + * genc_def_operator_fwd + * genc_def_specifier + * genc_def_specifiers + * genc_def_struct + * genc_def_struct_body + * genc_def_struct_fwd + * genc_def_variable + * genc_def_type + * genc_def_using + * genc_def_using_namespace + + ### Incremental construction + + A Code ast is provided but only completed upfront if all components are provided. + Components are then added using the AST API for adding ASTs: + + * genc_code_add( AST*, AST* other ) // Adds AST with validation. + * genc_code_add_entry( AST*, AST* other ) // Adds AST entry without validation. + + Code ASTs may be explictly validated at anytime using Code's check() member function. + + Interface : + + * genc_make_class + * genc_make_enum + * genc_make_enum_class + * genc_make_fwd + * genc_make_global_body + * genc_make_namespace + * genc_make_operator + * genc_make_params + * genc_make_proc + * genc_make_specifiers + * genc_make_struct + * genc_make_variable + * genc_make_type + * genc_make_using + + ### Parse construction + + A string provided to the API is parsed for the intended language construct. + + Interface : + + * genc_parse_class + * genc_parse_classes + * genc_parse_class_fwd + * genc_parse_classes_fwd + * genc_parse_enum + * genc_parse_enums + * genc_parse_global_body + * genc_parse_namespace + * genc_parse_namespaces + * genc_parse_params + * genc_parse_proc + * genc_parse_procs + * genc_parse_operator + * genc_parse_operators + * genc_parse_specifiers + * genc_parse_struct + * genc_parse_strucs + * genc_parse_variable + * genc_parse_variables + * genc_parse_type + * genc_parse_types + * genc_parse_using + * genc_parse_usings + + The parse API treats any execution scope definitions with no validation and are turned into untyped Code ASTs. + This includes the assignmetn of variables; due to the library not yet supporting c/c++ expression parsing. + + The plural variants provide an array of codes, its up to the user to add them to a body AST + (they are not auto-added to a body) + + ### Untyped constructions + + Code ASTs are constructed using unvalidated strings. + + Interface : + + * genc_untyped_str + * genc_untyped_fmt + * genc_untyped_token_fmt + + During serialization any untyped Code AST is has its string value directly injected inline of + whatever context the content existed as an entry within. + Even though thse are not validated from somewhat correct c/c++ syntax or components, it doesn't mean that + Untyped code can be added as any component of a Code AST: + + * Untyped code cannot have children, thus there cannot be recursive injection this way. + * Untyped code can only be a child of a parent of body AST, or for values of an assignment. + + These restrictions help prevent abuse of untyped code to some extent. +*/ +)"; + +Code IfDef_GENC_IMPLEMENTATION; +Code EndIf_GENC_IMPLEMENTATION; + +Code make_log_failure() +{ + Code result = make_global_body(); + { + result->add( untyped_str( + R"(#ifdef GEN_USE_FATAL)" + )); + + result->add( proc_code( + inline + sw genc_log_failure(char const *fmt, ...) + { + if ( genc_global_ShouldShowDebug == false ) + return 0; + + sw res; + va_list va; + + va_start(va, fmt); + res = zpl_printf_va(fmt, va); + va_end(va); + + return res; + } + )); + + result->add( untyped_str( + R"(#else)" + )); + + result->add( parse_proc( +R"(inline +sw genc_log_failure(char const *fmt, ...) +{ + local_persist thread_local + char buf[ZPL_PRINTF_MAXLEN] = { 0 }; + + va_list va; + +#if Build_Debug + va_start(va, fmt); + zpl_snprintf_va(buf, ZPL_PRINTF_MAXLEN, fmt, va); + va_end(va); + + assert_crash(buf); + return -1; +#else + va_start(va, fmt); + zpl_printf_err_va( fmt, va); + va_end(va); + + zpl_exit(1); + return -1; +#endif +})" + , 372 + )); + + result->add( untyped_str( + R"(#endif)" + )); + + result->check(); + } + + return result; +} + +Code make_ECode() +{ + Code ECode = make_global_body(); + { + ECode->add( parse_enum( +R"(enum genc_ECode +{ + Invalid, + Untyped, + Enum, + Enum_Body, + Global_Body, + Parameters, + Proc, + Proc_Body, + Proc_Forward, + Specifiers, + Struct, + Struct_Body, + Variable, + Typedef, + Typename, + + Num_Types +}; +typedef u32 genc_CodeT;)" + 1 + , 280 + )); + + ECode->add( proc_code( + inline + char const* genc_ecode_to_str( Type type ) + { + genc_local_persist + char const* lookup[Num_Types] = { + "Invalid", + "Untyped", + "Access_Public", + "Access_Private", + "Access_Protected", + "Class", + "Enum", + "Enum_Body", + "Global_Body", + "Namespace", + "Namespace_Body", + "Parameters", + "Proc", + "Proc_Body", + "Proc_Forward", + "Specifiers", + "Struct", + "Struct_Body", + "Variable", + "Typedef", + "Typename", + "Using", + }; + + return lookup[ type ]; + } + )); + + ECode->check(); + } + + return ECode; +} + +Code make_EOperator() +{ + Code eoperator = make_global_body(); + + return eoperator; +} + +Code make_ESpecifier() +{ + Code especifier = make_global_body(); + + return especifier; +} + +Code make_Code() +{ + Code code = make_global_body(); + + code->add( struct_code( + struct genc_AST + { + genc_CodeT Type; + bool Readonly; + genc_AST* Parent; + genc_string Name; + genc_string Comment; + union { + array(genc_AST*) Entries; + genc_string Content; + }; + }; + typedef struct genc_AST genc_AST; + typedef genc_AST* Code; + )); + + code->add( proc_code( + bool genc_ast_add( genc_Code other ); + )); + + code->add( proc_code( + genc_forceinline + void genc_ast_add_entry( genc_Code self, genc_Code other ) + { + genc_array_append( self->Entries, other ); + + other->Parent = self; + } + )); + + code->add( untyped_str( + R"(#define genc_code_body( AST_ ) AST->Entries[0])", 47 + )); + + code->add( proc_code( + genc_forceinline + bool genc_code_has_entries( Code self ) const + { + genc_local_persist + bool lookup[ genc_ECode::Num_Types] = { + false, // Invalid + false, // Untyped + true, // Global_Body + true, // Parameters + true, // Proc + true, // Proc_Body + true, // Proc_Foward + false, // Specifies + true, // Struct + true, // Struct_Body + true, // Variable + true, // Typedef + true, // Typename + }; + + return lookup[self->Type]; + } + )); + + code->check(); + + return code; +} + +Code make_static_Data() +{ + Code + data = make_global_body(); + data->add( IfDef_GENC_IMPLEMENTATION ); + data->add( global_code( + static genc_array(genc_AST) genc_CodePool = nullptr; + + static genc_array(genc_arena) genc_StringArenas = nullptr; + + static genc_StringTable genc_StringMap; + static genc_TypeTable genc_TypeMap; + + static sw genc_InitSize_CodePool = genc_megabytes(64); + static sw genc_InitSize_StringArena = genc_megabytes(32); + static sw genc_InitSize_StringTable = genc_megabytes(4); + static sw genc_InitSize_TypeTable = genc_megabytes(4); + + static allocator genc_Allocator_CodePool = zpl_heap(); + static allocator genc_Allocator_StringArena = zpl_heap(); + static allocator genc_Allocator_StringTable = zpl_heap(); + static allocator genc_Allocator_TypeTable = zpl_heap(); + )); + data->add( untyped_str( R"(#ifdef GENC_DEFINE_LIBRARY_CODE_CONSTANTS)")); + data->add( global_code( + Code t_void; + + Code t_bool; + Code t_char; + Code t_char_wide; + + Code t_s8; + Code t_s16; + Code t_s32; + Code t_s64; + + Code t_u8; + Code t_u16; + Code t_u32; + Code t_u64; + + Code t_sw; + Code t_uw; + + Code t_f32; + Code t_f64; + )); + data->add( untyped_str( R"(#endif)")); + data->add( global_code( + Code spec_inline; + Code spec_const; + )); + data->add( EndIf_GENC_IMPLEMENTATION ); + + return data; +} + +Code make_make_code() +{ + +} + +Code make_init() +{ + +} + +Code make_mem_config_interface() +{ + +} + +Code make_internal_funcs() +{ + +} + +Code make_upfront() +{ + +} + +Code make_incremental() +{ + +} + +Code make_parsing() +{ + +} + +Code make_untyped() +{ + +} + +Code make_interface() +{ + +} + + +int gen_main() +{ + Memory::setup(); + gen::init(); + + IfDef_GENC_IMPLEMENTATION = untyped( + R"(#ifdef GENC_IMPLEMENTATION)" + ); + + EndIf_GENC_IMPLEMENTATION = untyped( + R"(#endif // GENC_IMPLEMENTATION)" + ); + + Code header_comment = untyped_str( Header_Comment, sizeof( Header_Comment ) ); + Code log_failure = make_log_failure(); + + Code ecode = make_ECode(); + Code eoperator = make_EOperator(); + Code especifier = make_ESpecifier(); + Code code = make_Code(); + + Builder + builder; + builder.open( "genc.h" ); + + builder.print( header_comment ); + builder.print( log_failure ); + builder.print( ecode ); + builder.print( eoperator ); + builder.print( especifier ); + builder.print( code ); + + builder.write(); + + Memory::cleanup(); + return 0; +} diff --git a/project/gen.singleheader.c99.data b/project/gen.singleheader.c99.data new file mode 100644 index 0000000..d733db8 --- /dev/null +++ b/project/gen.singleheader.c99.data @@ -0,0 +1,55 @@ +# HEADER COMMENT +1, 166 + +# CODE TYPES +Invalid +Untyped +Enum +Enum_Body +Global_Body +Parameters +Proc +Proc_Body +Proc_Forward +Specifiers +Struct +Struct_Body +Variable +Typedef +Typename +Using + +# CODE_HAS_ENTRIES + +# SPECIFIER_TYPES +Attribute +Alignas +Constexpr +Const +Inline +Pointer +API_Import +API_Export +External_Linkage +Internal_Linkage +Local_Persist +Thread_Local +Invalid + +# SPECIFIER_STRINGS +"alignas", +"const", +"inline", +"*", +#if defined(ZPL_SYSTEM_WINDOWS) +"__declspec(dllexport)", +"__declspec(dllimport)", +#elif defined(ZPL_SYSTEM_MACOS) +"__attribute__ ((visibility (\"default\")))", +"__attribute__ ((visibility (\"default\")))", +#endif +"extern", +"static", +"static", +"thread_local" + diff --git a/project/gen.singleheader.c99.refactor b/project/gen.singleheader.c99.refactor new file mode 100644 index 0000000..f9de46a --- /dev/null +++ b/project/gen.singleheader.c99.refactor @@ -0,0 +1,34 @@ +__VERSION 1 + +word AST, genc_AST +word Code, genc_Code + +word ECode, genc_ECode +word EOperator, genc_EOperator +word ESpecifier, genc_ESpecifier +word CodeT, genc_CodeT + +word string, genc_string +word init, genc_init + +namespace def_, genc_def_ +namespace make_, genc_make_ +namespace parse_, genc_parse_ +namespace untyped_, genc_untyped_ +namespace set_init_, genc_set_init_ +namespace set_allocator_, genc_set_allocator_ + +word clean_code_pool, genc_clean_code_pool + +word Builder, genc_Builder + +word CodePool, genc_CodePool +word StringArenas, genc_StringArenas +word CodePOD, genc_AST +word StringMap, genc_StringMap +word TypeMap, genc_TypeMap + +namespace InitSize_, genc_InitSize_ +namespace Allocator_, genc_Allocator_ + +namespace spec_, genc_spec diff --git a/project/gen.singleheader.cpp b/project/gen.singleheader.cpp new file mode 100644 index 0000000..e69de29 diff --git a/singleheader/genc.refactor b/singleheader/genc.refactor new file mode 100644 index 0000000..ecf0643 --- /dev/null +++ b/singleheader/genc.refactor @@ -0,0 +1,4 @@ +// Removes the genc_ namespace if desired + +namespace genc_ + diff --git a/test/Array.hpp b/test/Array.hpp index 7a285e6..317bf41 100644 --- a/test/Array.hpp +++ b/test/Array.hpp @@ -16,7 +16,7 @@ #ifndef GEN_DEFINE_DSL using namespace gen; - Code t_allocator = def_type( txt(allocator) ); + Code t_allocator = def_type( txt(allocator) ); Code header; { @@ -64,7 +64,6 @@ return ArrayBase; } - #define gen_array( Type_ ) gen__array( #Type_, sizeof(Type_), a_base ) Code gen__array( char const* type_str, s32 type_size, Code parent ) { #ifndef GEN_DEFINE_DSL @@ -155,6 +154,7 @@ Code append = make_proc( "append" ); { append->add( def_params( 1, type, "value") ); + append->add( t_bool ); Code body = append.body(); @@ -172,6 +172,8 @@ return true; ))); + + append->check(); } Code back; @@ -295,7 +297,7 @@ } Code set_capacity = parse_proc( txt_with_length( - bool set_capacity( new_capacity ) + bool set_capacity( sw new_capacity ) { Header& header = get_header(); @@ -305,7 +307,7 @@ if ( capacity < header.Num ) header.Num = capacity; - uw size = sizeof(Header) + sizeof(Type) * capacity; + sw size = sizeof(Header) + sizeof(Type) * capacity; Header* new_header = rcast( Header* alloc( header.Allocator, size )); if ( new_header == nullptr ) @@ -591,18 +593,58 @@ return array_def; } + struct ArrayRequest + { + char const* Name; + sw Size; + }; + + array(ArrayRequest) UserArrayGenQueue; + + #define gen_array( Type_ ) add_gen_array_request( #Type_, sizeof(Type_) ) + + void add_gen_array_request( const char* type_str, sw type_size ) + { + ArrayRequest request = { type_str, type_size }; + + array_append( UserArrayGenQueue, request ); + } + u32 gen_array_file() { Code a_base = gen__array_base(); - Code a_u32 = gen_array( u32 ); - Code a_cstr = gen_array( char const* ); + add_gen_array_request( "u32", sizeof(u32) ); + gen_array( char const* ); + + array(Code) array_asts; + array_init( array_asts, g_allocator ); + + sw left = array_count( UserArrayGenQueue ); + sw index = 0; + while( left -- ) + { + ArrayRequest request = UserArrayGenQueue[index]; + + Code result = gen__array( request.Name, request.Size, a_base ); + + array_append( array_asts, result ); + } Builder arraygen; arraygen.open( "Array.gen.hpp" ); - arraygen.print( a_u32 ); - arraygen.print( a_cstr ); + + left = array_count( array_asts ); + index = 0; + + while( left-- ) + { + Code code = array_asts[index]; + + arraygen.print( code ); + } + arraygen.write(); return 0; } diff --git a/test/c99/meson.build b/test/c99/meson.build new file mode 100644 index 0000000..cd45b44 --- /dev/null +++ b/test/c99/meson.build @@ -0,0 +1,22 @@ +project( 'test', 'c', default_options : ['buildtype=debug'] ) + +# add_global_arguments('-E', language : 'cpp') + +includes = include_directories( + [ + '../gen', + '../../singleheader' + ]) + +# get_sources = files('./get_sources.ps1') +# sources = files(run_command('powershell', get_sources, check: true).stdout().strip().split('\n')) + +sources = [ 'test.c99.c' ] + +if get_option('buildtype').startswith('debug') + + add_project_arguments('-DBuild_Debug', language : ['c' ]) + +endif + +executable( 'test_c99', sources, include_directories : includes ) diff --git a/test/c99/table.h b/test/c99/table.h new file mode 100644 index 0000000..6f7ec24 --- /dev/null +++ b/test/c99/table.h @@ -0,0 +1,79 @@ +#include "gen.h" + +#define Table( Type_ ) Table_##Type_ + +typedef u64(*)(void*) HashingFn; + +#if gen_time +# define gen_table( Type_, HashingFn_ ) gen_request_table( #Type_, sizeof(Type_), HashingFn_ ) + + u64 table_default_hash_fn( void* address ) + { + return crc32( address, 4 ); + } + + Code gen_table_code( char const* type_str, sw type_size, HashingFn hash_fn ) + { + Code table; + + return table; + } + + struct TableRequest + { + char const* Type; + sw Size; + HashingFn HashFn; + }; + + array(TableRequest) TableRequests; + + void gen_request_table( const char* type_str, sw type_size, HashingFn hash_fn ) + { + TableRequest request = { type_str, type_size, hash_fn }; + + array_append( TableRequests, request ); + } + + u32 gen_table_file() + { + gen_table( u32 ); + gen_table( char const* ); + + array(Code) array_asts; + array_init( array_asts, g_allocator ); + + sw left = array_count( TableRequests ); + sw index = 0; + while( left -- ) + { + ArrayRequest request = TableRequests[index]; + + Code result = gen_table_code( request.Name, request.Size, request.HashFn ); + + array_append( array_asts, result ); + } + + Builder + arraygen; + arraygen.open( "table.gen.h" ); + + left = array_count( array_asts ); + index = 0; + + while( left-- ) + { + Code code = array_asts[index]; + + arraygen.print( code ); + } + + arraygen.write(); + return 0; + } +#endif + +#ifndef gen_time +# include "table.gen.h" +#endif + diff --git a/test/c99/test.c99.c b/test/c99/test.c99.c new file mode 100644 index 0000000..012c6a7 --- /dev/null +++ b/test/c99/test.c99.c @@ -0,0 +1,40 @@ +#define GENC_IMPLEMENTATION +#include "genc.h" +#include "table.h" + + +struct Test +{ + u64 A; + u64 B; +}; + + + +#if gen_time + + +u64 hash_struct( void* test ) +{ + return crc32( ((Test)test).A, sizeof(u64) ); +} + + +int gen_main() +{ + gen_table( Test, & hash_struct ) + + gen_table_file(); +} +#endif + + + +#if runtime +int main() +{ + Table(Test) test_table; + + +} +#endif \ No newline at end of file diff --git a/test/math.hpp b/test/math.hpp index 5d34c30..18c70bf 100644 --- a/test/math.hpp +++ b/test/math.hpp @@ -25,7 +25,7 @@ #ifndef GEN_DEFINE_DSL string name = string_sprintf( g_allocator, (char*)sprintf_buf, ZPL_PRINTF_MAXLEN, "square", type ); - #if 1 + #if 0 Code square; { Code params = def_params( 1, integral_type, "value" ); @@ -43,9 +43,9 @@ return value * value; } ); - char const* gen_code = token_fmt( tmpl, 1, type ); + char const* gen_code = token_fmt( tmpl, 1, "type", type ); - Code square = parse_proc(gen_code); + Code square = parse_proc(gen_code, strlen(gen_code)); #endif #else diff --git a/test/test.singleheader.cpp b/test/test.singleheader.cpp new file mode 100644 index 0000000..e69de29 diff --git a/thirdparty/zpl.h b/thirdparty/zpl.h index 1e77756..ac5541f 100644 --- a/thirdparty/zpl.h +++ b/thirdparty/zpl.h @@ -5095,8 +5095,8 @@ License: typedef struct string_header { allocator allocator; - sw length; sw capacity; + sw length; } string_header; #define ZPL_STRING_HEADER(str) (zpl_cast(ZPL_NS string_header *)(str) - 1)