From ed6a1d0f954db0308125060951a5bf0700de26ee Mon Sep 17 00:00:00 2001 From: Ed_ Date: Mon, 10 Jul 2023 01:15:25 -0400 Subject: [PATCH] Templates, test changes (prob not working), progress on parsing bodies and operators. This time, really not touching for a couple of weeks. --- .vscode/gencpp.natvis | 45 +- Readme.md | 60 +- project/Banned.define.hpp | 44 +- project/Bloat.hpp | 487 +++++++++- project/gen.cpp | 1137 ++++++++++++++++-------- project/gen.hpp | 8 +- test/NonParsed/Array.NonParsed.hpp | 9 +- test/NonParsed/Buffer.NonParsed.hpp | 11 +- test/NonParsed/HashTable.NonParsed.hpp | 12 +- test/NonParsed/Ring.NonParsed.hpp | 7 +- test/Parsed/Array.Parsed.hpp | 16 +- test/Parsed/Sanity.Parsed.hpp | 1 - test/Readme.md | 7 +- test/gen/meson.build | 2 +- 14 files changed, 1343 insertions(+), 503 deletions(-) diff --git a/.vscode/gencpp.natvis b/.vscode/gencpp.natvis index 298fa42..91ff517 100644 --- a/.vscode/gencpp.natvis +++ b/.vscode/gencpp.natvis @@ -1,23 +1,12 @@ - - {ast.Name} {ast.Type} - - - - {Name} {Type} - - null {Data,na} - - {(Header*)((char*)Data - sizeof(Header))} - ((Header*)((char*)Data - sizeof(Header)))->Allocator ((Header*)((char*)Data - sizeof(Header)))->Length @@ -36,8 +25,40 @@ + + {Name} {Type} + + Type + Name + ArrStatic + + + + + {ast.Name} {ast.Type} + + + + Type:{Type} Text:{Text, [Length]s} Length:{Length} + + - Current = { Arr[Idx] } + Current[ { Arr[Idx] } ] + + + {(ArrayHeader*)((char*)Arr - sizeof(ArrayHeader))} + + ((ArrayHeader*)((char*)Arr - sizeof(ArrayHeader)))->elem_size + ((ArrayHeader*)((char*)Arr - sizeof(ArrayHeader)))->count + ((ArrayHeader*)((char*)Arr - sizeof(ArrayHeader)))->capacity + ((ArrayHeader*)((char*)Arr - sizeof(ArrayHeader)))->allocator + + + + ((ArrayHeader*)((char*)Arr - sizeof(ArrayHeader)))->count + Arr + + diff --git a/Readme.md b/Readme.md index 3dec5ea..665e193 100644 --- a/Readme.md +++ b/Readme.md @@ -5,8 +5,6 @@ An attempt at simple staged metaprogramming for c/c++. The library API is a compositon of code element constructors. These build up a code AST to then serialize with a file builder. -Intended for small-to midsized projects. - ### TOC * [Notes](#notes) @@ -156,31 +154,43 @@ If in your use case, you decide to have exclusive separation or partial separati ### *WHAT IS NOT PROVIDED* -* Macro or template generation : This library is *currently* intended to avoid those, adding support for them adds unnecessary complexity. - * There may be an argument to support basic templates for substitution, to reduce symbol redundancy for the user, since debuggers tend to do well for them. - * Any sort of template complexity however to resolve if the subtiution is valid with templates would not be supported. -* Vendor provided dynamic dispatch (virtuals) : `override` and `final` specifiers complicate the specifier serialization. (I'll problably end up adding in later) +* Lambdas +* Vendor provided dynamic dispatch (virtuals) : `override` and `final` specifiers complicate the specifier parsing and serialization. (I'll problably end up adding in later) * RTTI * Exceptions * Execution statement validation : Execution expressions are defined using the untyped API. -Keywords in from "Modern C++": +Keywords kept from "Modern C++": -* constexpr : Great to store compile-time constants, (easier to guarantee when emitted from gentime) -* consteval : Technically fine so long as templates are not used. Need to make sure to execute in moderation. +* constexpr : Great to store compile-time constants. +* consteval : Technically fine, need to make sure to execute in moderation. * constinit : Better than constexpr at doing its job, however, its only c++ 20. * export : Useful if c++ modules ever come around to actually being usable. * import : ^^ * module : ^^ -These features are not horrible when used conservatively, or are a performance benefit (modules). - When it comes to expressions: -There is no support for validating expressions. -The reason: thats where the can of worms open for parsing validation. This library would most likey more than double in size with that addition alone. -Most of the time, the critical complex metaprogramming conundrums are producing the frame of abstractions around the expressions. -Thus its not very much a priority to add such a level of complexity to the library when there isn't a high reward or need for it. +**There is no support for validating expressions.** +**The reason:** Its difficult to parse with not much of a benefit from doing so. +Most of the time, the critical complex metaprogramming conundrums are producing the frame of abstractions around the expressions (which this library provides constructors to help validate, you can skip that process by using the untyped constructors). +Its not very much a priority to add such a level of complexity to the library when there isn't a high reward or need for it. +Especially when the priority is to keep this library small and easy to grasp for what it is. + +When it comes to templates: + +Only trivial template support is provided. the intention is for only simple, non-recursive subsitution. +The parameters of the template are treated like regular parameter AST entries. +This means that the typename entry for the parameter AST would be either: + +* `class` +* `typename` +* A fundamental type, function, or pointer type. + +Anything beyond this usage is not supported by parse_template for arguments (at least not intentionally). +Use at your own mental peril... + +*Concepts and Constraints are not supported, its usage is non-tirival substiution.* ### The Data & Interface @@ -194,7 +204,7 @@ Data layout of AST struct: ```cpp union { AST* ArrStatic[AST::ArrS_Cap]; - Array(AST*) ArrDyn; + Array ArrDyn; StringCached Content; SpecifierT ArrSpecs[AST::ArrSpecs_Cap]; }; @@ -250,6 +260,12 @@ Data Notes: * They are currently using `Memory::GlobalAllocator`, which are tracked array of arenas that grows as needed (adds buckets when one runs out). * Memory within the buckets is not resused, so its inherently wasteful (most likely will give non-cached strings their own tailored alloator later) +Two generic templated containers throughout the library: + +`template< class Type> struct Array` and `template< class Type> struct HashTable >` + +Otherwise the library is free of any templates. + ## There are three sets of interfaces for Code AST generation the library provides * Upfront @@ -261,7 +277,7 @@ Data Notes: All component ASTs must be previously constructed, and provided on creation of the code AST. The construction will fail and return InvalidCode otherwise. -Interface : +Interface :`` * def_attributes * *This is preappened right before the function symbol, or placed after the class or struct keyword for any flavor of attributes used.* @@ -283,6 +299,7 @@ Interface : * def_specifier * def_specifiers * def_struct +* def_template * def_type * def_typedef * def_union @@ -327,14 +344,16 @@ Interface : * parse_export_body * parse_extern_link * parse_friend + * Purposefully are only support forward declares with this constructor. * parse_function * parse_global_body * parse_namespace -* parse_operator +* parse_operator (Not ready) * parse_struct +* parse_template (Not ready) * parse_type * parse_typedef -* parse_union +* parse_union (Not ready) * parse_using * parse_variable @@ -539,9 +558,6 @@ Currently unsupported. The following changes would have to be made: * The builder should be done on a per-thread basis. * Due to the design of the editor and scanner, it will most likely be best to make each file a job to process request entries on. Receipts should have an an array to store per thread. They can be combined to the final reciepts array when all files have been processed. -For now single-threaded has a bunch of optimization that most likely have done to it and will be more than capable -for the majority of projects this thing is intended for. (IF you use this on Unreal... well your asking for it...) - ## Extending the library This library is relatively very small, and can be extended without much hassle. diff --git a/project/Banned.define.hpp b/project/Banned.define.hpp index 62bafd5..b9e01bf 100644 --- a/project/Banned.define.hpp +++ b/project/Banned.define.hpp @@ -1,35 +1,35 @@ // Standard Allocation -#define new static_assert( false, "Banned keyword used: " new ) -#define delete static_assert( false, "Banned keyword used: " delete ) +#define new static_assert( false, "Banned keyword used: new" ) +#define delete static_assert( false, "Banned keyword used: delete" ) // Standard Coroutines -#define co_await static_assert( false, "Banned keyword used: " co_await ) -#define co_return static_assert( false, "Banned keyword used: " co_return ) -#define co_yield static_assert( false, "Banned keyword used: " co_yield ) +#define co_await static_assert( false, "Banned keyword used: co_await" ) +#define co_return static_assert( false, "Banned keyword used: co_return" ) +#define co_yield static_assert( false, "Banned keyword used: co_yield" ) // Standard Exceptions -#define atomic_cancel static_assert( false, "Banned keyword used: " atomic_cancel ) -#define atomic_commit static_assert( false, "Banned keyword used: " atomic_commit ) -#define atomic_noexcept static_assert( false, "Banned keyword used: " atomic_noexcept ) -#define catch static_assert( false, "Banned keyword used: " catch ) -#define noexcept static_assert( false, "Banned keyword used: " noexcept ) -#define throw static_assert( false, "Banned keyword used: " throw ) -#define try static_assert( false, "Banned keyword used: " try ) +#define atomic_cancel static_assert( false, "Banned keyword used: atomic_cancel" ) +#define atomic_commit static_assert( false, "Banned keyword used: atomic_commit" ) +#define atomic_noexcept static_assert( false, "Banned keyword used: atomic_noexcept" ) +#define catch static_assert( false, "Banned keyword used: catch" ) +#define noexcept static_assert( false, "Banned keyword used: noexcept" ) +#define throw static_assert( false, "Banned keyword used: throw" ) +#define try static_assert( false, "Banned keyword used: try" ) // Standard RTTI -#define decltype static_assert( false, "Banned keyword used: " decltype ) -#define reflexpr static_assert( false, "Banned keyword used: " reflexpr ) -#define typeid static_assert( false, "Banned keyword used: " typeid ) +#define decltype static_assert( false, "Banned keyword used: decltype" ) +#define reflexpr static_assert( false, "Banned keyword used: reflexpr" ) +#define typeid static_assert( false, "Banned keyword used: typeid" ) // Object-Oriented Dynamic Dispatch -#define final static_assert( false, "Banned keyword used: " final ) -#define override static_assert( false, "Banned keyword used: " override ) -#define virtual static_assert( false, "Banned keyword used: " virtual ) +#define final static_assert( false, "Banned keyword used: final" ) +#define override static_assert( false, "Banned keyword used: override" ) +#define virtual static_assert( false, "Banned keyword used: virtual" ) // Private Access Specifier -#define private static_assert( false, "Banned keyword used: " private ) +#define private static_assert( false, "Banned keyword used: private" ) // Template Meta-programming -#define concept static_assert( false, "Banned keyword used: " concept ) -#define requires static_assert( false, "Banned keyword used: " requires ) -#define template static_assert( false, "Banned keyword used: " template ) +#define concept static_assert( false, "Banned keyword used: concept" ) +#define requires static_assert( false, "Banned keyword used: requires" ) +#define template static_assert( false, "Banned keyword used: template" ) diff --git a/project/Bloat.hpp b/project/Bloat.hpp index 304aaa6..ceb4a05 100644 --- a/project/Bloat.hpp +++ b/project/Bloat.hpp @@ -25,15 +25,6 @@ # define ZPL_MODULE_CORE # define ZPL_MODULE_TIMER # define ZPL_MODULE_HASHING -// # define ZPL_MODULE_REGEX -// # define ZPL_MODULE_EVENT -// # define ZPL_MODULE_DLL -// # define ZPL_MODULE_OPTS -// # define ZPL_MODULE_PROCESS -// # define ZPL_MODULE_MAT -// # define ZPL_MODULE_THREADING -// # define ZPL_MODULE_JOBS -// # define ZPL_MODULE_PARSER #include "zpl.h" using zpl::b32; @@ -185,6 +176,450 @@ while(0); constexpr char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED"; +#pragma region Containers +#pragma push_macro("template") +#undef template + +template +struct TArray +{ + struct Header + { + AllocatorInfo Allocator; + uw Capacity; + uw Num; + }; + + static + TArray init( AllocatorInfo allocator ) + { + return init_reserve( allocator, grow_formula(0) ); + } + + static + TArray init_reserve( AllocatorInfo allocator, sw capacity ) + { + Header* header = rcast( Header*, alloc( allocator, sizeof(Header) + sizeof(Type) )); + + if ( header == nullptr ) + return { nullptr }; + + header->Allocator = allocator; + header->Capacity = capacity; + header->Num = 0; + + return { rcast( Type*, header + 1) }; + } + + static + uw grow_formula( uw value ) + { + return 2 * value * 8; + } + + bool append( Type value ) + { + Header& header = get_header(); + + if ( header.Num == header.Capacity ) + { + if ( ! grow( header.Capacity )) + return false; + } + + Data[ header.Num ] = value; + header.Num++; + + return true; + } + + Type& back( void ) + { + Header& header = get_header(); + return Data[ header.Num - 1 ]; + } + + void clear( void ) + { + Header& header = get_header(); + header.Num = 0; + } + + bool fill( uw begin, uw end, Type value ) + { + Header& header = get_header(); + + if ( begin < 0 || end >= header.Num ) + return false; + + for ( sw idx = begin; idx < end; idx++ ) + { + Data[ idx ] = value; + } + + return true; + } + + void free( void ) + { + Header& header = get_header(); + zpl::free( header.Allocator, &header ); + } + + Header& get_header( void ) + { + return *( reinterpret_cast< Header* >( Data ) - 1 ); + } + + bool grow( uw min_capacity ) + { + Header& header = get_header(); + uw new_capacity = grow_formula( header.Capacity ); + + if ( new_capacity < min_capacity ) + new_capacity = 8; + + return set_capacity( new_capacity ); + } + + uw num( void ) + { + return get_header().Num; + } + + bool pop( void ) + { + Header& header = get_header(); + + ZPL_ASSERT( header.Num > 0 ); + header.Num--; + } + + void remove_at( uw idx ) + { + Header* header = &get_header(); + ZPL_ASSERT( idx < header->Num ); + + mem_move( header + idx, header + idx + 1, sizeof( Type ) * ( header->Num - idx - 1 ) ); + header->Num--; + } + + bool reserve( uw new_capacity ) + { + Header& header = get_header(); + + if ( header.Capacity < new_capacity ) + return set_capacity( new_capacity ); + + return true; + } + + bool resize( uw num ) + { + Header& header = get_header(); + + if ( num > header.Capacity ) + { + if ( ! grow( header.Capacity ) ) + return false; + } + + header.Num = num; + return true; + } + + bool set_capacity( uw new_capacity ) + { + Header& header = get_header(); + + if ( new_capacity == header.Capacity ) + return true; + + if ( new_capacity < header.Num ) + header.Num = new_capacity; + + sw size = sizeof( Header ) + sizeof( Type ) * new_capacity; + Header* new_header = reinterpret_cast< Header* >( alloc( header.Allocator, size ) ); + + if ( new_header == nullptr ) + return false; + + mem_move( new_header, &header, sizeof( Header ) + sizeof( Type ) * header.Num ); + + new_header->Allocator = header.Allocator; + new_header->Num = header.Num; + new_header->Capacity = new_capacity; + + zpl::free( header.Allocator, &header ); + + Data = ( Type* )new_header + 1; + return true; + } + + Type* Data; + + operator Type*() + { + return Data; + } + + operator Type const*() const + { + return Data; + } +}; + +template +struct THashTable +{ + struct FindResult + { + sw HashIndex; + sw PrevIndex; + sw EntryIndex; + }; + + struct Entry + { + u64 Key; + sw Next; + Type Value; + }; + + static + THashTable init( AllocatorInfo allocator ) + { + THashTable result = {0}; + + result.Hashes.init( allocator ); + result.Entries.init( allocator ); + + return result; + } + + void clear( void ) + { + for ( sw idx = 0; idx < Hashes.num(); idx++ ) + Hashes[ idx ] = -1; + + Hashes.clear(); + Entries.clear(); + } + + void destroy( void ) + { + if ( Hashes ) + Hashes.free(); + if ( Entries ) + Entries.free(); + } + + Type* get( u64 key ) + { + sw idx = find( key ).EntryIndex; + if ( idx > 0 ) + return & Entries[ idx ].Value; + + return nullptr; + } + + using MapProc = void (*)( u64 key, Type value ); + + void map( MapProc map_proc ) + { + ZPL_ASSERT_NOT_NULL( map_proc ); + + for ( sw idx = 0; idx < Entries.num(); idx++ ) + { + map_proc( Entries[ idx ].Key, Entries[ idx ].Value ); + } + } + + using MapMutProc = void (*)( u64 key, Type* value ); + + void map_mut( MapMutProc map_proc ) + { + ZPL_ASSERT_NOT_NULL( map_proc ); + + for ( sw idx = 0; idx < Entries.num(); idx++ ) + { + map_proc( Entries[ idx ].Key, & Entries[ idx ].Value ); + } + } + + void grow() + { + sw new_num = TArray::grow_formula( Entries.num() ) + rehash( new_num ); + } + + void rehash( sw new_num ) + { + sw idx; + sw last_added_index; + + THashTable new_ht = init( Hashes.get_header().Allocator ); + + new_ht.Hashes.resize( new_num ); + new_ht.Entries.reserve( new_ht.Hashes.num() ); + + for ( idx = 0; idx < new_ht.Hashes.num(); ++idx ) + new_ht.Hashes[ idx ] = -1; + + for ( idx = 0; idx < Entries.num(); ++idx ) + { + Entry& entry = Entries[ idx ]; + + FindResult find_result; + + if ( new_ht.Hashes.num() == 0 ) + new_ht.grow(); + + entry = Entries[ idx ]; + find_result = new_ht.find( entry.Key ); + last_added_index = new_ht.add_entry( entry.Key ); + + if ( find_result.PrevIndex < 0 ) + new_ht.Hashes[ find_result.HashIndex ] = last_added_index; + + else + new_ht.Entries[ find_result.PrevIndex ].Next = last_added_index; + + new_ht.Entries[ last_added_index ].Next = find_result.EntryIndex; + new_ht.Entries[ last_added_index ].Value = entry.Value; + } + + // *this = new_ht; + + // old_ht.destroy(); + + destroy(); + Hashes = new_ht.Hashes; + Entries = new_ht.Entries; + } + + void rehash_fast() + { + sw idx; + + for ( idx = 0; idx < Entries.num(); idx++ ) + Entries[ idx ].Next = -1; + + for ( idx = 0; idx < Hashes.num(); idx++ ) + Hashes[ idx ] = -1; + + for ( idx = 0; idx < Entries.num(); idx++ ) + { + Entry* entry; + + FindResult find_result; + } + } + + void remove( u64 key ) + { + FindResult find_result = find( key); + + if ( find_result.EntryIndex >= 0 ) + { + Entries.remove_at( find_result.EntryIndex ); + rehash_fast(); + } + } + + void remove_entry( sw idx ) + { + Entries.remove_at( idx ); + } + + void set( u64 key, Type value ) + { + sw idx; + FindResult find_result; + + if ( Hashes.num() == 0 ) + grow(); + + find_result = find( key ); + + if ( find_result.EntryIndex >= 0 ) + { + idx = find_result.EntryIndex; + } + else + { + idx = add_entry( key ); + + if ( find_result.PrevIndex >= 0 ) + { + Entries[ find_result.PrevIndex ].Next = idx; + } + else + { + Hashes[ find_result.HashIndex ] = idx; + } + } + + Entries[ idx ].Value = value; + + if ( full() ) + grow(); + } + + sw slot( u64 key ) + { + for ( sw idx = 0; idx < Hashes.num(); ++idx ) + if ( Hashes[ idx ] == key ) + return idx; + + return -1; + } + + TArray< sw> Hashes; + TArray< Entry> Entries; + +protected: + + sw add_entry( u64 key ) + { + sw idx; + Entry entry = { key, -1 }; + + idx = Entries.num(); + Entries.append( entry ); + return idx; + } + + FindResult find( u64 key ) + { + FindResult result = { -1, -1, -1 }; + + if ( Hashes.num() > 0 ) + { + result.HashIndex = key % Hashes.num(); + result.EntryIndex = Hashes[ result.HashIndex ]; + + while ( result.EntryIndex >= 0 ) + { + if ( Entries[ result.EntryIndex ].Key == key ) + break; + + result.PrevIndex = result.EntryIndex; + result.EntryIndex = Entries[ result.EntryIndex ].Next; + } + } + + return result; + } + + b32 full() + { + return 0.75f * Hashes.num() < Entries.num(); + } +}; + +#pragma pop_macro("template") +#pragma endregion Containers #pragma region Memory #pragma endregion Memory @@ -221,18 +656,21 @@ char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED"; sw Capacity; }; - static String make( AllocatorInfo allocator, char const* str ) + static + String make( AllocatorInfo allocator, char const* str ) { sw length = str ? str_len( str ) : 0; return make_length( allocator, str, length ); } - static String make( AllocatorInfo allocator, StrC str ) + static + String make( AllocatorInfo allocator, StrC str ) { return make_length( allocator, str.Ptr, str.Len ); } - static String make_reserve( AllocatorInfo allocator, sw capacity ) + static + String make_reserve( AllocatorInfo allocator, sw capacity ) { constexpr sw header_size = sizeof( Header ); @@ -254,7 +692,8 @@ char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED"; return result; } - static String make_length( AllocatorInfo allocator, char const* str, sw length ) + static + String make_length( AllocatorInfo allocator, char const* str, sw length ) { constexpr sw header_size = sizeof( Header ); @@ -281,7 +720,8 @@ char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED"; return result; } - static String fmt( AllocatorInfo allocator, char* buf, sw buf_size, char const* fmt, ... ) + static + String fmt( AllocatorInfo allocator, char* buf, sw buf_size, char const* fmt, ... ) { va_list va; va_start( va, fmt ); @@ -291,7 +731,8 @@ char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED"; return make( allocator, buf ); } - static String fmt_buf( AllocatorInfo allocator, char const* fmt, ... ) + static + String fmt_buf( AllocatorInfo allocator, char const* fmt, ... ) { local_persist thread_local char buf[ ZPL_PRINTF_MAXLEN ] = { 0 }; @@ -304,7 +745,8 @@ char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED"; return make( allocator, buf ); } - static String join( AllocatorInfo allocator, char const** parts, sw num_parts, char const* glue ) + static + String join( AllocatorInfo allocator, char const** parts, sw num_parts, char const* glue ) { String result = make( allocator, "" ); @@ -319,7 +761,8 @@ char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED"; return result; } - static bool are_equal( String lhs, String rhs ) + static + bool are_equal( String lhs, String rhs ) { if ( lhs.length() != rhs.length() ) return false; @@ -520,6 +963,8 @@ char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED"; }; } + // Used with cached strings + // Essentially makes the string a string view. String const& operator = ( String const& other ) const { if ( this == & other ) @@ -532,12 +977,6 @@ char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED"; return this_; } - String& operator += ( String const& other ) - { - append( other ); - return *this; - } - char& operator [] ( sw index ) { return Data[ index ]; @@ -548,7 +987,6 @@ char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED"; return Data[ index ]; } - char* Data = nullptr; }; @@ -581,6 +1019,7 @@ namespace Memory void cleanup(); } + inline sw log_fmt(char const* fmt, ...) { diff --git a/project/gen.cpp b/project/gen.cpp index 88e45c0..d032417 100644 --- a/project/gen.cpp +++ b/project/gen.cpp @@ -32,6 +32,8 @@ namespace gen global Code t_bool; global Code t_char; global Code t_wchar_t; + global Code t_class; + global Code t_typename; #ifdef GEN_DEFINE_LIBRARY_CODE_CONSTANTS global Code t_b32; @@ -216,6 +218,7 @@ namespace gen case Struct: case Struct_Fwd: case Struct_Body: + case Template: case Typedef: case Typename: case Union: @@ -824,6 +827,14 @@ namespace gen } break; + case Template: + { + ProcessModuleFlags(); + + result.append_fmt( "template< %s >\n%s", entry( 1 )->to_string(), body()->to_string() ); + } + break; + case Typedef: { ProcessModuleFlags(); @@ -889,10 +900,20 @@ namespace gen idx++; } - result.append_fmt( "%s\n{\n%s\n};" - , Name - , body()->to_string() - ); + if ( Name ) + { + result.append_fmt( "%s\n{\n%s\n};" + , Name + , body()->to_string() + ); + } + else + { + // Anonymous union + result.append_fmt( "\n{\n%s\n};" + , body()->to_string() + ); + } } break; @@ -1051,6 +1072,8 @@ namespace gen def_constant_code_type( bool ); def_constant_code_type( char ); def_constant_code_type( wchar_t ); + def_constant_code_type( class ); + def_constant_code_type( typename ); #ifdef GEN_DEFINE_LIBRARY_CODE_CONSTANTS t_b32 = def_type( name(b32) ); @@ -1937,11 +1960,14 @@ namespace gen case Function_Fwd: case Operator_Fwd: case Struct_Fwd: + case Class: + case Function: + case Operator: + case Struct: break; default: - // Technically friends can have a function body. I will not support it so if you want that add it yourself. - Ed. - log_failure("gen::def_friend: declaration cannot be used with friend, must be a forward declare - %s", declaration->debug_str()); + log_failure("gen::def_friend: requires declartion to have class, function, operator, or struct - %s", declaration->debug_str()); return Code::Invalid; } @@ -2264,6 +2290,40 @@ namespace gen return result; } + Code def_template( Code params, Code definition, ModuleFlag mflags ) + { + null_check( def_template, definition ); + + if ( params && params->Type != ECode::Parameters ) + { + log_failure( "gen::def_template: params is not of parameters type - %s", params->debug_str() ); + return Code::Invalid; + } + + switch (definition->Type ) + { + case ECode::Class: + case ECode::Function: + case ECode::Struct: + case ECode::Variable: + case ECode::Using: + break; + + default: + log_failure( "gen::def_template: definition is not of class, function, struct, variable, or using type - %s", definition->debug_str() ); + } + + Code + result = make_code(); + result->Type = ECode::Template; + result->ModuleFlags = mflags; + + result->add_entry( definition ); + result->add_entry( params ); + + return result; + } + Code def_type( StrC name, Code arrayexpr, Code specifiers, Code attributes ) { name_check( def_type, name ); @@ -2345,7 +2405,6 @@ namespace gen Code def_union( StrC name, Code body, Code attributes, ModuleFlag mflags ) { - name_check( def_union, name ); null_check( def_union, body ); if ( body->Type != ECode::Union_Body ) @@ -2362,10 +2421,12 @@ namespace gen Code result = make_code(); - result->Name = get_cached_string( name ); result->ModuleFlags = mflags; result->Type = ECode::Union; + if ( name.Ptr ) + result->Name = get_cached_string( name ); + result->add_entry( body ); if ( attributes ) @@ -3080,9 +3141,6 @@ namespace gen It will not be capable of lexing C++ code with unsupported features. */ - // Angle brackets not supported as they are used for template arguments outside of expressions - // Any angle brackets found will be considered an operator token. - # define Define_TokType \ Entry( Access_Private, "private" ) \ Entry( Access_Protected, "protected" ) \ @@ -3134,6 +3192,7 @@ namespace gen Entry( Star, "*" ) \ Entry( Statement_End, ";" ) \ Entry( String, "__String__" ) \ + Entry( Template, "template" ) \ Entry( Type_Unsigned, "unsigned" ) \ Entry( Type_Signed, "signed" ) \ Entry( Type_Short, "short" ) \ @@ -3796,6 +3855,7 @@ namespace gen internal Code parse_namespace ( Parser::TokArray& toks, char const* context ); internal Code parse_struct ( Parser::TokArray& toks, char const* context ); internal Code parse_variable ( Parser::TokArray& toks, char const* context ); + internal Code parse_template ( Parser::TokArray& toks, char const* context ); internal Code parse_type ( Parser::TokArray& toks, char const* context ); internal Code parse_typedef ( Parser::TokArray& toks, char const* context ); internal Code parse_union ( Parser::TokArray& toks, char const* context ); @@ -3895,12 +3955,51 @@ namespace gen return { nullptr }; } + Code type = { nullptr }; + Code value = { nullptr }; + + type = parse_type( toks, context ); + if ( type == Code::Invalid ) + return Code::Invalid; + + Token name = currtok; + eat( TokType::Identifier ); + + if ( currtok.IsAssign ) + { + eat( TokType::Operator ); + + Token value_tok = currtok; + + if ( currtok.Type == TokType::Statement_End ) + { + log_failure( "gen::%s: Expected value after assignment operator", context ); + return Code::Invalid; + } + + while ( left && currtok.Type != TokType::Statement_End ) + { + value_tok.Length = ( (sptr)currtok.Text + currtok.Length ) - (sptr)value_tok.Text; + eat( currtok.Type ); + } + + value = parse_type( toks, context ); + } + Code - result = make_code(); + result = make_code(); result->Type = Parameters; + result->Name = get_cached_string( name ); + + result->add_entry( type ); + + if ( value ) + result->add_entry( value ); while ( left && currtok.Type != TokType::Capture_End) { + eat( TokType::Comma ); + Code type = { nullptr }; Code value = { nullptr }; @@ -3933,7 +4032,7 @@ namespace gen } Code - param = make_code(); + param = make_code(); param->Type = Parameters; param->Name = get_cached_string( name ); @@ -3943,17 +4042,218 @@ namespace gen param->add_entry( value ); result->add_entry( param ); - - if ( check( TokType::Comma ) ) - eat( TokType::Comma ); } + eat( TokType::Capture_End ); return result; # undef context } - internal + // Function parsing is handled in multiple places because its initial signature is shared with variable parsing + internal inline + Code parse_function_after_name( + ModuleFlag mflags + , Code attributes + , Code specifiers + , Code ret_type + , StrC name + , Parser::TokArray& toks + , char const* context + ) + { + using namespace Parser; + + Code params = parse_params( toks, txt(parse_function) ); + + Code body = { nullptr }; + if ( check( TokType::BraceCurly_Open ) ) + { + body = parse_function_body( toks, txt(parse_function) ); + if ( body == Code::Invalid ) + return Code::Invalid; + } + else + { + eat( TokType::Statement_End ); + } + + using namespace ECode; + + Code + result = make_code(); + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + + if ( body ) + { + switch ( body->Type ) + { + case Function_Body: + case Untyped: + break; + + default: + { + log_failure("gen::def_function: body must be either of Function_Body or Untyped type. %s", body->debug_str()); + return Code::Invalid; + } + } + + result->Type = Function; + result->add_entry( body ); + } + else + { + result->Type = Function_Fwd; + } + + if ( specifiers ) + result->add_entry( specifiers ); + + result->add_entry( ret_type ); + + if ( params ) + result->add_entry( params ); + + return result; + } + + internal inline + Code parse_operator_after_ret_type( ModuleFlag mflags, Code attributes, Code specifiers, Code ret_type, Parser::TokArray& toks, char const* context ) + { + using namespace Parser; + + // Parse Operator + eat( TokType::Decl_Operator ); + + if ( ! check( TokType::Operator ) ) + { + log_failure( "gen::%s: Expected operator after 'operator' keyword", context ); + return Code::Invalid; + } + + OperatorT op = OperatorT::Invalid; + switch ( currtok.Text[0] ) + { + case '+': + case '-': + case '*': + case '/': + case '%': + case '&': + case '|': + case '^': + case '~': + case '!': + case '=': + case '<': + case '>': + case '.': + case ',': + case ':': + case ';': + case '?': + case '@': + case '#': + case '$': + case '`': + case '\'': + case '"': + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + { + log_failure( "gen::%s: Invalid operator '%c'", context, currtok.Text[0] ); + return Code::Invalid; + } + + default: + { + break; + } + } + + // Parse Params + Code params = parse_params( toks, txt(parse_operator) ); + + // Parse Body + Code body = { nullptr }; + + // OpValidateResult check_result = operator__validate( op, params, ret_type, specifiers ); + Code result = def_operator( op, params, ret_type, body, specifiers, attributes, mflags ); + return result; + } + + // Variable parsing is handled in multiple places because its initial signature is shared with function parsing + internal inline + Code parse_variable_after_name( + ModuleFlag mflags + , Code attributes + , Code specifiers + , Code type + , StrC name + , Parser::TokArray& toks + , char const* context ) + { + using namespace Parser; + + Code array_expr = parse_array_decl( toks, txt(parse_variable) ); + + Code expr = Code::Invalid; + + if ( currtok.IsAssign ) + { + eat( TokType::Operator ); + + Token expr_tok = currtok; + + if ( currtok.Type == TokType::Statement_End ) + { + log_failure( "gen::parse_variable: expected expression after assignment operator" ); + return Code::Invalid; + } + + while ( left && currtok.Type != TokType::Statement_End ) + { + expr_tok.Length = ( (sptr)currtok.Text + currtok.Length ) - (sptr)expr_tok.Text; + eat( currtok.Type ); + } + + expr = untyped_str( expr_tok ); + } + + eat( TokType::Statement_End ); + + using namespace ECode; + + Code + result = make_code(); + result->Type = Variable; + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + + result->add_entry( type ); + + if (array_expr) + type->add_entry( array_expr ); + + if ( attributes ) + result->add_entry( attributes ); + + if (specifiers) + result->add_entry( specifiers ); + + if ( expr ) + result->add_entry( expr ); + + return result; + } + + internal inline Code parse_variable_assignment( Parser::TokArray& toks, char const* context ) { using namespace Parser; @@ -3984,6 +4284,50 @@ namespace gen return expr; } + internal inline + Code parse_operator_function_or_variable( bool expects_function, Code attributes, Code specifiers, Parser::TokArray& toks, char const* context ) + { + using namespace Parser; + + Code result = Code::Invalid; + + Code type = parse_type( toks, txt(parse_variable) ); + + if ( type == Code::Invalid ) + return Code::Invalid; + + if ( check( TokType::Operator) ) + { + // Dealing with an operator overload + result = parse_operator_after_ret_type( ModuleFlag::None, attributes, specifiers, type, toks, txt(parse_template) ); + } + else + { + StrC name = currtok; + eat( TokType::Identifier ); + + if ( check( TokType::Operator ) && currtok.Text[0] == '(' ) + { + // Dealing with a function + + result = parse_function_after_name( ModuleFlag::None, attributes, specifiers, type, name, toks, txt(parse_template) ); + } + else + { + if ( expects_function ) + { + log_failure( "gen::parse_template: expected function declaration (consteval was used)" ); + return Code::Invalid; + } + + // Dealing with a variable + result = parse_variable_after_name( ModuleFlag::None, attributes, specifiers, type, name, toks, txt(parse_template) ); + } + } + + return result; + } + internal Code parse_class_struct( Parser::TokType which, Parser::TokArray& toks, char const* context ) { @@ -4004,11 +4348,11 @@ namespace gen Code result = Code::Invalid; - // TODO: Parse module specifiers + // TODO : Parse module specifiers eat( which ); - // TODO: Parse attributes + // TODO : Parse attributes name = parse_identifier( toks, context ); @@ -4039,14 +4383,14 @@ namespace gen if ( which == TokType::Decl_Class ) result = def_class( name, body, parent, access - // TODO: Set these up later + // TODO : Set these up later , NoCode // Attributes , ModuleFlag::None ); else result = def_struct( name, body, parent, access - // TODO: Set these up later + // TODO : Set these up later , NoCode // Attributes , ModuleFlag::None ); @@ -4069,7 +4413,10 @@ namespace gen while ( left && currtok.Type != TokType::BraceCurly_Close ) { Code member = Code::Invalid; - Code specifiers = Code::Invalid; + Code attributes = { nullptr }; + Code specifiers = { nullptr }; + + bool expects_function = false; switch ( currtok.Type ) { @@ -4109,6 +4456,10 @@ namespace gen member = parse_struct( toks, context ); break; + case TokType::Template: + member = parse_template( toks, context ); + break; + case TokType::Decl_Typedef: member = parse_typedef( toks, context ); break; @@ -4121,12 +4472,44 @@ namespace gen member = parse_using( toks, context ); break; + case TokType::BraceSquare_Open: + case TokType::Attr_Keyword: + { + // Standard attribute + if ( currtok.Type == TokType::BraceSquare_Open ) + { + eat( TokType::BraceSquare_Open ); + + if ( currtok.Type != TokType::BraceSquare_Open ) + { + log_failure( "%s: Error, expected attribute name", context ); + return result; + } + + while ( left && currtok.Type != TokType::BraceSquare_Close ) + { + // TODO : Parse attributes + } + + eat( TokType::BraceSquare_Close ); + eat( TokType::BraceSquare_Close ); + } + + // Platform Specific attribute + eat( TokType::Attr_Keyword ); + eat( TokType::Capture_Start ); + + // TODO : Parse attributes + + eat( TokType::Capture_End ); + } + //! Fallthrough intended case TokType::Spec_Consteval: case TokType::Spec_Constexpr: case TokType::Spec_Constinit: case TokType::Spec_Inline: + case TokType::Spec_Mutable: case TokType::Spec_Static: - case TokType::Spec_ThreadLocal: case TokType::Spec_Volatile: { SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; @@ -4140,19 +4523,24 @@ namespace gen { case ESpecifier::Constexpr: case ESpecifier::Constinit: - case ESpecifier::External_Linkage: - case ESpecifier::Local_Persist: + case ESpecifier::Inline: case ESpecifier::Mutable: case ESpecifier::Static_Member: - case ESpecifier::Thread_Local: case ESpecifier::Volatile: break; + case ESpecifier::Consteval: + expects_function = true; + break; + default: - log_failure( "gen::parse_variable: invalid specifier " txt(spec) " for variable" ); + log_failure( "gen::parse_class_struct_body: invalid specifier " txt(spec) " for variable" ); return Code::Invalid; } + if ( spec == ESpecifier::Const ) + continue; + specs_found[num_specifiers] = spec; num_specifiers++; eat( currtok.Type ); @@ -4163,6 +4551,7 @@ namespace gen specifiers = def_specifiers( num_specifiers, specs_found ); } } + //! Fallthrough intentional case TokType::Identifier: case TokType::Spec_Const: case TokType::Type_Unsigned: @@ -4170,89 +4559,20 @@ namespace gen case TokType::Type_Short: case TokType::Type_Long: { - Code type = parse_type( toks, context ); - if ( type == Code::Invalid ) - { - log_failure( "gen::parse_variable: failed to parse type" ); - return Code::Invalid; - } - - Token name = currtok; - eat( TokType::Identifier ); - - // Parsing a member function - if ( check( TokType::Capture_Start )) - { - Code params = parse_params( toks, context ); - if ( params == Code::Invalid ) - { - log_failure( "gen::parse_variable: failed to parse function parameters" ); - return Code::Invalid; - } - - if ( check( TokType::BraceCurly_Open ) ) - { - Code body = parse_function_body( toks, context); - if ( body == Code::Invalid ) - { - log_failure( "gen::parse_variable: failed to parse function body" ); - return Code::Invalid; - } - - member = make_code(); - member->Name = get_cached_string( name ); - - if ( body ) - { - switch ( body->Type ) - { - case Function_Body: - case Untyped: - break; - - default: - { - log_failure("gen::def_function: body must be either of Function_Body or Untyped type. %s", body->debug_str()); - return Code::Invalid; - } - } - - member->Type = Function; - member->add_entry( body ); - } - else - { - member->Type = Function_Fwd; - } - - member->add_entry( type ); - - if ( params ) - member->add_entry( params ); - - break; - } - } - - // Parsing a member variable - Code array_expr = parse_array_decl( toks, context ); - Code expr = parse_variable_assignment( toks, context ); - - member = make_code(); - member->Type = Variable; - member->Name = get_cached_string( name ); - - member->add_entry( type ); - - if (array_expr) - type->add_entry( array_expr ); - - if (specifiers) - member->add_entry( specifiers ); - - if ( expr ) - member->add_entry( expr ); + member = parse_operator_function_or_variable( expects_function, attributes, specifiers, toks, context ); } + + default: + Token untyped_tok = currtok; + + while ( left && currtok.Type != TokType::BraceCurly_Close ) + { + untyped_tok.Length = ( (sptr)currtok.Text + currtok.Length ) - (sptr)untyped_tok.Text; + eat( currtok.Type ); + } + + member = untyped_str( untyped_tok ); + break; } if ( member == Code::Invalid ) @@ -4282,7 +4602,11 @@ namespace gen while ( left && currtok.Type != TokType::BraceCurly_Close ) { - Code member = Code::Invalid; + Code member = Code::Invalid; + Code attributes = { nullptr }; + Code specifiers = { nullptr }; + + bool expects_function = false; switch ( currtok.Type ) { @@ -4315,17 +4639,55 @@ namespace gen member = parse_using( toks, context ); break; - case TokType::Identifier: - break; - - case TokType::Spec_Const: case TokType::Spec_Constexpr: case TokType::Spec_Constinit: + case TokType::Spec_Mutable: case TokType::Spec_Static: case TokType::Spec_ThreadLocal: case TokType::Spec_Volatile: - // Can be all kinds of crap + { + SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; + s32 num_specifiers = 0; + while ( left && tok_is_specifier( currtok ) ) + { + SpecifierT spec = ESpecifier::to_type( currtok ); + + switch ( spec ) + { + case ESpecifier::Constexpr: + case ESpecifier::Constinit: + case ESpecifier::Mutable: + case ESpecifier::Static_Member: + case ESpecifier::Thread_Local: + case ESpecifier::Volatile: + break; + + case ESpecifier::Consteval: + expects_function = true; + break; + + default: + log_failure( "gen::parse_class_struct_body: invalid specifier " txt(spec) " for variable" ); + return Code::Invalid; + } + + if ( spec == ESpecifier::Const ) + continue; + + specs_found[num_specifiers] = spec; + num_specifiers++; + eat( currtok.Type ); + } + + if ( num_specifiers ) + { + specifiers = def_specifiers( num_specifiers, specs_found ); + } + } + //! Fallthrough intentional + case TokType::Identifier: + case TokType::Spec_Const: case TokType::Type_Unsigned: case TokType::Type_Signed: case TokType::Type_Short: @@ -4376,7 +4738,10 @@ namespace gen while ( left && currtok.Type != TokType::BraceCurly_Close ) { Code member = Code::Invalid; - Code specifiers = Code::Invalid; + Code attributes = { nullptr }; + Code specifiers = { nullptr }; + + bool expects_function = false; switch ( currtok.Type ) { @@ -4393,13 +4758,6 @@ namespace gen member = parse_class( toks, context ); break; - case TokType::Module_Export: - if ( which == Export_Body ) - log_failure( "gen::parse_extern_link_body: nested export declaration" ); - - member = parse_export_body( toks, context ); - break; - case TokType::Decl_Extern_Linkage: if ( which == Extern_Linkage_Body ) log_failure( "gen::parse_extern_link_body: nested extern linkage" ); @@ -4415,6 +4773,10 @@ namespace gen member = parse_struct( toks, context ); break; + case TokType::Template: + member = parse_template( toks, context ); + break; + case TokType::Decl_Typedef: member = parse_typedef( toks, context ); break; @@ -4427,18 +4789,63 @@ namespace gen member = parse_using( toks, context ); break; - // TODO: Module support. - // case TokType::Module_Export: - // case TokType::Module_Import: + case TokType::Module_Export: + if ( which == Export_Body ) + log_failure( "gen::parse_extern_link_body: nested export declaration" ); + member = parse_export_body( toks, context ); + break; + + case TokType::Module_Import: + { + not_implemented(); + } + //! Fallthrough intentional + case TokType::BraceSquare_Open: + case TokType::Attr_Keyword: + { + not_implemented(); + + // Standard attribute + if ( currtok.Type == TokType::BraceSquare_Open ) + { + eat( TokType::BraceSquare_Open ); + + if ( currtok.Type != TokType::BraceSquare_Open ) + { + log_failure( "%s: Error, expected attribute name", context ); + return result; + } + + while ( left && currtok.Type != TokType::BraceSquare_Close ) + { + // TODO : Parse attributes + } + + eat( TokType::BraceSquare_Close ); + eat( TokType::BraceSquare_Close ); + } + + // Platform Specific attribute + eat( TokType::Attr_Keyword ); + eat( TokType::Capture_Start ); + + // TODO : Parse attributes + + eat( TokType::Capture_End ); + } + //! Fallthrough intentional + case TokType::Spec_Consteval: + case TokType::Spec_Constexpr: + case TokType::Spec_Constinit: case TokType::Spec_Extern: case TokType::Spec_Global: case TokType::Spec_Inline: case TokType::Spec_Internal_Linkage: case TokType::Spec_Static: { - SpecifierT specs_found[16] { ESpecifier::Invalid }; - s32 num_specs = 0; + SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; + s32 num_specifiers = 0; while ( left && tok_is_specifier( currtok ) ) { @@ -4446,28 +4853,37 @@ namespace gen switch ( spec ) { - case ESpecifier::Const: - case ESpecifier::External_Linkage: - case ESpecifier::Global: + case ESpecifier::Constexpr: + case ESpecifier::Constinit: case ESpecifier::Inline: - case ESpecifier::Internal_Linkage: + case ESpecifier::Mutable: + case ESpecifier::Static_Member: + case ESpecifier::Volatile: + break; + + case ESpecifier::Consteval: + expects_function = true; break; default: - log_failure( "gen::%s: invalid specifier " txt(spec) " for a global scope", context ); + log_failure( "gen::parse_class_struct_body: invalid specifier " txt(spec) " for variable" ); return Code::Invalid; } - specs_found[num_specs] = spec; - num_specs++; + if ( spec == ESpecifier::Const ) + continue; + + specs_found[num_specifiers] = spec; + num_specifiers++; eat( currtok.Type ); } - if ( num_specs ) + if ( num_specifiers ) { - specifiers = def_specifiers( num_specs, specs_found ); + specifiers = def_specifiers( num_specifiers, specs_found ); } } + //! Fallthrough intentional case TokType::Identifier: case TokType::Spec_Const: case TokType::Type_Long: @@ -4475,100 +4891,7 @@ namespace gen case TokType::Type_Signed: case TokType::Type_Unsigned: { - Code type = parse_type( toks, context ); - if ( type == Code::Invalid ) - { - log_failure( "gen::%s: failed to parse type", context ); - return Code::Invalid; - } - if ( check( TokType::Decl_Operator ) ) - { - // Already parsed the return type... - - eat( TokType::Decl_Operator ); - - // Parse op type - - // Parse parameters - - // Parse possible body - } - - Token name = currtok; - eat( TokType::Identifier ); - - // Parsing a function - if ( check( TokType::Capture_Start )) - { - Code params = parse_params( toks, context ); - if ( params == Code::Invalid ) - { - log_failure( "gen::%s: failed to parse function params", context ); - return Code::Invalid; - } - - if ( check( TokType::BraceCurly_Open )) - { - Code body = parse_function_body( toks, context ); - if ( body == Code::Invalid ) - { - log_failure( "gen::%s: failed to parse function body", context ); - return Code::Invalid; - } - - member = make_code(); - member->Name = get_cached_string( name ); - - if ( body ) - { - switch ( body->Type ) - { - case Function_Body: - case Untyped: - break; - - default: - { - log_failure( "gen::%s: invalid function body", context ); - return Code::Invalid; - } - } - - member->Type = Function; - member->add_entry( body ); - } - else - { - member->Type = Function_Fwd; - } - - member->add_entry( type ); - - if ( params ) - member->add_entry( params ); - - break; - } - } - - // Parsing a variable - Code array_expr = parse_array_decl( toks, context ); - Code expr = parse_variable_assignment( toks, context ); - - member = make_code(); - member->Type = Variable; - member->Name = get_cached_string( name ); - - member->add_entry( type ); - - if ( array_expr ) - type->add_entry( array_expr ); - - if ( specifiers ) - member->add_entry( specifiers ); - - if ( expr ) - member->add_entry( expr ); + member = parse_operator_function_or_variable( expects_function, attributes, specifiers, toks, context ); } } @@ -4630,7 +4953,7 @@ namespace gen is_enum_class = true; } - // TODO: Parse attributes + // TODO : Parse attributes if ( currtok.Type != TokType::Identifier ) { @@ -4679,13 +5002,10 @@ namespace gen body.Length = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)body.Text; eat( TokType::BraceCurly_Close ); - eat( TokType::Statement_End ); - } - else - { - eat( TokType::Statement_End ); } + eat( TokType::Statement_End ); + using namespace ECode; Code @@ -4725,7 +5045,7 @@ namespace gen return parse_enum( toks, txt(parse_enum) ); } - internal + internal inline Code parse_export_body( Parser::TokArray& toks, char const* context ) { return parse_global_nspace( ECode::Export_Body, toks, context ); @@ -4743,7 +5063,7 @@ namespace gen return parse_export_body( toks, txt(parse_export_body) ); } - internal + internal inline Code parse_extern_link_body( Parser::TokArray& toks, char const* context ) { return parse_global_nspace( ECode::Extern_Linkage_Body, toks, context ); @@ -4864,9 +5184,9 @@ namespace gen Code array_expr = { nullptr }; Code specifiers = { nullptr }; - // TODO: Parse module specifiers + // TODO : Parse module specifiers - // TODO: Parse attributes + // TODO : Parse attributes while ( left && tok_is_specifier( currtok ) ) { @@ -4921,56 +5241,7 @@ namespace gen if ( ! name ) return Code::Invalid; - Code params = parse_params( toks, txt(parse_function) ); - - Code body = { nullptr }; - if ( check( TokType::BraceCurly_Open ) ) - { - body = parse_function_body( toks, txt(parse_function) ); - if ( body == Code::Invalid ) - return Code::Invalid; - } - else - { - eat( TokType::Statement_End ); - } - - using namespace ECode; - - Code - result = make_code(); - result->Name = get_cached_string( name ); - - if ( body ) - { - switch ( body->Type ) - { - case Function_Body: - case Untyped: - break; - - default: - { - log_failure("gen::def_function: body must be either of Function_Body or Untyped type. %s", body->debug_str()); - return Code::Invalid; - } - } - - result->Type = Function; - result->add_entry( body ); - } - else - { - result->Type = Function_Fwd; - } - - if ( specifiers ) - result->add_entry( specifiers ); - - result->add_entry( ret_type ); - - if ( params ) - result->add_entry( params ); + Code result = parse_function_after_name( ModuleFlag::None, attributes, specifiers, ret_type, name, toks, context ); return result; } @@ -5040,18 +5311,16 @@ namespace gen // Parse Module specifier // Parse Attributes + Code attributes = { nullptr }; // Parse Speciifers + Code specifiers = { nullptr }; // Parse Return Type + Code ret_type = parse_type( toks, txt(parse_operator) ); - // Parse Operator - - // Parse Params - - // Parse Body - - return Code::Invalid; + Code result = parse_operator_after_ret_type( ModuleFlag::None, attributes, specifiers, ret_type, toks, context ); + return result; } Code parse_operator( StrC def ) @@ -5066,7 +5335,7 @@ namespace gen return parse_operator( toks, txt(parse_operator) ); } - internal + internal inline Code parse_struct( Parser::TokArray& toks, char const* context ) { return parse_class_struct( Parser::TokType::Decl_Struct, toks, txt(parse_struct) ); @@ -5084,6 +5353,135 @@ namespace gen return parse_class_struct( TokType::Decl_Struct, toks, txt(parse_struct) ); } + internal + Code parse_template( Parser::TokArray& toks, char const* context ) + { + using namespace Parser; + + // TODO : Parse Module specifier + + eat( TokType::Template ); + + if ( ! check( TokType::Operator) || currtok.Text[0] != '<' ) + { + log_failure("gen::parse_template: expected '<' after 'template' keyword. %s", str_tok_type( currtok.Type )); + return Code::Invalid; + } + eat( TokType::Operator ); + + Code params = parse_params( toks, txt(parse_template) ); + if ( params == Code::Invalid ) + return Code::Invalid; + + if ( ! check( TokType::Operator) || currtok.Text[0] != '>' ) + { + log_failure("gen::parse_template: expected '<' after 'template' keyword. %s", str_tok_type( currtok.Type )); + return Code::Invalid; + } + eat( TokType::Operator ); + + Code definition = Code::Invalid; + + while ( left ) + { + if ( check( TokType::Decl_Class ) ) + { + definition = parse_class( toks, txt(parse_template) ); + break; + } + + if ( check( TokType::Decl_Struct ) ) + { + definition = parse_enum( toks, txt(parse_template) ); + break; + } + + if ( check( TokType::Decl_Using )) + { + definition = parse_using( toks, txt(parse_template) ); + break; + } + + // Its either a function or a variable + Token name = { nullptr, 0, TokType::Invalid }; + + + Code attributes = Code::Invalid; + Code specifiers = Code::Invalid; + + // TODO : Parse attributes + + bool expects_function = false; + + SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; + s32 num_specifiers = 0; + + while ( left && tok_is_specifier( currtok ) ) + { + SpecifierT spec = ESpecifier::to_type( currtok ); + + switch ( spec ) + { + case ESpecifier::Const: + case ESpecifier::Constexpr: + case ESpecifier::Constinit: + case ESpecifier::External_Linkage: + case ESpecifier::Global: + case ESpecifier::Inline: + case ESpecifier::Local_Persist: + case ESpecifier::Mutable: + case ESpecifier::Static_Member: + case ESpecifier::Thread_Local: + case ESpecifier::Volatile: + break; + + case ESpecifier::Consteval: + expects_function = true; + break; + + default: + log_failure( "gen::parse_template: invalid specifier " txt(spec) " for variable or function" ); + return Code::Invalid; + } + + // Ignore const it will be handled by the type + if ( spec == ESpecifier::Const ) + continue; + + specs_found[num_specifiers] = spec; + num_specifiers++; + eat( currtok.Type ); + } + + if ( num_specifiers ) + { + specifiers = def_specifiers( num_specifiers, specs_found ); + } + + definition = parse_operator_function_or_variable( expects_function, attributes, specifiers, toks, txt(parse_template) ); + } + + Code + result = make_code(); + result->Type = ECode::Template; + result->add_entry( definition ); + result->add_entry( params ); + + return Code::Invalid; + } + + Code parse_template( StrC def ) + { + check_parse_args( parse_template, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return Code::Invalid; + + return parse_template( toks, txt(parse_template) ); + } + internal Code parse_type( Parser::TokArray& toks, char const* context ) { @@ -5183,7 +5581,7 @@ namespace gen eat( TokType::Capture_Start ); - // TODO: Change this to validate the parameters... + // TODO : Change this to validate the parameters... // Bruteforce lex the parameters, no validation. while ( ! check( TokType::Capture_End )) { @@ -5298,9 +5696,57 @@ namespace gen { using namespace Parser; - not_implemented(); + // TODO : Parse module spec - return Code::Invalid; + eat( TokType::Decl_Union ); + + // Parse attributes + Code attributes = { nullptr }; + + StrC name = { 0, nullptr }; + + if ( check( TokType::Identifier ) ) + { + name = currtok; + eat( TokType::Identifier ); + } + + Code body = { nullptr }; + + if ( check( TokType::BraceCurly_Open ) ) + { + eat( TokType::BraceCurly_Open ); + + body = make_code(); + body->Type = ECode::Union_Body; + + while ( ! check( TokType::BraceCurly_Close ) ) + { + Code entry = parse_variable( toks, txt(parse_union) ); + + if ( entry ) + body->add_entry( entry ); + } + + eat( TokType::BraceCurly_Close ); + } + + eat( TokType::Statement_End ); + + Code + result = make_code(); + result->Type = ECode::Union; + + if ( name ) + result->Name = get_cached_string( name ); + + if ( body ) + result->add_entry( body ); + + if ( attributes ) + result->add_entry( attributes ); + + return result; } Code parse_union( StrC def ) @@ -5329,7 +5775,7 @@ namespace gen bool is_namespace = false; - // TODO: Parse module specs + // TODO : Parse module specs eat( TokType::Decl_Using ); @@ -5344,7 +5790,7 @@ namespace gen if ( currtok.IsAssign ) { - // TODO: Parse Attributes (using type-alias) + // TODO : Parse Attributes (using type-alias) eat( TokType::Operator ); @@ -5392,14 +5838,12 @@ namespace gen SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; s32 num_specifiers = 0; - Code lang_linkage = Code::Invalid; - Code attributes = Code::Invalid; - Code array_expr = Code::Invalid; - Code specifiers = Code::Invalid; + Code attributes = Code::Invalid; + Code specifiers = Code::Invalid; - // TODO: Parse module specifiers + // TODO : Parse module specifiers - // TODO: Parse attributes + // TODO : Parse attributes while ( left && tok_is_specifier( currtok ) ) { @@ -5407,9 +5851,12 @@ namespace gen switch ( spec ) { + case ESpecifier::Const: case ESpecifier::Constexpr: case ESpecifier::Constinit: case ESpecifier::External_Linkage: + case ESpecifier::Global: + case ESpecifier::Inline: case ESpecifier::Local_Persist: case ESpecifier::Mutable: case ESpecifier::Static_Member: @@ -5422,20 +5869,9 @@ namespace gen return Code::Invalid; } - if ( spec == ESpecifier::External_Linkage ) - { - specs_found[num_specifiers] = spec; - num_specifiers++; - eat( TokType::Spec_Extern ); - - if ( currtok.Type == TokType::String ) - { - lang_linkage = untyped_str( currtok ); - eat( TokType::String ); - } - + // Ignore const specifiers, they're handled by the type + if ( spec == ESpecifier::Const ) continue; - } specs_found[num_specifiers] = spec; num_specifiers++; @@ -5452,61 +5888,10 @@ namespace gen if ( type == Code::Invalid ) return Code::Invalid; - if ( currtok.Type != TokType::Identifier ) - { - log_failure( "gen::parse_variable: expected identifier, recieved " txt(currtok.Type) ); - return Code::Invalid; - } - name = currtok; eat( TokType::Identifier ); - array_expr = parse_array_decl( toks, txt(parse_variable) ); - - Code expr = Code::Invalid; - - if ( currtok.IsAssign ) - { - eat( TokType::Operator ); - - Token expr_tok = currtok; - - if ( currtok.Type == TokType::Statement_End ) - { - log_failure( "gen::parse_variable: expected expression after assignment operator" ); - return Code::Invalid; - } - - while ( left && currtok.Type != TokType::Statement_End ) - { - expr_tok.Length = ( (sptr)currtok.Text + currtok.Length ) - (sptr)expr_tok.Text; - eat( currtok.Type ); - } - - expr = untyped_str( expr_tok ); - } - - eat( TokType::Statement_End ); - - using namespace ECode; - - Code result = make_code(); - result->Type = Variable; - result->Name = get_cached_string( name ); - - result->add_entry( type ); - - if (array_expr) - type->add_entry( array_expr ); - - if ( attributes ) - result->add_entry( attributes ); - - if (specifiers) - result->add_entry( specifiers ); - - if ( expr ) - result->add_entry( expr ); + Code result = parse_variable_after_name( ModuleFlag::None, attributes, specifiers, type, name, toks, txt(parse_variable) ); return result; } @@ -5548,7 +5933,7 @@ namespace gen TokMap tok_map; { - // TODO: Switch this to use an arena that makes use of the stack (cap the size of the token table to around 4096 bytes) + // TODO : Switch this to use an arena that makes use of the stack (cap the size of the token table to around 4096 bytes) tokmap_init( & tok_map, Memory::GlobalAllocator ); s32 left = num_tokens; diff --git a/project/gen.hpp b/project/gen.hpp index c9e5ab7..bc34a7c 100644 --- a/project/gen.hpp +++ b/project/gen.hpp @@ -71,6 +71,7 @@ namespace gen Entry( Struct ) \ Entry( Struct_Fwd ) \ Entry( Struct_Body ) \ + Entry( Template ) \ Entry( Typedef ) \ Entry( Typename ) \ Entry( Union ) \ @@ -771,7 +772,7 @@ namespace gen , Code specifiers = NoCode, Code attributes = NoCode , ModuleFlag mflags = ModuleFlag::None ); - Code def_param ( Code type, StrC name, Code value = NoCode ); + Code def_param ( Code type, StrC name, Code value = NoCode ); Code def_specifier( SpecifierT specifier ); Code def_struct( StrC name @@ -780,6 +781,8 @@ namespace gen , Code attributes = NoCode , ModuleFlag mflags = ModuleFlag::None ); + Code def_template( Code params, Code body, ModuleFlag mflags = ModuleFlag::None ); + Code def_type ( StrC name, Code arrayexpr = NoCode, Code specifiers = NoCode, Code attributes = NoCode ); Code def_typedef( StrC name, Code type, Code attributes = NoCode, ModuleFlag mflags = ModuleFlag::None ); @@ -831,6 +834,7 @@ namespace gen Code parse_namespace ( StrC namespace_def ); Code parse_operator ( StrC operator_def ); Code parse_struct ( StrC struct_def ); + Code parse_template ( StrC template_def ); Code parse_type ( StrC type_def ); Code parse_typedef ( StrC typedef_def ); Code parse_union ( StrC union_def ); @@ -1050,6 +1054,8 @@ namespace gen extern Code t_bool; extern Code t_char; extern Code t_wchar_t; + extern Code t_class; + extern Code t_typename; extern Code access_public; extern Code access_protected; diff --git a/test/NonParsed/Array.NonParsed.hpp b/test/NonParsed/Array.NonParsed.hpp index f92ffbe..b5d448b 100644 --- a/test/NonParsed/Array.NonParsed.hpp +++ b/test/NonParsed/Array.NonParsed.hpp @@ -114,7 +114,6 @@ Code gen__array( StrC type, sw type_size ) Header& header = get_header(); return Data[ header.Num - 1 ]; )) - , spec_inline ); Code clear = def_function( name(clear), __, t_void @@ -122,7 +121,6 @@ Code gen__array( StrC type, sw type_size ) Header& header = get_header(); header.Num = 0; )) - , spec_inline ); Code fill; @@ -155,14 +153,12 @@ Code gen__array( StrC type, sw type_size ) Header& header = get_header(); zpl::free( header.Allocator, & header ); )) - , spec_inline ); Code get_header = def_function( name(get_header), __, t_header_ref , def_execution( code( return * ( rcast( Header*, Data ) - 1 ); )) - , spec_inline ); Code grow = def_function( name(grow), def_param( t_uw, name(min_capacity)), t_bool @@ -182,7 +178,6 @@ Code gen__array( StrC type, sw type_size ) , def_execution( code( return get_header().Num; )) - , spec_inline ); Code pop = def_function( name(pop), __, t_bool @@ -192,7 +187,6 @@ Code gen__array( StrC type, sw type_size ) ZPL_ASSERT( header.Num > 0 ); header.Num--; )) - , spec_inline ); Code remove_at = def_function( name(remove_at), def_param( t_uw, name(idx)), t_void @@ -203,7 +197,6 @@ Code gen__array( StrC type, sw type_size ) mem_move( header + idx, header + idx + 1, sizeof( Type ) * ( header->Num - idx - 1 ) ); header->Num--; )) - , spec_inline ); Code reserve = def_function( name(reserve), def_param( t_uw, name(new_capacity)), t_bool @@ -316,7 +309,7 @@ Array(GenArrayRequest) GenArrayRequests; void gen__array_request( StrC type, sw size, StrC dep = {} ) { do_once_start - array_init( GenArrayRequests, g_allocator ); + array_init( GenArrayRequests, Memory::GlobalAllocator ); do_once_end // Make sure we don't already have a request for the type. diff --git a/test/NonParsed/Buffer.NonParsed.hpp b/test/NonParsed/Buffer.NonParsed.hpp index 2144608..5af1944 100644 --- a/test/NonParsed/Buffer.NonParsed.hpp +++ b/test/NonParsed/Buffer.NonParsed.hpp @@ -101,7 +101,6 @@ Code gen__buffer( StrC type, sw type_size ) Data[ header.Num ] = value; header.Num++; )) - , spec_inline ); Code appendv; @@ -121,7 +120,6 @@ Code gen__buffer( StrC type, sw type_size ) header.Num += num; )) - , spec_inline ); } @@ -130,7 +128,6 @@ Code gen__buffer( StrC type, sw type_size ) Header& header = get_header(); header.Num = 0; )) - , spec_inline ); Code end = def_function( name(end), __, t_type_ref @@ -138,7 +135,6 @@ Code gen__buffer( StrC type, sw type_size ) Header& header = get_header(); return Data[ header.Num - 1 ]; )) - , spec_inline ); Code free = def_function( name(free), __, t_void @@ -146,21 +142,18 @@ Code gen__buffer( StrC type, sw type_size ) Header& header = get_header(); zpl::free( header.Backing, & header ); )) - , spec_inline ); Code get_header = def_function( name(get_header), __, t_header_ref , def_execution( code( return * ( rcast( Header*, Data ) - 1 ); )) - , spec_inline ); Code num = def_function( name(num), __, t_sw , def_execution( code( return get_header().Num; )) - , spec_inline ); Code pop = def_function( name(pop), __, t_type @@ -169,7 +162,6 @@ Code gen__buffer( StrC type, sw type_size ) header.Num--; return Data[ header.Num ]; )) - , spec_inline ); Code wipe = def_function( name(wipe), __, t_void @@ -178,7 +170,6 @@ Code gen__buffer( StrC type, sw type_size ) header.Num = 0; mem_set( Data, 0, header.Capacity * sizeof( Type ) ); )) - , spec_inline ); Code op_type_ptr = untyped_str( code( @@ -223,7 +214,7 @@ Array(GenBufferRequest) GenBufferRequests; void gen__buffer_request( StrC type, sw size, StrC dep = {} ) { do_once_start - array_init( GenBufferRequests, g_allocator ); + array_init( GenBufferRequests, Memory::GlobalAllocator ); do_once_end // Make sure we don't already have a request for the type. diff --git a/test/NonParsed/HashTable.NonParsed.hpp b/test/NonParsed/HashTable.NonParsed.hpp index 6914cf8..4dfc79b 100644 --- a/test/NonParsed/HashTable.NonParsed.hpp +++ b/test/NonParsed/HashTable.NonParsed.hpp @@ -90,7 +90,7 @@ Code gen__hashtable( StrC type, sw type_size ) Code clear = def_function( name(clear), __, t_void , def_execution( code( - if ( s32 idx = 0; idx < Hashes.num(), idx++ ) + for ( s32 idx = 0; idx < Hashes.num(), idx++ ) Hashes[ idx ] = -1; Entries.clear(); @@ -100,11 +100,10 @@ Code gen__hashtable( StrC type, sw type_size ) Code destroy = def_function( name(destroy), __, t_void , def_execution( code( if ( Hashes ) - Hashes .free(); + Hashes.free(); if ( Entries ) Entries.free(); )) - , spec_inline ); Code get = def_function( name(get), def_param( t_u64, name(key)), t_type_ptr @@ -174,7 +173,6 @@ Code gen__hashtable( StrC type, sw type_size ) sw new_num = array_grow_formula( Entries.num() ); rehash( new_num ); )) - , spec_inline ); Code rehash; @@ -214,7 +212,6 @@ Code gen__hashtable( StrC type, sw type_size ) new_ht.Entries[ last_added_index ].Value = entry.Value; } - // * old_ht = this; // *this = new_ht; // old_ht.destroy(); @@ -320,7 +317,6 @@ Code gen__hashtable( StrC type, sw type_size ) return -1; )) - , spec_inline ); Code add_entry = def_function( name(add_entry), def_param( t_u64, name(key)), t_sw @@ -332,7 +328,6 @@ Code gen__hashtable( StrC type, sw type_size ) Entries.append( entry ); return idx; )) - , spec_inline ); Code find = def_function( name(find), def_param( t_u64, name(key)), t_find_result @@ -362,7 +357,6 @@ Code gen__hashtable( StrC type, sw type_size ) , def_execution( code( return 0.75f * Hashes.num() < Entries.num(); )) - , spec_inline ); hashtable = def_struct( name, def_struct_body( 24 @@ -411,7 +405,7 @@ Array(GenHashTableRequest) GenHashTableRequests; void gen__hashtable_request( StrC type, sw size, StrC dep = {} ) { do_once_start - array_init( GenHashTableRequests, g_allocator ); + array_init( GenHashTableRequests, Memory::GlobalAllocator ); gen_array( sw ); do_once_end diff --git a/test/NonParsed/Ring.NonParsed.hpp b/test/NonParsed/Ring.NonParsed.hpp index 683d795..e209927 100644 --- a/test/NonParsed/Ring.NonParsed.hpp +++ b/test/NonParsed/Ring.NonParsed.hpp @@ -80,7 +80,6 @@ Code gen__ring( StrC type, sw type_size ) if ( Head == Tail ) Tail = ( Tail + 1 ) % Capacity; )) - , spec_inline ); Code appendv; @@ -102,21 +101,18 @@ Code gen__ring( StrC type, sw type_size ) , def_execution( code( return Head == Tail; )) - , spec_inline ); Code free = def_function( name(free), __, t_void , def_execution( code( Buffer.free(); )) - , spec_inline ); Code full = def_function( name(full), __, t_bool , def_execution( code( return (Head + 1) % Capacity == Tail; )) - , spec_inline ); Code get = def_function( name(get), __, t_type_ref @@ -134,7 +130,6 @@ Code gen__ring( StrC type, sw type_size ) Tail = 0; Buffer.wipe(); )) - , spec_inline ); ring = def_struct( name, def_struct_body( 14, @@ -172,7 +167,7 @@ Array(GenRingRequest) GenRingRequests; void gen__ring_request( StrC type, sw size, StrC dep = {} ) { do_once_start - array_init( GenRingRequests, g_allocator ); + array_init( GenRingRequests, Memory::GlobalAllocator ); do_once_end // Make sure we don't already have a request for the type. diff --git a/test/Parsed/Array.Parsed.hpp b/test/Parsed/Array.Parsed.hpp index 995a090..3ac0584 100644 --- a/test/Parsed/Array.Parsed.hpp +++ b/test/Parsed/Array.Parsed.hpp @@ -78,13 +78,13 @@ Code gen__array( StrC type, sw type_size ) return true; } - inline Type& back( void ) + Type& back( void ) { Header& header = get_header(); return Data[ header.Num - 1 ]; } - inline void clear( void ) + void clear( void ) { Header& header = get_header(); header.Num = 0; @@ -105,13 +105,13 @@ Code gen__array( StrC type, sw type_size ) return true; } - inline void free( void ) + void free( void ) { Header& header = get_header(); zpl::free( header.Allocator, &header ); } - inline Header& get_header( void ) + Header& get_header( void ) { return *( reinterpret_cast< Header* >( Data ) - 1 ); } @@ -127,12 +127,12 @@ Code gen__array( StrC type, sw type_size ) return set_capacity( new_capacity ); } - inline uw num( void ) + uw num( void ) { return get_header().Num; } - inline bool pop( void ) + bool pop( void ) { Header& header = get_header(); @@ -140,7 +140,7 @@ Code gen__array( StrC type, sw type_size ) header.Num--; } - inline void remove_at( uw idx ) + void remove_at( uw idx ) { Header* header = &get_header(); ZPL_ASSERT( idx < header->Num ); @@ -233,7 +233,7 @@ Array(GenArrayRequest) GenArrayRequests; void gen__array_request( StrC type, sw size, StrC dep = {} ) { do_once_start - array_init( GenArrayRequests, g_allocator ); + array_init( GenArrayRequests, Memory::GlobalAllocator ); do_once_end // Make sure we don't already have a request for the type. diff --git a/test/Parsed/Sanity.Parsed.hpp b/test/Parsed/Sanity.Parsed.hpp index 9b24888..49f0596 100644 --- a/test/Parsed/Sanity.Parsed.hpp +++ b/test/Parsed/Sanity.Parsed.hpp @@ -192,7 +192,6 @@ u32 gen_sanity() gen_sanity_file.print_fmt("\n"); // Specifiers - if (0) { Code fwd_fn = parse_function( code( inline diff --git a/test/Readme.md b/test/Readme.md index 909eade..3579beb 100644 --- a/test/Readme.md +++ b/test/Readme.md @@ -2,10 +2,11 @@ The following tests focus on attempting to generate some math, containers, and the memory module of zpl. -Not all the files are written how I would practically use the librarry, +Not all the files are written how I would practically use the librarry, the containers for example would +be better on in c++ as templates, since the templates they generate are trivial symbols to inspect or debug. -There will be down the line a proper container, and memory libraries made with this gen library -once the stress test files are complete. +An exmaple of a non-trival generation is a container for elements with SOA or AOS policy for layout. +(If a unified element syntax is desired) The test is divided between two major sets of tests: Parsed and Nonparsed. diff --git a/test/gen/meson.build b/test/gen/meson.build index 15283bf..5fd39ef 100644 --- a/test/gen/meson.build +++ b/test/gen/meson.build @@ -25,5 +25,5 @@ endif add_project_arguments('-Dgen_time', language : ['c', 'cpp']) -# executable( 'gencpp', sources, include_directories : includes ) +executable( 'gencpp', sources, include_directories : includes ) executable( 'gencpp_parsed', sources_parsed, include_directories : includes )