Parser - Type checking

This commit is contained in:
Emmanuel BENOîT 2017-11-11 09:18:45 +01:00
parent 65cbebd150
commit e21e5cefb2
3 changed files with 157 additions and 113 deletions

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "odbg.hh" #include "odbg.hh"
#include <ebcl/SRDData.hh> #include <ebcl/SRDData.hh>
#include <ebcl/Sets.hh>
namespace opast { namespace opast {
@ -1152,11 +1153,21 @@ class T_BinaryOperatorNode : public A_ExpressionNode
/*= PARSER ===================================================================*/ /*= 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 class T_Parser : public A_PrivateImplementation
{ {
private: private:
T_Array< T_SRDError > errors_; T_Array< T_SRDError > errors_;
T_OwnPtr< T_RootNode > rootNode_; T_OwnPtr< T_ParserOutput > output_;
public: public:
T_Parser( ) noexcept; T_Parser( ) noexcept;
@ -1165,8 +1176,8 @@ class T_Parser : public A_PrivateImplementation
T_Array< T_SRDError > const& errors( ) const noexcept T_Array< T_SRDError > const& errors( ) const noexcept
{ return errors_; } { return errors_; }
T_OwnPtr< T_RootNode > result( ) noexcept T_OwnPtr< T_ParserOutput > result( ) noexcept
{ return std::move( rootNode_ ); } { return std::move( output_ ); }
}; };

View file

@ -147,7 +147,7 @@ struct T_ParserImpl_
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
T_OwnPtr< T_RootNode >& root; T_OwnPtr< T_ParserOutput >& output;
T_Array< T_SRDError >& errors; T_Array< T_SRDError >& errors;
T_Visitor< A_Node > visitor{ opast::ASTVisitorBrowser }; T_Visitor< A_Node > visitor{ opast::ASTVisitorBrowser };
T_MultiArray< uint32_t > calls; T_MultiArray< uint32_t > calls;
@ -161,18 +161,26 @@ struct T_ParserImpl_
} }; } };
T_ParserImpl_( T_Array< T_SRDError >* errors , 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 parseFunction( T_SRDList const& funcList ) noexcept;
void parseFunctionArguments( void parseFunctionArguments(
T_FuncNode& function , T_FuncNode& function ,
T_SRDToken const& argsToken ) noexcept; T_SRDToken const& argsToken ) noexcept;
// ---------------------------------------------------------------------
void parseInstructions( void parseInstructions(
T_InstrListNode& instructions , T_InstrListNode& instructions ,
T_SRDList const& input , T_SRDList const& input ,
@ -263,48 +271,47 @@ struct T_ParserImpl_
inline T_ParserImpl_::T_ParserImpl_( inline T_ParserImpl_::T_ParserImpl_(
T_Array< T_SRDError >* const errors , T_Array< T_SRDError >* const errors ,
T_OwnPtr< T_RootNode >* const root ) noexcept T_OwnPtr< T_ParserOutput >* const output ) noexcept
: root( *root ) , errors( *errors ) : 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 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]() { const T_SRDLocation missingErrLoc( ([&input]() {
if ( input.size( ) != 0 ) { if ( input.size( ) != 0 ) {
return T_SRDLocation( input[ 0 ].location( ).source( ) , 1 , 1 ); return T_SRDLocation( input[ 0 ].location( ).source( ) , 1 , 1 );
} }
return T_SRDLocation{}; return T_SRDLocation{};
})( )); })( ));
if ( !root->hasInit( ) ) { if ( !output->root.hasInit( ) ) {
errors.addNew( "no initialisation block" , missingErrLoc ); errors.addNew( "no initialisation block" , missingErrLoc );
} }
if ( !root->hasFrame( ) ) { if ( !output->root.hasFrame( ) ) {
errors.addNew( "no initialisation block" , missingErrLoc ); errors.addNew( "no frame rendering block" , missingErrLoc );
} }
if ( !errors.empty( ) ) { return errors.empty( );
return;
} }
bool T_ParserImpl_::checkCalls( ) noexcept
{
calls.clear( ); calls.clear( );
uint32_t cfi; uint32_t cfi;
for ( cfi = 0 ; cfi < root->nFunctions( ) ; cfi ++ ) { for ( cfi = 0 ; cfi < output->root.nFunctions( ) ; cfi ++ ) {
calls.next( ); 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 ) ) { if ( exit || dynamic_cast< A_ExpressionNode* >( &node ) ) {
return false; return false;
} }
@ -313,14 +320,14 @@ void T_ParserImpl_::parseTopLevel(
} }
auto& call( (T_CallInstrNode&) node ); auto& call( (T_CallInstrNode&) node );
const auto callee( root->functionIndex( call.id( ) ) ); const auto callee( output->root.functionIndex( call.id( ) ) );
if ( callee >= 0 ) { if ( callee >= 0 ) {
if ( !calls.contains( cfi , callee ) ) { if ( !calls.contains( cfi , callee ) ) {
calls.add( (uint32_t) callee ); calls.add( (uint32_t) callee );
} }
// Check argument count while we're at it // 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( ) ) { if ( fn.arguments( ) != call.arguments( ) ) {
T_StringBuilder sb; T_StringBuilder sb;
sb << "function expects " << fn.arguments( ) sb << "function expects " << fn.arguments( )
@ -336,12 +343,13 @@ void T_ParserImpl_::parseTopLevel(
return false; return false;
} ); } );
} }
if ( !errors.empty( ) ) { return errors.empty( );
return;
} }
bool T_ParserImpl_::checkInstructionRestrictions( ) noexcept
{
T_InstrRestriction callInfo[ calls.size( ) ]; T_InstrRestriction callInfo[ calls.size( ) ];
callGraphVisitor.visit( root->functionIndex( "*init*" ) , callGraphVisitor.visit( output->root.functionIndex( "*init*" ) ,
[&]( uint32_t id , const bool exit ) -> bool { [&]( uint32_t id , const bool exit ) -> bool {
if ( exit || callInfo[ id ] & E_InstrRestriction::INIT ) { if ( exit || callInfo[ id ] & E_InstrRestriction::INIT ) {
return false; return false;
@ -349,7 +357,7 @@ void T_ParserImpl_::parseTopLevel(
callInfo[ id ] |= E_InstrRestriction::INIT; callInfo[ id ] |= E_InstrRestriction::INIT;
return true; return true;
} ); } );
callGraphVisitor.visit( root->functionIndex( "*frame*" ) , callGraphVisitor.visit( output->root.functionIndex( "*frame*" ) ,
[&]( uint32_t id , const bool exit ) -> bool { [&]( uint32_t id , const bool exit ) -> bool {
if ( exit || callInfo[ id ] & E_InstrRestriction::FRAME ) { if ( exit || callInfo[ id ] & E_InstrRestriction::FRAME ) {
return false; return false;
@ -357,8 +365,8 @@ void T_ParserImpl_::parseTopLevel(
callInfo[ id ] |= E_InstrRestriction::FRAME; callInfo[ id ] |= E_InstrRestriction::FRAME;
return true; return true;
} ); } );
for ( auto i = 0u ; i < root->nFunctions( ) ; i ++ ) { for ( auto i = 0u ; i < output->root.nFunctions( ) ; i ++ ) {
visitor.visit( root->function( i ) , visitor.visit( output->root.function( i ) ,
[&]( A_Node& node , bool exit ) { [&]( A_Node& node , bool exit ) {
if ( exit ) { if ( exit ) {
return false; return false;
@ -374,6 +382,96 @@ void T_ParserImpl_::parseTopLevel(
return true; 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( void T_ParserImpl_::parseFunction(
@ -402,15 +500,15 @@ void T_ParserImpl_::parseFunction(
return; return;
} }
fn = NewOwned< T_FuncNode >( funcList[ 1 ].stringValue( ) , *root ); fn = NewOwned< T_FuncNode >( funcList[ 1 ].stringValue( ) , output->root );
parseFunctionArguments( dynamic_cast< T_FuncNode& >( *fn ) , parseFunctionArguments( dynamic_cast< T_FuncNode& >( *fn ) ,
funcList[ 2 ] ); funcList[ 2 ] );
} else { } else {
fn = NewOwned< T_SpecialFuncNode >( ftw == "init" , *root ); fn = NewOwned< T_SpecialFuncNode >( ftw == "init" , output->root );
} }
fn->location( ) = fw.location( ); fn->location( ) = fw.location( );
const auto af( root->addFunction( fn ) ); const auto af( output->root.addFunction( fn ) );
if ( af.dupLocation ) { if ( af.dupLocation ) {
T_StringBuilder esb( "duplicate " ); T_StringBuilder esb( "duplicate " );
switch ( fn->type( ) ) { switch ( fn->type( ) ) {
@ -1388,15 +1486,15 @@ P_ExpressionNode T_ParserImpl_::parseUnaryOp(
/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/
T_Parser::T_Parser( ) noexcept T_Parser::T_Parser( ) noexcept
: A_PrivateImplementation( new T_ParserImpl_( &errors_ , &rootNode_ ) ) , : A_PrivateImplementation( new T_ParserImpl_( &errors_ , &output_ ) ) ,
errors_( 64 ) , rootNode_{} errors_( 64 ) , output_{}
{} {}
bool T_Parser::parse( bool T_Parser::parse(
T_SRDList const& input ) noexcept T_SRDList const& input ) noexcept
{ {
errors_.clear( ); errors_.clear( );
rootNode_ = NewOwned< T_RootNode >( ); output_ = NewOwned< T_ParserOutput >( );
p< T_ParserImpl_ >( ).parseTopLevel( input ); p< T_ParserImpl_ >( ).main( input );
return errors_.empty( ); return errors_.empty( );
} }

View file

@ -37,75 +37,10 @@ void WriteSRDError(
/*============================================================================*/ /*============================================================================*/
T_Array< T_SRDError > parserPrototypes( 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_Visitor< opast::A_Node > visitor{ ASTVisitorBrowser };
T_Array< T_SRDError > errors; 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; return errors;
} }