diff --git a/.vscode/settings.json b/.vscode/settings.json index 0d85554..78f22e2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -22,7 +22,7 @@ }, "C_Cpp.intelliSenseEngineFallback": "disabled", "mesonbuild.configureOnOpen": true, - "C_Cpp.errorSquiggles": "enabled", + "C_Cpp.errorSquiggles": "disabled", // This doesn't work well with how the headers are included. "godot_tools.scene_file_config": "", "C_Cpp.default.compilerPath": "cl.exe", "C_Cpp.exclusionPolicy": "checkFilesAndFolders", diff --git a/gencpp.10x b/gencpp.10x index 035c4fc..8fba690 100644 --- a/gencpp.10x +++ b/gencpp.10x @@ -1,24 +1,24 @@ - *.* - *.obj,*.lib,*.pch,*.dll,*.pdb,.vs,Debug,Release,x64,obj,*.user,Intermediate + *.*, + *.obj,*.lib,*.pch,*.dll,*.pdb,.vs,Debug,Release,x64,obj,*.user,Intermediate,**/sanity.gen.hpp, true true true false false - - + powershell ./scripts/build.ps1 + powershell ./scripts/rebuild.ps1 - + powershell ./scripts/clean.ps1 - + ./test/gen/build/gencpp.exe - - - + powershell ./scripts/build.ps1 + ./test/gen/build/gencpp.exe + gencpp.sln false Debug @@ -39,7 +39,21 @@ C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\include\um - + + + Debug:x64 + + + + + Debug + + + + x64 + + + diff --git a/gencpp.vcxproj b/gencpp.vcxproj index f6ec0cb..8057c56 100644 --- a/gencpp.vcxproj +++ b/gencpp.vcxproj @@ -111,6 +111,23 @@ + + + + + + + + + + + + + + + + + @@ -118,6 +135,9 @@ + + + @@ -139,6 +159,14 @@ + + + + + + + + diff --git a/gencpp.vcxproj.filters b/gencpp.vcxproj.filters index 811419a..4d7e755 100644 --- a/gencpp.vcxproj.filters +++ b/gencpp.vcxproj.filters @@ -51,6 +51,30 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -131,6 +155,66 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + diff --git a/project/Readme.md b/project/Readme.md index b7b2170..1f8287c 100644 --- a/project/Readme.md +++ b/project/Readme.md @@ -5,6 +5,21 @@ Things related to the editor and scanner are in their own respective files. (Ex: Dependencies are within `gen.dep.` +The library is fragmented into a series of headers and sources files meant to be scanned in and then generated to a tailored format for the target +`gen` files. + +Both libraries use *pre-generated* (self-hosting I guess) version of the library to then generate the latest version of itself. +(sort of a verification that the generated version is equivalent) + +The default `gen.bootstrap.cpp` located in the project folder is meant to be produce a standard segmeneted library, where the components of the library +have relatively dedicated header and source files. With dependencies included at the top of the file and each header starting with a pragma once. + +`gen.singleheader.cpp` in the single header folder with its own `meson.build` generates the library as a single header `gen.hpp`. +Following the same convention seen in the gb, stb, and zpl libraries. + +Use those to get a general idea of how to make your own tailored version. + +If the naming convention is undesired, the `gencpp.refactor` script can be used with the [refactor]() ## gen.hpp diff --git a/project/components/gen.ast.cpp b/project/components/gen.ast.cpp index e69de29..348ef94 100644 --- a/project/components/gen.ast.cpp +++ b/project/components/gen.ast.cpp @@ -0,0 +1,1030 @@ +Code Code::Global; +Code Code::Invalid; + +AST* AST::duplicate() +{ + using namespace ECode; + + AST* + result = make_code().ast; +#ifndef GEN_USE_RECURSIVE_AST_DUPLICATION + mem_copy( result, this, sizeof( AST ) ); + result->Parent = nullptr; +#else + // TODO : Stress test this... + switch ( Type ) + { + case Invalid: + log_failure("Attempted to duplicate invalid code! - \n%s", Parent ? Parent->debug_str() : Name ); + return nullptr + case Untyped: + case Comment: + case Execution: + case Access_Private: + case Access_Protected: + case Access_Public: + case PlatformAttributes: + case Preprocessor_Include: + case Module: + case Specifiers: + case Using_Namespace: + mem_copy( result, this, sizeof( AST ) ); + break; + + case Extern_Linkage: + case Friend: + mem_copy( result, this, sizeof( AST ) ); + + if (Value) + result->Value = Value->duplicate(); + break; + + case Class: + case Struct: + case Enum: + mem_copy( result, this, sizeof( AST ) ); + + if ( Attributes) + result->Attributes = Attributes->duplicate(); + + if ( ParentType ) + result->ParentType = ParentType->duplicate(); + + result->Body = Body->duplicate(); + break; + + case Enum_Fwd: + case Class_Fwd: + case Struct_Fwd: + mem_copy( result, this, sizeof( AST ) ); + + if ( Attributes) + result->Attributes = Attributes->duplicate(); + + if ( ParentType ) + result->ParentType = ParentType->duplicate(); + break; + + case Function: + case Operator: + case Operator_Member: + mem_copy( result, this, sizeof( AST ) ); + + if ( Attributes) + result->Attributes = Attributes->duplicate(); + + if ( Specs ) + result->ParentType = ParentType->duplicate(); + + if ( ReturnType ) + result->ReturnType = ReturnType->duplicate(); + + if ( Params ) + result->Params = Params->duplicate(); + + result->Body = Body->duplicate(); + break; + + case Function_Fwd: + case Operator_Fwd: + case Operator_Member_Fwd: + mem_copy( result, this, sizeof( AST ) ); + + if ( Attributes) + result->Attributes = Attributes->duplicate(); + + if ( Specs ) + result->ParentType = ParentType->duplicate(); + + if ( ReturnType ) + result->ReturnType = ReturnType->duplicate(); + + if ( Params ) + result->Params = Params->duplicate(); + break; + + case Namespace: + mem_copy( result, this, sizeof( AST ) ); + + result->Body = Body->duplicate(); + break; + + case Operator_Cast: + mem_copy( result, this, sizeof( AST ) ); + + result->ValueType = ValueType->duplicate(); + result->Body = Body->duplicate(); + break; + case Operator_Cast_Fwd: + mem_copy( result, this, sizeof( AST ) ); + + result->ValueType = ValueType->duplicate(); + break; + + case Parameters: + mem_copy( result, this, sizeof( AST ) ); + + result->NumEntries = 0; + result->Last = nullptr; + result->Next = nullptr; + + if ( NumEntries - 1 > 0 ) + { + CodeParam parent = result->cast(); + for ( CodeParam param : Next->cast() ) + { + parent.append( param ); + } + } + break; + + case Template: + mem_copy( result, this, sizeof( AST ) ); + + result->Params = Params->duplicate(); + result->Declaration = Declaration->duplicate(); + break; + + case Typename: + mem_copy( result, this, sizeof( AST ) ); + + if (Attributes) + result->Attributes = Attributes->duplicate(); + + if ( Specs ) + result->Specs = Specs->duplicate(); + + if ( ArrExpr ) + result->ArrExpr = ArrExpr->duplicate(); + break; + + case Typedef: + case Using: + mem_copy( result, this, sizeof( AST ) ); + + if (Attributes) + result->Attributes = Attributes->duplicate(); + + if ( UnderlyingType ) + result->UnderlyingType = UnderlyingType->duplicate(); + break; + + case Union: + mem_copy( result, this, sizeof( AST ) ); + + if ( Attributes) + result->Attributes = Attributes->duplicate(); + + result->Body = Body->duplicate(); + break; + + case Variable: + mem_copy( result, this, sizeof( AST ) ); + + if (Attributes) + result->Attributes = Attributes->duplicate(); + + if ( Specs ) + result->Specs = Specs->duplicate(); + + result->ValueType = UnderlyingType->duplicate(); + + if ( Value ) + result->Value = Value->duplicate(); + break; + + case Class_Body: + case Enum_Body: + case Export_Body: + case Extern_Linkage_Body: + case Function_Body: + case Global_Body: + case Namespace_Body: + case Struct_Body: + case Union_Body: + CodeBody + body = cast(); + body->Name = Name; + body->Type = Type; + for ( Code entry : cast() ) + { + result->append( entry.ast ); + } + break; + } +#endif + + return result; +} + +String AST::to_string() +{ +# define ProcessModuleFlags() \ + if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export )) \ + result.append( "export " ); \ + \ + if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Import )) \ + result.append( "import " ); \ + + local_persist thread_local + char SerializationLevel = 0; + +#if defined(GEN_BENCHMARK) && defined(GEN_BENCHMARK_SERIALIZATION) + u64 time_start = time_rel_ms(); +#endif + + // TODO : Need to refactor so that intermeidate strings are freed conviently. + String result = String::make( GlobalAllocator, "" ); + + switch ( Type ) + { + using namespace ECode; + + case Invalid: + log_failure("Attempted to serialize invalid code! - %s", Parent ? Parent->debug_str() : Name ); + break; + + case Untyped: + case Execution: + result.append( Content ); + break; + + case Comment: + { + static char line[MaxCommentLineLength]; + + s32 left = Content.length(); + s32 index = 0; + do + { + s32 length = 0; + while ( left && Content[index] != '\n' ) + { + length++; + left--; + } + + str_copy( line, Content, length ); + line[length] = '\0'; + + result.append_fmt( "// %s", line ); + } + while ( left--, left > 0 ); + } + break; + + case Access_Private: + case Access_Protected: + case Access_Public: + result.append( Name ); + break; + + case PlatformAttributes: + result.append( Content ); + + case Class: + { + ProcessModuleFlags(); + + if ( Attributes || ParentType ) + { + result.append( "class " ); + + if ( Attributes ) + { + result.append_fmt( "%s ", Attributes->to_string() ); + } + + if ( ParentType ) + { + char const* access_level = to_str( ParentAccess ); + + result.append_fmt( "%s : %s %s\n{\n%s\n};" + , Name + , access_level + , ParentType->to_string() + , Body->to_string() + ); + + CodeType interface = Next->cast(); + if ( interface ) + result.append("\n"); + + while ( interface ) + { + result.append_fmt( ", %s", interface.to_string() ); + + interface = interface->Next->cast(); + } + } + else + { + result.append_fmt( "%s \n{\n%s\n};", Name, Body->to_string() ); + } + } + else + { + result.append_fmt( "class %s\n{\n%s\n};", Name, Body->to_string() ); + } + } + break; + + case Class_Fwd: + { + ProcessModuleFlags(); + + if ( Attributes ) + result.append_fmt( "class %s %s;", Attributes->to_string(), Name ); + + else result.append_fmt( "class %s;", Name ); + } + break; + + case Enum: + { + ProcessModuleFlags(); + + if ( Attributes || UnderlyingType ) + { + result.append( "enum " ); + + if ( Attributes ) + result.append_fmt( "%s ", Attributes->to_string() ); + + if ( UnderlyingType ) + result.append_fmt( "%s : %s\n{\n%s\n};" + , Name + , UnderlyingType->to_string() + , Body->to_string() + ); + + else result.append_fmt( "%s\n{\n%s\n};" + , Name + , Body->to_string() + ); + } + else result.append_fmt( "enum %s\n{\n%s\n};" + , Name + , Body->to_string() + ); + } + break; + + case Enum_Fwd: + { + ProcessModuleFlags(); + + if ( Attributes ) + result.append_fmt( "%s ", Attributes->to_string() ); + + result.append_fmt( "enum %s : %s;", Name, UnderlyingType->to_string() ); + } + break; + + case Enum_Class: + { + ProcessModuleFlags(); + + if ( Attributes || UnderlyingType ) + { + result.append( "enum class " ); + + if ( Attributes ) + { + result.append_fmt( "%s ", Attributes->to_string() ); + } + + if ( UnderlyingType ) + { + result.append_fmt( "%s : %s\n{\n%s\n};" + , Name + , UnderlyingType->to_string() + , Body->to_string() + ); + } + else + { + result.append_fmt( "%s\n{\n%s\n};" + , Name + , Body->to_string() + ); + } + } + else + { + result.append_fmt( "enum class %s\n{\n%s\n};" + , Body->to_string() + ); + } + } + break; + + case Enum_Class_Fwd: + { + ProcessModuleFlags(); + + result.append( "enum class " ); + + if ( Attributes ) + result.append_fmt( "%s ", Attributes->to_string() ); + + result.append_fmt( "%s : %s;", Name, UnderlyingType->to_string() ); + } + break; + + case Export_Body: + { + result.append_fmt( "export\n{\n" ); + + Code curr = { this }; + s32 left = NumEntries; + while ( left-- ) + { + result.append_fmt( "%s\n", curr.to_string() ); + ++curr; + } + + result.append_fmt( "};" ); + } + break; + + case Extern_Linkage: + result.append_fmt( "extern \"%s\"\n{\n%s\n}" + , Name + , Body->to_string() + ); + break; + + case Friend: + result.append_fmt( "friend %s", Declaration->to_string() ); + + if ( result[ result.length() -1 ] != ';' ) + result.append( ";" ); + break; + + case Function: + { + ProcessModuleFlags(); + + if ( Attributes ) + result.append_fmt( "%s ", Attributes->to_string() ); + + if ( Specs ) + result.append_fmt( "%s\n", Specs->to_string() ); + + if ( ReturnType ) + result.append_fmt( "%s %s(", ReturnType->to_string(), Name ); + + else + result.append_fmt( "%s(", Name ); + + if ( Params ) + result.append_fmt( "%s)", Params->to_string() ); + + else + result.append( "void)" ); + + if ( Specs ) + { + CodeSpecifiers specs = cast(); + + for ( SpecifierT spec : specs ) + { + if ( ESpecifier::is_trailing( spec ) ) + result.append_fmt( " %s", (char const*)ESpecifier::to_str( spec ) ); + } + } + + result.append_fmt( "\n{\n%s\n}" + , Body->to_string() + ); + } + break; + + case Function_Fwd: + { + ProcessModuleFlags(); + + if ( Attributes ) + result.append_fmt( "%s ", Attributes->to_string() ); + + if ( Specs ) + result.append_fmt( "%s\n", Specs->to_string() ); + + if ( ReturnType ) + result.append_fmt( "%s %s(", ReturnType->to_string(), Name ); + + else + result.append_fmt( "%s(", Name ); + + if ( Params ) + result.append_fmt( "%s)", Params->to_string() ); + + else + result.append( "void)" ); + + if ( Specs ) + { + CodeSpecifiers specs = cast(); + + for ( SpecifierT spec : specs ) + { + if ( ESpecifier::is_trailing( spec ) ) + result.append_fmt( " %s", (char const*)ESpecifier::to_str( spec ) ); + } + } + + result.append( ";" ); + } + break; + + case Module: + if (((u32(ModuleFlag::Export) & u32(ModuleFlags)) == u32(ModuleFlag::Export))) + result.append("export "); + + if (((u32(ModuleFlag::Import) & u32(ModuleFlags)) == u32(ModuleFlag::Import))) + result.append("import "); + + result.append_fmt( "%s;", Name ); + break; + + case Namespace: + ProcessModuleFlags(); + + result.append_fmt( "namespace %s\n{\n%s}" + , Name + , Body->to_string() + ); + break; + + case Operator: + case Operator_Member: + { + ProcessModuleFlags(); + + if ( Attributes ) + result.append_fmt( "%s ", Attributes->to_string() ); + + if ( Attributes ) + result.append_fmt( "%s\n", Attributes->to_string() ); + + if ( ReturnType ) + result.append_fmt( "%s %s (", ReturnType->to_string(), Name ); + + if ( Params ) + result.append_fmt( "%s)", Params->to_string() ); + + else + result.append( "void)" ); + + if ( Specs ) + { + CodeSpecifiers specs = cast(); + + for ( SpecifierT spec : specs ) + { + if ( ESpecifier::is_trailing( spec ) ) + result.append_fmt( " %s", (char const*)ESpecifier::to_str( spec ) ); + } + } + + result.append_fmt( "\n{\n%s\n}" + , Body->to_string() + ); + } + break; + + case Operator_Fwd: + case Operator_Member_Fwd: + { + ProcessModuleFlags(); + + if ( Attributes ) + result.append_fmt( "%s ", Attributes->to_string() ); + + if ( Specs ) + result.append_fmt( "%s", Specs->to_string() ); + + result.append_fmt( "%s %s (", ReturnType->to_string(), Name ); + + if ( Params ) + result.append_fmt( "%s)", Params->to_string() ); + + else + result.append_fmt( "void)" ); + + if ( Specs ) + { + CodeSpecifiers specs = cast(); + + for ( SpecifierT spec : specs ) + { + if ( ESpecifier::is_trailing( spec ) ) + result.append_fmt( " %s", (char const*)ESpecifier::to_str( spec ) ); + } + } + + result.append( ";" ); + } + break; + + case Operator_Cast: + { + if ( Specs ) + { + result.append_fmt( "operator %s()" ); + + CodeSpecifiers specs = cast(); + + for ( SpecifierT spec : specs ) + { + if ( ESpecifier::is_trailing( spec ) ) + result.append_fmt( " %s", (char const*)ESpecifier::to_str( spec ) ); + } + + result.append_fmt( "\n{\n%s\n}", Body->to_string() ); + break; + } + + result.append_fmt("operator %s()\n{\n%s\n}", ValueType->to_string(), Body->to_string() ); + } + break; + + case Operator_Cast_Fwd: + if ( Specs ) + { + result.append_fmt( "operator %s()" ); + + CodeSpecifiers specs = cast(); + + for ( SpecifierT spec : specs ) + { + if ( ESpecifier::is_trailing( spec ) ) + result.append_fmt( " %s", (char const*)ESpecifier::to_str( spec ) ); + } + + result.append_fmt( ";", Body->to_string() ); + break; + } + + result.append_fmt("operator %s();", ValueType->to_string() ); + break; + + case Parameters: + { + if ( Name ) + result.append_fmt( "%s %s", ValueType->to_string(), Name ); + + else + result.append_fmt( "%s", ValueType->to_string() ); + + if ( Value ) + result.append_fmt( "= %s", Value->to_string() ); + + if ( NumEntries - 1 > 0) + { + for ( CodeParam param : CodeParam { (AST_Param*) Next } ) + { + result.append_fmt( ", %s", param.to_string() ); + } + } + } + break; + + case Preprocessor_Include: + result.append_fmt( "#include \"%s\"", Name ); + break; + + case Specifiers: + { + s32 idx = 0; + s32 left = NumEntries; + while ( left-- ) + { + if ( ESpecifier::is_trailing( ArrSpecs[idx]) ) + { + idx++; + continue; + } + + result.append_fmt( "%s ", (char const*)ESpecifier::to_str( ArrSpecs[idx]) ); + idx++; + } + } + break; + + case Struct: + { + ProcessModuleFlags(); + + if ( Name == nullptr) + { + result.append( "struct\n{\n%s\n};", Body->to_string() ); + break; + } + + if ( Attributes || ParentType ) + { + result.append( "struct " ); + + if ( Attributes ) + result.append_fmt( "%s ", Attributes->to_string() ); + + if ( ParentType ) + { + char const* access_level = to_str( ParentAccess ); + + result.append_fmt( "%s : %s %s\n{\n%s\n};" + , Name + , access_level + , ParentType->to_string() + , Body->to_string() + ); + + CodeType interface = Next->cast(); + if ( interface ) + result.append("\n"); + + while ( interface ) + { + result.append_fmt( ", public %s", interface.to_string() ); + + interface = interface->Next->cast(); + } + } + else + { + if ( Name ) + + result.append_fmt( "%s \n{\n%s\n};", Name, Body->to_string() ); + } + } + else + { + result.append_fmt( "struct %s\n{\n%s\n};", Name, Body->to_string() ); + } + } + break; + + case Struct_Fwd: + { + ProcessModuleFlags(); + + if ( Attributes ) + result.append_fmt( "struct %s %s;", Attributes->to_string(), Name ); + + else result.append_fmt( "struct %s;", Name ); + } + break; + + case Template: + { + ProcessModuleFlags(); + + result.append_fmt( "template< %s >\n%s", Params->to_string(), Declaration->to_string() ); + } + break; + + case Typedef: + { + ProcessModuleFlags(); + + result.append( "typedef "); + + result.append_fmt( "%s %s", UnderlyingType->to_string(), Name ); + + if ( UnderlyingType->Type == Typename && UnderlyingType->ArrExpr ) + { + result.append_fmt( "[%s];", UnderlyingType->ArrExpr->to_string() ); + } + else + { + result.append( ";" ); + } + } + break; + + case Typename: + { + if ( Attributes || Specs ) + { + if ( Attributes ) + result.append_fmt( "%s ", Attributes->to_string() ); + + if ( Specs ) + result.append_fmt( "%s %s", Name, Specs->to_string() ); + + else + result.append_fmt( "%s", Name ); + } + else + { + result.append_fmt( "%s", Name ); + } + } + break; + + case Union: + { + ProcessModuleFlags(); + + result.append( "union " ); + + if ( Attributes ) + result.append_fmt( "%s ", Attributes->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; + + case Using: + { + ProcessModuleFlags(); + + if ( Attributes ) + result.append_fmt( "%s ", Attributes->to_string() ); + + if ( UnderlyingType ) + { + result.append_fmt( "using %s = %s", Name, UnderlyingType->to_string() ); + + if ( UnderlyingType->ArrExpr ) + result.append_fmt( "[%s]", UnderlyingType->ArrExpr->to_string() ); + + result.append( ";" ); + } + else + result.append_fmt( "using %s;", Name ); + } + break; + + case Using_Namespace: + result.append_fmt( "using namespace %s;", Name ); + break; + + case Variable: + { + ProcessModuleFlags(); + + if ( Attributes || Specs ) + { + if ( Attributes ) + result.append_fmt( "%s ", Specs->to_string() ); + + if ( Specs ) + result.append_fmt( "%s\n", Specs->to_string() ); + + result.append_fmt( "%s %s", ValueType->to_string(), Name ); + + if ( ValueType->ArrExpr ) + result.append_fmt( "[%s]", ValueType->ArrExpr->to_string() ); + + if ( Value ) + result.append_fmt( " = %s", Value->to_string() ); + + result.append( ";" ); + + break; + } + + if ( UnderlyingType->ArrExpr ) + result.append_fmt( "%s %s[%s];", UnderlyingType->to_string(), Name, UnderlyingType->ArrExpr->to_string() ); + + else + result.append_fmt( "%s %s;", UnderlyingType->to_string(), Name ); + } + break; + + case Class_Body: + case Enum_Body: + case Extern_Linkage_Body: + case Function_Body: + case Global_Body: + case Namespace_Body: + case Struct_Body: + case Union_Body: + { + Code curr = Front->cast(); + s32 left = NumEntries; + while ( left -- ) + { + result.append_fmt( "%s\n", curr.to_string() ); + ++curr; + } + } + break; + } + +#if defined(GEN_BENCHMARK) && defined(GEN_BENCHMARK_SERIALIZATION) + log_fmt("AST::to_string() time taken: %llu for: %s\n", time_rel_ms() - time_start, result ); +#endif + return result; +#undef ProcessModuleFlags +} + +bool AST::is_equal( AST* other ) +{ + if ( Type != other->Type ) + return false; + + switch ( Type ) + { + case ECode::Typedef: + case ECode::Typename: + { + if ( Name != other->Name ) + return false; + + return true; + } + } + + if ( Name != other->Name ) + return false; + + return true; +} + +bool AST::validate_body() +{ + using namespace ECode; + +#define CheckEntries( Unallowed_Types ) \ + do \ + { \ + for ( Code entry : cast() ) \ + { \ + switch ( entry->Type ) \ + { \ + Unallowed_Types \ + log_failure( "AST::validate_body: Invalid entry in body %s", entry.debug_str() ); \ + return false; \ + } \ + } \ + } \ + while (0); + + switch ( Type ) + { + case Class_Body: + CheckEntries( AST_BODY_CLASS_UNALLOWED_TYPES ); + break; + case Enum_Body: + for ( Code entry : cast() ) + { + if ( entry->Type != Untyped ) + { + log_failure( "AST::validate_body: Invalid entry in enum body (needs to be untyped or comment) %s", entry.debug_str() ); + return false; + } + } + break; + case Export_Body: + CheckEntries( AST_BODY_CLASS_UNALLOWED_TYPES ); + break; + case Extern_Linkage: + CheckEntries( AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES ); + break; + case Function_Body: + CheckEntries( AST_BODY_FUNCTION_UNALLOWED_TYPES ); + break; + case Global_Body: + CheckEntries( AST_BODY_GLOBAL_UNALLOWED_TYPES ); + break; + case Namespace_Body: + CheckEntries( AST_BODY_NAMESPACE_UNALLOWED_TYPES ); + break; + case Struct_Body: + CheckEntries( AST_BODY_STRUCT_UNALLOWED_TYPES ); + break; + case Union_Body: + for ( Code entry : Body->cast() ) + { + if ( entry->Type != Untyped ) + { + log_failure( "AST::validate_body: Invalid entry in union body (needs to be untyped or comment) %s", entry.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; +} diff --git a/project/components/gen.ast_case_macros.cpp b/project/components/gen.ast_case_macros.cpp new file mode 100644 index 0000000..902d22c --- /dev/null +++ b/project/components/gen.ast_case_macros.cpp @@ -0,0 +1,64 @@ +# define AST_BODY_CLASS_UNALLOWED_TYPES \ + case PlatformAttributes: \ + case Class_Body: \ + case Enum_Body: \ + case Extern_Linkage: \ + case Function_Body: \ + case Function_Fwd: \ + case Global_Body: \ + case Namespace: \ + case Namespace_Body: \ + case Operator: \ + case Operator_Fwd: \ + case Parameters: \ + case Specifiers: \ + case Struct_Body: \ + case Typename: + +# define AST_BODY_FUNCTION_UNALLOWED_TYPES \ + case Access_Public: \ + case Access_Protected: \ + case Access_Private: \ + case PlatformAttributes: \ + case Class_Body: \ + case Enum_Body: \ + case Extern_Linkage: \ + case Friend: \ + case Function_Body: \ + case Function_Fwd: \ + case Global_Body: \ + case Namespace: \ + case Namespace_Body: \ + case Operator: \ + case Operator_Fwd: \ + case Operator_Member: \ + case Operator_Member_Fwd: \ + case Parameters: \ + case Specifiers: \ + case Struct_Body: \ + case Typename: + +# define AST_BODY_GLOBAL_UNALLOWED_TYPES \ + case Access_Public: \ + case Access_Protected: \ + case Access_Private: \ + case PlatformAttributes: \ + case Class_Body: \ + case Enum_Body: \ + case Execution: \ + case Friend: \ + case Function_Body: \ + case Global_Body: \ + case Namespace_Body: \ + case Operator_Member: \ + case Operator_Member_Fwd: \ + case Parameters: \ + case Specifiers: \ + case Struct_Body: \ + case Typename: + +# define AST_BODY_EXPORT_UNALLOWED_TYPES AST_BODY_GLOBAL_UNALLOWED_TYPES +# define AST_BODY_NAMESPACE_UNALLOWED_TYPES AST_BODY_GLOBAL_UNALLOWED_TYPES +# define AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES AST_BODY_GLOBAL_UNALLOWED_TYPES + +# define AST_BODY_STRUCT_UNALLOWED_TYPES AST_BODY_CLASS_UNALLOWED_TYPES diff --git a/project/components/gen.data.cpp b/project/components/gen.data.cpp new file mode 100644 index 0000000..4399da9 --- /dev/null +++ b/project/components/gen.data.cpp @@ -0,0 +1,86 @@ +#pragma region StaticData +// TODO : Convert global allocation strategy to use a slab allocation strategy. +global AllocatorInfo GlobalAllocator; +global Array Global_AllocatorBuckets; + +global Array< Pool > CodePools = { nullptr }; +global Array< Arena > StringArenas = { nullptr }; + +global StringTable StringCache; + +global Arena LexArena; + +global AllocatorInfo Allocator_DataArrays = heap(); +global AllocatorInfo Allocator_CodePool = heap(); +global AllocatorInfo Allocator_Lexer = heap(); +global AllocatorInfo Allocator_StringArena = heap(); +global AllocatorInfo Allocator_StringTable = heap(); +global AllocatorInfo Allocator_TypeTable = heap(); +#pragma endregion StaticData + +#pragma region Constants +global CodeType t_empty; +global CodeType t_auto; +global CodeType t_void; +global CodeType t_int; +global CodeType t_bool; +global CodeType t_char; +global CodeType t_wchar_t; +global CodeType t_class; +global CodeType t_typename; + +#ifdef GEN_DEFINE_LIBRARY_CODE_CONSTANTS +global CodeType t_b32; + +global CodeType t_s8; +global CodeType t_s16; +global CodeType t_s32; +global CodeType t_s64; + +global CodeType t_u8; +global CodeType t_u16; +global CodeType t_u32; +global CodeType t_u64; + +global CodeType t_sw; +global CodeType t_uw; + +global CodeType t_f32; +global CodeType t_f64; +#endif + +global CodeParam param_varadic; + +global CodeAttributes attrib_api_export; +global CodeAttributes attrib_api_import; + +global Code access_public; +global Code access_protected; +global Code access_private; + +global Code module_global_fragment; +global Code module_private_fragment; + +global Code pragma_once; + +global CodeSpecifiers spec_const; +global CodeSpecifiers spec_consteval; +global CodeSpecifiers spec_constexpr; +global CodeSpecifiers spec_constinit; +global CodeSpecifiers spec_extern_linkage; +global CodeSpecifiers spec_final; +global CodeSpecifiers spec_global; +global CodeSpecifiers spec_inline; +global CodeSpecifiers spec_internal_linkage; +global CodeSpecifiers spec_local_persist; +global CodeSpecifiers spec_mutable; +global CodeSpecifiers spec_override; +global CodeSpecifiers spec_ptr; +global CodeSpecifiers spec_ref; +global CodeSpecifiers spec_register; +global CodeSpecifiers spec_rvalue; +global CodeSpecifiers spec_static_member; +global CodeSpecifiers spec_thread_local; +global CodeSpecifiers spec_virtual; +global CodeSpecifiers spec_volatile; +#pragma endregion Constants diff --git a/project/components/gen.data_structures.hpp b/project/components/gen.data_structures.hpp index e69de29..7a8f482 100644 --- a/project/components/gen.data_structures.hpp +++ b/project/components/gen.data_structures.hpp @@ -0,0 +1,1013 @@ +// Implements basic string interning. Data structure is based off the ZPL Hashtable. +using StringTable = HashTable; + +// Represents strings cached with the string table. +// Should never be modified, if changed string is desired, cache_string( str ) another. +using StringCached = String const; + +struct AST; +struct AST_Body; +struct AST_Attributes; +struct AST_Comment; +struct AST_Class; +struct AST_Enum; +struct AST_Exec; +struct AST_Extern; +struct AST_Include; +struct AST_Friend; +struct AST_Fn; +struct AST_Module; +struct AST_Namespace; +struct AST_Operator; +struct AST_OpCast; +struct AST_Param; +struct AST_Specifiers; +struct AST_Struct; +struct AST_Template; +struct AST_Type; +struct AST_Typedef; +struct AST_Union; +struct AST_Using; +struct AST_Var; + +struct Code; +struct CodeBody; +// These are to offer ease of use and optionally strong type safety for the AST. +struct CodeAttributes; +struct CodeComment; +struct CodeClass; +struct CodeEnum; +struct CodeExec; +struct CodeExtern; +struct CodeInclude; +struct CodeFriend; +struct CodeFn; +struct CodeModule; +struct CodeNamespace; +struct CodeOperator; +struct CodeOpCast; +struct CodeParam; +struct CodeSpecifiers; +struct CodeStruct; +struct CodeTemplate; +struct CodeType; +struct CodeTypedef; +struct CodeUnion; +struct CodeUsing; +struct CodeVar; + +/* + AST* wrapper + - Not constantly have to append the '*' as this is written often.. + - Allows for implicit conversion to any of the ASTs (raw or filtered). +*/ +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 + + #define Using_Code( Typename ) \ + char const* debug_str(); \ + Code duplicate(); \ + bool is_equal( Code other ); \ + bool is_valid(); \ + void set_global(); \ + String to_string(); \ + Typename& operator = ( AST* other ); \ + Typename& operator = ( Code other ); \ + bool operator ==( Code other ); \ + bool operator !=( Code other ); \ + operator bool() \ + { \ + return ast != nullptr; \ + } + + template< class Type > + Type cast() + { + return * rcast( Type*, this ); + } + + AST* operator ->() + { + return ast; + } + Code& operator ++(); + Code& operator*() + { + return *this; + } + + Using_Code( Code ); + + AST* ast; + +#ifdef GEN_ENFORCE_STRONG_CODE_TYPES +# define operator explicit operator +#endif + operator CodeAttributes() const; + operator CodeComment() const; + operator CodeClass() const; + operator CodeExec() const; + operator CodeEnum() const; + operator CodeExtern() const; + operator CodeInclude() const; + operator CodeFriend() const; + operator CodeFn() const; + operator CodeModule() const; + operator CodeNamespace() const; + operator CodeOperator() const; + operator CodeOpCast() const; + operator CodeParam() const; + operator CodeSpecifiers() const; + operator CodeStruct() const; + operator CodeTemplate() const; + operator CodeType() const; + operator CodeTypedef() const; + operator CodeUnion() const; + operator CodeUsing() const; + operator CodeVar() const; + operator CodeBody() const; + #undef operator +}; + +struct Code_POD +{ + AST* ast; +}; + +static_assert( sizeof(Code) == sizeof(Code_POD), "ERROR: Code is not POD" ); + +// Desired width of the AST data structure. +constexpr u32 AST_POD_Size = 128; + +/* + Simple AST POD with functionality to seralize into C++ syntax. +*/ +struct AST +{ +# pragma region Member Functions + void append ( AST* other ); + char const* debug_str (); + AST* duplicate (); + Code& entry ( u32 idx ); + bool has_entries(); + bool is_equal ( AST* other ); + String to_string (); + char const* type_str(); + bool validate_body(); + + template< class Type > + Type cast() + { + return * this; + } + + operator Code(); + operator CodeBody(); + operator CodeAttributes(); + operator CodeComment(); + operator CodeClass(); + operator CodeEnum(); + operator CodeExec(); + operator CodeExtern(); + operator CodeInclude(); + operator CodeFriend(); + operator CodeFn(); + operator CodeModule(); + operator CodeNamespace(); + operator CodeOperator(); + operator CodeOpCast(); + operator CodeParam(); + operator CodeSpecifiers(); + operator CodeStruct(); + operator CodeTemplate(); + operator CodeType(); + operator CodeTypedef(); + operator CodeUnion(); + operator CodeUsing(); + operator CodeVar(); +# pragma endregion Member Functions + + constexpr static + uw ArrSpecs_Cap = + ( + AST_POD_Size + - sizeof(AST*) * 3 + - sizeof(StringCached) + - sizeof(CodeT) + - sizeof(ModuleFlag) + - sizeof(s32) + ) + / sizeof(SpecifierT) -1; // -1 for 4 extra bytes + + union { + struct + { + AST* Attributes; // Class, Enum, Function, Struct, Typedef, Union, Using, Variable + AST* Specs; // Function, Operator, Type symbol, Variable + union { + AST* ParentType; // Class, Struct + AST* ReturnType; // Function, Operator + AST* UnderlyingType; // Enum, Typedef + AST* ValueType; // Parameter, Variable + }; + AST* Params; // Function, Operator, Template + union { + AST* ArrExpr; // Type Symbol + AST* Body; // Class, Enum, Function, Namespace, Struct, Union + AST* Declaration; // Friend, Template + AST* Value; // Parameter, Variable + }; + }; + StringCached Content; // Attributes, Comment, Execution, Include + SpecifierT ArrSpecs[AST::ArrSpecs_Cap]; // Specifiers + }; + union { + AST* Prev; + AST* Front; + AST* Last; + }; + union { + AST* Next; + AST* Back; + }; + AST* Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + union { + OperatorT Op; + AccessSpec ParentAccess; + s32 NumEntries; + }; +}; + +struct AST_POD +{ + union { + struct + { + AST* Attributes; // Class, Enum, Function, Struct, Typename, Union, Using, Variable + AST* Specs; // Function, Operator, Type symbol, Variable + union { + AST* ParentType; // Class, Struct + AST* ReturnType; // Function, Operator + AST* UnderlyingType; // Enum, Typedef + AST* ValueType; // Parameter, Variable + }; + AST* Params; // Function, Operator, Template + union { + AST* ArrExpr; // Type Symbol + AST* Body; // Class, Enum, Function, Namespace, Struct, Union + AST* Declaration; // Friend, Template + AST* Value; // Parameter, Variable + }; + }; + StringCached Content; // Attributes, Comment, Execution, Include + SpecifierT ArrSpecs[AST::ArrSpecs_Cap]; // Specifiers + }; + union { + AST* Prev; + AST* Front; + AST* Last; + }; + union { + AST* Next; + AST* Back; + }; + AST* Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + union { + OperatorT Op; + AccessSpec ParentAccess; + s32 NumEntries; + }; +}; + +// Its intended for the AST to have equivalent size to its POD. +// All extra functionality within the AST namespace should just be syntatic sugar. +static_assert( sizeof(AST) == sizeof(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" ); + +// Used when the its desired when omission is allowed in a definition. +#define NoCode { nullptr } +#define CodeInvalid (* Code::Invalid.ast) // Uses an implicitly overloaded cast from the AST to the desired code type. + +#pragma region Code Types +#define Define_CodeType( Typename ) \ +struct Code##Typename \ +{ \ + Using_Code( Code##Typename ); \ + AST* raw() \ + { \ + return rcast( AST*, ast ); \ + } \ + operator Code() \ + { \ + return * rcast( Code*, this ); \ + } \ + AST_##Typename* operator->() \ + { \ + if ( ast == nullptr ) \ + { \ + log_failure("Attempt to dereference a nullptr!"); \ + return nullptr; \ + } \ + return ast; \ + } \ + AST_##Typename* ast; \ +} + +struct CodeBody +{ + Using_Code( CodeBody ); + + void append( Code other ) + { + raw()->append( other.ast ); + } + void append( CodeBody body ) + { + for ( Code entry : body ) + { + append( entry ); + } + } + bool has_entries() + { + return rcast( AST*, ast )->has_entries(); + } + AST* raw() + { + return rcast( AST*, ast ); + } + AST_Body* operator->() + { + return ast; + } + operator Code() + { + return * rcast( Code*, this ); + } +#pragma region Iterator + Code begin() + { + if ( ast ) + return { rcast( AST*, ast)->Front }; + + return { nullptr }; + } + Code end() + { + return { rcast(AST*, ast)->Back->Next }; + } +#pragma endregion Iterator + + AST_Body* ast; +}; + +Define_CodeType( Attributes ); +Define_CodeType( Comment ); +Define_CodeType( Enum ); +Define_CodeType( Exec ); +Define_CodeType( Extern ); +Define_CodeType( Include ); +Define_CodeType( Friend ); +Define_CodeType( Fn ); +Define_CodeType( Module ); +Define_CodeType( Namespace ); +Define_CodeType( Operator ); +Define_CodeType( OpCast ); +Define_CodeType( Template ); +Define_CodeType( Type ); +Define_CodeType( Typedef ); +Define_CodeType( Union ); +Define_CodeType( Using ); +Define_CodeType( Var ); + +struct CodeClass +{ + Using_Code( CodeClass ); + + void add_interface( CodeType interface ); + + AST* raw() + { + return rcast( AST*, ast ); + } + operator Code() + { + return * rcast( Code*, this ); + } + AST_Class* operator->() + { + if ( ast == nullptr ) + { + log_failure("Attempt to dereference a nullptr"); + return nullptr; + } + return ast; + } + AST_Class* ast; +}; + +struct CodeParam +{ + Using_Code( CodeParam ); + + void append( CodeParam other ); + + CodeParam get( s32 idx ); + bool has_entries(); + AST* raw() + { + return rcast( AST*, ast ); + } + AST_Param* operator->() + { + if ( ast == nullptr ) + { + log_failure("Attempt to dereference a nullptr!"); + return nullptr; + } + return ast; + } + operator Code() + { + return { (AST*)ast }; + } +#pragma region Iterator + CodeParam begin() + { + if ( ast ) + return { ast }; + + return { nullptr }; + } + CodeParam end() + { + return { (AST_Param*) rcast( AST*, ast)->Last }; + } + CodeParam& operator++(); + CodeParam operator*() + { + return * this; + } +#pragma endregion Iterator + + AST_Param* ast; +}; + +struct CodeSpecifiers +{ + Using_Code( CodeSpecifiers ); + + bool append( SpecifierT spec ) + { + if ( raw()->NumEntries == AST::ArrSpecs_Cap ) + { + log_failure("CodeSpecifiers: Attempted to append over %d specifiers to a specifiers AST!", AST::ArrSpecs_Cap ); + return false; + } + + raw()->ArrSpecs[ raw()->NumEntries ] = spec; + raw()->NumEntries++; + return true; + } + s32 has( SpecifierT spec ) + { + for ( s32 idx = 0; idx < raw()->NumEntries; idx++ ) + { + if ( raw()->ArrSpecs[ raw()->NumEntries ] == spec ) + return idx; + } + + return -1; + } + AST* raw() + { + return rcast( AST*, ast ); + } + AST_Specifiers* operator->() + { + if ( ast == nullptr ) + { + log_failure("Attempt to dereference a nullptr!"); + return nullptr; + } + return ast; + } + operator Code() + { + return { (AST*) ast }; + } +#pragma region Iterator + SpecifierT* begin() + { + if ( ast ) + return & raw()->ArrSpecs[0]; + + return nullptr; + } + SpecifierT* end() + { + return raw()->ArrSpecs + raw()->NumEntries; + } +#pragma endregion Iterator + + AST_Specifiers* ast; +}; + +struct CodeStruct +{ + Using_Code( CodeStruct ); + + void add_interface( CodeType interface ); + + AST* raw() + { + return rcast( AST*, ast ); + } + operator Code() + { + return * rcast( Code*, this ); + } + AST_Struct* operator->() + { + if ( ast == nullptr ) + { + log_failure("Attempt to dereference a nullptr"); + return nullptr; + } + return ast; + } + AST_Struct* ast; +}; + +#undef Define_CodeType +#undef Using_Code +#pragma endregion Code Types + +#pragma region Filtered ASTs +/* + Show only relevant members of the AST for its type. + AST* fields are replaced with Code types. + - Guards assignemnts to AST* fields to ensure the AST is duplicated if assigned to another parent. +*/ + +struct AST_Body +{ + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + Code Front; + Code Back; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; + s32 NumEntries; +}; +static_assert( sizeof(AST_Body) == sizeof(AST), "ERROR: AST_Filtered is not the same size as AST"); + +struct AST_Attributes +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + StringCached Content; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Attributes) == sizeof(AST), "ERROR: AST_Attributes is not the same size as AST"); + +struct AST_Comment +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + StringCached Content; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Comment) == sizeof(AST), "ERROR: AST_Comment is not the same size as AST"); + +struct AST_Class +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + CodeAttributes Attributes; + char _PAD_SPECS_ [ sizeof(AST*) ]; + CodeType ParentType; + char _PAD_PARAMS_[ sizeof(AST*) ]; + CodeBody Body; + }; + }; + CodeType Last; + CodeType Next; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + AccessSpec ParentAccess; +}; +static_assert( sizeof(AST_Class) == sizeof(AST), "ERROR: AST_Class is not the same size as AST"); + +struct AST_Enum +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + CodeAttributes Attributes; + char _PAD_SPEC_ [ sizeof(AST*) ]; + CodeType UnderlyingType; + char _PAD_PARAMS_[ sizeof(AST*) ]; + CodeBody Body; + }; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Enum) == sizeof(AST), "ERROR: AST_Enum is not the same size as AST"); + +struct AST_Exec +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + char _PAD_PROPERTIES_[ sizeof(AST*) * 5 ]; + }; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Exec) == sizeof(AST), "ERROR: AST_Exec is not the same size as AST"); + +struct AST_Extern +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + char _PAD_PROPERTIES_[ sizeof(AST*) * 4 ]; + CodeBody Body; + }; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Extern) == sizeof(AST), "ERROR: AST_Extern is not the same size as AST"); + +struct AST_Include +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + StringCached Content; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Include) == sizeof(AST), "ERROR: AST_Include is not the same size as AST"); + +struct AST_Friend +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + char _PAD_PROPERTIES_[ sizeof(AST*) * 4 ]; + Code Declaration; + }; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Friend) == sizeof(AST), "ERROR: AST_Friend is not the same size as AST"); + +struct AST_Fn +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + CodeAttributes Attributes; + CodeSpecifiers Specs; + CodeType ReturnType; + CodeParam Params; + CodeBody Body; + }; + }; + Code Prev; + Code Parent; + Code Next; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Fn) == sizeof(AST), "ERROR: AST_Fn is not the same size as AST"); + +struct AST_Module +{ + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Module) == sizeof(AST), "ERROR: AST_Module is not the same size as AST"); + +struct AST_Namespace +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct { + char _PAD_PROPERTIES_[ sizeof(AST*) * 4 ]; + CodeBody Body; + }; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Namespace) == sizeof(AST), "ERROR: AST_Namespace is not the same size as AST"); + +struct AST_Operator +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + CodeAttributes Attributes; + CodeSpecifiers Specs; + CodeType ReturnType; + CodeParam Params; + CodeBody Body; + }; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + OperatorT Op; +}; +static_assert( sizeof(AST_Operator) == sizeof(AST), "ERROR: AST_Operator is not the same size as AST"); + +struct AST_OpCast +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + char _PAD_PROPERTIES_[ sizeof(AST*) ]; + CodeSpecifiers Specs; + CodeType ValueType; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + CodeBody Body; + }; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_OpCast) == sizeof(AST), "ERROR: AST_OpCast is not the same size as AST"); + +struct AST_Param +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + char _PAD_PROPERTIES_2_[ sizeof(AST*) * 2 ]; + CodeType ValueType; + char _PAD_PROPERTIES_[ sizeof(AST*) ]; + Code Value; + }; + }; + CodeParam Last; + CodeParam Next; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; + s32 NumEntries; +}; +static_assert( sizeof(AST_Param) == sizeof(AST), "ERROR: AST_Param is not the same size as AST"); + +struct AST_Specifiers +{ + SpecifierT ArrSpecs[ AST::ArrSpecs_Cap ]; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; + s32 NumEntries; +}; + static_assert( sizeof(AST_Specifiers) == sizeof(AST), "ERROR: AST_Specifier is not the same size as AST"); + +struct AST_Struct +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + CodeAttributes Attributes; + char _PAD_SPECS_ [ sizeof(AST*) ]; + CodeType ParentType; + char _PAD_PARAMS_[ sizeof(AST*) ]; + CodeBody Body; + }; + }; + CodeType Last; + CodeType Next; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + AccessSpec ParentAccess; +}; +static_assert( sizeof(AST_Struct) == sizeof(AST), "ERROR: AST_Struct is not the same size as AST"); + +struct AST_Template +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + char _PAD_PROPERTIES_[ sizeof(AST*) * 3 ]; + CodeParam Params; + Code Declaration; + }; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Template) == sizeof(AST), "ERROR: AST_Template is not the same size as AST"); + +struct AST_Type +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + CodeAttributes Attributes; + CodeSpecifiers Specs; + char _PAD_PROPERTIES_[ sizeof(AST*) * 2 ]; + Code ArrExpr; + }; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Type) == sizeof(AST), "ERROR: AST_Type is not the same size as AST"); + +struct AST_Typedef +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + char _PAD_PROPERTIES_[ sizeof(AST*) * 2 ]; + Code UnderlyingType; + char _PAD_PROPERTIES_2_[ sizeof(AST*) * 2 ]; + }; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Typedef) == sizeof(AST), "ERROR: AST_Typedef is not the same size as AST"); + +struct AST_Union +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + CodeAttributes Attributes; + char _PAD_PROPERTIES_[ sizeof(AST*) * 3 ]; + CodeBody Body; + }; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Union) == sizeof(AST), "ERROR: AST_Union is not the same size as AST"); + +struct AST_Using +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + CodeAttributes Attributes; + char _PAD_SPECS_ [ sizeof(AST*) ]; + CodeType UnderlyingType; + char _PAD_PROPERTIES_[ sizeof(AST*) * 2 ]; + }; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Using) == sizeof(AST), "ERROR: AST_Using is not the same size as AST"); + +struct AST_Var +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; + struct + { + CodeAttributes Attributes; + CodeSpecifiers Specs; + CodeType ValueType; + char _PAD_PROPERTIES_[ sizeof(AST*) ]; + Code Value; + }; + }; + Code Prev; + Code Next; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Var) == sizeof(AST), "ERROR: AST_Var is not the same size as AST"); +#pragma endregion Filtered ASTs + diff --git a/project/components/gen.header_end.hpp b/project/components/gen.header_end.hpp new file mode 100644 index 0000000..d6d6276 --- /dev/null +++ b/project/components/gen.header_end.hpp @@ -0,0 +1,520 @@ +#pragma region Inlines + +void AST::append( AST* other ) +{ + if ( other->Parent ) + other = other->duplicate(); + + other->Parent = this; + + if ( Front == nullptr ) + { + Front = other; + Back = other; + + NumEntries++; + return; + } + + AST* + Current = Back; + Current->Next = other; + other->Prev = Current; + Back = other; + NumEntries++; +} + +char const* AST::debug_str() +{ + char const* fmt = stringize( + \nCode Debug: + \nType : %s + \nParent : %s + \nName : %s + \nComment : %s + ); + + // These should be used immediately in a log. + // Thus if its desired to keep the debug str + // for multiple calls to bprintf, + // allocate this to proper string. + return str_fmt_buf( fmt + , type_str() + , Parent ? Parent->Name : "" + , Name ? Name : "" + ); +} + +Code& AST::entry( u32 idx ) +{ + AST** current = & Front; + while ( idx >= 0 && current != nullptr ) + { + if ( idx == 0 ) + return * rcast( Code*, current); + + current = & ( * current )->Next; + idx--; + } + + return * rcast( Code*, current); +} + +bool AST::has_entries() +{ + return NumEntries; +} + +char const* AST::type_str() +{ + return ECode::to_str( Type ); +} + +AST::operator Code() +{ + return { this }; +} + +Code& Code::operator ++() +{ + if ( ast ) + ast = ast->Next; + + return *this; +} + +#pragma region AST & Code Gen Common +#define Define_CodeImpl( Typename ) \ +char const* Typename::debug_str() \ +{ \ + if ( ast == nullptr ) \ + return "Code::debug_str: AST is null!"; \ + \ + return rcast(AST*, ast)->debug_str(); \ +} \ +Code Typename::duplicate() \ +{ \ + if ( ast == nullptr ) \ + { \ + log_failure("Code::duplicate: Cannot duplicate code, AST is null!"); \ + return Code::Invalid; \ + } \ + \ + return { rcast(AST*, ast)->duplicate() }; \ +} \ +bool Typename::is_equal( Code other ) \ +{ \ + if ( ast == nullptr || other.ast == nullptr ) \ + { \ + log_failure("Code::is_equal: Cannot compare code, AST is null!"); \ + return false; \ + } \ + \ + return rcast(AST*, ast)->is_equal( other.ast ); \ +} \ +bool Typename::is_valid() \ +{ \ + return (AST*) ast != nullptr && rcast( AST*, ast)->Type != CodeT::Invalid; \ +} \ +void Typename::set_global() \ +{ \ + if ( ast == nullptr ) \ + { \ + log_failure("Code::set_global: Cannot set code as global, AST is null!"); \ + return; \ + } \ + \ + rcast(AST*, ast)->Parent = Code::Global.ast; \ +} \ +String Typename::to_string() \ +{ \ + if ( ast == nullptr ) \ + { \ + log_failure("Code::to_string: Cannot convert code to string, AST is null!"); \ + return { nullptr }; \ + } \ + \ + return rcast(AST*, ast)->to_string(); \ +} \ +Typename& Typename::operator =( Code other ) \ +{ \ + if ( other.ast && other->Parent ) \ + { \ + ast = rcast( decltype(ast), other.ast->duplicate() ); \ + rcast( AST*, ast)->Parent = nullptr; \ + } \ + \ + ast = rcast( decltype(ast), other.ast ); \ + return *this; \ +} \ +bool Typename::operator ==( Code other ) \ +{ \ + return (AST*) ast == other.ast; \ +} \ +bool Typename::operator !=( Code other ) \ +{ \ + return (AST*) ast != other.ast; \ +} + +Define_CodeImpl( Code ); +Define_CodeImpl( CodeBody ); +Define_CodeImpl( CodeAttributes ); +Define_CodeImpl( CodeComment ); +Define_CodeImpl( CodeClass ); +Define_CodeImpl( CodeEnum ); +Define_CodeImpl( CodeExec ); +Define_CodeImpl( CodeExtern ); +Define_CodeImpl( CodeInclude ); +Define_CodeImpl( CodeFriend ); +Define_CodeImpl( CodeFn ); +Define_CodeImpl( CodeModule ); +Define_CodeImpl( CodeNamespace ); +Define_CodeImpl( CodeOperator ); +Define_CodeImpl( CodeOpCast ); +Define_CodeImpl( CodeParam ); +Define_CodeImpl( CodeSpecifiers ); +Define_CodeImpl( CodeStruct ); +Define_CodeImpl( CodeTemplate ); +Define_CodeImpl( CodeType ); +Define_CodeImpl( CodeTypedef ); +Define_CodeImpl( CodeUnion ); +Define_CodeImpl( CodeUsing ); +Define_CodeImpl( CodeVar ); +#undef Define_CodeImpl + +#define Define_AST_Cast( typename ) \ +AST::operator Code ## typename() \ +{ \ + return { rcast( AST_ ## typename*, this ) }; \ +} + +Define_AST_Cast( Body ); +Define_AST_Cast( Attributes ); +Define_AST_Cast( Comment ); +Define_AST_Cast( Class ); +Define_AST_Cast( Enum ); +Define_AST_Cast( Exec ); +Define_AST_Cast( Extern ); +Define_AST_Cast( Include ); +Define_AST_Cast( Friend ); +Define_AST_Cast( Fn ); +Define_AST_Cast( Module ); +Define_AST_Cast( Namespace ); +Define_AST_Cast( Operator ); +Define_AST_Cast( OpCast ); +Define_AST_Cast( Param ); +Define_AST_Cast( Struct ); +Define_AST_Cast( Specifiers ); +Define_AST_Cast( Template ); +Define_AST_Cast( Type ); +Define_AST_Cast( Typedef ); +Define_AST_Cast( Union ); +Define_AST_Cast( Using ); +Define_AST_Cast( Var ); +#undef Define_AST_Cast + +#define Define_CodeCast( type ) \ +Code::operator Code ## type() const \ +{ \ + return { (AST_ ## type*) ast }; \ +} + +Define_CodeCast( Attributes ); +Define_CodeCast( Comment ); +Define_CodeCast( Class ); +Define_CodeCast( Exec ); +Define_CodeCast( Enum ); +Define_CodeCast( Extern ); +Define_CodeCast( Include ); +Define_CodeCast( Friend ); +Define_CodeCast( Fn ); +Define_CodeCast( Module ); +Define_CodeCast( Namespace ); +Define_CodeCast( Operator ); +Define_CodeCast( OpCast ); +Define_CodeCast( Param ); +Define_CodeCast( Specifiers ); +Define_CodeCast( Struct ); +Define_CodeCast( Template ); +Define_CodeCast( Type ); +Define_CodeCast( Typedef ); +Define_CodeCast( Union ); +Define_CodeCast( Using ); +Define_CodeCast( Var ); +Define_CodeCast( Body); +#undef Define_CodeCast +#pragma endregion AST & Code Gen Common + +void CodeClass::add_interface( CodeType type ) +{ + if ( ! ast->Next ) + { + ast->Next = type; + ast->Last = ast->Next; + return; + } + + ast->Next->Next = type; + ast->Last = ast->Next->Next; +} + +void CodeParam::append( CodeParam other ) +{ + AST* self = (AST*) ast; + AST* entry = (AST*) other.ast; + + if ( entry->Parent ) + entry = entry->duplicate(); + + entry->Parent = self; + + if ( self->Last == nullptr ) + { + self->Last = entry; + self->Next = entry; + self->NumEntries++; + return; + } + + self->Last->Next = entry; + self->Last = entry; + self->NumEntries++; +} + +CodeParam CodeParam::get( s32 idx ) +{ + CodeParam param = *this; + do + { + if ( ! ++ param ) + return { nullptr }; + + return { (AST_Param*) param.raw()->Next }; + } + while ( --idx ); + + return { nullptr }; +} + +bool CodeParam::has_entries() +{ + return ast->NumEntries > 0; +} + +CodeParam& CodeParam::operator ++() +{ + ast = ast->Next.ast; + return * this; +} + +void CodeStruct::add_interface( CodeType type ) +{ + if ( ! ast->Next ) + { + ast->Next = type; + ast->Last = ast->Next; + } + + ast->Next->Next = type; + ast->Last = ast->Next->Next; +} + +CodeBody 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 (CodeBody)Code::Invalid; + } + + Code + result = make_code(); + result->Type = type; + return (CodeBody)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. +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 + +#pragma region Constants +#ifdef GEN_DEFINE_LIBRARY_CODE_CONSTANTS + // Predefined typename codes. Are set to readonly and are setup during gen::init() + + extern CodeType t_b32; + + extern CodeType t_s8; + extern CodeType t_s16; + extern CodeType t_s32; + extern CodeType t_s64; + + extern CodeType t_u8; + extern CodeType t_u16; + extern CodeType t_u32; + extern CodeType t_u64; + + extern CodeType t_sw; + extern CodeType t_uw; + + extern CodeType t_f32; + extern CodeType t_f64; +#endif + +#ifndef GEN_GLOBAL_BUCKET_SIZE +# define GEN_GLOBAL_BUCKET_SIZE megabytes(10) +#endif +#ifndef GEN_CODEPOOL_NUM_BLOCKS +# define GEN_CODEPOOL_NUM_BLOCKS kilobytes(64) +#endif +#ifndef GEN_SIZE_PER_STRING_ARENA +# define GEN_SIZE_PER_STRING_ARENA megabytes(1) +#endif +#ifndef GEN_MAX_COMMENT_LINE_LENGTH +# define GEN_MAX_COMMENT_LINE_LENGTH 1024 +#endif +#ifndef GEN_MAX_NAME_LENGTH +# define GEN_MAX_NAME_LENGTH 128 +#endif +#ifndef GEN_MAX_UNTYPED_STR_LENGTH +# define GEN_MAX_UNTYPED_STR_LENGTH kilobytes(640) +#endif +#ifndef GEN_TOKEN_FMT_TOKEN_MAP_MEM_SIZE +# define GEN_TOKEN_FMT_TOKEN_MAP_MEM_SIZE kilobytes(4) +#endif +#ifndef GEN_LEX_ALLOCATOR_SIZE +# define GEN_LEX_ALLOCATOR_SIZE megabytes(10) +#endif +#ifndef GEN_BUILDER_STR_BUFFER_RESERVE +# define GEN_BUILDER_STR_BUFFER_RESERVE megabytes(1) +#endif + +// These constexprs are used for allocation behavior of data structures +// or string handling while constructing or serializing. +// Change them to suit your needs. + +constexpr s32 InitSize_DataArrays = 16; + +// NOTE: This limits the maximum size of an allocation +// If you are generating a string larger than this, increase the size of the bucket here. +constexpr uw Global_BucketSize = GEN_GLOBAL_BUCKET_SIZE; +constexpr s32 CodePool_NumBlocks = GEN_CODEPOOL_NUM_BLOCKS; +constexpr s32 SizePer_StringArena = GEN_SIZE_PER_STRING_ARENA; + +constexpr s32 MaxCommentLineLength = GEN_MAX_COMMENT_LINE_LENGTH; +constexpr s32 MaxNameLength = GEN_MAX_NAME_LENGTH; +constexpr s32 MaxUntypedStrLength = GEN_MAX_UNTYPED_STR_LENGTH; +constexpr s32 TokenFmt_TokenMap_MemSize = GEN_TOKEN_FMT_TOKEN_MAP_MEM_SIZE; +constexpr s32 LexAllocator_Size = GEN_LEX_ALLOCATOR_SIZE; +constexpr s32 Builder_StrBufferReserve = GEN_BUILDER_STR_BUFFER_RESERVE; + +extern CodeType t_empty; // Used with varaidc parameters. (Exposing just in case its useful for another circumstance) +extern CodeType t_auto; +extern CodeType t_void; +extern CodeType t_int; +extern CodeType t_bool; +extern CodeType t_char; +extern CodeType t_wchar_t; +extern CodeType t_class; +extern CodeType t_typename; + +extern CodeParam param_varadic; + +extern CodeAttributes attrib_api_export; +extern CodeAttributes attrib_api_import; + +extern Code access_public; +extern Code access_protected; +extern Code access_private; + +extern Code module_global_fragment; +extern Code module_private_fragment; + +extern Code pragma_once; + +extern CodeSpecifiers spec_const; +extern CodeSpecifiers spec_consteval; +extern CodeSpecifiers spec_constexpr; +extern CodeSpecifiers spec_constinit; +extern CodeSpecifiers spec_extern_linkage; +extern CodeSpecifiers spec_final; +extern CodeSpecifiers spec_global; +extern CodeSpecifiers spec_inline; +extern CodeSpecifiers spec_internal_linkage; +extern CodeSpecifiers spec_local_persist; +extern CodeSpecifiers spec_mutable; +extern CodeSpecifiers spec_override; +extern CodeSpecifiers spec_ptr; +extern CodeSpecifiers spec_ref; +extern CodeSpecifiers spec_register; +extern CodeSpecifiers spec_rvalue; +extern CodeSpecifiers spec_static_member; +extern CodeSpecifiers spec_thread_local; +extern CodeSpecifiers spec_virtual; +extern CodeSpecifiers spec_volatile; +#pragma endregion Constants + +#pragma region Macros +# define gen_main main + +# define __ NoCode + + // Convienence for defining any name used with the gen api. + // Lets you provide the length and string literal to the functions without the need for the DSL. +# define name( Id_ ) { sizeof(stringize( Id_ )) - 1, stringize(Id_) } + + // Same as name just used to indicate intention of literal for code instead of names. +# define code( ... ) { sizeof(stringize(__VA_ARGS__)) - 1, stringize( __VA_ARGS__ ) } + +# define args( ... ) num_args( __VA_ARGS__ ), __VA_ARGS__ + +# define code_str( ... ) gen::untyped_str( code( __VA_ARGS__ ) ) +# define code_fmt( ... ) gen::untyped_str( token_fmt( __VA_ARGS__ ) ) + + // Takes a format string (char const*) and a list of tokens (StrC) and returns a StrC of the formatted string. +# define token_fmt( ... ) gen::token_fmt_impl( (num_args( __VA_ARGS__ ) + 1) / 2, __VA_ARGS__ ) +#pragma endregion Macros + +#ifdef GEN_EXPOSE_BACKEND + // Global allocator used for data with process lifetime. + extern AllocatorInfo GlobalAllocator; + extern Array< Arena > Global_AllocatorBuckets; + extern Array< Pool > CodePools; + extern Array< Arena > StringArenas; + + extern StringTable StringCache; + + extern Arena LexArena; + + extern AllocatorInfo Allocator_DataArrays; + extern AllocatorInfo Allocator_CodePool; + extern AllocatorInfo Allocator_Lexer; + extern AllocatorInfo Allocator_StringArena; + extern AllocatorInfo Allocator_StringTable; + extern AllocatorInfo Allocator_TypeTable; +#endif diff --git a/project/components/gen.interface.cpp b/project/components/gen.interface.cpp new file mode 100644 index 0000000..d48e0c1 --- /dev/null +++ b/project/components/gen.interface.cpp @@ -0,0 +1,426 @@ +internal +void* Global_Allocator_Proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ) +{ + Arena* last = & Global_AllocatorBuckets.back(); + + switch ( type ) + { + case EAllocation_ALLOC: + { + if ( ( last->TotalUsed + size ) > last->TotalSize ) + { + Arena bucket = Arena::init_from_allocator( heap(), Global_BucketSize ); + + if ( bucket.PhysicalStart == nullptr ) + fatal( "Failed to create bucket for Global_AllocatorBuckets"); + + if ( ! Global_AllocatorBuckets.append( bucket ) ) + fatal( "Failed to append bucket to Global_AllocatorBuckets"); + + last = & Global_AllocatorBuckets.back(); + } + + return alloc_align( * last, size, alignment ); + } + case EAllocation_FREE: + { + // Doesn't recycle. + } + break; + case EAllocation_FREE_ALL: + { + // Memory::cleanup instead. + } + break; + case EAllocation_RESIZE: + { + if ( last->TotalUsed + size > last->TotalSize ) + { + Arena bucket = Arena::init_from_allocator( heap(), Global_BucketSize ); + + if ( bucket.PhysicalStart == nullptr ) + fatal( "Failed to create bucket for Global_AllocatorBuckets"); + + if ( ! Global_AllocatorBuckets.append( bucket ) ) + fatal( "Failed to append bucket to Global_AllocatorBuckets"); + + last = & Global_AllocatorBuckets.back(); + } + + void* result = alloc_align( last->Backing, size, alignment ); + + if ( result != nullptr && old_memory != nullptr ) + { + mem_copy( result, old_memory, old_size ); + } + + return result; + } + } + + return nullptr; +} + +internal + void define_constants() +{ + Code::Global = make_code(); + Code::Global->Name = get_cached_string( txt_StrC("Global Code") ); + Code::Global->Content = Code::Global->Name; + + Code::Invalid = make_code(); + Code::Invalid.set_global(); + +# define def_constant_code_type( Type_ ) \ + t_##Type_ = def_type( name(Type_) ); \ + t_##Type_.set_global(); + + def_constant_code_type( auto ); + def_constant_code_type( void ); + def_constant_code_type( int ); + 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) ); + + def_constant_code_type( s8 ); + def_constant_code_type( s16 ); + def_constant_code_type( s32 ); + def_constant_code_type( s64 ); + + def_constant_code_type( u8 ); + def_constant_code_type( u16 ); + def_constant_code_type( u32 ); + def_constant_code_type( u64 ); + + def_constant_code_type( sw ); + def_constant_code_type( uw ); + + def_constant_code_type( f32 ); + def_constant_code_type( f64 ); +#endif +# undef def_constant_code_type + + t_empty = (CodeType) make_code(); + t_empty->Type = ECode::Typename; + t_empty->Name = get_cached_string( txt_StrC("") ); + t_empty.set_global(); + + param_varadic = (CodeType) make_code(); + param_varadic->Type = ECode::Parameters; + param_varadic->Name = get_cached_string( txt_StrC("...") ); + param_varadic->ValueType = t_empty; + param_varadic.set_global(); + + attrib_api_export = def_attributes( code(GEN_API_Export_Code)); + attrib_api_export.set_global(); + + attrib_api_import = def_attributes( code(GEN_API_Import_Code)); + attrib_api_import.set_global(); + + access_private = make_code(); + access_private->Type = ECode::Access_Private; + access_private->Name = get_cached_string( txt_StrC("private:") ); + access_private.set_global(); + + access_protected = make_code(); + access_protected->Type = ECode::Access_Protected; + access_protected->Name = get_cached_string( txt_StrC("protected:") ); + access_protected.set_global(); + + access_public = make_code(); + access_public->Type = ECode::Access_Public; + access_public->Name = get_cached_string( txt_StrC("public:") ); + access_public.set_global(); + + module_global_fragment = make_code(); + module_global_fragment->Type = ECode::Untyped; + module_global_fragment->Name = get_cached_string( txt_StrC("module;") ); + module_global_fragment->Content = module_global_fragment->Name; + module_global_fragment.set_global(); + + module_private_fragment = make_code(); + module_private_fragment->Type = ECode::Untyped; + module_private_fragment->Name = get_cached_string( txt_StrC("module : private;") ); + module_private_fragment->Content = module_private_fragment->Name; + module_private_fragment.set_global(); + + pragma_once = make_code(); + pragma_once->Type = ECode::Untyped; + pragma_once->Name = get_cached_string( txt_StrC("#pragma once") ); + pragma_once->Content = pragma_once->Name; + pragma_once.set_global(); + +# pragma push_macro( "global" ) +# pragma push_macro( "internal" ) +# pragma push_macro( "local_persist" ) +# undef global +# undef internal +# undef local_persist + +# define def_constant_spec( Type_, ... ) \ + spec_##Type_ = def_specifiers( num_args(__VA_ARGS__), __VA_ARGS__); \ + spec_##Type_.set_global(); + + def_constant_spec( const, ESpecifier::Const ); + def_constant_spec( consteval, ESpecifier::Consteval ); + def_constant_spec( constexpr, ESpecifier::Constexpr ); + def_constant_spec( constinit, ESpecifier::Constinit ); + def_constant_spec( extern_linkage, ESpecifier::External_Linkage ); + def_constant_spec( final, ESpecifier::Final ); + def_constant_spec( global, ESpecifier::Global ); + def_constant_spec( inline, ESpecifier::Inline ); + def_constant_spec( internal_linkage, ESpecifier::Internal_Linkage ); + def_constant_spec( local_persist, ESpecifier::Local_Persist ); + def_constant_spec( mutable, ESpecifier::Mutable ); + def_constant_spec( override, ESpecifier::Override ); + def_constant_spec( ptr, ESpecifier::Ptr ); + def_constant_spec( ref, ESpecifier::Ref ); + def_constant_spec( register, ESpecifier::Register ); + def_constant_spec( rvalue, ESpecifier::RValue ); + def_constant_spec( static_member, ESpecifier::Static ); + def_constant_spec( thread_local, ESpecifier::Thread_Local ); + def_constant_spec( virtual, ESpecifier::Virtual ); + def_constant_spec( volatile, ESpecifier::Volatile) + + spec_local_persist = def_specifiers( 1, ESpecifier::Local_Persist ); + spec_local_persist.set_global(); + +# pragma pop_macro( "global" ) +# pragma pop_macro( "internal" ) +# pragma pop_macro( "local_persist" ) + +# undef def_constant_spec +} + +void init() +{ + // Setup global allocator + { + GlobalAllocator = AllocatorInfo { & Global_Allocator_Proc, nullptr }; + + Global_AllocatorBuckets = Array::init_reserve( heap(), 128 ); + + if ( Global_AllocatorBuckets == nullptr ) + fatal( "Failed to reserve memory for Global_AllocatorBuckets"); + + Arena bucket = Arena::init_from_allocator( heap(), Global_BucketSize ); + + if ( bucket.PhysicalStart == nullptr ) + fatal( "Failed to create first bucket for Global_AllocatorBuckets"); + + Global_AllocatorBuckets.append( bucket ); + + } + + // Setup the arrays + { + CodePools = Array::init_reserve( Allocator_DataArrays, InitSize_DataArrays ); + + if ( CodePools == nullptr ) + fatal( "gen::init: Failed to initialize the CodePools array" ); + + StringArenas = Array::init_reserve( Allocator_DataArrays, InitSize_DataArrays ); + + if ( StringArenas == nullptr ) + fatal( "gen::init: Failed to initialize the StringArenas array" ); + } + + // Setup the code pool and code entries arena. + { + Pool code_pool = Pool::init( Allocator_CodePool, CodePool_NumBlocks, sizeof(AST) ); + + if ( code_pool.PhysicalStart == nullptr ) + fatal( "gen::init: Failed to initialize the code pool" ); + + CodePools.append( code_pool ); + + LexArena = Arena::init_from_allocator( Allocator_Lexer, LexAllocator_Size ); + + Arena string_arena = Arena::init_from_allocator( Allocator_StringArena, SizePer_StringArena ); + + if ( string_arena.PhysicalStart == nullptr ) + fatal( "gen::init: Failed to initialize the string arena" ); + + StringArenas.append( string_arena ); + } + + // Setup the hash tables + { + StringCache = StringTable::init( Allocator_StringTable ); + + if ( StringCache.Entries == nullptr ) + fatal( "gen::init: Failed to initialize the StringCache"); + } + + define_constants(); +} + +void deinit() +{ + uw index = 0; + uw left = CodePools.num(); + do + { + Pool* code_pool = & CodePools[index]; + code_pool->free(); + index++; + } + while ( left--, left ); + + index = 0; + left = StringArenas.num(); + do + { + Arena* string_arena = & StringArenas[index]; + string_arena->free(); + index++; + } + while ( left--, left ); + + StringCache.destroy(); + + CodePools.free(); + StringArenas.free(); + + LexArena.free(); + + index = 0; + left = Global_AllocatorBuckets.num(); + do + { + Arena* bucket = & Global_AllocatorBuckets[ index ]; + bucket->free(); + index++; + } + while ( left--, left ); + + Global_AllocatorBuckets.free(); +} + +void reset() +{ + s32 index = 0; + s32 left = CodePools.num(); + do + { + Pool* code_pool = & CodePools[index]; + code_pool->clear(); + index++; + } + while ( left--, left ); + + index = 0; + left = StringArenas.num(); + do + { + Arena* string_arena = & StringArenas[index]; + string_arena->TotalUsed = 0;; + index++; + } + while ( left--, left ); + + StringCache.clear(); + + define_constants(); +} + +AllocatorInfo get_string_allocator( s32 str_length ) +{ + Arena* last = & StringArenas.back(); + + uw size_req = str_length + sizeof(String::Header) + sizeof(char*); + + if ( last->TotalUsed + size_req > last->TotalSize ) + { + Arena new_arena = Arena::init_from_allocator( Allocator_StringArena, SizePer_StringArena ); + + if ( ! StringArenas.append( new_arena ) ) + fatal( "gen::get_string_allocator: Failed to allocate a new string arena" ); + + last = & StringArenas.back(); + } + + return * last; +} + +// Will either make or retrive a code string. +StringCached get_cached_string( StrC str ) +{ + s32 hash_length = str.Len > kilobytes(1) ? kilobytes(1) : str.Len; + u64 key = crc32( str.Ptr, hash_length ); + { + StringCached* result = StringCache.get( key ); + + if ( result ) + return * result; + } + + String result = String::make( get_string_allocator( str.Len ), str ); + + StringCache.set( key, result ); + + return result; +} + +/* + Used internally to retireve a Code object form the CodePool. + */ +Code make_code() +{ + Pool* allocator = & CodePools.back(); + if ( allocator->FreeList == nullptr ) + { + Pool code_pool = Pool::init( Allocator_CodePool, CodePool_NumBlocks, sizeof(AST) ); + + if ( code_pool.PhysicalStart == nullptr ) + fatal( "gen::make_code: Failed to allocate a new code pool - CodePool allcoator returned nullptr." ); + + if ( ! CodePools.append( code_pool ) ) + fatal( "gen::make_code: Failed to allocate a new code pool - CodePools failed to append new pool." ); + + allocator = & CodePools.back(); + } + + Code result { rcast( AST*, alloc( * allocator, sizeof(AST) )) }; + + result->Content = { nullptr }; + result->Prev = { nullptr }; + result->Next = { nullptr }; + result->Parent = { nullptr }; + result->Name = { nullptr }; + result->Type = ECode::Invalid; + result->ModuleFlags = ModuleFlag::Invalid; + result->NumEntries = 0; + + return result; +} + +void set_allocator_data_arrays( AllocatorInfo allocator ) +{ + Allocator_DataArrays = allocator; +} + +void set_allocator_code_pool( AllocatorInfo allocator ) +{ + Allocator_CodePool = allocator; +} + +void set_allocator_lexer( AllocatorInfo allocator ) +{ + Allocator_Lexer = allocator; +} + +void set_allocator_string_arena( AllocatorInfo allocator ) +{ + Allocator_StringArena = allocator; +} + +void set_allocator_string_table( AllocatorInfo allocator ) +{ + Allocator_StringArena = allocator; +} diff --git a/project/components/gen.interface.hpp b/project/components/gen.interface.hpp index e69de29..823ad37 100644 --- a/project/components/gen.interface.hpp +++ b/project/components/gen.interface.hpp @@ -0,0 +1,155 @@ +// Initialize the library. +// This currently just initializes the CodePool. +void init(); + +// Currently manually free's the arenas, code for checking for leaks. +// However on Windows at least, it doesn't need to occur as the OS will clean up after the process. +void deinit(); + +// Clears the allocations, but doesn't return to the heap, the calls init() again. +// Ease of use. +void reset(); + +// Used internally to retrive or make string allocations. +// Strings are stored in a series of string arenas of fixed size (SizePer_StringArena) +StringCached get_cached_string( StrC str ); + +/* + This provides a fresh Code AST. + The gen interface use this as their method from getting a new AST object from the CodePool. + Use this if you want to make your own API for formatting the supported Code Types. +*/ +Code make_code(); + +// Set these before calling gen's init() procedure. +// Data + +void set_allocator_data_arrays ( AllocatorInfo data_array_allocator ); +void set_allocator_code_pool ( AllocatorInfo pool_allocator ); +void set_allocator_lexer ( AllocatorInfo lex_allocator ); +void set_allocator_string_arena ( AllocatorInfo string_allocator ); +void set_allocator_string_table ( AllocatorInfo string_allocator ); +void set_allocator_type_table ( AllocatorInfo type_reg_allocator ); + +#pragma region Upfront +CodeAttributes def_attributes( StrC content ); +CodeComment def_comment ( StrC content ); + +CodeClass def_class( StrC name + , Code body = NoCode + , CodeType parent = NoCode, AccessSpec access = AccessSpec::Default + , CodeAttributes attributes = NoCode + , ModuleFlag mflags = ModuleFlag::None + , CodeType* interfaces = nullptr, s32 num_interfaces = 0 ); + +CodeEnum def_enum( StrC name + , Code body = NoCode, CodeType type = NoCode + , EnumT specifier = EnumRegular, CodeAttributes attributes = NoCode + , ModuleFlag mflags = ModuleFlag::None ); + +CodeExec def_execution ( StrC content ); +CodeExtern def_extern_link( StrC name, Code body ); +CodeFriend def_friend ( Code symbol ); + +CodeFn def_function( StrC name + , CodeParam params = NoCode, CodeType ret_type = NoCode, Code body = NoCode + , CodeSpecifiers specifiers = NoCode, CodeAttributes attributes = NoCode + , ModuleFlag mflags = ModuleFlag::None ); + +CodeInclude def_include ( StrC content ); +CodeModule def_module ( StrC name, ModuleFlag mflags = ModuleFlag::None ); +CodeNamespace def_namespace( StrC name, Code body, ModuleFlag mflags = ModuleFlag::None ); + +CodeOperator def_operator( OperatorT op + , CodeParam params = NoCode, CodeType ret_type = NoCode, Code body = NoCode + , CodeSpecifiers specifiers = NoCode, CodeAttributes attributes = NoCode + , ModuleFlag mflags = ModuleFlag::None ); + +CodeOpCast def_operator_cast( CodeType type, Code body = NoCode, CodeSpecifiers specs = NoCode ); + +CodeParam def_param ( CodeType type, StrC name, Code value = NoCode ); +CodeSpecifiers def_specifier( SpecifierT specifier ); + +CodeStruct def_struct( StrC name + , Code body = NoCode + , CodeType parent = NoCode, AccessSpec access = AccessSpec::Default + , CodeAttributes attributes = NoCode + , ModuleFlag mflags = ModuleFlag::None + , CodeType* interfaces = nullptr, s32 num_interfaces = 0 ); + +CodeTemplate def_template( CodeParam params, Code definition, ModuleFlag mflags = ModuleFlag::None ); + +CodeType def_type ( StrC name, Code arrayexpr = NoCode, CodeSpecifiers specifiers = NoCode, CodeAttributes attributes = NoCode ); +CodeTypedef def_typedef( StrC name, Code type, CodeAttributes attributes = NoCode, ModuleFlag mflags = ModuleFlag::None ); + +CodeUnion def_union( StrC name, Code body, CodeAttributes attributes = NoCode, ModuleFlag mflags = ModuleFlag::None ); + +CodeUsing def_using( StrC name, CodeType type = NoCode + , CodeAttributes attributess = NoCode + , ModuleFlag mflags = ModuleFlag::None ); + +CodeUsing def_using_namespace( StrC name ); + +CodeVar def_variable( CodeType type, StrC name, Code value = NoCode + , CodeSpecifiers specifiers = NoCode, CodeAttributes attributes = NoCode + , ModuleFlag mflags = ModuleFlag::None ); + +// Constructs an empty body. Use AST::validate_body() to check if the body is was has valid entries. +CodeBody def_body( CodeT type ); + +// 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. + +CodeBody def_class_body ( s32 num, ... ); +CodeBody def_class_body ( s32 num, Code* codes ); +CodeBody def_enum_body ( s32 num, ... ); +CodeBody def_enum_body ( s32 num, Code* codes ); +CodeBody def_export_body ( s32 num, ... ); +CodeBody def_export_body ( s32 num, Code* codes); +CodeBody def_extern_link_body( s32 num, ... ); +CodeBody def_extern_link_body( s32 num, Code* codes ); +CodeBody def_function_body ( s32 num, ... ); +CodeBody def_function_body ( s32 num, Code* codes ); +CodeBody def_global_body ( s32 num, ... ); +CodeBody def_global_body ( s32 num, Code* codes ); +CodeBody def_namespace_body ( s32 num, ... ); +CodeBody def_namespace_body ( s32 num, Code* codes ); +CodeParam def_params ( s32 num, ... ); +CodeParam def_params ( s32 num, CodeParam* params ); +CodeSpecifiers def_specifiers ( s32 num, ... ); +CodeSpecifiers def_specifiers ( s32 num, SpecifierT* specs ); +CodeBody def_struct_body ( s32 num, ... ); +CodeBody def_struct_body ( s32 num, Code* codes ); +CodeBody def_union_body ( s32 num, ... ); +CodeBody def_union_body ( s32 num, Code* codes ); +#pragma endregion Upfront + +#pragma region Parsing +CodeClass parse_class ( StrC class_def ); +CodeEnum parse_enum ( StrC enum_def ); +CodeBody parse_export_body ( StrC export_def ); +CodeExtern parse_extern_link ( StrC exten_link_def); +CodeFriend parse_friend ( StrC friend_def ); +CodeFn parse_function ( StrC fn_def ); +CodeBody parse_global_body ( StrC body_def ); +CodeNamespace parse_namespace ( StrC namespace_def ); +CodeOperator parse_operator ( StrC operator_def ); +CodeOpCast parse_operator_cast( StrC operator_def ); +CodeStruct parse_struct ( StrC struct_def ); +CodeTemplate parse_template ( StrC template_def ); +CodeType parse_type ( StrC type_def ); +CodeTypedef parse_typedef ( StrC typedef_def ); +CodeUnion parse_union ( StrC union_def ); +CodeUsing parse_using ( StrC using_def ); +CodeVar parse_variable ( StrC var_def ); +#pragma endregion Parsing + +#pragma region Untyped text +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, ... ); +Code untyped_token_fmt( char const* fmt, s32 num_tokens, ... ); +#pragma endregion Untyped text + diff --git a/project/components/gen.interface.parsing.cpp b/project/components/gen.interface.parsing.cpp index e69de29..a8b0959 100644 --- a/project/components/gen.interface.parsing.cpp +++ b/project/components/gen.interface.parsing.cpp @@ -0,0 +1,3205 @@ +/* +These constructors are the most implementation intensive other than the editor or scanner. +*/ + +namespace Parser +{ +/* + This is a simple lexer that focuses on tokenizing only tokens relevant to the library. + It will not be capable of lexing C++ code with unsupported features. + + For the sake of scanning files, it can scan preprocessor directives + + Attributes_Start is only used to indicate the start of the user_defined attribute list. +*/ + +# define Define_TokType \ + Entry( Access_Private, "private" ) \ + Entry( Access_Protected, "protected" ) \ + Entry( Access_Public, "public" ) \ + Entry( Access_MemberSymbol, "." ) \ + Entry( Access_StaticSymbol, "::") \ + Entry( Ampersand, "&" ) \ + Entry( Ampersand_DBL, "&&" ) \ + Entry( Assign_Classifer, ":" ) \ + Entry( Attribute_Open, "[[" ) \ + Entry( Attribute_Close, "]]" ) \ + Entry( BraceCurly_Open, "{" ) \ + Entry( BraceCurly_Close, "}" ) \ + Entry( BraceSquare_Open, "[" ) \ + Entry( BraceSquare_Close, "]" ) \ + Entry( Capture_Start, "(" ) \ + Entry( Capture_End, ")" ) \ + Entry( Comment, "__comment__" ) \ + Entry( Char, "__char__" ) \ + Entry( Comma, "," ) \ + Entry( Decl_Class, "class" ) \ + Entry( Decl_GNU_Attribute, "__attribute__" ) \ + Entry( Decl_MSVC_Attribute, "__declspec" ) \ + Entry( Decl_Enum, "enum" ) \ + Entry( Decl_Extern_Linkage, "extern" ) \ + Entry( Decl_Friend, "friend" ) \ + Entry( Decl_Module, "module" ) \ + Entry( Decl_Namespace, "namespace" ) \ + Entry( Decl_Operator, "operator" ) \ + Entry( Decl_Struct, "struct" ) \ + Entry( Decl_Template, "template" ) \ + Entry( Decl_Typedef, "typedef" ) \ + Entry( Decl_Using, "using" ) \ + Entry( Decl_Union, "union" ) \ + Entry( Identifier, "__identifier__" ) \ + Entry( Module_Import, "import" ) \ + Entry( Module_Export, "export" ) \ + Entry( Number, "number" ) \ + Entry( Operator, "operator" ) \ + Entry( Preprocessor_Directive, "#") \ + Entry( Preprocessor_Include, "include" ) \ + Entry( Spec_Alignas, "alignas" ) \ + Entry( Spec_Const, "const" ) \ + Entry( Spec_Consteval, "consteval" ) \ + Entry( Spec_Constexpr, "constexpr" ) \ + Entry( Spec_Constinit, "constinit" ) \ + Entry( Spec_Explicit, "explicit" ) \ + Entry( Spec_Extern, "extern" ) \ + Entry( Spec_Final, "final" ) \ + Entry( Spec_Global, "global" ) \ + Entry( Spec_Inline, "inline" ) \ + Entry( Spec_Internal_Linkage, "internal" ) \ + Entry( Spec_LocalPersist, "local_persist" ) \ + Entry( Spec_Mutable, "mutable" ) \ + Entry( Spec_Override, "override" ) \ + Entry( Spec_Static, "static" ) \ + Entry( Spec_ThreadLocal, "thread_local" ) \ + Entry( Spec_Volatile, "volatile") \ + Entry( Star, "*" ) \ + Entry( Statement_End, ";" ) \ + Entry( String, "__string__" ) \ + Entry( Type_Unsigned, "unsigned" ) \ + Entry( Type_Signed, "signed" ) \ + Entry( Type_Short, "short" ) \ + Entry( Type_Long, "long" ) \ + Entry( Type_char, "char" ) \ + Entry( Type_int, "int" ) \ + Entry( Type_double, "double" ) \ + Entry( Varadic_Argument, "..." ) \ + Entry( Attributes_Start, "__attrib_start__" ) + + enum class TokType : u32 + { + # define Entry( Name_, Str_ ) Name_, + Define_TokType + GEN_Define_Attribute_Tokens + # undef Entry + Num, + Invalid + }; + + struct Token + { + char const* Text; + sptr Length; + TokType Type; + bool IsAssign; + + operator bool() + { + return Text && Length && Type != TokType::Invalid; + } + + operator StrC() + { + return { Length, Text }; + } + }; + + internal inline + TokType get_tok_type( char const* word, s32 length ) + { + local_persist + StrC lookup[(u32)TokType::Num] = + { + # define Entry( Name_, Str_ ) { sizeof(Str_), Str_ }, + Define_TokType + GEN_Define_Attribute_Tokens + # undef Entry + }; + + for ( u32 index = 0; index < (u32)TokType::Num; index++ ) + { + s32 lookup_len = lookup[index].Len - 1; + char const* lookup_str = lookup[index].Ptr; + + if ( lookup_len != length ) + continue; + + if ( str_compare( word, lookup_str, lookup_len ) == 0 ) + return scast(TokType, index); + } + + return TokType::Invalid; + } + + internal inline + char const* str_tok_type( TokType type ) + { + local_persist + char const* lookup[(u32)TokType::Num] = + { + # define Entry( Name_, Str_ ) Str_, + Define_TokType + GEN_Define_Attribute_Tokens + # undef Entry + }; + + return lookup[(u32)type]; + } + +# undef Define_TokType + + internal inline + bool tok_is_specifier( Token const& tok ) + { + return (tok.Type <= TokType::Star && tok.Type >= TokType::Spec_Alignas) + || tok.Type == TokType::Ampersand + || tok.Type == TokType::Ampersand_DBL + ; + } + + internal inline + bool tok_is_access_specifier( Token const& tok ) + { + return tok.Type >= TokType::Access_Private && tok.Type <= TokType::Access_Public; + } + + internal inline + AccessSpec tok_to_access_specifier( Token const& tok ) + { + return scast(AccessSpec, tok.Type); + } + + internal inline + bool tok_is_attribute( Token const& tok ) + { + return tok.Type > TokType::Attributes_Start; + } + + struct TokArray + { + Array Arr; + s32 Idx; + + bool __eat( TokType type, char const* context ) + { + if ( Arr.num() - Idx <= 0 ) + { + log_failure( "gen::%s: No tokens left", context ); + return Code::Invalid; + } + + if ( Arr[Idx].Type != type ) + { + String token_str = String::make( GlobalAllocator, { Arr[Idx].Length, Arr[Idx].Text } ); + + log_failure( "gen::%s: expected %s, got %s", context, str_tok_type(type), str_tok_type(Arr[Idx].Type) ); + + return Code::Invalid; + } + + Idx++; + return true; + } + + Token& current() + { + return Arr[Idx]; + } + + Token& previous() + { + return Arr[Idx - 1]; + } + + Token* next() + { + return Idx + 1 < Arr.num() ? &Arr[Idx + 1] : nullptr; + } + }; + + TokArray lex( StrC content, bool keep_preprocess_directives = false ) + { + # define current ( * scanner ) + + # define move_forward() \ + left--; \ + scanner++ + + # define SkipWhitespace() \ + while ( left && char_is_space( current ) ) \ + { \ + move_forward(); \ + } + + # define SkipWhitespace_Checked( Context_, Msg_, ... ) \ + while ( left && char_is_space( current ) ) \ + { \ + move_forward(); \ + } \ + if ( left <= 0 ) \ + { \ + log_failure( "gen::" txt(Context_) ": " Msg_, __VA_ARGS__ ); \ + return { 0, nullptr }; \ + } + + local_persist thread_local + Array Tokens = { nullptr }; + + s32 left = content.Len; + char const* scanner = content.Ptr; + + char const* word = scanner; + s32 word_length = 0; + + SkipWhitespace(); + if ( left <= 0 ) + { + log_failure( "gen::lex: no tokens found (only whitespace provided)" ); + return { { nullptr }, 0 }; + } + + if ( Tokens ) + { + Tokens.free(); + } + + Tokens = Array::init_reserve( LexArena, content.Len / 6 ); + + while (left ) + { + Token token = { nullptr, 0, TokType::Invalid, false }; + + SkipWhitespace(); + if ( left <= 0 ) + break; + + switch ( current ) + { + case '#': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Preprocessor_Directive; + move_forward(); + + while (left && current != '\n' ) + { + if ( current == '\\' ) + { + move_forward(); + + if ( current != '\n' && keep_preprocess_directives ) + { + log_failure( "gen::lex: invalid preprocessor directive, will still grab but will not compile %s", token.Text ); + } + } + + move_forward(); + token.Length++; + } + goto FoundToken; + + case '.': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Access_MemberSymbol; + + if (left) + move_forward(); + + if ( current == '.' ) + { + move_forward(); + if( current == '.' ) + { + token.Length = 3; + token.Type = TokType::Varadic_Argument; + move_forward(); + } + else + { + log_failure( "gen::lex: invalid varadic argument, expected '...' got '..%c'", current ); + } + } + + goto FoundToken; + + case '&' : + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Ampersand; + + if (left) + move_forward(); + + if ( current == '&' ) // && + { + token.Length = 2; + token.Type = TokType::Ampersand_DBL; + + if (left) + move_forward(); + } + + goto FoundToken; + + case ':': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Assign_Classifer; + + if (left) + move_forward(); + + if ( current == ':' ) + { + move_forward(); + token.Type = TokType::Access_StaticSymbol; + token.Length++; + } + goto FoundToken; + + case '{': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::BraceCurly_Open; + + if (left) + move_forward(); + goto FoundToken; + + case '}': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::BraceCurly_Close; + + if (left) + move_forward(); + goto FoundToken; + + case '[': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::BraceSquare_Open; + if ( left ) + { + move_forward(); + + if ( current == ']' ) + { + token.Length = 2; + token.Type = TokType::Operator; + move_forward(); + } + } + goto FoundToken; + + case ']': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::BraceSquare_Close; + + if (left) + move_forward(); + goto FoundToken; + + case '(': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Capture_Start; + + if (left) + move_forward(); + goto FoundToken; + + case ')': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Capture_End; + + if (left) + move_forward(); + goto FoundToken; + + case '\'': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Char; + + move_forward(); + + while ( left && current != '\'' ) + { + move_forward(); + token.Length++; + } + + if ( left ) + { + move_forward(); + token.Length++; + } + goto FoundToken; + + case ',': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Comma; + + if (left) + move_forward(); + goto FoundToken; + + case '*': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Star; + + if (left) + move_forward(); + goto FoundToken; + + case ';': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Statement_End; + + if (left) + move_forward(); + goto FoundToken; + + case '"': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::String; + + move_forward(); + while ( left ) + { + if ( current == '"' ) + { + move_forward(); + break; + } + + if ( current == '\\' ) + { + move_forward(); + token.Length++; + + if ( left ) + { + move_forward(); + token.Length++; + } + continue; + } + + move_forward(); + token.Length++; + } + goto FoundToken; + + // All other operators we just label as an operator and move forward. + case '=': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Operator; + token.IsAssign = true; + + if (left) + move_forward(); + + goto FoundToken; + + case '+': + case '%': + case '^': + case '~': + case '!': + case '<': + case '>': + case '|': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Operator; + + if (left) + move_forward(); + + if ( current == '=' ) + { + token.Length++; + token.IsAssign = true; + + if (left) + move_forward(); + } + else while ( left && current == *(scanner - 1) && token.Length < 3 ) + { + token.Length++; + + if (left) + move_forward(); + } + goto FoundToken; + + // Dash is unfortunatlly a bit more complicated... + case '-': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Operator; + if ( left ) + { + move_forward(); + + if ( current == '>' ) + { + token.Length++; + move_forward(); + + if ( current == '*' ) + { + token.Length++; + move_forward(); + } + } + else if ( current == '=' ) + { + token.Length++; + token.IsAssign = true; + + if (left) + move_forward(); + } + else while ( left && current == *(scanner - 1) && token.Length < 3 ) + { + token.Length++; + + if (left) + move_forward(); + } + } + goto FoundToken; + + case '/': + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Operator; + + if ( left ) + { + move_forward(); + + if ( current == '/' ) + { + token.Type = TokType::Comment; + + move_forward(); + token.Text = scanner; + token.Length = 0; + + while ( left && current != '\n' ) + { + move_forward(); + token.Length++; + } + } + else if ( current == '*' ) + { + token.Type = TokType::Comment; + + move_forward(); + token.Text = scanner; + token.Length = 0; + + while ( left && ( current != '*' && *(scanner + 1) != '/' ) ) + { + move_forward(); + token.Length++; + } + move_forward(); + move_forward(); + } + } + goto FoundToken; + } + + if ( char_is_alpha( current ) || current == '_' ) + { + token.Text = scanner; + token.Length = 1; + move_forward(); + + while ( left && ( char_is_alphanumeric(current) || current == '_' ) ) + { + move_forward(); + token.Length++; + } + + goto FoundToken; + } + else if ( char_is_digit(current) ) + { + // This is a very brute force lex, no checks are done for validity of literal. + + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Number; + move_forward(); + + if (left + && ( current == 'x' || current == 'X' + || current == 'b' || current == 'B' + || current == 'o' || current == 'O' ) + ) + { + move_forward(); + token.Length++; + + while ( left && char_is_hex_digit(current) ) + { + move_forward(); + token.Length++; + } + + goto FoundToken; + } + + while ( left && char_is_digit(current) ) + { + move_forward(); + token.Length++; + } + + if ( left && current == '.' ) + { + move_forward(); + token.Length++; + + while ( left && char_is_digit(current) ) + { + move_forward(); + token.Length++; + } + } + + goto FoundToken; + } + else + { + String context_str = String::fmt_buf( GlobalAllocator, "%s", scanner, min( 100, left ) ); + + log_failure( "Failed to lex token %s", context_str ); + + // Skip to next whitespace since we can't know if anything else is valid until then. + while ( left && ! char_is_space( current ) ) + { + move_forward(); + } + } + + FoundToken: + + if ( token.Type != TokType::Invalid ) + { + if ( token.Type == TokType::Preprocessor_Directive && keep_preprocess_directives == false ) + continue; + + Tokens.append( token ); + continue; + } + + TokType type = get_tok_type( token.Text, token.Length ); + + if ( type == TokType::Invalid) + type = TokType::Identifier; + + token.Type = type; + Tokens.append( token ); + } + + if ( Tokens.num() == 0 ) + { + log_failure( "Failed to lex any tokens" ); + return { { nullptr }, 0 }; + } + + return { Tokens, 0 }; + # undef current + # undef move_forward + # undef SkipWhitespace + # undef SkipWhitespace_Checked + } +} + +#pragma region Helper Macros +# define check_parse_args( func, def ) \ +if ( def.Len <= 0 ) \ +{ \ + log_failure( "gen::" stringize(func) ": length must greater than 0" ); \ + return CodeInvalid; \ +} \ +if ( def.Ptr == nullptr ) \ +{ \ + log_failure( "gen::" stringize(func) ": def was null" ); \ + return CodeInvalid; \ +} + +# define nexttok toks.next() +# define currtok toks.current() +# define prevtok toks.previous() +# define eat( Type_ ) toks.__eat( Type_, context ) +# define left ( toks.Arr.num() - toks.Idx ) + +# define check( Type_ ) ( left && currtok.Type == Type_ ) +#pragma endregion Helper Macros + +struct ParseContext +{ + ParseContext* Parent; + char const* Fn; +}; + +internal Code parse_function_body( Parser::TokArray& toks, char const* context ); +internal Code parse_global_nspace( Parser::TokArray& toks, char const* context ); + +internal CodeClass parse_class ( Parser::TokArray& toks, char const* context ); +internal CodeEnum parse_enum ( Parser::TokArray& toks, char const* context ); +internal CodeBody parse_export_body ( Parser::TokArray& toks, char const* context ); +internal CodeBody parse_extern_link_body( Parser::TokArray& toks, char const* context ); +internal CodeExtern parse_exten_link ( Parser::TokArray& toks, char const* context ); +internal CodeFriend parse_friend ( Parser::TokArray& toks, char const* context ); +internal CodeFn parse_function ( Parser::TokArray& toks, char const* context ); +internal CodeNamespace parse_namespace ( Parser::TokArray& toks, char const* context ); +internal CodeOpCast parse_operator_cast ( Parser::TokArray& toks, char const* context ); +internal CodeStruct parse_struct ( Parser::TokArray& toks, char const* context ); +internal CodeVar parse_variable ( Parser::TokArray& toks, char const* context ); +internal CodeTemplate parse_template ( Parser::TokArray& toks, char const* context ); +internal CodeType parse_type ( Parser::TokArray& toks, char const* context ); +internal CodeTypedef parse_typedef ( Parser::TokArray& toks, char const* context ); +internal CodeUnion parse_union ( Parser::TokArray& toks, char const* context ); +internal CodeUsing parse_using ( Parser::TokArray& toks, char const* context ); + +internal inline +Code parse_array_decl( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + + if ( check( TokType::BraceSquare_Open ) ) + { + eat( TokType::BraceSquare_Open ); + + if ( left == 0 ) + { + log_failure( "%s: Error, unexpected end of typedef definition ( '[]' scope started )", stringize(parse_typedef) ); + return Code::Invalid; + } + + if ( currtok.Type == TokType::BraceSquare_Close ) + { + log_failure( "%s: Error, empty array expression in typedef definition", stringize(parse_typedef) ); + return Code::Invalid; + } + + Token untyped_tok = currtok; + + while ( left && currtok.Type != TokType::BraceSquare_Close ) + { + eat( currtok.Type ); + } + + untyped_tok.Length = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)untyped_tok.Text; + + Code array_expr = untyped_str( untyped_tok ); + + if ( left == 0 ) + { + log_failure( "%s: Error, unexpected end of type definition, expected ]", stringize(parse_typedef) ); + return Code::Invalid; + } + + if ( currtok.Type != TokType::BraceSquare_Close ) + { + log_failure( "%s: Error, expected ] in type definition, not %s", stringize(parse_typedef), str_tok_type( currtok.Type ) ); + return Code::Invalid; + } + + eat( TokType::BraceSquare_Close ); + return array_expr; + } + + return { nullptr }; +} + +internal inline +CodeAttributes parse_attributes( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + + Token start; + s32 len = 0; + + if ( check(TokType::Attribute_Open) ) + { + eat( TokType::Attribute_Open); + + while ( left && currtok.Type != TokType::Attribute_Close ) + { + eat( currtok.Type ); + } + + eat( TokType::Attribute_Close ); + + s32 len = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)start.Text; + } + + else if ( check(TokType::Decl_GNU_Attribute) ) + { + eat(TokType::BraceCurly_Open); + eat(TokType::BraceCurly_Open); + + while ( left && currtok.Type != TokType::BraceCurly_Close ) + { + eat(currtok.Type); + } + + eat(TokType::BraceCurly_Close); + eat(TokType::BraceCurly_Close); + + s32 len = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)start.Text; + } + + else if ( check(TokType::Decl_MSVC_Attribute) ) + { + eat( TokType::Decl_MSVC_Attribute ); + eat( TokType::BraceCurly_Open); + + while ( left && currtok.Type != TokType::BraceCurly_Close ) + { + eat(currtok.Type); + } + + eat(TokType::BraceCurly_Close); + + s32 len = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)start.Text; + } + + else if ( tok_is_attribute( currtok ) ) + { + eat(currtok.Type); + s32 len = start.Length; + } + + if ( len > 0 ) + { + StrC attribute_txt = { len, start.Text }; + return def_attributes( attribute_txt ); + } + + return { nullptr }; +} + +internal inline +Parser::Token parse_identifier( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + Token name = currtok; + + eat( TokType::Identifier ); + + while ( check( TokType::Access_StaticSymbol ) ) + { + eat( TokType::Access_StaticSymbol ); + + if ( left == 0 ) + { + log_failure( "%s: Error, unexpected end of type definition, expected identifier", context ); + return { nullptr, 0, TokType::Invalid }; + } + + if ( currtok.Type != TokType::Identifier ) + { + log_failure( "%s: Error, expected identifier in type definition, not %s", context, str_tok_type( currtok.Type ) ); + return { nullptr, 0, TokType::Invalid }; + } + + name.Length = ( (sptr)currtok.Text + currtok.Length ) - (sptr)name.Text; + eat( TokType::Identifier ); + } + + return name; +} + +internal +CodeParam parse_params( Parser::TokArray& toks, char const* context, bool use_template_capture = false ) +{ + using namespace Parser; + using namespace ECode; + + if ( ! use_template_capture ) + eat( TokType::Capture_Start ); + + else + { + if ( check ( TokType::Operator ) && currtok.Text[0] == '<' ) + eat( TokType::Operator ); + } + + if ( ! use_template_capture && check(TokType::Capture_End) ) + { + eat( TokType::Capture_End ); + return { nullptr }; + } + + CodeType type = { nullptr }; + Code value = { nullptr }; + + if ( check( TokType::Varadic_Argument) ) + { + eat( TokType::Varadic_Argument ); + + return param_varadic; + } + + type = parse_type( toks, context ); + if ( type == Code::Invalid ) + return CodeInvalid; + + Token name = { nullptr, 0, TokType::Invalid, false }; + + if ( check( TokType::Identifier ) ) + { + 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 CodeInvalid; + } + + 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 ); + } + } + + CodeParam + result = (CodeParam) make_code(); + result->Type = Parameters; + + if ( name.Length > 0 ) + result->Name = get_cached_string( name ); + + result->ValueType = type; + + if ( value ) + result->Value = value; + + result->NumEntries++; + + while ( left + && use_template_capture ? + currtok.Type != TokType::Operator && currtok.Text[0] != '>' + : currtok.Type != TokType::Capture_End ) + { + eat( TokType::Comma ); + + Code type = { nullptr }; + Code value = { nullptr }; + + if ( check( TokType::Varadic_Argument) ) + { + eat( TokType::Varadic_Argument ); + result.append( param_varadic ); + continue; + } + + type = parse_type( toks, context ); + if ( type == Code::Invalid ) + return CodeInvalid; + + name = { nullptr, 0, TokType::Invalid, false }; + + if ( check( TokType::Identifier ) ) + { + 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 CodeInvalid; + } + + 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 ); + } + } + + CodeParam + param = (CodeParam) make_code(); + param->Type = Parameters; + + if ( name.Length > 0 ) + param->Name = get_cached_string( name ); + + param->ValueType = type; + + if ( value ) + param->Value = value; + + result.append( param ); + } + + if ( ! use_template_capture ) + eat( TokType::Capture_End ); + + else + { + if ( ! check( TokType::Operator) || currtok.Text[0] != '>' ) + { + log_failure("gen::parse_template: expected '<' after 'template' keyword. %s", str_tok_type( currtok.Type )); + return CodeInvalid; + } + eat( TokType::Operator ); + } + + return result; +# undef context +} + +// Function parsing is handled in multiple places because its initial signature is shared with variable parsing +internal inline +CodeFn parse_function_after_name( + ModuleFlag mflags + , CodeAttributes attributes + , CodeSpecifiers specifiers + , CodeType ret_type + , StrC name + , Parser::TokArray& toks + , char const* context +) +{ + using namespace Parser; + + CodeParam params = parse_params( toks, stringize(parse_function) ); + + while ( left && tok_is_specifier( currtok ) ) + { + specifiers.append( ESpecifier::to_type(currtok) ); + eat( currtok.Type ); + } + + CodeBody body = { nullptr }; + if ( check( TokType::BraceCurly_Open ) ) + { + body = parse_function_body( toks, stringize(parse_function) ); + if ( body == Code::Invalid ) + return CodeInvalid; + } + else + { + eat( TokType::Statement_End ); + } + + using namespace ECode; + + CodeFn + result = (CodeFn) 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 CodeInvalid; + } + } + + result->Type = Function; + result->Body = body; + } + else + { + result->Type = Function_Fwd; + } + + if ( specifiers ) + result->Specs = specifiers; + + result->ReturnType = ret_type; + + if ( params ) + result->Params = params; + + return result; +} + +internal inline +CodeOperator parse_operator_after_ret_type( ModuleFlag mflags + , CodeAttributes attributes + , CodeSpecifiers specifiers + , CodeType ret_type + , Parser::TokArray& toks + , char const* context ) +{ + using namespace Parser; + using namespace EOperator; + + // Parse Operator + eat( TokType::Decl_Operator ); + + if ( ! check( TokType::Operator ) ) + { + log_failure( "gen::%s: Expected operator after 'operator' keyword", context ); + return CodeInvalid; + } + + OperatorT op = Invalid; + switch ( currtok.Text[0] ) + { + case '+': + { + if ( currtok.Text[1] == '=' ) + op = Assign_Add; + + else + op = Add; + } + break; + case '-': + { + if ( currtok.Text[1] == '=' ) + op = Assign_Subtract; + + else + op = Subtract; + } + break; + case '*': + { + if ( currtok.Text[1] == '=' ) + op = Assign_Multiply; + + else + { + Token& finder = prevtok; + while ( finder.Type != TokType::Decl_Operator ) + { + if ( finder.Type == TokType::Identifier) + { + op = Indirection; + break; + } + } + + if ( op == Invalid) + op = Multiply; + } + } + break; + case '/': + { + if ( currtok.Text[1] == '=' ) + op = Assign_Divide; + + else + op = Divide; + } + break; + case '%': + { + if ( currtok.Text[1] == '=' ) + op = Assign_Modulo; + + else + op = Modulo; + } + break; + case '&': + { + if ( currtok.Text[1] == '=' ) + op = Assign_BAnd; + + else if ( currtok.Text[1] == '&' ) + op = LAnd; + + else + { + + + if ( op == Invalid ) + op = BAnd; + } + } + break; + case '|': + { + if ( currtok.Text[1] == '=' ) + op = Assign_BOr; + + else if ( currtok.Text[1] == '|' ) + op = LOr; + + else + op = BOr; + } + break; + case '^': + { + if ( currtok.Text[1] == '=' ) + op = Assign_BXOr; + + else + op = BXOr; + } + break; + case '~': + { + op = BNot; + } + break; + case '!': + { + if ( currtok.Text[1] == '=' ) + op = LNot; + + else + op = UnaryNot; + } + break; + case '=': + { + if ( currtok.Text[1] == '=' ) + op = LEqual; + + else + op = Assign; + } + break; + case '<': + { + if ( currtok.Text[1] == '=' ) + op = LEqual; + + else if ( currtok.Text[1] == '<' ) + { + if ( currtok.Text[2] == '=' ) + op = Assign_LShift; + + else + op = LShift; + } + else + op = Lesser; + } + break; + case '>': + { + if ( currtok.Text[1] == '=' ) + op = GreaterEqual; + + else if ( currtok.Text[1] == '>' ) + { + if ( currtok.Text[2] == '=' ) + op = Assign_RShift; + + else + op = RShift; + } + else + op = Greater; + } + break; + case '(': + { + if ( currtok.Text[1] == ')' ) + op = FunctionCall; + + else + op = Invalid; + } + break; + case '[': + { + if ( currtok.Text[1] == ']' ) + op = Subscript; + + else + op = Invalid; + } + break; + default: + { + break; + } + } + + if ( op == Invalid ) + { + log_failure( "gen::%s: Invalid operator '%s'", context, currtok.Text ); + return CodeInvalid; + } + + eat( TokType::Operator ); + + // Parse Params + CodeParam params = parse_params( toks, stringize(parse_operator) ); + + while ( left && tok_is_specifier( currtok ) ) + { + specifiers.append( ESpecifier::to_type(currtok) ); + eat( currtok.Type ); + } + + // Parse Body + CodeBody body = { nullptr }; + if ( check( TokType::BraceCurly_Open ) ) + { + body = parse_function_body( toks, stringize(parse_function) ); + if ( body == Code::Invalid ) + return CodeInvalid; + } + else + { + eat( TokType::Statement_End ); + } + + // OpValidateResult check_result = operator__validate( op, params, ret_type, specifiers ); + CodeOperator 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 +CodeVar parse_variable_after_name( + ModuleFlag mflags + , CodeAttributes attributes + ,CodeSpecifiers specifiers + , CodeType type + , StrC name + , Parser::TokArray& toks + , char const* context ) +{ + using namespace Parser; + + Code array_expr = parse_array_decl( toks, stringize(parse_variable) ); + + Code expr = { nullptr }; + + 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 CodeInvalid; + } + + while ( left && currtok.Type != TokType::Statement_End ) + { + eat( currtok.Type ); + } + + expr_tok.Length = ( (sptr)currtok.Text + currtok.Length ) - (sptr)expr_tok.Text; + expr = untyped_str( expr_tok ); + } + + eat( TokType::Statement_End ); + + using namespace ECode; + + CodeVar + result = (CodeVar) make_code(); + result->Type = Variable; + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + + result->ValueType = type; + + if (array_expr ) + type->ArrExpr = array_expr; + + if ( attributes ) + result->Attributes = attributes; + + if ( specifiers ) + result->Specs = specifiers; + + if ( expr ) + result->Value = expr; + + return result; +} + +internal inline +Code parse_variable_assignment( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + + 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 ); + } + + return expr; +} + +internal inline +Code parse_operator_function_or_variable( bool expects_function, CodeAttributes attributes, CodeSpecifiers specifiers, Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + + Code result = Code::Invalid; + + CodeType type = parse_type( toks, stringize(parse_variable) ); + + if ( type == Code::Invalid ) + return CodeInvalid; + + if ( check( TokType::Operator) ) + { + // Dealing with an operator overload + result = parse_operator_after_ret_type( ModuleFlag::None, attributes, specifiers, type, toks, stringize(parse_template) ); + } + else + { + StrC name = currtok; + eat( TokType::Identifier ); + + if ( check( TokType::Capture_Start) ) + { + // Dealing with a function + + result = parse_function_after_name( ModuleFlag::None, attributes, specifiers, type, name, toks, stringize(parse_template) ); + } + else + { + if ( expects_function ) + { + log_failure( "gen::parse_operator_function_or_variable: 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, stringize(parse_template) ); + } + } + + return result; +} + +internal +CodeBody parse_class_struct_body( Parser::TokType which, Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + using namespace ECode; + + eat( TokType::BraceCurly_Open ); + + CodeBody + result = (CodeBody) make_code(); + + if ( which == TokType::Decl_Class ) + result->Type = Class_Body; + + else + result->Type = Struct_Body; + + while ( left && currtok.Type != TokType::BraceCurly_Close ) + { + Code member = Code::Invalid; + CodeAttributes attributes = { nullptr }; + CodeSpecifiers specifiers = { nullptr }; + + bool expects_function = false; + + switch ( currtok.Type ) + { + case TokType::Comment: + member = def_comment( currtok ); + eat( TokType::Comment ); + break; + + case TokType::Access_Public: + member = access_public; + eat( TokType::Access_Public ); + eat( TokType::Assign_Classifer ); + break; + + case TokType::Access_Protected: + member = access_protected; + eat( TokType::Access_Protected ); + eat( TokType::Assign_Classifer ); + break; + + case TokType::Access_Private: + member = access_private; + eat( TokType::Access_Private ); + eat( TokType::Assign_Classifer ); + break; + + case TokType::Decl_Class: + member = parse_class( toks, context ); + break; + + case TokType::Decl_Enum: + member = parse_enum( toks, context ); + break; + + case TokType::Decl_Friend: + member = parse_friend( toks, context ); + break; + + case TokType::Decl_Operator: + member = parse_operator_cast( toks, context ); + break; + + case TokType::Decl_Struct: + member = parse_struct( toks, context ); + break; + + case TokType::Decl_Template: + member = parse_template( toks, context ); + break; + + case TokType::Decl_Typedef: + member = parse_typedef( toks, context ); + break; + + case TokType::Decl_Union: + member = parse_variable( toks, context ); + break; + + case TokType::Decl_Using: + member = parse_using( toks, context ); + break; + + case TokType::Attribute_Open: + case TokType::Decl_GNU_Attribute: + case TokType::Decl_MSVC_Attribute: + #define Entry( attribute, str ) case TokType::attribute: + GEN_Define_Attribute_Tokens + #undef Entry + { + attributes = parse_attributes( toks, context ); + } + //! 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_Volatile: + { + 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::Inline: + case ESpecifier::Mutable: + case ESpecifier::Static: + case ESpecifier::Volatile: + break; + + case ESpecifier::Consteval: + expects_function = true; + break; + + default: + log_failure( "gen::parse_class_struct_body: invalid specifier " "%s" " for variable", ESpecifier::to_str(spec) ); + return CodeInvalid; + } + + 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: + case TokType::Type_Long: + { + member = parse_operator_function_or_variable( expects_function, attributes, specifiers, toks, context ); + } + break; + + 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 ) + { + log_failure( "gen::parse_variable: failed to parse member" ); + return CodeInvalid; + } + + result.append( member ); + } + + eat( TokType::BraceCurly_Close ); + return result; +} + +internal +Code parse_class_struct( Parser::TokType which, Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + + if ( which != TokType::Decl_Class && which != TokType::Decl_Struct ) + { + log_failure( "%s: Error, expected class or struct, not %s", context, str_tok_type( which ) ); + return CodeInvalid; + } + + Token name { nullptr, 0, TokType::Invalid }; + + AccessSpec access = AccessSpec::Default; + CodeType parent = { nullptr }; + CodeBody body = { nullptr }; + CodeAttributes attributes = { nullptr }; + ModuleFlag mflags = ModuleFlag::None; + + CodeClass result = CodeInvalid; + + if ( check(TokType::Module_Export) ) + { + mflags = ModuleFlag::Export; + eat( TokType::Module_Export ); + } + + eat( which ); + + attributes = parse_attributes( toks, context ); + + if ( check( TokType::Identifier ) ) + name = parse_identifier( toks, context ); + + local_persist + char interface_arr_mem[ kilobytes(4) ] {0}; + Array interfaces = Array::init_reserve( Arena::init_from_memory(interface_arr_mem, kilobytes(4) ), 4 ); + + if ( check( TokType::Assign_Classifer ) ) + { + eat( TokType::Assign_Classifer ); + + if ( tok_is_access_specifier( currtok ) ) + { + access = tok_to_access_specifier( currtok ); + } + + Token parent_tok = parse_identifier( toks, context ); + parent = def_type( parent_tok ); + + while ( check(TokType::Comma) ) + { + eat(TokType::Access_Public); + + if ( tok_is_access_specifier( currtok ) ) + { + eat(currtok.Type); + } + + Token interface_tok = parse_identifier( toks, context ); + + interfaces.append( def_type( interface_tok ) ); + } + } + + if ( check( TokType::BraceCurly_Open ) ) + { + body = parse_class_struct_body( which, toks, context ); + } + + eat( TokType::Statement_End ); + + if ( which == TokType::Decl_Class ) + result = def_class( name, body, parent, access + , attributes + , mflags + ); + + else + result = def_struct( name, body, (CodeType)parent, access + , attributes + , mflags + ); + + interfaces.free(); + return result; +} + +internal +Code parse_function_body( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + using namespace ECode; + + eat( TokType::BraceCurly_Open ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Function_Body; + + Token start = currtok; + + s32 level = 0; + while ( left && ( currtok.Type != TokType::BraceCurly_Close || level > 0 ) ) + { + if ( currtok.Type == TokType::BraceCurly_Open ) + level++; + + else if ( currtok.Type == TokType::BraceCurly_Close && level > 0 ) + level--; + + eat( currtok.Type ); + } + + Token previous = prevtok; + + s32 len = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)start.Text; + + if ( len > 0 ) + { + result.append( def_execution( { len, start.Text } ) ); + } + + eat( TokType::BraceCurly_Close ); + return result; +} + +internal +CodeBody parse_global_nspace( CodeT which, Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + using namespace ECode; + + if ( which != Namespace_Body && which != Global_Body && which != Export_Body && which != Extern_Linkage_Body ) + return CodeInvalid; + + if ( which != Global_Body ) + eat( TokType::BraceCurly_Open ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = which; + + while ( left && currtok.Type != TokType::BraceCurly_Close ) + { + Code member = Code::Invalid; + CodeAttributes attributes = { nullptr }; + CodeSpecifiers specifiers = { nullptr }; + + bool expects_function = false; + + switch ( currtok.Type ) + { + case TokType::Comment: + member = def_comment( currtok ); + eat( TokType::Comment ); + break; + + case TokType::Decl_Enum: + member = parse_enum( toks, context); + break; + + case TokType::Decl_Class: + member = parse_class( toks, context ); + break; + + case TokType::Decl_Extern_Linkage: + if ( which == Extern_Linkage_Body ) + log_failure( "gen::parse_global_nspace: nested extern linkage" ); + + member = parse_extern_link_body( toks, context ); + break; + + case TokType::Decl_Namespace: + member = parse_namespace( toks, context ); + break; + + case TokType::Decl_Struct: + member = parse_struct( toks, context ); + break; + + case TokType::Decl_Template: + member = parse_template( toks, context ); + break; + + case TokType::Decl_Typedef: + member = parse_typedef( toks, context ); + break; + + case TokType::Decl_Union: + member = parse_union( toks, context ); + break; + + case TokType::Decl_Using: + member = parse_using( toks, context ); + break; + + case TokType::Module_Export: + if ( which == Export_Body ) + log_failure( "gen::parse_global_nspace: nested export declaration" ); + + member = parse_export_body( toks, context ); + break; + + case TokType::Module_Import: + { + not_implemented( context ); + } + //! Fallthrough intentional + case TokType::Decl_GNU_Attribute: + case TokType::Decl_MSVC_Attribute: + #define Entry( attribute, str ) case TokType::attribute: + GEN_Define_Attribute_Tokens + #undef Entry + { + attributes = parse_attributes( toks, context ); + } + //! 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::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::Inline: + case ESpecifier::Mutable: + case ESpecifier::Static: + case ESpecifier::Volatile: + break; + + case ESpecifier::Consteval: + expects_function = true; + break; + + default: + log_failure( "gen::parse_global_nspace: invalid specifier " "%s" " for variable", ESpecifier::to_str(spec) ); + return CodeInvalid; + } + + 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_Long: + case TokType::Type_Short: + case TokType::Type_Signed: + case TokType::Type_Unsigned: + { + member = parse_operator_function_or_variable( expects_function, attributes, specifiers, toks, context ); + } + } + + if ( member == Code::Invalid ) + { + log_failure( "gen::%s: failed to parse extern linkage member", context ); + return CodeInvalid; + } + + result.append( member ); + } + + if ( which != Global_Body ) + eat( TokType::BraceCurly_Close ); + + return result; +} + +internal +CodeClass parse_class( Parser::TokArray& toks, char const* context ) +{ + return (CodeClass) parse_class_struct( Parser::TokType::Decl_Class, toks, context ); +} + +CodeClass parse_class( StrC def ) +{ + check_parse_args( parse_class, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return (CodeClass) parse_class_struct( TokType::Decl_Class, toks, stringize(parse_class) ); +} + +internal +CodeEnum parse_enum( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + using namespace ECode; + + SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; + s32 num_specifiers = 0; + + Token name = { nullptr, 0, TokType::Invalid }; + Code array_expr = { nullptr }; + CodeType type = { nullptr }; + Token body = { nullptr, 0, TokType::Invalid }; + + char entries_code[ kilobytes(128) ] { 0 }; + s32 entries_length = 0; + + bool is_enum_class = false; + + eat( TokType::Decl_Enum ); + + if ( currtok.Type == TokType::Decl_Class ) + { + eat( TokType::Decl_Class); + is_enum_class = true; + } + + // TODO : Parse attributes + + if ( currtok.Type != TokType::Identifier ) + { + log_failure( "gen::parse_enum: expected identifier for enum name" ); + return CodeInvalid; + } + + name = currtok; + eat( TokType::Identifier ); + + if ( currtok.Type == TokType::Assign_Classifer ) + { + eat( TokType::Assign_Classifer ); + + type = parse_type( toks, stringize(parse_enum) ); + if ( type == Code::Invalid ) + return CodeInvalid; + } + + if ( currtok.Type == TokType::BraceCurly_Open ) + { + eat( TokType::BraceCurly_Open ); + + body = currtok; + + while ( currtok.Type != TokType::BraceCurly_Close ) + { + eat( TokType::Identifier); + + if ( currtok.Type == TokType::Operator && currtok.Text[0] == '=' ) + { + eat( TokType::Operator ); + + while ( currtok.Type != TokType::Comma && currtok.Type != TokType::BraceCurly_Close ) + { + eat( currtok.Type ); + } + } + + if ( currtok.Type == TokType::Comma ) + { + eat( TokType::Comma ); + } + } + + body.Length = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)body.Text; + + eat( TokType::BraceCurly_Close ); + } + + eat( TokType::Statement_End ); + + using namespace ECode; + + CodeEnum + result = (CodeEnum) make_code(); + + if ( body.Length ) + { + // mem_copy( entries_code, body.Text, body.Length ); + + Code untyped_body = untyped_str( body ); + + result->Type = is_enum_class ? Enum_Class : Enum; + result->Body = untyped_body; + } + else + { + result->Type = is_enum_class ? Enum_Class_Fwd : Enum_Fwd; + } + + result->Name = get_cached_string( name ); + + if ( type ) + result->UnderlyingType = type; + + return result; +} + +CodeEnum parse_enum( StrC def ) +{ + check_parse_args( parse_enum, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return parse_enum( toks, stringize(parse_enum) ); +} + +internal inline +CodeBody parse_export_body( Parser::TokArray& toks, char const* context ) +{ + return parse_global_nspace( ECode::Export_Body, toks, context ); +} + +CodeBody parse_export_body( StrC def ) +{ + check_parse_args( parse_export_body, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return parse_export_body( toks, stringize(parse_export_body) ); +} + +internal inline +CodeBody parse_extern_link_body( Parser::TokArray& toks, char const* context ) +{ + return parse_global_nspace( ECode::Extern_Linkage_Body, toks, context ); +} + +internal +CodeExtern parse_extern_link( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + + eat( TokType::Decl_Extern_Linkage ); + + Token name = currtok; + eat( TokType::String ); + + name.Text += 1; + name.Length -= 1; + + CodeExtern + result = (CodeExtern) make_code(); + result->Type = ECode::Extern_Linkage; + result->Name = get_cached_string( name ); + + Code entry = parse_extern_link_body( toks, context ); + if ( entry == Code::Invalid ) + { + log_failure( "gen::parse_extern_link: failed to parse body" ); + return result; + } + + result->Body = entry; + + return result; +} + +CodeExtern parse_extern_link( StrC def ) +{ + check_parse_args( parse_extern_link, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return parse_extern_link( toks, stringize(parse_extern_link) ); +} + +internal +CodeFriend parse_friend( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + using namespace ECode; + + eat( TokType::Decl_Friend ); + + CodeFn function = { nullptr }; + + // Type declaration or return type + CodeType type = parse_type( toks, stringize(parse_friend) ); + if ( type == Code::Invalid ) + return CodeInvalid; + + // Funciton declaration + if ( currtok.Type == TokType::Identifier ) + { + // Name + Token name = parse_identifier( toks, stringize(parse_friend) ); + + // Parameter list + CodeParam params = parse_params( toks, stringize(parse_friend) ); + + function = make_code(); + function->Type = Function_Fwd; + function->Name = get_cached_string( name ); + function->ReturnType = type; + + if ( params ) + function->Params = params; + } + + eat( TokType::Statement_End ); + + CodeFriend + result = (CodeFriend) make_code(); + result->Type = Friend; + + if ( function ) + result->Declaration = function; + + else + result->Declaration = type; + + return result; +} + +CodeFriend parse_friend( StrC def ) +{ + check_parse_args( parse_friend, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return parse_friend( toks, stringize(parse_friend) ); +} + +internal +CodeFn parse_functon( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + + SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; + s32 num_specifiers = 0; + + CodeAttributes attributes = { nullptr }; + CodeSpecifiers specifiers = { nullptr }; + ModuleFlag mflags = ModuleFlag::None; + + if ( check(TokType::Module_Export) ) + { + mflags = ModuleFlag::Export; + eat( TokType::Module_Export ); + } + + attributes = parse_attributes( toks, context ); + + while ( left && tok_is_specifier( currtok ) ) + { + SpecifierT spec = ESpecifier::to_type( currtok ); + + switch ( spec ) + { + case ESpecifier::Const: + case ESpecifier::Consteval: + case ESpecifier::Constexpr: + case ESpecifier::External_Linkage: + case ESpecifier::Inline: + case ESpecifier::Static: + break; + + default: + log_failure( "gen::parse_functon: invalid specifier " "%s" " for functon", ESpecifier::to_str(spec) ); + return CodeInvalid; + } + + 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 ); + } + + CodeType ret_type = parse_type( toks, stringize(parse_function) ); + if ( ret_type == Code::Invalid ) + return CodeInvalid; + + Token name = parse_identifier( toks, stringize(parse_function) ); + if ( ! name ) + return CodeInvalid; + + CodeFn result = parse_function_after_name( mflags, attributes, specifiers, ret_type, name, toks, context ); + + return result; +} + +CodeFn parse_function( StrC def ) +{ + using namespace Parser; + + check_parse_args( parse_function, def ); + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return (CodeFn) parse_functon( toks, stringize(parse_function) ); +} + +CodeBody parse_global_body( StrC def ) +{ + check_parse_args( parse_global_body, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return parse_global_nspace( ECode::Global_Body, toks, stringize(parse_global_body) ); +} + +internal +CodeNamespace parse_namespace( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + eat( TokType::Decl_Namespace ); + + Token name = parse_identifier( toks, stringize(parse_namespace) ); + + CodeBody body = parse_global_nspace( ECode::Namespace_Body, toks, stringize(parse_namespace) ); + if ( body == Code::Invalid ) + return CodeInvalid; + + CodeNamespace + result = (CodeNamespace) make_code(); + result->Type = ECode::Namespace; + result->Name = get_cached_string( name ); + + result->Body = body; + + return result; +} + +CodeNamespace parse_namespace( StrC def ) +{ + check_parse_args( parse_namespace, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return parse_namespace( toks, stringize(parse_namespace) ); +} + +internal +CodeOperator parse_operator( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + + CodeAttributes attributes = { nullptr }; + CodeSpecifiers specifiers = { nullptr }; + ModuleFlag mflags = ModuleFlag::None; + + SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; + s32 num_specifiers = 0; + + if ( check(TokType::Module_Export) ) + { + mflags = ModuleFlag::Export; + eat( TokType::Module_Export ); + } + + attributes = parse_attributes( toks, context ); + + while ( left && tok_is_specifier( currtok ) ) + { + SpecifierT spec = ESpecifier::to_type( currtok ); + + switch ( spec ) + { + case ESpecifier::Const: + case ESpecifier::Constexpr: + case ESpecifier::Inline: + case ESpecifier::Static: + break; + + default: + log_failure( "gen::parse_operator: invalid specifier " "%s" " for operator", ESpecifier::to_str(spec) ); + return CodeInvalid; + } + + 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 ); + } + + // Parse Return Type + CodeType ret_type = parse_type( toks, stringize(parse_operator) ); + + CodeOperator result = parse_operator_after_ret_type( mflags, attributes, specifiers, ret_type, toks, context ); + return result; +} + +CodeOperator parse_operator( StrC def ) +{ + check_parse_args( parse_operator, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return (CodeOperator) parse_operator( toks, stringize(parse_operator) ); +} + +CodeOpCast parse_operator_cast( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + + eat( TokType::Decl_Operator ); + + Code type = parse_type( toks, stringize(parse_operator_cast) ); + + eat( TokType::Capture_Start ); + eat( TokType::Capture_End ); + + CodeSpecifiers specifiers = { nullptr }; + + if ( check(TokType::Spec_Const)) + specifiers = spec_const; + + Code body = { nullptr }; + + if ( check( TokType::BraceCurly_Open) ) + { + eat( TokType::BraceCurly_Open ); + + Token body_str = currtok; + + s32 level = 0; + while ( left && ( currtok.Type != TokType::BraceCurly_Close || level > 0 ) ) + { + if ( currtok.Type == TokType::BraceCurly_Open ) + level++; + + else if ( currtok.Type == TokType::BraceCurly_Close && level > 0 ) + level--; + + eat( currtok.Type ); + } + + body_str.Length = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)body_str.Text; + + body = untyped_str( body_str ); + + eat( TokType::BraceCurly_Close ); + } + + CodeOpCast result = (CodeOpCast) make_code(); + + if (body) + { + result->Type = ECode::Operator_Cast; + result->Body = body; + } + else + { + result->Type = ECode::Operator_Cast_Fwd; + } + + if ( specifiers ) + result->Specs = specifiers; + + result->ValueType = type; + + return result; +} + +CodeOpCast parse_operator_cast( StrC def ) +{ + check_parse_args( parse_operator_cast, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return parse_operator_cast( toks, stringize(parse_operator_cast) ); +} + +internal inline +CodeStruct parse_struct( Parser::TokArray& toks, char const* context ) +{ + return (CodeStruct) parse_class_struct( Parser::TokType::Decl_Struct, toks, stringize(parse_struct) ); +} + +CodeStruct parse_struct( StrC def ) +{ + check_parse_args( parse_struct, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return (CodeStruct) parse_class_struct( TokType::Decl_Struct, toks, stringize(parse_struct) ); +} + +internal +CodeTemplate parse_template( Parser::TokArray& toks, char const* context ) +{ +# define UseTemplateCapture true + + using namespace Parser; + + ModuleFlag mflags = ModuleFlag::None; + + if ( check(TokType::Module_Export) ) + { + mflags = ModuleFlag::Export; + eat( TokType::Module_Export ); + } + + eat( TokType::Decl_Template ); + + Code params = parse_params( toks, stringize(parse_template), UseTemplateCapture ); + if ( params == Code::Invalid ) + return CodeInvalid; + + Code definition = { nullptr }; + + while ( left ) + { + if ( check( TokType::Decl_Class ) ) + { + definition = parse_class( toks, stringize(parse_template) ); + break; + } + + if ( check( TokType::Decl_Struct ) ) + { + definition = parse_enum( toks, stringize(parse_template) ); + break; + } + + if ( check( TokType::Decl_Using )) + { + definition = parse_using( toks, stringize(parse_template) ); + break; + } + + // Its either a function or a variable + Token name = { nullptr, 0, TokType::Invalid }; + + CodeAttributes attributes = { nullptr }; + CodeSpecifiers specifiers = { nullptr }; + + bool expects_function = false; + + SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; + s32 num_specifiers = 0; + + attributes = parse_attributes( toks, stringize(parse_template) ); + + 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: + case ESpecifier::Thread_Local: + case ESpecifier::Volatile: + break; + + case ESpecifier::Consteval: + expects_function = true; + break; + + default: + log_failure( "gen::parse_template: invalid specifier %s for variable or function", ESpecifier::to_str( spec ) ); + return CodeInvalid; + } + + // 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, stringize(parse_template) ); + break; + } + + CodeTemplate + result = (CodeTemplate) make_code(); + result->Type = ECode::Template; + result->Params = params; + result->Declaration = definition; + result->ModuleFlags = mflags; + + return result; +# undef UseTemplateCapture +} + +CodeTemplate parse_template( StrC def ) +{ + check_parse_args( parse_template, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return parse_template( toks, stringize(parse_template) ); +} + +internal +CodeType parse_type( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + + Token context_tok = prevtok; + + SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; + s32 num_specifiers = 0; + + Token name = { nullptr, 0, TokType::Invalid }; + Token brute_sig = { currtok.Text, 0, TokType::Invalid }; + + CodeAttributes attributes = parse_attributes( toks, context ); + + while ( left && tok_is_specifier( currtok ) ) + { + SpecifierT spec = ESpecifier::to_type( currtok ); + + if ( spec != ESpecifier::Const ) + { + log_failure( "gen::parse_type: Error, invalid specifier used in type definition: %s", currtok.Text ); + return CodeInvalid; + } + + specs_found[num_specifiers] = spec; + num_specifiers++; + eat( currtok.Type ); + } + + if ( left == 0 ) + { + log_failure( "%s: Error, unexpected end of type definition", context ); + return CodeInvalid; + } + + if ( currtok.Type == TokType::Decl_Class + || currtok.Type == TokType::Decl_Enum + || currtok.Type == TokType::Decl_Struct ) + { + name = currtok; + eat( currtok.Type ); + + name.Length = ( (sptr)currtok.Text + currtok.Length ) - (sptr)name.Text; + eat( TokType::Identifier ); + } + else if ( currtok.Type >= TokType::Type_Unsigned ) + { + name = currtok; + eat( currtok.Type ); + + while (currtok.Type >= TokType::Type_Unsigned) + { + eat( currtok.Type ); + } + + name.Length = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)name.Text; + } + else + { + name = parse_identifier( toks, context ); + if ( ! name ) + return CodeInvalid; + + // Problably dealing with a templated symbol + if ( currtok.Type == TokType::Operator && currtok.Text[0] == '<' && currtok.Length == 1 ) + { + eat( TokType::Operator ); + + s32 level = 0; + while ( left && ( currtok.Text[0] != '>' || level > 0 )) + { + if ( currtok.Text[0] == '<' ) + level++; + + if ( currtok.Text[0] == '>' ) + level--; + + eat( currtok.Type ); + } + + eat( TokType::Operator ); + + // Extend length of name to last token + name.Length = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)name.Text; + } + } + + while ( left && tok_is_specifier( currtok ) ) + { + SpecifierT spec = ESpecifier::to_type( currtok ); + + if ( spec != ESpecifier::Const + && spec != ESpecifier::Ptr + && spec != ESpecifier::Ref + && spec != ESpecifier::RValue ) + { + log_failure( "%s: Error, invalid specifier used in type definition: %s", context, currtok.Text ); + return CodeInvalid; + } + + specs_found[num_specifiers] = spec; + num_specifiers++; + eat( currtok.Type ); + } + + // Not sure if its technically possible to cast ot a function pointer user defined operator cast... + // Supporting it is not worth the effort. + if ( check( TokType::Capture_Start ) && context_tok.Type != TokType::Decl_Operator ) + { + // Its a function type + eat( TokType::Capture_Start ); + + while ( check( TokType::Star ) || currtok.Type == TokType::Spec_Const ) + { + eat( currtok.Type ); + } + + // if its a using statement there will not be an ID. + if ( check( TokType::Identifier ) ) + eat(TokType::Identifier); + + eat( TokType::Capture_End ); + + // Parameters + + eat( TokType::Capture_Start ); + + // TODO : Change this to validate the parameters... + // Bruteforce lex the parameters, no validation. + s32 level = 0; + while ( ! check( TokType::Capture_End ) || level > 0 ) + { + if ( check( TokType::Capture_Start ) ) + level++; + + if ( check( TokType::Capture_End ) ) + level--; + + eat( currtok.Type ); + } + + eat(TokType::Capture_End); + + brute_sig.Length = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)brute_sig.Text; + } + + using namespace ECode; + + CodeType + result = (CodeType) make_code(); + result->Type = Typename; + + if ( brute_sig.Length > 0 ) + { + // Bruteforce all tokens together. + name = brute_sig; + } + else + { + if (num_specifiers) + { + Code specifiers = def_specifiers( num_specifiers, (SpecifierT*)specs_found ); + result->Specs = specifiers; + } + } + + result->Name = get_cached_string( name ); + + if ( attributes ) + result->Attributes = attributes; + + return result; +} + +CodeType parse_type( StrC def ) +{ + check_parse_args( parse_type, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + CodeType result = parse_type( toks, stringize(parse_type) ); + + return result; +} + +internal +CodeTypedef parse_typedef( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + + Token name = { nullptr, 0, TokType::Invalid }; + Code array_expr = { nullptr }; + Code type = { nullptr }; + + ModuleFlag mflags = ModuleFlag::None; + + if ( check(TokType::Module_Export) ) + { + mflags = ModuleFlag::Export; + eat( TokType::Module_Export ); + } + + eat( TokType::Decl_Typedef ); + + if ( check( TokType::Decl_Enum ) ) + type = parse_enum( toks, context ); + + else if ( check(TokType::Decl_Class ) ) + type = parse_class( toks, context ); + + else if ( check(TokType::Decl_Struct ) ) + type = parse_struct( toks, context ); + + else if ( check(TokType::Decl_Union) ) + type = parse_union( toks, context ); + + else + type = parse_type( toks, context ); + + if ( ! check( TokType::Identifier ) ) + { + log_failure( "gen::parse_typedef: Error, expected identifier for typedef" ); + return CodeInvalid; + } + + name = currtok; + eat( TokType::Identifier ); + + array_expr = parse_array_decl( toks, context ); + + eat( TokType::Statement_End ); + + using namespace ECode; + + CodeTypedef + result = (CodeTypedef) make_code(); + result->Type = Typedef; + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + + result->UnderlyingType = type; + + if ( type->Type == Typename && array_expr && array_expr->Type != Invalid ) + type.cast()->ArrExpr = array_expr; + + return result; +} + +CodeTypedef parse_typedef( StrC def ) +{ + check_parse_args( parse_typedef, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return parse_typedef( toks, stringize(parse_typedef) ); +} + +internal +CodeUnion parse_union( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + + ModuleFlag mflags = ModuleFlag::None; + + if ( check(TokType::Module_Export) ) + { + mflags = ModuleFlag::Export; + eat( TokType::Module_Export ); + } + + eat( TokType::Decl_Union ); + + CodeAttributes attributes = parse_attributes( toks, context ); + + StrC name = { 0, nullptr }; + + if ( check( TokType::Identifier ) ) + { + name = currtok; + eat( TokType::Identifier ); + } + + CodeBody body = { nullptr }; + + eat( TokType::BraceCurly_Open ); + + body = make_code(); + body->Type = ECode::Union_Body; + + while ( ! check( TokType::BraceCurly_Close ) ) + { + Code entry = parse_variable( toks, context ); + + if ( entry ) + body.append( entry ); + } + + eat( TokType::BraceCurly_Close ); + eat( TokType::Statement_End ); + + CodeUnion + result = (CodeUnion) make_code(); + result->Type = ECode::Union; + result->ModuleFlags = mflags; + + if ( name ) + result->Name = get_cached_string( name ); + + if ( body ) + result->Body = body; + + if ( attributes ) + result->Attributes = attributes; + + return result; +} + +CodeUnion parse_union( StrC def ) +{ + check_parse_args( parse_union, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return parse_union( toks, stringize(parse_union) ); +} + +internal +CodeUsing parse_using( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + + SpecifierT specs_found[16] { ESpecifier::Invalid }; + s32 num_specifiers = 0; + + Token name = { nullptr, 0, TokType::Invalid }; + Code array_expr = { nullptr }; + CodeType type = { nullptr }; + + bool is_namespace = false; + + ModuleFlag mflags = ModuleFlag::None; + CodeAttributes attributes = { nullptr }; + + if ( check(TokType::Module_Export) ) + { + mflags = ModuleFlag::Export; + eat( TokType::Module_Export ); + } + + eat( TokType::Decl_Using ); + + if ( currtok.Type == TokType::Decl_Namespace ) + { + is_namespace = true; + eat( TokType::Decl_Namespace ); + } + + name = currtok; + eat( TokType::Identifier ); + + if ( currtok.IsAssign ) + { + attributes = parse_attributes( toks, context ); + + eat( TokType::Operator ); + + type = parse_type( toks, context ); + } + + array_expr = parse_array_decl( toks, context ); + + eat( TokType::Statement_End ); + + using namespace ECode; + + CodeUsing + result = (CodeUsing) make_code(); + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + + if ( is_namespace) + { + result->Type = Using_Namespace; + } + else + { + result->Type = Using; + + if ( type ) + result->UnderlyingType = type; + + if ( array_expr ) + type->ArrExpr = array_expr; + + if ( attributes ) + result->Attributes = attributes; + } + return result; +} + +CodeUsing parse_using( StrC def ) +{ + check_parse_args( parse_using, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return parse_using( toks, stringize(parse_using) ); +} + +internal +CodeVar parse_variable( Parser::TokArray& toks, char const* context ) +{ + using namespace Parser; + + Token name = { nullptr, 0, TokType::Invalid }; + + SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; + s32 num_specifiers = 0; + + ModuleFlag mflags = ModuleFlag::None; + CodeAttributes attributes = { nullptr }; + CodeSpecifiers specifiers = { nullptr }; + + if ( check(TokType::Module_Export) ) + { + mflags = ModuleFlag::Export; + eat( TokType::Module_Export ); + } + + attributes = parse_attributes( toks, context ); + + 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: + case ESpecifier::Thread_Local: + case ESpecifier::Volatile: + break; + + default: + log_failure( "gen::parse_variable: invalid specifier %s for variable", ESpecifier::to_str( spec ) ); + return CodeInvalid; + } + + // Ignore const specifiers, they're 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 ); + } + + CodeType type = parse_type( toks, context ); + + if ( type == Code::Invalid ) + return CodeInvalid; + + name = currtok; + eat( TokType::Identifier ); + + CodeVar result = parse_variable_after_name( mflags, attributes, specifiers, type, name, toks, context ); + + return result; +} + +CodeVar parse_variable( StrC def ) +{ + check_parse_args( parse_variable, def ); + using namespace Parser; + + TokArray toks = lex( def ); + if ( toks.Arr == nullptr ) + return CodeInvalid; + + return parse_variable( toks, stringize(parse_variable) ); +} + +// Undef helper macros +# undef check_parse_args +# undef curr_tok +# undef eat +# undef left + diff --git a/project/components/gen.interface.upfront.cpp b/project/components/gen.interface.upfront.cpp index e69de29..1044e8e 100644 --- a/project/components/gen.interface.upfront.cpp +++ b/project/components/gen.interface.upfront.cpp @@ -0,0 +1,1831 @@ +enum class OpValidateResult : u32 +{ + Fail, + Global, + Member +}; + +inline + OpValidateResult operator__validate( OperatorT op, CodeParam params_code, CodeType ret_type, CodeSpecifiers specifier ) +{ + using namespace EOperator; + + if ( op == EOperator::Invalid ) + { + log_failure("gen::def_operator: op cannot be invalid"); + return OpValidateResult::Fail; + } + +#pragma region Helper Macros +# define check_params() \ + if ( ! params_code ) \ + { \ + log_failure("gen::def_operator: params is null and operator%s requires it", to_str(op)); \ + return OpValidateResult::Fail; \ + } \ + if ( params_code->Type != ECode::Parameters ) \ + { \ + log_failure("gen::def_operator: params is not of Parameters type - %s", params_code.debug_str()); \ + return OpValidateResult::Fail; \ + } + +# define check_param_eq_ret() \ + if ( ! is_member_symbol && params_code->ValueType != ret_type ) \ + { \ + log_failure("gen_def_operator: operator%s requires first parameter to equal return type\n" \ + "param types: %s\n" \ + "return type: %s", \ + to_str(op), \ + params_code.debug_str(), \ + ret_type.debug_str() \ + ); \ + return OpValidateResult::Fail; \ + } +#pragma endregion Helper Macros + + if ( ! ret_type ) + { + log_failure("gen::def_operator: ret_type is null but is required by operator%s", to_str(op)); + } + + if ( ret_type->Type != ECode::Typename ) + { + log_failure("gen::def_operator: ret_type is not of typename type - %s", ret_type.debug_str()); + return OpValidateResult::Fail; + } + + bool is_member_symbol = false; + + switch ( op ) + { +# define specs( ... ) num_args( __VA_ARGS__ ), __VA_ARGS__ + case Assign: + check_params(); + + if ( params_code->NumEntries > 1 ) + { + log_failure("gen::def_operator: " + "operator%s does not support non-member definition (more than one parameter provided) - %s", + to_str(op), + params_code.debug_str() + ); + return OpValidateResult::Fail; + } + + is_member_symbol = true; + break; + + case Assign_Add: + case Assign_Subtract: + case Assign_Multiply: + case Assign_Divide: + case Assign_Modulo: + case Assign_BAnd: + case Assign_BOr: + case Assign_BXOr: + case Assign_LShift: + case Assign_RShift: + check_params(); + + if ( params_code->NumEntries == 1 ) + is_member_symbol = true; + + else + check_param_eq_ret(); + + if (params_code->NumEntries > 2 ) + { + log_failure("gen::def_operator: operator%s may not be defined with more than two parametes - param count; %d\n%s" + , to_str(op) + , params_code->NumEntries + , params_code.debug_str() + ); + return OpValidateResult::Fail; + } + break; + + case Increment: + case Decrement: + // If its not set, it just means its a prefix member op. + if ( params_code ) + { + if ( params_code->Type != ECode::Parameters ) + { + log_failure("gen::def_operator: operator%s params code provided is not of Parameters type - %s" + , to_str(op) + , params_code.debug_str() + ); + return OpValidateResult::Fail; + } + + switch ( params_code->NumEntries ) + { + case 1: + if ( params_code->ValueType.is_equal( t_int ) ) + is_member_symbol = true; + + else + check_param_eq_ret(); + break; + + case 2: + check_param_eq_ret(); + + if ( ! params_code.get(1).is_equal( t_int ) ) + { + log_failure("gen::def_operator: " + "operator%s requires second parameter of non-member definition to be int for post-decrement", + to_str(op) + ); + return OpValidateResult::Fail; + } + break; + + default: + log_failure("gen::def_operator: operator%s recieved unexpected number of parameters recived %d instead of 0-2" + , to_str(op) + , params_code->NumEntries + ); + return OpValidateResult::Fail; + } + } + break; + + case Unary_Plus: + case Unary_Minus: + case BNot: + if ( ! params_code ) + is_member_symbol = true; + + else + { + if ( params_code->Type != ECode::Parameters ) + { + log_failure("gen::def_operator: params is not of Parameters type - %s", params_code.debug_str()); + return OpValidateResult::Fail; + } + + if ( params_code->ValueType.is_equal( ret_type ) ) + { + log_failure("gen::def_operator: " + "operator%s is non-member symbol yet first paramter does not equal return type\n" + "param type: %s\n" + "return type: %s\n" + , params_code.debug_str() + , ret_type.debug_str() + ); + return OpValidateResult::Fail; + } + + if ( params_code->NumEntries > 1 ) + { + log_failure("gen::def_operator: operator%s may not have more than one parameter - param count: %d" + , to_str(op) + , params_code->NumEntries + ); + return OpValidateResult::Fail; + } + } + break; + + case Add: + case Subtract: + case Multiply: + case Divide: + case Modulo: + case BAnd: + case BOr: + case BXOr: + case LShift: + case RShift: + check_params(); + + switch ( params_code->NumEntries ) + { + case 1: + is_member_symbol = true; + break; + + case 2: + if ( ! params_code->ValueType.is_equal( ret_type ) ) + { + log_failure("gen::def_operator: " + "operator%s is non-member symbol yet first paramter does not equal return type\n" + "param type: %s\n" + "return type: %s\n" + , params_code.debug_str() + , ret_type.debug_str() + ); + return OpValidateResult::Fail; + } + break; + + default: + log_failure("gen::def_operator: operator%s recieved unexpected number of paramters recived %d instead of 0-2" + , to_str(op) + , params_code->NumEntries + ); + return OpValidateResult::Fail; + } + break; + + case UnaryNot: + if ( ! params_code ) + is_member_symbol = true; + + else + { + if ( params_code->Type != ECode::Parameters ) + { + log_failure("gen::def_operator: params is not of Parameters type - %s", params_code.debug_str()); + return OpValidateResult::Fail; + } + + if ( params_code->NumEntries != 1 ) + { + log_failure("gen::def_operator: operator%s recieved unexpected number of paramters recived %d instead of 0-1" + , to_str(op) + , params_code->NumEntries + ); + return OpValidateResult::Fail; + } + } + + if ( ! ret_type.is_equal( t_bool )) + { + log_failure("gen::def_operator: operator%s return type must be of type bool - %s" + , to_str(op) + , ret_type.debug_str() + ); + return OpValidateResult::Fail; + } + break; + + case LAnd: + case LOr: + case LEqual: + case LNot: + case Lesser: + case Greater: + case LesserEqual: + case GreaterEqual: + check_params(); + + switch ( params_code->NumEntries ) + { + case 1: + is_member_symbol = true; + break; + + case 2: + break; + + default: + log_failure("gen::def_operator: operator%s recieved unexpected number of paramters recived %d instead of 1-2" + , to_str(op) + , params_code->NumEntries + ); + return OpValidateResult::Fail; + } + break; + + case Indirection: + case AddressOf: + case MemberOfPointer: + if ( params_code && params_code->NumEntries > 1) + { + log_failure("gen::def_operator: operator%s recieved unexpected number of paramters recived %d instead of 0-1" + , to_str(op) + , params_code->NumEntries + ); + return OpValidateResult::Fail; + } + else + { + is_member_symbol = true; + } + break; + + case PtrToMemOfPtr: + if ( params_code ) + { + log_failure("gen::def_operator: operator%s expects no paramters - %s", to_str(op), params_code.debug_str()); + return OpValidateResult::Fail; + } + break; + + case Subscript: + case FunctionCall: + case Comma: + check_params(); + break; +# undef specs + } + + return is_member_symbol ? OpValidateResult::Member : OpValidateResult::Global; +# undef check_params +# undef check_ret_type +# undef check_param_eq_ret +} + + +#pragma region Helper Marcos +// This snippet is used in nearly all the functions. +#define name_check( Context_, Name_ ) \ +{ \ + if ( Name_.Len <= 0 ) \ + { \ + log_failure( "gen::" stringize(Context_) ": Invalid name length provided - %d", Name_.Len ); \ + return CodeInvalid; \ + } \ + \ + if ( Name_.Ptr == nullptr ) \ + { \ + log_failure( "gen::" stringize(Context_) ": name is null" ); \ + return CodeInvalid; \ + } \ +} + +#define null_check( Context_, Code_ ) \ + if ( ! Code_ ) \ + { \ + log_failure( "gen::" stringize(Context_) ": " stringize(Code_) " provided is null" ); \ + return CodeInvalid; \ + } + +#define null_or_invalid_check( Context_, Code_ ) \ +{ \ + if ( ! Code_ ) \ + { \ + log_failure( "gen::" stringize(Context_) ": " stringize(Code_) " provided is null" ); \ + return CodeInvalid; \ + } \ + \ + if ( Code_->is_invalid() ) \ + { \ + log_failure("gen::" stringize(Context_) ": " stringize(Code_) " provided is invalid" ); \ + return CodeInvalid; \ + } \ +} + +#define not_implemented( Context_ ) \ + log_failure( "gen::%s: This function is not implemented" ); \ + return CodeInvalid; +#pragma endregion Helper Marcos + + +/* +The implementaiton of the upfront constructors involves bascially doing three things: +* Validate the arguments given to construct the intended type of AST is valid. +* Construct said AST type. +* Lock the AST (set to readonly) and return the valid object. + +If any of the validation fails, it triggers a call to log_failure with as much info the give the user so that they can hopefully +identify the issue without having to debug too much (at least they can debug though...) + +The largest of the functions is related to operator overload definitions. +The library validates a good protion of their form and thus the argument processing for is quite a bit. +*/ +CodeAttributes def_attributes( StrC content ) +{ + if ( content.Len <= 0 || content.Ptr == nullptr ) + { + log_failure( "gen::def_attributes: Invalid attributes provided" ); + return CodeInvalid; + } + + Code + result = make_code(); + result->Type = ECode::PlatformAttributes; + result->Name = get_cached_string( content ); + result->Content = result->Name; + + return (CodeAttributes) result; +} + +CodeComment def_comment( StrC content ) +{ + if ( content.Len <= 0 || content.Ptr == nullptr ) + { + log_failure( "gen::def_comment: Invalid comment provided:" ); + return CodeInvalid; + } + + Code + result = make_code(); + result->Type = ECode::Comment; + result->Name = get_cached_string( content ); + result->Content = result->Name; + + return (CodeComment) result; +} + +CodeClass def_class( StrC name + , Code body + , CodeType parent, AccessSpec parent_access + , CodeAttributes attributes + , ModuleFlag mflags + , CodeType* interfaces, s32 num_interfaces ) +{ + using namespace ECode; + + name_check( def_class, name ); + + if ( attributes && attributes->Type != PlatformAttributes ) + { + log_failure( "gen::def_class: attributes was not a 'PlatformAttributes' type: %s", attributes.debug_str() ); + return CodeInvalid; + } + + if ( parent && (parent->Type != Class || parent->Type != Struct || parent->Type != Typename || parent->Type != Untyped) ) + { + log_failure( "gen::def_class: parent provided is not type 'Class', 'Struct', 'Typeanme', or 'Untyped': %s", parent.debug_str() ); + return CodeInvalid; + } + + CodeClass + result = (CodeClass) make_code(); + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + + if ( body ) + { + switch ( body->Type ) + { + case Class_Body: + case Untyped: + break; + + default: + log_failure("gen::def_class: body must be either of Class_Body or Untyped type - %s", body.debug_str()); + return CodeInvalid; + } + + result->Type = Class; + result->Body = body; + } + else + { + result->Type = Class_Fwd; + } + + if ( attributes ) + result->Attributes = attributes; + + if ( parent ) + { + result->ParentAccess = parent_access; + result->ParentType = parent; + } + + if ( interfaces ) + { + for (s32 idx = 0; idx < num_interfaces; idx++ ) + { + result.add_interface( interfaces[idx] ); + } + } + + return result; +} + +CodeEnum def_enum( StrC name + , Code body, CodeType type + , EnumT specifier, CodeAttributes attributes + , ModuleFlag mflags ) +{ + using namespace ECode; + + name_check( def_enum, name ); + + if ( type && type->Type != Typename ) + { + log_failure( "gen::def_enum: enum underlying type provided was not of type Typename: %s", type.debug_str() ); + return CodeInvalid; + } + + if ( attributes && attributes->Type != PlatformAttributes ) + { + log_failure( "gen::def_enum: attributes was not a 'PlatformAttributes' type: %s", attributes.debug_str() ); + return CodeInvalid; + } + + CodeEnum + result = (CodeEnum) make_code(); + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + + if ( body ) + { + switch ( body->Type ) + { + case Enum_Body: + case Untyped: + break; + + default: + log_failure( "gen::def_enum: body must be of Enum_Body or Untyped type %s", body.debug_str()); + return CodeInvalid; + } + + result->Type = specifier == EnumClass ? + Enum_Class : Enum; + + result->Body = body; + } + else + { + result->Type = specifier == EnumClass ? + Enum_Class_Fwd : Enum_Fwd; + } + + if ( attributes ) + result->Attributes = attributes; + + if ( type ) + { + result->UnderlyingType = type; + } + else if ( result->Type != Enum_Class_Fwd && result->Type != Enum_Fwd ) + { + log_failure( "gen::def_enum: enum forward declaration must have an underlying type" ); + return CodeInvalid; + } + + return result; +} + +CodeExec def_execution( StrC content ) +{ + if ( content.Len <= 0 || content.Ptr == nullptr ) + { + log_failure( "gen::def_execution: Invalid execution provided" ); + return CodeInvalid; + } + + Code + result = make_code(); + result->Type = ECode::Execution; + result->Name = get_cached_string( content ); + result->Content = result->Name; + + return (CodeExec) result; +} + +CodeExtern def_extern_link( StrC name, Code body ) +{ + using namespace ECode; + + name_check( def_extern_linkage, name ); + null_check( def_extern_linkage, body ); + + if ( body->Type != Extern_Linkage_Body && body->Type != Untyped ) + { + log_failure("gen::def_extern_linkage: body is not of extern_linkage or untyped type %s", body->debug_str()); + return CodeInvalid; + } + + CodeExtern + result = (CodeExtern)make_code(); + result->Type = Extern_Linkage; + result->Name = get_cached_string( name ); + result->Body = body; + + return (CodeExtern) result; +} + +CodeFriend def_friend( Code declaration ) +{ + using namespace ECode; + + null_check( def_friend, declaration ); + + switch ( declaration->Type ) + { + case Class_Fwd: + case Function_Fwd: + case Operator_Fwd: + case Struct_Fwd: + case Class: + case Function: + case Operator: + case Struct: + break; + + default: + log_failure("gen::def_friend: requires declartion to have class, function, operator, or struct - %s", declaration->debug_str()); + return CodeInvalid; + } + + CodeFriend + result = (CodeFriend) make_code(); + result->Type = Friend; + + result->Declaration = declaration; + + return result; +} + +CodeFn def_function( StrC name + , CodeParam params , CodeType ret_type, Code body + , CodeSpecifiers specifiers, CodeAttributes attributes + , ModuleFlag mflags ) +{ + using namespace ECode; + + name_check( def_function, name ); + + if ( params && params->Type != Parameters ) + { + log_failure( "gen::def_function: params was not a `Parameters` type: %s", params.debug_str() ); + return CodeInvalid; + } + + if ( ret_type && ret_type->Type != Typename ) + { + log_failure( "gen::def_function: ret_type was not a Typename: %s", ret_type.debug_str() ); + return CodeInvalid; + } + + if ( specifiers && specifiers->Type != Specifiers ) + { + log_failure( "gen::def_function: specifiers was not a `Specifiers` type: %s", specifiers.debug_str() ); + return CodeInvalid; + } + + if ( attributes && attributes->Type != PlatformAttributes ) + { + log_failure( "gen::def_function: attributes was not a `PlatformAttributes` type: %s", attributes.debug_str() ); + return CodeInvalid; + } + + CodeFn + result = (CodeFn) make_code(); + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + + if ( body ) + { + switch ( body->Type ) + { + case Function_Body: + case Execution: + case Untyped: + break; + + default: + { + log_failure("gen::def_function: body must be either of Function_Body, Execution, or Untyped type. %s", body->debug_str()); + return CodeInvalid; + } + } + + result->Type = Function; + result->Body = body; + } + else + { + result->Type = Function_Fwd; + } + + if ( attributes ) + result->Attributes = attributes; + + if ( specifiers ) + result->Specs = specifiers; + + if ( ret_type ) + { + result->ReturnType = ret_type; + } + else + { + result->ReturnType = t_void; + } + + if ( params ) + result->Params = params; + + return result; +} + +CodeInclude def_include ( StrC path ) +{ + if ( path.Len <= 0 || path.Ptr == nullptr ) + { + log_failure( "gen::def_include: Invalid path provided - %d" ); + return CodeInvalid; + } + + Code + result = make_code(); + result->Type = ECode::Preprocessor_Include; + result->Name = get_cached_string( path ); + result->Content = result->Name; + + return (CodeInclude) result; +} + +CodeModule def_module( StrC name, ModuleFlag mflags ) +{ + name_check( def_module, name ); + + Code + result = make_code(); + result->Type = ECode::Module; + result->Name = get_cached_string( name ); + result->Content = result->Name; + result->ModuleFlags = mflags; + + return (CodeModule) result; +} + +CodeNamespace def_namespace( StrC name, Code body, ModuleFlag mflags ) +{ + using namespace ECode; + + name_check( def_namespace, name ); + null_check( def_namespace, body ); + + if ( body->Type != Namespace_Body && body->Type != Untyped ) + { + log_failure("gen::def_namespace: body is not of namespace or untyped type %s", body.debug_str()); + return CodeInvalid; + } + + CodeNamespace + result = (CodeNamespace) make_code(); + result->Type = Namespace; + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + result->Body = body; + + return result; +} + +CodeOperator def_operator( OperatorT op + , CodeParam params_code, CodeType ret_type, Code body + , CodeSpecifiers specifiers, CodeAttributes attributes + , ModuleFlag mflags ) +{ + using namespace ECode; + + if ( attributes && attributes->Type != PlatformAttributes ) + { + log_failure( "gen::def_operator: PlatformAttributes was provided but its not of attributes type: %s", attributes.debug_str() ); + return CodeInvalid; + } + + if ( specifiers && specifiers->Type != Specifiers ) + { + log_failure( "gen::def_operator: Specifiers was provided but its not of specifiers type: %s", specifiers.debug_str() ); + return CodeInvalid; + } + + OpValidateResult check_result = operator__validate( op, params_code, ret_type, specifiers ); + + if ( check_result == OpValidateResult::Fail ) + { + return CodeInvalid; + } + + char const* name = str_fmt_buf( "operator %s", to_str(op) ); + + CodeOperator + result = (CodeOperator) make_code(); + result->Name = get_cached_string( { str_len(name), name } ); + result->ModuleFlags = mflags; + + if ( body ) + { + switch ( body->Type ) + { + case Function_Body: + case Execution: + case Untyped: + break; + + default: + { + log_failure("gen::def_operator: body must be either of Function_Body, Execution, or Untyped type. %s", body->debug_str()); + return CodeInvalid; + } + } + + result->Type = check_result == OpValidateResult::Global ? + Operator : Operator_Member; + + result->Body = body; + } + else + { + result->Type = check_result == OpValidateResult::Global ? + Operator_Fwd : Operator_Member_Fwd; + } + + if ( attributes ) + result->Attributes = attributes; + + if ( specifiers ) + result->Specs = specifiers; + + result->ReturnType = ret_type; + + if (params_code) + result->Params = params_code; + + return result; +} + +CodeOpCast def_operator_cast( CodeType type, Code body, CodeSpecifiers const_spec ) +{ + using namespace ECode; + null_check( def_operator_cast, type ); + + if ( type->Type != Typename ) + { + log_failure( "gen::def_operator_cast: type is not a typename - %s", type.debug_str() ); + return CodeInvalid; + } + + CodeOpCast result = (CodeOpCast) make_code(); + + if (body) + { + result->Type = Operator_Cast; + + if ( body->Type != Function_Body && body->Type != Execution ) + { + log_failure( "gen::def_operator_cast: body is not of function body or execution type - %s", body.debug_str() ); + return CodeInvalid; + } + + result->Body = body; + } + else + { + result->Type = Operator_Cast_Fwd; + } + + if ( const_spec ) + { + result->Specs = const_spec; + } + + result->ValueType = type; + return result; +} + +CodeParam def_param( CodeType type, StrC name, Code value ) +{ + using namespace ECode; + + name_check( def_param, name ); + null_check( def_param, type ); + + if ( type->Type != Typename ) + { + log_failure( "gen::def_param: type is not a typename - %s", type.debug_str() ); + return CodeInvalid; + } + + if ( value && value->Type != Untyped ) + { + log_failure( "gen::def_param: value is not untyped - %s", value.debug_str() ); + return CodeInvalid; + } + + CodeParam + result = (CodeParam) make_code(); + result->Type = Parameters; + result->Name = get_cached_string( name ); + + result->ValueType = type; + + if ( value ) + result->Value = value; + + result->NumEntries++; + + return result; +} + +CodeSpecifiers def_specifier( SpecifierT spec ) +{ + CodeSpecifiers + result = (CodeSpecifiers) make_code(); + result->Type = ECode::Specifiers; + result.append( spec ); + + return result; +} + +CodeStruct def_struct( StrC name + , Code body + , CodeType parent, AccessSpec parent_access + , CodeAttributes attributes + , ModuleFlag mflags + , CodeType* interfaces, s32 num_interfaces ) +{ + using namespace ECode; + + if ( attributes && attributes->Type != PlatformAttributes ) + { + log_failure( "gen::def_struct: attributes was not a `PlatformAttributes` type - %s", attributes.debug_str() ); + return CodeInvalid; + } + + if ( parent && parent->Type != Typename ) + { + log_failure( "gen::def_struct: parent was not a `Struct` type - %s", parent.debug_str() ); + return CodeInvalid; + } + + if ( body && body->Type != Struct_Body ) + { + log_failure( "gen::def_struct: body was not a Struct_Body type - %s", body.debug_str() ); + return CodeInvalid; + } + + CodeStruct + result = (CodeStruct) make_code(); + result->ModuleFlags = mflags; + + if ( name ) + result->Name = get_cached_string( name ); + + if ( body ) + { + result->Type = Struct; + result->Body = body; + } + else + { + result->Type = Struct_Fwd; + } + + if ( attributes ) + result->Attributes = attributes; + + if ( parent ) + { + result->ParentAccess = parent_access; + result->ParentType = parent; + } + + if ( interfaces ) + { + for (s32 idx = 0; idx < num_interfaces; idx++ ) + { + result.add_interface( interfaces[idx] ); + } + } + + return result; +} + +CodeTemplate def_template( CodeParam params, Code declaration, ModuleFlag mflags ) +{ + null_check( def_template, declaration ); + + if ( params && params->Type != ECode::Parameters ) + { + log_failure( "gen::def_template: params is not of parameters type - %s", params.debug_str() ); + return CodeInvalid; + } + + switch (declaration->Type ) + { + case ECode::Class: + case ECode::Function: + case ECode::Struct: + case ECode::Variable: + case ECode::Using: + break; + + default: + log_failure( "gen::def_template: declaration is not of class, function, struct, variable, or using type - %s", declaration.debug_str() ); + } + + CodeTemplate + result = (CodeTemplate) make_code(); + result->Type = ECode::Template; + result->ModuleFlags = mflags; + result->Params = params; + result->Declaration = declaration; + + return result; +} + +CodeType def_type( StrC name, Code arrayexpr, CodeSpecifiers specifiers, CodeAttributes attributes ) +{ + name_check( def_type, name ); + + if ( attributes && attributes->Type != ECode::PlatformAttributes ) + { + log_failure( "gen::def_type: attributes is not of attributes type - %s", attributes.debug_str() ); + return CodeInvalid; + } + + if ( specifiers && specifiers->Type != ECode::Specifiers ) + { + log_failure( "gen::def_type: specifiers is not of specifiers type - %s", specifiers.debug_str() ); + return CodeInvalid; + } + + if ( arrayexpr && arrayexpr->Type != ECode::Untyped ) + { + log_failure( "gen::def_type: arrayexpr is not of untyped type - %s", arrayexpr->debug_str() ); + return CodeInvalid; + } + + CodeType + result = (CodeType) make_code(); + result->Name = get_cached_string( name ); + result->Type = ECode::Typename; + + if ( attributes ) + result->Attributes = attributes; + + if ( specifiers ) + result->Specs = specifiers; + + if ( arrayexpr ) + result->ArrExpr = arrayexpr; + + return result; +} + +CodeTypedef def_typedef( StrC name, Code type, CodeAttributes attributes, ModuleFlag mflags ) +{ + using namespace ECode; + + name_check( def_typedef, name ); + null_check( def_typedef, type ); + + switch ( type->Type ) + { + case Class: + case Class_Fwd: + case Enum: + case Enum_Fwd: + case Enum_Class: + case Enum_Class_Fwd: + case Function_Fwd: + case Struct: + case Struct_Fwd: + case Union: + case Typename: + break; + default: + log_failure( "gen::def_typedef: type was not a Class, Enum, Function Forward, Struct, Typename, or Union - %s", type.debug_str() ); + return CodeInvalid; + } + + if ( attributes && attributes->Type != ECode::PlatformAttributes ) + { + log_failure( "gen::def_typedef: attributes was not a PlatformAttributes - %s", attributes.debug_str() ); + return CodeInvalid; + } + + // Registering the type. + Code registered_type = def_type( name ); + + if ( ! registered_type ) + { + log_failure( "gen::def_typedef: failed to register type" ); + return CodeInvalid; + } + + CodeTypedef + result = (CodeTypedef) make_code(); + result->Name = get_cached_string( name ); + result->Type = ECode::Typedef; + result->ModuleFlags = mflags; + + result->UnderlyingType = type; + + return result; +} + +CodeUnion def_union( StrC name, Code body, CodeAttributes attributes, ModuleFlag mflags ) +{ + null_check( def_union, body ); + + if ( body->Type != ECode::Union_Body ) + { + log_failure( "gen::def_union: body was not a Union_Body type - %s", body.debug_str() ); + return CodeInvalid; + } + + if ( attributes && attributes->Type != ECode::PlatformAttributes ) + { + log_failure( "gen::def_union: attributes was not a PlatformAttributes type - %s", attributes.debug_str() ); + return CodeInvalid; + } + + CodeUnion + result = (CodeUnion) make_code(); + result->ModuleFlags = mflags; + result->Type = ECode::Union; + + if ( name.Ptr ) + result->Name = get_cached_string( name ); + + result->Body = body; + + if ( attributes ) + result->Attributes = attributes; + + return result; +} + +CodeUsing def_using( StrC name, CodeType type + , CodeAttributes attributes + , ModuleFlag mflags ) +{ + name_check( def_using, name ); + null_check( def_using, type ); + + Code register_type = def_type( name ); + + if ( ! register_type ) + { + log_failure( "gen::def_using: failed to register type" ); + return CodeInvalid; + } + + if ( attributes && attributes->Type != ECode::PlatformAttributes ) + { + log_failure( "gen::def_using: attributes was not a PlatformAttributes type - %s", attributes.debug_str() ); + return CodeInvalid; + } + + CodeUsing + result = (CodeUsing) make_code(); + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + result->Type = ECode::Using; + + result->UnderlyingType = type; + + if ( attributes ) + result->Attributes = attributes; + + return result; +} + +CodeUsing def_using_namespace( StrC name ) +{ + name_check( def_using_namespace, name ); + + Code + result = make_code(); + result->Name = get_cached_string( name ); + result->Content = result->Name; + result->Type = ECode::Using_Namespace; + + return (CodeUsing) result; +} + +CodeVar def_variable( CodeType type, StrC name, Code value + , CodeSpecifiers specifiers, CodeAttributes attributes + , ModuleFlag mflags ) +{ + name_check( def_variable, name ); + null_check( def_variable, type ); + + if ( attributes && attributes->Type != ECode::PlatformAttributes ) + { + log_failure( "gen::def_variable: attributes was not a `PlatformAttributes` type - %s", attributes.debug_str() ); + return CodeInvalid; + } + + if ( specifiers && specifiers->Type != ECode::Specifiers ) + { + log_failure( "gen::def_variable: specifiers was not a `Specifiers` type - %s", specifiers.debug_str() ); + return CodeInvalid; + } + + if ( type->Type != ECode::Typename ) + { + log_failure( "gen::def_variable: type was not a Typename - %s", type.debug_str() ); + return CodeInvalid; + } + + if ( value && value->Type != ECode::Untyped ) + { + log_failure( "gen::def_variable: value was not a `Untyped` type - %s", value.debug_str() ); + return CodeInvalid; + } + + CodeVar + result = (CodeVar) make_code(); + result->Name = get_cached_string( name ); + result->Type = ECode::Variable; + result->ModuleFlags = mflags; + + result->ValueType = type; + + if ( attributes ) + result->Attributes = attributes; + + if ( specifiers ) + result->Specs = specifiers; + + if ( value ) + result->Value = value; + + return result; +} + +/* +Body related functions typically follow the same implementation pattern. +Opted to use inline helper macros to get the implementaiton done. + +The implementation pattern is as follows: +* Validate a valid parameter num was provided, or code array + def_body_start or def_body_code_array_start( ) + +* Begin the code entry do-while loop, make sure each entry is valid processing its type in the switc + def_body_code_validation_start( ) + +* Define the switch case statements between the macros. + +* Add the code entry, finish the closing implemenation for the do-while loop. + def_body_code_validation_end( ) + +* Lock the body AST and return it. + +If a function's implementation deviates from the macros then its just writen it out. +*/ + +#pragma region Helper Macros for def_**_body functions +#define def_body_start( Name_ ) \ +using namespace ECode; \ + \ +if ( num <= 0 ) \ +{ \ + log_failure("gen::" stringize(Name_) ": num cannot be zero or negative"); \ + return CodeInvalid; \ +} + +#define def_body_code_array_start( Name_ ) \ +using namespace ECode; \ + \ +if ( num <= 0 ) \ +{ \ + log_failure("gen::" stringize(Name_) ": num cannot be zero or negative"); \ + return CodeInvalid; \ +} \ + \ +if ( codes == nullptr ) \ +{ \ + log_failure("gen::" stringize(Name_)" : Provided a null array of codes"); \ + return CodeInvalid; \ +} + +#define def_body_code_validation_start( Name_ ) \ +do \ +{ \ + Code_POD pod = va_arg(va, Code_POD); \ + Code entry = pcast(Code, pod); \ + \ + if ( ! entry ) \ + { \ + log_failure("gen::" stringize(Name_) ": Provided an null entry"); \ + return CodeInvalid; \ + } \ + \ + switch ( entry->Type ) \ + { + +#define def_body_code_array_validation_start( Name_ ) \ +do \ +{ \ + Code entry = *codes; codes++; \ + \ + if ( ! entry ) \ + { \ + log_failure("gen::" stringize(Name_) ": Provided an null entry"); \ + return CodeInvalid; \ + } \ + \ + switch ( entry->Type ) \ + { + +#define def_body_code_validation_end( Name_ ) \ + log_failure("gen::" stringize(Name_) ": Entry type is not allowed: %s", entry.debug_str() ); \ + return CodeInvalid; \ + \ + default: \ + break; \ + } \ + \ + result.append( entry ); \ +} \ +while ( num--, num > 0 ) +#pragma endregion Helper Macros for def_**_body functions + +CodeBody def_class_body( s32 num, ... ) +{ + def_body_start( def_class_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Class_Body; + + va_list va; + va_start(va, num); + def_body_code_validation_start( def_class_body ); + AST_BODY_CLASS_UNALLOWED_TYPES + def_body_code_validation_end( def_class_body ); + va_end(va); + + return result; +} + +CodeBody def_class_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_class_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Function_Body; + + def_body_code_array_validation_start( def_class_body ); + AST_BODY_CLASS_UNALLOWED_TYPES + def_body_code_validation_end( def_class_body ); + + return result; +} + +CodeBody def_enum_body( s32 num, ... ) +{ + def_body_start( def_enum_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Enum_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + + if ( ! entry ) + { + log_failure("gen::def_enum_body: Provided a null entry"); + return CodeInvalid; + } + + if ( entry->Type != Untyped && entry->Type != Comment ) + { + log_failure("gen::def_enum_body: Entry type is not allowed - %s. Must be of untyped or comment type.", entry.debug_str() ); \ + return CodeInvalid; + } + + result.append( entry ); + } + while ( num--, num > 0 ); + va_end(va); + + return (CodeBody) result; +} + +CodeBody def_enum_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_enum_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Enum_Body; + + do + { + Code entry = *codes; + + if ( ! entry ) + { + log_failure("gen::def_enum_body: Provided a null entry"); + return CodeInvalid; + } + + if ( entry->Type != Untyped && entry->Type != Comment ) + { + log_failure("gen::def_enum_body: Entry type is not allowed: %s", entry.debug_str() ); \ + return CodeInvalid; + } + + result.append( entry ); + } + while ( codes++, num--, num > 0 ); + + return result; +} + +CodeBody def_export_body( s32 num, ... ) +{ + def_body_start( def_export_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Export_Body; + + va_list va; + va_start(va, num); + def_body_code_validation_start( def_export_body ); + AST_BODY_EXPORT_UNALLOWED_TYPES + def_body_code_validation_end( def_export_body ); + va_end(va); + + return result; +} + +CodeBody def_export_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_export_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Export_Body; + + def_body_code_array_validation_start( def_export_body ); + AST_BODY_EXPORT_UNALLOWED_TYPES + def_body_code_validation_end( def_export_body ); + + return result; +} + +CodeBody def_extern_link_body( s32 num, ... ) +{ + def_body_start( def_extern_linkage_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Extern_Linkage_Body; + + va_list va; + va_start(va, num); + def_body_code_validation_start( def_extern_linkage_body ); + AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES + def_body_code_validation_end( def_extern_linkage_body ); + va_end(va); + + return result; +} + +CodeBody def_extern_link_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_extern_linkage_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Extern_Linkage_Body; + + def_body_code_array_validation_start( def_extern_linkage_body ); + AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES + def_body_code_validation_end( def_extern_linkage_body ); + + return result; +} + +CodeBody def_function_body( s32 num, ... ) +{ + def_body_start( def_function_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Function_Body; + + va_list va; + va_start(va, num); + def_body_code_validation_start( def_function_body ); + AST_BODY_FUNCTION_UNALLOWED_TYPES + def_body_code_validation_end( def_function_body ); + va_end(va); + + return result; +} + +CodeBody def_function_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_function_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Function_Body; + + def_body_code_array_validation_start( def_function_body ); + AST_BODY_FUNCTION_UNALLOWED_TYPES + def_body_code_validation_end( def_function_body ); + + return result; +} + +CodeBody def_global_body( s32 num, ... ) +{ + def_body_start( def_global_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Global_Body; + + va_list va; + va_start(va, num); + def_body_code_validation_start( def_global_body ); + AST_BODY_GLOBAL_UNALLOWED_TYPES + def_body_code_validation_end( def_global_body ); + va_end(va); + + return result; +} + +CodeBody def_global_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_global_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Global_Body; + + def_body_code_array_validation_start( def_global_body ); + AST_BODY_GLOBAL_UNALLOWED_TYPES + def_body_code_validation_end( def_global_body ); + + return result; +} + +CodeBody def_namespace_body( s32 num, ... ) +{ + def_body_start( def_namespace_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Namespace_Body; + + va_list va; + va_start(va, num); + def_body_code_validation_start( def_namespace_body ); + AST_BODY_NAMESPACE_UNALLOWED_TYPES + def_body_code_validation_end( def_namespace_body ); + va_end(va); + + return result; +} + +CodeBody def_namespace_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_namespace_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Global_Body; + + def_body_code_array_validation_start( def_namespace_body ); + AST_BODY_NAMESPACE_UNALLOWED_TYPES + def_body_code_validation_end( def_namespace_body ); + + return result; +} + +CodeParam def_params( s32 num, ... ) +{ + def_body_start( def_params ); + + va_list va; + va_start(va, num); + + Code_POD pod = va_arg(va, Code_POD); + CodeParam param = pcast( CodeParam, pod ); + + null_check( def_params, param ); + + if ( param->Type != Parameters ) + { + log_failure( "gen::def_params: param %d is not a Parameters", num - num + 1 ); + return CodeInvalid; + } + + CodeParam result = (CodeParam) param.duplicate(); + + while ( -- num ) + { + pod = va_arg(va, Code_POD); + param = pcast( CodeParam, pod ); + + if ( param->Type != Parameters ) + { + log_failure( "gen::def_params: param %d is not a Parameters", num - num + 1 ); + return CodeInvalid; + } + + result.append( param ); + } + va_end(va); + + return result; +} + +CodeParam def_params( s32 num, CodeParam* codes ) +{ + def_body_code_array_start( def_params ); + +# define check_current() \ + if ( current.ast == nullptr ) \ + { \ + log_failure("gen::def_params: Provide a null code in codes array"); \ + return CodeInvalid; \ + } \ + \ + if (current->Type != Parameters ) \ + { \ + log_failure("gen::def_params: Code in coes array is not of paramter type - %s", current.debug_str() ); \ + return CodeInvalid; \ + } + + CodeParam current = (CodeParam) codes->duplicate(); + check_current(); + + CodeParam + result = (CodeParam) make_code(); + result->Name = current->Name; + result->Type = current->Type; + result->ValueType = current->ValueType; + + while( codes++, current = * codes, num--, num > 0 ) + { + check_current(); + result.append( current ); + } +# undef check_current + + return result; +} + +CodeSpecifiers def_specifiers( s32 num, ... ) +{ + if ( num <= 0 ) + { + log_failure("gen::def_specifiers: num cannot be zero or less"); + return CodeInvalid; + } + + if ( num > AST::ArrSpecs_Cap ) + { + log_failure("gen::def_specifiers: num of speciifers to define AST larger than AST specicifier capacity - %d", num); + return CodeInvalid; + } + + CodeSpecifiers + result = (CodeSpecifiers) make_code(); + result->Type = ECode::Specifiers; + + va_list va; + va_start(va, num); + do + { + SpecifierT type = (SpecifierT)va_arg(va, int); + + result.append( type ); + } + while ( --num, num ); + va_end(va); + + return result; +} + +CodeSpecifiers def_specifiers( s32 num, SpecifierT* specs ) +{ + if ( num <= 0 ) + { + log_failure("gen::def_specifiers: num cannot be zero or less"); + return CodeInvalid; + } + + if ( num > AST::ArrSpecs_Cap ) + { + log_failure("gen::def_specifiers: num of speciifers to define AST larger than AST specicifier capacity - %d", num); + return CodeInvalid; + } + + CodeSpecifiers + result = (CodeSpecifiers) make_code(); + result->Type = ECode::Specifiers; + + s32 idx = 0; + do + { + result.append( specs[idx] ); + idx++; + } + while ( --num, num ); + + return result; +} + +CodeBody def_struct_body( s32 num, ... ) +{ + def_body_start( def_struct_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Struct_Body; + + va_list va; + va_start(va, num); + def_body_code_validation_start( def_struct_body ); + AST_BODY_STRUCT_UNALLOWED_TYPES + def_body_code_validation_end( def_struct_body ); + va_end(va); + + return result; +} + +CodeBody def_struct_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_struct_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Struct_Body; + + def_body_code_array_validation_start( def_struct_body ); + AST_BODY_STRUCT_UNALLOWED_TYPES + def_body_code_validation_end( def_struct_body ); + + return result; +} + +CodeBody def_union_body( s32 num, ... ) +{ + def_body_start( def_union_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Union_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast( Code, pod ); + + if ( ! entry ) + { + log_failure("gen::def_union_body: Provided a null entry"); + return CodeInvalid; + } + + if ( entry->Type != Untyped && entry->Type != Comment ) + { + log_failure("gen::def_union_body: Entry type is not allowed - %s. Must be of untyped or comment type.", entry.debug_str() ); \ + return CodeInvalid; + } + + result.append( entry ); + } + while ( num--, num > 0 ); + va_end(va); + + return result; +} + +CodeBody def_union_body( s32 num, CodeUnion* codes ) +{ + def_body_code_array_start( def_union_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Union_Body; + + do + { + Code entry = *codes; + + if ( ! entry ) + { + log_failure("gen::def_union_body: Provided a null entry"); + return CodeInvalid; + } + + if ( entry->Type != Untyped && entry->Type != Comment ) + { + log_failure("gen::def_union_body: Entry type is not allowed: %s", entry.debug_str() ); + return CodeInvalid; + } + + result.append( entry ); + } + while ( codes++, num--, num > 0 ); + + return (CodeBody) result; +} + +# undef name_check +# undef null_check +# undef null_or_invalid_check + diff --git a/project/components/gen.types.hpp b/project/components/gen.types.hpp index e69de29..6910691 100644 --- a/project/components/gen.types.hpp +++ b/project/components/gen.types.hpp @@ -0,0 +1,363 @@ +using LogFailType = sw(*)(char const*, ...); + +// By default this library will either crash or exit if an error is detected while generating codes. +// Even if set to not use fatal, fatal will still be used for memory failures as the library is unusable when they occur. +#ifdef GEN_DONT_USE_FATAL + constexpr LogFailType log_failure = log_fmt; +#else + constexpr LogFailType log_failure = fatal; +#endif + +namespace ECode +{ +# define Define_Types \ + Entry( Untyped ) \ + Entry( Comment ) \ + Entry( Access_Private ) \ + Entry( Access_Protected ) \ + Entry( Access_Public ) \ + Entry( PlatformAttributes ) \ + Entry( Class ) \ + Entry( Class_Fwd ) \ + Entry( Class_Body ) \ + Entry( Enum ) \ + Entry( Enum_Fwd ) \ + Entry( Enum_Body ) \ + Entry( Enum_Class ) \ + Entry( Enum_Class_Fwd ) \ + Entry( Execution ) \ + Entry( Export_Body ) \ + Entry( Extern_Linkage ) \ + Entry( Extern_Linkage_Body ) \ + Entry( Friend ) \ + Entry( Function ) \ + Entry( Function_Fwd ) \ + Entry( Function_Body ) \ + Entry( Global_Body ) \ + Entry( Module ) \ + Entry( Namespace ) \ + Entry( Namespace_Body ) \ + Entry( Operator ) \ + Entry( Operator_Fwd ) \ + Entry( Operator_Member ) \ + Entry( Operator_Member_Fwd ) \ + Entry( Operator_Cast ) \ + Entry( Operator_Cast_Fwd ) \ + Entry( Parameters ) \ + Entry( Preprocessor_Include ) \ + Entry( Specifiers ) \ + Entry( Struct ) \ + Entry( Struct_Fwd ) \ + Entry( Struct_Body ) \ + Entry( Template ) \ + Entry( Typedef ) \ + Entry( Typename ) \ + Entry( Union ) \ + Entry( Union_Body) \ + Entry( Using ) \ + Entry( Using_Namespace ) \ + Entry( Variable ) + + enum Type : u32 + { + # define Entry( Type ) Type, + Define_Types + # undef Entry + + Num_Types, + Invalid + }; + + inline + StrC to_str( Type type ) + { + static + StrC lookup[Num_Types] = { + # define Entry( Type ) { sizeof(stringize(Type)), stringize(Type) }, + Define_Types + # undef Entry + }; + + return lookup[ type ]; + } + +# undef Define_Types +} +using CodeT = ECode::Type; + +// Used to indicate if enum definitoin is an enum class or regular enum. +enum class EnumT : u8 +{ + Regular, + Class +}; + +constexpr EnumT EnumClass = EnumT::Class; +constexpr EnumT EnumRegular = EnumT::Regular; + +namespace EOperator +{ +# define Define_Operators \ + Entry( Assign, = ) \ + Entry( Assign_Add, += ) \ + Entry( Assign_Subtract, -= ) \ + Entry( Assign_Multiply, *= ) \ + Entry( Assign_Divide, /= ) \ + Entry( Assign_Modulo, %= ) \ + Entry( Assign_BAnd, &= ) \ + Entry( Assign_BOr, |= ) \ + Entry( Assign_BXOr, ^= ) \ + Entry( Assign_LShift, <<= ) \ + Entry( Assign_RShift, >>= ) \ + Entry( Increment, ++ ) \ + Entry( Decrement, -- ) \ + Entry( Unary_Plus, + ) \ + Entry( Unary_Minus, - ) \ + Entry( UnaryNot, ! ) \ + Entry( Add, + ) \ + Entry( Subtract, - ) \ + Entry( Multiply, * ) \ + Entry( Divide, / ) \ + Entry( Modulo, % ) \ + Entry( BNot, ~ ) \ + Entry( BAnd, & ) \ + Entry( BOr, | ) \ + Entry( BXOr, ^ ) \ + Entry( LShift, << ) \ + Entry( RShift, >> ) \ + Entry( LAnd, && ) \ + Entry( LOr, || ) \ + Entry( LEqual, == ) \ + Entry( LNot, != ) \ + Entry( Lesser, < ) \ + Entry( Greater, > ) \ + Entry( LesserEqual, <= ) \ + Entry( GreaterEqual, >= ) \ + Entry( Subscript, [] ) \ + Entry( Indirection, * ) \ + Entry( AddressOf, & ) \ + Entry( MemberOfPointer, -> ) \ + Entry( PtrToMemOfPtr, ->* ) \ + Entry( FunctionCall, () ) + + enum Type : u32 + { + # define Entry( Type_, Token_ ) Type_, + Define_Operators + # undef Entry + Comma, + + Num_Ops, + Invalid + }; + + inline + char const* to_str( Type op ) + { + local_persist + char const* lookup[ Num_Ops ] = { + # define Entry( Type_, Token_ ) stringize(Token_), + Define_Operators + # undef Entry + "," + }; + + return lookup[ op ]; + } + +# undef Define_Operators +} +using OperatorT = EOperator::Type; + +namespace ESpecifier +{ +/* + Note: The following are handled separately: + attributes + alignas +*/ + +# define Define_Specifiers \ + Entry( Invalid, INVALID ) \ + Entry( Consteval, consteval ) \ + Entry( Constexpr, constexpr ) \ + Entry( Constinit, constinit ) \ + Entry( Explicit, explicit ) \ + Entry( External_Linkage, extern ) \ + Entry( Global, global ) \ + Entry( Inline, inline ) \ + Entry( Internal_Linkage, internal ) \ + Entry( Local_Persist, local_persist ) \ + Entry( Mutable, mutable ) \ + Entry( Ptr, * ) \ + Entry( Ref, & ) \ + Entry( Register, register ) \ + Entry( RValue, && ) \ + Entry( Static, static ) \ + Entry( Thread_Local, thread_local ) \ + Entry( Volatile, volatile ) \ + Entry( Virtual, virtual ) \ + Entry( Const, const ) \ + Entry( Final, final ) \ + Entry( Override, override ) + + enum Type : u32 + { + # define Entry( Specifier, Code ) Specifier, + Define_Specifiers + # undef Entry + + Num_Specifiers, + }; + + inline + bool is_trailing( Type specifier ) + { + return specifier > Virtual; + } + + // Specifier to string + inline + StrC to_str( Type specifier ) + { + local_persist + StrC lookup[ Num_Specifiers ] = { + # pragma push_macro( "global" ) + # pragma push_macro( "internal" ) + # pragma push_macro( "local_persist" ) + # undef global + # undef internal + # undef local_persist + + # define Entry( Spec_, Code_ ) { sizeof(stringize(Code_)), stringize(Code_) }, + Define_Specifiers + # undef Entry + + # pragma pop_macro( "global" ) + # pragma pop_macro( "internal" ) + # pragma pop_macro( "local_persist" ) + }; + + return lookup[ specifier ]; + } + + inline + Type to_type( StrC str ) + { + local_persist + u32 keymap[ Num_Specifiers ]; + do_once_start + for ( u32 index = 0; index < Num_Specifiers; index++ ) + { + StrC enum_str = to_str( (Type)index ); + + // We subtract 1 to remove the null terminator + // This is because the tokens lexed are not null terminated. + keymap[index] = crc32( enum_str.Ptr, enum_str.Len - 1); + } + do_once_end + + u32 hash = crc32( str.Ptr, str.Len ); + + for ( u32 index = 0; index < Num_Specifiers; index++ ) + { + if ( keymap[index] == hash ) + return (Type)index; + } + + return Invalid; + } + +# undef Define_Specifiers +} +using SpecifierT = ESpecifier::Type; + +enum class AccessSpec : u32 +{ + Default, + Public, + Protected, + Private, + + Num_AccessSpec, + Invalid, +}; + +inline +char const* to_str( AccessSpec type ) +{ + local_persist + char const* lookup[ (u32)AccessSpec::Num_AccessSpec ] = { + "", + "public", + "protected", + "private", + }; + + if ( type > AccessSpec::Public ) + return "Invalid"; + + return lookup[ (u32)type ]; +} + +enum class ModuleFlag : u32 +{ + None = 0, + Export = bit(0), + Import = bit(1), + // Private = bit(2), + + Num_ModuleFlags, + Invalid, +}; + +ModuleFlag operator|( ModuleFlag A, ModuleFlag B) +{ + return (ModuleFlag)( (u32)A | (u32)B ); +} + +/* + Predefined attributes + Used for the parser constructors to identify non-standard attributes + + Override these to change the attribute to your own unique identifier convention. + + The tokenizer identifies attribute defines with the GEN_Define_Attribute_Tokens macros. + See the example below and the Define_TokType macro used in gen.cpp to know the format. + While the library can parse raw attributes, most projects use defines to wrap them for compiler + platform indendence. The token define allows support for them without having to modify the library. +*/ +#if defined(GEN_SYSTEM_WINDOWS) || defined( __CYGWIN__ ) +#ifndef GEN_Attribute_Keyword +# define GEN_API_Export_Code __declspec(dllexport) +# define GEN_API_Import_Code __declspec(dllimport) +# define GEN_Attribute_Keyword __declspec +#endif + +constexpr char const* Attribute_Keyword = stringize( GEN_Attribute_Keyword); + +#elif GEN_HAS_ATTRIBUTE( visibility ) || GEN_GCC_VERSION_CHECK( 3, 3, 0 ) +#ifndef GEN_Attribute_Keyword +# define GEN_API_Export_Code __attribute__ ((visibility ("default"))) +# define GEN_API_Import_Code __attribute__ ((visibility ("default"))) +# define GEN_Attribute_Keyword __attribute__ +#endif + +constexpr char const* Attribute_Keyword = stringize( GEN_Attribute_Keyword ); + +#else +#ifndef GEN_Attribute_Keyword +# define GEN_API_Export_Code +# define GEN_API_Import_Code +# define GEN_Attribute_Keyword +#endif + +constexpr char const* Attribute_Keyword = ""; +#endif + +#ifndef GEN_Define_Attribute_Tokens +# define GEN_Define_Attribute_Tokens \ + Entry( API_Export, "GEN_API_Export_Code" ) \ + Entry( API_Import, "GEN_API_Import_Code" ) +#endif + diff --git a/project/components/gen.untyped.cpp b/project/components/gen.untyped.cpp new file mode 100644 index 0000000..c65cdc0 --- /dev/null +++ b/project/components/gen.untyped.cpp @@ -0,0 +1,148 @@ +sw token_fmt_va( char* buf, uw buf_size, s32 num_tokens, va_list va ) +{ + char const* buf_begin = buf; + sw remaining = buf_size; + + local_persist + Arena tok_map_arena; + + HashTable tok_map; + { + local_persist + char tok_map_mem[ TokenFmt_TokenMap_MemSize ]; + + tok_map_arena = Arena::init_from_memory( tok_map_mem, sizeof(tok_map_mem) ); + + tok_map = HashTable::init( tok_map_arena ); + + s32 left = num_tokens - 1; + + while ( left-- ) + { + char const* token = va_arg( va, char const* ); + StrC value = va_arg( va, StrC ); + + u32 key = crc32( token, str_len(token) ); + + tok_map.set( key, value ); + } + } + + char const* fmt = va_arg( va, char const* ); + char current = *fmt; + + while ( current ) + { + sw len = 0; + + while ( current && current != '<' && remaining ) + { + * buf = * fmt; + buf++; + fmt++; + remaining--; + + current = * fmt; + } + + if ( current == '<' ) + { + char const* scanner = fmt + 1; + + s32 tok_len = 0; + + while ( *scanner != '>' ) + { + tok_len++; + scanner++; + } + + char const* token = fmt + 1; + + u32 key = crc32( token, tok_len ); + StrC* value = tok_map.get( key ); + + if ( value ) + { + sw left = value->Len; + char const* str = value->Ptr; + + while ( left-- ) + { + * buf = * str; + buf++; + str++; + remaining--; + } + + scanner++; + fmt = scanner; + current = * fmt; + continue; + } + + * buf = * fmt; + buf++; + fmt++; + remaining--; + + current = * fmt; + } + } + + tok_map.clear(); + tok_map_arena.free(); + + sw result = buf_size - remaining; + + return result; +} + +Code untyped_str( StrC content ) +{ + Code + result = make_code(); + result->Name = get_cached_string( content ); + result->Type = ECode::Untyped; + result->Content = result->Name; + + return result; +} + +Code untyped_fmt( char const* fmt, ...) +{ + local_persist thread_local + char buf[GEN_PRINTF_MAXLEN] = { 0 }; + + va_list va; + va_start(va, fmt); + sw length = str_fmt_va(buf, GEN_PRINTF_MAXLEN, fmt, va); + va_end(va); + + Code + result = make_code(); + result->Name = get_cached_string( { str_len(fmt, MaxNameLength), fmt } ); + result->Type = ECode::Untyped; + result->Content = get_cached_string( { length, buf } ); + + return result; +} + +Code untyped_token_fmt( s32 num_tokens, ... ) +{ + local_persist thread_local + char buf[GEN_PRINTF_MAXLEN] = { 0 }; + + va_list va; + va_start(va, num_tokens); + sw length = token_fmt_va(buf, GEN_PRINTF_MAXLEN, num_tokens, va); + va_end(va); + + Code + result = make_code(); + result->Name = get_cached_string( { length, buf } ); + result->Type = ECode::Untyped; + result->Content = result->Name; + + return result; +} diff --git a/project/filesystem/gen.builder.cpp b/project/filesystem/gen.builder.cpp new file mode 100644 index 0000000..b8d5bc5 --- /dev/null +++ b/project/filesystem/gen.builder.cpp @@ -0,0 +1,43 @@ +void Builder::print( Code code ) +{ + Buffer.append_fmt( "%s\n", code->to_string() ); +} + +void Builder::print_fmt( char const* fmt, ... ) +{ + sw res; + char buf[ GEN_PRINTF_MAXLEN ] = { 0 }; + + va_list va; + va_start( va, fmt ); + res = str_fmt_va( buf, count_of( buf ) - 1, fmt, va ) - 1; + va_end( va ); + + Buffer.append( buf, res ); +} + +bool Builder::open( char const* path ) +{ + FileError error = file_open_mode( & File, EFileMode_WRITE, path ); + + if ( error != EFileError_NONE ) + { + log_failure( "gen::File::open - Could not open file: %s", path); + return false; + } + + Buffer = String::make_reserve( GlobalAllocator, Builder_StrBufferReserve ); + + return true; +} + +void Builder::write() +{ + bool result = file_write( & File, Buffer, Buffer.length() ); + + if ( result == false ) + log_failure("gen::File::write - Failed to write to file: %s", file_name( & File ) ); + + file_close( & File ); + Buffer.free(); +} diff --git a/project/filesystem/gen.builder.hpp b/project/filesystem/gen.builder.hpp index e69de29..fd02e10 100644 --- a/project/filesystem/gen.builder.hpp +++ b/project/filesystem/gen.builder.hpp @@ -0,0 +1,11 @@ +struct Builder +{ + FileInfo File; + String Buffer; + + void print( Code ); + void print_fmt( char const* fmt, ... ); + + bool open( char const* path ); + void write(); +}; diff --git a/project/gen.cpp b/project/gen.cpp index aa312e6..af73402 100644 --- a/project/gen.cpp +++ b/project/gen.cpp @@ -1,3 +1,5 @@ +#pragma once + // ReSharper disable CppClangTidyClangDiagnosticSwitchEnum #if ! defined(GEN_DONT_ENFORCE_GEN_TIME_GUARD) && ! defined(GEN_TIME) @@ -16,6857 +18,19 @@ GEN_NS_BEGIN -#pragma region StaticData -// TODO : Convert global allocation strategy to use a slab allocation strategy. -global AllocatorInfo GlobalAllocator; -global Array Global_AllocatorBuckets; +#include "components/gen.data.cpp" -global Array< Pool > CodePools = { nullptr }; -global Array< Arena > StringArenas = { nullptr }; +#include "components/gen.ast_case_macros.cpp" +#include "components/gen.ast.cpp" -global StringTable StringCache; +#include "components/gen.interface.cpp" +#include "components/gen.interface.upfront.cpp" +#include "components/gen.interface.parsing.cpp" +#include "components/gen.untyped.cpp" -global Arena LexArena; - -global AllocatorInfo Allocator_DataArrays = heap(); -global AllocatorInfo Allocator_CodePool = heap(); -global AllocatorInfo Allocator_Lexer = heap(); -global AllocatorInfo Allocator_StringArena = heap(); -global AllocatorInfo Allocator_StringTable = heap(); -global AllocatorInfo Allocator_TypeTable = heap(); -#pragma endregion StaticData - -#pragma region Constants -global CodeType t_empty; -global CodeType t_auto; -global CodeType t_void; -global CodeType t_int; -global CodeType t_bool; -global CodeType t_char; -global CodeType t_wchar_t; -global CodeType t_class; -global CodeType t_typename; - -#ifdef GEN_DEFINE_LIBRARY_CODE_CONSTANTS -global CodeType t_b32; - -global CodeType t_s8; -global CodeType t_s16; -global CodeType t_s32; -global CodeType t_s64; - -global CodeType t_u8; -global CodeType t_u16; -global CodeType t_u32; -global CodeType t_u64; - -global CodeType t_sw; -global CodeType t_uw; - -global CodeType t_f32; -global CodeType t_f64; -#endif - -global CodeParam param_varadic; - -global CodeAttributes attrib_api_export; -global CodeAttributes attrib_api_import; - -global Code access_public; -global Code access_protected; -global Code access_private; - -global Code module_global_fragment; -global Code module_private_fragment; - -global Code pragma_once; - -global CodeSpecifiers spec_const; -global CodeSpecifiers spec_consteval; -global CodeSpecifiers spec_constexpr; -global CodeSpecifiers spec_constinit; -global CodeSpecifiers spec_extern_linkage; -global CodeSpecifiers spec_final; -global CodeSpecifiers spec_global; -global CodeSpecifiers spec_inline; -global CodeSpecifiers spec_internal_linkage; -global CodeSpecifiers spec_local_persist; -global CodeSpecifiers spec_mutable; -global CodeSpecifiers spec_override; -global CodeSpecifiers spec_ptr; -global CodeSpecifiers spec_ref; -global CodeSpecifiers spec_register; -global CodeSpecifiers spec_rvalue; -global CodeSpecifiers spec_static_member; -global CodeSpecifiers spec_thread_local; -global CodeSpecifiers spec_virtual; -global CodeSpecifiers spec_volatile; -#pragma endregion Constants - -#pragma region AST Body Case Macros -# define AST_BODY_CLASS_UNALLOWED_TYPES \ -case PlatformAttributes: \ -case Class_Body: \ -case Enum_Body: \ -case Extern_Linkage: \ -case Function_Body: \ -case Function_Fwd: \ -case Global_Body: \ -case Namespace: \ -case Namespace_Body: \ -case Operator: \ -case Operator_Fwd: \ -case Parameters: \ -case Specifiers: \ -case Struct_Body: \ -case Typename: - -# define AST_BODY_FUNCTION_UNALLOWED_TYPES \ -case Access_Public: \ -case Access_Protected: \ -case Access_Private: \ -case PlatformAttributes: \ -case Class_Body: \ -case Enum_Body: \ -case Extern_Linkage: \ -case Friend: \ -case Function_Body: \ -case Function_Fwd: \ -case Global_Body: \ -case Namespace: \ -case Namespace_Body: \ -case Operator: \ -case Operator_Fwd: \ -case Operator_Member: \ -case Operator_Member_Fwd: \ -case Parameters: \ -case Specifiers: \ -case Struct_Body: \ -case Typename: - -# define AST_BODY_GLOBAL_UNALLOWED_TYPES \ -case Access_Public: \ -case Access_Protected: \ -case Access_Private: \ -case PlatformAttributes: \ -case Class_Body: \ -case Enum_Body: \ -case Execution: \ -case Friend: \ -case Function_Body: \ -case Global_Body: \ -case Namespace_Body: \ -case Operator_Member: \ -case Operator_Member_Fwd: \ -case Parameters: \ -case Specifiers: \ -case Struct_Body: \ -case Typename: - -# define AST_BODY_EXPORT_UNALLOWED_TYPES AST_BODY_GLOBAL_UNALLOWED_TYPES -# define AST_BODY_NAMESPACE_UNALLOWED_TYPES AST_BODY_GLOBAL_UNALLOWED_TYPES -# define AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES AST_BODY_GLOBAL_UNALLOWED_TYPES - -# define AST_BODY_STRUCT_UNALLOWED_TYPES AST_BODY_CLASS_UNALLOWED_TYPES -#pragma endregion AST Body Case Macros - -#pragma region AST -Code Code::Global; -Code Code::Invalid; - -AST* AST::duplicate() -{ - using namespace ECode; - - AST* - result = make_code().ast; -#ifndef GEN_USE_RECURSIVE_AST_DUPLICATION - mem_copy( result, this, sizeof( AST ) ); - result->Parent = nullptr; -#else - // TODO : Stress test this... - switch ( Type ) - { - case Invalid: - log_failure("Attempted to duplicate invalid code! - \n%s", Parent ? Parent->debug_str() : Name ); - return nullptr - case Untyped: - case Comment: - case Execution: - case Access_Private: - case Access_Protected: - case Access_Public: - case PlatformAttributes: - case Preprocessor_Include: - case Module: - case Specifiers: - case Using_Namespace: - mem_copy( result, this, sizeof( AST ) ); - break; - - case Extern_Linkage: - case Friend: - mem_copy( result, this, sizeof( AST ) ); - - if (Value) - result->Value = Value->duplicate(); - break; - - case Class: - case Struct: - case Enum: - mem_copy( result, this, sizeof( AST ) ); - - if ( Attributes) - result->Attributes = Attributes->duplicate(); - - if ( ParentType ) - result->ParentType = ParentType->duplicate(); - - result->Body = Body->duplicate(); - break; - - case Enum_Fwd: - case Class_Fwd: - case Struct_Fwd: - mem_copy( result, this, sizeof( AST ) ); - - if ( Attributes) - result->Attributes = Attributes->duplicate(); - - if ( ParentType ) - result->ParentType = ParentType->duplicate(); - break; - - case Function: - case Operator: - case Operator_Member: - mem_copy( result, this, sizeof( AST ) ); - - if ( Attributes) - result->Attributes = Attributes->duplicate(); - - if ( Specs ) - result->ParentType = ParentType->duplicate(); - - if ( ReturnType ) - result->ReturnType = ReturnType->duplicate(); - - if ( Params ) - result->Params = Params->duplicate(); - - result->Body = Body->duplicate(); - break; - - case Function_Fwd: - case Operator_Fwd: - case Operator_Member_Fwd: - mem_copy( result, this, sizeof( AST ) ); - - if ( Attributes) - result->Attributes = Attributes->duplicate(); - - if ( Specs ) - result->ParentType = ParentType->duplicate(); - - if ( ReturnType ) - result->ReturnType = ReturnType->duplicate(); - - if ( Params ) - result->Params = Params->duplicate(); - break; - - case Namespace: - mem_copy( result, this, sizeof( AST ) ); - - result->Body = Body->duplicate(); - break; - - case Operator_Cast: - mem_copy( result, this, sizeof( AST ) ); - - result->ValueType = ValueType->duplicate(); - result->Body = Body->duplicate(); - break; - case Operator_Cast_Fwd: - mem_copy( result, this, sizeof( AST ) ); - - result->ValueType = ValueType->duplicate(); - break; - - case Parameters: - mem_copy( result, this, sizeof( AST ) ); - - result->NumEntries = 0; - result->Last = nullptr; - result->Next = nullptr; - - if ( NumEntries - 1 > 0 ) - { - CodeParam parent = result->cast(); - for ( CodeParam param : Next->cast() ) - { - parent.append( param ); - } - } - break; - - case Template: - mem_copy( result, this, sizeof( AST ) ); - - result->Params = Params->duplicate(); - result->Declaration = Declaration->duplicate(); - break; - - case Typename: - mem_copy( result, this, sizeof( AST ) ); - - if (Attributes) - result->Attributes = Attributes->duplicate(); - - if ( Specs ) - result->Specs = Specs->duplicate(); - - if ( ArrExpr ) - result->ArrExpr = ArrExpr->duplicate(); - break; - - case Typedef: - case Using: - mem_copy( result, this, sizeof( AST ) ); - - if (Attributes) - result->Attributes = Attributes->duplicate(); - - if ( UnderlyingType ) - result->UnderlyingType = UnderlyingType->duplicate(); - break; - - case Union: - mem_copy( result, this, sizeof( AST ) ); - - if ( Attributes) - result->Attributes = Attributes->duplicate(); - - result->Body = Body->duplicate(); - break; - - case Variable: - mem_copy( result, this, sizeof( AST ) ); - - if (Attributes) - result->Attributes = Attributes->duplicate(); - - if ( Specs ) - result->Specs = Specs->duplicate(); - - result->ValueType = UnderlyingType->duplicate(); - - if ( Value ) - result->Value = Value->duplicate(); - break; - - case Class_Body: - case Enum_Body: - case Export_Body: - case Extern_Linkage_Body: - case Function_Body: - case Global_Body: - case Namespace_Body: - case Struct_Body: - case Union_Body: - CodeBody - body = cast(); - body->Name = Name; - body->Type = Type; - for ( Code entry : cast() ) - { - result->append( entry.ast ); - } - break; - } -#endif - - return result; -} - -String AST::to_string() -{ -# define ProcessModuleFlags() \ - if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export )) \ - result.append( "export " ); \ - \ - if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Import )) \ - result.append( "import " ); \ - - local_persist thread_local - char SerializationLevel = 0; - -#if defined(GEN_BENCHMARK) && defined(GEN_BENCHMARK_SERIALIZATION) - u64 time_start = time_rel_ms(); -#endif - - // TODO : Need to refactor so that intermeidate strings are freed conviently. - String result = String::make( GlobalAllocator, "" ); - - switch ( Type ) - { - using namespace ECode; - - case Invalid: - log_failure("Attempted to serialize invalid code! - %s", Parent ? Parent->debug_str() : Name ); - break; - - case Untyped: - case Execution: - result.append( Content ); - break; - - case Comment: - { - static char line[MaxCommentLineLength]; - - s32 left = Content.length(); - s32 index = 0; - do - { - s32 length = 0; - while ( left && Content[index] != '\n' ) - { - length++; - left--; - } - - str_copy( line, Content, length ); - line[length] = '\0'; - - result.append_fmt( "// %s", line ); - } - while ( left--, left > 0 ); - } - break; - - case Access_Private: - case Access_Protected: - case Access_Public: - result.append( Name ); - break; - - case PlatformAttributes: - result.append( Content ); - - case Class: - { - ProcessModuleFlags(); - - if ( Attributes || ParentType ) - { - result.append( "class " ); - - if ( Attributes ) - { - result.append_fmt( "%s ", Attributes->to_string() ); - } - - if ( ParentType ) - { - char const* access_level = to_str( ParentAccess ); - - result.append_fmt( "%s : %s %s\n{\n%s\n};" - , Name - , access_level - , ParentType->to_string() - , Body->to_string() - ); - - CodeType interface = Next->cast(); - if ( interface ) - result.append("\n"); - - while ( interface ) - { - result.append_fmt( ", %s", interface.to_string() ); - - interface = interface->Next->cast(); - } - } - else - { - result.append_fmt( "%s \n{\n%s\n};", Name, Body->to_string() ); - } - } - else - { - result.append_fmt( "class %s\n{\n%s\n};", Name, Body->to_string() ); - } - } - break; - - case Class_Fwd: - { - ProcessModuleFlags(); - - if ( Attributes ) - result.append_fmt( "class %s %s;", Attributes->to_string(), Name ); - - else result.append_fmt( "class %s;", Name ); - } - break; - - case Enum: - { - ProcessModuleFlags(); - - if ( Attributes || UnderlyingType ) - { - result.append( "enum " ); - - if ( Attributes ) - result.append_fmt( "%s ", Attributes->to_string() ); - - if ( UnderlyingType ) - result.append_fmt( "%s : %s\n{\n%s\n};" - , Name - , UnderlyingType->to_string() - , Body->to_string() - ); - - else result.append_fmt( "%s\n{\n%s\n};" - , Name - , Body->to_string() - ); - } - else result.append_fmt( "enum %s\n{\n%s\n};" - , Name - , Body->to_string() - ); - } - break; - - case Enum_Fwd: - { - ProcessModuleFlags(); - - if ( Attributes ) - result.append_fmt( "%s ", Attributes->to_string() ); - - result.append_fmt( "enum %s : %s;", Name, UnderlyingType->to_string() ); - } - break; - - case Enum_Class: - { - ProcessModuleFlags(); - - if ( Attributes || UnderlyingType ) - { - result.append( "enum class " ); - - if ( Attributes ) - { - result.append_fmt( "%s ", Attributes->to_string() ); - } - - if ( UnderlyingType ) - { - result.append_fmt( "%s : %s\n{\n%s\n};" - , Name - , UnderlyingType->to_string() - , Body->to_string() - ); - } - else - { - result.append_fmt( "%s\n{\n%s\n};" - , Name - , Body->to_string() - ); - } - } - else - { - result.append_fmt( "enum class %s\n{\n%s\n};" - , Body->to_string() - ); - } - } - break; - - case Enum_Class_Fwd: - { - ProcessModuleFlags(); - - result.append( "enum class " ); - - if ( Attributes ) - result.append_fmt( "%s ", Attributes->to_string() ); - - result.append_fmt( "%s : %s;", Name, UnderlyingType->to_string() ); - } - break; - - case Export_Body: - { - result.append_fmt( "export\n{\n" ); - - Code curr = { this }; - s32 left = NumEntries; - while ( left-- ) - { - result.append_fmt( "%s\n", curr.to_string() ); - ++curr; - } - - result.append_fmt( "};" ); - } - break; - - case Extern_Linkage: - result.append_fmt( "extern \"%s\"\n{\n%s\n}" - , Name - , Body->to_string() - ); - break; - - case Friend: - result.append_fmt( "friend %s", Declaration->to_string() ); - - if ( result[ result.length() -1 ] != ';' ) - result.append( ";" ); - break; - - case Function: - { - ProcessModuleFlags(); - - if ( Attributes ) - result.append_fmt( "%s ", Attributes->to_string() ); - - if ( Specs ) - result.append_fmt( "%s\n", Specs->to_string() ); - - if ( ReturnType ) - result.append_fmt( "%s %s(", ReturnType->to_string(), Name ); - - else - result.append_fmt( "%s(", Name ); - - if ( Params ) - result.append_fmt( "%s)", Params->to_string() ); - - else - result.append( "void)" ); - - if ( Specs ) - { - CodeSpecifiers specs = cast(); - - for ( SpecifierT spec : specs ) - { - if ( ESpecifier::is_trailing( spec ) ) - result.append_fmt( " %s", (char const*)ESpecifier::to_str( spec ) ); - } - } - - result.append_fmt( "\n{\n%s\n}" - , Body->to_string() - ); - } - break; - - case Function_Fwd: - { - ProcessModuleFlags(); - - if ( Attributes ) - result.append_fmt( "%s ", Attributes->to_string() ); - - if ( Specs ) - result.append_fmt( "%s\n", Specs->to_string() ); - - if ( ReturnType ) - result.append_fmt( "%s %s(", ReturnType->to_string(), Name ); - - else - result.append_fmt( "%s(", Name ); - - if ( Params ) - result.append_fmt( "%s)", Params->to_string() ); - - else - result.append( "void)" ); - - if ( Specs ) - { - CodeSpecifiers specs = cast(); - - for ( SpecifierT spec : specs ) - { - if ( ESpecifier::is_trailing( spec ) ) - result.append_fmt( " %s", (char const*)ESpecifier::to_str( spec ) ); - } - } - - result.append( ";" ); - } - break; - - case Module: - if (((u32(ModuleFlag::Export) & u32(ModuleFlags)) == u32(ModuleFlag::Export))) - result.append("export "); - - if (((u32(ModuleFlag::Import) & u32(ModuleFlags)) == u32(ModuleFlag::Import))) - result.append("import "); - - result.append_fmt( "%s;", Name ); - break; - - case Namespace: - ProcessModuleFlags(); - - result.append_fmt( "namespace %s\n{\n%s}" - , Name - , Body->to_string() - ); - break; - - case Operator: - case Operator_Member: - { - ProcessModuleFlags(); - - if ( Attributes ) - result.append_fmt( "%s ", Attributes->to_string() ); - - if ( Attributes ) - result.append_fmt( "%s\n", Attributes->to_string() ); - - if ( ReturnType ) - result.append_fmt( "%s %s (", ReturnType->to_string(), Name ); - - if ( Params ) - result.append_fmt( "%s)", Params->to_string() ); - - else - result.append( "void)" ); - - if ( Specs ) - { - CodeSpecifiers specs = cast(); - - for ( SpecifierT spec : specs ) - { - if ( ESpecifier::is_trailing( spec ) ) - result.append_fmt( " %s", (char const*)ESpecifier::to_str( spec ) ); - } - } - - result.append_fmt( "\n{\n%s\n}" - , Body->to_string() - ); - } - break; - - case Operator_Fwd: - case Operator_Member_Fwd: - { - ProcessModuleFlags(); - - if ( Attributes ) - result.append_fmt( "%s ", Attributes->to_string() ); - - if ( Specs ) - result.append_fmt( "%s", Specs->to_string() ); - - result.append_fmt( "%s %s (", ReturnType->to_string(), Name ); - - if ( Params ) - result.append_fmt( "%s)", Params->to_string() ); - - else - result.append_fmt( "void)" ); - - if ( Specs ) - { - CodeSpecifiers specs = cast(); - - for ( SpecifierT spec : specs ) - { - if ( ESpecifier::is_trailing( spec ) ) - result.append_fmt( " %s", (char const*)ESpecifier::to_str( spec ) ); - } - } - - result.append( ";" ); - } - break; - - case Operator_Cast: - { - if ( Specs ) - { - result.append_fmt( "operator %s()" ); - - CodeSpecifiers specs = cast(); - - for ( SpecifierT spec : specs ) - { - if ( ESpecifier::is_trailing( spec ) ) - result.append_fmt( " %s", (char const*)ESpecifier::to_str( spec ) ); - } - - result.append_fmt( "\n{\n%s\n}", Body->to_string() ); - break; - } - - result.append_fmt("operator %s()\n{\n%s\n}", ValueType->to_string(), Body->to_string() ); - } - break; - - case Operator_Cast_Fwd: - if ( Specs ) - { - result.append_fmt( "operator %s()" ); - - CodeSpecifiers specs = cast(); - - for ( SpecifierT spec : specs ) - { - if ( ESpecifier::is_trailing( spec ) ) - result.append_fmt( " %s", (char const*)ESpecifier::to_str( spec ) ); - } - - result.append_fmt( ";", Body->to_string() ); - break; - } - - result.append_fmt("operator %s();", ValueType->to_string() ); - break; - - case Parameters: - { - if ( Name ) - result.append_fmt( "%s %s", ValueType->to_string(), Name ); - - else - result.append_fmt( "%s", ValueType->to_string() ); - - if ( Value ) - result.append_fmt( "= %s", Value->to_string() ); - - if ( NumEntries - 1 > 0) - { - for ( CodeParam param : CodeParam { (AST_Param*) Next } ) - { - result.append_fmt( ", %s", param.to_string() ); - } - } - } - break; - - case Preprocessor_Include: - result.append_fmt( "#include \"%s\"", Name ); - break; - - case Specifiers: - { - s32 idx = 0; - s32 left = NumEntries; - while ( left-- ) - { - if ( ESpecifier::is_trailing( ArrSpecs[idx]) ) - { - idx++; - continue; - } - - result.append_fmt( "%s ", (char const*)ESpecifier::to_str( ArrSpecs[idx]) ); - idx++; - } - } - break; - - case Struct: - { - ProcessModuleFlags(); - - if ( Name == nullptr) - { - result.append( "struct\n{\n%s\n};", Body->to_string() ); - break; - } - - if ( Attributes || ParentType ) - { - result.append( "struct " ); - - if ( Attributes ) - result.append_fmt( "%s ", Attributes->to_string() ); - - if ( ParentType ) - { - char const* access_level = to_str( ParentAccess ); - - result.append_fmt( "%s : %s %s\n{\n%s\n};" - , Name - , access_level - , ParentType->to_string() - , Body->to_string() - ); - - CodeType interface = Next->cast(); - if ( interface ) - result.append("\n"); - - while ( interface ) - { - result.append_fmt( ", public %s", interface.to_string() ); - - interface = interface->Next->cast(); - } - } - else - { - if ( Name ) - - result.append_fmt( "%s \n{\n%s\n};", Name, Body->to_string() ); - } - } - else - { - result.append_fmt( "struct %s\n{\n%s\n};", Name, Body->to_string() ); - } - } - break; - - case Struct_Fwd: - { - ProcessModuleFlags(); - - if ( Attributes ) - result.append_fmt( "struct %s %s;", Attributes->to_string(), Name ); - - else result.append_fmt( "struct %s;", Name ); - } - break; - - case Template: - { - ProcessModuleFlags(); - - result.append_fmt( "template< %s >\n%s", Params->to_string(), Declaration->to_string() ); - } - break; - - case Typedef: - { - ProcessModuleFlags(); - - result.append( "typedef "); - - result.append_fmt( "%s %s", UnderlyingType->to_string(), Name ); - - if ( UnderlyingType->Type == Typename && UnderlyingType->ArrExpr ) - { - result.append_fmt( "[%s];", UnderlyingType->ArrExpr->to_string() ); - } - else - { - result.append( ";" ); - } - } - break; - - case Typename: - { - if ( Attributes || Specs ) - { - if ( Attributes ) - result.append_fmt( "%s ", Attributes->to_string() ); - - if ( Specs ) - result.append_fmt( "%s %s", Name, Specs->to_string() ); - - else - result.append_fmt( "%s", Name ); - } - else - { - result.append_fmt( "%s", Name ); - } - } - break; - - case Union: - { - ProcessModuleFlags(); - - result.append( "union " ); - - if ( Attributes ) - result.append_fmt( "%s ", Attributes->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; - - case Using: - { - ProcessModuleFlags(); - - if ( Attributes ) - result.append_fmt( "%s ", Attributes->to_string() ); - - if ( UnderlyingType ) - { - result.append_fmt( "using %s = %s", Name, UnderlyingType->to_string() ); - - if ( UnderlyingType->ArrExpr ) - result.append_fmt( "[%s]", UnderlyingType->ArrExpr->to_string() ); - - result.append( ";" ); - } - else - result.append_fmt( "using %s;", Name ); - } - break; - - case Using_Namespace: - result.append_fmt( "using namespace %s;", Name ); - break; - - case Variable: - { - ProcessModuleFlags(); - - if ( Attributes || Specs ) - { - if ( Attributes ) - result.append_fmt( "%s ", Specs->to_string() ); - - if ( Specs ) - result.append_fmt( "%s\n", Specs->to_string() ); - - result.append_fmt( "%s %s", ValueType->to_string(), Name ); - - if ( ValueType->ArrExpr ) - result.append_fmt( "[%s]", ValueType->ArrExpr->to_string() ); - - if ( Value ) - result.append_fmt( " = %s", Value->to_string() ); - - result.append( ";" ); - - break; - } - - if ( UnderlyingType->ArrExpr ) - result.append_fmt( "%s %s[%s];", UnderlyingType->to_string(), Name, UnderlyingType->ArrExpr->to_string() ); - - else - result.append_fmt( "%s %s;", UnderlyingType->to_string(), Name ); - } - break; - - case Class_Body: - case Enum_Body: - case Extern_Linkage_Body: - case Function_Body: - case Global_Body: - case Namespace_Body: - case Struct_Body: - case Union_Body: - { - Code curr = Front->cast(); - s32 left = NumEntries; - while ( left -- ) - { - result.append_fmt( "%s\n", curr.to_string() ); - ++curr; - } - } - break; - } - -#if defined(GEN_BENCHMARK) && defined(GEN_BENCHMARK_SERIALIZATION) - log_fmt("AST::to_string() time taken: %llu for: %s\n", time_rel_ms() - time_start, result ); -#endif - return result; -#undef ProcessModuleFlags -} - -bool AST::is_equal( AST* other ) -{ - if ( Type != other->Type ) - return false; - - switch ( Type ) - { - case ECode::Typedef: - case ECode::Typename: - { - if ( Name != other->Name ) - return false; - - return true; - } - } - - if ( Name != other->Name ) - return false; - - return true; -} - -bool AST::validate_body() -{ - using namespace ECode; - -#define CheckEntries( Unallowed_Types ) \ - do \ - { \ - for ( Code entry : cast() ) \ - { \ - switch ( entry->Type ) \ - { \ - Unallowed_Types \ - log_failure( "AST::validate_body: Invalid entry in body %s", entry.debug_str() ); \ - return false; \ - } \ - } \ - } \ - while (0); - - switch ( Type ) - { - case Class_Body: - CheckEntries( AST_BODY_CLASS_UNALLOWED_TYPES ); - break; - case Enum_Body: - for ( Code entry : cast() ) - { - if ( entry->Type != Untyped ) - { - log_failure( "AST::validate_body: Invalid entry in enum body (needs to be untyped or comment) %s", entry.debug_str() ); - return false; - } - } - break; - case Export_Body: - CheckEntries( AST_BODY_CLASS_UNALLOWED_TYPES ); - break; - case Extern_Linkage: - CheckEntries( AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES ); - break; - case Function_Body: - CheckEntries( AST_BODY_FUNCTION_UNALLOWED_TYPES ); - break; - case Global_Body: - CheckEntries( AST_BODY_GLOBAL_UNALLOWED_TYPES ); - break; - case Namespace_Body: - CheckEntries( AST_BODY_NAMESPACE_UNALLOWED_TYPES ); - break; - case Struct_Body: - CheckEntries( AST_BODY_STRUCT_UNALLOWED_TYPES ); - break; - case Union_Body: - for ( Code entry : Body->cast() ) - { - if ( entry->Type != Untyped ) - { - log_failure( "AST::validate_body: Invalid entry in union body (needs to be untyped or comment) %s", entry.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 -internal -void* Global_Allocator_Proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ) -{ - Arena* last = & Global_AllocatorBuckets.back(); - - switch ( type ) - { - case EAllocation_ALLOC: - { - if ( ( last->TotalUsed + size ) > last->TotalSize ) - { - Arena bucket = Arena::init_from_allocator( heap(), Global_BucketSize ); - - if ( bucket.PhysicalStart == nullptr ) - fatal( "Failed to create bucket for Global_AllocatorBuckets"); - - if ( ! Global_AllocatorBuckets.append( bucket ) ) - fatal( "Failed to append bucket to Global_AllocatorBuckets"); - - last = & Global_AllocatorBuckets.back(); - } - - return alloc_align( * last, size, alignment ); - } - case EAllocation_FREE: - { - // Doesn't recycle. - } - break; - case EAllocation_FREE_ALL: - { - // Memory::cleanup instead. - } - break; - case EAllocation_RESIZE: - { - if ( last->TotalUsed + size > last->TotalSize ) - { - Arena bucket = Arena::init_from_allocator( heap(), Global_BucketSize ); - - if ( bucket.PhysicalStart == nullptr ) - fatal( "Failed to create bucket for Global_AllocatorBuckets"); - - if ( ! Global_AllocatorBuckets.append( bucket ) ) - fatal( "Failed to append bucket to Global_AllocatorBuckets"); - - last = & Global_AllocatorBuckets.back(); - } - - void* result = alloc_align( last->Backing, size, alignment ); - - if ( result != nullptr && old_memory != nullptr ) - { - mem_copy( result, old_memory, old_size ); - } - - return result; - } - } - - return nullptr; -} - -internal -void define_constants() -{ - Code::Global = make_code(); - Code::Global->Name = get_cached_string( txt_StrC("Global Code") ); - Code::Global->Content = Code::Global->Name; - - Code::Invalid = make_code(); - Code::Invalid.set_global(); - -# define def_constant_code_type( Type_ ) \ - t_##Type_ = def_type( name(Type_) ); \ - t_##Type_.set_global(); - - def_constant_code_type( auto ); - def_constant_code_type( void ); - def_constant_code_type( int ); - 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) ); - - def_constant_code_type( s8 ); - def_constant_code_type( s16 ); - def_constant_code_type( s32 ); - def_constant_code_type( s64 ); - - def_constant_code_type( u8 ); - def_constant_code_type( u16 ); - def_constant_code_type( u32 ); - def_constant_code_type( u64 ); - - def_constant_code_type( sw ); - def_constant_code_type( uw ); - - def_constant_code_type( f32 ); - def_constant_code_type( f64 ); -#endif -# undef def_constant_code_type - - t_empty = (CodeType) make_code(); - t_empty->Type = ECode::Typename; - t_empty->Name = get_cached_string( txt_StrC("") ); - t_empty.set_global(); - - param_varadic = (CodeType) make_code(); - param_varadic->Type = ECode::Parameters; - param_varadic->Name = get_cached_string( txt_StrC("...") ); - param_varadic->ValueType = t_empty; - param_varadic.set_global(); - - attrib_api_export = def_attributes( code(GEN_API_Export_Code)); - attrib_api_export.set_global(); - - attrib_api_import = def_attributes( code(GEN_API_Import_Code)); - attrib_api_import.set_global(); - - access_private = make_code(); - access_private->Type = ECode::Access_Private; - access_private->Name = get_cached_string( txt_StrC("private:") ); - access_private.set_global(); - - access_protected = make_code(); - access_protected->Type = ECode::Access_Protected; - access_protected->Name = get_cached_string( txt_StrC("protected:") ); - access_protected.set_global(); - - access_public = make_code(); - access_public->Type = ECode::Access_Public; - access_public->Name = get_cached_string( txt_StrC("public:") ); - access_public.set_global(); - - module_global_fragment = make_code(); - module_global_fragment->Type = ECode::Untyped; - module_global_fragment->Name = get_cached_string( txt_StrC("module;") ); - module_global_fragment->Content = module_global_fragment->Name; - module_global_fragment.set_global(); - - module_private_fragment = make_code(); - module_private_fragment->Type = ECode::Untyped; - module_private_fragment->Name = get_cached_string( txt_StrC("module : private;") ); - module_private_fragment->Content = module_private_fragment->Name; - module_private_fragment.set_global(); - - pragma_once = make_code(); - pragma_once->Type = ECode::Untyped; - pragma_once->Name = get_cached_string( txt_StrC("#pragma once") ); - pragma_once->Content = pragma_once->Name; - pragma_once.set_global(); - -# pragma push_macro( "global" ) -# pragma push_macro( "internal" ) -# pragma push_macro( "local_persist" ) -# undef global -# undef internal -# undef local_persist - -# define def_constant_spec( Type_, ... ) \ - spec_##Type_ = def_specifiers( num_args(__VA_ARGS__), __VA_ARGS__); \ - spec_##Type_.set_global(); - - def_constant_spec( const, ESpecifier::Const ); - def_constant_spec( consteval, ESpecifier::Consteval ); - def_constant_spec( constexpr, ESpecifier::Constexpr ); - def_constant_spec( constinit, ESpecifier::Constinit ); - def_constant_spec( extern_linkage, ESpecifier::External_Linkage ); - def_constant_spec( final, ESpecifier::Final ); - def_constant_spec( global, ESpecifier::Global ); - def_constant_spec( inline, ESpecifier::Inline ); - def_constant_spec( internal_linkage, ESpecifier::Internal_Linkage ); - def_constant_spec( local_persist, ESpecifier::Local_Persist ); - def_constant_spec( mutable, ESpecifier::Mutable ); - def_constant_spec( override, ESpecifier::Override ); - def_constant_spec( ptr, ESpecifier::Ptr ); - def_constant_spec( ref, ESpecifier::Ref ); - def_constant_spec( register, ESpecifier::Register ); - def_constant_spec( rvalue, ESpecifier::RValue ); - def_constant_spec( static_member, ESpecifier::Static ); - def_constant_spec( thread_local, ESpecifier::Thread_Local ); - def_constant_spec( virtual, ESpecifier::Virtual ); - def_constant_spec( volatile, ESpecifier::Volatile) - - spec_local_persist = def_specifiers( 1, ESpecifier::Local_Persist ); - spec_local_persist.set_global(); - -# pragma pop_macro( "global" ) -# pragma pop_macro( "internal" ) -# pragma pop_macro( "local_persist" ) - -# undef def_constant_spec -} - -void init() -{ - // Setup global allocator - { - GlobalAllocator = AllocatorInfo { & Global_Allocator_Proc, nullptr }; - - Global_AllocatorBuckets = Array::init_reserve( heap(), 128 ); - - if ( Global_AllocatorBuckets == nullptr ) - fatal( "Failed to reserve memory for Global_AllocatorBuckets"); - - Arena bucket = Arena::init_from_allocator( heap(), Global_BucketSize ); - - if ( bucket.PhysicalStart == nullptr ) - fatal( "Failed to create first bucket for Global_AllocatorBuckets"); - - Global_AllocatorBuckets.append( bucket ); - - } - - // Setup the arrays - { - CodePools = Array::init_reserve( Allocator_DataArrays, InitSize_DataArrays ); - - if ( CodePools == nullptr ) - fatal( "gen::init: Failed to initialize the CodePools array" ); - - StringArenas = Array::init_reserve( Allocator_DataArrays, InitSize_DataArrays ); - - if ( StringArenas == nullptr ) - fatal( "gen::init: Failed to initialize the StringArenas array" ); - } - - // Setup the code pool and code entries arena. - { - Pool code_pool = Pool::init( Allocator_CodePool, CodePool_NumBlocks, sizeof(AST) ); - - if ( code_pool.PhysicalStart == nullptr ) - fatal( "gen::init: Failed to initialize the code pool" ); - - CodePools.append( code_pool ); - - LexArena = Arena::init_from_allocator( Allocator_Lexer, LexAllocator_Size ); - - Arena string_arena = Arena::init_from_allocator( Allocator_StringArena, SizePer_StringArena ); - - if ( string_arena.PhysicalStart == nullptr ) - fatal( "gen::init: Failed to initialize the string arena" ); - - StringArenas.append( string_arena ); - } - - // Setup the hash tables - { - StringCache = StringTable::init( Allocator_StringTable ); - - if ( StringCache.Entries == nullptr ) - fatal( "gen::init: Failed to initialize the StringCache"); - } - - define_constants(); -} - -void deinit() -{ - uw index = 0; - uw left = CodePools.num(); - do - { - Pool* code_pool = & CodePools[index]; - code_pool->free(); - index++; - } - while ( left--, left ); - - index = 0; - left = StringArenas.num(); - do - { - Arena* string_arena = & StringArenas[index]; - string_arena->free(); - index++; - } - while ( left--, left ); - - StringCache.destroy(); - - CodePools.free(); - StringArenas.free(); - - LexArena.free(); - - index = 0; - left = Global_AllocatorBuckets.num(); - do - { - Arena* bucket = & Global_AllocatorBuckets[ index ]; - bucket->free(); - index++; - } - while ( left--, left ); - - Global_AllocatorBuckets.free(); -} - -void reset() -{ - s32 index = 0; - s32 left = CodePools.num(); - do - { - Pool* code_pool = & CodePools[index]; - code_pool->clear(); - index++; - } - while ( left--, left ); - - index = 0; - left = StringArenas.num(); - do - { - Arena* string_arena = & StringArenas[index]; - string_arena->TotalUsed = 0;; - index++; - } - while ( left--, left ); - - StringCache.clear(); - - define_constants(); -} - -AllocatorInfo get_string_allocator( s32 str_length ) -{ - Arena* last = & StringArenas.back(); - - uw size_req = str_length + sizeof(String::Header) + sizeof(char*); - - if ( last->TotalUsed + size_req > last->TotalSize ) - { - Arena new_arena = Arena::init_from_allocator( Allocator_StringArena, SizePer_StringArena ); - - if ( ! StringArenas.append( new_arena ) ) - fatal( "gen::get_string_allocator: Failed to allocate a new string arena" ); - - last = & StringArenas.back(); - } - - return * last; -} - -// Will either make or retrive a code string. -StringCached get_cached_string( StrC str ) -{ - s32 hash_length = str.Len > kilobytes(1) ? kilobytes(1) : str.Len; - u64 key = crc32( str.Ptr, hash_length ); - { - StringCached* result = StringCache.get( key ); - - if ( result ) - return * result; - } - - String result = String::make( get_string_allocator( str.Len ), str ); - - StringCache.set( key, result ); - - return result; -} - -/* - Used internally to retireve a Code object form the CodePool. -*/ -Code make_code() -{ - Pool* allocator = & CodePools.back(); - if ( allocator->FreeList == nullptr ) - { - Pool code_pool = Pool::init( Allocator_CodePool, CodePool_NumBlocks, sizeof(AST) ); - - if ( code_pool.PhysicalStart == nullptr ) - fatal( "gen::make_code: Failed to allocate a new code pool - CodePool allcoator returned nullptr." ); - - if ( ! CodePools.append( code_pool ) ) - fatal( "gen::make_code: Failed to allocate a new code pool - CodePools failed to append new pool." ); - - allocator = & CodePools.back(); - } - - Code result { rcast( AST*, alloc( * allocator, sizeof(AST) )) }; - - result->Content = { nullptr }; - result->Prev = { nullptr }; - result->Next = { nullptr }; - result->Parent = { nullptr }; - result->Name = { nullptr }; - result->Type = ECode::Invalid; - result->ModuleFlags = ModuleFlag::Invalid; - result->NumEntries = 0; - - return result; -} - -enum class OpValidateResult : u32 -{ - Fail, - Global, - Member -}; - -inline -OpValidateResult operator__validate( OperatorT op, CodeParam params_code, CodeType ret_type, CodeSpecifiers specifier ) -{ - using namespace EOperator; - - if ( op == EOperator::Invalid ) - { - log_failure("gen::def_operator: op cannot be invalid"); - return OpValidateResult::Fail; - } - -#pragma region Helper Macros -# define check_params() \ - if ( ! params_code ) \ - { \ - log_failure("gen::def_operator: params is null and operator%s requires it", to_str(op)); \ - return OpValidateResult::Fail; \ - } \ - if ( params_code->Type != ECode::Parameters ) \ - { \ - log_failure("gen::def_operator: params is not of Parameters type - %s", params_code.debug_str()); \ - return OpValidateResult::Fail; \ - } - -# define check_param_eq_ret() \ - if ( ! is_member_symbol && params_code->ValueType != ret_type ) \ - { \ - log_failure("gen_def_operator: operator%s requires first parameter to equal return type\n" \ - "param types: %s\n" \ - "return type: %s", \ - to_str(op), \ - params_code.debug_str(), \ - ret_type.debug_str() \ - ); \ - return OpValidateResult::Fail; \ - } -#pragma endregion Helper Macros - - if ( ! ret_type ) - { - log_failure("gen::def_operator: ret_type is null but is required by operator%s", to_str(op)); - } - - if ( ret_type->Type != ECode::Typename ) - { - log_failure("gen::def_operator: ret_type is not of typename type - %s", ret_type.debug_str()); - return OpValidateResult::Fail; - } - - bool is_member_symbol = false; - - switch ( op ) - { - # define specs( ... ) num_args( __VA_ARGS__ ), __VA_ARGS__ - case Assign: - check_params(); - - if ( params_code->NumEntries > 1 ) - { - log_failure("gen::def_operator: " - "operator%s does not support non-member definition (more than one parameter provided) - %s", - to_str(op), - params_code.debug_str() - ); - return OpValidateResult::Fail; - } - - is_member_symbol = true; - break; - - case Assign_Add: - case Assign_Subtract: - case Assign_Multiply: - case Assign_Divide: - case Assign_Modulo: - case Assign_BAnd: - case Assign_BOr: - case Assign_BXOr: - case Assign_LShift: - case Assign_RShift: - check_params(); - - if ( params_code->NumEntries == 1 ) - is_member_symbol = true; - - else - check_param_eq_ret(); - - if (params_code->NumEntries > 2 ) - { - log_failure("gen::def_operator: operator%s may not be defined with more than two parametes - param count; %d\n%s" - , to_str(op) - , params_code->NumEntries - , params_code.debug_str() - ); - return OpValidateResult::Fail; - } - break; - - case Increment: - case Decrement: - // If its not set, it just means its a prefix member op. - if ( params_code ) - { - if ( params_code->Type != ECode::Parameters ) - { - log_failure("gen::def_operator: operator%s params code provided is not of Parameters type - %s" - , to_str(op) - , params_code.debug_str() - ); - return OpValidateResult::Fail; - } - - switch ( params_code->NumEntries ) - { - case 1: - if ( params_code->ValueType.is_equal( t_int ) ) - is_member_symbol = true; - - else - check_param_eq_ret(); - break; - - case 2: - check_param_eq_ret(); - - if ( ! params_code.get(1).is_equal( t_int ) ) - { - log_failure("gen::def_operator: " - "operator%s requires second parameter of non-member definition to be int for post-decrement", - to_str(op) - ); - return OpValidateResult::Fail; - } - break; - - default: - log_failure("gen::def_operator: operator%s recieved unexpected number of parameters recived %d instead of 0-2" - , to_str(op) - , params_code->NumEntries - ); - return OpValidateResult::Fail; - } - } - break; - - case Unary_Plus: - case Unary_Minus: - case BNot: - if ( ! params_code ) - is_member_symbol = true; - - else - { - if ( params_code->Type != ECode::Parameters ) - { - log_failure("gen::def_operator: params is not of Parameters type - %s", params_code.debug_str()); - return OpValidateResult::Fail; - } - - if ( params_code->ValueType.is_equal( ret_type ) ) - { - log_failure("gen::def_operator: " - "operator%s is non-member symbol yet first paramter does not equal return type\n" - "param type: %s\n" - "return type: %s\n" - , params_code.debug_str() - , ret_type.debug_str() - ); - return OpValidateResult::Fail; - } - - if ( params_code->NumEntries > 1 ) - { - log_failure("gen::def_operator: operator%s may not have more than one parameter - param count: %d" - , to_str(op) - , params_code->NumEntries - ); - return OpValidateResult::Fail; - } - } - break; - - case Add: - case Subtract: - case Multiply: - case Divide: - case Modulo: - case BAnd: - case BOr: - case BXOr: - case LShift: - case RShift: - check_params(); - - switch ( params_code->NumEntries ) - { - case 1: - is_member_symbol = true; - break; - - case 2: - if ( ! params_code->ValueType.is_equal( ret_type ) ) - { - log_failure("gen::def_operator: " - "operator%s is non-member symbol yet first paramter does not equal return type\n" - "param type: %s\n" - "return type: %s\n" - , params_code.debug_str() - , ret_type.debug_str() - ); - return OpValidateResult::Fail; - } - break; - - default: - log_failure("gen::def_operator: operator%s recieved unexpected number of paramters recived %d instead of 0-2" - , to_str(op) - , params_code->NumEntries - ); - return OpValidateResult::Fail; - } - break; - - case UnaryNot: - if ( ! params_code ) - is_member_symbol = true; - - else - { - if ( params_code->Type != ECode::Parameters ) - { - log_failure("gen::def_operator: params is not of Parameters type - %s", params_code.debug_str()); - return OpValidateResult::Fail; - } - - if ( params_code->NumEntries != 1 ) - { - log_failure("gen::def_operator: operator%s recieved unexpected number of paramters recived %d instead of 0-1" - , to_str(op) - , params_code->NumEntries - ); - return OpValidateResult::Fail; - } - } - - if ( ! ret_type.is_equal( t_bool )) - { - log_failure("gen::def_operator: operator%s return type must be of type bool - %s" - , to_str(op) - , ret_type.debug_str() - ); - return OpValidateResult::Fail; - } - break; - - case LAnd: - case LOr: - case LEqual: - case LNot: - case Lesser: - case Greater: - case LesserEqual: - case GreaterEqual: - check_params(); - - switch ( params_code->NumEntries ) - { - case 1: - is_member_symbol = true; - break; - - case 2: - break; - - default: - log_failure("gen::def_operator: operator%s recieved unexpected number of paramters recived %d instead of 1-2" - , to_str(op) - , params_code->NumEntries - ); - return OpValidateResult::Fail; - } - break; - - case Indirection: - case AddressOf: - case MemberOfPointer: - if ( params_code && params_code->NumEntries > 1) - { - log_failure("gen::def_operator: operator%s recieved unexpected number of paramters recived %d instead of 0-1" - , to_str(op) - , params_code->NumEntries - ); - return OpValidateResult::Fail; - } - else - { - is_member_symbol = true; - } - break; - - case PtrToMemOfPtr: - if ( params_code ) - { - log_failure("gen::def_operator: operator%s expects no paramters - %s", to_str(op), params_code.debug_str()); - return OpValidateResult::Fail; - } - break; - - case Subscript: - case FunctionCall: - case Comma: - check_params(); - break; - # undef specs - } - - return is_member_symbol ? OpValidateResult::Member : OpValidateResult::Global; -# undef check_params -# undef check_ret_type -# undef check_param_eq_ret -} - -void set_allocator_data_arrays( AllocatorInfo allocator ) -{ - Allocator_DataArrays = allocator; -} - -void set_allocator_code_pool( AllocatorInfo allocator ) -{ - Allocator_CodePool = allocator; -} - -void set_allocator_lexer( AllocatorInfo allocator ) -{ - Allocator_Lexer = allocator; -} - -void set_allocator_string_arena( AllocatorInfo allocator ) -{ - Allocator_StringArena = allocator; -} - -void set_allocator_string_table( AllocatorInfo allocator ) -{ - Allocator_StringArena = allocator; -} - -#pragma region Helper Marcojs -// This snippet is used in nearly all the functions. -#define name_check( Context_, Name_ ) \ -{ \ - if ( Name_.Len <= 0 ) \ - { \ - log_failure( "gen::" stringize(Context_) ": Invalid name length provided - %d", Name_.Len ); \ - return CodeInvalid; \ - } \ - \ - if ( Name_.Ptr == nullptr ) \ - { \ - log_failure( "gen::" stringize(Context_) ": name is null" ); \ - return CodeInvalid; \ - } \ -} - -#define null_check( Context_, Code_ ) \ - if ( ! Code_ ) \ - { \ - log_failure( "gen::" stringize(Context_) ": " stringize(Code_) " provided is null" ); \ - return CodeInvalid; \ - } - -#define null_or_invalid_check( Context_, Code_ ) \ -{ \ - if ( ! Code_ ) \ - { \ - log_failure( "gen::" stringize(Context_) ": " stringize(Code_) " provided is null" ); \ - return CodeInvalid; \ - } \ - \ - if ( Code_->is_invalid() ) \ - { \ - log_failure("gen::" stringize(Context_) ": " stringize(Code_) " provided is invalid" ); \ - return CodeInvalid; \ - } \ -} - -#define not_implemented( Context_ ) \ - log_failure( "gen::%s: This function is not implemented" ); \ - return CodeInvalid; -#pragma endregion Helper Marcos - -#pragma region Upfront Constructors -/* -The implementaiton of the upfront constructors involves bascially doing three things: -* Validate the arguments given to construct the intended type of AST is valid. -* Construct said AST type. -* Lock the AST (set to readonly) and return the valid object. - -If any of the validation fails, it triggers a call to log_failure with as much info the give the user so that they can hopefully -identify the issue without having to debug too much (at least they can debug though...) - -The largest of the functions is related to operator overload definitions. -The library validates a good protion of their form and thus the argument processing for is quite a bit. -*/ -CodeAttributes def_attributes( StrC content ) -{ - if ( content.Len <= 0 || content.Ptr == nullptr ) - { - log_failure( "gen::def_attributes: Invalid attributes provided" ); - return CodeInvalid; - } - - Code - result = make_code(); - result->Type = ECode::PlatformAttributes; - result->Name = get_cached_string( content ); - result->Content = result->Name; - - return (CodeAttributes) result; -} - -CodeComment def_comment( StrC content ) -{ - if ( content.Len <= 0 || content.Ptr == nullptr ) - { - log_failure( "gen::def_comment: Invalid comment provided:" ); - return CodeInvalid; - } - - Code - result = make_code(); - result->Type = ECode::Comment; - result->Name = get_cached_string( content ); - result->Content = result->Name; - - return (CodeComment) result; -} - -CodeClass def_class( StrC name - , Code body - , CodeType parent, AccessSpec parent_access - , CodeAttributes attributes - , ModuleFlag mflags - , CodeType* interfaces, s32 num_interfaces ) -{ - using namespace ECode; - - name_check( def_class, name ); - - if ( attributes && attributes->Type != PlatformAttributes ) - { - log_failure( "gen::def_class: attributes was not a 'PlatformAttributes' type: %s", attributes.debug_str() ); - return CodeInvalid; - } - - if ( parent && (parent->Type != Class || parent->Type != Struct || parent->Type != Typename || parent->Type != Untyped) ) - { - log_failure( "gen::def_class: parent provided is not type 'Class', 'Struct', 'Typeanme', or 'Untyped': %s", parent.debug_str() ); - return CodeInvalid; - } - - CodeClass - result = (CodeClass) make_code(); - result->Name = get_cached_string( name ); - result->ModuleFlags = mflags; - - if ( body ) - { - switch ( body->Type ) - { - case Class_Body: - case Untyped: - break; - - default: - log_failure("gen::def_class: body must be either of Class_Body or Untyped type - %s", body.debug_str()); - return CodeInvalid; - } - - result->Type = Class; - result->Body = body; - } - else - { - result->Type = Class_Fwd; - } - - if ( attributes ) - result->Attributes = attributes; - - if ( parent ) - { - result->ParentAccess = parent_access; - result->ParentType = parent; - } - - if ( interfaces ) - { - for (s32 idx = 0; idx < num_interfaces; idx++ ) - { - result.add_interface( interfaces[idx] ); - } - } - - return result; -} - -CodeEnum def_enum( StrC name - , Code body, CodeType type - , EnumT specifier, CodeAttributes attributes - , ModuleFlag mflags ) -{ - using namespace ECode; - - name_check( def_enum, name ); - - if ( type && type->Type != Typename ) - { - log_failure( "gen::def_enum: enum underlying type provided was not of type Typename: %s", type.debug_str() ); - return CodeInvalid; - } - - if ( attributes && attributes->Type != PlatformAttributes ) - { - log_failure( "gen::def_enum: attributes was not a 'PlatformAttributes' type: %s", attributes.debug_str() ); - return CodeInvalid; - } - - CodeEnum - result = (CodeEnum) make_code(); - result->Name = get_cached_string( name ); - result->ModuleFlags = mflags; - - if ( body ) - { - switch ( body->Type ) - { - case Enum_Body: - case Untyped: - break; - - default: - log_failure( "gen::def_enum: body must be of Enum_Body or Untyped type %s", body.debug_str()); - return CodeInvalid; - } - - result->Type = specifier == EnumClass ? - Enum_Class : Enum; - - result->Body = body; - } - else - { - result->Type = specifier == EnumClass ? - Enum_Class_Fwd : Enum_Fwd; - } - - if ( attributes ) - result->Attributes = attributes; - - if ( type ) - { - result->UnderlyingType = type; - } - else if ( result->Type != Enum_Class_Fwd && result->Type != Enum_Fwd ) - { - log_failure( "gen::def_enum: enum forward declaration must have an underlying type" ); - return CodeInvalid; - } - - return result; -} - -CodeExec def_execution( StrC content ) -{ - if ( content.Len <= 0 || content.Ptr == nullptr ) - { - log_failure( "gen::def_execution: Invalid execution provided" ); - return CodeInvalid; - } - - Code - result = make_code(); - result->Type = ECode::Execution; - result->Name = get_cached_string( content ); - result->Content = result->Name; - - return (CodeExec) result; -} - -CodeExtern def_extern_link( StrC name, Code body ) -{ - using namespace ECode; - - name_check( def_extern_linkage, name ); - null_check( def_extern_linkage, body ); - - if ( body->Type != Extern_Linkage_Body && body->Type != Untyped ) - { - log_failure("gen::def_extern_linkage: body is not of extern_linkage or untyped type %s", body->debug_str()); - return CodeInvalid; - } - - CodeExtern - result = (CodeExtern)make_code(); - result->Type = Extern_Linkage; - result->Name = get_cached_string( name ); - result->Body = body; - - return (CodeExtern) result; -} - -CodeFriend def_friend( Code declaration ) -{ - using namespace ECode; - - null_check( def_friend, declaration ); - - switch ( declaration->Type ) - { - case Class_Fwd: - case Function_Fwd: - case Operator_Fwd: - case Struct_Fwd: - case Class: - case Function: - case Operator: - case Struct: - break; - - default: - log_failure("gen::def_friend: requires declartion to have class, function, operator, or struct - %s", declaration->debug_str()); - return CodeInvalid; - } - - CodeFriend - result = (CodeFriend) make_code(); - result->Type = Friend; - - result->Declaration = declaration; - - return result; -} - -CodeFn def_function( StrC name - , CodeParam params , CodeType ret_type, Code body - , CodeSpecifiers specifiers, CodeAttributes attributes - , ModuleFlag mflags ) -{ - using namespace ECode; - - name_check( def_function, name ); - - if ( params && params->Type != Parameters ) - { - log_failure( "gen::def_function: params was not a `Parameters` type: %s", params.debug_str() ); - return CodeInvalid; - } - - if ( ret_type && ret_type->Type != Typename ) - { - log_failure( "gen::def_function: ret_type was not a Typename: %s", ret_type.debug_str() ); - return CodeInvalid; - } - - if ( specifiers && specifiers->Type != Specifiers ) - { - log_failure( "gen::def_function: specifiers was not a `Specifiers` type: %s", specifiers.debug_str() ); - return CodeInvalid; - } - - if ( attributes && attributes->Type != PlatformAttributes ) - { - log_failure( "gen::def_function: attributes was not a `PlatformAttributes` type: %s", attributes.debug_str() ); - return CodeInvalid; - } - - CodeFn - result = (CodeFn) make_code(); - result->Name = get_cached_string( name ); - result->ModuleFlags = mflags; - - if ( body ) - { - switch ( body->Type ) - { - case Function_Body: - case Execution: - case Untyped: - break; - - default: - { - log_failure("gen::def_function: body must be either of Function_Body, Execution, or Untyped type. %s", body->debug_str()); - return CodeInvalid; - } - } - - result->Type = Function; - result->Body = body; - } - else - { - result->Type = Function_Fwd; - } - - if ( attributes ) - result->Attributes = attributes; - - if ( specifiers ) - result->Specs = specifiers; - - if ( ret_type ) - { - result->ReturnType = ret_type; - } - else - { - result->ReturnType = t_void; - } - - if ( params ) - result->Params = params; - - return result; -} - -CodeInclude def_include ( StrC path ) -{ - if ( path.Len <= 0 || path.Ptr == nullptr ) - { - log_failure( "gen::def_include: Invalid path provided - %d" ); - return CodeInvalid; - } - - Code - result = make_code(); - result->Type = ECode::Preprocessor_Include; - result->Name = get_cached_string( path ); - result->Content = result->Name; - - return (CodeInclude) result; -} - -CodeModule def_module( StrC name, ModuleFlag mflags ) -{ - name_check( def_module, name ); - - Code - result = make_code(); - result->Type = ECode::Module; - result->Name = get_cached_string( name ); - result->Content = result->Name; - result->ModuleFlags = mflags; - - return (CodeModule) result; -} - -CodeNamespace def_namespace( StrC name, Code body, ModuleFlag mflags ) -{ - using namespace ECode; - - name_check( def_namespace, name ); - null_check( def_namespace, body ); - - if ( body->Type != Namespace_Body && body->Type != Untyped ) - { - log_failure("gen::def_namespace: body is not of namespace or untyped type %s", body.debug_str()); - return CodeInvalid; - } - - CodeNamespace - result = (CodeNamespace) make_code(); - result->Type = Namespace; - result->Name = get_cached_string( name ); - result->ModuleFlags = mflags; - result->Body = body; - - return result; -} - -CodeOperator def_operator( OperatorT op - , CodeParam params_code, CodeType ret_type, Code body - , CodeSpecifiers specifiers, CodeAttributes attributes - , ModuleFlag mflags ) -{ - using namespace ECode; - - if ( attributes && attributes->Type != PlatformAttributes ) - { - log_failure( "gen::def_operator: PlatformAttributes was provided but its not of attributes type: %s", attributes.debug_str() ); - return CodeInvalid; - } - - if ( specifiers && specifiers->Type != Specifiers ) - { - log_failure( "gen::def_operator: Specifiers was provided but its not of specifiers type: %s", specifiers.debug_str() ); - return CodeInvalid; - } - - OpValidateResult check_result = operator__validate( op, params_code, ret_type, specifiers ); - - if ( check_result == OpValidateResult::Fail ) - { - return CodeInvalid; - } - - char const* name = str_fmt_buf( "operator %s", to_str(op) ); - - CodeOperator - result = (CodeOperator) make_code(); - result->Name = get_cached_string( { str_len(name), name } ); - result->ModuleFlags = mflags; - - if ( body ) - { - switch ( body->Type ) - { - case Function_Body: - case Execution: - case Untyped: - break; - - default: - { - log_failure("gen::def_operator: body must be either of Function_Body, Execution, or Untyped type. %s", body->debug_str()); - return CodeInvalid; - } - } - - result->Type = check_result == OpValidateResult::Global ? - Operator : Operator_Member; - - result->Body = body; - } - else - { - result->Type = check_result == OpValidateResult::Global ? - Operator_Fwd : Operator_Member_Fwd; - } - - if ( attributes ) - result->Attributes = attributes; - - if ( specifiers ) - result->Specs = specifiers; - - result->ReturnType = ret_type; - - if (params_code) - result->Params = params_code; - - return result; -} - -CodeOpCast def_operator_cast( CodeType type, Code body, CodeSpecifiers const_spec ) -{ - using namespace ECode; - null_check( def_operator_cast, type ); - - if ( type->Type != Typename ) - { - log_failure( "gen::def_operator_cast: type is not a typename - %s", type.debug_str() ); - return CodeInvalid; - } - - CodeOpCast result = (CodeOpCast) make_code(); - - if (body) - { - result->Type = Operator_Cast; - - if ( body->Type != Function_Body && body->Type != Execution ) - { - log_failure( "gen::def_operator_cast: body is not of function body or execution type - %s", body.debug_str() ); - return CodeInvalid; - } - - result->Body = body; - } - else - { - result->Type = Operator_Cast_Fwd; - } - - if ( const_spec ) - { - result->Specs = const_spec; - } - - result->ValueType = type; - return result; -} - -CodeParam def_param( CodeType type, StrC name, Code value ) -{ - using namespace ECode; - - name_check( def_param, name ); - null_check( def_param, type ); - - if ( type->Type != Typename ) - { - log_failure( "gen::def_param: type is not a typename - %s", type.debug_str() ); - return CodeInvalid; - } - - if ( value && value->Type != Untyped ) - { - log_failure( "gen::def_param: value is not untyped - %s", value.debug_str() ); - return CodeInvalid; - } - - CodeParam - result = (CodeParam) make_code(); - result->Type = Parameters; - result->Name = get_cached_string( name ); - - result->ValueType = type; - - if ( value ) - result->Value = value; - - result->NumEntries++; - - return result; -} - -CodeSpecifiers def_specifier( SpecifierT spec ) -{ - CodeSpecifiers - result = (CodeSpecifiers) make_code(); - result->Type = ECode::Specifiers; - result.append( spec ); - - return result; -} - -CodeStruct def_struct( StrC name - , Code body - , CodeType parent, AccessSpec parent_access - , CodeAttributes attributes - , ModuleFlag mflags - , CodeType* interfaces, s32 num_interfaces ) -{ - using namespace ECode; - - if ( attributes && attributes->Type != PlatformAttributes ) - { - log_failure( "gen::def_struct: attributes was not a `PlatformAttributes` type - %s", attributes.debug_str() ); - return CodeInvalid; - } - - if ( parent && parent->Type != Typename ) - { - log_failure( "gen::def_struct: parent was not a `Struct` type - %s", parent.debug_str() ); - return CodeInvalid; - } - - if ( body && body->Type != Struct_Body ) - { - log_failure( "gen::def_struct: body was not a Struct_Body type - %s", body.debug_str() ); - return CodeInvalid; - } - - CodeStruct - result = (CodeStruct) make_code(); - result->ModuleFlags = mflags; - - if ( name ) - result->Name = get_cached_string( name ); - - if ( body ) - { - result->Type = Struct; - result->Body = body; - } - else - { - result->Type = Struct_Fwd; - } - - if ( attributes ) - result->Attributes = attributes; - - if ( parent ) - { - result->ParentAccess = parent_access; - result->ParentType = parent; - } - - if ( interfaces ) - { - for (s32 idx = 0; idx < num_interfaces; idx++ ) - { - result.add_interface( interfaces[idx] ); - } - } - - return result; -} - -CodeTemplate def_template( CodeParam params, Code declaration, ModuleFlag mflags ) -{ - null_check( def_template, declaration ); - - if ( params && params->Type != ECode::Parameters ) - { - log_failure( "gen::def_template: params is not of parameters type - %s", params.debug_str() ); - return CodeInvalid; - } - - switch (declaration->Type ) - { - case ECode::Class: - case ECode::Function: - case ECode::Struct: - case ECode::Variable: - case ECode::Using: - break; - - default: - log_failure( "gen::def_template: declaration is not of class, function, struct, variable, or using type - %s", declaration.debug_str() ); - } - - CodeTemplate - result = (CodeTemplate) make_code(); - result->Type = ECode::Template; - result->ModuleFlags = mflags; - result->Params = params; - result->Declaration = declaration; - - return result; -} - -CodeType def_type( StrC name, Code arrayexpr, CodeSpecifiers specifiers, CodeAttributes attributes ) -{ - name_check( def_type, name ); - - if ( attributes && attributes->Type != ECode::PlatformAttributes ) - { - log_failure( "gen::def_type: attributes is not of attributes type - %s", attributes.debug_str() ); - return CodeInvalid; - } - - if ( specifiers && specifiers->Type != ECode::Specifiers ) - { - log_failure( "gen::def_type: specifiers is not of specifiers type - %s", specifiers.debug_str() ); - return CodeInvalid; - } - - if ( arrayexpr && arrayexpr->Type != ECode::Untyped ) - { - log_failure( "gen::def_type: arrayexpr is not of untyped type - %s", arrayexpr->debug_str() ); - return CodeInvalid; - } - - CodeType - result = (CodeType) make_code(); - result->Name = get_cached_string( name ); - result->Type = ECode::Typename; - - if ( attributes ) - result->Attributes = attributes; - - if ( specifiers ) - result->Specs = specifiers; - - if ( arrayexpr ) - result->ArrExpr = arrayexpr; - - return result; -} - -CodeTypedef def_typedef( StrC name, Code type, CodeAttributes attributes, ModuleFlag mflags ) -{ - using namespace ECode; - - name_check( def_typedef, name ); - null_check( def_typedef, type ); - - switch ( type->Type ) - { - case Class: - case Class_Fwd: - case Enum: - case Enum_Fwd: - case Enum_Class: - case Enum_Class_Fwd: - case Function_Fwd: - case Struct: - case Struct_Fwd: - case Union: - case Typename: - break; - default: - log_failure( "gen::def_typedef: type was not a Class, Enum, Function Forward, Struct, Typename, or Union - %s", type.debug_str() ); - return CodeInvalid; - } - - if ( attributes && attributes->Type != ECode::PlatformAttributes ) - { - log_failure( "gen::def_typedef: attributes was not a PlatformAttributes - %s", attributes.debug_str() ); - return CodeInvalid; - } - - // Registering the type. - Code registered_type = def_type( name ); - - if ( ! registered_type ) - { - log_failure( "gen::def_typedef: failed to register type" ); - return CodeInvalid; - } - - CodeTypedef - result = (CodeTypedef) make_code(); - result->Name = get_cached_string( name ); - result->Type = ECode::Typedef; - result->ModuleFlags = mflags; - - result->UnderlyingType = type; - - return result; -} - -CodeUnion def_union( StrC name, Code body, CodeAttributes attributes, ModuleFlag mflags ) -{ - null_check( def_union, body ); - - if ( body->Type != ECode::Union_Body ) - { - log_failure( "gen::def_union: body was not a Union_Body type - %s", body.debug_str() ); - return CodeInvalid; - } - - if ( attributes && attributes->Type != ECode::PlatformAttributes ) - { - log_failure( "gen::def_union: attributes was not a PlatformAttributes type - %s", attributes.debug_str() ); - return CodeInvalid; - } - - CodeUnion - result = (CodeUnion) make_code(); - result->ModuleFlags = mflags; - result->Type = ECode::Union; - - if ( name.Ptr ) - result->Name = get_cached_string( name ); - - result->Body = body; - - if ( attributes ) - result->Attributes = attributes; - - return result; -} - -CodeUsing def_using( StrC name, CodeType type - , CodeAttributes attributes - , ModuleFlag mflags ) -{ - name_check( def_using, name ); - null_check( def_using, type ); - - Code register_type = def_type( name ); - - if ( ! register_type ) - { - log_failure( "gen::def_using: failed to register type" ); - return CodeInvalid; - } - - if ( attributes && attributes->Type != ECode::PlatformAttributes ) - { - log_failure( "gen::def_using: attributes was not a PlatformAttributes type - %s", attributes.debug_str() ); - return CodeInvalid; - } - - CodeUsing - result = (CodeUsing) make_code(); - result->Name = get_cached_string( name ); - result->ModuleFlags = mflags; - result->Type = ECode::Using; - - result->UnderlyingType = type; - - if ( attributes ) - result->Attributes = attributes; - - return result; -} - -CodeUsing def_using_namespace( StrC name ) -{ - name_check( def_using_namespace, name ); - - Code - result = make_code(); - result->Name = get_cached_string( name ); - result->Content = result->Name; - result->Type = ECode::Using_Namespace; - - return (CodeUsing) result; -} - -CodeVar def_variable( CodeType type, StrC name, Code value - , CodeSpecifiers specifiers, CodeAttributes attributes - , ModuleFlag mflags ) -{ - name_check( def_variable, name ); - null_check( def_variable, type ); - - if ( attributes && attributes->Type != ECode::PlatformAttributes ) - { - log_failure( "gen::def_variable: attributes was not a `PlatformAttributes` type - %s", attributes.debug_str() ); - return CodeInvalid; - } - - if ( specifiers && specifiers->Type != ECode::Specifiers ) - { - log_failure( "gen::def_variable: specifiers was not a `Specifiers` type - %s", specifiers.debug_str() ); - return CodeInvalid; - } - - if ( type->Type != ECode::Typename ) - { - log_failure( "gen::def_variable: type was not a Typename - %s", type.debug_str() ); - return CodeInvalid; - } - - if ( value && value->Type != ECode::Untyped ) - { - log_failure( "gen::def_variable: value was not a `Untyped` type - %s", value.debug_str() ); - return CodeInvalid; - } - - CodeVar - result = (CodeVar) make_code(); - result->Name = get_cached_string( name ); - result->Type = ECode::Variable; - result->ModuleFlags = mflags; - - result->ValueType = type; - - if ( attributes ) - result->Attributes = attributes; - - if ( specifiers ) - result->Specs = specifiers; - - if ( value ) - result->Value = value; - - return result; -} - -/* -Body related functions typically follow the same implementation pattern. -Opted to use inline helper macros to get the implementaiton done. - -The implementation pattern is as follows: -* Validate a valid parameter num was provided, or code array - def_body_start or def_body_code_array_start( ) - -* Begin the code entry do-while loop, make sure each entry is valid processing its type in the switc - def_body_code_validation_start( ) - -* Define the switch case statements between the macros. - -* Add the code entry, finish the closing implemenation for the do-while loop. - def_body_code_validation_end( ) - -* Lock the body AST and return it. - -If a function's implementation deviates from the macros then its just writen it out. -*/ - -#pragma region Helper Macros for def_**_body functions -#define def_body_start( Name_ ) \ -using namespace ECode; \ - \ -if ( num <= 0 ) \ -{ \ - log_failure("gen::" stringize(Name_) ": num cannot be zero or negative"); \ - return CodeInvalid; \ -} - -#define def_body_code_array_start( Name_ ) \ -using namespace ECode; \ - \ -if ( num <= 0 ) \ -{ \ - log_failure("gen::" stringize(Name_) ": num cannot be zero or negative"); \ - return CodeInvalid; \ -} \ - \ -if ( codes == nullptr ) \ -{ \ - log_failure("gen::" stringize(Name_)" : Provided a null array of codes"); \ - return CodeInvalid; \ -} - -#define def_body_code_validation_start( Name_ ) \ -do \ -{ \ - Code_POD pod = va_arg(va, Code_POD); \ - Code entry = pcast(Code, pod); \ - \ - if ( ! entry ) \ - { \ - log_failure("gen::" stringize(Name_) ": Provided an null entry"); \ - return CodeInvalid; \ - } \ - \ - switch ( entry->Type ) \ - { - -#define def_body_code_array_validation_start( Name_ ) \ -do \ -{ \ - Code entry = *codes; codes++; \ - \ - if ( ! entry ) \ - { \ - log_failure("gen::" stringize(Name_) ": Provided an null entry"); \ - return CodeInvalid; \ - } \ - \ - switch ( entry->Type ) \ - { - -#define def_body_code_validation_end( Name_ ) \ - log_failure("gen::" stringize(Name_) ": Entry type is not allowed: %s", entry.debug_str() ); \ - return CodeInvalid; \ - \ - default: \ - break; \ - } \ - \ - result.append( entry ); \ -} \ -while ( num--, num > 0 ) -#pragma endregion Helper Macros for def_**_body functions - -CodeBody def_class_body( s32 num, ... ) -{ - def_body_start( def_class_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Class_Body; - - va_list va; - va_start(va, num); - def_body_code_validation_start( def_class_body ); - AST_BODY_CLASS_UNALLOWED_TYPES - def_body_code_validation_end( def_class_body ); - va_end(va); - - return result; -} - -CodeBody def_class_body( s32 num, Code* codes ) -{ - def_body_code_array_start( def_class_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Function_Body; - - def_body_code_array_validation_start( def_class_body ); - AST_BODY_CLASS_UNALLOWED_TYPES - def_body_code_validation_end( def_class_body ); - - return result; -} - -CodeBody def_enum_body( s32 num, ... ) -{ - def_body_start( def_enum_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Enum_Body; - - va_list va; - va_start(va, num); - do - { - Code_POD pod = va_arg(va, Code_POD); - Code entry = pcast(Code, pod); - - if ( ! entry ) - { - log_failure("gen::def_enum_body: Provided a null entry"); - return CodeInvalid; - } - - if ( entry->Type != Untyped && entry->Type != Comment ) - { - log_failure("gen::def_enum_body: Entry type is not allowed - %s. Must be of untyped or comment type.", entry.debug_str() ); \ - return CodeInvalid; - } - - result.append( entry ); - } - while ( num--, num > 0 ); - va_end(va); - - return (CodeBody) result; -} - -CodeBody def_enum_body( s32 num, Code* codes ) -{ - def_body_code_array_start( def_enum_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Enum_Body; - - do - { - Code entry = *codes; - - if ( ! entry ) - { - log_failure("gen::def_enum_body: Provided a null entry"); - return CodeInvalid; - } - - if ( entry->Type != Untyped && entry->Type != Comment ) - { - log_failure("gen::def_enum_body: Entry type is not allowed: %s", entry.debug_str() ); \ - return CodeInvalid; - } - - result.append( entry ); - } - while ( codes++, num--, num > 0 ); - - return result; -} - -CodeBody def_export_body( s32 num, ... ) -{ - def_body_start( def_export_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Export_Body; - - va_list va; - va_start(va, num); - def_body_code_validation_start( def_export_body ); - AST_BODY_EXPORT_UNALLOWED_TYPES - def_body_code_validation_end( def_export_body ); - va_end(va); - - return result; -} - -CodeBody def_export_body( s32 num, Code* codes ) -{ - def_body_code_array_start( def_export_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Export_Body; - - def_body_code_array_validation_start( def_export_body ); - AST_BODY_EXPORT_UNALLOWED_TYPES - def_body_code_validation_end( def_export_body ); - - return result; -} - -CodeBody def_extern_link_body( s32 num, ... ) -{ - def_body_start( def_extern_linkage_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Extern_Linkage_Body; - - va_list va; - va_start(va, num); - def_body_code_validation_start( def_extern_linkage_body ); - AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES - def_body_code_validation_end( def_extern_linkage_body ); - va_end(va); - - return result; -} - -CodeBody def_extern_link_body( s32 num, Code* codes ) -{ - def_body_code_array_start( def_extern_linkage_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Extern_Linkage_Body; - - def_body_code_array_validation_start( def_extern_linkage_body ); - AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES - def_body_code_validation_end( def_extern_linkage_body ); - - return result; -} - -CodeBody def_function_body( s32 num, ... ) -{ - def_body_start( def_function_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Function_Body; - - va_list va; - va_start(va, num); - def_body_code_validation_start( def_function_body ); - AST_BODY_FUNCTION_UNALLOWED_TYPES - def_body_code_validation_end( def_function_body ); - va_end(va); - - return result; -} - -CodeBody def_function_body( s32 num, Code* codes ) -{ - def_body_code_array_start( def_function_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Function_Body; - - def_body_code_array_validation_start( def_function_body ); - AST_BODY_FUNCTION_UNALLOWED_TYPES - def_body_code_validation_end( def_function_body ); - - return result; -} - -CodeBody def_global_body( s32 num, ... ) -{ - def_body_start( def_global_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Global_Body; - - va_list va; - va_start(va, num); - def_body_code_validation_start( def_global_body ); - AST_BODY_GLOBAL_UNALLOWED_TYPES - def_body_code_validation_end( def_global_body ); - va_end(va); - - return result; -} - -CodeBody def_global_body( s32 num, Code* codes ) -{ - def_body_code_array_start( def_global_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Global_Body; - - def_body_code_array_validation_start( def_global_body ); - AST_BODY_GLOBAL_UNALLOWED_TYPES - def_body_code_validation_end( def_global_body ); - - return result; -} - -CodeBody def_namespace_body( s32 num, ... ) -{ - def_body_start( def_namespace_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Namespace_Body; - - va_list va; - va_start(va, num); - def_body_code_validation_start( def_namespace_body ); - AST_BODY_NAMESPACE_UNALLOWED_TYPES - def_body_code_validation_end( def_namespace_body ); - va_end(va); - - return result; -} - -CodeBody def_namespace_body( s32 num, Code* codes ) -{ - def_body_code_array_start( def_namespace_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Global_Body; - - def_body_code_array_validation_start( def_namespace_body ); - AST_BODY_NAMESPACE_UNALLOWED_TYPES - def_body_code_validation_end( def_namespace_body ); - - return result; -} - -CodeParam def_params( s32 num, ... ) -{ - def_body_start( def_params ); - - va_list va; - va_start(va, num); - - Code_POD pod = va_arg(va, Code_POD); - CodeParam param = pcast( CodeParam, pod ); - - null_check( def_params, param ); - - if ( param->Type != Parameters ) - { - log_failure( "gen::def_params: param %d is not a Parameters", num - num + 1 ); - return CodeInvalid; - } - - CodeParam result = (CodeParam) param.duplicate(); - - while ( -- num ) - { - pod = va_arg(va, Code_POD); - param = pcast( CodeParam, pod ); - - if ( param->Type != Parameters ) - { - log_failure( "gen::def_params: param %d is not a Parameters", num - num + 1 ); - return CodeInvalid; - } - - result.append( param ); - } - va_end(va); - - return result; -} - -CodeParam def_params( s32 num, CodeParam* codes ) -{ - def_body_code_array_start( def_params ); - -# define check_current() \ - if ( current.ast == nullptr ) \ - { \ - log_failure("gen::def_params: Provide a null code in codes array"); \ - return CodeInvalid; \ - } \ - \ - if (current->Type != Parameters ) \ - { \ - log_failure("gen::def_params: Code in coes array is not of paramter type - %s", current.debug_str() ); \ - return CodeInvalid; \ - } - - CodeParam current = (CodeParam) codes->duplicate(); - check_current(); - - CodeParam - result = (CodeParam) make_code(); - result->Name = current->Name; - result->Type = current->Type; - result->ValueType = current->ValueType; - - while( codes++, current = * codes, num--, num > 0 ) - { - check_current(); - result.append( current ); - } -# undef check_current - - return result; -} - -CodeSpecifiers def_specifiers( s32 num, ... ) -{ - if ( num <= 0 ) - { - log_failure("gen::def_specifiers: num cannot be zero or less"); - return CodeInvalid; - } - - if ( num > AST::ArrSpecs_Cap ) - { - log_failure("gen::def_specifiers: num of speciifers to define AST larger than AST specicifier capacity - %d", num); - return CodeInvalid; - } - - CodeSpecifiers - result = (CodeSpecifiers) make_code(); - result->Type = ECode::Specifiers; - - va_list va; - va_start(va, num); - do - { - SpecifierT type = (SpecifierT)va_arg(va, int); - - result.append( type ); - } - while ( --num, num ); - va_end(va); - - return result; -} - -CodeSpecifiers def_specifiers( s32 num, SpecifierT* specs ) -{ - if ( num <= 0 ) - { - log_failure("gen::def_specifiers: num cannot be zero or less"); - return CodeInvalid; - } - - if ( num > AST::ArrSpecs_Cap ) - { - log_failure("gen::def_specifiers: num of speciifers to define AST larger than AST specicifier capacity - %d", num); - return CodeInvalid; - } - - CodeSpecifiers - result = (CodeSpecifiers) make_code(); - result->Type = ECode::Specifiers; - - s32 idx = 0; - do - { - result.append( specs[idx] ); - idx++; - } - while ( --num, num ); - - return result; -} - -CodeBody def_struct_body( s32 num, ... ) -{ - def_body_start( def_struct_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Struct_Body; - - va_list va; - va_start(va, num); - def_body_code_validation_start( def_struct_body ); - AST_BODY_STRUCT_UNALLOWED_TYPES - def_body_code_validation_end( def_struct_body ); - va_end(va); - - return result; -} - -CodeBody def_struct_body( s32 num, Code* codes ) -{ - def_body_code_array_start( def_struct_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Struct_Body; - - def_body_code_array_validation_start( def_struct_body ); - AST_BODY_STRUCT_UNALLOWED_TYPES - def_body_code_validation_end( def_struct_body ); - - return result; -} - -CodeBody def_union_body( s32 num, ... ) -{ - def_body_start( def_union_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Union_Body; - - va_list va; - va_start(va, num); - do - { - Code_POD pod = va_arg(va, Code_POD); - Code entry = pcast( Code, pod ); - - if ( ! entry ) - { - log_failure("gen::def_union_body: Provided a null entry"); - return CodeInvalid; - } - - if ( entry->Type != Untyped && entry->Type != Comment ) - { - log_failure("gen::def_union_body: Entry type is not allowed - %s. Must be of untyped or comment type.", entry.debug_str() ); \ - return CodeInvalid; - } - - result.append( entry ); - } - while ( num--, num > 0 ); - va_end(va); - - return result; -} - -CodeBody def_union_body( s32 num, CodeUnion* codes ) -{ - def_body_code_array_start( def_union_body ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Union_Body; - - do - { - Code entry = *codes; - - if ( ! entry ) - { - log_failure("gen::def_union_body: Provided a null entry"); - return CodeInvalid; - } - - if ( entry->Type != Untyped && entry->Type != Comment ) - { - log_failure("gen::def_union_body: Entry type is not allowed: %s", entry.debug_str() ); - return CodeInvalid; - } - - result.append( entry ); - } - while ( codes++, num--, num > 0 ); - - return (CodeBody) result; -} - -# undef name_check -# undef null_check -# undef null_or_invalid_check -#pragma endregion Upfront Constructors - -#pragma region Parsing Constructors -/* -These constructors are the most implementation intensive other than the editor or scanner. -*/ - -namespace Parser -{ -/* - This is a simple lexer that focuses on tokenizing only tokens relevant to the library. - It will not be capable of lexing C++ code with unsupported features. - - For the sake of scanning files, it can scan preprocessor directives - - Attributes_Start is only used to indicate the start of the user_defined attribute list. -*/ - -# define Define_TokType \ - Entry( Access_Private, "private" ) \ - Entry( Access_Protected, "protected" ) \ - Entry( Access_Public, "public" ) \ - Entry( Access_MemberSymbol, "." ) \ - Entry( Access_StaticSymbol, "::") \ - Entry( Ampersand, "&" ) \ - Entry( Ampersand_DBL, "&&" ) \ - Entry( Assign_Classifer, ":" ) \ - Entry( Attribute_Open, "[[" ) \ - Entry( Attribute_Close, "]]" ) \ - Entry( BraceCurly_Open, "{" ) \ - Entry( BraceCurly_Close, "}" ) \ - Entry( BraceSquare_Open, "[" ) \ - Entry( BraceSquare_Close, "]" ) \ - Entry( Capture_Start, "(" ) \ - Entry( Capture_End, ")" ) \ - Entry( Comment, "__comment__" ) \ - Entry( Char, "__char__" ) \ - Entry( Comma, "," ) \ - Entry( Decl_Class, "class" ) \ - Entry( Decl_GNU_Attribute, "__attribute__" ) \ - Entry( Decl_MSVC_Attribute, "__declspec" ) \ - Entry( Decl_Enum, "enum" ) \ - Entry( Decl_Extern_Linkage, "extern" ) \ - Entry( Decl_Friend, "friend" ) \ - Entry( Decl_Module, "module" ) \ - Entry( Decl_Namespace, "namespace" ) \ - Entry( Decl_Operator, "operator" ) \ - Entry( Decl_Struct, "struct" ) \ - Entry( Decl_Template, "template" ) \ - Entry( Decl_Typedef, "typedef" ) \ - Entry( Decl_Using, "using" ) \ - Entry( Decl_Union, "union" ) \ - Entry( Identifier, "__identifier__" ) \ - Entry( Module_Import, "import" ) \ - Entry( Module_Export, "export" ) \ - Entry( Number, "number" ) \ - Entry( Operator, "operator" ) \ - Entry( Preprocessor_Directive, "#") \ - Entry( Preprocessor_Include, "include" ) \ - Entry( Spec_Alignas, "alignas" ) \ - Entry( Spec_Const, "const" ) \ - Entry( Spec_Consteval, "consteval" ) \ - Entry( Spec_Constexpr, "constexpr" ) \ - Entry( Spec_Constinit, "constinit" ) \ - Entry( Spec_Explicit, "explicit" ) \ - Entry( Spec_Extern, "extern" ) \ - Entry( Spec_Final, "final" ) \ - Entry( Spec_Global, "global" ) \ - Entry( Spec_Inline, "inline" ) \ - Entry( Spec_Internal_Linkage, "internal" ) \ - Entry( Spec_LocalPersist, "local_persist" ) \ - Entry( Spec_Mutable, "mutable" ) \ - Entry( Spec_Override, "override" ) \ - Entry( Spec_Static, "static" ) \ - Entry( Spec_ThreadLocal, "thread_local" ) \ - Entry( Spec_Volatile, "volatile") \ - Entry( Star, "*" ) \ - Entry( Statement_End, ";" ) \ - Entry( String, "__string__" ) \ - Entry( Type_Unsigned, "unsigned" ) \ - Entry( Type_Signed, "signed" ) \ - Entry( Type_Short, "short" ) \ - Entry( Type_Long, "long" ) \ - Entry( Type_char, "char" ) \ - Entry( Type_int, "int" ) \ - Entry( Type_double, "double" ) \ - Entry( Varadic_Argument, "..." ) \ - Entry( Attributes_Start, "__attrib_start__" ) - - enum class TokType : u32 - { - # define Entry( Name_, Str_ ) Name_, - Define_TokType - GEN_Define_Attribute_Tokens - # undef Entry - Num, - Invalid - }; - - struct Token - { - char const* Text; - sptr Length; - TokType Type; - bool IsAssign; - - operator bool() - { - return Text && Length && Type != TokType::Invalid; - } - - operator StrC() - { - return { Length, Text }; - } - }; - - internal inline - TokType get_tok_type( char const* word, s32 length ) - { - local_persist - StrC lookup[(u32)TokType::Num] = - { - # define Entry( Name_, Str_ ) { sizeof(Str_), Str_ }, - Define_TokType - GEN_Define_Attribute_Tokens - # undef Entry - }; - - for ( u32 index = 0; index < (u32)TokType::Num; index++ ) - { - s32 lookup_len = lookup[index].Len - 1; - char const* lookup_str = lookup[index].Ptr; - - if ( lookup_len != length ) - continue; - - if ( str_compare( word, lookup_str, lookup_len ) == 0 ) - return scast(TokType, index); - } - - return TokType::Invalid; - } - - internal inline - char const* str_tok_type( TokType type ) - { - local_persist - char const* lookup[(u32)TokType::Num] = - { - # define Entry( Name_, Str_ ) Str_, - Define_TokType - GEN_Define_Attribute_Tokens - # undef Entry - }; - - return lookup[(u32)type]; - } - -# undef Define_TokType - - internal inline - bool tok_is_specifier( Token const& tok ) - { - return (tok.Type <= TokType::Star && tok.Type >= TokType::Spec_Alignas) - || tok.Type == TokType::Ampersand - || tok.Type == TokType::Ampersand_DBL - ; - } - - internal inline - bool tok_is_access_specifier( Token const& tok ) - { - return tok.Type >= TokType::Access_Private && tok.Type <= TokType::Access_Public; - } - - internal inline - AccessSpec tok_to_access_specifier( Token const& tok ) - { - return scast(AccessSpec, tok.Type); - } - - internal inline - bool tok_is_attribute( Token const& tok ) - { - return tok.Type > TokType::Attributes_Start; - } - - struct TokArray - { - Array Arr; - s32 Idx; - - bool __eat( TokType type, char const* context ) - { - if ( Arr.num() - Idx <= 0 ) - { - log_failure( "gen::%s: No tokens left", context ); - return Code::Invalid; - } - - if ( Arr[Idx].Type != type ) - { - String token_str = String::make( GlobalAllocator, { Arr[Idx].Length, Arr[Idx].Text } ); - - log_failure( "gen::%s: expected %s, got %s", context, str_tok_type(type), str_tok_type(Arr[Idx].Type) ); - - return Code::Invalid; - } - - Idx++; - return true; - } - - Token& current() - { - return Arr[Idx]; - } - - Token& previous() - { - return Arr[Idx - 1]; - } - - Token* next() - { - return Idx + 1 < Arr.num() ? &Arr[Idx + 1] : nullptr; - } - }; - - TokArray lex( StrC content, bool keep_preprocess_directives = false ) - { - # define current ( * scanner ) - - # define move_forward() \ - left--; \ - scanner++ - - # define SkipWhitespace() \ - while ( left && char_is_space( current ) ) \ - { \ - move_forward(); \ - } - - # define SkipWhitespace_Checked( Context_, Msg_, ... ) \ - while ( left && char_is_space( current ) ) \ - { \ - move_forward(); \ - } \ - if ( left <= 0 ) \ - { \ - log_failure( "gen::" txt(Context_) ": " Msg_, __VA_ARGS__ ); \ - return { 0, nullptr }; \ - } - - local_persist thread_local - Array Tokens = { nullptr }; - - s32 left = content.Len; - char const* scanner = content.Ptr; - - char const* word = scanner; - s32 word_length = 0; - - SkipWhitespace(); - if ( left <= 0 ) - { - log_failure( "gen::lex: no tokens found (only whitespace provided)" ); - return { { nullptr }, 0 }; - } - - if ( Tokens ) - { - Tokens.free(); - } - - Tokens = Array::init_reserve( LexArena, content.Len / 6 ); - - while (left ) - { - Token token = { nullptr, 0, TokType::Invalid, false }; - - SkipWhitespace(); - if ( left <= 0 ) - break; - - switch ( current ) - { - case '#': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Preprocessor_Directive; - move_forward(); - - while (left && current != '\n' ) - { - if ( current == '\\' ) - { - move_forward(); - - if ( current != '\n' && keep_preprocess_directives ) - { - log_failure( "gen::lex: invalid preprocessor directive, will still grab but will not compile %s", token.Text ); - } - } - - move_forward(); - token.Length++; - } - goto FoundToken; - - case '.': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Access_MemberSymbol; - - if (left) - move_forward(); - - if ( current == '.' ) - { - move_forward(); - if( current == '.' ) - { - token.Length = 3; - token.Type = TokType::Varadic_Argument; - move_forward(); - } - else - { - log_failure( "gen::lex: invalid varadic argument, expected '...' got '..%c'", current ); - } - } - - goto FoundToken; - - case '&' : - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Ampersand; - - if (left) - move_forward(); - - if ( current == '&' ) // && - { - token.Length = 2; - token.Type = TokType::Ampersand_DBL; - - if (left) - move_forward(); - } - - goto FoundToken; - - case ':': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Assign_Classifer; - - if (left) - move_forward(); - - if ( current == ':' ) - { - move_forward(); - token.Type = TokType::Access_StaticSymbol; - token.Length++; - } - goto FoundToken; - - case '{': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::BraceCurly_Open; - - if (left) - move_forward(); - goto FoundToken; - - case '}': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::BraceCurly_Close; - - if (left) - move_forward(); - goto FoundToken; - - case '[': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::BraceSquare_Open; - if ( left ) - { - move_forward(); - - if ( current == ']' ) - { - token.Length = 2; - token.Type = TokType::Operator; - move_forward(); - } - } - goto FoundToken; - - case ']': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::BraceSquare_Close; - - if (left) - move_forward(); - goto FoundToken; - - case '(': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Capture_Start; - - if (left) - move_forward(); - goto FoundToken; - - case ')': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Capture_End; - - if (left) - move_forward(); - goto FoundToken; - - case '\'': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Char; - - move_forward(); - - while ( left && current != '\'' ) - { - move_forward(); - token.Length++; - } - - if ( left ) - { - move_forward(); - token.Length++; - } - goto FoundToken; - - case ',': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Comma; - - if (left) - move_forward(); - goto FoundToken; - - case '*': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Star; - - if (left) - move_forward(); - goto FoundToken; - - case ';': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Statement_End; - - if (left) - move_forward(); - goto FoundToken; - - case '"': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::String; - - move_forward(); - while ( left ) - { - if ( current == '"' ) - { - move_forward(); - break; - } - - if ( current == '\\' ) - { - move_forward(); - token.Length++; - - if ( left ) - { - move_forward(); - token.Length++; - } - continue; - } - - move_forward(); - token.Length++; - } - goto FoundToken; - - // All other operators we just label as an operator and move forward. - case '=': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Operator; - token.IsAssign = true; - - if (left) - move_forward(); - - goto FoundToken; - - case '+': - case '%': - case '^': - case '~': - case '!': - case '<': - case '>': - case '|': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Operator; - - if (left) - move_forward(); - - if ( current == '=' ) - { - token.Length++; - token.IsAssign = true; - - if (left) - move_forward(); - } - else while ( left && current == *(scanner - 1) && token.Length < 3 ) - { - token.Length++; - - if (left) - move_forward(); - } - goto FoundToken; - - // Dash is unfortunatlly a bit more complicated... - case '-': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Operator; - if ( left ) - { - move_forward(); - - if ( current == '>' ) - { - token.Length++; - move_forward(); - - if ( current == '*' ) - { - token.Length++; - move_forward(); - } - } - else if ( current == '=' ) - { - token.Length++; - token.IsAssign = true; - - if (left) - move_forward(); - } - else while ( left && current == *(scanner - 1) && token.Length < 3 ) - { - token.Length++; - - if (left) - move_forward(); - } - } - goto FoundToken; - - case '/': - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Operator; - - if ( left ) - { - move_forward(); - - if ( current == '/' ) - { - token.Type = TokType::Comment; - - move_forward(); - token.Text = scanner; - token.Length = 0; - - while ( left && current != '\n' ) - { - move_forward(); - token.Length++; - } - } - else if ( current == '*' ) - { - token.Type = TokType::Comment; - - move_forward(); - token.Text = scanner; - token.Length = 0; - - while ( left && ( current != '*' && *(scanner + 1) != '/' ) ) - { - move_forward(); - token.Length++; - } - move_forward(); - move_forward(); - } - } - goto FoundToken; - } - - if ( char_is_alpha( current ) || current == '_' ) - { - token.Text = scanner; - token.Length = 1; - move_forward(); - - while ( left && ( char_is_alphanumeric(current) || current == '_' ) ) - { - move_forward(); - token.Length++; - } - - goto FoundToken; - } - else if ( char_is_digit(current) ) - { - // This is a very brute force lex, no checks are done for validity of literal. - - token.Text = scanner; - token.Length = 1; - token.Type = TokType::Number; - move_forward(); - - if (left - && ( current == 'x' || current == 'X' - || current == 'b' || current == 'B' - || current == 'o' || current == 'O' ) - ) - { - move_forward(); - token.Length++; - - while ( left && char_is_hex_digit(current) ) - { - move_forward(); - token.Length++; - } - - goto FoundToken; - } - - while ( left && char_is_digit(current) ) - { - move_forward(); - token.Length++; - } - - if ( left && current == '.' ) - { - move_forward(); - token.Length++; - - while ( left && char_is_digit(current) ) - { - move_forward(); - token.Length++; - } - } - - goto FoundToken; - } - else - { - String context_str = String::fmt_buf( GlobalAllocator, "%s", scanner, min( 100, left ) ); - - log_failure( "Failed to lex token %s", context_str ); - - // Skip to next whitespace since we can't know if anything else is valid until then. - while ( left && ! char_is_space( current ) ) - { - move_forward(); - } - } - - FoundToken: - - if ( token.Type != TokType::Invalid ) - { - if ( token.Type == TokType::Preprocessor_Directive && keep_preprocess_directives == false ) - continue; - - Tokens.append( token ); - continue; - } - - TokType type = get_tok_type( token.Text, token.Length ); - - if ( type == TokType::Invalid) - type = TokType::Identifier; - - token.Type = type; - Tokens.append( token ); - } - - if ( Tokens.num() == 0 ) - { - log_failure( "Failed to lex any tokens" ); - return { { nullptr }, 0 }; - } - - return { Tokens, 0 }; - # undef current - # undef move_forward - # undef SkipWhitespace - # undef SkipWhitespace_Checked - } -} - -#pragma region Helper Macros -# define check_parse_args( func, def ) \ -if ( def.Len <= 0 ) \ -{ \ - log_failure( "gen::" stringize(func) ": length must greater than 0" ); \ - return CodeInvalid; \ -} \ -if ( def.Ptr == nullptr ) \ -{ \ - log_failure( "gen::" stringize(func) ": def was null" ); \ - return CodeInvalid; \ -} - -# define nexttok toks.next() -# define currtok toks.current() -# define prevtok toks.previous() -# define eat( Type_ ) toks.__eat( Type_, context ) -# define left ( toks.Arr.num() - toks.Idx ) - -# define check( Type_ ) ( left && currtok.Type == Type_ ) -#pragma endregion Helper Macros - -struct ParseContext -{ - ParseContext* Parent; - char const* Fn; -}; - -internal Code parse_function_body( Parser::TokArray& toks, char const* context ); -internal Code parse_global_nspace( Parser::TokArray& toks, char const* context ); - -internal CodeClass parse_class ( Parser::TokArray& toks, char const* context ); -internal CodeEnum parse_enum ( Parser::TokArray& toks, char const* context ); -internal CodeBody parse_export_body ( Parser::TokArray& toks, char const* context ); -internal CodeBody parse_extern_link_body( Parser::TokArray& toks, char const* context ); -internal CodeExtern parse_exten_link ( Parser::TokArray& toks, char const* context ); -internal CodeFriend parse_friend ( Parser::TokArray& toks, char const* context ); -internal CodeFn parse_function ( Parser::TokArray& toks, char const* context ); -internal CodeNamespace parse_namespace ( Parser::TokArray& toks, char const* context ); -internal CodeOpCast parse_operator_cast ( Parser::TokArray& toks, char const* context ); -internal CodeStruct parse_struct ( Parser::TokArray& toks, char const* context ); -internal CodeVar parse_variable ( Parser::TokArray& toks, char const* context ); -internal CodeTemplate parse_template ( Parser::TokArray& toks, char const* context ); -internal CodeType parse_type ( Parser::TokArray& toks, char const* context ); -internal CodeTypedef parse_typedef ( Parser::TokArray& toks, char const* context ); -internal CodeUnion parse_union ( Parser::TokArray& toks, char const* context ); -internal CodeUsing parse_using ( Parser::TokArray& toks, char const* context ); - -internal inline -Code parse_array_decl( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - - if ( check( TokType::BraceSquare_Open ) ) - { - eat( TokType::BraceSquare_Open ); - - if ( left == 0 ) - { - log_failure( "%s: Error, unexpected end of typedef definition ( '[]' scope started )", stringize(parse_typedef) ); - return Code::Invalid; - } - - if ( currtok.Type == TokType::BraceSquare_Close ) - { - log_failure( "%s: Error, empty array expression in typedef definition", stringize(parse_typedef) ); - return Code::Invalid; - } - - Token untyped_tok = currtok; - - while ( left && currtok.Type != TokType::BraceSquare_Close ) - { - eat( currtok.Type ); - } - - untyped_tok.Length = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)untyped_tok.Text; - - Code array_expr = untyped_str( untyped_tok ); - - if ( left == 0 ) - { - log_failure( "%s: Error, unexpected end of type definition, expected ]", stringize(parse_typedef) ); - return Code::Invalid; - } - - if ( currtok.Type != TokType::BraceSquare_Close ) - { - log_failure( "%s: Error, expected ] in type definition, not %s", stringize(parse_typedef), str_tok_type( currtok.Type ) ); - return Code::Invalid; - } - - eat( TokType::BraceSquare_Close ); - return array_expr; - } - - return { nullptr }; -} - -internal inline -CodeAttributes parse_attributes( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - - Token start; - s32 len = 0; - - if ( check(TokType::Attribute_Open) ) - { - eat( TokType::Attribute_Open); - - while ( left && currtok.Type != TokType::Attribute_Close ) - { - eat( currtok.Type ); - } - - eat( TokType::Attribute_Close ); - - s32 len = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)start.Text; - } - - else if ( check(TokType::Decl_GNU_Attribute) ) - { - eat(TokType::BraceCurly_Open); - eat(TokType::BraceCurly_Open); - - while ( left && currtok.Type != TokType::BraceCurly_Close ) - { - eat(currtok.Type); - } - - eat(TokType::BraceCurly_Close); - eat(TokType::BraceCurly_Close); - - s32 len = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)start.Text; - } - - else if ( check(TokType::Decl_MSVC_Attribute) ) - { - eat( TokType::Decl_MSVC_Attribute ); - eat( TokType::BraceCurly_Open); - - while ( left && currtok.Type != TokType::BraceCurly_Close ) - { - eat(currtok.Type); - } - - eat(TokType::BraceCurly_Close); - - s32 len = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)start.Text; - } - - else if ( tok_is_attribute( currtok ) ) - { - eat(currtok.Type); - s32 len = start.Length; - } - - if ( len > 0 ) - { - StrC attribute_txt = { len, start.Text }; - return def_attributes( attribute_txt ); - } - - return { nullptr }; -} - -internal inline -Parser::Token parse_identifier( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - Token name = currtok; - - eat( TokType::Identifier ); - - while ( check( TokType::Access_StaticSymbol ) ) - { - eat( TokType::Access_StaticSymbol ); - - if ( left == 0 ) - { - log_failure( "%s: Error, unexpected end of type definition, expected identifier", context ); - return { nullptr, 0, TokType::Invalid }; - } - - if ( currtok.Type != TokType::Identifier ) - { - log_failure( "%s: Error, expected identifier in type definition, not %s", context, str_tok_type( currtok.Type ) ); - return { nullptr, 0, TokType::Invalid }; - } - - name.Length = ( (sptr)currtok.Text + currtok.Length ) - (sptr)name.Text; - eat( TokType::Identifier ); - } - - return name; -} - -internal -CodeParam parse_params( Parser::TokArray& toks, char const* context, bool use_template_capture = false ) -{ - using namespace Parser; - using namespace ECode; - - if ( ! use_template_capture ) - eat( TokType::Capture_Start ); - - else - { - if ( check ( TokType::Operator ) && currtok.Text[0] == '<' ) - eat( TokType::Operator ); - } - - if ( ! use_template_capture && check(TokType::Capture_End) ) - { - eat( TokType::Capture_End ); - return { nullptr }; - } - - CodeType type = { nullptr }; - Code value = { nullptr }; - - if ( check( TokType::Varadic_Argument) ) - { - eat( TokType::Varadic_Argument ); - - return param_varadic; - } - - type = parse_type( toks, context ); - if ( type == Code::Invalid ) - return CodeInvalid; - - Token name = { nullptr, 0, TokType::Invalid, false }; - - if ( check( TokType::Identifier ) ) - { - 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 CodeInvalid; - } - - 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 ); - } - } - - CodeParam - result = (CodeParam) make_code(); - result->Type = Parameters; - - if ( name.Length > 0 ) - result->Name = get_cached_string( name ); - - result->ValueType = type; - - if ( value ) - result->Value = value; - - result->NumEntries++; - - while ( left - && use_template_capture ? - currtok.Type != TokType::Operator && currtok.Text[0] != '>' - : currtok.Type != TokType::Capture_End ) - { - eat( TokType::Comma ); - - Code type = { nullptr }; - Code value = { nullptr }; - - if ( check( TokType::Varadic_Argument) ) - { - eat( TokType::Varadic_Argument ); - result.append( param_varadic ); - continue; - } - - type = parse_type( toks, context ); - if ( type == Code::Invalid ) - return CodeInvalid; - - name = { nullptr, 0, TokType::Invalid, false }; - - if ( check( TokType::Identifier ) ) - { - 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 CodeInvalid; - } - - 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 ); - } - } - - CodeParam - param = (CodeParam) make_code(); - param->Type = Parameters; - - if ( name.Length > 0 ) - param->Name = get_cached_string( name ); - - param->ValueType = type; - - if ( value ) - param->Value = value; - - result.append( param ); - } - - if ( ! use_template_capture ) - eat( TokType::Capture_End ); - - else - { - if ( ! check( TokType::Operator) || currtok.Text[0] != '>' ) - { - log_failure("gen::parse_template: expected '<' after 'template' keyword. %s", str_tok_type( currtok.Type )); - return CodeInvalid; - } - eat( TokType::Operator ); - } - - return result; -# undef context -} - -// Function parsing is handled in multiple places because its initial signature is shared with variable parsing -internal inline -CodeFn parse_function_after_name( - ModuleFlag mflags - , CodeAttributes attributes - , CodeSpecifiers specifiers - , CodeType ret_type - , StrC name - , Parser::TokArray& toks - , char const* context -) -{ - using namespace Parser; - - CodeParam params = parse_params( toks, stringize(parse_function) ); - - while ( left && tok_is_specifier( currtok ) ) - { - specifiers.append( ESpecifier::to_type(currtok) ); - eat( currtok.Type ); - } - - CodeBody body = { nullptr }; - if ( check( TokType::BraceCurly_Open ) ) - { - body = parse_function_body( toks, stringize(parse_function) ); - if ( body == Code::Invalid ) - return CodeInvalid; - } - else - { - eat( TokType::Statement_End ); - } - - using namespace ECode; - - CodeFn - result = (CodeFn) 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 CodeInvalid; - } - } - - result->Type = Function; - result->Body = body; - } - else - { - result->Type = Function_Fwd; - } - - if ( specifiers ) - result->Specs = specifiers; - - result->ReturnType = ret_type; - - if ( params ) - result->Params = params; - - return result; -} - -internal inline -CodeOperator parse_operator_after_ret_type( ModuleFlag mflags - , CodeAttributes attributes - , CodeSpecifiers specifiers - , CodeType ret_type - , Parser::TokArray& toks - , char const* context ) -{ - using namespace Parser; - using namespace EOperator; - - // Parse Operator - eat( TokType::Decl_Operator ); - - if ( ! check( TokType::Operator ) ) - { - log_failure( "gen::%s: Expected operator after 'operator' keyword", context ); - return CodeInvalid; - } - - OperatorT op = Invalid; - switch ( currtok.Text[0] ) - { - case '+': - { - if ( currtok.Text[1] == '=' ) - op = Assign_Add; - - else - op = Add; - } - break; - case '-': - { - if ( currtok.Text[1] == '=' ) - op = Assign_Subtract; - - else - op = Subtract; - } - break; - case '*': - { - if ( currtok.Text[1] == '=' ) - op = Assign_Multiply; - - else - { - Token& finder = prevtok; - while ( finder.Type != TokType::Decl_Operator ) - { - if ( finder.Type == TokType::Identifier) - { - op = Indirection; - break; - } - } - - if ( op == Invalid) - op = Multiply; - } - } - break; - case '/': - { - if ( currtok.Text[1] == '=' ) - op = Assign_Divide; - - else - op = Divide; - } - break; - case '%': - { - if ( currtok.Text[1] == '=' ) - op = Assign_Modulo; - - else - op = Modulo; - } - break; - case '&': - { - if ( currtok.Text[1] == '=' ) - op = Assign_BAnd; - - else if ( currtok.Text[1] == '&' ) - op = LAnd; - - else - { - - - if ( op == Invalid ) - op = BAnd; - } - } - break; - case '|': - { - if ( currtok.Text[1] == '=' ) - op = Assign_BOr; - - else if ( currtok.Text[1] == '|' ) - op = LOr; - - else - op = BOr; - } - break; - case '^': - { - if ( currtok.Text[1] == '=' ) - op = Assign_BXOr; - - else - op = BXOr; - } - break; - case '~': - { - op = BNot; - } - break; - case '!': - { - if ( currtok.Text[1] == '=' ) - op = LNot; - - else - op = UnaryNot; - } - break; - case '=': - { - if ( currtok.Text[1] == '=' ) - op = LEqual; - - else - op = Assign; - } - break; - case '<': - { - if ( currtok.Text[1] == '=' ) - op = LEqual; - - else if ( currtok.Text[1] == '<' ) - { - if ( currtok.Text[2] == '=' ) - op = Assign_LShift; - - else - op = LShift; - } - else - op = Lesser; - } - break; - case '>': - { - if ( currtok.Text[1] == '=' ) - op = GreaterEqual; - - else if ( currtok.Text[1] == '>' ) - { - if ( currtok.Text[2] == '=' ) - op = Assign_RShift; - - else - op = RShift; - } - else - op = Greater; - } - break; - case '(': - { - if ( currtok.Text[1] == ')' ) - op = FunctionCall; - - else - op = Invalid; - } - break; - case '[': - { - if ( currtok.Text[1] == ']' ) - op = Subscript; - - else - op = Invalid; - } - break; - default: - { - break; - } - } - - if ( op == Invalid ) - { - log_failure( "gen::%s: Invalid operator '%s'", context, currtok.Text ); - return CodeInvalid; - } - - eat( TokType::Operator ); - - // Parse Params - CodeParam params = parse_params( toks, stringize(parse_operator) ); - - while ( left && tok_is_specifier( currtok ) ) - { - specifiers.append( ESpecifier::to_type(currtok) ); - eat( currtok.Type ); - } - - // Parse Body - CodeBody body = { nullptr }; - if ( check( TokType::BraceCurly_Open ) ) - { - body = parse_function_body( toks, stringize(parse_function) ); - if ( body == Code::Invalid ) - return CodeInvalid; - } - else - { - eat( TokType::Statement_End ); - } - - // OpValidateResult check_result = operator__validate( op, params, ret_type, specifiers ); - CodeOperator 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 -CodeVar parse_variable_after_name( - ModuleFlag mflags - , CodeAttributes attributes - ,CodeSpecifiers specifiers - , CodeType type - , StrC name - , Parser::TokArray& toks - , char const* context ) -{ - using namespace Parser; - - Code array_expr = parse_array_decl( toks, stringize(parse_variable) ); - - Code expr = { nullptr }; - - 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 CodeInvalid; - } - - while ( left && currtok.Type != TokType::Statement_End ) - { - eat( currtok.Type ); - } - - expr_tok.Length = ( (sptr)currtok.Text + currtok.Length ) - (sptr)expr_tok.Text; - expr = untyped_str( expr_tok ); - } - - eat( TokType::Statement_End ); - - using namespace ECode; - - CodeVar - result = (CodeVar) make_code(); - result->Type = Variable; - result->Name = get_cached_string( name ); - result->ModuleFlags = mflags; - - result->ValueType = type; - - if (array_expr ) - type->ArrExpr = array_expr; - - if ( attributes ) - result->Attributes = attributes; - - if ( specifiers ) - result->Specs = specifiers; - - if ( expr ) - result->Value = expr; - - return result; -} - -internal inline -Code parse_variable_assignment( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - - 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 ); - } - - return expr; -} - -internal inline -Code parse_operator_function_or_variable( bool expects_function, CodeAttributes attributes, CodeSpecifiers specifiers, Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - - Code result = Code::Invalid; - - CodeType type = parse_type( toks, stringize(parse_variable) ); - - if ( type == Code::Invalid ) - return CodeInvalid; - - if ( check( TokType::Operator) ) - { - // Dealing with an operator overload - result = parse_operator_after_ret_type( ModuleFlag::None, attributes, specifiers, type, toks, stringize(parse_template) ); - } - else - { - StrC name = currtok; - eat( TokType::Identifier ); - - if ( check( TokType::Capture_Start) ) - { - // Dealing with a function - - result = parse_function_after_name( ModuleFlag::None, attributes, specifiers, type, name, toks, stringize(parse_template) ); - } - else - { - if ( expects_function ) - { - log_failure( "gen::parse_operator_function_or_variable: 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, stringize(parse_template) ); - } - } - - return result; -} - -internal -CodeBody parse_class_struct_body( Parser::TokType which, Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - using namespace ECode; - - eat( TokType::BraceCurly_Open ); - - CodeBody - result = (CodeBody) make_code(); - - if ( which == TokType::Decl_Class ) - result->Type = Class_Body; - - else - result->Type = Struct_Body; - - while ( left && currtok.Type != TokType::BraceCurly_Close ) - { - Code member = Code::Invalid; - CodeAttributes attributes = { nullptr }; - CodeSpecifiers specifiers = { nullptr }; - - bool expects_function = false; - - switch ( currtok.Type ) - { - case TokType::Comment: - member = def_comment( currtok ); - eat( TokType::Comment ); - break; - - case TokType::Access_Public: - member = access_public; - eat( TokType::Access_Public ); - eat( TokType::Assign_Classifer ); - break; - - case TokType::Access_Protected: - member = access_protected; - eat( TokType::Access_Protected ); - eat( TokType::Assign_Classifer ); - break; - - case TokType::Access_Private: - member = access_private; - eat( TokType::Access_Private ); - eat( TokType::Assign_Classifer ); - break; - - case TokType::Decl_Class: - member = parse_class( toks, context ); - break; - - case TokType::Decl_Enum: - member = parse_enum( toks, context ); - break; - - case TokType::Decl_Friend: - member = parse_friend( toks, context ); - break; - - case TokType::Decl_Operator: - member = parse_operator_cast( toks, context ); - break; - - case TokType::Decl_Struct: - member = parse_struct( toks, context ); - break; - - case TokType::Decl_Template: - member = parse_template( toks, context ); - break; - - case TokType::Decl_Typedef: - member = parse_typedef( toks, context ); - break; - - case TokType::Decl_Union: - member = parse_variable( toks, context ); - break; - - case TokType::Decl_Using: - member = parse_using( toks, context ); - break; - - case TokType::Attribute_Open: - case TokType::Decl_GNU_Attribute: - case TokType::Decl_MSVC_Attribute: - #define Entry( attribute, str ) case TokType::attribute: - GEN_Define_Attribute_Tokens - #undef Entry - { - attributes = parse_attributes( toks, context ); - } - //! 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_Volatile: - { - 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::Inline: - case ESpecifier::Mutable: - case ESpecifier::Static: - case ESpecifier::Volatile: - break; - - case ESpecifier::Consteval: - expects_function = true; - break; - - default: - log_failure( "gen::parse_class_struct_body: invalid specifier " "%s" " for variable", ESpecifier::to_str(spec) ); - return CodeInvalid; - } - - 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: - case TokType::Type_Long: - { - member = parse_operator_function_or_variable( expects_function, attributes, specifiers, toks, context ); - } - break; - - 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 ) - { - log_failure( "gen::parse_variable: failed to parse member" ); - return CodeInvalid; - } - - result.append( member ); - } - - eat( TokType::BraceCurly_Close ); - return result; -} - -internal -Code parse_class_struct( Parser::TokType which, Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - - if ( which != TokType::Decl_Class && which != TokType::Decl_Struct ) - { - log_failure( "%s: Error, expected class or struct, not %s", context, str_tok_type( which ) ); - return CodeInvalid; - } - - Token name { nullptr, 0, TokType::Invalid }; - - AccessSpec access = AccessSpec::Default; - CodeType parent = { nullptr }; - CodeBody body = { nullptr }; - CodeAttributes attributes = { nullptr }; - ModuleFlag mflags = ModuleFlag::None; - - CodeClass result = CodeInvalid; - - if ( check(TokType::Module_Export) ) - { - mflags = ModuleFlag::Export; - eat( TokType::Module_Export ); - } - - eat( which ); - - attributes = parse_attributes( toks, context ); - - if ( check( TokType::Identifier ) ) - name = parse_identifier( toks, context ); - - local_persist - char interface_arr_mem[ kilobytes(4) ] {0}; - Array interfaces = Array::init_reserve( Arena::init_from_memory(interface_arr_mem, kilobytes(4) ), 4 ); - - if ( check( TokType::Assign_Classifer ) ) - { - eat( TokType::Assign_Classifer ); - - if ( tok_is_access_specifier( currtok ) ) - { - access = tok_to_access_specifier( currtok ); - } - - Token parent_tok = parse_identifier( toks, context ); - parent = def_type( parent_tok ); - - while ( check(TokType::Comma) ) - { - eat(TokType::Access_Public); - - if ( tok_is_access_specifier( currtok ) ) - { - eat(currtok.Type); - } - - Token interface_tok = parse_identifier( toks, context ); - - interfaces.append( def_type( interface_tok ) ); - } - } - - if ( check( TokType::BraceCurly_Open ) ) - { - body = parse_class_struct_body( which, toks, context ); - } - - eat( TokType::Statement_End ); - - if ( which == TokType::Decl_Class ) - result = def_class( name, body, parent, access - , attributes - , mflags - ); - - else - result = def_struct( name, body, (CodeType)parent, access - , attributes - , mflags - ); - - interfaces.free(); - return result; -} - -internal -Code parse_function_body( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - using namespace ECode; - - eat( TokType::BraceCurly_Open ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = Function_Body; - - Token start = currtok; - - s32 level = 0; - while ( left && ( currtok.Type != TokType::BraceCurly_Close || level > 0 ) ) - { - if ( currtok.Type == TokType::BraceCurly_Open ) - level++; - - else if ( currtok.Type == TokType::BraceCurly_Close && level > 0 ) - level--; - - eat( currtok.Type ); - } - - Token previous = prevtok; - - s32 len = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)start.Text; - - if ( len > 0 ) - { - result.append( def_execution( { len, start.Text } ) ); - } - - eat( TokType::BraceCurly_Close ); - return result; -} - -internal -CodeBody parse_global_nspace( CodeT which, Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - using namespace ECode; - - if ( which != Namespace_Body && which != Global_Body && which != Export_Body && which != Extern_Linkage_Body ) - return CodeInvalid; - - if ( which != Global_Body ) - eat( TokType::BraceCurly_Open ); - - CodeBody - result = (CodeBody) make_code(); - result->Type = which; - - while ( left && currtok.Type != TokType::BraceCurly_Close ) - { - Code member = Code::Invalid; - CodeAttributes attributes = { nullptr }; - CodeSpecifiers specifiers = { nullptr }; - - bool expects_function = false; - - switch ( currtok.Type ) - { - case TokType::Comment: - member = def_comment( currtok ); - eat( TokType::Comment ); - break; - - case TokType::Decl_Enum: - member = parse_enum( toks, context); - break; - - case TokType::Decl_Class: - member = parse_class( toks, context ); - break; - - case TokType::Decl_Extern_Linkage: - if ( which == Extern_Linkage_Body ) - log_failure( "gen::parse_global_nspace: nested extern linkage" ); - - member = parse_extern_link_body( toks, context ); - break; - - case TokType::Decl_Namespace: - member = parse_namespace( toks, context ); - break; - - case TokType::Decl_Struct: - member = parse_struct( toks, context ); - break; - - case TokType::Decl_Template: - member = parse_template( toks, context ); - break; - - case TokType::Decl_Typedef: - member = parse_typedef( toks, context ); - break; - - case TokType::Decl_Union: - member = parse_union( toks, context ); - break; - - case TokType::Decl_Using: - member = parse_using( toks, context ); - break; - - case TokType::Module_Export: - if ( which == Export_Body ) - log_failure( "gen::parse_global_nspace: nested export declaration" ); - - member = parse_export_body( toks, context ); - break; - - case TokType::Module_Import: - { - not_implemented( context ); - } - //! Fallthrough intentional - case TokType::Decl_GNU_Attribute: - case TokType::Decl_MSVC_Attribute: - #define Entry( attribute, str ) case TokType::attribute: - GEN_Define_Attribute_Tokens - #undef Entry - { - attributes = parse_attributes( toks, context ); - } - //! 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::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::Inline: - case ESpecifier::Mutable: - case ESpecifier::Static: - case ESpecifier::Volatile: - break; - - case ESpecifier::Consteval: - expects_function = true; - break; - - default: - log_failure( "gen::parse_global_nspace: invalid specifier " "%s" " for variable", ESpecifier::to_str(spec) ); - return CodeInvalid; - } - - 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_Long: - case TokType::Type_Short: - case TokType::Type_Signed: - case TokType::Type_Unsigned: - { - member = parse_operator_function_or_variable( expects_function, attributes, specifiers, toks, context ); - } - } - - if ( member == Code::Invalid ) - { - log_failure( "gen::%s: failed to parse extern linkage member", context ); - return CodeInvalid; - } - - result.append( member ); - } - - if ( which != Global_Body ) - eat( TokType::BraceCurly_Close ); - - return result; -} - -internal -CodeClass parse_class( Parser::TokArray& toks, char const* context ) -{ - return (CodeClass) parse_class_struct( Parser::TokType::Decl_Class, toks, context ); -} - -CodeClass parse_class( StrC def ) -{ - check_parse_args( parse_class, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return (CodeClass) parse_class_struct( TokType::Decl_Class, toks, stringize(parse_class) ); -} - -internal -CodeEnum parse_enum( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - using namespace ECode; - - SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; - s32 num_specifiers = 0; - - Token name = { nullptr, 0, TokType::Invalid }; - Code array_expr = { nullptr }; - CodeType type = { nullptr }; - Token body = { nullptr, 0, TokType::Invalid }; - - char entries_code[ kilobytes(128) ] { 0 }; - s32 entries_length = 0; - - bool is_enum_class = false; - - eat( TokType::Decl_Enum ); - - if ( currtok.Type == TokType::Decl_Class ) - { - eat( TokType::Decl_Class); - is_enum_class = true; - } - - // TODO : Parse attributes - - if ( currtok.Type != TokType::Identifier ) - { - log_failure( "gen::parse_enum: expected identifier for enum name" ); - return CodeInvalid; - } - - name = currtok; - eat( TokType::Identifier ); - - if ( currtok.Type == TokType::Assign_Classifer ) - { - eat( TokType::Assign_Classifer ); - - type = parse_type( toks, stringize(parse_enum) ); - if ( type == Code::Invalid ) - return CodeInvalid; - } - - if ( currtok.Type == TokType::BraceCurly_Open ) - { - eat( TokType::BraceCurly_Open ); - - body = currtok; - - while ( currtok.Type != TokType::BraceCurly_Close ) - { - eat( TokType::Identifier); - - if ( currtok.Type == TokType::Operator && currtok.Text[0] == '=' ) - { - eat( TokType::Operator ); - - while ( currtok.Type != TokType::Comma && currtok.Type != TokType::BraceCurly_Close ) - { - eat( currtok.Type ); - } - } - - if ( currtok.Type == TokType::Comma ) - { - eat( TokType::Comma ); - } - } - - body.Length = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)body.Text; - - eat( TokType::BraceCurly_Close ); - } - - eat( TokType::Statement_End ); - - using namespace ECode; - - CodeEnum - result = (CodeEnum) make_code(); - - if ( body.Length ) - { - // mem_copy( entries_code, body.Text, body.Length ); - - Code untyped_body = untyped_str( body ); - - result->Type = is_enum_class ? Enum_Class : Enum; - result->Body = untyped_body; - } - else - { - result->Type = is_enum_class ? Enum_Class_Fwd : Enum_Fwd; - } - - result->Name = get_cached_string( name ); - - if ( type ) - result->UnderlyingType = type; - - return result; -} - -CodeEnum parse_enum( StrC def ) -{ - check_parse_args( parse_enum, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return parse_enum( toks, stringize(parse_enum) ); -} - -internal inline -CodeBody parse_export_body( Parser::TokArray& toks, char const* context ) -{ - return parse_global_nspace( ECode::Export_Body, toks, context ); -} - -CodeBody parse_export_body( StrC def ) -{ - check_parse_args( parse_export_body, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return parse_export_body( toks, stringize(parse_export_body) ); -} - -internal inline -CodeBody parse_extern_link_body( Parser::TokArray& toks, char const* context ) -{ - return parse_global_nspace( ECode::Extern_Linkage_Body, toks, context ); -} - -internal -CodeExtern parse_extern_link( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - - eat( TokType::Decl_Extern_Linkage ); - - Token name = currtok; - eat( TokType::String ); - - name.Text += 1; - name.Length -= 1; - - CodeExtern - result = (CodeExtern) make_code(); - result->Type = ECode::Extern_Linkage; - result->Name = get_cached_string( name ); - - Code entry = parse_extern_link_body( toks, context ); - if ( entry == Code::Invalid ) - { - log_failure( "gen::parse_extern_link: failed to parse body" ); - return result; - } - - result->Body = entry; - - return result; -} - -CodeExtern parse_extern_link( StrC def ) -{ - check_parse_args( parse_extern_link, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return parse_extern_link( toks, stringize(parse_extern_link) ); -} - -internal -CodeFriend parse_friend( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - using namespace ECode; - - eat( TokType::Decl_Friend ); - - CodeFn function = { nullptr }; - - // Type declaration or return type - CodeType type = parse_type( toks, stringize(parse_friend) ); - if ( type == Code::Invalid ) - return CodeInvalid; - - // Funciton declaration - if ( currtok.Type == TokType::Identifier ) - { - // Name - Token name = parse_identifier( toks, stringize(parse_friend) ); - - // Parameter list - CodeParam params = parse_params( toks, stringize(parse_friend) ); - - function = make_code(); - function->Type = Function_Fwd; - function->Name = get_cached_string( name ); - function->ReturnType = type; - - if ( params ) - function->Params = params; - } - - eat( TokType::Statement_End ); - - CodeFriend - result = (CodeFriend) make_code(); - result->Type = Friend; - - if ( function ) - result->Declaration = function; - - else - result->Declaration = type; - - return result; -} - -CodeFriend parse_friend( StrC def ) -{ - check_parse_args( parse_friend, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return parse_friend( toks, stringize(parse_friend) ); -} - -internal -CodeFn parse_functon( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - - SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; - s32 num_specifiers = 0; - - CodeAttributes attributes = { nullptr }; - CodeSpecifiers specifiers = { nullptr }; - ModuleFlag mflags = ModuleFlag::None; - - if ( check(TokType::Module_Export) ) - { - mflags = ModuleFlag::Export; - eat( TokType::Module_Export ); - } - - attributes = parse_attributes( toks, context ); - - while ( left && tok_is_specifier( currtok ) ) - { - SpecifierT spec = ESpecifier::to_type( currtok ); - - switch ( spec ) - { - case ESpecifier::Const: - case ESpecifier::Consteval: - case ESpecifier::Constexpr: - case ESpecifier::External_Linkage: - case ESpecifier::Inline: - case ESpecifier::Static: - break; - - default: - log_failure( "gen::parse_functon: invalid specifier " "%s" " for functon", ESpecifier::to_str(spec) ); - return CodeInvalid; - } - - 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 ); - } - - CodeType ret_type = parse_type( toks, stringize(parse_function) ); - if ( ret_type == Code::Invalid ) - return CodeInvalid; - - Token name = parse_identifier( toks, stringize(parse_function) ); - if ( ! name ) - return CodeInvalid; - - CodeFn result = parse_function_after_name( mflags, attributes, specifiers, ret_type, name, toks, context ); - - return result; -} - -CodeFn parse_function( StrC def ) -{ - using namespace Parser; - - check_parse_args( parse_function, def ); - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return (CodeFn) parse_functon( toks, stringize(parse_function) ); -} - -CodeBody parse_global_body( StrC def ) -{ - check_parse_args( parse_global_body, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return parse_global_nspace( ECode::Global_Body, toks, stringize(parse_global_body) ); -} - -internal -CodeNamespace parse_namespace( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - eat( TokType::Decl_Namespace ); - - Token name = parse_identifier( toks, stringize(parse_namespace) ); - - CodeBody body = parse_global_nspace( ECode::Namespace_Body, toks, stringize(parse_namespace) ); - if ( body == Code::Invalid ) - return CodeInvalid; - - CodeNamespace - result = (CodeNamespace) make_code(); - result->Type = ECode::Namespace; - result->Name = get_cached_string( name ); - - result->Body = body; - - return result; -} - -CodeNamespace parse_namespace( StrC def ) -{ - check_parse_args( parse_namespace, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return parse_namespace( toks, stringize(parse_namespace) ); -} - -internal -CodeOperator parse_operator( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - - CodeAttributes attributes = { nullptr }; - CodeSpecifiers specifiers = { nullptr }; - ModuleFlag mflags = ModuleFlag::None; - - SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; - s32 num_specifiers = 0; - - if ( check(TokType::Module_Export) ) - { - mflags = ModuleFlag::Export; - eat( TokType::Module_Export ); - } - - attributes = parse_attributes( toks, context ); - - while ( left && tok_is_specifier( currtok ) ) - { - SpecifierT spec = ESpecifier::to_type( currtok ); - - switch ( spec ) - { - case ESpecifier::Const: - case ESpecifier::Constexpr: - case ESpecifier::Inline: - case ESpecifier::Static: - break; - - default: - log_failure( "gen::parse_operator: invalid specifier " "%s" " for operator", ESpecifier::to_str(spec) ); - return CodeInvalid; - } - - 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 ); - } - - // Parse Return Type - CodeType ret_type = parse_type( toks, stringize(parse_operator) ); - - CodeOperator result = parse_operator_after_ret_type( mflags, attributes, specifiers, ret_type, toks, context ); - return result; -} - -CodeOperator parse_operator( StrC def ) -{ - check_parse_args( parse_operator, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return (CodeOperator) parse_operator( toks, stringize(parse_operator) ); -} - -CodeOpCast parse_operator_cast( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - - eat( TokType::Decl_Operator ); - - Code type = parse_type( toks, stringize(parse_operator_cast) ); - - eat( TokType::Capture_Start ); - eat( TokType::Capture_End ); - - CodeSpecifiers specifiers = { nullptr }; - - if ( check(TokType::Spec_Const)) - specifiers = spec_const; - - Code body = { nullptr }; - - if ( check( TokType::BraceCurly_Open) ) - { - eat( TokType::BraceCurly_Open ); - - Token body_str = currtok; - - s32 level = 0; - while ( left && ( currtok.Type != TokType::BraceCurly_Close || level > 0 ) ) - { - if ( currtok.Type == TokType::BraceCurly_Open ) - level++; - - else if ( currtok.Type == TokType::BraceCurly_Close && level > 0 ) - level--; - - eat( currtok.Type ); - } - - body_str.Length = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)body_str.Text; - - body = untyped_str( body_str ); - - eat( TokType::BraceCurly_Close ); - } - - CodeOpCast result = (CodeOpCast) make_code(); - - if (body) - { - result->Type = ECode::Operator_Cast; - result->Body = body; - } - else - { - result->Type = ECode::Operator_Cast_Fwd; - } - - if ( specifiers ) - result->Specs = specifiers; - - result->ValueType = type; - - return result; -} - -CodeOpCast parse_operator_cast( StrC def ) -{ - check_parse_args( parse_operator_cast, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return parse_operator_cast( toks, stringize(parse_operator_cast) ); -} - -internal inline -CodeStruct parse_struct( Parser::TokArray& toks, char const* context ) -{ - return (CodeStruct) parse_class_struct( Parser::TokType::Decl_Struct, toks, stringize(parse_struct) ); -} - -CodeStruct parse_struct( StrC def ) -{ - check_parse_args( parse_struct, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return (CodeStruct) parse_class_struct( TokType::Decl_Struct, toks, stringize(parse_struct) ); -} - -internal -CodeTemplate parse_template( Parser::TokArray& toks, char const* context ) -{ -# define UseTemplateCapture true - - using namespace Parser; - - ModuleFlag mflags = ModuleFlag::None; - - if ( check(TokType::Module_Export) ) - { - mflags = ModuleFlag::Export; - eat( TokType::Module_Export ); - } - - eat( TokType::Decl_Template ); - - Code params = parse_params( toks, stringize(parse_template), UseTemplateCapture ); - if ( params == Code::Invalid ) - return CodeInvalid; - - Code definition = { nullptr }; - - while ( left ) - { - if ( check( TokType::Decl_Class ) ) - { - definition = parse_class( toks, stringize(parse_template) ); - break; - } - - if ( check( TokType::Decl_Struct ) ) - { - definition = parse_enum( toks, stringize(parse_template) ); - break; - } - - if ( check( TokType::Decl_Using )) - { - definition = parse_using( toks, stringize(parse_template) ); - break; - } - - // Its either a function or a variable - Token name = { nullptr, 0, TokType::Invalid }; - - CodeAttributes attributes = { nullptr }; - CodeSpecifiers specifiers = { nullptr }; - - bool expects_function = false; - - SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; - s32 num_specifiers = 0; - - attributes = parse_attributes( toks, stringize(parse_template) ); - - 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: - case ESpecifier::Thread_Local: - case ESpecifier::Volatile: - break; - - case ESpecifier::Consteval: - expects_function = true; - break; - - default: - log_failure( "gen::parse_template: invalid specifier %s for variable or function", ESpecifier::to_str( spec ) ); - return CodeInvalid; - } - - // 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, stringize(parse_template) ); - break; - } - - CodeTemplate - result = (CodeTemplate) make_code(); - result->Type = ECode::Template; - result->Params = params; - result->Declaration = definition; - result->ModuleFlags = mflags; - - return result; -# undef UseTemplateCapture -} - -CodeTemplate parse_template( StrC def ) -{ - check_parse_args( parse_template, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return parse_template( toks, stringize(parse_template) ); -} - -internal -CodeType parse_type( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - - Token context_tok = prevtok; - - SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; - s32 num_specifiers = 0; - - Token name = { nullptr, 0, TokType::Invalid }; - Token brute_sig = { currtok.Text, 0, TokType::Invalid }; - - CodeAttributes attributes = parse_attributes( toks, context ); - - while ( left && tok_is_specifier( currtok ) ) - { - SpecifierT spec = ESpecifier::to_type( currtok ); - - if ( spec != ESpecifier::Const ) - { - log_failure( "gen::parse_type: Error, invalid specifier used in type definition: %s", currtok.Text ); - return CodeInvalid; - } - - specs_found[num_specifiers] = spec; - num_specifiers++; - eat( currtok.Type ); - } - - if ( left == 0 ) - { - log_failure( "%s: Error, unexpected end of type definition", context ); - return CodeInvalid; - } - - if ( currtok.Type == TokType::Decl_Class - || currtok.Type == TokType::Decl_Enum - || currtok.Type == TokType::Decl_Struct ) - { - name = currtok; - eat( currtok.Type ); - - name.Length = ( (sptr)currtok.Text + currtok.Length ) - (sptr)name.Text; - eat( TokType::Identifier ); - } - else if ( currtok.Type >= TokType::Type_Unsigned ) - { - name = currtok; - eat( currtok.Type ); - - while (currtok.Type >= TokType::Type_Unsigned) - { - eat( currtok.Type ); - } - - name.Length = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)name.Text; - } - else - { - name = parse_identifier( toks, context ); - if ( ! name ) - return CodeInvalid; - - // Problably dealing with a templated symbol - if ( currtok.Type == TokType::Operator && currtok.Text[0] == '<' && currtok.Length == 1 ) - { - eat( TokType::Operator ); - - s32 level = 0; - while ( left && ( currtok.Text[0] != '>' || level > 0 )) - { - if ( currtok.Text[0] == '<' ) - level++; - - if ( currtok.Text[0] == '>' ) - level--; - - eat( currtok.Type ); - } - - eat( TokType::Operator ); - - // Extend length of name to last token - name.Length = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)name.Text; - } - } - - while ( left && tok_is_specifier( currtok ) ) - { - SpecifierT spec = ESpecifier::to_type( currtok ); - - if ( spec != ESpecifier::Const - && spec != ESpecifier::Ptr - && spec != ESpecifier::Ref - && spec != ESpecifier::RValue ) - { - log_failure( "%s: Error, invalid specifier used in type definition: %s", context, currtok.Text ); - return CodeInvalid; - } - - specs_found[num_specifiers] = spec; - num_specifiers++; - eat( currtok.Type ); - } - - // Not sure if its technically possible to cast ot a function pointer user defined operator cast... - // Supporting it is not worth the effort. - if ( check( TokType::Capture_Start ) && context_tok.Type != TokType::Decl_Operator ) - { - // Its a function type - eat( TokType::Capture_Start ); - - while ( check( TokType::Star ) || currtok.Type == TokType::Spec_Const ) - { - eat( currtok.Type ); - } - - // if its a using statement there will not be an ID. - if ( check( TokType::Identifier ) ) - eat(TokType::Identifier); - - eat( TokType::Capture_End ); - - // Parameters - - eat( TokType::Capture_Start ); - - // TODO : Change this to validate the parameters... - // Bruteforce lex the parameters, no validation. - s32 level = 0; - while ( ! check( TokType::Capture_End ) || level > 0 ) - { - if ( check( TokType::Capture_Start ) ) - level++; - - if ( check( TokType::Capture_End ) ) - level--; - - eat( currtok.Type ); - } - - eat(TokType::Capture_End); - - brute_sig.Length = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)brute_sig.Text; - } - - using namespace ECode; - - CodeType - result = (CodeType) make_code(); - result->Type = Typename; - - if ( brute_sig.Length > 0 ) - { - // Bruteforce all tokens together. - name = brute_sig; - } - else - { - if (num_specifiers) - { - Code specifiers = def_specifiers( num_specifiers, (SpecifierT*)specs_found ); - result->Specs = specifiers; - } - } - - result->Name = get_cached_string( name ); - - if ( attributes ) - result->Attributes = attributes; - - return result; -} - -CodeType parse_type( StrC def ) -{ - check_parse_args( parse_type, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - CodeType result = parse_type( toks, stringize(parse_type) ); - - return result; -} - -internal -CodeTypedef parse_typedef( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - - Token name = { nullptr, 0, TokType::Invalid }; - Code array_expr = { nullptr }; - Code type = { nullptr }; - - ModuleFlag mflags = ModuleFlag::None; - - if ( check(TokType::Module_Export) ) - { - mflags = ModuleFlag::Export; - eat( TokType::Module_Export ); - } - - eat( TokType::Decl_Typedef ); - - if ( check( TokType::Decl_Enum ) ) - type = parse_enum( toks, context ); - - else if ( check(TokType::Decl_Class ) ) - type = parse_class( toks, context ); - - else if ( check(TokType::Decl_Struct ) ) - type = parse_struct( toks, context ); - - else if ( check(TokType::Decl_Union) ) - type = parse_union( toks, context ); - - else - type = parse_type( toks, context ); - - if ( ! check( TokType::Identifier ) ) - { - log_failure( "gen::parse_typedef: Error, expected identifier for typedef" ); - return CodeInvalid; - } - - name = currtok; - eat( TokType::Identifier ); - - array_expr = parse_array_decl( toks, context ); - - eat( TokType::Statement_End ); - - using namespace ECode; - - CodeTypedef - result = (CodeTypedef) make_code(); - result->Type = Typedef; - result->Name = get_cached_string( name ); - result->ModuleFlags = mflags; - - result->UnderlyingType = type; - - if ( type->Type == Typename && array_expr && array_expr->Type != Invalid ) - type.cast()->ArrExpr = array_expr; - - return result; -} - -CodeTypedef parse_typedef( StrC def ) -{ - check_parse_args( parse_typedef, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return parse_typedef( toks, stringize(parse_typedef) ); -} - -internal -CodeUnion parse_union( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - - ModuleFlag mflags = ModuleFlag::None; - - if ( check(TokType::Module_Export) ) - { - mflags = ModuleFlag::Export; - eat( TokType::Module_Export ); - } - - eat( TokType::Decl_Union ); - - CodeAttributes attributes = parse_attributes( toks, context ); - - StrC name = { 0, nullptr }; - - if ( check( TokType::Identifier ) ) - { - name = currtok; - eat( TokType::Identifier ); - } - - CodeBody body = { nullptr }; - - eat( TokType::BraceCurly_Open ); - - body = make_code(); - body->Type = ECode::Union_Body; - - while ( ! check( TokType::BraceCurly_Close ) ) - { - Code entry = parse_variable( toks, context ); - - if ( entry ) - body.append( entry ); - } - - eat( TokType::BraceCurly_Close ); - eat( TokType::Statement_End ); - - CodeUnion - result = (CodeUnion) make_code(); - result->Type = ECode::Union; - result->ModuleFlags = mflags; - - if ( name ) - result->Name = get_cached_string( name ); - - if ( body ) - result->Body = body; - - if ( attributes ) - result->Attributes = attributes; - - return result; -} - -CodeUnion parse_union( StrC def ) -{ - check_parse_args( parse_union, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return parse_union( toks, stringize(parse_union) ); -} - -internal -CodeUsing parse_using( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - - SpecifierT specs_found[16] { ESpecifier::Invalid }; - s32 num_specifiers = 0; - - Token name = { nullptr, 0, TokType::Invalid }; - Code array_expr = { nullptr }; - CodeType type = { nullptr }; - - bool is_namespace = false; - - ModuleFlag mflags = ModuleFlag::None; - CodeAttributes attributes = { nullptr }; - - if ( check(TokType::Module_Export) ) - { - mflags = ModuleFlag::Export; - eat( TokType::Module_Export ); - } - - eat( TokType::Decl_Using ); - - if ( currtok.Type == TokType::Decl_Namespace ) - { - is_namespace = true; - eat( TokType::Decl_Namespace ); - } - - name = currtok; - eat( TokType::Identifier ); - - if ( currtok.IsAssign ) - { - attributes = parse_attributes( toks, context ); - - eat( TokType::Operator ); - - type = parse_type( toks, context ); - } - - array_expr = parse_array_decl( toks, context ); - - eat( TokType::Statement_End ); - - using namespace ECode; - - CodeUsing - result = (CodeUsing) make_code(); - result->Name = get_cached_string( name ); - result->ModuleFlags = mflags; - - if ( is_namespace) - { - result->Type = Using_Namespace; - } - else - { - result->Type = Using; - - if ( type ) - result->UnderlyingType = type; - - if ( array_expr ) - type->ArrExpr = array_expr; - - if ( attributes ) - result->Attributes = attributes; - } - return result; -} - -CodeUsing parse_using( StrC def ) -{ - check_parse_args( parse_using, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return parse_using( toks, stringize(parse_using) ); -} - -internal -CodeVar parse_variable( Parser::TokArray& toks, char const* context ) -{ - using namespace Parser; - - Token name = { nullptr, 0, TokType::Invalid }; - - SpecifierT specs_found[16] { ESpecifier::Num_Specifiers }; - s32 num_specifiers = 0; - - ModuleFlag mflags = ModuleFlag::None; - CodeAttributes attributes = { nullptr }; - CodeSpecifiers specifiers = { nullptr }; - - if ( check(TokType::Module_Export) ) - { - mflags = ModuleFlag::Export; - eat( TokType::Module_Export ); - } - - attributes = parse_attributes( toks, context ); - - 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: - case ESpecifier::Thread_Local: - case ESpecifier::Volatile: - break; - - default: - log_failure( "gen::parse_variable: invalid specifier %s for variable", ESpecifier::to_str( spec ) ); - return CodeInvalid; - } - - // Ignore const specifiers, they're 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 ); - } - - CodeType type = parse_type( toks, context ); - - if ( type == Code::Invalid ) - return CodeInvalid; - - name = currtok; - eat( TokType::Identifier ); - - CodeVar result = parse_variable_after_name( mflags, attributes, specifiers, type, name, toks, context ); - - return result; -} - -CodeVar parse_variable( StrC def ) -{ - check_parse_args( parse_variable, def ); - using namespace Parser; - - TokArray toks = lex( def ); - if ( toks.Arr == nullptr ) - return CodeInvalid; - - return parse_variable( toks, stringize(parse_variable) ); -} - -// Undef helper macros -# undef check_parse_args -# undef curr_tok -# undef eat -# undef left -#pragma endregion Parsing Constructors - -#pragma region Untyped Constructors -sw token_fmt_va( char* buf, uw buf_size, s32 num_tokens, va_list va ) -{ - char const* buf_begin = buf; - sw remaining = buf_size; - - local_persist - Arena tok_map_arena; - - HashTable tok_map; - { - local_persist - char tok_map_mem[ TokenFmt_TokenMap_MemSize ]; - - tok_map_arena = Arena::init_from_memory( tok_map_mem, sizeof(tok_map_mem) ); - - tok_map = HashTable::init( tok_map_arena ); - - s32 left = num_tokens - 1; - - while ( left-- ) - { - char const* token = va_arg( va, char const* ); - StrC value = va_arg( va, StrC ); - - u32 key = crc32( token, str_len(token) ); - - tok_map.set( key, value ); - } - } - - char const* fmt = va_arg( va, char const* ); - char current = *fmt; - - while ( current ) - { - sw len = 0; - - while ( current && current != '<' && remaining ) - { - * buf = * fmt; - buf++; - fmt++; - remaining--; - - current = * fmt; - } - - if ( current == '<' ) - { - char const* scanner = fmt + 1; - - s32 tok_len = 0; - - while ( *scanner != '>' ) - { - tok_len++; - scanner++; - } - - char const* token = fmt + 1; - - u32 key = crc32( token, tok_len ); - StrC* value = tok_map.get( key ); - - if ( value ) - { - sw left = value->Len; - char const* str = value->Ptr; - - while ( left-- ) - { - * buf = * str; - buf++; - str++; - remaining--; - } - - scanner++; - fmt = scanner; - current = * fmt; - continue; - } - - * buf = * fmt; - buf++; - fmt++; - remaining--; - - current = * fmt; - } - } - - tok_map.clear(); - tok_map_arena.free(); - - sw result = buf_size - remaining; - - return result; -} - -Code untyped_str( StrC content ) -{ - Code - result = make_code(); - result->Name = get_cached_string( content ); - result->Type = ECode::Untyped; - result->Content = result->Name; - - return result; -} - -Code untyped_fmt( char const* fmt, ...) -{ - local_persist thread_local - char buf[GEN_PRINTF_MAXLEN] = { 0 }; - - va_list va; - va_start(va, fmt); - sw length = str_fmt_va(buf, GEN_PRINTF_MAXLEN, fmt, va); - va_end(va); - - Code - result = make_code(); - result->Name = get_cached_string( { str_len(fmt, MaxNameLength), fmt } ); - result->Type = ECode::Untyped; - result->Content = get_cached_string( { length, buf } ); - - return result; -} - -Code untyped_token_fmt( s32 num_tokens, ... ) -{ - local_persist thread_local - char buf[GEN_PRINTF_MAXLEN] = { 0 }; - - va_list va; - va_start(va, num_tokens); - sw length = token_fmt_va(buf, GEN_PRINTF_MAXLEN, num_tokens, va); - va_end(va); - - Code - result = make_code(); - result->Name = get_cached_string( { length, buf } ); - result->Type = ECode::Untyped; - result->Content = result->Name; - - return result; -} -#pragma endregion Untyped Constructors -#pragma endregion Gen Interface - -#pragma region Builder -void Builder::print( Code code ) -{ - Buffer.append_fmt( "%s\n", code->to_string() ); -} - -void Builder::print_fmt( char const* fmt, ... ) -{ - sw res; - char buf[ GEN_PRINTF_MAXLEN ] = { 0 }; - - va_list va; - va_start( va, fmt ); - res = str_fmt_va( buf, count_of( buf ) - 1, fmt, va ) - 1; - va_end( va ); - - Buffer.append( buf, res ); -} - -bool Builder::open( char const* path ) -{ - FileError error = file_open_mode( & File, EFileMode_WRITE, path ); - - if ( error != EFileError_NONE ) - { - log_failure( "gen::File::open - Could not open file: %s", path); - return false; - } - - Buffer = String::make_reserve( GlobalAllocator, Builder_StrBufferReserve ); - - return true; -} - -void Builder::write() -{ - bool result = file_write( & File, Buffer, Buffer.length() ); - - if ( result == false ) - log_failure("gen::File::write - Failed to write to file: %s", file_name( & File ) ); - - file_close( & File ); - Buffer.free(); -} -#pragma endregion Builder +#include "filesystem/gen.builder.cpp" GEN_NS_END #include "helpers/gen.pop_ignores.inline.hpp" + diff --git a/project/gen.hpp b/project/gen.hpp index fcfb072..8d80161 100644 --- a/project/gen.hpp +++ b/project/gen.hpp @@ -30,2080 +30,12 @@ GEN_NS_BEGIN -#pragma region Types -using LogFailType = sw(*)(char const*, ...); +#include "components/gen.types.hpp" +#include "components/gen.data_structures.hpp" +#include "components/gen.interface.hpp" +#include "filesystem/gen.builder.hpp" -// By default this library will either crash or exit if an error is detected while generating codes. -// Even if set to not use fatal, fatal will still be used for memory failures as the library is unusable when they occur. -#ifdef GEN_DONT_USE_FATAL - constexpr LogFailType log_failure = log_fmt; -#else - constexpr LogFailType log_failure = fatal; -#endif - -namespace ECode -{ -# define Define_Types \ - Entry( Untyped ) \ - Entry( Comment ) \ - Entry( Access_Private ) \ - Entry( Access_Protected ) \ - Entry( Access_Public ) \ - Entry( PlatformAttributes ) \ - Entry( Class ) \ - Entry( Class_Fwd ) \ - Entry( Class_Body ) \ - Entry( Enum ) \ - Entry( Enum_Fwd ) \ - Entry( Enum_Body ) \ - Entry( Enum_Class ) \ - Entry( Enum_Class_Fwd ) \ - Entry( Execution ) \ - Entry( Export_Body ) \ - Entry( Extern_Linkage ) \ - Entry( Extern_Linkage_Body ) \ - Entry( Friend ) \ - Entry( Function ) \ - Entry( Function_Fwd ) \ - Entry( Function_Body ) \ - Entry( Global_Body ) \ - Entry( Module ) \ - Entry( Namespace ) \ - Entry( Namespace_Body ) \ - Entry( Operator ) \ - Entry( Operator_Fwd ) \ - Entry( Operator_Member ) \ - Entry( Operator_Member_Fwd ) \ - Entry( Operator_Cast ) \ - Entry( Operator_Cast_Fwd ) \ - Entry( Parameters ) \ - Entry( Preprocessor_Include ) \ - Entry( Specifiers ) \ - Entry( Struct ) \ - Entry( Struct_Fwd ) \ - Entry( Struct_Body ) \ - Entry( Template ) \ - Entry( Typedef ) \ - Entry( Typename ) \ - Entry( Union ) \ - Entry( Union_Body) \ - Entry( Using ) \ - Entry( Using_Namespace ) \ - Entry( Variable ) - - enum Type : u32 - { - # define Entry( Type ) Type, - Define_Types - # undef Entry - - Num_Types, - Invalid - }; - - inline - StrC to_str( Type type ) - { - static - StrC lookup[Num_Types] = { - # define Entry( Type ) { sizeof(stringize(Type)), stringize(Type) }, - Define_Types - # undef Entry - }; - - return lookup[ type ]; - } - -# undef Define_Types -} -using CodeT = ECode::Type; - -// Used to indicate if enum definitoin is an enum class or regular enum. -enum class EnumT : u8 -{ - Regular, - Class -}; - -constexpr EnumT EnumClass = EnumT::Class; -constexpr EnumT EnumRegular = EnumT::Regular; - -namespace EOperator -{ -# define Define_Operators \ - Entry( Assign, = ) \ - Entry( Assign_Add, += ) \ - Entry( Assign_Subtract, -= ) \ - Entry( Assign_Multiply, *= ) \ - Entry( Assign_Divide, /= ) \ - Entry( Assign_Modulo, %= ) \ - Entry( Assign_BAnd, &= ) \ - Entry( Assign_BOr, |= ) \ - Entry( Assign_BXOr, ^= ) \ - Entry( Assign_LShift, <<= ) \ - Entry( Assign_RShift, >>= ) \ - Entry( Increment, ++ ) \ - Entry( Decrement, -- ) \ - Entry( Unary_Plus, + ) \ - Entry( Unary_Minus, - ) \ - Entry( UnaryNot, ! ) \ - Entry( Add, + ) \ - Entry( Subtract, - ) \ - Entry( Multiply, * ) \ - Entry( Divide, / ) \ - Entry( Modulo, % ) \ - Entry( BNot, ~ ) \ - Entry( BAnd, & ) \ - Entry( BOr, | ) \ - Entry( BXOr, ^ ) \ - Entry( LShift, << ) \ - Entry( RShift, >> ) \ - Entry( LAnd, && ) \ - Entry( LOr, || ) \ - Entry( LEqual, == ) \ - Entry( LNot, != ) \ - Entry( Lesser, < ) \ - Entry( Greater, > ) \ - Entry( LesserEqual, <= ) \ - Entry( GreaterEqual, >= ) \ - Entry( Subscript, [] ) \ - Entry( Indirection, * ) \ - Entry( AddressOf, & ) \ - Entry( MemberOfPointer, -> ) \ - Entry( PtrToMemOfPtr, ->* ) \ - Entry( FunctionCall, () ) - - enum Type : u32 - { - # define Entry( Type_, Token_ ) Type_, - Define_Operators - # undef Entry - Comma, - - Num_Ops, - Invalid - }; - - inline - char const* to_str( Type op ) - { - local_persist - char const* lookup[ Num_Ops ] = { - # define Entry( Type_, Token_ ) stringize(Token_), - Define_Operators - # undef Entry - "," - }; - - return lookup[ op ]; - } - -# undef Define_Operators -} -using OperatorT = EOperator::Type; - -namespace ESpecifier -{ -/* - Note: The following are handled separately: - attributes - alignas -*/ - -# define Define_Specifiers \ - Entry( Invalid, INVALID ) \ - Entry( Consteval, consteval ) \ - Entry( Constexpr, constexpr ) \ - Entry( Constinit, constinit ) \ - Entry( Explicit, explicit ) \ - Entry( External_Linkage, extern ) \ - Entry( Global, global ) \ - Entry( Inline, inline ) \ - Entry( Internal_Linkage, internal ) \ - Entry( Local_Persist, local_persist ) \ - Entry( Mutable, mutable ) \ - Entry( Ptr, * ) \ - Entry( Ref, & ) \ - Entry( Register, register ) \ - Entry( RValue, && ) \ - Entry( Static, static ) \ - Entry( Thread_Local, thread_local ) \ - Entry( Volatile, volatile ) \ - Entry( Virtual, virtual ) \ - Entry( Const, const ) \ - Entry( Final, final ) \ - Entry( Override, override ) - - enum Type : u32 - { - # define Entry( Specifier, Code ) Specifier, - Define_Specifiers - # undef Entry - - Num_Specifiers, - }; - - inline - bool is_trailing( Type specifier ) - { - return specifier > Virtual; - } - - // Specifier to string - inline - StrC to_str( Type specifier ) - { - local_persist - StrC lookup[ Num_Specifiers ] = { - # pragma push_macro( "global" ) - # pragma push_macro( "internal" ) - # pragma push_macro( "local_persist" ) - # undef global - # undef internal - # undef local_persist - - # define Entry( Spec_, Code_ ) { sizeof(stringize(Code_)), stringize(Code_) }, - Define_Specifiers - # undef Entry - - # pragma pop_macro( "global" ) - # pragma pop_macro( "internal" ) - # pragma pop_macro( "local_persist" ) - }; - - return lookup[ specifier ]; - } - - inline - Type to_type( StrC str ) - { - local_persist - u32 keymap[ Num_Specifiers ]; - do_once_start - for ( u32 index = 0; index < Num_Specifiers; index++ ) - { - StrC enum_str = to_str( (Type)index ); - - // We subtract 1 to remove the null terminator - // This is because the tokens lexed are not null terminated. - keymap[index] = crc32( enum_str.Ptr, enum_str.Len - 1); - } - do_once_end - - u32 hash = crc32( str.Ptr, str.Len ); - - for ( u32 index = 0; index < Num_Specifiers; index++ ) - { - if ( keymap[index] == hash ) - return (Type)index; - } - - return Invalid; - } - -# undef Define_Specifiers -} -using SpecifierT = ESpecifier::Type; - -enum class AccessSpec : u32 -{ - Default, - Public, - Protected, - Private, - - Num_AccessSpec, - Invalid, -}; - -inline -char const* to_str( AccessSpec type ) -{ - local_persist - char const* lookup[ (u32)AccessSpec::Num_AccessSpec ] = { - "", - "public", - "protected", - "private", - }; - - if ( type > AccessSpec::Public ) - return "Invalid"; - - return lookup[ (u32)type ]; -} - -enum class ModuleFlag : u32 -{ - None = 0, - Export = bit(0), - Import = bit(1), - // Private = bit(2), - - Num_ModuleFlags, - Invalid, -}; - -ModuleFlag operator|( ModuleFlag A, ModuleFlag B) -{ - return (ModuleFlag)( (u32)A | (u32)B ); -} - -/* - Predefined attributes - Used for the parser constructors to identify non-standard attributes - - Override these to change the attribute to your own unique identifier convention. - - The tokenizer identifies attribute defines with the GEN_Define_Attribute_Tokens macros. - See the example below and the Define_TokType macro used in gen.cpp to know the format. - While the library can parse raw attributes, most projects use defines to wrap them for compiler - platform indendence. The token define allows support for them without having to modify the library. -*/ -#if defined(GEN_SYSTEM_WINDOWS) || defined( __CYGWIN__ ) -#ifndef GEN_Attribute_Keyword -# define GEN_API_Export_Code __declspec(dllexport) -# define GEN_API_Import_Code __declspec(dllimport) -# define GEN_Attribute_Keyword __declspec -#endif - -constexpr char const* Attribute_Keyword = stringize( GEN_Attribute_Keyword); - -#elif GEN_HAS_ATTRIBUTE( visibility ) || GEN_GCC_VERSION_CHECK( 3, 3, 0 ) -#ifndef GEN_Attribute_Keyword -# define GEN_API_Export_Code __attribute__ ((visibility ("default"))) -# define GEN_API_Import_Code __attribute__ ((visibility ("default"))) -# define GEN_Attribute_Keyword __attribute__ -#endif - -constexpr char const* Attribute_Keyword = stringize( GEN_Attribute_Keyword ); - -#else -#ifndef GEN_Attribute_Keyword -# define GEN_API_Export_Code -# define GEN_API_Import_Code -# define GEN_Attribute_Keyword -#endif - -constexpr char const* Attribute_Keyword = ""; -#endif - -#ifndef GEN_Define_Attribute_Tokens -# define GEN_Define_Attribute_Tokens \ - Entry( API_Export, "GEN_API_Export_Code" ) \ - Entry( API_Import, "GEN_API_Import_Code" ) -#endif -#pragma endregion Types - -#pragma region Data Structures -// Implements basic string interning. Data structure is based off the ZPL Hashtable. -using StringTable = HashTable; - -// Represents strings cached with the string table. -// Should never be modified, if changed string is desired, cache_string( str ) another. -using StringCached = String const; - -struct AST; -struct AST_Body; -struct AST_Attributes; -struct AST_Comment; -struct AST_Class; -struct AST_Enum; -struct AST_Exec; -struct AST_Extern; -struct AST_Include; -struct AST_Friend; -struct AST_Fn; -struct AST_Module; -struct AST_Namespace; -struct AST_Operator; -struct AST_OpCast; -struct AST_Param; -struct AST_Specifiers; -struct AST_Struct; -struct AST_Template; -struct AST_Type; -struct AST_Typedef; -struct AST_Union; -struct AST_Using; -struct AST_UsingNamespace; -struct AST_Var; - -struct Code; -struct CodeBody; -// These are to offer ease of use and optionally strong type safety for the AST. -struct CodeAttributes; -struct CodeComment; -struct CodeClass; -struct CodeEnum; -struct CodeExec; -struct CodeExtern; -struct CodeInclude; -struct CodeFriend; -struct CodeFn; -struct CodeModule; -struct CodeNamespace; -struct CodeOperator; -struct CodeOpCast; -struct CodeParam; -struct CodeSpecifiers; -struct CodeStruct; -struct CodeTemplate; -struct CodeType; -struct CodeTypedef; -struct CodeUnion; -struct CodeUsing; -struct CodeUsingNamespace; -struct CodeVar; - -/* - AST* wrapper - - Not constantly have to append the '*' as this is written often.. - - Allows for implicit conversion to any of the ASTs (raw or filtered). -*/ -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 - - #define Using_Code( Typename ) \ - char const* debug_str(); \ - Code duplicate(); \ - bool is_equal( Code other ); \ - bool is_valid(); \ - void set_global(); \ - String to_string(); \ - Typename& operator = ( AST* other ); \ - Typename& operator = ( Code other ); \ - bool operator ==( Code other ); \ - bool operator !=( Code other ); \ - operator bool() \ - { \ - return ast != nullptr; \ - } - - template< class Type > - Type cast() - { - return * rcast( Type*, this ); - } - - AST* operator ->() - { - return ast; - } - Code& operator ++(); - Code& operator*() - { - return *this; - } - - Using_Code( Code ); - - AST* ast; - -#ifdef GEN_ENFORCE_STRONG_CODE_TYPES -# define operator explicit operator -#endif - operator CodeAttributes() const; - operator CodeComment() const; - operator CodeClass() const; - operator CodeExec() const; - operator CodeEnum() const; - operator CodeExtern() const; - operator CodeInclude() const; - operator CodeFriend() const; - operator CodeFn() const; - operator CodeModule() const; - operator CodeNamespace() const; - operator CodeOperator() const; - operator CodeOpCast() const; - operator CodeParam() const; - operator CodeSpecifiers() const; - operator CodeStruct() const; - operator CodeTemplate() const; - operator CodeType() const; - operator CodeTypedef() const; - operator CodeUnion() const; - operator CodeUsing() const; - operator CodeUsingNamespace() const; - operator CodeVar() const; - operator CodeBody() const; - #undef operator -}; - -struct Code_POD -{ - AST* ast; -}; - -static_assert( sizeof(Code) == sizeof(Code_POD), "ERROR: Code is not POD" ); - -// Desired width of the AST data structure. -constexpr u32 AST_POD_Size = 128; - -/* - Simple AST POD with functionality to seralize into C++ syntax. -*/ -struct AST -{ -# pragma region Member Functions - void append ( AST* other ); - char const* debug_str (); - AST* duplicate (); - Code& entry ( u32 idx ); - bool has_entries(); - bool is_equal ( AST* other ); - String to_string (); - char const* type_str(); - bool validate_body(); - - template< class Type > - Type cast() - { - return * this; - } - - operator Code(); - operator CodeBody(); - operator CodeAttributes(); - operator CodeComment(); - operator CodeClass(); - operator CodeEnum(); - operator CodeExec(); - operator CodeExtern(); - operator CodeInclude(); - operator CodeFriend(); - operator CodeFn(); - operator CodeModule(); - operator CodeNamespace(); - operator CodeOperator(); - operator CodeOpCast(); - operator CodeParam(); - operator CodeSpecifiers(); - operator CodeStruct(); - operator CodeTemplate(); - operator CodeType(); - operator CodeTypedef(); - operator CodeUnion(); - operator CodeUsing(); - operator CodeUsingNamespace(); - operator CodeVar(); -# pragma endregion Member Functions - - constexpr static - uw ArrSpecs_Cap = - ( - AST_POD_Size - - sizeof(AST*) * 3 - - sizeof(StringCached) - - sizeof(CodeT) - - sizeof(ModuleFlag) - - sizeof(s32) - ) - / sizeof(SpecifierT) -1; // -1 for 4 extra bytes - - union { - struct - { - AST* Attributes; // Class, Enum, Function, Struct, Typedef, Union, Using, Variable - AST* Specs; // Function, Operator, Type symbol, Variable - union { - AST* ParentType; // Class, Struct - AST* ReturnType; // Function, Operator - AST* UnderlyingType; // Enum, Typedef - AST* ValueType; // Parameter, Variable - }; - AST* Params; // Function, Operator, Template - union { - AST* ArrExpr; // Type Symbol - AST* Body; // Class, Enum, Function, Namespace, Struct, Union - AST* Declaration; // Friend, Template - AST* Value; // Parameter, Variable - }; - }; - StringCached Content; // Attributes, Comment, Execution, Include - SpecifierT ArrSpecs[AST::ArrSpecs_Cap]; // Specifiers - }; - union { - AST* Prev; - AST* Front; - AST* Last; - }; - union { - AST* Next; - AST* Back; - }; - AST* Parent; - StringCached Name; - CodeT Type; - ModuleFlag ModuleFlags; - union { - OperatorT Op; - AccessSpec ParentAccess; - s32 NumEntries; - }; -}; - -struct AST_POD -{ - union { - struct - { - AST* Attributes; // Class, Enum, Function, Struct, Typename, Union, Using, Variable - AST* Specs; // Function, Operator, Type symbol, Variable - union { - AST* ParentType; // Class, Struct - AST* ReturnType; // Function, Operator - AST* UnderlyingType; // Enum, Typedef - AST* ValueType; // Parameter, Variable - }; - AST* Params; // Function, Operator, Template - union { - AST* ArrExpr; // Type Symbol - AST* Body; // Class, Enum, Function, Namespace, Struct, Union - AST* Declaration; // Friend, Template - AST* Value; // Parameter, Variable - }; - }; - StringCached Content; // Attributes, Comment, Execution, Include - SpecifierT ArrSpecs[AST::ArrSpecs_Cap]; // Specifiers - }; - union { - AST* Prev; - AST* Front; - AST* Last; - }; - union { - AST* Next; - AST* Back; - }; - AST* Parent; - StringCached Name; - CodeT Type; - ModuleFlag ModuleFlags; - union { - OperatorT Op; - AccessSpec ParentAccess; - s32 NumEntries; - }; -}; - -// Its intended for the AST to have equivalent size to its POD. -// All extra functionality within the AST namespace should just be syntatic sugar. -static_assert( sizeof(AST) == sizeof(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" ); - -// Used when the its desired when omission is allowed in a definition. -#define NoCode { nullptr } -#define CodeInvalid (* Code::Invalid.ast) // Uses an implicitly overloaded cast from the AST to the desired code type. - -#pragma region Code Types -#define Define_CodeType( Typename ) \ -struct Code##Typename \ -{ \ - Using_Code( Code##Typename ); \ - AST* raw() \ - { \ - return rcast( AST*, ast ); \ - } \ - operator Code() \ - { \ - return * rcast( Code*, this ); \ - } \ - AST_##Typename* operator->() \ - { \ - if ( ast == nullptr ) \ - { \ - log_failure("Attempt to dereference a nullptr!"); \ - return nullptr; \ - } \ - return ast; \ - } \ - AST_##Typename* ast; \ -} - -struct CodeBody -{ - Using_Code( CodeBody ); - - void append( Code other ) - { - raw()->append( other.ast ); - } - void append( CodeBody body ) - { - for ( Code entry : body ) - { - append( entry ); - } - } - bool has_entries() - { - return rcast( AST*, ast )->has_entries(); - } - AST* raw() - { - return rcast( AST*, ast ); - } - AST_Body* operator->() - { - return ast; - } - operator Code() - { - return * rcast( Code*, this ); - } -#pragma region Iterator - Code begin() - { - if ( ast ) - return { rcast( AST*, ast)->Front }; - - return { nullptr }; - } - Code end() - { - return { rcast(AST*, ast)->Back->Next }; - } -#pragma endregion Iterator - - AST_Body* ast; -}; - -Define_CodeType( Attributes ); -Define_CodeType( Comment ); -Define_CodeType( Enum ); -Define_CodeType( Exec ); -Define_CodeType( Extern ); -Define_CodeType( Include ); -Define_CodeType( Friend ); -Define_CodeType( Fn ); -Define_CodeType( Module ); -Define_CodeType( Namespace ); -Define_CodeType( Operator ); -Define_CodeType( OpCast ); -Define_CodeType( Template ); -Define_CodeType( Type ); -Define_CodeType( Typedef ); -Define_CodeType( Union ); -Define_CodeType( Using ); -Define_CodeType( Var ); - -struct CodeClass -{ - Using_Code( CodeClass ); - - void add_interface( CodeType interface ); - - AST* raw() - { - return rcast( AST*, ast ); - } - operator Code() - { - return * rcast( Code*, this ); - } - AST_Class* operator->() - { - if ( ast == nullptr ) - { - log_failure("Attempt to dereference a nullptr"); - return nullptr; - } - return ast; - } - AST_Class* ast; -}; - -struct CodeParam -{ - Using_Code( CodeParam ); - - void append( CodeParam other ); - - CodeParam get( s32 idx ); - bool has_entries(); - AST* raw() - { - return rcast( AST*, ast ); - } - AST_Param* operator->() - { - if ( ast == nullptr ) - { - log_failure("Attempt to dereference a nullptr!"); - return nullptr; - } - return ast; - } - operator Code() - { - return { (AST*)ast }; - } -#pragma region Iterator - CodeParam begin() - { - if ( ast ) - return { ast }; - - return { nullptr }; - } - CodeParam end() - { - return { (AST_Param*) rcast( AST*, ast)->Last }; - } - CodeParam& operator++(); - CodeParam operator*() - { - return * this; - } -#pragma endregion Iterator - - AST_Param* ast; -}; - -struct CodeSpecifiers -{ - Using_Code( CodeSpecifiers ); - - bool append( SpecifierT spec ) - { - if ( raw()->NumEntries == AST::ArrSpecs_Cap ) - { - log_failure("CodeSpecifiers: Attempted to append over %d specifiers to a specifiers AST!", AST::ArrSpecs_Cap ); - return false; - } - - raw()->ArrSpecs[ raw()->NumEntries ] = spec; - raw()->NumEntries++; - return true; - } - s32 has( SpecifierT spec ) - { - for ( s32 idx = 0; idx < raw()->NumEntries; idx++ ) - { - if ( raw()->ArrSpecs[ raw()->NumEntries ] == spec ) - return idx; - } - - return -1; - } - AST* raw() - { - return rcast( AST*, ast ); - } - AST_Specifiers* operator->() - { - if ( ast == nullptr ) - { - log_failure("Attempt to dereference a nullptr!"); - return nullptr; - } - return ast; - } - operator Code() - { - return { (AST*) ast }; - } -#pragma region Iterator - SpecifierT* begin() - { - if ( ast ) - return & raw()->ArrSpecs[0]; - - return nullptr; - } - SpecifierT* end() - { - return raw()->ArrSpecs + raw()->NumEntries; - } -#pragma endregion Iterator - - AST_Specifiers* ast; -}; - -struct CodeStruct -{ - Using_Code( CodeStruct ); - - void add_interface( CodeType interface ); - - AST* raw() - { - return rcast( AST*, ast ); - } - operator Code() - { - return * rcast( Code*, this ); - } - AST_Struct* operator->() - { - if ( ast == nullptr ) - { - log_failure("Attempt to dereference a nullptr"); - return nullptr; - } - return ast; - } - AST_Struct* ast; -}; - -#undef Define_CodeType -#undef Using_Code -#pragma endregion Code Types - -#pragma region Filtered ASTs -/* - Show only relevant members of the AST for its type. - AST* fields are replaced with Code types. - - Guards assignemnts to AST* fields to ensure the AST is duplicated if assigned to another parent. -*/ - -struct AST_Body -{ - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - Code Front; - Code Back; - Code Parent; - StringCached Name; - CodeT Type; - char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; - s32 NumEntries; -}; -static_assert( sizeof(AST_Body) == sizeof(AST), "ERROR: AST_Filtered is not the same size as AST"); - -struct AST_Attributes -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - StringCached Content; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; -}; -static_assert( sizeof(AST_Attributes) == sizeof(AST), "ERROR: AST_Attributes is not the same size as AST"); - -struct AST_Comment -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - StringCached Content; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; -}; -static_assert( sizeof(AST_Comment) == sizeof(AST), "ERROR: AST_Comment is not the same size as AST"); - -struct AST_Class -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - CodeAttributes Attributes; - char _PAD_SPECS_ [ sizeof(AST*) ]; - CodeType ParentType; - char _PAD_PARAMS_[ sizeof(AST*) ]; - CodeBody Body; - }; - }; - CodeType Last; - CodeType Next; - Code Parent; - StringCached Name; - CodeT Type; - ModuleFlag ModuleFlags; - AccessSpec ParentAccess; -}; -static_assert( sizeof(AST_Class) == sizeof(AST), "ERROR: AST_Class is not the same size as AST"); - -struct AST_Enum -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - CodeAttributes Attributes; - char _PAD_SPEC_ [ sizeof(AST*) ]; - CodeType UnderlyingType; - char _PAD_PARAMS_[ sizeof(AST*) ]; - CodeBody Body; - }; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - ModuleFlag ModuleFlags; - char _PAD_UNUSED_[ sizeof(u32) ]; -}; -static_assert( sizeof(AST_Enum) == sizeof(AST), "ERROR: AST_Enum is not the same size as AST"); - -struct AST_Exec -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - char _PAD_PROPERTIES_[ sizeof(AST*) * 5 ]; - }; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; -}; -static_assert( sizeof(AST_Exec) == sizeof(AST), "ERROR: AST_Exec is not the same size as AST"); - -struct AST_Extern -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - char _PAD_PROPERTIES_[ sizeof(AST*) * 4 ]; - CodeBody Body; - }; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; -}; -static_assert( sizeof(AST_Extern) == sizeof(AST), "ERROR: AST_Extern is not the same size as AST"); - -struct AST_Include -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - StringCached Content; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; -}; -static_assert( sizeof(AST_Include) == sizeof(AST), "ERROR: AST_Include is not the same size as AST"); - -struct AST_Friend -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - char _PAD_PROPERTIES_[ sizeof(AST*) * 4 ]; - Code Declaration; - }; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; -}; -static_assert( sizeof(AST_Friend) == sizeof(AST), "ERROR: AST_Friend is not the same size as AST"); - -struct AST_Fn -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - CodeAttributes Attributes; - CodeSpecifiers Specs; - CodeType ReturnType; - CodeParam Params; - CodeBody Body; - }; - }; - Code Prev; - Code Parent; - Code Next; - StringCached Name; - CodeT Type; - ModuleFlag ModuleFlags; - char _PAD_UNUSED_[ sizeof(u32) ]; -}; -static_assert( sizeof(AST_Fn) == sizeof(AST), "ERROR: AST_Fn is not the same size as AST"); - -struct AST_Module -{ - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - ModuleFlag ModuleFlags; - char _PAD_UNUSED_[ sizeof(u32) ]; -}; -static_assert( sizeof(AST_Module) == sizeof(AST), "ERROR: AST_Module is not the same size as AST"); - -struct AST_Namespace -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct { - char _PAD_PROPERTIES_[ sizeof(AST*) * 4 ]; - CodeBody Body; - }; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - ModuleFlag ModuleFlags; - char _PAD_UNUSED_[ sizeof(u32) ]; -}; -static_assert( sizeof(AST_Namespace) == sizeof(AST), "ERROR: AST_Namespace is not the same size as AST"); - -struct AST_Operator -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - CodeAttributes Attributes; - CodeSpecifiers Specs; - CodeType ReturnType; - CodeParam Params; - CodeBody Body; - }; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - ModuleFlag ModuleFlags; - OperatorT Op; -}; -static_assert( sizeof(AST_Operator) == sizeof(AST), "ERROR: AST_Operator is not the same size as AST"); - -struct AST_OpCast -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - char _PAD_PROPERTIES_[ sizeof(AST*) ]; - CodeSpecifiers Specs; - CodeType ValueType; - char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; - CodeBody Body; - }; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; -}; -static_assert( sizeof(AST_OpCast) == sizeof(AST), "ERROR: AST_OpCast is not the same size as AST"); - -struct AST_Param -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - char _PAD_PROPERTIES_2_[ sizeof(AST*) * 2 ]; - CodeType ValueType; - char _PAD_PROPERTIES_[ sizeof(AST*) ]; - Code Value; - }; - }; - CodeParam Last; - CodeParam Next; - Code Parent; - StringCached Name; - CodeT Type; - char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; - s32 NumEntries; -}; -static_assert( sizeof(AST_Param) == sizeof(AST), "ERROR: AST_Param is not the same size as AST"); - -struct AST_Specifiers -{ - SpecifierT ArrSpecs[ AST::ArrSpecs_Cap ]; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; - s32 NumEntries; -}; - static_assert( sizeof(AST_Specifiers) == sizeof(AST), "ERROR: AST_Specifier is not the same size as AST"); - -struct AST_Struct -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - CodeAttributes Attributes; - char _PAD_SPECS_ [ sizeof(AST*) ]; - CodeType ParentType; - char _PAD_PARAMS_[ sizeof(AST*) ]; - CodeBody Body; - }; - }; - CodeType Last; - CodeType Next; - Code Parent; - StringCached Name; - CodeT Type; - ModuleFlag ModuleFlags; - AccessSpec ParentAccess; -}; -static_assert( sizeof(AST_Struct) == sizeof(AST), "ERROR: AST_Struct is not the same size as AST"); - -struct AST_Template -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - char _PAD_PROPERTIES_[ sizeof(AST*) * 3 ]; - CodeParam Params; - Code Declaration; - }; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - ModuleFlag ModuleFlags; - char _PAD_UNUSED_[ sizeof(u32) ]; -}; -static_assert( sizeof(AST_Template) == sizeof(AST), "ERROR: AST_Template is not the same size as AST"); - -struct AST_Type -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - CodeAttributes Attributes; - CodeSpecifiers Specs; - char _PAD_PROPERTIES_[ sizeof(AST*) * 2 ]; - Code ArrExpr; - }; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; -}; -static_assert( sizeof(AST_Type) == sizeof(AST), "ERROR: AST_Type is not the same size as AST"); - -struct AST_Typedef -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - char _PAD_PROPERTIES_[ sizeof(AST*) * 2 ]; - Code UnderlyingType; - char _PAD_PROPERTIES_2_[ sizeof(AST*) * 2 ]; - }; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - ModuleFlag ModuleFlags; - char _PAD_UNUSED_[ sizeof(u32) ]; -}; -static_assert( sizeof(AST_Typedef) == sizeof(AST), "ERROR: AST_Typedef is not the same size as AST"); - -struct AST_Union -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - CodeAttributes Attributes; - char _PAD_PROPERTIES_[ sizeof(AST*) * 3 ]; - CodeBody Body; - }; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - ModuleFlag ModuleFlags; - char _PAD_UNUSED_[ sizeof(u32) ]; -}; -static_assert( sizeof(AST_Union) == sizeof(AST), "ERROR: AST_Union is not the same size as AST"); - -struct AST_Using -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - CodeAttributes Attributes; - char _PAD_SPECS_ [ sizeof(AST*) ]; - CodeType UnderlyingType; - char _PAD_PROPERTIES_[ sizeof(AST*) * 2 ]; - }; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - ModuleFlag ModuleFlags; - char _PAD_UNUSED_[ sizeof(u32) ]; -}; -static_assert( sizeof(AST_Using) == sizeof(AST), "ERROR: AST_Using is not the same size as AST"); - -struct AST_Var -{ - union { - char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ]; - struct - { - CodeAttributes Attributes; - CodeSpecifiers Specs; - CodeType ValueType; - char _PAD_PROPERTIES_[ sizeof(AST*) ]; - Code Value; - }; - }; - Code Prev; - Code Next; - Code Parent; - StringCached Name; - CodeT Type; - ModuleFlag ModuleFlags; - char _PAD_UNUSED_[ sizeof(u32) ]; -}; -static_assert( sizeof(AST_Var) == sizeof(AST), "ERROR: AST_Var is not the same size as AST"); -#pragma endregion Filtered ASTs - -#pragma endregion Data Structures - -#pragma region Gen Interface -// Initialize the library. -// This currently just initializes the CodePool. -void init(); - -// Currently manually free's the arenas, code for checking for leaks. -// However on Windows at least, it doesn't need to occur as the OS will clean up after the process. -void deinit(); - -// Clears the allocations, but doesn't return to the heap, the calls init() again. -// Ease of use. -void reset(); - -// Used internally to retrive or make string allocations. -// Strings are stored in a series of string arenas of fixed size (SizePer_StringArena) -StringCached get_cached_string( StrC str ); - -/* - This provides a fresh Code AST. - The gen interface use this as their method from getting a new AST object from the CodePool. - Use this if you want to make your own API for formatting the supported Code Types. -*/ -Code make_code(); - -// Set these before calling gen's init() procedure. -// Data - -void set_allocator_data_arrays ( AllocatorInfo data_array_allocator ); -void set_allocator_code_pool ( AllocatorInfo pool_allocator ); -void set_allocator_lexer ( AllocatorInfo lex_allocator ); -void set_allocator_string_arena ( AllocatorInfo string_allocator ); -void set_allocator_string_table ( AllocatorInfo string_allocator ); -void set_allocator_type_table ( AllocatorInfo type_reg_allocator ); - -#pragma region Upfront -CodeAttributes def_attributes( StrC content ); -CodeComment def_comment ( StrC content ); - -CodeClass def_class( StrC name - , Code body = NoCode - , CodeType parent = NoCode, AccessSpec access = AccessSpec::Default - , CodeAttributes attributes = NoCode - , ModuleFlag mflags = ModuleFlag::None - , CodeType* interfaces = nullptr, s32 num_interfaces = 0 ); - -CodeEnum def_enum( StrC name - , Code body = NoCode, CodeType type = NoCode - , EnumT specifier = EnumRegular, CodeAttributes attributes = NoCode - , ModuleFlag mflags = ModuleFlag::None ); - -CodeExec def_execution ( StrC content ); -CodeExtern def_extern_link( StrC name, Code body ); -CodeFriend def_friend ( Code symbol ); - -CodeFn def_function( StrC name - , CodeParam params = NoCode, CodeType ret_type = NoCode, Code body = NoCode - , CodeSpecifiers specifiers = NoCode, CodeAttributes attributes = NoCode - , ModuleFlag mflags = ModuleFlag::None ); - -CodeInclude def_include ( StrC content ); -CodeModule def_module ( StrC name, ModuleFlag mflags = ModuleFlag::None ); -CodeNamespace def_namespace( StrC name, Code body, ModuleFlag mflags = ModuleFlag::None ); - -CodeOperator def_operator( OperatorT op - , CodeParam params = NoCode, CodeType ret_type = NoCode, Code body = NoCode - , CodeSpecifiers specifiers = NoCode, CodeAttributes attributes = NoCode - , ModuleFlag mflags = ModuleFlag::None ); - -CodeOpCast def_operator_cast( CodeType type, Code body = NoCode, CodeSpecifiers specs = NoCode ); - -CodeParam def_param ( CodeType type, StrC name, Code value = NoCode ); -CodeSpecifiers def_specifier( SpecifierT specifier ); - -CodeStruct def_struct( StrC name - , Code body = NoCode - , CodeType parent = NoCode, AccessSpec access = AccessSpec::Default - , CodeAttributes attributes = NoCode - , ModuleFlag mflags = ModuleFlag::None - , CodeType* interfaces = nullptr, s32 num_interfaces = 0 ); - -CodeTemplate def_template( CodeParam params, Code definition, ModuleFlag mflags = ModuleFlag::None ); - -CodeType def_type ( StrC name, Code arrayexpr = NoCode, CodeSpecifiers specifiers = NoCode, CodeAttributes attributes = NoCode ); -CodeTypedef def_typedef( StrC name, Code type, CodeAttributes attributes = NoCode, ModuleFlag mflags = ModuleFlag::None ); - -CodeUnion def_union( StrC name, Code body, CodeAttributes attributes = NoCode, ModuleFlag mflags = ModuleFlag::None ); - -CodeUsing def_using( StrC name, CodeType type = NoCode - , CodeAttributes attributess = NoCode - , ModuleFlag mflags = ModuleFlag::None ); - -CodeUsing def_using_namespace( StrC name ); - -CodeVar def_variable( CodeType type, StrC name, Code value = NoCode - , CodeSpecifiers specifiers = NoCode, CodeAttributes attributes = NoCode - , ModuleFlag mflags = ModuleFlag::None ); - -// Constructs an empty body. Use AST::validate_body() to check if the body is was has valid entries. -CodeBody def_body( CodeT type ); - -// 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. - -CodeBody def_class_body ( s32 num, ... ); -CodeBody def_class_body ( s32 num, Code* codes ); -CodeBody def_enum_body ( s32 num, ... ); -CodeBody def_enum_body ( s32 num, Code* codes ); -CodeBody def_export_body ( s32 num, ... ); -CodeBody def_export_body ( s32 num, Code* codes); -CodeBody def_extern_link_body( s32 num, ... ); -CodeBody def_extern_link_body( s32 num, Code* codes ); -CodeBody def_function_body ( s32 num, ... ); -CodeBody def_function_body ( s32 num, Code* codes ); -CodeBody def_global_body ( s32 num, ... ); -CodeBody def_global_body ( s32 num, Code* codes ); -CodeBody def_namespace_body ( s32 num, ... ); -CodeBody def_namespace_body ( s32 num, Code* codes ); -CodeParam def_params ( s32 num, ... ); -CodeParam def_params ( s32 num, CodeParam* params ); -CodeSpecifiers def_specifiers ( s32 num, ... ); -CodeSpecifiers def_specifiers ( s32 num, SpecifierT* specs ); -CodeBody def_struct_body ( s32 num, ... ); -CodeBody def_struct_body ( s32 num, Code* codes ); -CodeBody def_union_body ( s32 num, ... ); -CodeBody def_union_body ( s32 num, Code* codes ); -#pragma endregion Upfront - -#pragma region Parsing -CodeClass parse_class ( StrC class_def ); -CodeEnum parse_enum ( StrC enum_def ); -CodeBody parse_export_body ( StrC export_def ); -CodeExtern parse_extern_link ( StrC exten_link_def); -CodeFriend parse_friend ( StrC friend_def ); -CodeFn parse_function ( StrC fn_def ); -CodeBody parse_global_body ( StrC body_def ); -CodeNamespace parse_namespace ( StrC namespace_def ); -CodeOperator parse_operator ( StrC operator_def ); -CodeOpCast parse_operator_cast( StrC operator_def ); -CodeStruct parse_struct ( StrC struct_def ); -CodeTemplate parse_template ( StrC template_def ); -CodeType parse_type ( StrC type_def ); -CodeTypedef parse_typedef ( StrC typedef_def ); -CodeUnion parse_union ( StrC union_def ); -CodeUsing parse_using ( StrC using_def ); -CodeVar parse_variable ( StrC var_def ); -#pragma endregion Parsing - -#pragma region Untyped text -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, ... ); -Code untyped_token_fmt( char const* fmt, s32 num_tokens, ... ); -#pragma endregion Untyped text - -#pragma endregion Gen Interaface - -struct Builder -{ - FileInfo File; - String Buffer; - - void print( Code ); - void print_fmt( char const* fmt, ... ); - - bool open( char const* path ); - void write(); -}; - -#pragma region Inlines -void AST::append( AST* other ) -{ - if ( other->Parent ) - other = other->duplicate(); - - other->Parent = this; - - if ( Front == nullptr ) - { - Front = other; - Back = other; - - NumEntries++; - return; - } - - AST* - Current = Back; - Current->Next = other; - other->Prev = Current; - Back = other; - NumEntries++; -} - -char const* AST::debug_str() -{ - char const* fmt = stringize( - \nCode Debug: - \nType : %s - \nParent : %s - \nName : %s - \nComment : %s - ); - - // These should be used immediately in a log. - // Thus if its desired to keep the debug str - // for multiple calls to bprintf, - // allocate this to proper string. - return str_fmt_buf( fmt - , type_str() - , Parent ? Parent->Name : "" - , Name ? Name : "" - ); -} - -Code& AST::entry( u32 idx ) -{ - AST** current = & Front; - while ( idx >= 0 && current != nullptr ) - { - if ( idx == 0 ) - return * rcast( Code*, current); - - current = & ( * current )->Next; - idx--; - } - - return * rcast( Code*, current); -} - -bool AST::has_entries() -{ - return NumEntries; -} - -char const* AST::type_str() -{ - return ECode::to_str( Type ); -} - -AST::operator Code() -{ - return { this }; -} - -Code& Code::operator ++() -{ - if ( ast ) - ast = ast->Next; - - return *this; -} - -#pragma region AST & Code Gen Common -#define Define_CodeImpl( Typename ) \ -char const* Typename::debug_str() \ -{ \ - if ( ast == nullptr ) \ - return "Code::debug_str: AST is null!"; \ - \ - return rcast(AST*, ast)->debug_str(); \ -} \ -Code Typename::duplicate() \ -{ \ - if ( ast == nullptr ) \ - { \ - log_failure("Code::duplicate: Cannot duplicate code, AST is null!"); \ - return Code::Invalid; \ - } \ - \ - return { rcast(AST*, ast)->duplicate() }; \ -} \ -bool Typename::is_equal( Code other ) \ -{ \ - if ( ast == nullptr || other.ast == nullptr ) \ - { \ - log_failure("Code::is_equal: Cannot compare code, AST is null!"); \ - return false; \ - } \ - \ - return rcast(AST*, ast)->is_equal( other.ast ); \ -} \ -bool Typename::is_valid() \ -{ \ - return (AST*) ast != nullptr && rcast( AST*, ast)->Type != CodeT::Invalid; \ -} \ -void Typename::set_global() \ -{ \ - if ( ast == nullptr ) \ - { \ - log_failure("Code::set_global: Cannot set code as global, AST is null!"); \ - return; \ - } \ - \ - rcast(AST*, ast)->Parent = Code::Global.ast; \ -} \ -String Typename::to_string() \ -{ \ - if ( ast == nullptr ) \ - { \ - log_failure("Code::to_string: Cannot convert code to string, AST is null!"); \ - return { nullptr }; \ - } \ - \ - return rcast(AST*, ast)->to_string(); \ -} \ -Typename& Typename::operator =( Code other ) \ -{ \ - if ( other.ast && other->Parent ) \ - { \ - ast = rcast( decltype(ast), other.ast->duplicate() ); \ - rcast( AST*, ast)->Parent = nullptr; \ - } \ - \ - ast = rcast( decltype(ast), other.ast ); \ - return *this; \ -} \ -bool Typename::operator ==( Code other ) \ -{ \ - return (AST*) ast == other.ast; \ -} \ -bool Typename::operator !=( Code other ) \ -{ \ - return (AST*) ast != other.ast; \ -} - -Define_CodeImpl( Code ); -Define_CodeImpl( CodeBody ); -Define_CodeImpl( CodeAttributes ); -Define_CodeImpl( CodeComment ); -Define_CodeImpl( CodeClass ); -Define_CodeImpl( CodeEnum ); -Define_CodeImpl( CodeExec ); -Define_CodeImpl( CodeExtern ); -Define_CodeImpl( CodeInclude ); -Define_CodeImpl( CodeFriend ); -Define_CodeImpl( CodeFn ); -Define_CodeImpl( CodeModule ); -Define_CodeImpl( CodeNamespace ); -Define_CodeImpl( CodeOperator ); -Define_CodeImpl( CodeOpCast ); -Define_CodeImpl( CodeParam ); -Define_CodeImpl( CodeSpecifiers ); -Define_CodeImpl( CodeStruct ); -Define_CodeImpl( CodeTemplate ); -Define_CodeImpl( CodeType ); -Define_CodeImpl( CodeTypedef ); -Define_CodeImpl( CodeUnion ); -Define_CodeImpl( CodeUsing ); -Define_CodeImpl( CodeVar ); -#undef Define_CodeImpl - -#define Define_AST_Cast( typename ) \ -AST::operator Code ## typename() \ -{ \ - return { rcast( AST_ ## typename*, this ) }; \ -} - -Define_AST_Cast( Body ); -Define_AST_Cast( Attributes ); -Define_AST_Cast( Comment ); -Define_AST_Cast( Class ); -Define_AST_Cast( Enum ); -Define_AST_Cast( Exec ); -Define_AST_Cast( Extern ); -Define_AST_Cast( Include ); -Define_AST_Cast( Friend ); -Define_AST_Cast( Fn ); -Define_AST_Cast( Module ); -Define_AST_Cast( Namespace ); -Define_AST_Cast( Operator ); -Define_AST_Cast( OpCast ); -Define_AST_Cast( Param ); -Define_AST_Cast( Struct ); -Define_AST_Cast( Specifiers ); -Define_AST_Cast( Template ); -Define_AST_Cast( Type ); -Define_AST_Cast( Typedef ); -Define_AST_Cast( Union ); -Define_AST_Cast( Using ); -Define_AST_Cast( Var ); -#undef Define_AST_Cast - -#define Define_CodeCast( type ) \ -Code::operator Code ## type() const \ -{ \ - return { (AST_ ## type*) ast }; \ -} - -Define_CodeCast( Attributes ); -Define_CodeCast( Comment ); -Define_CodeCast( Class ); -Define_CodeCast( Exec ); -Define_CodeCast( Enum ); -Define_CodeCast( Extern ); -Define_CodeCast( Include ); -Define_CodeCast( Friend ); -Define_CodeCast( Fn ); -Define_CodeCast( Module ); -Define_CodeCast( Namespace ); -Define_CodeCast( Operator ); -Define_CodeCast( OpCast ); -Define_CodeCast( Param ); -Define_CodeCast( Specifiers ); -Define_CodeCast( Struct ); -Define_CodeCast( Template ); -Define_CodeCast( Type ); -Define_CodeCast( Typedef ); -Define_CodeCast( Union ); -Define_CodeCast( Using ); -Define_CodeCast( Var ); -Define_CodeCast( Body); -#undef Define_CodeCast -#pragma endregion AST & Code Gen Common - -void CodeClass::add_interface( CodeType type ) -{ - if ( ! ast->Next ) - { - ast->Next = type; - ast->Last = ast->Next; - return; - } - - ast->Next->Next = type; - ast->Last = ast->Next->Next; -} - -void CodeParam::append( CodeParam other ) -{ - AST* self = (AST*) ast; - AST* entry = (AST*) other.ast; - - if ( entry->Parent ) - entry = entry->duplicate(); - - entry->Parent = self; - - if ( self->Last == nullptr ) - { - self->Last = entry; - self->Next = entry; - self->NumEntries++; - return; - } - - self->Last->Next = entry; - self->Last = entry; - self->NumEntries++; -} - -CodeParam CodeParam::get( s32 idx ) -{ - CodeParam param = *this; - do - { - if ( ! ++ param ) - return { nullptr }; - - return { (AST_Param*) param.raw()->Next }; - } - while ( --idx ); - - return { nullptr }; -} - -bool CodeParam::has_entries() -{ - return ast->NumEntries > 0; -} - -CodeParam& CodeParam::operator ++() -{ - ast = ast->Next.ast; - return * this; -} - -void CodeStruct::add_interface( CodeType type ) -{ - if ( ! ast->Next ) - { - ast->Next = type; - ast->Last = ast->Next; - } - - ast->Next->Next = type; - ast->Last = ast->Next->Next; -} - -CodeBody 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 (CodeBody)Code::Invalid; - } - - Code - result = make_code(); - result->Type = type; - return (CodeBody)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. -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 - -#pragma region Constants -#ifdef GEN_DEFINE_LIBRARY_CODE_CONSTANTS - // Predefined typename codes. Are set to readonly and are setup during gen::init() - - extern CodeType t_b32; - - extern CodeType t_s8; - extern CodeType t_s16; - extern CodeType t_s32; - extern CodeType t_s64; - - extern CodeType t_u8; - extern CodeType t_u16; - extern CodeType t_u32; - extern CodeType t_u64; - - extern CodeType t_sw; - extern CodeType t_uw; - - extern CodeType t_f32; - extern CodeType t_f64; -#endif - -#ifndef GEN_GLOBAL_BUCKET_SIZE -# define GEN_GLOBAL_BUCKET_SIZE megabytes(10) -#endif -#ifndef GEN_CODEPOOL_NUM_BLOCKS -# define GEN_CODEPOOL_NUM_BLOCKS kilobytes(64) -#endif -#ifndef GEN_SIZE_PER_STRING_ARENA -# define GEN_SIZE_PER_STRING_ARENA megabytes(1) -#endif -#ifndef GEN_MAX_COMMENT_LINE_LENGTH -# define GEN_MAX_COMMENT_LINE_LENGTH 1024 -#endif -#ifndef GEN_MAX_NAME_LENGTH -# define GEN_MAX_NAME_LENGTH 128 -#endif -#ifndef GEN_MAX_UNTYPED_STR_LENGTH -# define GEN_MAX_UNTYPED_STR_LENGTH kilobytes(640) -#endif -#ifndef GEN_TOKEN_FMT_TOKEN_MAP_MEM_SIZE -# define GEN_TOKEN_FMT_TOKEN_MAP_MEM_SIZE kilobytes(4) -#endif -#ifndef GEN_LEX_ALLOCATOR_SIZE -# define GEN_LEX_ALLOCATOR_SIZE megabytes(10) -#endif -#ifndef GEN_BUILDER_STR_BUFFER_RESERVE -# define GEN_BUILDER_STR_BUFFER_RESERVE megabytes(1) -#endif - - // These constexprs are used for allocation behavior of data structures - // or string handling while constructing or serializing. - // Change them to suit your needs. - - constexpr s32 InitSize_DataArrays = 16; - - // NOTE: This limits the maximum size of an allocation - // If you are generating a string larger than this, increase the size of the bucket here. - constexpr uw Global_BucketSize = GEN_GLOBAL_BUCKET_SIZE; - constexpr s32 CodePool_NumBlocks = GEN_CODEPOOL_NUM_BLOCKS; - constexpr s32 SizePer_StringArena = GEN_SIZE_PER_STRING_ARENA; - - constexpr s32 MaxCommentLineLength = GEN_MAX_COMMENT_LINE_LENGTH; - constexpr s32 MaxNameLength = GEN_MAX_NAME_LENGTH; - constexpr s32 MaxUntypedStrLength = GEN_MAX_UNTYPED_STR_LENGTH; - constexpr s32 TokenFmt_TokenMap_MemSize = GEN_TOKEN_FMT_TOKEN_MAP_MEM_SIZE; - constexpr s32 LexAllocator_Size = GEN_LEX_ALLOCATOR_SIZE; - constexpr s32 Builder_StrBufferReserve = GEN_BUILDER_STR_BUFFER_RESERVE; - - extern CodeType t_empty; // Used with varaidc parameters. (Exposing just in case its useful for another circumstance) - extern CodeType t_auto; - extern CodeType t_void; - extern CodeType t_int; - extern CodeType t_bool; - extern CodeType t_char; - extern CodeType t_wchar_t; - extern CodeType t_class; - extern CodeType t_typename; - - extern CodeParam param_varadic; - - extern CodeAttributes attrib_api_export; - extern CodeAttributes attrib_api_import; - - extern Code access_public; - extern Code access_protected; - extern Code access_private; - - extern Code module_global_fragment; - extern Code module_private_fragment; - - extern Code pragma_once; - - extern CodeSpecifiers spec_const; - extern CodeSpecifiers spec_consteval; - extern CodeSpecifiers spec_constexpr; - extern CodeSpecifiers spec_constinit; - extern CodeSpecifiers spec_extern_linkage; - extern CodeSpecifiers spec_final; - extern CodeSpecifiers spec_global; - extern CodeSpecifiers spec_inline; - extern CodeSpecifiers spec_internal_linkage; - extern CodeSpecifiers spec_local_persist; - extern CodeSpecifiers spec_mutable; - extern CodeSpecifiers spec_override; - extern CodeSpecifiers spec_ptr; - extern CodeSpecifiers spec_ref; - extern CodeSpecifiers spec_register; - extern CodeSpecifiers spec_rvalue; - extern CodeSpecifiers spec_static_member; - extern CodeSpecifiers spec_thread_local; - extern CodeSpecifiers spec_virtual; - extern CodeSpecifiers spec_volatile; -#pragma endregion Constants - -#pragma region Macros -# define gen_main main - -# define __ NoCode - -// Convienence for defining any name used with the gen api. -// Lets you provide the length and string literal to the functions without the need for the DSL. -# define name( Id_ ) { sizeof(stringize( Id_ )) - 1, stringize(Id_) } - -// Same as name just used to indicate intention of literal for code instead of names. -# define code( ... ) { sizeof(stringize(__VA_ARGS__)) - 1, stringize( __VA_ARGS__ ) } - -# define args( ... ) num_args( __VA_ARGS__ ), __VA_ARGS__ - -# define code_str( ... ) gen::untyped_str( code( __VA_ARGS__ ) ) -# define code_fmt( ... ) gen::untyped_str( token_fmt( __VA_ARGS__ ) ) - -// Takes a format string (char const*) and a list of tokens (StrC) and returns a StrC of the formatted string. -# define token_fmt( ... ) gen::token_fmt_impl( (num_args( __VA_ARGS__ ) + 1) / 2, __VA_ARGS__ ) -#pragma endregion Macros - -#ifdef GEN_EXPOSE_BACKEND - // Global allocator used for data with process lifetime. - extern AllocatorInfo GlobalAllocator; - extern Array< Arena > Global_AllocatorBuckets; - extern Array< Pool > CodePools; - extern Array< Arena > StringArenas; - - extern StringTable StringCache; - - extern Arena LexArena; - - extern AllocatorInfo Allocator_DataArrays; - extern AllocatorInfo Allocator_CodePool; - extern AllocatorInfo Allocator_Lexer; - extern AllocatorInfo Allocator_StringArena; - extern AllocatorInfo Allocator_StringTable; - extern AllocatorInfo Allocator_TypeTable; -#endif +#include "components/gen.header_end.hpp" GEN_NS_END