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
#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_ ); }
};

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_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( );
}

View file

@ -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;
}