demotool/opast.cc

517 lines
14 KiB
C++

#include "opast.hh"
using namespace ebcl;
using namespace opast;
/*= A_Node ===================================================================*/
A_Node::A_Node( const E_Type type ,
A_Node* const parent ) noexcept
: type_( type ) , parent_( parent )
{
assert( ( type == ROOT && !parent ) || ( type != ROOT && parent ) );
}
A_Node::~A_Node( ) { }
T_RootNode& A_Node::root( ) const noexcept
{
A_Node const* node( this );
while ( node->parent_ ) {
node = node->parent_;
}
assert( node );
assert( node->type_ == ROOT );
return *dynamic_cast< T_RootNode* >(
const_cast< A_Node* >( node ) );
}
/*= T_InstrListNode ==========================================================*/
T_InstrListNode::T_InstrListNode(
A_Node& parent ) noexcept
: A_Node( ILIST , &parent )
{ }
/*= A_FuncNode ===============================================================*/
A_FuncNode::A_FuncNode(
const bool isInit ,
T_RootNode* const root ) noexcept
: A_Node( isInit ? DECL_INIT : DECL_FRAME , root ) ,
name_( isInit ? "*init*" : "*frame*" ) ,
instructions_( *this )
{ }
A_FuncNode::A_FuncNode(
T_String const& name ,
T_RootNode* const root ) noexcept
: A_Node( DECL_FN , root ) , name_( name ) ,
instructions_( *this )
{ }
/*= T_RootNode ===============================================================*/
T_RootNode::T_RootNode( ) noexcept
: A_Node( ROOT , nullptr ) , functions_(
[]( T_OwnPtr< A_FuncNode > const& f ) {
return f->name( );
} )
{ }
T_RootNode::T_AddFunctionResult T_RootNode::addFunction(
T_OwnPtr< A_FuncNode >& function ) noexcept
{
T_String const& fn( function->name( ) );
auto const* const pf( functions_.get( fn ) );
if ( !pf ) {
auto* const rv( function.get( ) );
functions_.add( std::move( function ) );
return *rv;
}
T_String dfn;
uint32_t dupCtr( 0 );
do {
T_StringBuilder fnsb;
fnsb << fn << " dup " << dupCtr ++;
dfn = std::move( fnsb );
} while ( functions_.contains( dfn ) );
T_OwnPtr< A_FuncNode > df( NewOwned< T_FuncNode >( dfn , *this ) );
auto* const rv( df.get( ) );
functions_.add( std::move( df ) );
return T_AddFunctionResult{ *rv , (*pf)->location( ) };
}
/*= T_SpecialFuncNode ========================================================*/
T_SpecialFuncNode::T_SpecialFuncNode(
bool isInit ,
T_RootNode& parent ) noexcept
: A_FuncNode( isInit , &parent )
{ }
/*= T_FuncNode ===============================================================*/
T_FuncNode::T_FuncNode(
T_String const& name ,
T_RootNode& parent ) noexcept
: A_FuncNode( name , &parent )
{ }
T_Optional< T_SRDLocation > T_FuncNode::addArgument(
T_SRDToken const& token ) noexcept
{
assert( token.type( ) == E_SRDTokenType::WORD );
assert( token.hasLocation( ) );
const auto pnp( argNames_.indexOf( token.stringValue( ) ) );
if ( pnp != -1 ) {
return argLocations_[ pnp ];
}
argNames_.add( token.stringValue( ) );
argLocations_.add( token.location( ) );
return {};
}
/*= T_PipelineInstrNode ======================================================*/
T_PipelineInstrNode::T_PipelineInstrNode(
T_InstrListNode& parent ,
T_SRDToken const& idToken ) noexcept
: A_InstructionNode( OP_PIPELINE , parent ) ,
id_( idToken.stringValue( ) ) ,
idLocation_( idToken.location( ) )
{ }
T_PipelineInstrNode::T_PipelineInstrNode(
T_InstrListNode& parent ) noexcept
: A_InstructionNode( OP_PIPELINE , parent ) ,
id_( "*invalid*" ) ,
idLocation_{ }
{ }
T_Optional< T_SRDLocation > T_PipelineInstrNode::addProgram(
T_SRDToken const& pidToken ) noexcept
{
T_String const& name( pidToken.stringValue( ) );
const auto pIndex( pids_.indexOf( name ) );
if ( pIndex != -1 ) {
return pidLocations_[ pIndex ];
}
pids_.add( name );
pidLocations_.add( pidToken.location( ) );
return {};
}
/*= T_ProfileInstrNode =======================================================*/
T_ProfileInstrNode::T_ProfileInstrNode(
T_InstrListNode& parent ,
T_String const& text ) noexcept
: A_InstructionNode( OP_PROFILE , parent ) ,
text_( text ) ,
instructions_( *this )
{ }
/*= T_ProgramInstrNode =======================================================*/
T_ProgramInstrNode::T_ProgramInstrNode(
T_InstrListNode& parent ,
T_SRDToken const& idToken ,
T_SRDToken const& pathToken ) noexcept
: A_InstructionNode( OP_PROGRAM , parent ) ,
id_( idToken.stringValue( ) ) ,
idLocation_( idToken.location( ) ) ,
path_( pathToken.stringValue( ) ) ,
pathLocation_( pathToken.location( ) )
{ }
/*= T_Parser =================================================================*/
namespace {
struct T_ParserImpl_
{
enum class E_InstrType {
IF ,
PIPELINE ,
PROFILE ,
PROGRAM ,
};
const T_KeyValueTable< T_String , E_InstrType > instrMap{ ([]() {
T_KeyValueTable< T_String , E_InstrType > temp;
const auto add{ [&temp]( char const* name , E_InstrType it ) {
temp.add( T_String::Pooled( name ) , it );
} };
add( "if" , E_InstrType::IF );
add( "pipeline" , E_InstrType::PIPELINE );
add( "profiling" , E_InstrType::PROFILE );
add( "program" , E_InstrType::PROGRAM );
return temp;
})( ) };
// ---------------------------------------------------------------------
T_OwnPtr< T_RootNode >& root;
T_Array< T_SRDError >& errors;
T_ParserImpl_( T_Array< T_SRDError >* errors ,
T_OwnPtr< T_RootNode >* root ) noexcept;
// ---------------------------------------------------------------------
void parseFunction( T_SRDList const& funcList ) noexcept;
void parseFunctionArguments(
T_FuncNode& function ,
T_SRDToken const& argsToken ) noexcept;
void parseInstructions(
T_InstrListNode& instructions ,
T_SRDList const& input ,
uint32_t start ) noexcept;
// ---------------------------------------------------------------------
void parsePipelineInstruction(
T_InstrListNode& instructions ,
T_SRDList const& input ) noexcept;
void parseProfileInstruction(
T_InstrListNode& instructions ,
T_SRDList const& input ) noexcept;
void parseProgramInstruction(
T_InstrListNode& instructions ,
T_SRDList const& input ) noexcept;
};
T_ParserImpl_::T_ParserImpl_(
T_Array< T_SRDError >* const errors ,
T_OwnPtr< T_RootNode >* const root ) noexcept
: root( *root ) , errors( *errors )
{ }
void T_ParserImpl_::parseFunction(
T_SRDList const& funcList ) noexcept
{
assert( funcList.size( ) != 0 );
auto const& fw( funcList[ 0 ] );
if ( fw.type( ) != E_SRDTokenType::WORD
|| ( fw.stringValue( ) != "init" && fw.stringValue( ) != "frame"
&& fw.stringValue( ) != "fn" ) ) {
errors.addNew( "init, frame or fn expected" , fw.location( ) );
return;
}
T_String const& ftw( fw.stringValue( ) );
T_OwnPtr< A_FuncNode > fn;
if ( ftw == "fn" ) {
if ( funcList.size( ) < 3 ) {
errors.addNew( "function name and arguments expected" ,
fw.location( ) );
return;
}
if ( funcList[ 1 ].type( ) != E_SRDTokenType::WORD ) {
errors.addNew( "function name expected" , funcList[ 1 ].location( ) );
return;
}
fn = NewOwned< T_FuncNode >( funcList[ 1 ].stringValue( ) , *root );
parseFunctionArguments( dynamic_cast< T_FuncNode& >( *fn ) ,
funcList[ 2 ] );
} else {
fn = NewOwned< T_SpecialFuncNode >( ftw == "init" , *root );
}
fn->location( ) = fw.location( );
const auto af( root->addFunction( fn ) );
if ( af.dupLocation.present( ) ) {
T_StringBuilder esb( "duplicate " );
switch ( fn->type( ) ) {
case A_Node::DECL_FN:
esb << "function '" << fn->name( ) << "'";
break;
case A_Node::DECL_INIT:
esb << "initialisation function";
break;
case A_Node::DECL_FRAME:
esb << "frame function";
break;
default: std::abort( );
}
esb << "; previous declaration: " << *af.dupLocation.target( );
errors.addNew( std::move( esb ) , fw.location( ) );
}
parseInstructions( af.function.instructions( ) , funcList , ftw == "fn" ? 3 : 1 );
}
void T_ParserImpl_::parseFunctionArguments(
T_FuncNode& function ,
T_SRDToken const& argsToken ) noexcept
{
if ( argsToken.type( ) != E_SRDTokenType::LIST ) {
errors.addNew( "arguments list expected" , argsToken.location( ) );
return;
}
for ( auto const& token : argsToken.list( ) ) {
if ( token.type( ) != E_SRDTokenType::WORD ) {
errors.addNew( "argument name expected" , token.location( ) );
continue;
}
const auto rv( function.addArgument( token ) );
if ( rv.present( ) ) {
T_StringBuilder esb;
esb << "duplicate argument '" << token.stringValue( )
<< "'; previous declaration: " << *rv.target( );
errors.addNew( std::move( esb ) , token.location( ) );
}
}
}
void T_ParserImpl_::parseInstructions(
T_InstrListNode& instructions ,
T_SRDList const& input ,
const uint32_t start ) noexcept
{
for ( auto iter( input.begin( ) + start ) ; iter.valid( ) ; iter ++ ) {
T_SRDToken const& itok( *iter );
if ( itok.type( ) != E_SRDTokenType::LIST ) {
errors.addNew( "instruction expected" , itok.location( ) );
continue;
}
T_SRDList const& ilist( itok.list( ) );
if ( ilist.empty( ) ) {
errors.addNew( "instruction expected" , itok.location( ) );
continue;
}
T_SRDToken const& iname( ilist[ 0 ] );
if ( iname.type( ) != E_SRDTokenType::WORD ) {
errors.addNew( "instruction name expected" , iname.location( ) );
continue;
}
T_String const& iword( iname.stringValue( ) );
if ( !instrMap.contains( iword ) ) {
errors.addNew( "unknown instruction" , iname.location( ) );
continue;
}
switch ( *instrMap.get( iword ) ) {
case E_InstrType::PIPELINE:
parsePipelineInstruction( instructions , ilist );
break;
case E_InstrType::PROFILE:
parseProfileInstruction( instructions , ilist );
break;
case E_InstrType::PROGRAM:
parseProgramInstruction( instructions , ilist );
break;
}
}
}
/*----------------------------------------------------------------------------*/
void T_ParserImpl_::parsePipelineInstruction(
T_InstrListNode& instructions ,
T_SRDList const& input ) noexcept
{
if ( input.size( ) < 3 ) {
errors.addNew( "identifier and program identifiers expected" ,
input[ 0 ].location( ) );
return;
}
const bool validId( input[ 1 ].type( ) == E_SRDTokenType::WORD );
if ( !validId ) {
errors.addNew( "pipeline identifier expected" , input[ 1 ].location( ) );
}
T_PipelineInstrNode& pipeline{ ([&instructions,&input,validId]( ) -> T_PipelineInstrNode& {
if ( validId ) {
return instructions.add< T_PipelineInstrNode >( input[ 0 ] );
}
return instructions.add< T_PipelineInstrNode >( );
})() };
pipeline.location( ) = input[ 0 ].location( );
const auto nMax{ std::min( input.size( ) , 8u ) };
for ( auto i = 2u ; i < nMax ; i ++ ) {
T_SRDToken const& tok( input[ i ] );
if ( tok.type( ) != E_SRDTokenType::WORD ) {
errors.addNew( "program identifier expected" ,
tok.location( ) );
continue;
}
const auto dup( pipeline.addProgram( tok ) );
if ( dup.present( ) ) {
T_StringBuilder esb;
esb << "duplicate program identifier; previous use: "
<< *dup.target( );
errors.addNew( std::move( esb ) , tok.location( ) );
}
}
if ( input.size( ) > 8 ) {
errors.addNew( "too many arguments" , input[ 8 ].location( ) );
}
}
/*----------------------------------------------------------------------------*/
void T_ParserImpl_::parseProfileInstruction(
T_InstrListNode& instructions ,
T_SRDList const& input ) noexcept
{
const bool hasEnough( input.size( ) < 2 );
if ( hasEnough || !input[ 1 ].isText( ) ) {
errors.addNew( "profiling section name expected" ,
hasEnough ? input[ 1 ].location( ) : T_SRDLocation{} );
if ( !hasEnough ) {
return;
}
}
const T_String text( input[ 1 ].isText( ) ? input[ 1 ].stringValue( ) : "*invalid*" );
T_ProfileInstrNode& profile{ instructions.add< T_ProfileInstrNode >( text ) };
profile.location( ) = input[ 0 ].location( );
parseInstructions( profile.instructions( ) , input , 2 );
}
/*----------------------------------------------------------------------------*/
void T_ParserImpl_::parseProgramInstruction(
T_InstrListNode& instructions ,
T_SRDList const& input ) noexcept
{
bool ok{ true };
if ( input.size( ) == 1 ) {
errors.addNew( "identifier and program name required" ,
input[ 0 ].location( ) );
return;
}
if ( input[ 1 ].type( ) != E_SRDTokenType::WORD ) {
errors.addNew( "identifier (word) expected" , input[ 1 ].location( ) );
ok = false;
}
if ( input.size( ) == 2 ) {
errors.addNew( "program name required" , input[ 0 ].location( ) );
return;
}
if ( !input[ 2 ].isText( ) ) {
errors.addNew( "program name (string or word) expected" ,
input[ 2 ].location( ) );
ok = false;
}
if ( input.size( ) > 3 ) {
errors.addNew( "too many arguments" , input[ 3 ].location( ) );
}
if ( !ok ) {
return;
}
T_ProgramInstrNode& program{ instructions.add< T_ProgramInstrNode >(
input[ 1 ] , input[ 2 ] ) };
program.location( ) = input[ 0 ].location( );
}
} // namespace
/*----------------------------------------------------------------------------*/
T_Parser::T_Parser( ) noexcept
: A_PrivateImplementation( new T_ParserImpl_( &errors_ , &rootNode_ ) ) ,
errors_( 64 ) , rootNode_{}
{}
bool T_Parser::parse(
T_SRDList const& input ) noexcept
{
errors_.clear( );
rootNode_ = NewOwned< T_RootNode >( );
for ( auto const& t : input ) {
if ( t.type( ) == E_SRDTokenType::LIST && t.list( ).size( ) > 0 ) {
p< T_ParserImpl_ >( ).parseFunction( t.list( ) );
} else {
errors_.addNew( "function, init or frame list expected" ,
t.location( ) );
}
}
if ( errors_.empty( ) ) {
T_SRDLocation loc( ([&input]() {
if ( input.size( ) != 0 ) {
return T_SRDLocation( input[ 0 ].location( ).source( ) , 1 , 1 );
}
return T_SRDLocation{};
})( ));
if ( !rootNode_->hasInit( ) ) {
errors_.addNew( "no initialisation block" , loc );
}
if ( !rootNode_->hasFrame( ) ) {
errors_.addNew( "no initialisation block" , loc );
}
}
return errors_.empty( );
}