Parser - Initial ugly impl of argument type resolution

This commit is contained in:
Emmanuel BENOîT 2017-11-11 15:05:20 +01:00
parent 3e184f5796
commit 84cdfa38de
2 changed files with 173 additions and 1 deletions

View file

@ -253,6 +253,26 @@ class A_FuncNode : public A_Node
auto const* const ptr( locals_.get( id ) );
return ptr && ptr->argument;
}
uint32_t getLocalIndex(
T_String const& name ) const noexcept
{ return locals_.indexOf( name ); }
T_String const& getLocalName(
const uint32_t index ) const noexcept
{ return locals_[ index ].name; }
E_DataType getLocalType(
T_String const& name ) const noexcept
{ return locals_.get( name )->type; }
E_DataType getLocalType(
const uint32_t index ) const noexcept
{ return locals_[ index ].type; }
void setLocalType(
const uint32_t index ,
const E_DataType type ) noexcept
{ locals_[ index ].type = type; }
};
using P_InstrListNode = T_OwnPtr< T_InstrListNode >;

View file

@ -174,6 +174,7 @@ struct T_ParserImpl_
bool checkInstructionRestrictions( ) noexcept;
bool collectGlobalTypes( ) noexcept;
bool checkLocalVariables( ) noexcept;
bool checkArgumentTypes( ) noexcept;
bool checkIdentifierExpressions( ) noexcept;
// ---------------------------------------------------------------------
@ -288,6 +289,7 @@ void T_ParserImpl_::main(
&& checkInstructionRestrictions( )
&& checkLocalVariables( )
&& collectGlobalTypes( )
&& checkArgumentTypes( )
&& checkIdentifierExpressions( );
}
@ -486,6 +488,12 @@ bool T_ParserImpl_::collectGlobalTypes( ) noexcept
}
assert( dt != E_DataType::UNKNOWN );
#ifdef INVASIVE_TRACES
esb << "id " << id << " as " << dt << " at " << location << '\n' << '\0';
printf( "%s" , esb.data( ) );
esb.clear( );
#endif
if ( function.hasLocal( id ) ) {
if ( function.isArgument( id ) ) {
errors.addNew( "trying to override argument",
@ -527,6 +535,150 @@ bool T_ParserImpl_::collectGlobalTypes( ) noexcept
return errors.empty( );
}
bool T_ParserImpl_::checkArgumentTypes( ) noexcept
{
// Find functions for which arguments types need to be resolved
const auto nFunctions( output->root.nFunctions( ) );
bool argsResolved[ nFunctions ];
uint32_t nSolved = 0;
for ( auto i = 0u ; i < nFunctions ; i ++ ) {
auto& fn( output->root.function( i ) );
if ( fn.type( ) == A_Node::DECL_FN ) {
auto& rfn( dynamic_cast< T_FuncNode& >( fn ) );
argsResolved[ i ] = rfn.arguments( ) == 0;
} else {
argsResolved[ i ] = true;
}
if ( argsResolved[ i ] ) {
nSolved ++;
}
}
// No functions use arguments -> we're done.
if ( nSolved == nFunctions ) {
return true;
}
// Find all calls with arguments
T_MultiArray< T_CallInstrNode* > callInstuctions;
for ( auto i = 0u ; i < nFunctions ; i ++ ) {
callInstuctions.next( );
visitor.visit( output->root.function( i ) , [&]( A_Node& node , const bool exit ) {
if ( exit || node.type( ) != A_Node::OP_CALL ) {
return true;
}
auto& call( dynamic_cast< T_CallInstrNode& >( node ) );
if ( call.arguments( ) != 0 ) {
callInstuctions.add( &call );
}
return false;
} );
}
#ifdef INVASIVE_TRACES
T_StringBuilder tracer;
#define TRACE( x ) do { tracer.clear( ) << x << '\0'; printf( "%s\n" , tracer.data( ) ); } while (0)
#else
#define TRACE( x )
#endif
T_StringBuilder esb;
bool changed = true;
while ( changed ) {
changed = false;
// Go through all functions for which argument types have been
// resolved and check all calls.
for ( auto i = 0u ; i < nFunctions ; i ++ ) {
auto& f( output->root.function( i ) );
TRACE( "about to check function " << f.name( ) );
if ( !argsResolved[ i ] ) {
TRACE( " -> arguments not resolved, skipped" );
continue;
}
TRACE( " -> " << callInstuctions.sizeOf( i ) << " calls w/ arguments" );
for ( auto c = 0u ; c < callInstuctions.sizeOf( i ) ; c ++ ) {
auto const* call( callInstuctions.get( i , c ) );
if ( ! call ) {
continue;
}
auto const& called( call->id( ) );
const auto calledIdx( output->root.functionIndex( called ) );
auto& calledFn( dynamic_cast< T_FuncNode& >(
output->root.function( calledIdx ) ) );
TRACE( " -> checking call to " << called << " (idx " << calledIdx
<< ") at " << call->location( ) );
assert( call->arguments( ) == calledFn.arguments( ) );
if ( argsResolved[ calledIdx ] ) {
TRACE( " argument types already resolved, checking" );
for ( auto a = 0u ; a < call->arguments( ) ; a ++ ) {
auto& arg( call->argument( a ) );
E_DataType ndt{ E_DataType::UNKNOWN };
if ( arg.type( ) == A_Node::EXPR_ID ) {
T_String const& id( dynamic_cast< T_IdentifierExprNode& >( arg ).id( ) );
if ( f.hasLocal( id ) ) {
ndt = f.getLocalType( id );
} else {
auto const* const ptr( output->types.get( id ) );
if ( ptr ) {
ndt = *ptr;
} else {
errors.addNew( "unknown identifier" ,
arg.location( ) );
}
}
} else {
ndt = E_DataType::VARIABLE;
}
TRACE( " [" << a << "] " << ndt );
if ( ndt != E_DataType::UNKNOWN && calledFn.getLocalType( a ) != ndt ) {
esb << "argument " << ( a + 1 ) << " of function '"
<< called << "' should be a "
<< calledFn.getLocalType( a )
<< " but a " << ndt << " is being passed";
errors.addNew( std::move( esb ) , arg.location( ) );
}
}
changed = true;
} else {
TRACE( " resolving arguments" );
bool ok = true;
for ( auto a = 0u ; a < call->arguments( ) ; a ++ ) {
auto& arg( call->argument( a ) );
E_DataType ndt{ E_DataType::UNKNOWN };
if ( arg.type( ) == A_Node::EXPR_ID ) {
T_String const& id( dynamic_cast< T_IdentifierExprNode& >( arg ).id( ) );
if ( f.hasLocal( id ) ) {
ndt = f.getLocalType( id );
} else {
auto const* const ptr( output->types.get( id ) );
if ( ptr ) {
ndt = *ptr;
} else {
errors.addNew( "unknown identifier" ,
arg.location( ) );
ok = false;
}
}
} else {
ndt = E_DataType::VARIABLE;
}
TRACE( " [" << a << "] " << ndt );
calledFn.setLocalType( a , ndt );
}
argsResolved[ calledIdx ] = ok;
changed = changed || ok;
}
callInstuctions.get( i , c ) = nullptr;
}
}
}
return errors.empty( );
}
bool T_ParserImpl_::checkIdentifierExpressions( ) noexcept
{
uint32_t cfi;
@ -1059,7 +1211,7 @@ M_INSTR_( Pipeline )
return;
}
auto& pipeline{ instructions.add< T_PipelineInstrNode >( input[ 0 ] ) };
auto& pipeline{ instructions.add< T_PipelineInstrNode >( input[ 1 ] ) };
pipeline.location( ) = input[ 0 ].location( );
const auto nMax{ std::min( input.size( ) , 8u ) };