Updated readme, added def_body and AST::validate_body

Fixed meson first setup error with missing thirdparty dir (removed it since its no longer used)

Improved SOA test to use newly added funtions.
This commit is contained in:
2023-07-12 15:59:47 -04:00
parent 128b0e17fe
commit 4e61fefc55
13 changed files with 319 additions and 193 deletions

View File

@ -8,24 +8,6 @@ All dependencies are currently held within `Bloat.hpp` and `Bloat.cpp`
All the library code is contained in two files: `gen.hpp` and `gen.cpp`
## Bloat.hpp/cpp
Currently acts as the isolation header for thridparty dependencies along with code not directly related to the library.
Organization:
* ZPL inclusion and selective symbol exposure to global scope.
* Utility macro definitions used throughout the library.
* Global memory arena definition
* StrC and String definitions
* Token string formatter
* Formatted and Fatal Logs
The cpp contains the implementation of the global memory arena and the token formmatter.
Any global symbol pollution will be removed when dependencies are intergrated properly into the library.
## gen.hpp
While getting fleshed out, all feature macros are defined on the top of the header.

View File

@ -2795,6 +2795,114 @@ namespace gen
return true;
}
bool AST::validate_body()
{
using namespace ECode;
#define CheckBodyType( BodyType ) \
if ( Type != BodyType ) \
{ \
log_failure( "AST::validate_body: Invalid body type %s", debug_str() ); \
return false; \
}
#define CheckEntries( Unallowed_Types ) \
do \
{ \
for ( s32 idx = 0; idx < num_entries(); idx++ ) \
{ \
AST* elem = entry( idx ); \
\
switch ( elem->Type ) \
{ \
Unallowed_Types \
log_failure( "AST::validate_body: Invalid entry in body %s", elem->debug_str() ); \
return false; \
} \
} \
} \
while (0);
switch ( Type )
{
case Class_Body:
CheckBodyType( Class_Body );
CheckEntries( AST_BODY_CLASS_UNALLOWED_TYPES );
break;
case Enum_Body:
CheckBodyType( Enum_Body );
for ( s32 idx = 0; idx < body()->num_entries(); idx++ )
{
AST* elem = entry( idx );
if ( elem->Type != Untyped )
{
log_failure( "AST::validate_body: Invalid entry in enum body (needs to be untyped or comment) %s", elem->debug_str() );
return false;
}
}
break;
case Export_Body:
for ( s32 idx = 0; idx < num_entries(); idx++ )
{
AST* elem = entry( idx );
if ( elem->Type != Untyped )
{
log_failure( "AST::validate_body: Invalid entry in export body %s", elem->debug_str() );
return false;
}
}
break;
case Extern_Linkage:
CheckBodyType( Extern_Linkage );
CheckEntries( AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES );
break;
case Function_Body:
CheckBodyType( Function_Body );
CheckEntries( AST_BODY_FUNCTION_UNALLOWED_TYPES );
break;
case Global_Body:
for ( s32 idx = 0; idx < num_entries(); idx++ )
{
AST* elem = entry( idx );
if ( elem->Type != Untyped )
{
log_failure( "AST::validate_body: Invalid entry in global body %s", elem->debug_str() );
return false;
}
}
break;
case Namespace_Body:
CheckBodyType( Namespace_Body );
CheckEntries( AST_BODY_NAMESPACE_UNALLOWED_TYPES );
break;
case Struct_Body:
CheckBodyType( Struct_Body );
CheckEntries( AST_BODY_STRUCT_UNALLOWED_TYPES );
break;
case Union_Body:
CheckBodyType( Union_Body );
for ( s32 idx = 0; idx < body()->num_entries(); idx++ )
{
AST* elem = entry( idx );
if ( elem->Type != Untyped )
{
log_failure( "AST::validate_body: Invalid entry in union body (needs to be untyped or comment) %s", elem->debug_str() );
return false;
}
}
break;
default:
log_failure( "AST::validate_body: Invalid this AST does not have a body %s", debug_str() );
return false;
}
return false;
}
#pragma endregion AST
#pragma region Gen Interface
@ -4404,7 +4512,7 @@ namespace gen
\
if ( codes == nullptr ) \
{ \
log_failure("gen::def_class_body: Provided a null array of codes"); \
log_failure("gen::" stringize(Name_)" : Provided a null array of codes"); \
return Code::Invalid; \
}
@ -4504,9 +4612,9 @@ namespace gen
return Code::Invalid;
}
if ( entry->Type != Untyped )
if ( entry->Type != Untyped && entry->Type != Comment )
{
log_failure("gen::def_enum_body: Entry type is not allowed - %s. Must be of untyped type.", entry->debug_str() ); \
log_failure("gen::def_enum_body: Entry type is not allowed - %s. Must be of untyped or comment type.", entry->debug_str() ); \
return Code::Invalid;
}
@ -4536,7 +4644,7 @@ namespace gen
return Code::Invalid;
}
if ( entry->Type != Untyped )
if ( entry->Type != Untyped && entry->Type != Comment )
{
log_failure("gen::def_enum_body: Entry type is not allowed: %s", entry->debug_str() ); \
return Code::Invalid;
@ -4906,7 +5014,7 @@ namespace gen
if ( entry->Type != Untyped && entry->Type != Comment )
{
log_failure("gen::def_union_body: Entry type is not allowed - %s. Must be of untyped type.", entry->debug_str() ); \
log_failure("gen::def_union_body: Entry type is not allowed - %s. Must be of untyped or comment type.", entry->debug_str() ); \
return Code::Invalid;
}
@ -4936,7 +5044,7 @@ namespace gen
return Code::Invalid;
}
if ( entry->Type != Untyped )
if ( entry->Type != Untyped && entry->Type != Comment )
{
log_failure("gen::def_union_body: Entry type is not allowed: %s", entry->debug_str() ); \
return Code::Invalid;
@ -8099,7 +8207,7 @@ namespace gen
#pragma region Builder
void Builder::print( Code code )
{
Buffer.append_fmt( "%s\n", code.to_string() );
Buffer.append_fmt( "%s\n", code->to_string() );
}
void Builder::print_fmt( char const* fmt, ... )

View File

@ -2666,6 +2666,61 @@ namespace gen
// Desired width of the AST data structure.
constexpr u32 AST_POD_Size = 256;
struct AST;
/*
AST* typedef as to not constantly have to add the '*' as this is written often..
*/
struct Code
{
# pragma region Statics
// Used to identify ASTs that should always be duplicated. (Global constant ASTs)
static Code Global;
// Used to identify invalid generated code.
static Code Invalid;
# pragma endregion Statics
# pragma region Member Functions
void set_global();
bool is_valid();
bool operator ==( Code other )
{
return ast == other.ast;
}
bool operator !=( Code other )
{
return ast != other.ast;
}
operator AST*()
{
return ast;
}
AST* operator->()
{
if ( ast == nullptr )
{
log_failure("Attempt to dereference a nullptr!");
return nullptr;
}
return ast;
}
# pragma endregion Member Functions
AST* ast;
};
struct Code_POD
{
AST* ast;
};
// TODO: If perf needs it, convert layout an SOA format.
/*
Simple AST POD with functionality to seralize into C++ syntax.
@ -2679,6 +2734,9 @@ namespace gen
struct AST
{
# pragma region Member Functions
// add_entry with validation
void add( AST* other );
void add_entry( AST* other );
AST* body()
@ -2688,7 +2746,7 @@ namespace gen
AST* duplicate();
AST*& entry( u32 idx )
AST* entry( u32 idx )
{
return DynamicEntries ? ArrDyn[ idx ] : ArrStatic[ idx ];
}
@ -2698,11 +2756,6 @@ namespace gen
return num_entries();
}
bool is_invalid()
{
return Type != ECode::Invalid;
}
bool is_equal( AST* other );
s32 num_entries()
@ -2799,12 +2852,16 @@ namespace gen
);
}
String to_string();
char const* type_str()
{
return ECode::to_str( Type );
}
String to_string();
bool validate_body();
operator Code();
# pragma endregion Member Functions
constexpr static
@ -2852,106 +2909,10 @@ namespace gen
// 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(Code) == sizeof(Code_POD), "ERROR: Code is not POD" );
static_assert( sizeof(AST) == sizeof(AST_POD), "ERROR: AST IS NOT POD" );
static_assert( sizeof(AST_POD) == AST_POD_Size, "ERROR: AST POD is not size of AST_POD_Size" );
/*
AST* typedef as to not constantly have to add the '*' as this is written often..
*/
struct Code
{
# pragma region Statics
// Used to identify ASTs that should always be duplicated. (Global constant ASTs)
static Code Global;
// Used to identify invalid generated code.
static Code Invalid;
# pragma endregion Statics
# pragma region Member Functions
Code body()
{
if ( ast == nullptr )
{
log_failure("Code::body: AST is null!");
return Invalid;
}
if ( ast->Type == ECode::Invalid )
{
log_failure("Code::body: Type is invalid, cannot get");
return Invalid;
}
return { ast->body() };
}
String to_string() const
{
return ast->to_string();
}
void set_global()
{
if ( ast == nullptr )
{
log_failure("Code::set_global: Cannot set code as global, AST is null!");
return;
}
ast->Parent = Global.ast;
}
bool is_valid()
{
// Originally intended to use operator bool(), however for some reason
// The C++ standard has operator Type*() with higher precedence than operator bool().
// Even when directly casting to bool. Amazing.
return ast != nullptr && ast->Type != ECode::Invalid;
}
operator bool() const
{
return ast != nullptr && ast->Type != ECode::Invalid;
}
bool operator ==( Code other )
{
return ast == other.ast;
}
bool operator !=( Code other )
{
return ast != other.ast;
}
operator AST*()
{
return ast;
}
AST* operator->()
{
if ( ast == nullptr )
{
log_failure("Attempt to dereference a nullptr!");
return nullptr;
}
return ast;
}
# pragma endregion Member Functions
AST* ast;
};
struct Code_POD
{
AST_POD* ast;
};
static_assert( sizeof(Code) == sizeof(Code_POD), "ERROR: Code is not POD" );
// Used when the its desired when omission is allowed in a definition.
constexpr Code NoCode = { nullptr };
#pragma endregion Data Structures
@ -3051,8 +3012,11 @@ namespace gen
, Code specifiers = NoCode, Code attributes = NoCode
, ModuleFlag mflags = ModuleFlag::None );
Code def_class_body ( s32 num, ... );
Code def_class_body ( s32 num, Code* codes );
// There are two options for defining a struct body, either varadically provided with the args macro to auto-deduce the arg num,
/// or provide as an array of Code objects.
Code def_class_body ( s32 num, ... );
Code def_class_body ( s32 num, Code* codes );
Code def_enum_body ( s32 num, ... );
Code def_enum_body ( s32 num, Code* codes );
Code def_export_body ( s32 num, ... );
@ -3073,6 +3037,9 @@ namespace gen
Code def_struct_body ( s32 num, Code* codes );
Code def_union_body ( s32 num, ... );
Code def_union_body ( s32 num, Code* codes );
// Constructs an empty body. Use AST::validate_body() to check if the body is was has valid entries.
Code def_body( CodeT type );
# pragma endregion Upfront
# pragma region Parsing
@ -3098,24 +3065,8 @@ namespace gen
# pragma endregion Parsing
# pragma region Untyped text
sw token_fmt_va( char* buf, uw buf_size, s32 num_tokens, va_list va );
//! Do not use directly. Use the token_fmt macro instead.
// Takes a format string (char const*) and a list of tokens (StrC) and returns a StrC of the formatted string.
inline
StrC token_fmt_impl( sw num, ... )
{
local_persist thread_local
char buf[GEN_PRINTF_MAXLEN] = { 0 };
mem_set( buf, 0, GEN_PRINTF_MAXLEN );
va_list va;
va_start(va, num );
sw result = token_fmt_va(buf, GEN_PRINTF_MAXLEN, num, va);
va_end(va);
return { result, buf };
}
sw token_fmt_va( char* buf, uw buf_size, s32 num_tokens, va_list va );
StrC token_fmt_impl( sw, ... );
Code untyped_str ( StrC content);
Code untyped_fmt ( char const* fmt, ... );
@ -3383,6 +3334,73 @@ namespace gen
to_add->Parent = this;
}
inline
void Code::set_global()
{
if ( ast == nullptr )
{
log_failure("Code::set_global: Cannot set code as global, AST is null!");
return;
}
ast->Parent = Global.ast;
}
inline
bool Code::is_valid()
{
return ast != nullptr && ast->Type != CodeT::Invalid;
}
AST::operator gen::Code()
{
return { this };
}
Code def_body( CodeT type )
{
switch ( type )
{
using namespace ECode;
case Class_Body:
case Enum_Body:
case Export_Body:
case Extern_Linkage:
case Function_Body:
case Global_Body:
case Namespace_Body:
case Struct_Body:
case Union_Body:
break;
default:
log_failure( "def_body: Invalid type %s", (char const*)ECode::to_str(type) );
return Code::Invalid;
}
Code
result = make_code();
result->Type = type;
return result;
}
//! Do not use directly. Use the token_fmt macro instead.
// Takes a format string (char const*) and a list of tokens (StrC) and returns a StrC of the formatted string.
inline
StrC token_fmt_impl( sw num, ... )
{
local_persist thread_local
char buf[GEN_PRINTF_MAXLEN] = { 0 };
mem_set( buf, 0, GEN_PRINTF_MAXLEN );
va_list va;
va_start(va, num );
sw result = token_fmt_va(buf, GEN_PRINTF_MAXLEN, num, va);
va_end(va);
return { result, buf };
}
}
#pragma endregion Inlines