From 9957ef0e7d6f7860d42bf4be401fe9f77f249fff Mon Sep 17 00:00:00 2001 From: Ed_ Date: Mon, 3 Apr 2023 03:55:28 -0400 Subject: [PATCH] Major changes to library design, change test to reflect it. --- Readme.md | 13 +- project/Bloat.hpp | 24 +- project/gen.cpp | 575 +++++++++++++++++++++++++++++++++++++--------- project/gen.hpp | 471 +++++++++++++++++++++++++++++++------ test/Array.hpp | 507 +++++++++++++++++++++++++++++++--------- test/math.hpp | 18 +- test/test.cpp | 7 +- 7 files changed, 1321 insertions(+), 294 deletions(-) diff --git a/Readme.md b/Readme.md index 0bf8bf5..a83b6a3 100644 --- a/Readme.md +++ b/Readme.md @@ -2,9 +2,17 @@ An attempt at simple staged metaprogramming for c/c++. -This project is not minimum feature complete yet. +This library is intended for small-to midsize projects that want rapid complation times. +for fast debugging. + +## Notes + +This project is not minimum feature complete yet. Version 1 will have c and a subset of c++ features available to it. +I will generate with this library a C99 or 11 variant when Version 1 is complete. +A single-header version will also be genrated. + ## How it works A metaprogram is built to generate files before the main program is built. We'll term runtime for this program as `gen_time`. The metaprogram's core implementation are within `gen.hpp` and `gen.cpp` in the project directory. @@ -70,3 +78,6 @@ However, if the code being generated becomes complex, or from a datatable or dat * Need problably a better name, I found a few repos with this same one... * Actually get to version 1. +* Make a test suite made up of collections based of the ZPL library templated colllection macros and the memory module. +* Generate a single-header library. +* Generate a C-supported single-header library. diff --git a/project/Bloat.hpp b/project/Bloat.hpp index d4f718a..3cb9e4a 100644 --- a/project/Bloat.hpp +++ b/project/Bloat.hpp @@ -4,6 +4,28 @@ #pragma once +#if defined(__GNUC__) || defined(__clang__) || 1 + // Supports 0-10 arguments + #define VA_NARGS_IMPL( _0, \ + _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ + N, ...) N + // ## deletes preceding comma if _VA_ARGS__ is empty (GCC, Clang) + #define VA_NARGS(...) VA_NARGS_IMPL(_, ## __VA_ARGS__, \ + 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \ + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \ + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, \ + 0) + +#else + // Supports 1-10 arguments + #define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + #define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) +#endif + +#define VA_NARGS2(...) ((int)(sizeof((int[]){ __VA_ARGS__ })/sizeof(int))) + #ifdef BLOAT_IMPL # define ZPL_IMPLEMENTATION #endif @@ -21,7 +43,7 @@ # define ZPL_MODULE_ESSENTIALS # define ZPL_MODULE_CORE # define ZPL_MODULE_TIMER -// # define ZPL_MODULE_HASHING +# define ZPL_MODULE_HASHING // # define ZPL_MODULE_REGEX // # define ZPL_MODULE_EVENT // # define ZPL_MODULE_DLL diff --git a/project/gen.cpp b/project/gen.cpp index 5719430..1119461 100644 --- a/project/gen.cpp +++ b/project/gen.cpp @@ -5,26 +5,48 @@ #ifdef gen_time namespace gen { - void init() + namespace StaticData { - + static array(CodePOD) CodePool = nullptr; } - ct Code make() + /* + Used internally to retireve a Code object form the CodePool. + */ + Code make() { - return { Code::Invalid, nullptr, nullptr, { nullptr } }; + using namespace StaticData; + + array_append( CodePool, InvalidCode ); + + return * (Code*) & array_back( CodePool ); + } + + + + void init() + { + array_init( StaticData::CodePool, g_allocator ); } Code decl_type( char const* name, Code type, Code specifiers ) { - Code - result = make(); - result.Type = Code::Decl_Type; - result.Name = string_make( g_allocator, name ); + using namespace ECode; - array_init( result.Entries, g_allocator ); - result.add( specifiers ); - result.add( type ); + if ( type->Type != Specifiers ) + fatal( "gen::decl_type: type is not a Typename"); + + if ( type->Type != Typename ) + fatal( "gen::decl_type: specifiers is not a 'Specfiers' type"); + + Code + result = make(); + result->Type = Decl_Type; + result->Name = string_make( g_allocator, name ); + + array_init( result->Entries, g_allocator ); + result->add( specifiers ); + result->add( type ); return result; } @@ -35,57 +57,77 @@ namespace gen , Code ret_type ) { - Code - result = make(); - result.Type = Code::Decl_Function; - result.Name = string_make( g_allocator, name ); + using namespace ECode; + + if ( specifiers->Type != Specifiers ) + fatal( "gen::decl_fn: specifiers was not a `Specifiers` type" ); + + if ( params->Type != Parameters ) + fatal( "gen::decl_fn: params was not a `Parameters` type" ); + + if ( ret_type->Type != Typename ) + fatal( "gen::decl_fn: ret_type was not a Typename" ); + + Code + result = make(); + result->Type = Decl_Function; + result->Name = string_make( g_allocator, name ); - array_init( result.Entries, g_allocator ); + array_init( result->Entries, g_allocator ); if ( specifiers ) - result.add( specifiers ); + result->add( specifiers ); - result.add( ret_type ); + result->add( ret_type ); if ( params ) - result.add( params ); + result->add( params ); return result; } Code def_parameters( s32 num, ... ) { - if (num <= 0) - fatal("TT::make_paramters: num is %d", num); + using namespace ECode; - Code - result = make(); - result.Type = Code::Parameters; + if (num <= 0) + fatal( "TT::make_paramters: num cannot be zero or neg" ); + + Code + result = make(); + result->Type = Parameters; va_list va; va_start(va, num); - result.Name = string_make( g_allocator, va_arg(va, char const*) ); + result->Name = string_make( g_allocator, va_arg(va, char const*) ); - array_init( result.Entries, g_allocator ); + array_init( result->Entries, g_allocator ); Code type = va_arg(va, Code); - result.add( type ); + + if ( type->Type != Typename ) + fatal( "gen::def_parameters: type of param %d is not a Typename", num - num + 1 ); + + result->add( type ); while( num -= 2, num && num % 2 == 0 ) { - Code - param = make(); - param.Name = string_make( g_allocator, va_arg(va, char const*) ); - - array_init( param.Entries, g_allocator ); - type = va_arg(va, Code); - param.add( type ); - result.add(param); + Code + param = make(); + param->Type = Parameters; + param->Name = string_make( g_allocator, va_arg(va, char const*) ); + + array_init( param->Entries, g_allocator ); + + if ( type->Type != Typename ) + fatal( "gen::def_parameters: type of param %d is not a Typename", num - num + 1 ); + + param->add( type ); + result->add(param); } - va_end(va); return result; @@ -98,50 +140,155 @@ namespace gen , Code body ) { + using namespace ECode; + + if ( specifiers && specifiers->Type != Specifiers ) + fatal( "gen::def_function: specifiers was not a `Specifiers` type" ); + + if ( params && params->Type != Parameters ) + fatal( "gen::def_function: params was not a `Parameters` type" ); + + if ( ret_type == nullptr || ret_type->Type != Typename ) + fatal( "gen::def_function: ret_type was not a Typename" ); + + switch ( body->Type ) + { + case Function_Body: + case Untyped: + break; + + default: + fatal("gen::def_function: body must be either of Function_Body or Untyped type."); + } + Code - result = make(); - result.Name = string_make( g_allocator, name ); - result.Type = Code::Function; + result = make(); + result->Name = string_make( g_allocator, name ); + result->Type = Function; - array_init( result.Entries, g_allocator ); + array_init( result->Entries, g_allocator ); if ( specifiers ) - result.add( specifiers ); + result->add( specifiers ); - result.add( ret_type ); + result->add( ret_type ); if ( params ) - result.add( params ); + result->add( params ); - result.add( body ); + result->add( body ); + + body->Parent = result; return result; } - Code def_function_body( u32 num, ... ) + Code def_function_body( s32 num, ... ) { + using namespace ECode; + if ( num <= 0 ) + fatal("gen::def_function_body: num cannot zero or neg"); + + Code result = make(); + + array_init( result->Entries, g_allocator ); + + va_list va; + va_start(va, num); + do + { + Code entry = va_arg(va, Code); + + switch ( entry->Type ) + { + case Decl_Function: + case Decl_Type: + case Namespace: + case Namespace_Body: + case Parameters: + case Specifiers: + case Struct_Body: + case Typename: + fatal("gen::def_function_body: Entry type is not allowed: %s", entry->type_str() ); + + default: + break; + } + + result->add( entry ); + } + while ( num--, num > 0 ); + va_end(va); + + return result; } Code def_namespace( char const* name, Code body ) { - - } - - Code def_namespace_body( u32 num, ... ) - { - - } - - Code def_specifiers( u32 num, ... ) - { - if ( num <= 0 ) - fatal("gen::make_specifier: num cannot be zero."); + using namespace ECode; Code - result = make(); - result.Type = Code::Specifiers; - result.Content = string_make( g_allocator, "" ); + result = make(); + result->Type = Namespace; + + array_init( result->Entries, g_allocator ); + + if ( body->Type != Namespace_Body || body->Type != Untyped ) + fatal("gen::def_namespace: body is not of namespace or untyped type"); + + result->add( body ); + + return result; + } + + Code def_namespace_body( s32 num, ... ) + { + using namespace ECode; + + if ( num <= 0 ) + fatal("gen::make_specifier: num cannot be zero or less"); + + Code + result = make(); + result->Type = Namespace_Body; + + va_list va; + va_start(va, num); + do + { + Code entry = va_arg(va, Code); + + switch ( entry->Type ) + { + case Namespace_Body: + case Parameters: + case Specifiers: + case Struct_Body: + case Typename: + fatal("gen::def_function_body: Entry type is not allowed: %s", ECode::str(entry->Type) ); + + default: + break; + } + + result->add( entry ); + } + while ( num--, num > 0 ); + va_end(va); + + return result; + } + + Code def_specifiers( s32 num, ... ) + { + if ( num <= 0 ) + fatal("gen::make_specifier: num cannot be zero or less"); + + Code + result = make(); + result->Type = ECode::Specifiers; + result->Content = string_make( g_allocator, "" ); va_list va; va_start(va, num); @@ -152,13 +299,13 @@ namespace gen switch ( type ) { case Alignas: - result.Content = string_append_fmt( result.Content, "%s(%d)", specifier_str(type), va_arg(va, u32) ); + result->Content = string_append_fmt( result->Content, "%s(%d)", specifier_str(type), va_arg(va, u32) ); break; default: const char* str = specifier_str(type); - result.Content = string_append_fmt( result.Content, "%s", str ); + result->Content = string_append_fmt( result->Content, "%s", str ); break; } } @@ -170,29 +317,125 @@ namespace gen Code def_struct( char const* name, Code body, Code parent, Code specifiers ) { + using namespace ECode; + if ( specifiers && specifiers->Type != Specifiers ) + fatal( "gen::def_struct: specifiers was not a `Specifiers` type" ); + + if ( parent && parent->Type != Struct ) + fatal( "gen::def_struct: parent was not a `Struct` type" ); + + if ( body && body->Type != Struct_Body ) + fatal( "gen::def_struct: body was not a Struct_Body type" ); + + Code + result = make(); + result->Type = Struct; + result->Name = string_make( g_allocator, name ); + + array_init( result->Entries, g_allocator ); + + if ( body ) + result->add( body ); + + if ( parent ) + result->add( parent ); + + if ( specifiers ) + result->add( specifiers ); + + return result; } - Code def_struct_body( u32 num, ... ) + Code def_struct_body( s32 num, ... ) { + using namespace ECode; + if ( num == 0 ) + fatal("gen::def_struct_body: num cannot be zero"); + + Code result = make(); + + array_init( result->Entries, g_allocator ); + + va_list va; + va_start(va, num); + do + { + Code entry = va_arg(va, Code); + + switch ( entry->Type ) + { + case Namespace: + case Namespace_Body: + case Parameters: + case Specifiers: + case Struct_Body: + case Typename: + fatal("gen::def_struct_body: Entry type is not allowed: %s", ECode::str(entry->Type) ); + } + + result->add( entry ); + } + while ( num--, num > 0 ); + va_end(va); + + return result; } Code def_variable( char const* name, Code type, Code value, Code specifiers ) { + if ( specifiers && specifiers->Type != ECode::Specifiers ) + fatal( "gen::def_variable: specifiers was not a `Specifiers` type" ); + + if ( type->Type != ECode::Typename ) + fatal( "gen::def_variable: type was not a Typename" ); + + if ( value && value->Type != ECode::Untyped ) + fatal( "gen::def_variable: value was not a `Untyped` type" ); + + Code + result = make(); + result->Name = string_make( g_allocator, name ); + result->Type = ECode::Variable; + array_init( result->Entries, g_allocator ); + + if ( specifiers ) + result->add( specifiers ); + + result->add( type ); + + if ( value ) + result->add( value ); + + return result; } Code def_type( char const* name ) { Code - result = make(); - result.Name = string_make( g_allocator, name ); - result.Type = Code::Typename; + result = make(); + result->Name = string_make( g_allocator, name ); + result->Type = ECode::Typename; return result; } + Code def_using( char const* name, Code type ) + { + Code + result = make(); + result->Name = string_make( g_allocator, name ); + result->Type = ECode::Using; + + array_init( result->Entries, g_allocator ); + + type->Parent = result; + result->add( type ); + + return result; + } Code untyped_fmt(char const* fmt, ...) { @@ -205,22 +448,123 @@ namespace gen va_end(va); Code - result = make(); - result.Name = string_make( g_allocator, fmt ); - result.Type = Code::Untyped; - result.Content = string_make( g_allocator, buf ); + result = make(); + result->Name = string_make( g_allocator, fmt ); + result->Type = ECode::Untyped; + result->Content = string_make( g_allocator, buf ); + + return result; + } + + + struct TokEntry + { + char const* Str; + s32 Length; + }; + + ZPL_TABLE( static, TokMap, tokmap_, TokEntry ) + + sw token_fmt_va( char* buf, uw buf_size, char const* fmt, s32 num_tokens, va_list va ) + { + char const* buf_begin = buf; + sw remaining = buf_size; + + TokMap tok_map; + { + tokmap_init( & tok_map, g_allocator ); + + s32 left = num_tokens; + + while ( left-- ) + { + char const* token = va_arg( va, char const* ); + char const* value = va_arg( va, char const* ); + + TokEntry entry + { + value, + zpl_strnlen(value, 128) + }; + + u32 key = crc32( token, zpl_strnlen(token, 32) ); + + tokmap_set( & tok_map, key, entry ); + } + } + + sw result = 0; + char current = *fmt; + + while ( current ) + { + sw len = 0; + + while ( current && current != '{' && remaining ) + { + *buf = *fmt; + buf++; + fmt++; + + current = *fmt; + } + + if ( current == '{' ) + { + char const* scanner = fmt; + + s32 tok_len = 0; + + while ( *scanner != '}' ) + { + tok_len++; + scanner++; + } + + char const* token = fmt; + + s32 key = crc32( token, tok_len ); + TokEntry value = *tokmap_get( & tok_map, key ); + s32 left = value.Length; + + while ( left-- ) + { + *buf = *value.Str; + buf++; + value.Str++; + } + + scanner++; + fmt = scanner; + current = *fmt; + } + } return result; } - Code token_fmt( char const* fmt, ... ) + Code 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(); + result->Name = string_make( g_allocator, fmt ); + result->Type = ECode::Untyped; + result->Content = string_make( g_allocator, buf ); + + return result; } - string Code::to_string() + string AST::to_string() { string result = string_make( g_allocator, "" ); @@ -229,6 +573,8 @@ namespace gen switch ( Type ) { + using namespace ECode; + case Invalid: fatal("Attempted to serialize invalid code! - %s", Name); break; @@ -237,13 +583,6 @@ namespace gen result = string_append_length( result, Content, string_length(Content) ); 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 Decl_Function: { u32 index = 0; @@ -252,9 +591,9 @@ namespace gen if ( left <= 0 ) fatal( "Code::to_string - Name: %s Type: %s, expected definition", Name, Type ); - if ( Entries[index].Type == Specifiers ) + if ( Entries[index]->Type == Specifiers ) { - result = string_append_fmt( result, "%s\n", Entries[index].to_string() ); + result = string_append_fmt( result, "%s\n", Entries[index]->to_string() ); index++; left--; } @@ -262,13 +601,13 @@ namespace gen if ( left <= 0 ) fatal( "Code::to_string - Name: %s Type: %s, expected return type", Name, Type ); - result = string_append_fmt( result, "\n%s %s(", Entries[index].to_string(), Name ); + result = string_append_fmt( result, "\n%s %s(", Entries[index]->to_string(), Name ); index++; left--; - if ( left && Entries[index].Type == Parameters ) + if ( left && Entries[index]->Type == Parameters ) { - result = string_append_fmt( result, "%s", Entries[index].to_string() ); + result = string_append_fmt( result, "%s", Entries[index]->to_string() ); index++; left--; } @@ -277,22 +616,11 @@ namespace gen } break; - case Function_Body: - break; + case Decl_Type: + if ( Entries[0]->Type == Specifiers ) + result = string_append_fmt( result, "%s\n", Entries[0]->to_string()); - case Parameters: - { - result = string_append_fmt( result, "%s %s", Entries[0].to_string(), Name ); - - s32 index = 1; - s32 left = array_count( Entries ) - 1; - - while ( left--, left > 0 ) - result = string_append_fmt( result, ", %s %s" - , Entries[index].Entries[0].to_string() - , Entries[index].Name - ); - } + result = string_append_fmt( result, "%s %s;\n", Entries[1]->to_string(), Name ); break; case Function: @@ -303,9 +631,9 @@ namespace gen if ( left <= 0 ) fatal( "Code::to_string - Name: %s Type: %s, expected definition", Name, Type ); - if ( Entries[index].Type == Specifiers ) + if ( Entries[index]->Type == Specifiers ) { - result = string_append_fmt( result, "%s", Entries[index].to_string() ); + result = string_append_fmt( result, "%s", Entries[index]->to_string() ); index++; left--; } @@ -313,18 +641,45 @@ namespace gen if ( left <= 0 ) fatal( "Code::to_string - Name: %s Type: %s, expected return type", Name, Type ); - result = string_append_fmt( result, "\n%s %s(", Entries[index].to_string(), Name ); + result = string_append_fmt( result, "\n%s %s(", Entries[index]->to_string(), Name ); index++; left--; - if ( left && Entries[index].Type == Parameters ) + if ( left && Entries[index]->Type == Parameters ) { - result = string_append_fmt( result, "%s", Entries[index].to_string() ); + result = string_append_fmt( result, "%s", Entries[index]->to_string() ); index++; left--; } - result = string_append_fmt( result, ")\n{\n%s\n}", Entries[index].to_string() ); + result = string_append_fmt( result, ")\n{\n%s\n}", Entries[index]->to_string() ); + } + break; + + case Function_Body: + fatal("NOT SUPPORTED YET"); + break; + + case Namespace: + fatal("NOT SUPPORTED YET"); + break; + + case Namespace_Body: + fatal("NOT SUPPORTED YET"); + break; + + case Parameters: + { + result = string_append_fmt( result, "%s %s", Entries[0]->to_string(), Name ); + + s32 index = 1; + s32 left = array_count( Entries ) - 1; + + while ( left--, left > 0 ) + result = string_append_fmt( result, ", %s %s" + , Entries[index]->Entries[0]->to_string() + , Entries[index]->Name + ); } break; @@ -344,9 +699,17 @@ namespace gen fatal("NOT SUPPORTED YET"); break; + case Typedef: + fatal("NOT SUPPORTED YET"); + break; + case Typename: result = string_append_fmt( result, "%s", Name ); break; + + case Using: + fatal("NOT SUPPORTED YET"); + break; } return result; @@ -356,7 +719,7 @@ namespace gen void Builder::print( Code code ) { - Buffer = string_append_fmt( Buffer, "%s\n\n", code.to_string() ); + Buffer = string_append_fmt( Buffer, "%s\n\n", code->to_string() ); } bool Builder::open( char const* path ) diff --git a/project/gen.hpp b/project/gen.hpp index 1b900e8..aa8f21d 100644 --- a/project/gen.hpp +++ b/project/gen.hpp @@ -1,20 +1,42 @@ +/* + gencpp: A simple staged metaprogramming library for C++. + + This library is intended for small-to midsize projects that want rapid complation times + for fast debugging. + + AST type checking supports only a small subset of c++. + See the 'ECode' namespace and 'gen API' region to see what is supported. + + There is no support for accessability fields in structs. +*/ + #pragma once #include "Bloat.hpp" +// Defined by default. + +#define GEN_ENABLE_READONLY_AST +// #define GEN_DEFINE_DSL + +#define gen_time #ifdef gen_time namespace gen { + #if 0 ct sw ColumnLimit = 256; ct sw MaxLines = kilobytes(256); using LineStr = char[ColumnLimit]; + #endif + // Specifier Type enum Specifier : u8 { Alignas, // alignas(#) Constexpr, // constexpr Inline, // inline + C_Linkage, // extern "C" API_Import, // Vendor specific way dynamic import symbol API_Export, // Vendor specific way to dynamic export @@ -27,12 +49,16 @@ namespace gen Num_Specifiers }; + // Specifier to string + inline char const* specifier_str( Specifier specifier ) { - static char const* lookup[ Num_Specifiers ] = { + static + char const* lookup[ Num_Specifiers ] = { "alignas", "constexpr", "inline", + "extern \"C\"", #if defined(ZPL_SYSTEM_WINDOWS) "__declspec(dllexport)", @@ -44,63 +70,93 @@ namespace gen "extern", "static", "static", + "static", "thread_local" }; return lookup[ specifier ]; } - struct Code + // Code Type + namespace ECode { - enum EType : u8 + enum Type : u8 { Invalid, - Unused, Untyped, // User provided raw string. - Decl_Type, - Decl_Function, + Decl_Function, // Forward a function + Decl_Type, // Forward a type. + Function, // ( ) + Function_Body, // { } + Namespace, + Namespace_Body, Parameters, // Used with functions. + Specifiers, Struct, Struct_Body, - Function, - Function_Body, - Specifiers, Variable, + Typedef, Typename, + Using, Num_Types }; - #pragma region Member API - void comment( string value ) + inline + char const* str( Type type ) { - Comment = value; - } + static + char const* lookup[Num_Types] = { + "Invalid", + "Untyped", + + "Decl_Function", + "Decl_type", + "Function", + "Function_Body", + "Namespace", + "Namespace_Body", + "Parameters", + "Specifiers", + "Struct", + "Struct_Body", + "Variable", + "Typedef", + "Typename", + "using" + }; + + return lookup[ type ]; + } + } + using CodeT = ECode::Type; + + // TODO: If perf needs it, convert layout an SOA format. + /* + Simple AST POD with functionality to seralize into C++ syntax. + + ASTs are currently stored as an AOS. They are always reconstructed on demand. + Thus redundant AST can easily occur. + Not sure if its better to store them in a hashmap. + */ + struct AST + { + #pragma region Member API forceinline - void add( Code other ) + void add( AST* other ) { array_append( Entries, other ); - } - forceinline - void add( array(Code) other ) - { - array_appendv( Entries, other, sizeof(other) ); - } - - forceinline - void add( Code* entries, u32 num_entries ) - { - array_appendv( Entries, entries, num_entries ); + other->Parent = this; } forceinline bool has_entries() { - static bool lookup[Num_Types] = { + static bool lookup[ ECode::Num_Types] = { false, // Invalid false, // Unused false, // Untyped @@ -117,105 +173,310 @@ namespace gen return lookup[Type]; } - string to_string(); + forceinline + bool is_invalid() + { + return Type != ECode::Invalid; + } forceinline - operator bool() + char const* type_str() { - return Type != Invalid; + return ECode::str( Type ); } - operator char const*() - { - return to_string(); - } + string to_string(); - #if 0 - bool operator ==( Code& other ) - { - bool children_equal = true; - - #define is( Value_ ) Type == Value_ - - if ( has_children() ) - { - u32 left = array_count( Children ); - do - { - - } - while ( left--, left > 0 ) - } - - return - Type == other.Type - && Name == other.Name - && children_equal - ; - } - #endif #pragma endregion Member API - #define Using_Code_POD \ - Code::EType Type; \ - string Name; \ - string Comment; \ - union { \ - array(Code) Entries; \ - string Content; \ + #define Using_Code_POD \ + CodeT Type; \ + bool Readonly; \ + AST* Parent; \ + string Name; \ + string Comment; \ + union { \ + array(AST*) Entries; \ + string Content; \ }; Using_Code_POD; }; - using CodeType = Code::EType; - - struct Code_POD + struct CodePOD { Using_Code_POD; }; - constexpr Code UnusedCode = { Code::Unused, nullptr, nullptr, { nullptr } }; + // Its intended for the AST to have equivalent size to its POD. + // All extra functionality within the AST namespace should just be syntatic sugar. + static_assert( sizeof(AST) == sizeof(CodePOD), "ERROR: AST IS NOT POD" ); + /* + AST* typedef as to not constantly have to add the '*' as this is written often.. + + If GEN_ENABLE_READONLY_AST is defined, readonly assertions will be done on any member dreference, + and the 'gen API' related functions. will set their created ASTs to readonly before returning. + + Casting to AST* will bypass. + */ + struct Code + { + AST* ast; + + forceinline + operator bool() + { + return ast->is_invalid(); + } + + bool operator ==( Code other ) + { + return ast == other.ast; + } + + operator AST*() + { + return ast; + } + + Code& operator =( Code other ) + { + ast = other.ast; + + return *this; + } + + #ifdef GEN_ENABLE_READONLY_AST + forceinline + AST* operator ->() + { + if ( ast == nullptr ) + fatal("Attempt to dereference a nullptr!"); + + if ( ast->Readonly ) + fatal("Attempted to access a member from a readonly ast!"); + + return ast; + } + + Code& operator *() = delete; + #endif + }; + + // Used when the its desired when omission is allowed in a definition. + ct Code UnusedCode = { nullptr }; + + // Used internally for the most part to identify invaidly generated code. + ct CodePOD InvalidCode = { ECode::Invalid, false, nullptr, nullptr, nullptr, { nullptr } }; + + /* + Type registy: Used to store Typename ASTs. Types are registered by their string literal value. + + Purely used as a memory optimization. + 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 ); + +#pragma region gen API + /* + Initialize the library. + This currently just initializes the CodePool. + */ void init(); + /* + Foward Declare a type: + ; + */ Code decl_type( char const* name, Code type, Code specifiers = UnusedCode ); + /* + Foward Declare a function: + ( ); + */ Code decl_fn( char const* name , Code specifiers , Code params , Code ret_type ); - Code def_parameters( s32 num, ... ); + /* + Define an expression: + < c/c++ expression > + */ + Code def_expression( Code value ); + /* + Define a function: + ( ) + { + + } + */ Code def_function( char const* name , Code specifiers , Code params , Code ret_type , Code body ); - Code def_function_body( u32 num, ... ); + /* + Define a fucntion body: + { + + + ... + } + + Each entry is provided an empty line separation. + */ + Code def_function_body( s32 num, ... ); + + /* + Define a namespace; + namespace + { + + } + */ Code def_namespace( char const* name, Code body ); - Code def_namespace_body( u32 num, ... ); - Code def_specifiers( u32 num , ... ); + /* + Define a namespace body: + { + + ... + } + + Each entry is provided an empty line separation. + */ + Code def_namespace_body( s32 num, ... ); + + /* + Define a set of parameters for a function: + , ... + */ + Code def_parameters( s32 num, ... ); + + /* + Define a set of specifiers for a function, struct, type, or varaible + */ + Code def_specifiers( s32 num , ... ); + + /* + Define a struct: + struct : + { + + } + */ Code def_struct( char const* name, Code body, Code parent = UnusedCode, Code specifiers = UnusedCode ); - Code def_struct_body( u32 num, ... ); + /* + Define a struct's body: + { + + + ... + } + + Each entry is provided an empty line separation. + */ + Code def_struct_body( s32 num, ... ); + + /* + Define a variable: + = ; + */ Code def_variable( char const* name, Code type, Code value = UnusedCode, Code specifiers = UnusedCode ); + /* + Define a type AST value. + Useless by itself, its intended to be used in conjunction with + */ 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_namespace( char const* name ); + + /* + Define an untyped code string. + + Untyped code may be used in bodies of functions, namespaces, or structs + or the in places where expressions may be placed. + + Because the code within it is untyped, errors will naturally not be provided. + Consider this an a preprocessor define. + */ + Code untyped_str( char const* str ); + + /* + Define an untyped code string using traditional 'printf'. + + Untyped code may be used in bodies of functions, namespaces, or structs + or the in places where expressions may be placed. + + Because the code within it is untyped, errors will naturally not be provided. + Consider this an a preprocessor define. + */ Code untyped_fmt( char const* fmt, ... ); - Code token_fmt( char const* fmt, ... ); + /* + Define an untyped code string using token formatting: + ... { } ... Will be repalced with value of token ID. + + Values are to provided as: , , ... + + num_tokens : The number of ID-Value pairs provided. + + Untyped code may be used in bodies of functions, namespaces, or structs + or the in places where expressions may be placed. + + Because the code within it is untyped, errors will naturally not be provided. + Consider this an a preprocessor define. + */ + Code token_fmt( char const* fmt, s32 num_tokens, ... ); + /* + Creates a unit file. + + 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. + + The name provided is the name of the file. + */ + Code create_Unit( char const* name ); + + /* + Used to generate the files. + This is inspired by jai's usage of the string_builder with #insert. + + Its expected when using this library that Code ast will be serialized with the: + Builder::print() proc + + The seralized content of the Code ast will be appended to Buffer within an empty line + prepared for a another definition or to add an empty newline to the end of the file. + + Builder::write() should be called when all Code has been seralized for that file. + + The #insert directive is thus represented by an #include of the generated file at your desired line + of any file in the target project. + */ struct Builder { zpl_file File; @@ -226,7 +487,67 @@ namespace gen bool open( char const* path ); void write(); }; +#pragma endregion gen API } -#define gen_main main +#pragma region MACROS +# define gen_main main + +# define __ UnusedCode + +/* + gen's Domain Specific Langauge. + + Completely optional, makes the code gen syntax less verbose.. +*/ +#ifdef GEN_DEFINE_DSL +# define type( Name_, Value_ ) Code Name_ = gen::def_type( txt(Value_) ) +# define type_fmt( Name_, Fmt_, ... ) Code Name_ = gen::def_type( bprintf( Fmt_, __VA_ARGS__ ) ) +# define value( Name_, Value_ ) Code Name_ = gen::untyped_str( Value_ ) +# define specifiers( Name_, ... ) Code Name_ = gen::def_specifiers( VA_NARGS( __VA_ARGS__ ), __VA_ARGS__ ) +# define using( Name_, Type_ ) Code Name_ = gen::def_using( #Name_, Type_ ) + +# define var( Name_, Type_, Value_, Specifiers_ ) \ + Code Name_ = gen::def_variable( #Name_, Type_, untyped_str( #Value_ ), Specifiers_ ) + +// # define def ( Name _ ) Code Name_; + +# define params( ... ) gen::def_parameters( VA_NARGS( __VA_ARGS__ ) / 2, __VA_ARGS__ ) + +/* + Defines scoped symbol. + + Used with: + - function + - namespace + - struct +*/ +# define def( Name_ ) Code Name_; + +# define function( Name_, Specifiers_, ReturnType_, Parameters_, Body_ ) \ + Name_ = gen::def_function( #Name_, Specifiers_, Parameters_, ReturnType_, Body_ ) + +# define function_body( ... ) \ + gen::def_function_body( VA_NARGS( __VA_ARS__ ), __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__ ) +#endif +#pragma endregion MACROS + +#pragma region CONSTANTS +namespace gen +{ + // Predefined typename codes. + + extern const Code t_bool; + extern const Code t_sw; + extern const Code t_uw; + + extern const Code spec_inline; +} +#pragma endregion CONSTANTS #endif diff --git a/test/Array.hpp b/test/Array.hpp index 6ccc4f4..956d9d8 100644 --- a/test/Array.hpp +++ b/test/Array.hpp @@ -17,6 +17,7 @@ Code t_uw = def_type( txt(uw) ); Code t_allocator = def_type( txt(allocator) ); + #ifndef GEN_DEFINE_DSL Code header; { Code num = def_variable( "Num", t_uw ); @@ -30,20 +31,44 @@ Code grow_formula; { Code spec = def_specifiers(1, Specifier::Inline); - Code params = def_parameters(1, "value", t_uw ); - Code body = untyped_fmt( "\t""return 2 * value * 8;" ); + Code params = def_parameters(1, t_uw, "value" ); + Code body = untyped_str( "return 2 * value * 8;" ); grow_formula = def_function( "grow_formula", spec, params, t_sw, body ); } - Code base_body = def_struct_body(2, header, grow_formula); - Code base = def_struct( "ArrayBase", base_body ); - return base; + Code body = def_struct_body(2, header, grow_formula); + Code ArrayBase = def_struct( "ArrayBase", body ); + + #else + def( ArrayBase ) + def ( Header ) + { + var( Num, t_uw, __, __ ); + var( Capacity, t_uw, __, __); + var( Allocator, t_allocator, __, __); + + Code body = struct_body( Num, Capacity, Allocator ); + + struct( Header, __, __, body ); + } + + def( grow_formula ) + { + function( grow_formula, spec_inline, t_uw, params( t_uw, "value" ), untyped_str("return 2 * value * 8") ); + } + + Code body = struct_body( Header, grow_formula ); + struct( ArrayBase, __, __, body ); + #endif + + 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 // Make these global consts to be accessed anywhere... Code t_uw = def_type( txt(uw) ); Code t_sw = def_type( txt(sw) ); @@ -51,16 +76,14 @@ Code t_allocator = def_type( txt(allocator) ); Code t_void = def_type( txt(void) ); - Code v_nullptr = untyped_fmt( "nullptr" ); + Code v_nullptr = untyped_str( "nullptr" ); Code spec_ct = def_specifiers(1, Specifier::Constexpr ); Code spec_inline = def_specifiers(1, Specifier::Inline ); Code type = def_type( type_str ); - Code ptr_type = def_type( string_sprintf_buf( g_allocator, "%s*", type_str ) ); - Code ref_type = def_type( string_sprintf_buf( g_allocator, "%s&", type_str ) ); - - string name = string_sprintf_buf( g_allocator, "Array_%s", type_str ); + Code ptr_type = def_type( bprintf( "%s*", type_str ) ); + Code ref_type = def_type( bprintf( "%s&", type_str ) ); // From ArrayBase Code t_header = def_type( "Header" ); @@ -69,43 +92,43 @@ Code array_def; { - Code using_type = def_using( "type", type ); + Code using_type = def_using( "Type", type ); Code data = def_variable( "Data", ptr_type ); Code init; { - Code params = def_parameters( 1, "mem_hanlder", t_allocator ); - Code body = untyped_fmt( "\t""return init_reserve( mem_handler, grow_formula(0) );" ); + Code params = def_parameters( 1, t_allocator, "mem_handler" ); + Code body = untyped_str( "return init_reserve( mem_handler, grow_formula(0) );" ); init = def_function( "init", UnusedCode, params, t_bool, body ); } Code init_reserve; { - Code params = def_parameters( 2, "mem_handler", ref_type, "capacity", t_sw ); + Code params = def_parameters( 2, t_allocator, "mem_handler", t_sw, "capacity" ); Code body; { - Code header_value = untyped_fmt( - "rcast( Header*, alloc( mem_handler, sizeof( Header ) + sizeof(type) + capacity ))" + Code header_value = untyped_str( + "rcast( Header*, alloc( mem_handler, sizeof( Header ) + sizeof(Type) + capacity ))" ); - Code header = def_variable( "", ptr_header, header_value ); + Code header = def_variable( "header", ptr_header, header_value ); - Code null_check = untyped_fmt( - "\t" "if (header == nullptr)" - "\n\t\t" "return false;" + Code null_check = untyped_str( + "if (header == nullptr)" + "\n" "return false;" ); - Code header_init = untyped_fmt( - "\n\t" "header->Num = 0;" - "\n\t" "header->Capacity = capacity;" - "\n\t" "header->Allocator = mem_handler;" + Code header_init = untyped_str( + "header->Num = 0;" + "\n""header->Capacity = capacity;" + "\n""header->Allocator = mem_handler;" ); - Code assign_data = untyped_fmt( - "\t" "Data = rcast( %s, header + 1 );", ptr_type + Code assign_data = untyped_str( + "Data = rcast( %s, header + 1 );", ptr_type ); - Code ret_true = untyped_fmt( "\t""return true" ); + Code ret_true = untyped_str( "\t""return true" ); body = def_function_body( 5 , header @@ -121,9 +144,9 @@ Code free; { - Code body = untyped_fmt( - "\t" "Header& header = get_header();" - "\n\t" "::free( header.Allocator, & get_header() );" + Code body = untyped_str( + "Header& header = get_header();" + "\n""::free( header.Allocator, & get_header() );" ); free = def_function( "free", UnusedCode, UnusedCode, t_void, body ); @@ -131,22 +154,22 @@ Code append; { - Code params = def_parameters( 1, "value", type ); + Code params = def_parameters( 1, type, "value" ); Code body; { - Code header = def_variable( "", ref_header, untyped_fmt( "get_header()") ); + Code header = def_variable( "header", ref_header, untyped_str( "get_header()") ); - Code check_cap = untyped_fmt( - "\t" "if ( header.Capacity < header.Num + 1 )" - "\n\t\t" "if ( ! grow(0) )" - "\n\t\t\t" "return false;" + Code check_cap = untyped_str( + "if ( header.Capacity < header.Num + 1 )" + "\n" "if ( ! grow(0) )" + "\n" "return false;" ); - Code assign = untyped_fmt( - "\t" "Data[ header.Num ] = value;" - "\t\n" "header.Num++;" + Code assign = untyped_str( + "Data[ header.Num ] = value;" + "\n" "header.Num++;" "\n" - "\n\t" "return true;" + "\n" "return true;" ); body = def_function_body( 3, header, check_cap, assign ); @@ -157,9 +180,9 @@ Code back; { - Code body = untyped_fmt( - "\t" "Header& header = get_header();" - "\n\t" "return data[ header.Num - 1 ];" + Code body = untyped_str( + "Header& header = get_header();" + "\n" "return data[ header.Num - 1 ];" ); back = def_function( "back", UnusedCode, UnusedCode, type, body ); @@ -167,26 +190,26 @@ Code clear; { - Code body = untyped_fmt( "\t""get_header().Num = 0;" ); + Code body = untyped_str( "get_header().Num = 0;" ); clear = def_function( "clear", UnusedCode, UnusedCode, t_void, body ); } Code fill; { - Code params = def_parameters( 3, "begin", t_uw, "end", t_uw, "value", type ); + Code params = def_parameters( 3, t_uw, "begin", t_uw, "end", type, "value" ); Code body; { - Code header = def_variable( "", ref_header, untyped_fmt( "get_header()") ); + Code header = def_variable( "header", ref_header, untyped_str( "get_header()") ); - Code check = untyped_fmt( - "\t" "if ( begin < 0 || end >= header.Num )" - "\n\t\t" "fatal( \"Range out of bounds\" );" + Code check = untyped_str( + "if ( begin < 0 || end >= header.Num )" + "\n" "fatal( \"Range out of bounds\" );" ); - Code iter = untyped_fmt( - "\t" "for ( sw index = begin; index < end; index++ )" - "\n\t\t" "Data[index] = vallue;" + Code iter = untyped_str( + "for ( sw index = begin; index < end; index++ )" + "\n" "Data[index] = vallue;" ); body = def_function_body( 3, header, check, iter ); @@ -197,25 +220,25 @@ Code get_header; { - Code body = untyped_fmt( "\t""return pcast( Header, Data - 1 );" ); + Code body = untyped_str( "return pcast( Header, Data - 1 );" ); get_header = def_function( "get_header", spec_inline, UnusedCode, ref_header, body ); } Code grow; { - Code param = def_parameters( 1, "min_capacity", t_uw ); + Code param = def_parameters( 1, t_uw, "min_capacity" ); Code body; { - Code header = def_variable( "header", ref_header, untyped_fmt("get_header") ); - Code new_capacity = def_variable( "new_capacity", t_uw, untyped_fmt("grow_formula( header.Capacity )") ); + Code header = def_variable( "header", ref_header, untyped_str("get_header()") ); + Code new_capacity = def_variable( "new_capacity", t_uw, untyped_str("grow_formula( header.Capacity )") ); - Code check_n_set = untyped_fmt( - "\t" "if ( new_capacity < min_capacity )" - "\n\t\t" "new_capacity = min_capacity;" + Code check_n_set = untyped_str( + "if ( new_capacity < min_capacity )" + "\n" "new_capacity = min_capacity;" ); - Code ret = untyped_fmt( "\t" "return set_capacity( new_capacity );" ); + Code ret = untyped_str( "return set_capacity( new_capacity );" ); body = def_function_body( 4, header, new_capacity, check_n_set, ret ); } @@ -227,10 +250,10 @@ { Code body; { - Code header = def_variable( "header", ref_header, untyped_fmt("get_header()") ); + Code header = def_variable( "header", ref_header, untyped_str("get_header()") ); - Code assertion = untyped_fmt( "\t" "assert( header.Num > 0 );" ); - Code decrement = untyped_fmt( "\t" "header.Num--; " ); + Code assertion = untyped_str( "assert( header.Num > 0 );" ); + Code decrement = untyped_str( "header.Num--; " ); body = def_function_body( 3, header, assertion, decrement ); } @@ -240,17 +263,17 @@ Code reserve; { - Code params = def_parameters( 1, "new_capacity", t_uw ); + Code params = def_parameters( 1, t_uw, "new_capacity" ); Code body; { - Code header = def_variable( "header", ref_header, untyped_fmt("get_header()") ); + Code header = def_variable( "header", ref_header, untyped_str("get_header()") ); - Code check_n_set = untyped_fmt( - "\t" "if ( header.Capacity < new_capacity )" - "\n\t\t" "return set_capacity( new_capacity );" + Code check_n_set = untyped_str( + "if ( header.Capacity < new_capacity )" + "\n" "return set_capacity( new_capacity );" ); - Code ret = untyped_fmt( "\t" "return true" ); + Code ret = untyped_str( "\t" "return true" ); body = def_function_body( 3, header, check_n_set, ret ); } @@ -260,21 +283,21 @@ Code resize; { - Code param = def_parameters( 1, "new_num", t_uw ); + Code param = def_parameters( 1, t_uw, "new_num" ); Code body; { - Code header = def_variable( "header", ref_header, untyped_fmt("get_header()") ); + Code header = def_variable( "header", ref_header, untyped_str("get_header()") ); - Code check_n_grow = untyped_fmt( - "\t" "if ( header.Capacity < new_num )" - "\n\t\t" "if ( ! grow( new_num) )" - "\n\t\t\t" "return false;" + Code check_n_grow = untyped_str( + "if ( header.Capacity < new_num )" + "\n" "if ( ! grow( new_num) )" + "\n" "return false;" ); - Code set_n_ret = untyped_fmt( - "\t" "header.Count = new_num;" - "\n\t" "return true;" + Code set_n_ret = untyped_str( + "header.Count = new_num;" + "\n""return true;" ); body = def_function_body( 3, header, check_n_grow, set_n_ret ); @@ -285,40 +308,40 @@ Code set_capacity; { - Code param = def_parameters( 1, "capacity", t_uw ); + Code param = def_parameters( 1, t_uw, "capacity" ); Code body; { - Code header = def_variable( "header", ref_header, untyped_fmt("get_header()") ); + Code header = def_variable( "header", ref_header, untyped_str("get_header()") ); - Code checks = untyped_fmt( - "\t" "if ( capacity == header.Capacity )" - "\n\t\t" "return true;" - "\n" - "\n\t" "if ( capacity < header.Num )" - "\n\t\t" "header.Num = capacity;" + Code checks = untyped_str( + "if ( capacity == header.Capacity )" + "\n" "return true;" + + "if ( capacity < header.Num )" + "\n" "header.Num = capacity;" ); - Code size = def_variable( "size", t_uw, untyped_fmt("sizeof(Header) + sizeof(type) * capacity")); - Code new_header = def_variable( "new_header", ptr_header, untyped_fmt("rcast( Header*, alloc( header.Allocator, size ));")); + Code size = def_variable( "size", t_uw, untyped_str("sizeof(Header) + sizeof(Type) * capacity")); + Code new_header = def_variable( "new_header", ptr_header, untyped_str("rcast( Header*, alloc( header.Allocator, size ));")); - Code check_n_move = untyped_fmt( - "\t""if ( new_header == nullptr )" - "\n\t\t""return false;" - "\n" - "\n\t""memmove( new_header, & header, sizeof(Header) + sizeof(type) * header.Num );" + Code check_n_move = untyped_str( + "if ( new_header == nullptr )" + "\n" "return false;" + "\n" + "\n" "memmove( new_header, & header, sizeof(Header) + sizeof(Type) * header.Num );" ); - Code set_free_ret = untyped_fmt( - "\t" "new_header->Allocator = header.Allocator;" - "\n\t" "new_header->Num = header.Num;" - "\n\t" "new_header->Capacity = header.Capacity;" - "\n" - "\n\t" "zpl_free( header );" - "\n" - "\n\t" "*Data = new_header + 1;" - "\n" - "\n\t" "return true;" + Code set_free_ret = untyped_str( + "new_header->Allocator = header.Allocator;" + "\n" "new_header->Num = header.Num;" + "\n" "new_header->Capacity = header.Capacity;" + "\n" + "\n" "zpl_free( header );" + "\n" + "\n" "*Data = new_header + 1;" + "\n" + "\n" "return true;" ); body = def_function_body( 6, header, checks, size, new_header, check_n_move, set_free_ret ); @@ -327,6 +350,8 @@ set_capacity = def_function( "set_capacity", UnusedCode, param, t_bool, body ); } + string name = bprintf( "Array_%s", type_str ); + Code body = def_struct_body( 15 , using_type , data @@ -348,6 +373,282 @@ array_def = def_struct( name, body, parent ); } + #else + type( t_uw, uw ); + type( t_sw, sw ); + type( t_bool, bool ); + type( t_allocator, allocator ); + type( t_void, void ); + + value( v_nullptr, nullptr ); + + specifiers( spec_ct, Specifier::Constexpr ); + specifiers( spec_inline, Specifier::Inline ); + + type_fmt( type, type_str, __); + type_fmt( ptr_type, "%s*", type_str ); + type_fmt( ref_type, "%&", type_str ); + + + // From ArrayBase + type( t_header, Header ); + type( ptr_header, Header* ); + type( ref_header, Header& ); + + def( array_def ) + { + using( Type, type ); + var( Data, ptr_type, __, __ ); + + def( init ) + { + Code body = function_body( untyped_str("return init_reserve( mem_handler, grow_formula(0) );" )); + + function( init, __, t_bool, params( t_allocator, "mem_handler" ), body ); + } + + def( init_reserve ) + { + def( body ) + { + var(header, ptr_header, untyped_str("rcast( Header*, alloc( mem_handler, sizeof(Header) + sizeof(Type) + capacity))" ), __); + + Code null_check = untyped_str( + "if (header == nullptr)" + "\n" "return false;" + ); + + Code header_init = untyped_str( + "header->Num = 0;" + "\n""header->Capacity = capacity;" + "\n""header->Allocator = mem_handler;" + ); + + Code assign_data = untyped_str( "Data = rcast( Type*, header + 1 );" ); + Code ret_true = untyped_str( "return true" ); + + Code body = function_body( header, null_check, header_init, assign_data, ret_true ); + } + + function( init_reserve, __, t_bool, params( ref_type, "mem_handler" ) , body); + } + + def( free ) + { + Code body = untyped_str( + "Header& header = get_header();" + "\n""free( header.Allocator, & get_header() );" + ); + + function( free, __, t_void, __, body ); + } + + def( append ) + { + def( body ) + { + var( header, ref_header, untyped_str("get_header()"), __ ); + + Code check_cap = untyped_str( + "if ( header.Capacity < header.Num + 1 )" + "\n" "if ( ! grow(0) )" + "\n" "return false;" + ); + + Code assign = untyped_str( + "Data[ header.Num ] = value;" + "\n" "header.Num++;" + "\n" + "\n" "return true;" + ); + + body = function_body( header, check_cap, assign ); + } + + function( append, __, t_void, params( type, "value" ), body ); + } + + def( back ); + { + Code body = untyped_str( + "Header& header = get_header();" + "\n" "return data[ header.Num - 1 ];" + ); + + function( back, __, type, __, body ); + } + + def( clear ) + function( clear, __, t_void, __, untyped_str("get_header().Num = 0;") ); + + def( fill ); + { + def( body ) + { + var( header, ref_header, untyped_str("get_header()"), __ ); + + Code check = untyped_str( + "if ( begin < 0 || end >= header.Num )" + "\n" "fatal( \"Range out of bounds\" );" + ); + + Code iter = untyped_str( + "for ( sw index = begin; index < end; index++ )" + "\n" "Data[index] = vallue;" + ); + + function_body( header, check, iter ); + } + + function( fill, __, t_void, params( t_uw, "begin", t_uw, "end", type, "value" ), body ); + } + + def( get_header ) + function( get_header, spec_inline, ref_header, __, untyped_str("return pcast( Header, Data - 1);") ); + + def( grow ) + { + def( body ) + { + var( header, ref_header, untyped_str("get_header()"), __ ); + var( new_capacity, t_uw, untyped_str("grow_formula( header.Capacity)"), __); + + Code check_n_set = untyped_str( + "if ( new_capacity < min_capacity )" + "new_capacity = min_capacity;" + ); + + Code ret = untyped_str( "return set_capacity( new_capacity );" ); + + body = function_body( header, new_capacity, check_n_set, ret ); + } + + function( grow, __, t_bool, params( t_uw, "min_capacity" ), body ); + } + + def( pop ) + { + def( body ) + { + var( header, ref_header, get_header(), UnusedCode ); + + Code assertion = untyped_str( "assert( header.Num > 0 );" ); + Code decrement = untyped_str( "header.Num--; " ); + + body = function_body( header, assertion, decrement ); + } + + function( pop, __, t_void, __, body ); + } + + def( reserve ) + { + def( body ) + { + var( header, ref_header, untyped_str("get_header()"), __ ); + + Code check_n_set = untyped_str( + "if ( header.Capacity < new_capacity )" + "return set_capacity( new_capacity );" + ); + + Code ret = untyped_str("return true"); + + body = function_body( header, check_n_set, ret ); + } + + function( reserve, __, t_bool, params( t_uw, "new_capacity" ), body ); + } + + def( resize ) + { + def( body ) + { + var( header, ref_header, untyped_str("get_header()"), __ ); + + Code check_n_grow = untyped_str( + "if ( header.Capacity < new_num )" + "if ( ! grow( new_num) )" + "return false;" + ); + + Code set_n_ret = untyped_str( + "header.Count = new_num;" + "return true;" + ); + + body = function_body( header, check_n_grow, set_n_ret ); + } + + function( resize, __, t_bool, params( t_uw, "new_num" ), body ); + } + + def( set_capacity ) + { + def( body ) + { + var( header, ref_header, untyped_str("get_header()"), __ ); + + Code checks = untyped_str( + "if ( capacity == header.Capacity )" + "return true;" + "\n\n" + "if ( capacity < header.Num )" + "header.Num = capacity;" + ); + + var( size, t_uw, untyped_str("sizeof(Header) + sizeof(Type) * capacity"), __ ); + var( new_header, ptr_header, untyped_str("rcast( Header*, alloc( header.Allocator, size ))"), __ ); + + Code check_n_move = untyped_str( + "if ( new_header == nullptr )" + "return false;" + "\n\n" + "memmove( new_header, & header, sizeof(Header) + sizeof(Type) * header.Num );" + ); + + Code set_free_ret = untyped_str( + "new_header->Allocator = header.Allocator;" + "new_header->Num = header.Num;" + "new_header->Capacity = header.Capacity;" + "\n\n" + "zpl_free( header );" + "\n\n" + "*Data = new_header + 1;" + "\n\n" + "return true;" + ); + + body = function_body( header, checks, size, new_header, check_n_move, set_free_ret ); + } + + function( set_capacity, __, t_bool, params( t_uw, "capacity" ), body ); + } + + char const* name = bprintf( "Array_%s", type_str ); + + Code body = struct_body( + Type + , Data + + , init + , init_reserve + , append + , back + , clear + , fill + , free + , get_header + , grow + , pop + , reserve + , resize + , set_capacity + ); + + array_def = def_struct( name, body, parent ); + } + #endif return array_def; } diff --git a/test/math.hpp b/test/math.hpp index 8d5f6c5..be5eb85 100644 --- a/test/math.hpp +++ b/test/math.hpp @@ -22,21 +22,27 @@ { Code integral_type = def_type( type ); + #ifndef GEN_DEFINE_DSL string name = string_sprintf( g_allocator, (char*)sprintf_buf, ZPL_PRINTF_MAXLEN, "square", type ); - Code result; + Code square; { - Code params = def_parameters( 1, "value", integral_type ); + Code params = def_parameters( 1, integral_type, "value" ); Code specifiers = def_specifiers( 1, Specifier::Inline ); - Code ret_stmt = untyped_fmt( "\treturn value * value;" ); + Code ret_stmt = untyped_fmt( "return value * value;" ); - result = def_function( name, specifiers, params, integral_type, ret_stmt ); + square = def_function( name, specifiers, params, integral_type, ret_stmt ); } - if ( ! result ) + #else + def( square ) + function( square, __, integral_type, params( integral_type, "value" ), untyped_str("return value * value") ); + #endif + + if ( ! square ) fatal( "Failed to generate square function for: %s", type ); - return result; + return square; } u32 gen_math() diff --git a/test/test.cpp b/test/test.cpp index 74a2853..618733a 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,5 +1,6 @@ #include "Bloat.cpp" #include "math.hpp" +#include "Array.hpp" #ifdef gen_time @@ -12,9 +13,11 @@ int gen_main() zpl_printf("\nPress any key after attaching to process\n"); getchar(); - gen::init() + gen::init(); - int result = gen_math(); + int + result = gen_math(); + result = gen_array_file(); Memory::cleanup(); return result;