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:
Edward R. Gonzalez 2023-07-12 15:59:47 -04:00
parent 128b0e17fe
commit 4e61fefc55
13 changed files with 319 additions and 193 deletions

View File

@ -21,7 +21,7 @@ These build up a code AST to then serialize with a file builder.
## Notes
The project has reached a sort of *alpha* state, all the current functionality works however there is some kinks with the design to iterate on.
The project has reached a sort of *alpha* state, all the current functionality works for the test cases but it will most likely break in many other cases.
The project has no external dependencies beyond:
@ -224,7 +224,7 @@ u8 _Align_Pad[3];
*`CodeT` is a typedef for `ECode::Type` which has an underlying type of `u32`*
*`OperatorT` is a typedef for `EOperator::Type` which has an underlying type of `u32`*
*`StringCahced` is a typedef for `String const`, to denote it is an interned string*
*`String` is the dynamically allocated string type for the library.
*`String` is the dynamically allocated string type for the library*
AST widths are setup to be AST_POD_Size.
The width dictates how much the static array can hold before it must give way to using an allocated array:
@ -322,6 +322,8 @@ Bodies:
* def_struct_body
* def_union_body
* def_body
Usage:
```cpp
@ -343,6 +345,9 @@ def_global_body( args( ht_entry, array_ht_entry, hashtable ));
def_global_body( 3, ht_entry, array_ht_entry, hashtable );
```
If a more incremental approach is desired for the body ASTs, `Code def_body( CodeT type )` can be used to create an empty body.
When the members have been populated use: `AST::validate_body` to verify that the members are valid entires for that type.
### Parse construction
A string provided to the API is parsed for the intended language construct.

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,6 +3012,9 @@ namespace gen
, Code specifiers = NoCode, Code attributes = NoCode
, ModuleFlag mflags = ModuleFlag::None );
// 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, ... );
@ -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
@ -3099,23 +3066,7 @@ namespace gen
# 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 };
}
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

View File

@ -36,7 +36,7 @@ u32 gen_sanity()
{};
));
empty_body.body()->add_entry( def_comment( txt_StrC("Empty class body") ) );
empty_body->body()->add_entry( def_comment( txt_StrC("Empty class body") ) );
gen_sanity_file.print(fwd);
gen_sanity_file.print(empty_body);
@ -80,7 +80,7 @@ u32 gen_sanity()
};
));
c_extern.body()->add_entry( empty_comment );
c_extern->body()->add_entry( empty_comment );
gen_sanity_file.print(c_extern);
}
@ -118,7 +118,7 @@ u32 gen_sanity()
}
));
def.body()->add_entry( def_comment( txt_StrC("Empty function body") ) );
def->body()->add_entry( def_comment( txt_StrC("Empty function body") ) );
gen_sanity_file.print(fwd);
gen_sanity_file.print(def);
@ -134,7 +134,7 @@ u32 gen_sanity()
}
));
def.body()->add_entry( def_comment( txt_StrC("Empty namespace body") ) );
def->body()->add_entry( def_comment( txt_StrC("Empty namespace body") ) );
gen_sanity_file.print(def);
}
@ -182,7 +182,7 @@ u32 gen_sanity()
};
));
class_def.body()->add_entry( op_ptr );
class_def->body()->add_entry( op_ptr );
gen_sanity_file.print(class_def);
}
@ -201,7 +201,7 @@ u32 gen_sanity()
}
));
def.body()->add_entry( def_comment( txt_StrC("Empty function body") ) );
def->body()->add_entry( def_comment( txt_StrC("Empty function body") ) );
gen_sanity_file.print(fwd);
gen_sanity_file.print(def);
@ -237,7 +237,7 @@ u32 gen_sanity()
{};
));
empty_body.body()->add_entry( def_comment( txt_StrC("Empty struct body") ) );
empty_body->body()->add_entry( def_comment( txt_StrC("Empty struct body") ) );
gen_sanity_file.print(fwd);
gen_sanity_file.print(empty_body);
@ -253,7 +253,7 @@ u32 gen_sanity()
};
));
empty.body()->add_entry( def_comment( txt_StrC("Empty union body") ) );
empty->body()->add_entry( def_comment( txt_StrC("Empty union body") ) );
gen_sanity_file.print( parse_typedef( code( typedef unsigned short u16; )) );
gen_sanity_file.print( parse_typedef( code( typedef unsigned long u32; )) );

