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 ] ) };
|
||||
instr.location( ) = input[ 0 ].location( );
|
||||
for ( auto it = input.begin( ) + 2 ; it.valid( ) ; ++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
|
||||
{ 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
|
||||
{ return *functions_.values( )[ index ]; }
|
||||
};
|
||||
|
@ -227,6 +229,9 @@ class T_FuncNode : public A_FuncNode
|
|||
// of the initial argument.
|
||||
T_Optional< T_SRDLocation > addArgument(
|
||||
T_SRDToken const& token ) noexcept;
|
||||
|
||||
uint32_t arguments( ) const noexcept
|
||||
{ return argNames_.size( ); }
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
|
212
parsercheck.cc
212
parsercheck.cc
|
@ -8,9 +8,98 @@ 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
|
||||
|
||||
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 >
|
||||
class T_Visitor
|
||||
{
|
||||
|
@ -45,7 +134,7 @@ class T_Visitor
|
|||
|
||||
public:
|
||||
T_Visitor( ) = delete;
|
||||
T_Visitor( T_Visitor const& ) noexcept = default;
|
||||
T_Visitor( T_Visitor const& ) = default;
|
||||
T_Visitor( T_Visitor&& ) noexcept = default;
|
||||
|
||||
explicit T_Visitor( F_NodeBrowser browser ) noexcept;
|
||||
|
@ -231,30 +320,111 @@ A_Node* OpASTBrowser(
|
|||
}
|
||||
|
||||
|
||||
/*============================================================================*/
|
||||
|
||||
|
||||
void PrintStreamError(
|
||||
char const* const prefix ,
|
||||
T_String const& name ,
|
||||
X_StreamError const& error )
|
||||
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 << prefix << " '" << name << "': " << error.what( );
|
||||
if ( error.code( ) == E_StreamError::SYSTEM_ERROR ) {
|
||||
sb << " (error code " << error.systemError( ) << ")";
|
||||
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( ) );
|
||||
}
|
||||
sb << '\n' << '\0';
|
||||
} 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;
|
||||
}
|
||||
|
||||
void WriteSRDError(
|
||||
T_StringBuilder& sb ,
|
||||
T_SRDError const& error )
|
||||
{
|
||||
sb << error.location( ) << " - " << error.error( ) << "\n";
|
||||
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;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -297,13 +467,7 @@ int main( int argc , char** argv )
|
|||
printf( "Success!\n" );
|
||||
|
||||
auto result( parser.result( ) );
|
||||
T_Visitor< A_Node > visitor( OpASTBrowser );
|
||||
visitor.visit( *result , []( A_Node& node , bool enter ) {
|
||||
if ( enter ) {
|
||||
printf( "Enter node %p\n" , &node );
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
checkCalls( *result );
|
||||
return 0;
|
||||
} else {
|
||||
T_StringBuilder sb;
|
||||
|
|
Loading…
Reference in a new issue