Parser - Prototyping call checks
This commit is contained in:
parent
67b644ac54
commit
14288e3c87
3 changed files with 198 additions and 28 deletions
1
opast.cc
1
opast.cc
|
@ -614,6 +614,7 @@ M_INSTR_( Call )
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& instr{ instructions.add< T_CallInstrNode >( input[ 1 ] ) };
|
auto& instr{ instructions.add< T_CallInstrNode >( input[ 1 ] ) };
|
||||||
|
instr.location( ) = input[ 0 ].location( );
|
||||||
for ( auto it = input.begin( ) + 2 ; it.valid( ) ; ++it ) {
|
for ( auto it = input.begin( ) + 2 ; it.valid( ) ; ++it ) {
|
||||||
instr.addArgument( parseExpression( instr , *it ) );
|
instr.addArgument( parseExpression( instr , *it ) );
|
||||||
}
|
}
|
||||||
|
|
5
opast.hh
5
opast.hh
|
@ -197,6 +197,8 @@ class T_RootNode : public A_Node
|
||||||
|
|
||||||
uint32_t nFunctions( ) const noexcept
|
uint32_t nFunctions( ) const noexcept
|
||||||
{ return functions_.size( ); }
|
{ return functions_.size( ); }
|
||||||
|
int32_t functionIndex( T_String const& name ) const noexcept
|
||||||
|
{ return functions_.indexOf( name ); }
|
||||||
A_FuncNode& function( const uint32_t index ) const noexcept
|
A_FuncNode& function( const uint32_t index ) const noexcept
|
||||||
{ return *functions_.values( )[ index ]; }
|
{ return *functions_.values( )[ index ]; }
|
||||||
};
|
};
|
||||||
|
@ -227,6 +229,9 @@ class T_FuncNode : public A_FuncNode
|
||||||
// of the initial argument.
|
// of the initial argument.
|
||||||
T_Optional< T_SRDLocation > addArgument(
|
T_Optional< T_SRDLocation > addArgument(
|
||||||
T_SRDToken const& token ) noexcept;
|
T_SRDToken const& token ) noexcept;
|
||||||
|
|
||||||
|
uint32_t arguments( ) const noexcept
|
||||||
|
{ return argNames_.size( ); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
218
parsercheck.cc
218
parsercheck.cc
|
@ -8,9 +8,98 @@ using namespace opast;
|
||||||
|
|
||||||
namespace {
|
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
|
// FIXME TESTING, MOVE THIS LATER
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename Enum ,
|
||||||
|
typename Storage = uint32_t
|
||||||
|
> class T_Flags
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Storage flags_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr T_Flags( ) noexcept
|
||||||
|
: flags_( 0 ) {}
|
||||||
|
|
||||||
|
constexpr T_Flags( T_Flags const& other ) noexcept
|
||||||
|
: flags_( other.flags_ ) { }
|
||||||
|
|
||||||
|
constexpr T_Flags( const Enum flag ) noexcept
|
||||||
|
: flags_( 1 << int( flag ) ) {}
|
||||||
|
|
||||||
|
constexpr T_Flags( std::initializer_list< Enum > flags ) noexcept
|
||||||
|
: flags_( 0 )
|
||||||
|
{
|
||||||
|
for ( auto f : flags ) {
|
||||||
|
flags_ |= ( 1 << int( f ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit constexpr T_Flags( const Storage flags ) noexcept
|
||||||
|
: flags_( flags ) { }
|
||||||
|
|
||||||
|
constexpr T_Flags operator |=( T_Flags other ) noexcept
|
||||||
|
{ flags_ |= other.flags_; return *this; }
|
||||||
|
constexpr T_Flags operator &=( T_Flags other ) noexcept
|
||||||
|
{ flags_ &= other.flags_; return *this; }
|
||||||
|
constexpr T_Flags operator ^=( T_Flags other ) noexcept
|
||||||
|
{ flags_ ^= other.flags_; return *this; }
|
||||||
|
|
||||||
|
constexpr T_Flags operator ~( ) const noexcept
|
||||||
|
{ return T_Flags( ~flags_ ); }
|
||||||
|
constexpr T_Flags operator &( T_Flags other ) const noexcept
|
||||||
|
{ return T_Flags( flags_ & other.flags_ ); }
|
||||||
|
constexpr T_Flags operator |( T_Flags other ) const noexcept
|
||||||
|
{ return T_Flags( flags_ & other.flags_ ); }
|
||||||
|
constexpr T_Flags operator ^( T_Flags other ) const noexcept
|
||||||
|
{ return T_Flags( flags_ ^ other.flags_ ); }
|
||||||
|
|
||||||
|
constexpr operator bool( ) const noexcept
|
||||||
|
{ return flags_ != 0; }
|
||||||
|
constexpr bool operator!( ) const noexcept
|
||||||
|
{ return flags_ == 0; }
|
||||||
|
explicit constexpr operator Storage( ) const noexcept
|
||||||
|
{ return flags_; }
|
||||||
|
|
||||||
|
constexpr bool operator ==( const T_Flags other ) const noexcept
|
||||||
|
{ return flags_ == other.flags_; }
|
||||||
|
constexpr bool operator !=( const T_Flags other ) const noexcept
|
||||||
|
{ return flags_ != other.flags_; }
|
||||||
|
|
||||||
|
constexpr bool isSet( const T_Flags value ) const noexcept
|
||||||
|
{ return ( *this & value ) == value; }
|
||||||
|
constexpr bool isClear( const T_Flags value ) const noexcept
|
||||||
|
{ return !( *this & value ); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
template< typename NodeType >
|
template< typename NodeType >
|
||||||
class T_Visitor
|
class T_Visitor
|
||||||
{
|
{
|
||||||
|
@ -45,7 +134,7 @@ class T_Visitor
|
||||||
|
|
||||||
public:
|
public:
|
||||||
T_Visitor( ) = delete;
|
T_Visitor( ) = delete;
|
||||||
T_Visitor( T_Visitor const& ) noexcept = default;
|
T_Visitor( T_Visitor const& ) = default;
|
||||||
T_Visitor( T_Visitor&& ) noexcept = default;
|
T_Visitor( T_Visitor&& ) noexcept = default;
|
||||||
|
|
||||||
explicit T_Visitor( F_NodeBrowser browser ) noexcept;
|
explicit T_Visitor( F_NodeBrowser browser ) noexcept;
|
||||||
|
@ -231,30 +320,111 @@ A_Node* OpASTBrowser(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*============================================================================*/
|
bool checkCalls( T_RootNode& root )
|
||||||
|
|
||||||
|
|
||||||
void PrintStreamError(
|
|
||||||
char const* const prefix ,
|
|
||||||
T_String const& name ,
|
|
||||||
X_StreamError const& error )
|
|
||||||
{
|
{
|
||||||
T_StringBuilder sb;
|
T_Visitor< A_Node > visitor( OpASTBrowser );
|
||||||
sb << prefix << " '" << name << "': " << error.what( );
|
T_Array< T_SRDError > errors;
|
||||||
if ( error.code( ) == E_StreamError::SYSTEM_ERROR ) {
|
|
||||||
sb << " (error code " << error.systemError( ) << ")";
|
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;
|
||||||
}
|
}
|
||||||
sb << '\n' << '\0';
|
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( ) );
|
fprintf( stderr , "%s" , sb.data( ) );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
T_Visitor< uint32_t > callGraphVisitor(
|
||||||
|
[&]( uint32_t& v , uint32_t child ) -> uint32_t* {
|
||||||
|
const uint32_t nc( calls.sizeOf( v ) );
|
||||||
|
if ( child < nc ) {
|
||||||
|
return &calls.get( v , child );
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
} );
|
||||||
|
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( ) ];
|
||||||
|
|
||||||
|
uint32_t initId( root.functionIndex( "*init*" ) );
|
||||||
|
callGraphVisitor.visit( initId , [&]( 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;
|
||||||
|
} );
|
||||||
|
uint32_t frameId( root.functionIndex( "*frame*" ) );
|
||||||
|
callGraphVisitor.visit( frameId , [&]( 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteSRDError(
|
/*============================================================================*/
|
||||||
T_StringBuilder& sb ,
|
|
||||||
T_SRDError const& error )
|
|
||||||
{
|
|
||||||
sb << error.location( ) << " - " << error.error( ) << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -297,13 +467,7 @@ int main( int argc , char** argv )
|
||||||
printf( "Success!\n" );
|
printf( "Success!\n" );
|
||||||
|
|
||||||
auto result( parser.result( ) );
|
auto result( parser.result( ) );
|
||||||
T_Visitor< A_Node > visitor( OpASTBrowser );
|
checkCalls( *result );
|
||||||
visitor.visit( *result , []( A_Node& node , bool enter ) {
|
|
||||||
if ( enter ) {
|
|
||||||
printf( "Enter node %p\n" , &node );
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} );
|
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
T_StringBuilder sb;
|
T_StringBuilder sb;
|
||||||
|
|
Loading…
Reference in a new issue