mirror of
				https://github.com/Ed94/gencpp.git
				synced 2025-10-30 22:40:54 -07:00 
			
		
		
		
	Major changes to library design, change test to reflect it.
This commit is contained in:
		
							
								
								
									
										11
									
								
								Readme.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								Readme.md
									
									
									
									
									
								
							| @@ -2,9 +2,17 @@ | ||||
|  | ||||
| An attempt at simple staged metaprogramming for c/c++. | ||||
|  | ||||
| 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. | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										549
									
								
								project/gen.cpp
									
									
									
									
									
								
							
							
						
						
									
										549
									
								
								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 ) | ||||
| 	{ | ||||
| 		using namespace ECode; | ||||
|  | ||||
| 		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 = Code::Decl_Type; | ||||
| 		result.Name = string_make( g_allocator, name ); | ||||
| 		result->Type = Decl_Type; | ||||
| 		result->Name = string_make( g_allocator, name ); | ||||
|  | ||||
| 		array_init( result.Entries, g_allocator ); | ||||
| 		result.add( specifiers ); | ||||
| 		result.add( type ); | ||||
| 		array_init( result->Entries, g_allocator ); | ||||
| 		result->add( specifiers ); | ||||
| 		result->add( type ); | ||||
|  | ||||
| 		return result; | ||||
| 	} | ||||
| @@ -35,57 +57,77 @@ namespace gen | ||||
| 		, Code ret_type | ||||
| 	) | ||||
| 	{ | ||||
| 		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 = Code::Decl_Function; | ||||
| 		result.Name = string_make( g_allocator, name ); | ||||
| 		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, ... ) | ||||
| 	{ | ||||
| 		using namespace ECode; | ||||
|  | ||||
| 		if (num <= 0) | ||||
| 			fatal("TT::make_paramters: num is %d", num); | ||||
| 			fatal( "TT::make_paramters: num cannot be zero or neg" ); | ||||
|  | ||||
| 		Code | ||||
| 		result       = make(); | ||||
| 		result.Type = Code::Parameters; | ||||
| 		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 ) | ||||
| 		{ | ||||
| 			type = va_arg(va, Code); | ||||
|  | ||||
| 			Code | ||||
| 			param       = make(); | ||||
| 			param.Name = string_make( g_allocator, va_arg(va, char const*) ); | ||||
| 			param->Type = Parameters; | ||||
| 			param->Name = string_make( g_allocator, va_arg(va, char const*) ); | ||||
|  | ||||
| 			array_init( param.Entries, g_allocator ); | ||||
| 			array_init( param->Entries, g_allocator ); | ||||
|  | ||||
| 			type = va_arg(va, Code); | ||||
| 			param.add( type ); | ||||
| 			if ( type->Type != Typename ) | ||||
| 				fatal( "gen::def_parameters: type of param %d is not a Typename", num - num + 1 ); | ||||
|  | ||||
| 			result.add(param); | ||||
| 			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->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->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->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, ...) | ||||
| 	{ | ||||
| @@ -206,21 +449,122 @@ namespace gen | ||||
|           | ||||
| 		Code  | ||||
| 		result          = make(); | ||||
| 		result.Name    = string_make( g_allocator, fmt ); | ||||
| 		result.Type    = Code::Untyped; | ||||
| 		result.Content = string_make( g_allocator, buf ); | ||||
| 		result->Name    = string_make( g_allocator, fmt ); | ||||
| 		result->Type    = ECode::Untyped; | ||||
| 		result->Content = string_make( g_allocator, buf ); | ||||
|  | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	Code token_fmt( char const* fmt, ... ) | ||||
| 	{ | ||||
|  | ||||
| 	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, 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 ) | ||||
|   | ||||
							
								
								
									
										459
									
								
								project/gen.hpp
									
									
									
									
									
								
							
							
						
						
									
										459
									
								
								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;    \ | ||||
| 		CodeT             Type;     \ | ||||
| 		bool              Readonly; \ | ||||
| 		AST*              Parent;   \ | ||||
| 		string            Name;     \ | ||||
| 		string            Comment;  \ | ||||
| 		union {                     \ | ||||
| 			array(Code)   Entries; \ | ||||
| 			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 | ||||
| } | ||||
|  | ||||
| #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 | ||||
|   | ||||
							
								
								
									
										499
									
								
								test/Array.hpp
									
									
									
									
									
								
							
							
						
						
									
										499
									
								
								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;" | ||||
| 					Code check_n_move = untyped_str( | ||||
| 							"if ( new_header == nullptr )" | ||||
| 					"\n"		"return false;" | ||||
| 					"\n" | ||||
| 						"\n\t""memmove( new_header, & header, sizeof(Header) + sizeof(type) * header.Num );" | ||||
| 					"\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;" | ||||
| 					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\t"	"zpl_free( header );" | ||||
| 					"\n"	"zpl_free( header );" | ||||
| 					"\n" | ||||
| 						"\n\t"	"*Data = new_header + 1;" | ||||
| 					"\n"	"*Data = new_header + 1;" | ||||
| 					"\n" | ||||
| 						"\n\t"	"return true;" | ||||
| 					"\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; | ||||
| 	} | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user