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