Parser - Type checking
This commit is contained in:
parent
65cbebd150
commit
e21e5cefb2
3 changed files with 157 additions and 113 deletions
17
opast.hh
17
opast.hh
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include "odbg.hh"
|
||||
#include <ebcl/SRDData.hh>
|
||||
#include <ebcl/Sets.hh>
|
||||
|
||||
|
||||
namespace opast {
|
||||
|
@ -1152,11 +1153,21 @@ class T_BinaryOperatorNode : public A_ExpressionNode
|
|||
|
||||
/*= PARSER ===================================================================*/
|
||||
|
||||
// Parser output. Consists in a root node as well as other details (table of
|
||||
// constants, data types, etc...)
|
||||
struct T_ParserOutput
|
||||
{
|
||||
T_RootNode root;
|
||||
T_KeyValueTable< T_String , E_DataType > types;
|
||||
T_Set< float > constants{ UseTag< IndexBacked< > >( ) };
|
||||
};
|
||||
|
||||
// The actual parser
|
||||
class T_Parser : public A_PrivateImplementation
|
||||
{
|
||||
private:
|
||||
T_Array< T_SRDError > errors_;
|
||||
T_OwnPtr< T_RootNode > rootNode_;
|
||||
T_OwnPtr< T_ParserOutput > output_;
|
||||
|
||||
public:
|
||||
T_Parser( ) noexcept;
|
||||
|
@ -1165,8 +1176,8 @@ class T_Parser : public A_PrivateImplementation
|
|||
|
||||
T_Array< T_SRDError > const& errors( ) const noexcept
|
||||
{ return errors_; }
|
||||
T_OwnPtr< T_RootNode > result( ) noexcept
|
||||
{ return std::move( rootNode_ ); }
|
||||
T_OwnPtr< T_ParserOutput > result( ) noexcept
|
||||
{ return std::move( output_ ); }
|
||||
};
|
||||
|
||||
|
||||
|
|
186
opparser.cc
186
opparser.cc
|
@ -147,7 +147,7 @@ struct T_ParserImpl_
|
|||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
T_OwnPtr< T_RootNode >& root;
|
||||
T_OwnPtr< T_ParserOutput >& output;
|
||||
T_Array< T_SRDError >& errors;
|
||||
T_Visitor< A_Node > visitor{ opast::ASTVisitorBrowser };
|
||||
T_MultiArray< uint32_t > calls;
|
||||
|
@ -161,18 +161,26 @@ struct T_ParserImpl_
|
|||
} };
|
||||
|
||||
T_ParserImpl_( T_Array< T_SRDError >* errors ,
|
||||
T_OwnPtr< T_RootNode >* root ) noexcept;
|
||||
T_OwnPtr< T_ParserOutput >* root ) noexcept;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
void parseTopLevel( T_SRDList const& list ) noexcept;
|
||||
void main( T_SRDList const& list ) noexcept;
|
||||
|
||||
bool checkRequiredBlocks( T_SRDList const& list ) noexcept;
|
||||
bool checkCalls( ) noexcept;
|
||||
bool checkInstructionRestrictions( ) noexcept;
|
||||
bool collectGlobalTypes( ) noexcept;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
bool parseTopLevel( T_SRDList const& list ) noexcept;
|
||||
|
||||
void parseFunction( T_SRDList const& funcList ) noexcept;
|
||||
void parseFunctionArguments(
|
||||
T_FuncNode& function ,
|
||||
T_SRDToken const& argsToken ) noexcept;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
void parseInstructions(
|
||||
T_InstrListNode& instructions ,
|
||||
T_SRDList const& input ,
|
||||
|
@ -263,48 +271,47 @@ struct T_ParserImpl_
|
|||
|
||||
inline T_ParserImpl_::T_ParserImpl_(
|
||||
T_Array< T_SRDError >* const errors ,
|
||||
T_OwnPtr< T_RootNode >* const root ) noexcept
|
||||
: root( *root ) , errors( *errors )
|
||||
T_OwnPtr< T_ParserOutput >* const output ) noexcept
|
||||
: output( *output ) , errors( *errors )
|
||||
{ }
|
||||
|
||||
void T_ParserImpl_::main(
|
||||
T_SRDList const& input ) noexcept
|
||||
{
|
||||
parseTopLevel( input )
|
||||
&& checkRequiredBlocks( input )
|
||||
&& checkCalls( )
|
||||
&& checkInstructionRestrictions( )
|
||||
&& collectGlobalTypes( );
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
void T_ParserImpl_::parseTopLevel(
|
||||
bool T_ParserImpl_::checkRequiredBlocks(
|
||||
T_SRDList const& input ) noexcept
|
||||
{
|
||||
for ( auto const& t : input ) {
|
||||
if ( t.type( ) == E_SRDTokenType::LIST && t.list( ).size( ) > 0 ) {
|
||||
parseFunction( t.list( ) );
|
||||
} else {
|
||||
errors.addNew( "function, init or frame list expected" ,
|
||||
t.location( ) );
|
||||
}
|
||||
}
|
||||
if ( !errors.empty( ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const T_SRDLocation missingErrLoc( ([&input]() {
|
||||
if ( input.size( ) != 0 ) {
|
||||
return T_SRDLocation( input[ 0 ].location( ).source( ) , 1 , 1 );
|
||||
}
|
||||
return T_SRDLocation{};
|
||||
})( ));
|
||||
if ( !root->hasInit( ) ) {
|
||||
if ( !output->root.hasInit( ) ) {
|
||||
errors.addNew( "no initialisation block" , missingErrLoc );
|
||||
}
|
||||
if ( !root->hasFrame( ) ) {
|
||||
errors.addNew( "no initialisation block" , missingErrLoc );
|
||||
}
|
||||
if ( !errors.empty( ) ) {
|
||||
return;
|
||||
if ( !output->root.hasFrame( ) ) {
|
||||
errors.addNew( "no frame rendering block" , missingErrLoc );
|
||||
}
|
||||
return errors.empty( );
|
||||
}
|
||||
|
||||
bool T_ParserImpl_::checkCalls( ) noexcept
|
||||
{
|
||||
calls.clear( );
|
||||
uint32_t cfi;
|
||||
for ( cfi = 0 ; cfi < root->nFunctions( ) ; cfi ++ ) {
|
||||
for ( cfi = 0 ; cfi < output->root.nFunctions( ) ; cfi ++ ) {
|
||||
calls.next( );
|
||||
visitor.visit( root->function( cfi ) , [&]( A_Node& node , bool exit ) -> bool {
|
||||
visitor.visit( output->root.function( cfi ) , [&]( A_Node& node , bool exit ) -> bool {
|
||||
if ( exit || dynamic_cast< A_ExpressionNode* >( &node ) ) {
|
||||
return false;
|
||||
}
|
||||
|
@ -313,14 +320,14 @@ void T_ParserImpl_::parseTopLevel(
|
|||
}
|
||||
|
||||
auto& call( (T_CallInstrNode&) node );
|
||||
const auto callee( root->functionIndex( call.id( ) ) );
|
||||
const auto callee( output->root.functionIndex( call.id( ) ) );
|
||||
if ( callee >= 0 ) {
|
||||
if ( !calls.contains( cfi , callee ) ) {
|
||||
calls.add( (uint32_t) callee );
|
||||
}
|
||||
|
||||
// Check argument count while we're at it
|
||||
auto& fn( (T_FuncNode&) root->function( callee ) );
|
||||
auto& fn( (T_FuncNode&) output->root.function( callee ) );
|
||||
if ( fn.arguments( ) != call.arguments( ) ) {
|
||||
T_StringBuilder sb;
|
||||
sb << "function expects " << fn.arguments( )
|
||||
|
@ -336,12 +343,13 @@ void T_ParserImpl_::parseTopLevel(
|
|||
return false;
|
||||
} );
|
||||
}
|
||||
if ( !errors.empty( ) ) {
|
||||
return;
|
||||
}
|
||||
return errors.empty( );
|
||||
}
|
||||
|
||||
bool T_ParserImpl_::checkInstructionRestrictions( ) noexcept
|
||||
{
|
||||
T_InstrRestriction callInfo[ calls.size( ) ];
|
||||
callGraphVisitor.visit( root->functionIndex( "*init*" ) ,
|
||||
callGraphVisitor.visit( output->root.functionIndex( "*init*" ) ,
|
||||
[&]( uint32_t id , const bool exit ) -> bool {
|
||||
if ( exit || callInfo[ id ] & E_InstrRestriction::INIT ) {
|
||||
return false;
|
||||
|
@ -349,7 +357,7 @@ void T_ParserImpl_::parseTopLevel(
|
|||
callInfo[ id ] |= E_InstrRestriction::INIT;
|
||||
return true;
|
||||
} );
|
||||
callGraphVisitor.visit( root->functionIndex( "*frame*" ) ,
|
||||
callGraphVisitor.visit( output->root.functionIndex( "*frame*" ) ,
|
||||
[&]( uint32_t id , const bool exit ) -> bool {
|
||||
if ( exit || callInfo[ id ] & E_InstrRestriction::FRAME ) {
|
||||
return false;
|
||||
|
@ -357,8 +365,8 @@ void T_ParserImpl_::parseTopLevel(
|
|||
callInfo[ id ] |= E_InstrRestriction::FRAME;
|
||||
return true;
|
||||
} );
|
||||
for ( auto i = 0u ; i < root->nFunctions( ) ; i ++ ) {
|
||||
visitor.visit( root->function( i ) ,
|
||||
for ( auto i = 0u ; i < output->root.nFunctions( ) ; i ++ ) {
|
||||
visitor.visit( output->root.function( i ) ,
|
||||
[&]( A_Node& node , bool exit ) {
|
||||
if ( exit ) {
|
||||
return false;
|
||||
|
@ -374,6 +382,96 @@ void T_ParserImpl_::parseTopLevel(
|
|||
return true;
|
||||
} );
|
||||
}
|
||||
return errors.empty( );
|
||||
}
|
||||
|
||||
bool T_ParserImpl_::collectGlobalTypes( ) noexcept
|
||||
{
|
||||
struct T_Decl_ {
|
||||
E_DataType type;
|
||||
T_SRDLocation location;
|
||||
|
||||
T_Decl_( const E_DataType dt ,
|
||||
T_SRDLocation const& loc ) noexcept
|
||||
: type( dt ) , location( loc )
|
||||
{ }
|
||||
|
||||
T_Decl_( const E_DataType dt ) noexcept
|
||||
: type( dt ) , location{}
|
||||
{ }
|
||||
};
|
||||
T_KeyValueTable< T_String , T_Decl_ > type;
|
||||
type.add( T_String::Pooled( "time" ) , T_Decl_{ E_DataType::BUILTIN } );
|
||||
type.add( T_String::Pooled( "width" ) , T_Decl_{ E_DataType::BUILTIN } );
|
||||
type.add( T_String::Pooled( "height" ) , T_Decl_{ E_DataType::BUILTIN } );
|
||||
|
||||
visitor.visit( output->root , [&]( A_Node& node , bool exit ) -> bool {
|
||||
if ( exit ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
E_DataType dt{ E_DataType::UNKNOWN };
|
||||
T_String id;
|
||||
T_SRDLocation location;
|
||||
if ( node.type( ) == A_Node::OP_SET ) {
|
||||
dt = E_DataType::VARIABLE;
|
||||
id = dynamic_cast< T_SetInstrNode& >( node ).id( );
|
||||
location = dynamic_cast< T_SetInstrNode& >( node ).idLocation( );
|
||||
} else {
|
||||
auto* np{ dynamic_cast< A_ResourceDefInstrNode* >( &node ) };
|
||||
if ( !np ) {
|
||||
return !dynamic_cast< A_ExpressionNode* >( &node );
|
||||
}
|
||||
dt = np->dataType( );
|
||||
id = np->id( );
|
||||
location = np->idLocation( );
|
||||
}
|
||||
assert( dt != E_DataType::UNKNOWN );
|
||||
|
||||
T_Decl_ const* const existing( type.get( id ) );
|
||||
if ( !existing ) {
|
||||
type.add( id , T_Decl_{ dt , location } );
|
||||
return false;
|
||||
}
|
||||
if ( existing->type == dt ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
T_StringBuilder sb;
|
||||
if ( existing->type == E_DataType::BUILTIN ) {
|
||||
sb << "trying to redefine built-in variable " << id;
|
||||
} else {
|
||||
sb << "'" << id << "' redeclared as " << dt
|
||||
<< "; previous declaration as "
|
||||
<< existing->type << " at "
|
||||
<< existing->location;
|
||||
}
|
||||
errors.addNew( std::move( sb ) , location );
|
||||
|
||||
return false;
|
||||
} );
|
||||
|
||||
for ( auto const& k : type.keys( ) ) {
|
||||
output->types.add( k , type.get( k )->type );
|
||||
}
|
||||
|
||||
return errors.empty( );
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------*/
|
||||
|
||||
bool T_ParserImpl_::parseTopLevel(
|
||||
T_SRDList const& input ) noexcept
|
||||
{
|
||||
for ( auto const& t : input ) {
|
||||
if ( t.type( ) == E_SRDTokenType::LIST && t.list( ).size( ) > 0 ) {
|
||||
parseFunction( t.list( ) );
|
||||
} else {
|
||||
errors.addNew( "function, init or frame list expected" ,
|
||||
t.location( ) );
|
||||
}
|
||||
}
|
||||
return errors.empty( );
|
||||
}
|
||||
|
||||
void T_ParserImpl_::parseFunction(
|
||||
|
@ -402,15 +500,15 @@ void T_ParserImpl_::parseFunction(
|
|||
return;
|
||||
}
|
||||
|
||||
fn = NewOwned< T_FuncNode >( funcList[ 1 ].stringValue( ) , *root );
|
||||
fn = NewOwned< T_FuncNode >( funcList[ 1 ].stringValue( ) , output->root );
|
||||
parseFunctionArguments( dynamic_cast< T_FuncNode& >( *fn ) ,
|
||||
funcList[ 2 ] );
|
||||
} else {
|
||||
fn = NewOwned< T_SpecialFuncNode >( ftw == "init" , *root );
|
||||
fn = NewOwned< T_SpecialFuncNode >( ftw == "init" , output->root );
|
||||
}
|
||||
fn->location( ) = fw.location( );
|
||||
|
||||
const auto af( root->addFunction( fn ) );
|
||||
const auto af( output->root.addFunction( fn ) );
|
||||
if ( af.dupLocation ) {
|
||||
T_StringBuilder esb( "duplicate " );
|
||||
switch ( fn->type( ) ) {
|
||||
|
@ -1388,15 +1486,15 @@ P_ExpressionNode T_ParserImpl_::parseUnaryOp(
|
|||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
T_Parser::T_Parser( ) noexcept
|
||||
: A_PrivateImplementation( new T_ParserImpl_( &errors_ , &rootNode_ ) ) ,
|
||||
errors_( 64 ) , rootNode_{}
|
||||
: A_PrivateImplementation( new T_ParserImpl_( &errors_ , &output_ ) ) ,
|
||||
errors_( 64 ) , output_{}
|
||||
{}
|
||||
|
||||
bool T_Parser::parse(
|
||||
T_SRDList const& input ) noexcept
|
||||
{
|
||||
errors_.clear( );
|
||||
rootNode_ = NewOwned< T_RootNode >( );
|
||||
p< T_ParserImpl_ >( ).parseTopLevel( input );
|
||||
output_ = NewOwned< T_ParserOutput >( );
|
||||
p< T_ParserImpl_ >( ).main( input );
|
||||
return errors_.empty( );
|
||||
}
|
||||
|
|
|
@ -37,75 +37,10 @@ void WriteSRDError(
|
|||
/*============================================================================*/
|
||||
|
||||
T_Array< T_SRDError > parserPrototypes(
|
||||
T_OwnPtr< T_RootNode > const& root )
|
||||
T_OwnPtr< T_ParserOutput > const& output )
|
||||
{
|
||||
T_Visitor< opast::A_Node > visitor{ ASTVisitorBrowser };
|
||||
T_Array< T_SRDError > errors;
|
||||
|
||||
struct T_Decl_ {
|
||||
E_DataType type;
|
||||
T_SRDLocation location;
|
||||
|
||||
T_Decl_( const E_DataType dt ,
|
||||
T_SRDLocation const& loc ) noexcept
|
||||
: type( dt ) , location( loc )
|
||||
{ }
|
||||
|
||||
T_Decl_( const E_DataType dt ) noexcept
|
||||
: type( dt ) , location{}
|
||||
{ }
|
||||
};
|
||||
T_KeyValueTable< T_String , T_Decl_ > type;
|
||||
type.add( T_String::Pooled( "time" ) , T_Decl_{ E_DataType::BUILTIN } );
|
||||
type.add( T_String::Pooled( "width" ) , T_Decl_{ E_DataType::BUILTIN } );
|
||||
type.add( T_String::Pooled( "height" ) , T_Decl_{ E_DataType::BUILTIN } );
|
||||
|
||||
visitor.visit( *root , [&]( A_Node& node , bool exit ) -> bool {
|
||||
if ( exit ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
E_DataType dt{ E_DataType::UNKNOWN };
|
||||
T_String id;
|
||||
T_SRDLocation location;
|
||||
if ( node.type( ) == A_Node::OP_SET ) {
|
||||
dt = E_DataType::VARIABLE;
|
||||
id = dynamic_cast< T_SetInstrNode& >( node ).id( );
|
||||
location = dynamic_cast< T_SetInstrNode& >( node ).idLocation( );
|
||||
} else {
|
||||
auto* np{ dynamic_cast< A_ResourceDefInstrNode* >( &node ) };
|
||||
if ( !np ) {
|
||||
return !dynamic_cast< A_ExpressionNode* >( &node );
|
||||
}
|
||||
dt = np->dataType( );
|
||||
id = np->id( );
|
||||
location = np->idLocation( );
|
||||
}
|
||||
assert( dt != E_DataType::UNKNOWN );
|
||||
|
||||
T_Decl_ const* const existing( type.get( id ) );
|
||||
if ( !existing ) {
|
||||
type.add( id , T_Decl_{ dt , location } );
|
||||
return false;
|
||||
}
|
||||
if ( existing->type == dt ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
T_StringBuilder sb;
|
||||
if ( existing->type == E_DataType::BUILTIN ) {
|
||||
sb << "trying to redefine built-in variable " << id;
|
||||
} else {
|
||||
sb << "'" << id << "' redeclared as " << dt
|
||||
<< "; previous declaration as "
|
||||
<< existing->type << " at "
|
||||
<< existing->location;
|
||||
}
|
||||
errors.addNew( std::move( sb ) , location );
|
||||
|
||||
return false;
|
||||
} );
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue