diff --git a/demo.srd b/demo.srd index 9cc6023..7576a85 100644 --- a/demo.srd +++ b/demo.srd @@ -31,8 +31,8 @@ (fn dof-init () # Sampler used for the inputs (sampler dof-sampler - (sampling clamp-edge) (mipmaps no) + (wrapping clamp-edge) (sampling linear) (lod 0 0) ) diff --git a/opast.cc b/opast.cc index 5ac8adf..abc23e0 100644 --- a/opast.cc +++ b/opast.cc @@ -27,19 +27,30 @@ T_RootNode& A_Node::root( ) const noexcept 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*" ) + 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 ) + : A_Node( DECL_FN , root ) , name_( name ) , + instructions_( *this ) { } @@ -112,19 +123,119 @@ T_Optional< T_SRDLocation > T_FuncNode::addArgument( } +/*= 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_( @@ -160,7 +271,8 @@ void T_ParserImpl_::parseFunction( } fn = NewOwned< T_FuncNode >( funcList[ 1 ].stringValue( ) , *root ); - // TODO parseFunctionArguments( funcList[ 2 ] ); + parseFunctionArguments( dynamic_cast< T_FuncNode& >( *fn ) , + funcList[ 2 ] ); } else { fn = NewOwned< T_SpecialFuncNode >( ftw == "init" , *root ); } @@ -185,8 +297,181 @@ void T_ParserImpl_::parseFunction( errors.addNew( std::move( esb ) , fw.location( ) ); } - // TODO parseInstructions( fn->instructions , funcList , - // ftw == "fn" ? 3 : 1 ); + 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 @@ -208,10 +493,25 @@ bool T_Parser::parse( if ( t.type( ) == E_SRDTokenType::LIST && t.list( ).size( ) > 0 ) { p< T_ParserImpl_ >( ).parseFunction( t.list( ) ); } else { - errors_.addNew( "Function or special block expected" , + 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( ); } diff --git a/opast.hh b/opast.hh index f0ac73f..d059db1 100644 --- a/opast.hh +++ b/opast.hh @@ -16,20 +16,24 @@ class A_Node { public: enum E_Type { - ROOT , + ROOT , // Root node + ILIST , // Instruction list // - DECL_INIT , - DECL_FRAME , - DECL_FN , + DECL_INIT , // Initialisation block + DECL_FRAME , // Frame block + DECL_FN , // Function // - OP_SET , + OP_PIPELINE , // Shader pipeline declaration + OP_PROFILE , // Profiling block + OP_PROGRAM , // Shader program declaration + OP_SET , // Set instruction // - EXPR_ADD , - EXPR_MUL , - EXPR_SUB , - EXPR_DIV , - EXPR_VAR , - EXPR_CONST , + EXPR_ADD , // Binary ops: add + EXPR_MUL , // Binary ops: mul + EXPR_SUB , // Binary ops: sub + EXPR_DIV , // Binary ops: div + EXPR_VAR , // Variable access + EXPR_CONST , // Numeric constant }; private: @@ -60,11 +64,49 @@ class A_Node /*----------------------------------------------------------------------------*/ +class A_InstructionNode : public A_Node +{ + protected: + A_InstructionNode( E_Type type , + A_Node& parent ) noexcept + : A_Node( type , &parent ) + {} +}; + +// Nodes that store lists of instructions +class T_InstrListNode : public A_Node +{ + private: + T_Array< T_OwnPtr< A_InstructionNode > > instructions_; + + public: + T_InstrListNode( A_Node& parent ) noexcept; + + template< + typename IType , + typename... ArgTypes + > IType& add( ArgTypes&&... args ); +}; + +template< + typename IType , + typename... ArgTypes +> inline IType& T_InstrListNode::add( + ArgTypes&&... args ) +{ + instructions_.add( NewOwned< IType >( *this , + std::forward< ArgTypes >( args ) ... ) ); + return (IType&) *instructions_.last( ); +} + +/*----------------------------------------------------------------------------*/ + // Function-like nodes class A_FuncNode : public A_Node { private: T_String name_; + T_InstrListNode instructions_; protected: // For init or frame entry points. @@ -80,6 +122,11 @@ class A_FuncNode : public A_Node public: T_String const& name( ) const noexcept { return name_; } + + T_InstrListNode& instructions( ) noexcept + { return instructions_; } + T_InstrListNode const& instructions( ) const noexcept + { return instructions_; } }; // Root node, keeps track of the whole tree and related data (function table, @@ -92,6 +139,8 @@ class T_RootNode : public A_Node public: T_RootNode( ) noexcept; + // --------------------------------------------------------------------- + // Return type for addFunction. We'll always return a reference to a // function node (which may or may not be the same as the initial one, // if there were duplicates), and we'll return the location of the @@ -117,6 +166,15 @@ class T_RootNode : public A_Node // entry will be added to the table instead). T_AddFunctionResult addFunction( T_OwnPtr< A_FuncNode >& function ) noexcept; + + // --------------------------------------------------------------------- + + bool hasFunction( T_String const& name ) noexcept + { return functions_.contains( name ); } + bool hasInit( ) noexcept + { return hasFunction( "*init*" ); } + bool hasFrame( ) noexcept + { return hasFunction( "*frame*" ); } }; /*----------------------------------------------------------------------------*/ @@ -147,7 +205,78 @@ class T_FuncNode : public A_FuncNode T_SRDToken const& token ) noexcept; }; -/*----------------------------------------------------------------------------*/ +/*============================================================================*/ + +// Pipeline declaration instruction +class T_PipelineInstrNode : public A_InstructionNode +{ + private: + T_String id_; + T_SRDLocation idLocation_; + T_StaticArray< T_String , 6 > pids_; + T_StaticArray< T_SRDLocation , 6 > pidLocations_; + + public: + T_PipelineInstrNode( + T_InstrListNode& parent , + T_SRDToken const& idToken ) noexcept; + explicit T_PipelineInstrNode( + T_InstrListNode& parent ) noexcept; + + // Add a program identifier. The token is assumed to be a valid word, + // and the list of programs is assumed not to be full. If the identifer + // is already in the pipeline's list, the location of the first + // occurrence will be returned. + T_Optional< T_SRDLocation > addProgram( + T_SRDToken const& pidToken ) noexcept; + + uint32_t size( ) const noexcept + { return pids_.size( ); } + + T_String const& program( const uint32_t index ) const noexcept + { return pids_[ index ]; } + T_SRDLocation const& pLocation( const uint32_t index ) const noexcept + { return pidLocations_[ index ]; } +}; + +// Profiling instruction +class T_ProfileInstrNode : public A_InstructionNode +{ + private: + T_String text_; + T_InstrListNode instructions_; + + public: + T_ProfileInstrNode( + T_InstrListNode& parent , + T_String const& text ) noexcept; + + T_String const& text( ) const + { return text_; } + + T_InstrListNode& instructions( ) noexcept + { return instructions_; } + T_InstrListNode const& instructions( ) const noexcept + { return instructions_; } +}; + +// Program loader instruction +class T_ProgramInstrNode : public A_InstructionNode +{ + private: + T_String id_; + T_SRDLocation idLocation_; + T_String path_; + T_SRDLocation pathLocation_; + + public: + T_ProgramInstrNode( + T_InstrListNode& parent , + T_SRDToken const& idToken , + T_SRDToken const& pathToken ) noexcept; +}; + +/*============================================================================*/ class T_Parser : public A_PrivateImplementation {