gencpp/project/components/ast.cpp

1710 lines
41 KiB
C++
Raw Permalink Normal View History

#ifdef GEN_INTELLISENSE_DIRECTIVES
#pragma once
#include "static_data.cpp"
#endif
Code Code::Global;
Code Code::Invalid;
char const* AST::debug_str()
{
if ( Parent )
{
String
result = String::make_reserve( GlobalAllocator, kilobytes(1) );
result.append_fmt(
"\n\tType : %s"
"\n\tParent : %s %s"
"\n\tName : %s"
, type_str()
, Parent->type_str()
, Parent->Name, Name ? Name : ""
);
return result;
}
String
result = String::make_reserve( GlobalAllocator, kilobytes(1) );
result.append_fmt(
"\n\tType : %s"
"\n\tName : %s"
, type_str()
, Name ? Name : ""
);
return result;
}
AST* AST::duplicate()
{
using namespace ECode;
AST* result = make_code().ast;
mem_copy( result, this, sizeof( AST ) );
result->Parent = nullptr;
return result;
}
String AST::to_string()
{
local_persist thread_local
char SerializationLevel = 0;
// 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 NewLine:
result.append("\n");
break;
case Untyped:
case Execution:
case Comment:
result.append( Content );
break;
case Access_Private:
case Access_Protected:
case Access_Public:
result.append( Name );
break;
case PlatformAttributes:
result.append( Content );
case Class:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
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", Name, access_level, ParentType );
CodeType interface = ParentType->Next->cast< CodeType >();
if ( interface )
result.append( "\n" );
while ( interface )
{
result.append_fmt( ", %S", interface.to_string() );
interface = interface->Next ? interface->Next->cast< CodeType >() : Code { nullptr };
}
result.append_fmt( "\n{\n%S\n}", Body->to_string() );
}
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() );
}
if ( Parent == nullptr || ( Parent->Type != ECode::Typedef && Parent->Type != ECode::Variable ) )
result.append(";\n");
}
break;
case Class_Fwd:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
if ( Attributes )
result.append_fmt( "class %S %S", Attributes->to_string(), Name );
else result.append_fmt( "class %S", Name );
2023-08-22 23:17:47 -07:00
// Check if it can have an end-statement
if ( Parent == nullptr || ( Parent->Type != ECode::Typedef && Parent->Type != ECode::Variable ) )
{
if ( InlineCmt )
result.append_fmt( "; // %S\n", InlineCmt->Content );
else
result.append(";\n");
}
}
break;
case Constructor:
{
result.append( Parent->Name );
if ( Params )
result.append_fmt( "( %S )", Params->to_string() );
else
result.append( "(void)" );
if ( InitializerList )
result.append_fmt( " : %S", InitializerList->to_string() );
result.append_fmt( "\n{\n%S\n}\n", Body->to_string() );
}
break;
case Constructor_Fwd:
{
result.append( Parent->Name );
if ( Params )
result.append_fmt( "( %S )", Params->to_string() );
else
{
if ( InlineCmt )
result.append_fmt( "(void); // %S\n", InlineCmt->Content );
else
result.append( "(void);\n" );
}
}
break;
case Destructor:
{
if ( Specs )
{
CodeSpecifiers specs = Specs->cast<CodeSpecifiers>();
if ( specs.has( ESpecifier::Virtual ) )
result.append_fmt( "virtual ~%S()", Parent->Name );
else
result.append_fmt( "~%S()", Parent->Name );
}
else
result.append_fmt( "~%S()", Parent->Name );
result.append_fmt( "\n{\n%S\n}\n", Body->to_string() );
}
break;
case Destructor_Fwd:
{
if ( Specs )
{
CodeSpecifiers specs = Specs->cast<CodeSpecifiers>();
if ( specs.has( ESpecifier::Virtual ) )
result.append_fmt( "virtual ~%S();\n", Parent->Name );
else
result.append_fmt( "~%S()", Parent->Name );
if ( specs.has( ESpecifier::Pure ) )
result.append( " = 0;" );
}
else
result.append_fmt( "~%S();", Parent->Name );
2023-08-22 23:17:47 -07:00
if ( InlineCmt )
result.append_fmt( " %S", InlineCmt->Content );
else
result.append("\n");
}
break;
case Enum:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
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() );
if ( Parent == nullptr || ( Parent->Type != ECode::Typedef && Parent->Type != ECode::Variable ) )
result.append(";\n");
}
break;
case Enum_Fwd:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
if ( Attributes )
result.append_fmt( "%S ", Attributes->to_string() );
result.append_fmt( "enum %S : %S", Name, UnderlyingType->to_string() );
if ( Parent == nullptr || ( Parent->Type != ECode::Typedef && Parent->Type != ECode::Variable ) )
{
if ( InlineCmt )
result.append_fmt("; %S", InlineCmt->Content );
else
result.append(";\n");
}
}
break;
case Enum_Class:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
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() );
}
if ( Parent == nullptr || ( Parent->Type != ECode::Typedef && Parent->Type != ECode::Variable ) )
result.append(";\n");
}
break;
case Enum_Class_Fwd:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
result.append( "enum class " );
if ( Attributes )
result.append_fmt( "%S ", Attributes->to_string() );
result.append_fmt( "%S : %S", Name, UnderlyingType->to_string() );
if ( Parent == nullptr || ( Parent->Type != ECode::Typedef && Parent->Type != ECode::Variable ) )
{
if ( InlineCmt )
result.append_fmt("; %S", InlineCmt->Content );
else
result.append(";\n");
}
}
break;
case Export_Body:
{
result.append_fmt( "export\n{\n" );
Code curr = { this };
s32 left = NumEntries;
while ( left-- )
{
result.append_fmt( "%S", curr.to_string() );
++curr;
}
result.append_fmt( "};\n" );
}
break;
case Extern_Linkage:
result.append_fmt( "extern \"%S\"\n{\n%S\n}\n", Name, Body->to_string() );
break;
case Friend:
result.append_fmt( "friend %S", Declaration->to_string() );
if ( result[ result.length() -1 ] != ';' )
{
result.append( ";" );
}
2023-08-22 23:17:47 -07:00
if ( InlineCmt )
result.append_fmt(" %S", InlineCmt->Content );
else
result.append("\n");
break;
case Function:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
if ( Attributes )
result.append_fmt( "%S ", Attributes->to_string() );
if ( Specs )
result.append_fmt( "%S", 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( ")" );
if ( Specs )
{
for ( SpecifierT spec : Specs->cast<CodeSpecifiers>() )
{
if ( ESpecifier::is_trailing( spec ) )
{
StrC spec_str = ESpecifier::to_str( spec );
result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr );
}
}
}
result.append_fmt( "\n{\n%S\n}\n", Body->to_string() );
}
break;
case Function_Fwd:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
if ( Attributes )
result.append_fmt( "%S ", Attributes->to_string() );
if ( Specs )
result.append_fmt( "%S", 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( ")" );
if ( Specs )
{
for ( SpecifierT spec : Specs->cast<CodeSpecifiers>() )
{
if ( ESpecifier::is_trailing( spec ) )
{
StrC spec_str = ESpecifier::to_str( spec );
result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr );
}
}
}
if ( InlineCmt )
result.append_fmt( "; %S", InlineCmt->Content );
else
result.append( ";\n" );
}
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;\n", Name );
break;
case Namespace:
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
result.append_fmt( "namespace %S\n{\n%S\n}\n", Name , Body->to_string() );
break;
case Operator:
case Operator_Member:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
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( ")" );
if ( Specs )
{
for ( SpecifierT spec : Specs->cast<CodeSpecifiers>() )
{
if ( ESpecifier::is_trailing( spec ) )
{
StrC spec_str = ESpecifier::to_str( spec );
result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr );
}
}
}
result.append_fmt( "\n{\n%S\n}\n"
, Body->to_string()
);
}
break;
case Operator_Fwd:
case Operator_Member_Fwd:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
if ( Attributes )
result.append_fmt( "%S\n", Attributes->to_string() );
if ( Specs )
result.append_fmt( "%S\n", 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( ")" );
if ( Specs )
{
for ( SpecifierT spec : Specs->cast<CodeSpecifiers>() )
{
if ( ESpecifier::is_trailing( spec ) )
{
StrC spec_str = ESpecifier::to_str( spec );
result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr );
}
}
}
2023-08-22 23:17:47 -07:00
if ( InlineCmt )
result.append_fmt( "; %S", InlineCmt->Content );
else
result.append( ";\n" );
}
break;
case Operator_Cast:
{
if ( Specs )
{
// TODO : Add support for specifies before the operator keyword
2023-08-22 23:17:47 -07:00
if ( Name && Name.length() )
result.append_fmt( "%Soperator %S()", Name, ValueType->to_string() );
else
result.append_fmt( "operator %S()", ValueType->to_string() );
for ( SpecifierT spec : Specs->cast<CodeSpecifiers>() )
{
if ( ESpecifier::is_trailing( spec ) )
{
StrC spec_str = ESpecifier::to_str( spec );
result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr );
}
}
result.append_fmt( "\n{\n%S\n}\n", Body->to_string() );
break;
}
if ( Name && Name.length() )
result.append_fmt("%Soperator %S()\n{\n%S\n}\n", Name, ValueType->to_string(), Body->to_string() );
else
result.append_fmt("operator %S()\n{\n%S\n}\n", ValueType->to_string(), Body->to_string() );
}
break;
case Operator_Cast_Fwd:
if ( Specs )
{
// TODO : Add support for specifies before the operator keyword
2023-08-22 23:17:47 -07:00
result.append_fmt( "operator %S()", ValueType->to_string() );
for ( SpecifierT spec : Specs->cast<CodeSpecifiers>() )
{
if ( ESpecifier::is_trailing( spec ) )
{
StrC spec_str = ESpecifier::to_str( spec );
result.append_fmt( " %*s", spec_str.Len, spec_str.Ptr );
}
}
if ( InlineCmt )
result.append_fmt( "; %S", InlineCmt->Content );
else
result.append( ";\n" );
break;
}
if ( InlineCmt )
result.append_fmt("operator %S(); %S", ValueType->to_string() );
else
result.append_fmt("operator %S();\n", ValueType->to_string() );
break;
case Parameters:
{
if ( ValueType == nullptr )
{
result.append_fmt( "%S", Name );
break;
}
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 Preprocess_Define:
result.append_fmt( "#define %S %S\n", Name, Content );
break;
case Preprocess_If:
result.append_fmt( "#if %S\n", Content );
break;
case Preprocess_IfDef:
result.append_fmt( "#ifdef %S\n", Content );
break;
case Preprocess_IfNotDef:
result.append_fmt( "#ifndef %S\n", Content );
break;
case Preprocess_Include:
result.append_fmt( "#include %S\n", Content );
break;
case Preprocess_ElIf:
result.append_fmt( "#elif %S\n", Content );
break;
case Preprocess_Else:
result.append_fmt( "#else\n" );
break;
case Preprocess_EndIf:
result.append_fmt( "#endif\n" );
break;
case Preprocess_Pragma:
result.append_fmt( "#pragma %S\n", Content );
break;
case Specifiers:
{
s32 idx = 0;
s32 left = NumEntries;
while ( left-- )
{
if ( ESpecifier::is_trailing( ArrSpecs[idx]) && ArrSpecs[idx] != ESpecifier::Const )
{
idx++;
continue;
}
StrC spec = ESpecifier::to_str( ArrSpecs[idx] );
result.append_fmt( "%.*s ", spec.Len, spec.Ptr );
idx++;
}
}
break;
case Struct:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
if ( Name == nullptr)
{
result.append_fmt( "struct\n{\n%S\n};\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", Name, access_level, ParentType );
CodeType interface = ParentType->Next->cast< CodeType >();
if ( interface )
result.append( "\n" );
while ( interface )
{
result.append_fmt( ", %S", interface.to_string() );
interface = interface->Next ? interface->Next->cast< CodeType >() : Code { nullptr };
}
result.append_fmt( "\n{\n%S\n}", Body->to_string() );
}
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() );
}
if ( Parent == nullptr || ( Parent->Type != ECode::Typedef && Parent->Type != ECode::Variable ) )
{
if ( InlineCmt )
result.append_fmt("; %S", InlineCmt->Content );
else
result.append(";\n");
}
}
break;
case Struct_Fwd:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
if ( Attributes )
result.append_fmt( "struct %S %S", Attributes->to_string(), Name );
else result.append_fmt( "struct %S", Name );
if ( Parent == nullptr || ( Parent->Type != ECode::Typedef && Parent->Type != ECode::Variable ) )
{
if ( InlineCmt )
result.append_fmt("; %S", InlineCmt->Content );
else
result.append(";\n");
}
}
break;
case Template:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
result.append_fmt( "template< %S >\n%S", Params->to_string(), Declaration->to_string() );
}
break;
case Typedef:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
result.append( "typedef ");
// Determines if the typedef is a function typename
if ( UnderlyingType->ReturnType )
result.append( UnderlyingType->to_string() );
else
result.append_fmt( "%S %S", UnderlyingType->to_string(), Name );
if ( UnderlyingType->Type == Typename && UnderlyingType->ArrExpr )
{
result.append_fmt( "[ %S ];", UnderlyingType->ArrExpr->to_string() );
AST* next_arr_expr = UnderlyingType->ArrExpr->Next;
while ( next_arr_expr )
{
result.append_fmt( "[ %S ];", next_arr_expr->to_string() );
next_arr_expr = next_arr_expr->Next;
}
}
else
{
result.append( ";" );
}
2023-08-22 23:17:47 -07:00
if ( InlineCmt )
result.append_fmt(" %S", InlineCmt->Content);
else
result.append("\n");
}
break;
case Typename:
{
#if GEN_USE_NEW_TYPENAME_PARSING
if ( ReturnType && Params )
{
if ( Attributes )
result.append_fmt( "%S ", Attributes->to_string() );
else
{
if ( Specs )
result.append_fmt( "%S ( %S ) ( %S ) %S", ReturnType->to_string(), Name, Params->to_string(), Specs->to_string() );
else
result.append_fmt( "%S ( %S ) ( %S )", ReturnType->to_string(), Name, Params->to_string() );
}
break;
}
#else
if ( ReturnType && Params )
{
if ( Attributes )
result.append_fmt( "%S ", Attributes->to_string() );
else
{
if ( Specs )
result.append_fmt( "%S %S ( %S ) %S", ReturnType->to_string(), Name, Params->to_string(), Specs->to_string() );
else
result.append_fmt( "%S %S ( %S )", ReturnType->to_string(), Name, Params->to_string() );
}
break;
}
#endif
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 );
if ( IsParamPack )
result.append("...");
}
break;
case Union:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
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()
);
}
if ( Parent == nullptr || ( Parent->Type != ECode::Typedef && Parent->Type != ECode::Variable ) )
result.append(";\n");
}
break;
case Using:
{
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
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() );
AST* next_arr_expr = UnderlyingType->ArrExpr->Next;
while ( next_arr_expr )
{
result.append_fmt( "[ %S ]", next_arr_expr->to_string() );
next_arr_expr = next_arr_expr->Next;
}
}
result.append( ";" );
}
else
result.append_fmt( "using %S;", Name );
2023-08-22 23:17:47 -07:00
if ( InlineCmt )
result.append_fmt(" %S\n", InlineCmt->Content );
else
result.append("\n");
}
break;
case Using_Namespace:
if ( InlineCmt )
result.append_fmt( "using namespace $S; %S", Name, InlineCmt->Content );
else
result.append_fmt( "using namespace %s;\n", Name );
break;
case Variable:
{
if ( Parent && Parent->Type == Variable )
{
// Its a comma-separated variable ( a NextVar )
if ( Specs )
result.append_fmt( "%S ", Specs->to_string() );
result.append( Name );
if ( ArrExpr )
{
result.append_fmt( "[ %S ]", ArrExpr->to_string() );
AST* next_arr_expr = ArrExpr->Next;
while ( next_arr_expr )
{
result.append_fmt( "[ %S ]", next_arr_expr->to_string() );
next_arr_expr = next_arr_expr->Next;
}
}
if ( Value )
result.append_fmt( " = %S", Value->to_string() );
// Keep the chain going...
if ( NextVar )
result.append_fmt( ", %S", NextVar->to_string() );
}
if ( bitfield_is_equal( u32, ModuleFlags, ModuleFlag::Export ))
result.append( "export " );
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() );
AST* next_arr_expr = ValueType->ArrExpr->Next;
while ( next_arr_expr )
{
result.append_fmt( "[ %S ]", next_arr_expr->to_string() );
next_arr_expr = next_arr_expr->Next;
}
}
if ( BitfieldSize )
result.append_fmt( " : %S", BitfieldSize->to_string() );
if ( Value )
result.append_fmt( " = %S", Value->to_string() );
if ( NextVar )
result.append_fmt( ", %S", NextVar->to_string() );
if ( InlineCmt )
result.append_fmt("; %S", InlineCmt->Content);
else
result.append( ";\n" );
break;
}
if ( BitfieldSize )
result.append_fmt( "%S %S : %S", ValueType->to_string(), Name, BitfieldSize->to_string() );
else if ( ValueType->ArrExpr )
{
result.append_fmt( "%S %S[ %S ]", ValueType->to_string(), Name, ValueType->ArrExpr->to_string() );
AST* next_arr_expr = ValueType->ArrExpr->Next;
while ( next_arr_expr )
{
result.append_fmt( "[ %S ]", next_arr_expr->to_string() );
next_arr_expr = next_arr_expr->Next;
}
}
else
result.append_fmt( "%S %S", ValueType->to_string(), Name );
if ( Value )
result.append_fmt( " = %S", Value->to_string() );
if ( NextVar )
result.append_fmt( ", %S", NextVar->to_string() );
result.append( ";" );
2023-08-22 23:17:47 -07:00
if ( InlineCmt )
result.append_fmt(" %S", InlineCmt->Content);
else
result.append("\n");
}
break;
case Enum_Body:
case Class_Body:
case Extern_Linkage_Body:
case Function_Body:
case Global_Body:
case Namespace_Body:
case Struct_Body:
case Union_Body:
{
Code curr = Front->cast<Code>();
s32 left = NumEntries;
while ( left -- )
{
result.append_fmt( "%S", curr.to_string() );
++curr;
}
}
break;
}
return result;
}
bool AST::is_equal( AST* other )
{
/*
AST values are either some u32 value, a cached string, or a pointer to another AST.
u32 values are compared by value.
Cached strings are compared by pointer.
AST nodes are compared with AST::is_equal.
*/
if ( other == nullptr )
{
log_fmt( "AST::is_equal: other is null\nAST: %S", debug_str() );
return false;
}
if ( Type != other->Type )
{
log_fmt("AST::is_equal: Type check failure with other\nAST: %S\nOther: %S"
, debug_str()
, other->debug_str()
);
return false;
}
switch ( Type )
{
using namespace ECode;
#define check_member_val( val ) \
if ( val != other->val ) \
{ \
log_fmt("\nAST::is_equal: Member - " #val " failed\n" \
"AST : %S\n" \
"Other: %S\n" \
, debug_str() \
, other->debug_str() \
); \
\
return false; \
}
#define check_member_str( str ) \
if ( str != other->str ) \
{ \
log_fmt("\nAST::is_equal: Member string - "#str " failed\n" \
"AST : %S\n" \
"Other: %S\n" \
, debug_str() \
, other->debug_str() \
); \
\
return false; \
}
#define check_member_content( content ) \
if ( content != other->content ) \
{ \
log_fmt("\nAST::is_equal: Member content - "#content " failed\n" \
"AST : %S\n" \
"Other: %S\n" \
, debug_str() \
, other->debug_str() \
); \
\
log_fmt("Content cannot be trusted to be unique with this check " \
"so it must be verified by eye for now\n" \
"AST Content:\n%S\n" \
"Other Content:\n%S\n" \
, content.visualize_whitespace() \
, other->content.visualize_whitespace() \
); \
}
#define check_member_ast( ast ) \
if ( ast ) \
{ \
if ( other->ast == nullptr ) \
{ \
log_fmt("\nAST::is_equal: Failed for member " #ast " other equivalent param is null\n" \
"AST : %s\n" \
"Other: %s\n" \
"For ast member: %s\n" \
, debug_str() \
, other->debug_str() \
, ast->debug_str() \
); \
\
return false; \
} \
\
if ( ! ast->is_equal( other->ast ) ) \
{ \
log_fmt( "\nAST::is_equal: Failed for " #ast"\n" \
"AST : %S\n" \
"Other: %S\n" \
"For ast member: %S\n" \
"other's ast member: %S\n" \
, debug_str() \
, other->debug_str() \
, ast->debug_str() \
, other->ast->debug_str() \
); \
\
return false; \
} \
}
case NewLine:
case Access_Public:
case Access_Protected:
case Access_Private:
case Preprocess_Else:
case Preprocess_EndIf:
return true;
// Comments are not validated.
case Comment:
return true;
case Execution:
case PlatformAttributes:
case Untyped:
{
check_member_content( Content );
return true;
}
case Class_Fwd:
case Struct_Fwd:
{
check_member_str( Name );
check_member_ast( ParentType );
check_member_val( ParentAccess );
check_member_ast( Attributes );
return true;
}
case Class:
case Struct:
{
check_member_val( ModuleFlags );
check_member_str( Name );
check_member_ast( ParentType );
check_member_val( ParentAccess );
check_member_ast( Attributes );
check_member_ast( Body );
return true;
}
case Constructor:
{
check_member_ast( InitializerList );
check_member_ast( Params );
check_member_ast( Body );
return true;
}
case Constructor_Fwd:
{
check_member_ast( InitializerList );
check_member_ast( Params );
return true;
}
case Destructor:
{
check_member_ast( Specs );
check_member_ast( Body );
return true;
}
case Destructor_Fwd:
{
check_member_ast( Specs );
return true;
}
case Enum:
case Enum_Class:
{
check_member_val( ModuleFlags );
check_member_str( Name );
check_member_ast( Attributes );
check_member_ast( UnderlyingType );
check_member_ast( Body );
return true;
}
case Enum_Fwd:
case Enum_Class_Fwd:
{
check_member_val( ModuleFlags );
check_member_str( Name );
check_member_ast( Attributes );
check_member_ast( UnderlyingType );
return true;
}
case Extern_Linkage:
{
check_member_str( Name );
check_member_ast( Body );
return true;
}
case Friend:
{
check_member_str( Name );
check_member_ast( Declaration );
return true;
}
case Function:
{
check_member_val( ModuleFlags );
check_member_str( Name );
check_member_ast( ReturnType );
check_member_ast( Attributes );
check_member_ast( Specs );
check_member_ast( Params );
check_member_ast( Body );
return true;
}
case Function_Fwd:
{
check_member_val( ModuleFlags );
check_member_str( Name );
check_member_ast( ReturnType );
check_member_ast( Attributes );
check_member_ast( Specs );
check_member_ast( Params );
return true;
}
case Module:
{
check_member_val( ModuleFlags );
check_member_str( Name );
return true;
}
case Namespace:
{
check_member_val( ModuleFlags );
check_member_str( Name );
check_member_ast( Body );
return true;
}
case Operator:
case Operator_Member:
{
check_member_val( ModuleFlags );
check_member_str( Name );
check_member_ast( ReturnType );
check_member_ast( Attributes );
check_member_ast( Specs );
check_member_ast( Params );
check_member_ast( Body );
return true;
}
case Operator_Fwd:
case Operator_Member_Fwd:
{
check_member_val( ModuleFlags );
check_member_str( Name );
check_member_ast( ReturnType );
check_member_ast( Attributes );
check_member_ast( Specs );
check_member_ast( Params );
return true;
}
case Operator_Cast:
{
check_member_str( Name );
check_member_ast( Specs );
check_member_ast( ValueType );
check_member_ast( Body );
return true;
}
case Operator_Cast_Fwd:
{
check_member_str( Name );
check_member_ast( Specs );
check_member_ast( ValueType );
return true;
}
case Parameters:
{
if ( NumEntries > 1 )
{
AST* curr = this;
AST* curr_other = other;
while ( curr != nullptr )
{
if ( curr )
{
if ( curr_other == nullptr )
{
log_fmt("\nAST::is_equal: Failed for parameter, other equivalent param is null\n"
"AST : %S\n"
"Other: %S\n"
"For ast member: %S\n"
, curr->debug_str()
);
return false;
}
if ( curr->Name != curr_other->Name )
{
log_fmt( "\nAST::is_equal: Failed for parameter name check\n"
"AST : %S\n"
"Other: %S\n"
"For ast member: %S\n"
"other's ast member: %S\n"
, debug_str()
, other->debug_str()
, curr->debug_str()
, curr_other->debug_str()
);
return false;
}
if ( curr->ValueType && ! curr->ValueType->is_equal(curr_other->ValueType) )
{
log_fmt( "\nAST::is_equal: Failed for parameter value type check\n"
"AST : %S\n"
"Other: %S\n"
"For ast member: %S\n"
"other's ast member: %S\n"
, debug_str()
, other->debug_str()
, curr->debug_str()
, curr_other->debug_str()
);
return false;
}
if ( curr->Value && ! curr->Value->is_equal(curr_other->Value) )
{
log_fmt( "\nAST::is_equal: Failed for parameter value check\n"
"AST : %S\n"
"Other: %S\n"
"For ast member: %S\n"
"other's ast member: %S\n"
, debug_str()
, other->debug_str()
, curr->debug_str()
, curr_other->debug_str()
);
Work on AST::is_equal. The NumEntries checks need to be deferred until the end as a final unresolved check on valdiation. As if there really is a discrepancy of entires it should be revealed by the specific entry failing. Right now the latest failure with the single header check involves a define directive specifically the define does omit whitespace properly and so the check interprets the different cached content to be non-equivalent. This will happen with all unvalidated aspects of the AST ( expressions, function bodies, etc ) There are two ways to resolve, either make an AST that can tokenize all items (not realistic), or I need to strip non-syntax important whitespace and then cache the string. This would mean removing everything but a single whitespace for all content within a content string. Otherwise, I would have to somehow make sure the content of the string has the exact formatting between both files for the definitions that matter. AST types with this issue: * Define Directive * Pragma Directive * Comment * Execution * Platform Attributes * Untyped Comments can technically be left unverified as they do not matter semantically. When the serialization is first emitted, the content these strings should for the most part be equivalent. However I do see some possible failures for that if a different style of bracket placment is used (between the serialization). At that point what I could do is just leave those unverified and just emit the content to the user as warning that the ast and the other compared could not be verified. Those technically can be handled on a per-eye basis, and worst case the tests with the compiler will in the determine if any critical defintions are missing for the user.
2023-08-25 15:40:13 -07:00
return false;
}
}
curr = curr->Next;
curr_other = curr_other->Next;
}
Work on AST::is_equal. The NumEntries checks need to be deferred until the end as a final unresolved check on valdiation. As if there really is a discrepancy of entires it should be revealed by the specific entry failing. Right now the latest failure with the single header check involves a define directive specifically the define does omit whitespace properly and so the check interprets the different cached content to be non-equivalent. This will happen with all unvalidated aspects of the AST ( expressions, function bodies, etc ) There are two ways to resolve, either make an AST that can tokenize all items (not realistic), or I need to strip non-syntax important whitespace and then cache the string. This would mean removing everything but a single whitespace for all content within a content string. Otherwise, I would have to somehow make sure the content of the string has the exact formatting between both files for the definitions that matter. AST types with this issue: * Define Directive * Pragma Directive * Comment * Execution * Platform Attributes * Untyped Comments can technically be left unverified as they do not matter semantically. When the serialization is first emitted, the content these strings should for the most part be equivalent. However I do see some possible failures for that if a different style of bracket placment is used (between the serialization). At that point what I could do is just leave those unverified and just emit the content to the user as warning that the ast and the other compared could not be verified. Those technically can be handled on a per-eye basis, and worst case the tests with the compiler will in the determine if any critical defintions are missing for the user.
2023-08-25 15:40:13 -07:00
check_member_val( NumEntries );
return true;
}
check_member_str( Name );
check_member_ast( ValueType );
check_member_ast( Value );
check_member_ast( ArrExpr );
return true;
}
case Preprocess_Define:
{
check_member_str( Name );
check_member_content( Content );
return true;
}
case Preprocess_If:
case Preprocess_IfDef:
case Preprocess_IfNotDef:
case Preprocess_ElIf:
{
check_member_content( Content );
return true;
}
case Preprocess_Include:
case Preprocess_Pragma:
{
check_member_content( Content );
return true;
}
case Specifiers:
{
check_member_val( NumEntries );
check_member_str( Name );
for ( s32 idx = 0; idx < NumEntries; ++idx )
{
check_member_val( ArrSpecs[ idx ] );
}
return true;
}
case Template:
{
check_member_val( ModuleFlags );
check_member_str( Name );
check_member_ast( Params );
check_member_ast( Declaration );
return true;
}
case Typedef:
{
check_member_val( IsFunction );
check_member_val( ModuleFlags );
check_member_str( Name );
check_member_ast( Specs );
check_member_ast( UnderlyingType );
return true;
}
case Typename:
{
check_member_val( IsParamPack );
check_member_str( Name );
check_member_ast( Specs );
check_member_ast( ArrExpr );
return true;
}
case Union:
{
check_member_val( ModuleFlags );
check_member_str( Name );
check_member_ast( Attributes );
check_member_ast( Body );
return true;
}
case Using:
case Using_Namespace:
{
check_member_val( ModuleFlags );
check_member_str( Name );
check_member_ast( UnderlyingType );
check_member_ast( Attributes );
return true;
}
case Variable:
{
check_member_val( ModuleFlags );
check_member_str( Name );
check_member_ast( ValueType );
check_member_ast( BitfieldSize );
check_member_ast( Value );
check_member_ast( Attributes );
check_member_ast( Specs );
check_member_ast( NextVar );
return true;
}
case Class_Body:
case Enum_Body:
case Export_Body:
case Global_Body:
case Namespace_Body:
case Struct_Body:
case Union_Body:
{
check_member_ast( Front );
check_member_ast( Back );
AST* curr = Front;
AST* curr_other = other->Front;
while ( curr != nullptr )
{
if ( curr_other == nullptr )
{
log_fmt("\nAST::is_equal: Failed for body, other equivalent param is null\n"
"AST : %S\n"
"Other: %S\n"
"For ast member: %S\n"
, curr->debug_str()
);
return false;
}
if ( ! curr->is_equal( curr_other ) )
{
log_fmt( "\nAST::is_equal: Failed for body\n"
"AST : %S\n"
"Other: %S\n"
"For ast member: %S\n"
"other's ast member: %S\n"
, debug_str()
, other->debug_str()
, curr->debug_str()
, curr_other->debug_str()
);
return false;
}
curr = curr->Next;
curr_other = curr_other->Next;
}
Work on AST::is_equal. The NumEntries checks need to be deferred until the end as a final unresolved check on valdiation. As if there really is a discrepancy of entires it should be revealed by the specific entry failing. Right now the latest failure with the single header check involves a define directive specifically the define does omit whitespace properly and so the check interprets the different cached content to be non-equivalent. This will happen with all unvalidated aspects of the AST ( expressions, function bodies, etc ) There are two ways to resolve, either make an AST that can tokenize all items (not realistic), or I need to strip non-syntax important whitespace and then cache the string. This would mean removing everything but a single whitespace for all content within a content string. Otherwise, I would have to somehow make sure the content of the string has the exact formatting between both files for the definitions that matter. AST types with this issue: * Define Directive * Pragma Directive * Comment * Execution * Platform Attributes * Untyped Comments can technically be left unverified as they do not matter semantically. When the serialization is first emitted, the content these strings should for the most part be equivalent. However I do see some possible failures for that if a different style of bracket placment is used (between the serialization). At that point what I could do is just leave those unverified and just emit the content to the user as warning that the ast and the other compared could not be verified. Those technically can be handled on a per-eye basis, and worst case the tests with the compiler will in the determine if any critical defintions are missing for the user.
2023-08-25 15:40:13 -07:00
check_member_val( NumEntries );
return true;
}
#undef check_member_val
#undef check_member_str
#undef check_member_ast
}
return true;
}
bool AST::validate_body()
{
using namespace ECode;
#define CheckEntries( Unallowed_Types ) \
do \
{ \
for ( Code entry : cast<CodeBody>() ) \
{ \
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( GEN_AST_BODY_CLASS_UNALLOWED_TYPES );
break;
case Enum_Body:
for ( Code entry : cast<CodeBody>() )
{
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( GEN_AST_BODY_CLASS_UNALLOWED_TYPES );
break;
case Extern_Linkage:
CheckEntries( GEN_AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES );
break;
case Function_Body:
CheckEntries( GEN_AST_BODY_FUNCTION_UNALLOWED_TYPES );
break;
case Global_Body:
for (Code entry : cast<CodeBody>())
{
switch (entry->Type)
{
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:
log_failure("AST::validate_body: Invalid entry in body %s", entry.debug_str());
return false;
}
}
break;
case Namespace_Body:
CheckEntries( GEN_AST_BODY_NAMESPACE_UNALLOWED_TYPES );
break;
case Struct_Body:
CheckEntries( GEN_AST_BODY_STRUCT_UNALLOWED_TYPES );
break;
case Union_Body:
for ( Code entry : Body->cast<CodeBody>() )
{
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;
#undef CheckEntries
}