#include "opast.hh" using namespace ebcl; using namespace opast; /*= A_Node ===================================================================*/ A_Node::A_Node( const E_Type type , A_Node* const parent ) noexcept : type_( type ) , parent_( parent ) { assert( ( type == ROOT && !parent ) || ( type != ROOT && parent ) ); } A_Node::~A_Node( ) { } T_RootNode& A_Node::root( ) const noexcept { A_Node const* node( this ); while ( node->parent_ ) { node = node->parent_; } assert( node ); assert( node->type_ == ROOT ); return *dynamic_cast< T_RootNode* >( const_cast< A_Node* >( node ) ); } /*= T_InstrListNode ==========================================================*/ T_InstrListNode::T_InstrListNode( A_Node& parent ) noexcept : A_Node( ILIST , &parent ) { } /*= A_FuncNode ===============================================================*/ A_FuncNode::A_FuncNode( const bool isInit , T_RootNode* const root ) noexcept : A_Node( isInit ? DECL_INIT : DECL_FRAME , root ) , name_( isInit ? "*init*" : "*frame*" ) , instructions_( *this ) { } A_FuncNode::A_FuncNode( T_String const& name , T_RootNode* const root ) noexcept : A_Node( DECL_FN , root ) , name_( name ) , instructions_( *this ) { } /*= T_RootNode ===============================================================*/ T_RootNode::T_RootNode( ) noexcept : A_Node( ROOT , nullptr ) , functions_( []( T_OwnPtr< A_FuncNode > const& f ) { return f->name( ); } ) { } T_RootNode::T_AddFunctionResult T_RootNode::addFunction( T_OwnPtr< A_FuncNode >& function ) noexcept { T_String const& fn( function->name( ) ); auto const* const pf( functions_.get( fn ) ); if ( !pf ) { auto* const rv( function.get( ) ); functions_.add( std::move( function ) ); return *rv; } T_String dfn; uint32_t dupCtr( 0 ); do { T_StringBuilder fnsb; fnsb << fn << " dup " << dupCtr ++; dfn = std::move( fnsb ); } while ( functions_.contains( dfn ) ); T_OwnPtr< A_FuncNode > df( NewOwned< T_FuncNode >( dfn , *this ) ); auto* const rv( df.get( ) ); functions_.add( std::move( df ) ); return T_AddFunctionResult{ *rv , (*pf)->location( ) }; } /*= T_SpecialFuncNode ========================================================*/ T_SpecialFuncNode::T_SpecialFuncNode( bool isInit , T_RootNode& parent ) noexcept : A_FuncNode( isInit , &parent ) { } /*= T_FuncNode ===============================================================*/ T_FuncNode::T_FuncNode( T_String const& name , T_RootNode& parent ) noexcept : A_FuncNode( name , &parent ) { } T_Optional< T_SRDLocation > T_FuncNode::addArgument( T_SRDToken const& token ) noexcept { assert( token.type( ) == E_SRDTokenType::WORD ); assert( token.hasLocation( ) ); const auto pnp( argNames_.indexOf( token.stringValue( ) ) ); if ( pnp != -1 ) { return argLocations_[ pnp ]; } argNames_.add( token.stringValue( ) ); argLocations_.add( token.location( ) ); return {}; } /*= T_PipelineInstrNode ======================================================*/ T_PipelineInstrNode::T_PipelineInstrNode( T_InstrListNode& parent , T_SRDToken const& idToken ) noexcept : A_InstructionNode( OP_PIPELINE , parent ) , id_( idToken.stringValue( ) ) , idLocation_( idToken.location( ) ) { } T_PipelineInstrNode::T_PipelineInstrNode( T_InstrListNode& parent ) noexcept : A_InstructionNode( OP_PIPELINE , parent ) , id_( "*invalid*" ) , idLocation_{ } { } T_Optional< T_SRDLocation > T_PipelineInstrNode::addProgram( T_SRDToken const& pidToken ) noexcept { T_String const& name( pidToken.stringValue( ) ); const auto pIndex( pids_.indexOf( name ) ); if ( pIndex != -1 ) { return pidLocations_[ pIndex ]; } pids_.add( name ); pidLocations_.add( pidToken.location( ) ); return {}; } /*= T_ProfileInstrNode =======================================================*/ T_ProfileInstrNode::T_ProfileInstrNode( T_InstrListNode& parent , T_String const& text ) noexcept : A_InstructionNode( OP_PROFILE , parent ) , text_( text ) , instructions_( *this ) { } /*= T_ProgramInstrNode =======================================================*/ T_ProgramInstrNode::T_ProgramInstrNode( T_InstrListNode& parent , T_SRDToken const& idToken , T_SRDToken const& pathToken ) noexcept : A_InstructionNode( OP_PROGRAM , parent ) , id_( idToken.stringValue( ) ) , idLocation_( idToken.location( ) ) , path_( pathToken.stringValue( ) ) , pathLocation_( pathToken.location( ) ) { } /*= T_Parser =================================================================*/ namespace { struct T_ParserImpl_ { enum class E_InstrType { IF , PIPELINE , PROFILE , PROGRAM , }; const T_KeyValueTable< T_String , E_InstrType > instrMap{ ([]() { T_KeyValueTable< T_String , E_InstrType > temp; const auto add{ [&temp]( char const* name , E_InstrType it ) { temp.add( T_String::Pooled( name ) , it ); } }; add( "if" , E_InstrType::IF ); add( "pipeline" , E_InstrType::PIPELINE ); add( "profiling" , E_InstrType::PROFILE ); add( "program" , E_InstrType::PROGRAM ); return temp; })( ) }; // --------------------------------------------------------------------- T_OwnPtr< T_RootNode >& root; T_Array< T_SRDError >& errors; T_ParserImpl_( T_Array< T_SRDError >* errors , T_OwnPtr< T_RootNode >* root ) 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 , uint32_t start ) noexcept; // --------------------------------------------------------------------- void parsePipelineInstruction( T_InstrListNode& instructions , T_SRDList const& input ) noexcept; void parseProfileInstruction( T_InstrListNode& instructions , T_SRDList const& input ) noexcept; void parseProgramInstruction( T_InstrListNode& instructions , T_SRDList const& input ) noexcept; }; T_ParserImpl_::T_ParserImpl_( T_Array< T_SRDError >* const errors , T_OwnPtr< T_RootNode >* const root ) noexcept : root( *root ) , errors( *errors ) { } void T_ParserImpl_::parseFunction( T_SRDList const& funcList ) noexcept { assert( funcList.size( ) != 0 ); auto const& fw( funcList[ 0 ] ); if ( fw.type( ) != E_SRDTokenType::WORD || ( fw.stringValue( ) != "init" && fw.stringValue( ) != "frame" && fw.stringValue( ) != "fn" ) ) { errors.addNew( "init, frame or fn expected" , fw.location( ) ); return; } T_String const& ftw( fw.stringValue( ) ); T_OwnPtr< A_FuncNode > fn; if ( ftw == "fn" ) { if ( funcList.size( ) < 3 ) { errors.addNew( "function name and arguments expected" , fw.location( ) ); return; } if ( funcList[ 1 ].type( ) != E_SRDTokenType::WORD ) { errors.addNew( "function name expected" , funcList[ 1 ].location( ) ); return; } fn = NewOwned< T_FuncNode >( funcList[ 1 ].stringValue( ) , *root ); parseFunctionArguments( dynamic_cast< T_FuncNode& >( *fn ) , funcList[ 2 ] ); } else { fn = NewOwned< T_SpecialFuncNode >( ftw == "init" , *root ); } fn->location( ) = fw.location( ); const auto af( root->addFunction( fn ) ); if ( af.dupLocation.present( ) ) { T_StringBuilder esb( "duplicate " ); switch ( fn->type( ) ) { case A_Node::DECL_FN: esb << "function '" << fn->name( ) << "'"; break; case A_Node::DECL_INIT: esb << "initialisation function"; break; case A_Node::DECL_FRAME: esb << "frame function"; break; default: std::abort( ); } esb << "; previous declaration: " << *af.dupLocation.target( ); errors.addNew( std::move( esb ) , fw.location( ) ); } parseInstructions( af.function.instructions( ) , funcList , ftw == "fn" ? 3 : 1 ); } void T_ParserImpl_::parseFunctionArguments( T_FuncNode& function , T_SRDToken const& argsToken ) noexcept { if ( argsToken.type( ) != E_SRDTokenType::LIST ) { errors.addNew( "arguments list expected" , argsToken.location( ) ); return; } for ( auto const& token : argsToken.list( ) ) { if ( token.type( ) != E_SRDTokenType::WORD ) { errors.addNew( "argument name expected" , token.location( ) ); continue; } const auto rv( function.addArgument( token ) ); if ( rv.present( ) ) { T_StringBuilder esb; esb << "duplicate argument '" << token.stringValue( ) << "'; previous declaration: " << *rv.target( ); errors.addNew( std::move( esb ) , token.location( ) ); } } } void T_ParserImpl_::parseInstructions( T_InstrListNode& instructions , T_SRDList const& input , const uint32_t start ) noexcept { for ( auto iter( input.begin( ) + start ) ; iter.valid( ) ; iter ++ ) { T_SRDToken const& itok( *iter ); if ( itok.type( ) != E_SRDTokenType::LIST ) { errors.addNew( "instruction expected" , itok.location( ) ); continue; } T_SRDList const& ilist( itok.list( ) ); if ( ilist.empty( ) ) { errors.addNew( "instruction expected" , itok.location( ) ); continue; } T_SRDToken const& iname( ilist[ 0 ] ); if ( iname.type( ) != E_SRDTokenType::WORD ) { errors.addNew( "instruction name expected" , iname.location( ) ); continue; } T_String const& iword( iname.stringValue( ) ); if ( !instrMap.contains( iword ) ) { errors.addNew( "unknown instruction" , iname.location( ) ); continue; } switch ( *instrMap.get( iword ) ) { case E_InstrType::PIPELINE: parsePipelineInstruction( instructions , ilist ); break; case E_InstrType::PROFILE: parseProfileInstruction( instructions , ilist ); break; case E_InstrType::PROGRAM: parseProgramInstruction( instructions , ilist ); break; } } } /*----------------------------------------------------------------------------*/ void T_ParserImpl_::parsePipelineInstruction( T_InstrListNode& instructions , T_SRDList const& input ) noexcept { if ( input.size( ) < 3 ) { errors.addNew( "identifier and program identifiers expected" , input[ 0 ].location( ) ); return; } const bool validId( input[ 1 ].type( ) == E_SRDTokenType::WORD ); if ( !validId ) { errors.addNew( "pipeline identifier expected" , input[ 1 ].location( ) ); } T_PipelineInstrNode& pipeline{ ([&instructions,&input,validId]( ) -> T_PipelineInstrNode& { if ( validId ) { return instructions.add< T_PipelineInstrNode >( input[ 0 ] ); } return instructions.add< T_PipelineInstrNode >( ); })() }; pipeline.location( ) = input[ 0 ].location( ); const auto nMax{ std::min( input.size( ) , 8u ) }; for ( auto i = 2u ; i < nMax ; i ++ ) { T_SRDToken const& tok( input[ i ] ); if ( tok.type( ) != E_SRDTokenType::WORD ) { errors.addNew( "program identifier expected" , tok.location( ) ); continue; } const auto dup( pipeline.addProgram( tok ) ); if ( dup.present( ) ) { T_StringBuilder esb; esb << "duplicate program identifier; previous use: " << *dup.target( ); errors.addNew( std::move( esb ) , tok.location( ) ); } } if ( input.size( ) > 8 ) { errors.addNew( "too many arguments" , input[ 8 ].location( ) ); } } /*----------------------------------------------------------------------------*/ void T_ParserImpl_::parseProfileInstruction( T_InstrListNode& instructions , T_SRDList const& input ) noexcept { const bool hasEnough( input.size( ) < 2 ); if ( hasEnough || !input[ 1 ].isText( ) ) { errors.addNew( "profiling section name expected" , hasEnough ? input[ 1 ].location( ) : T_SRDLocation{} ); if ( !hasEnough ) { return; } } const T_String text( input[ 1 ].isText( ) ? input[ 1 ].stringValue( ) : "*invalid*" ); T_ProfileInstrNode& profile{ instructions.add< T_ProfileInstrNode >( text ) }; profile.location( ) = input[ 0 ].location( ); parseInstructions( profile.instructions( ) , input , 2 ); } /*----------------------------------------------------------------------------*/ void T_ParserImpl_::parseProgramInstruction( T_InstrListNode& instructions , T_SRDList const& input ) noexcept { bool ok{ true }; if ( input.size( ) == 1 ) { errors.addNew( "identifier and program name required" , input[ 0 ].location( ) ); return; } if ( input[ 1 ].type( ) != E_SRDTokenType::WORD ) { errors.addNew( "identifier (word) expected" , input[ 1 ].location( ) ); ok = false; } if ( input.size( ) == 2 ) { errors.addNew( "program name required" , input[ 0 ].location( ) ); return; } if ( !input[ 2 ].isText( ) ) { errors.addNew( "program name (string or word) expected" , input[ 2 ].location( ) ); ok = false; } if ( input.size( ) > 3 ) { errors.addNew( "too many arguments" , input[ 3 ].location( ) ); } if ( !ok ) { return; } T_ProgramInstrNode& program{ instructions.add< T_ProgramInstrNode >( input[ 1 ] , input[ 2 ] ) }; program.location( ) = input[ 0 ].location( ); } } // namespace /*----------------------------------------------------------------------------*/ T_Parser::T_Parser( ) noexcept : A_PrivateImplementation( new T_ParserImpl_( &errors_ , &rootNode_ ) ) , errors_( 64 ) , rootNode_{} {} bool T_Parser::parse( T_SRDList const& input ) noexcept { errors_.clear( ); rootNode_ = NewOwned< T_RootNode >( ); for ( auto const& t : input ) { if ( t.type( ) == E_SRDTokenType::LIST && t.list( ).size( ) > 0 ) { p< T_ParserImpl_ >( ).parseFunction( t.list( ) ); } else { errors_.addNew( "function, init or frame list expected" , t.location( ) ); } } if ( errors_.empty( ) ) { T_SRDLocation loc( ([&input]() { if ( input.size( ) != 0 ) { return T_SRDLocation( input[ 0 ].location( ).source( ) , 1 , 1 ); } return T_SRDLocation{}; })( )); if ( !rootNode_->hasInit( ) ) { errors_.addNew( "no initialisation block" , loc ); } if ( !rootNode_->hasFrame( ) ) { errors_.addNew( "no initialisation block" , loc ); } } return errors_.empty( ); }