#include "externals.hh" #include "opast.hh" #include #include #include using namespace ebcl; using namespace opast; namespace { /*============================================================================*/ void PrintStreamError( char const* const prefix , T_String const& name , X_StreamError const& error ) { T_StringBuilder sb; sb << prefix << " '" << name << "': " << error.what( ); if ( error.code( ) == E_StreamError::SYSTEM_ERROR ) { sb << " (error code " << error.systemError( ) << ")"; } sb << '\n' << '\0'; fprintf( stderr , "%s" , sb.data( ) ); } void WriteSRDError( T_StringBuilder& sb , T_SRDError const& error ) { sb << error.location( ) << " - " << error.error( ) << "\n"; } /*============================================================================*/ // 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 {}; } ); enum class E_CallInfo_ { INIT_CHECKED , FRAME_CHECKED , INIT_CALLED , FRAME_CALLED }; using T_CallInfo_ = T_Flags< E_CallInfo_ , uint8_t >; T_CallInfo_ callInfo[ calls.size( ) ]; callGraphVisitor.visit( root.functionIndex( "*init*" ) , [&]( uint32_t id , const bool exit ) -> bool { if ( exit || callInfo[ id ] & E_CallInfo_::INIT_CALLED ) { return false; } callInfo[ id ] |= E_CallInfo_::INIT_CALLED; return true; } ); callGraphVisitor.visit( root.functionIndex( "*frame*" ) , [&]( uint32_t id , const bool exit ) -> bool { if ( exit || callInfo[ id ] & E_CallInfo_::FRAME_CALLED ) { return false; } callInfo[ id ] |= E_CallInfo_::FRAME_CALLED; return true; } ); 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; } /*============================================================================*/ } // namespace int main( int argc , char** argv ) { // Open file const T_String inputName( argc >= 2 ? argv[ 1 ] : "demo.srd" ); T_File input( inputName , E_FileMode::READ_ONLY ); try { input.open( ); } catch ( X_StreamError const& e ) { PrintStreamError( "Could not open" , inputName , e ); return 1; } // Load SRD data T_SRDMemoryTarget srdOut; srdOut.clearComments( true ).clearFlushToken( true ); try { T_SRDTextReader srdReader{ srdOut }; T_FileInputStream fis{ input }; srdReader.read( inputName , fis ); } catch ( X_StreamError const& e ) { PrintStreamError( "Could not open" , inputName , e ); return 1; } catch ( X_SRDErrors const& e ) { T_StringBuilder sb; const auto nErrors( e.errors.size( ) ); for ( auto i = 0u ; i < nErrors ; i ++ ) { WriteSRDError( sb , e.errors[ i ] ); } sb << "No parsing happened due to format errors\n" << '\0'; fprintf( stderr , "%s" , sb.data( ) ); return 2; } // Parse the fuck T_Parser parser; if ( parser.parse( srdOut.list( ) ) ) { printf( "Success!\n" ); auto result( parser.result( ) ); checkCalls( *result ); return 0; } else { T_StringBuilder sb; for ( auto const& err : parser.errors( ) ) { WriteSRDError( sb , err ); } sb << "Parser failed\n" << '\0'; fprintf( stderr , "%s" , sb.data( ) ); return 3; } }