demotool/parsercheck.cc

364 lines
8.7 KiB
C++

#include "externals.hh"
#include "opast.hh"
#include <ebcl/Files.hh>
#include <ebcl/SRDText.hh>
#include <ebcl/Algorithms.hh>
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 {};
} );
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;
}
/*============================================================================*/
} // 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;
}
}