mirror of
https://github.com/Ed94/gencpp.git
synced 2025-06-15 03:01:47 -07:00
Major changes to library design, change test to reflect it.
This commit is contained in:
@ -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
|
||||
|
575
project/gen.cpp
575
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 )
|
||||
|
471
project/gen.hpp
471
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, // <type> <name>( <parameters> )
|
||||
Function_Body, // { <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:
|
||||
<specifiers> <type> <name>;
|
||||
*/
|
||||
Code decl_type( char const* name, Code type, Code specifiers = UnusedCode );
|
||||
|
||||
/*
|
||||
Foward Declare a function:
|
||||
<specifiers> <name> ( <params> );
|
||||
*/
|
||||
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:
|
||||
<specifiers> <name> ( <params> )
|
||||
{
|
||||
<body>
|
||||
}
|
||||
*/
|
||||
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:
|
||||
{
|
||||
<entry>
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
Each entry is provided an empty line separation.
|
||||
*/
|
||||
Code def_function_body( s32 num, ... );
|
||||
|
||||
/*
|
||||
Define a namespace;
|
||||
namespace <name>
|
||||
{
|
||||
<body>
|
||||
}
|
||||
*/
|
||||
Code def_namespace( char const* name, Code body );
|
||||
Code def_namespace_body( u32 num, ... );
|
||||
|
||||
Code def_specifiers( u32 num , ... );
|
||||
/*
|
||||
Define a namespace body:
|
||||
{
|
||||
<entry>
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
Each entry is provided an empty line separation.
|
||||
*/
|
||||
Code def_namespace_body( s32 num, ... );
|
||||
|
||||
/*
|
||||
Define a set of parameters for a function:
|
||||
<name> <type>, ...
|
||||
*/
|
||||
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 <specifiers> <name> : <parent>
|
||||
{
|
||||
<body>
|
||||
}
|
||||
*/
|
||||
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:
|
||||
{
|
||||
<entry>
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
Each entry is provided an empty line separation.
|
||||
*/
|
||||
Code def_struct_body( s32 num, ... );
|
||||
|
||||
/*
|
||||
Define a variable:
|
||||
<specifiers> <type> <name> = <value>;
|
||||
*/
|
||||
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 <name> = <type>;
|
||||
*/
|
||||
Code def_using( char const* name, Code type );
|
||||
|
||||
/*
|
||||
Define a using namespace:
|
||||
using namespace <name>;
|
||||
|
||||
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:
|
||||
... { <ID> } ... Will be repalced with value of token ID.
|
||||
|
||||
Values are to provided as: <char const* ID>, <char const* Value>, ...
|
||||
|
||||
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
|
||||
|
Reference in New Issue
Block a user