View File

@ -4,21 +4,29 @@
#include "gen.hpp"
using namespace gen;
Code gen_SOA( Code struct_def, bool use_dynamic = false )
Code gen_SOA( Code struct_def, s32 num_entries = 0 )
{
StrC name;
name.Ptr = str_fmt_buf( "SOA_%s", (char const*) struct_def->Name );
name.Len = str_len( name );
StringCached name = get_cached_string( token_fmt( "name", (StrC)struct_def->Name,
stringize( SOA_<name> )
));
Code
soa_entry = { struct_def->duplicate() };
soa_entry->Name = get_cached_string( name(Entry) );
Array<Code> vars = Array<Code>::init( Memory::GlobalAllocator );;
constexpr s32 Num_Vars_Cap = 128;
Code soa = def_struct( name, def_struct_body( 1, soa_entry ) );
local_persist Code var_memory[Num_Vars_Cap];
local_persist Arena var_arena;
do_once_start
var_arena = Arena::init_from_memory( var_memory, kilobytes(Num_Vars_Cap) );
do_once_end
Array<Code> vars = Array<Code>::init( var_arena );;
Code soa = def_struct( name, def_body( ECode::Struct_Body ));
{
Code body = struct_def.body();
Code body = *struct_def->body();
for ( s32 idx = 0; idx < body->num_entries(); idx++ )
{
Code struct_mem = { body->entry( idx ) };
@ -27,8 +35,10 @@ Code gen_SOA( Code struct_def, bool use_dynamic = false )
{
Code var_type = { struct_mem->entry(0) };
StrC num_entries_str = to_StrC( str_fmt_buf( "%d", num_entries ) );
Code entry_arr = { nullptr };
if ( use_dynamic)
if ( ! num_entries)
{
entry_arr = parse_variable( token_fmt( "type", (StrC)var_type->Name, "name", (StrC)struct_mem->Name,
stringize( Array<<type>> <name>; )
@ -36,20 +46,20 @@ Code gen_SOA( Code struct_def, bool use_dynamic = false )
}
else
{
entry_arr = parse_variable( token_fmt( "type", (StrC)var_type->Name, "name", (StrC)struct_mem->Name,
stringize( <type> <name>[100]; )
entry_arr = parse_variable( token_fmt( "type", (StrC)var_type->Name, "name", (StrC)struct_mem->Name, "num", num_entries_str,
stringize( <type> <name>[<num>]; )
));
}
vars.append( entry_arr );
soa.body()->add_entry( entry_arr );
soa->body()->add_entry( entry_arr );
}
}
}
Code make;
{
make = parse_function( token_fmt("SOA_Type", name,
make = parse_function( token_fmt("SOA_Type", (StrC)name,
stringize(
static
<SOA_Type> make( AllocatorInfo allocator )
@ -59,7 +69,7 @@ Code gen_SOA( Code struct_def, bool use_dynamic = false )
)
));
if ( use_dynamic )
if ( ! num_entries )
{
for ( s32 idx = 0; idx < vars.num(); idx++ )
{
@ -69,11 +79,11 @@ Code gen_SOA( Code struct_def, bool use_dynamic = false )
stringize( soa.<var_name> = <var_type>::init( allocator ); )
));
make.body()->add_entry( arr_init );
make->body()->add_entry( arr_init );
}
}
make.body()->add_entry( def_execution( code( return soa; ) ));
make->body()->add_entry( def_execution( code( return soa; ) ));
}
Code get;
@ -99,11 +109,14 @@ Code gen_SOA( Code struct_def, bool use_dynamic = false )
Code ret = def_execution( content );
get.body()->add_entry( ret );
get->body()->add_entry( ret );
}
soa.body()->add_entry( make );
soa.body()->add_entry( get );
soa->body()->add_entry( make );
soa->body()->add_entry( get );
soa->body()->validate_body();
vars.free();
return soa;
}
#endif

View File

@ -51,7 +51,7 @@ Code gen__buffer( StrC type, sw type_size )
Code init;
{
Code params = def_params( args(
, def_param( t_allocator_info, name(allocator))
def_param( t_allocator_info, name(allocator))
, def_param( t_sw, name(capacity))
));

View File

@ -140,7 +140,7 @@ Code gen__hashtable( StrC type )
char const* tmpl = stringize(
void (*) ( u64 key, <type> value )
);
Code value = untyped_str( token_fmt( "type", (StrC)t_type.to_string(), tmpl ) );
Code value = untyped_str( token_fmt( "type", (StrC)t_type->to_string(), tmpl ) );
using_map_proc = def_using ( name(MapProc), value);
}
@ -166,7 +166,7 @@ Code gen__hashtable( StrC type )
char const* tmpl = stringize(
void (*) ( u64 key, <type> value )
);
Code value = untyped_str( token_fmt( "type", (StrC)t_type_ptr.to_string(), tmpl ) );
Code value = untyped_str( token_fmt( "type", (StrC)t_type_ptr->to_string(), tmpl ) );
using_map_mut_proc = def_using ( name(MapMutProc), value);
}
@ -373,7 +373,7 @@ Code gen__hashtable( StrC type )
);
hashtable = def_struct( name, def_struct_body( args(
, using_entry
using_entry
, using_array_entry
, using_find_result
, using_map_proc

View File

@ -46,7 +46,7 @@ Code gen__ring( StrC type )
Code init;
{
Code params = def_params( args(
, def_param( t_allocator_info, name(allocator) )
def_param( t_allocator_info, name(allocator) )
, def_param( t_uw, name(max_size) )
));

View File

@ -3,7 +3,7 @@
using namespace gen;
u32 gen_sanity()
u32 gen_sanity_upfront()
{
Builder
gen_sanity_file;
@ -27,7 +27,8 @@ u32 gen_sanity()
Code empty_body;
{
Code cmt = def_comment( txt_StrC("Empty class body") );
Code body = def_class_body( 1, cmt );
Code body = def_class_body( args( cmt ) );
empty_body = def_class( name(TestEmptyClass), body );
}
@ -87,9 +88,7 @@ u32 gen_sanity()
// Friend
{
Code fwd = def_class( name(TestFriendFwd));
Code body = def_class_body( 1
, def_friend( fwd )
);
Code body = def_class_body( args( def_friend( fwd ) ) );
gen_sanity_file.print( def_class( name(TestFriend), body ) );
}
@ -196,7 +195,7 @@ u32 gen_sanity()
Code op_ptr = def_operator_cast( t_u8_ptr, __ );
Code op_class = def_class( name(TestOperatorCast), def_class_body( 1, op_ptr ) );
Code op_class = def_class( name(TestOperatorCast), def_class_body( args( op_ptr) ) );
gen_sanity_file.print(op_class);
}
@ -262,7 +261,7 @@ u32 gen_sanity()
Code empty_body;
{
Code cmt = def_comment( txt_StrC("Empty struct body") );
Code body = def_class_body( 1, cmt );
Code body = def_class_body( args( cmt ) );
empty_body = def_class( name(TestEmptyStruct), body );
}

View File

@ -5,7 +5,7 @@ project( 'test', 'c', 'cpp', default_options : ['buildtype=debug'] )
includes = include_directories(
[
'../../project',
'../../thirdparty'
# '../../thirdparty'
])
# get_sources = files('./get_sources.ps1')

View File

@ -6,7 +6,7 @@ includes = include_directories(
[
'./gen',
'../project',
'../thirdparty'
# '../thirdparty'
])
# get_sources = files('./get_sources.ps1')

View File

@ -1,6 +1,7 @@
#ifdef gen_time
#define GEN_DEFINE_LIBRARY_CODE_CONSTANTS
#define GEN_FEATURE_PARSING
#include "Upfront\Sanity.Upfront.hpp"
#include "Parsed\Array.Parsed.hpp"
#include "Parsed\Buffer.Parsed.hpp"
#include "Parsed\HashTable.Parsed.hpp"
@ -55,7 +56,7 @@ int gen_main()
u64 D;
};
)),
false
128
));
soa_test.write();