diff --git a/opast.cc b/opast.cc index 19f9ae8..5398795 100644 --- a/opast.cc +++ b/opast.cc @@ -1,5 +1,6 @@ #include "externals.hh" #include "opast.hh" +#include using namespace ebcl; using namespace opast; @@ -28,6 +29,149 @@ T_RootNode& A_Node::root( ) const noexcept const_cast< A_Node* >( node ) ); } +/*----------------------------------------------------------------------------*/ + +A_Node* opast::ASTVisitorBrowser( + A_Node& node , + const uint32_t child ) noexcept +{ + switch ( node.type( ) ) { + + // Root node + case A_Node::ROOT: { + auto& n( (T_RootNode&) node ); + if ( child < n.nFunctions( ) ) { + return &n.function( child ); + } + break; + } + + // Functions / special blocks + case A_Node::DECL_FN: case A_Node::DECL_INIT: case A_Node::DECL_FRAME: + if ( child == 0 ) { + auto& n( (A_FuncNode&) node ); + return &n.instructions( ); + } + break; + + // Instruction list + case A_Node::ILIST: { + auto& n( (T_InstrListNode&) node ); + if ( child < n.size( ) ) { + return &n.node( child ); + } + break; + } + + // Unary operators + case A_Node::EXPR_NEG: case A_Node::EXPR_INV: + case A_Node::EXPR_NOT: case A_Node::EXPR_SIN: + case A_Node::EXPR_COS: case A_Node::EXPR_TAN: + case A_Node::EXPR_SQRT: case A_Node::EXPR_EXP: + case A_Node::EXPR_LN: + { + auto& n( (T_UnaryOperatorNode&) node ); + if ( child == 0 && n.hasArgument( ) ) { + return &n.argument( ); + } + break; + } + + // Binary operators + case A_Node::EXPR_ADD: case A_Node::EXPR_SUB: + case A_Node::EXPR_MUL: case A_Node::EXPR_DIV: + case A_Node::EXPR_POW: + case A_Node::EXPR_CMP_EQ: case A_Node::EXPR_CMP_NE: + case A_Node::EXPR_CMP_GT: case A_Node::EXPR_CMP_GE: + case A_Node::EXPR_CMP_LT: case A_Node::EXPR_CMP_LE: + { + auto& n( (T_BinaryOperatorNode&) node ); + if ( child == 0 && n.hasLeft( ) ) { + return &n.left( ); + } + if ( child < 2 && n.hasRight( ) ) { + return &n.right( ); + } + break; + } + + // Nodes that do not have children + case A_Node::EXPR_ID: case A_Node::EXPR_CONST: + case A_Node::OP_PROGRAM: case A_Node::OP_PIPELINE: + case A_Node::OP_INPUT: + break; + + // Profile instruction + case A_Node::OP_PROFILE: + if ( child == 0 ) { + return &( ((T_ProfileInstrNode&) node).instructions( ) ); + } + break; + + // Call instruction + case A_Node::OP_CALL: + { + auto& n( (T_CallInstrNode&) node ); + if ( child < n.arguments( ) ) { + return &n.argument( child ); + } + break; + } + + // Conditional instruction + case A_Node::OP_COND: + { + auto& n( (T_CondInstrNode&) node ); + auto c = child; + if ( n.hasExpression( ) ) { + if ( c == 0 ) { + return &n.expression( ); + } + c --; + } + if ( !n.cases( ).empty( ) ) { + if ( c < n.cases( ).size( ) ) { + return &n.getCase( n.cases( )[ c ] ); + } + c -= n.cases( ).size( ); + } + if ( n.hasDefaultCase( ) && c == 0 ) { + return &n.defaultCase( ); + } + break; + } + + // Set instruction + case A_Node::OP_SET: + if ( child == 0 ) { + auto& n( (T_SetInstrNode&) node ); + if ( n.hasExpression( ) ) { + return &n.expression( ); + } + } + break; + + // Texture instruction + case A_Node::OP_TEXTURE: + { + auto& n( (T_TextureInstrNode&) node ); + auto c = child; + if ( n.hasWidth( ) ) { + if ( c == 0 ) { + return &n.width( ); + } + c --; + } + if ( n.hasHeight( ) && c == 0 ) { + return &n.height( ); + } + break; + } + + } + return nullptr; +} + /*= A_FuncNode ===============================================================*/ @@ -260,12 +404,23 @@ struct T_ParserImpl_ T_OwnPtr< T_RootNode >& root; T_Array< T_SRDError >& errors; + T_Visitor< A_Node > visitor{ opast::ASTVisitorBrowser }; + T_MultiArray< uint32_t > calls; + T_Visitor< uint32_t , uint32_t > callGraphVisitor{ + [this]( uint32_t v , uint32_t child ) -> T_Optional< uint32_t > { + const uint32_t nc( calls.sizeOf( v ) ); + if ( child < nc ) { + return calls.get( v , child ); + } + return {}; + } }; T_ParserImpl_( T_Array< T_SRDError >* errors , T_OwnPtr< T_RootNode >* root ) noexcept; // --------------------------------------------------------------------- + void parseTopLevel( T_SRDList const& list ) noexcept; void parseFunction( T_SRDList const& funcList ) noexcept; void parseFunctionArguments( T_FuncNode& function , @@ -317,12 +472,123 @@ struct T_ParserImpl_ T_UnaryOperatorNode::E_Operator op ) noexcept; }; -T_ParserImpl_::T_ParserImpl_( +/*----------------------------------------------------------------------------*/ + +inline T_ParserImpl_::T_ParserImpl_( T_Array< T_SRDError >* const errors , T_OwnPtr< T_RootNode >* const root ) noexcept : root( *root ) , errors( *errors ) { } +/*----------------------------------------------------------------------------*/ + +void 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( ) ); + } + } + 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( ) ) { + errors.addNew( "no initialisation block" , missingErrLoc ); + } + if ( !root->hasFrame( ) ) { + errors.addNew( "no initialisation block" , missingErrLoc ); + } + if ( !errors.empty( ) ) { + return; + } + + calls.clear( ); + uint32_t cfi; + for ( cfi = 0 ; cfi < root->nFunctions( ) ; cfi ++ ) { + calls.next( ); + visitor.visit( root->function( cfi ) , [&]( A_Node& node , bool exit ) -> bool { + if ( exit || dynamic_cast< A_ExpressionNode* >( &node ) ) { + return false; + } + if ( node.type( ) != A_Node::OP_CALL ) { + return true; + } + + auto& call( (T_CallInstrNode&) node ); + const auto callee( 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 ) ); + if ( fn.arguments( ) != call.arguments( ) ) { + T_StringBuilder sb; + sb << "function expects " << fn.arguments( ) + << " argument" << ( fn.arguments( ) == 1 ? "" : "s" ) + << ", " << call.arguments( ) + << " argument" << ( call.arguments( ) == 1 ? "" : "s" ) + << " provided"; + errors.addNew( std::move( sb ) , call.location( ) ); + } + } else { + errors.addNew( "unknown function" , call.idLocation( ) ); + } + return false; + } ); + } + if ( !errors.empty( ) ) { + return; + } + + T_InstrRestriction callInfo[ calls.size( ) ]; + callGraphVisitor.visit( root->functionIndex( "*init*" ) , + [&]( uint32_t id , const bool exit ) -> bool { + if ( exit || callInfo[ id ] & E_InstrRestriction::INIT ) { + return false; + } + callInfo[ id ] |= E_InstrRestriction::INIT; + return true; + } ); + callGraphVisitor.visit( root->functionIndex( "*frame*" ) , + [&]( uint32_t id , const bool exit ) -> bool { + if ( exit || callInfo[ id ] & E_InstrRestriction::FRAME ) { + return false; + } + callInfo[ id ] |= E_InstrRestriction::FRAME; + return true; + } ); + for ( auto i = 0u ; i < root->nFunctions( ) ; i ++ ) { + visitor.visit( root->function( i ) , + [&]( A_Node& node , bool exit ) { + if ( exit ) { + return false; + } + auto const* instr( dynamic_cast< A_InstructionNode const* >( &node ) ); + if ( instr && ( instr->restriction( ) & callInfo[ i ] ) ) { + T_StringBuilder sb; + sb << "instruction not allowed in " + << ( ( instr->restriction( ) & E_InstrRestriction::INIT ) + ? "initialisation" : "frame function" ); + errors.addNew( std::move( sb ) , instr->location( ) ); + } + return true; + } ); + } +} + void T_ParserImpl_::parseFunction( T_SRDList const& funcList ) noexcept { @@ -829,30 +1095,6 @@ bool T_Parser::parse( { 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 ); - } - } - + p< T_ParserImpl_ >( ).parseTopLevel( input ); return errors_.empty( ); } diff --git a/opast.hh b/opast.hh index a95c96f..69fa112 100644 --- a/opast.hh +++ b/opast.hh @@ -74,7 +74,12 @@ class A_Node T_RootNode& root( ) const noexcept; }; -/*----------------------------------------------------------------------------*/ +// Browser function to be used with T_Visitor +extern A_Node* ASTVisitorBrowser( + A_Node& node , + const uint32_t child ) noexcept; + +/*============================================================================*/ // Some instructions are restricted to either the initialisation code or the // frame code. diff --git a/parsercheck.cc b/parsercheck.cc index e30495d..a1a8cc1 100644 --- a/parsercheck.cc +++ b/parsercheck.cc @@ -2,7 +2,6 @@ #include "opast.hh" #include #include -#include using namespace ebcl; using namespace opast; @@ -35,277 +34,7 @@ void WriteSRDError( /*============================================================================*/ -// FIXME TESTING, MOVE THIS LATER -A_Node* OpASTBrowser( - A_Node& node , - const uint32_t child ) -{ - switch ( node.type( ) ) { - - // Root node - case A_Node::ROOT: { - auto& n( (T_RootNode&) node ); - if ( child < n.nFunctions( ) ) { - return &n.function( child ); - } - break; - } - - // Functions / special blocks - case A_Node::DECL_FN: case A_Node::DECL_INIT: case A_Node::DECL_FRAME: - if ( child == 0 ) { - auto& n( (A_FuncNode&) node ); - return &n.instructions( ); - } - break; - - // Instruction list - case A_Node::ILIST: { - auto& n( (T_InstrListNode&) node ); - if ( child < n.size( ) ) { - return &n.node( child ); - } - break; - } - - // Unary operators - case A_Node::EXPR_NEG: case A_Node::EXPR_INV: - case A_Node::EXPR_NOT: case A_Node::EXPR_SIN: - case A_Node::EXPR_COS: case A_Node::EXPR_TAN: - case A_Node::EXPR_SQRT: case A_Node::EXPR_EXP: - case A_Node::EXPR_LN: - { - auto& n( (T_UnaryOperatorNode&) node ); - if ( child == 0 && n.hasArgument( ) ) { - return &n.argument( ); - } - break; - } - - // Binary operators - case A_Node::EXPR_ADD: case A_Node::EXPR_SUB: - case A_Node::EXPR_MUL: case A_Node::EXPR_DIV: - case A_Node::EXPR_POW: - case A_Node::EXPR_CMP_EQ: case A_Node::EXPR_CMP_NE: - case A_Node::EXPR_CMP_GT: case A_Node::EXPR_CMP_GE: - case A_Node::EXPR_CMP_LT: case A_Node::EXPR_CMP_LE: - { - auto& n( (T_BinaryOperatorNode&) node ); - if ( child == 0 && n.hasLeft( ) ) { - return &n.left( ); - } - if ( child < 2 && n.hasRight( ) ) { - return &n.right( ); - } - break; - } - - // Nodes that do not have children - case A_Node::EXPR_ID: case A_Node::EXPR_CONST: - case A_Node::OP_PROGRAM: case A_Node::OP_PIPELINE: - case A_Node::OP_INPUT: - break; - - // Profile instruction - case A_Node::OP_PROFILE: - if ( child == 0 ) { - return &( ((T_ProfileInstrNode&) node).instructions( ) ); - } - break; - - // Call instruction - case A_Node::OP_CALL: - { - auto& n( (T_CallInstrNode&) node ); - if ( child < n.arguments( ) ) { - return &n.argument( child ); - } - break; - } - - // Conditional instruction - case A_Node::OP_COND: - { - auto& n( (T_CondInstrNode&) node ); - auto c = child; - if ( n.hasExpression( ) ) { - if ( c == 0 ) { - return &n.expression( ); - } - c --; - } - if ( !n.cases( ).empty( ) ) { - if ( c < n.cases( ).size( ) ) { - return &n.getCase( n.cases( )[ c ] ); - } - c -= n.cases( ).size( ); - } - if ( n.hasDefaultCase( ) && c == 0 ) { - return &n.defaultCase( ); - } - break; - } - - // Set instruction - case A_Node::OP_SET: - if ( child == 0 ) { - auto& n( (T_SetInstrNode&) node ); - if ( n.hasExpression( ) ) { - return &n.expression( ); - } - } - break; - - // Texture instruction - case A_Node::OP_TEXTURE: - { - auto& n( (T_TextureInstrNode&) node ); - auto c = child; - if ( n.hasWidth( ) ) { - if ( c == 0 ) { - return &n.width( ); - } - c --; - } - if ( n.hasHeight( ) && c == 0 ) { - return &n.height( ); - } - break; - } - - } - return nullptr; -} - - -bool checkCalls( T_RootNode& root ) -{ - T_Visitor< A_Node > visitor( OpASTBrowser ); - T_Array< T_SRDError > errors; - - T_MultiArray< uint32_t > calls; - uint32_t cfi; - for ( cfi = 0 ; cfi < root.nFunctions( ) ; cfi ++ ) { - calls.next( ); - visitor.visit( root.function( cfi ) , [&]( A_Node& node , bool exit ) -> bool { - if ( exit || dynamic_cast< A_ExpressionNode* >( &node ) ) { - return false; - } - if ( node.type( ) != A_Node::OP_CALL ) { - return true; - } - - auto& call( (T_CallInstrNode&) node ); - const auto callee( 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 ) ); - if ( fn.arguments( ) != call.arguments( ) ) { - T_StringBuilder sb; - sb << "function expects " << fn.arguments( ) - << " argument" << ( fn.arguments( ) == 1 ? "" : "s" ) - << ", " << call.arguments( ) - << " argument" << ( call.arguments( ) == 1 ? "" : "s" ) - << " provided"; - errors.addNew( std::move( sb ) , call.location( ) ); - } - } else { - errors.addNew( "unknown function" , call.idLocation( ) ); - } - return false; - } ); - } - - if ( !errors.empty( ) ) { - T_StringBuilder sb; - for ( auto const& err : errors ) { - WriteSRDError( sb , err ); - } - sb << "Parser failed\n" << '\0'; - fprintf( stderr , "%s" , sb.data( ) ); - return false; - } - - T_Visitor< uint32_t , uint32_t > callGraphVisitor( - [&]( uint32_t v , uint32_t child ) -> T_Optional< uint32_t > { - const uint32_t nc( calls.sizeOf( v ) ); - if ( child < nc ) { - return calls.get( v , child ); - } - return {}; - } ); - T_InstrRestriction callInfo[ calls.size( ) ]; - - callGraphVisitor.visit( root.functionIndex( "*init*" ) , - [&]( uint32_t id , const bool exit ) -> bool { - if ( exit || callInfo[ id ] & E_InstrRestriction::INIT ) { - return false; - } - callInfo[ id ] |= E_InstrRestriction::INIT; - return true; - } ); - callGraphVisitor.visit( root.functionIndex( "*frame*" ) , - [&]( uint32_t id , const bool exit ) -> bool { - if ( exit || callInfo[ id ] & E_InstrRestriction::FRAME ) { - return false; - } - callInfo[ id ] |= E_InstrRestriction::FRAME; - return true; - } ); - - for ( auto i = 0u ; i < root.nFunctions( ) ; i ++ ) { - visitor.visit( root.function( i ) , - [&]( A_Node& node , bool exit ) { - if ( exit ) { - return false; - } - - auto const* instr( dynamic_cast< A_InstructionNode const* >( &node ) ); - if ( instr && ( instr->restriction( ) & callInfo[ i ] ) ) { - T_StringBuilder sb; - sb << "instruction not allowed in " - << ( ( instr->restriction( ) & E_InstrRestriction::INIT ) - ? "initialisation" : "frame function" ); - errors.addNew( std::move( sb ) , instr->location( ) ); - } - return true; - } ); - } - - if ( !errors.empty( ) ) { - T_StringBuilder sb; - for ( auto const& err : errors ) { - WriteSRDError( sb , err ); - } - sb << "Parser failed\n" << '\0'; - fprintf( stderr , "%s" , sb.data( ) ); - return false; - } - - T_StringBuilder sb; - for ( auto callerId = 0u ; callerId < calls.size( ) ; callerId ++ ) { - const auto nCallees( calls.sizeOf( callerId ) ); - if ( !nCallees ) { - continue; - } - sb << root.function( callerId ).name( ) << " calls"; - for ( auto i = 0u ; i < nCallees ; i ++ ) { - auto const& callee( root.function( calls.get( callerId , i ) ) ); - sb << ' ' << callee.name( ); - - } - sb << '\n'; - } - sb << '\0'; - printf( "%s" , sb.data( ) ); - - return true; -} /*============================================================================*/ @@ -348,9 +77,6 @@ int main( int argc , char** argv ) T_Parser parser; if ( parser.parse( srdOut.list( ) ) ) { printf( "Success!\n" ); - - auto result( parser.result( ) ); - checkCalls( *result ); return 0; } else { T_StringBuilder sb;