Parser - Split parser off from nodes code
This commit is contained in:
parent
9cf7d8c434
commit
351614123e
3 changed files with 941 additions and 934 deletions
1
Makefile
1
Makefile
|
@ -30,6 +30,7 @@ COMMON = \
|
||||||
sync.cc \
|
sync.cc \
|
||||||
control.cc \
|
control.cc \
|
||||||
opast.cc \
|
opast.cc \
|
||||||
|
opparser.cc \
|
||||||
# END COMMON
|
# END COMMON
|
||||||
|
|
||||||
DEMO = \
|
DEMO = \
|
||||||
|
|
934
opast.cc
934
opast.cc
|
@ -325,937 +325,3 @@ T_BinaryOperatorNode::T_BinaryOperatorNode(
|
||||||
std::abort( );
|
std::abort( );
|
||||||
})( ) , parent ) , op_( op )
|
})( ) , parent ) , op_( op )
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
||||||
/*= T_Parser =================================================================*/
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
struct T_ParserImpl_
|
|
||||||
{
|
|
||||||
enum class E_InstrType {
|
|
||||||
CALL ,
|
|
||||||
FULLSCREEN ,
|
|
||||||
IF ,
|
|
||||||
INPUT ,
|
|
||||||
PIPELINE ,
|
|
||||||
PROFILE ,
|
|
||||||
PROGRAM ,
|
|
||||||
SET ,
|
|
||||||
TEXTURE ,
|
|
||||||
USE_FRAMEBUFFER ,
|
|
||||||
USE_PIPELINE ,
|
|
||||||
USE_PROGRAM ,
|
|
||||||
USE_TEXTURE ,
|
|
||||||
VIEWPORT ,
|
|
||||||
};
|
|
||||||
|
|
||||||
const T_KeyValueTable< T_String , E_InstrType > instrMap{ ([]() {
|
|
||||||
T_KeyValueTable< T_String , E_InstrType > temp{ 256 , 64 , 64 };
|
|
||||||
const auto add{ [&temp]( char const* name , E_InstrType it ) {
|
|
||||||
temp.add( T_String::Pooled( name ) , it );
|
|
||||||
} };
|
|
||||||
|
|
||||||
add( "call" , E_InstrType::CALL );
|
|
||||||
add( "fullscreen" , E_InstrType::FULLSCREEN );
|
|
||||||
add( "if" , E_InstrType::IF );
|
|
||||||
add( "input" , E_InstrType::INPUT );
|
|
||||||
add( "pipeline" , E_InstrType::PIPELINE );
|
|
||||||
add( "profiling" , E_InstrType::PROFILE );
|
|
||||||
add( "program" , E_InstrType::PROGRAM );
|
|
||||||
add( "set" , E_InstrType::SET );
|
|
||||||
add( "texture" , E_InstrType::TEXTURE );
|
|
||||||
add( "use-framebuffer" , E_InstrType::USE_FRAMEBUFFER );
|
|
||||||
add( "use-pipeline" , E_InstrType::USE_PIPELINE );
|
|
||||||
add( "use-program" , E_InstrType::USE_PROGRAM );
|
|
||||||
add( "use-texture" , E_InstrType::USE_TEXTURE );
|
|
||||||
add( "viewport" , E_InstrType::VIEWPORT );
|
|
||||||
|
|
||||||
return temp;
|
|
||||||
})( ) };
|
|
||||||
|
|
||||||
const T_KeyValueTable< T_String , T_UnaryOperatorNode::E_Operator > unaryOpMap{ ([]() {
|
|
||||||
T_KeyValueTable< T_String , T_UnaryOperatorNode::E_Operator > temp{ 64 , 32 , 32 };
|
|
||||||
const auto add{ [&temp]( char const* name ,
|
|
||||||
const T_UnaryOperatorNode::E_Operator it ) {
|
|
||||||
temp.add( T_String::Pooled( name ) , it );
|
|
||||||
} };
|
|
||||||
|
|
||||||
add( "neg" , T_UnaryOperatorNode::NEG );
|
|
||||||
add( "inv" , T_UnaryOperatorNode::INV );
|
|
||||||
add( "not" , T_UnaryOperatorNode::NOT );
|
|
||||||
add( "sin" , T_UnaryOperatorNode::SIN );
|
|
||||||
add( "cos" , T_UnaryOperatorNode::COS );
|
|
||||||
add( "tan" , T_UnaryOperatorNode::TAN );
|
|
||||||
add( "sqrt" , T_UnaryOperatorNode::SQRT );
|
|
||||||
add( "exp" , T_UnaryOperatorNode::EXP );
|
|
||||||
add( "ln" , T_UnaryOperatorNode::LN );
|
|
||||||
|
|
||||||
return temp;
|
|
||||||
})( ) };
|
|
||||||
|
|
||||||
const T_KeyValueTable< T_String , E_TexType > texTypeMap{ ([]() {
|
|
||||||
T_KeyValueTable< T_String , E_TexType > temp{ 64 , 16 , 16 };
|
|
||||||
const auto add{ [&temp]( char const* name ,
|
|
||||||
const E_TexType it ) {
|
|
||||||
temp.add( T_String::Pooled( name ) , it );
|
|
||||||
} };
|
|
||||||
|
|
||||||
add( "rgba-nu8" , E_TexType::RGBA8 );
|
|
||||||
add( "rgba-f16" , E_TexType::RGBA16F );
|
|
||||||
add( "rgb-nu8" , E_TexType::RGB8 );
|
|
||||||
add( "rgb-f16" , E_TexType::RGB16F );
|
|
||||||
add( "r-nu8" , E_TexType::R8 );
|
|
||||||
add( "r-f16" , E_TexType::R16F );
|
|
||||||
|
|
||||||
return temp;
|
|
||||||
})( ) };
|
|
||||||
|
|
||||||
const T_KeyValueTable< T_String , T_BinaryOperatorNode::E_Operator > binOpMap{ ([]() {
|
|
||||||
T_KeyValueTable< T_String , T_BinaryOperatorNode::E_Operator > temp{ 64 , 32 , 32 };
|
|
||||||
const auto add{ [&temp]( char const* name ,
|
|
||||||
const T_BinaryOperatorNode::E_Operator it ) {
|
|
||||||
temp.add( T_String::Pooled( name ) , it );
|
|
||||||
} };
|
|
||||||
|
|
||||||
add( "add" , T_BinaryOperatorNode::ADD );
|
|
||||||
add( "sub" , T_BinaryOperatorNode::SUB );
|
|
||||||
add( "mul" , T_BinaryOperatorNode::MUL );
|
|
||||||
add( "div" , T_BinaryOperatorNode::DIV );
|
|
||||||
add( "pow" , T_BinaryOperatorNode::POW );
|
|
||||||
add( "cmp-eq" , T_BinaryOperatorNode::CMP_EQ );
|
|
||||||
add( "cmp-ne" , T_BinaryOperatorNode::CMP_NE );
|
|
||||||
add( "cmp-gt" , T_BinaryOperatorNode::CMP_GT );
|
|
||||||
add( "cmp-ge" , T_BinaryOperatorNode::CMP_GE );
|
|
||||||
add( "cmp-lt" , T_BinaryOperatorNode::CMP_LT );
|
|
||||||
add( "cmp-le" , T_BinaryOperatorNode::CMP_LE );
|
|
||||||
|
|
||||||
return temp;
|
|
||||||
})( ) };
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
|
|
||||||
T_OwnPtr< T_RootNode >& root;
|
|
||||||
T_Array< T_SRDError >& errors;
|
|
||||||
T_Visitor< A_Node > visitor{ opast::ASTVisitorBrowser };
|
|
||||||
T_MultiArray< uint32_t > calls;
|
|
||||||
T_Visitor< uint32_t , uint32_t > callGraphVisitor{
|
|
||||||
[this]( 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_ParserImpl_( T_Array< T_SRDError >* errors ,
|
|
||||||
T_OwnPtr< T_RootNode >* root ) noexcept;
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
|
|
||||||
void parseTopLevel( T_SRDList const& list ) 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;
|
|
||||||
P_InstrListNode parseBlock(
|
|
||||||
A_Node& parent ,
|
|
||||||
T_SRDToken const& block ) noexcept;
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
|
|
||||||
#define M_DPARSER_( NAME ) \
|
|
||||||
void parse##NAME##Instruction( \
|
|
||||||
T_InstrListNode& instructions , \
|
|
||||||
T_SRDList const& input ) noexcept
|
|
||||||
|
|
||||||
M_DPARSER_( Call );
|
|
||||||
M_DPARSER_( If );
|
|
||||||
M_DPARSER_( Input );
|
|
||||||
M_DPARSER_( Pipeline );
|
|
||||||
M_DPARSER_( Profile );
|
|
||||||
M_DPARSER_( Program );
|
|
||||||
M_DPARSER_( Set );
|
|
||||||
M_DPARSER_( Texture );
|
|
||||||
M_DPARSER_( UseFramebuffer );
|
|
||||||
M_DPARSER_( UsePipeline );
|
|
||||||
M_DPARSER_( UseProgram );
|
|
||||||
void parseUseCommon(
|
|
||||||
T_InstrListNode& instructions ,
|
|
||||||
T_SRDList const& input ,
|
|
||||||
T_UseInstrNode::E_Type type ) noexcept;
|
|
||||||
M_DPARSER_( UseTexture );
|
|
||||||
M_DPARSER_( Viewport );
|
|
||||||
|
|
||||||
#undef M_DPARSER_
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
|
|
||||||
P_ExpressionNode parseExpression(
|
|
||||||
A_Node& parent ,
|
|
||||||
T_SRDToken const& token ) noexcept;
|
|
||||||
P_ExpressionNode parseOperation(
|
|
||||||
A_Node& parent ,
|
|
||||||
T_SRDList const& opList ) noexcept;
|
|
||||||
P_ExpressionNode parseBinOp(
|
|
||||||
A_Node& parent ,
|
|
||||||
T_SRDList const& opList ,
|
|
||||||
T_BinaryOperatorNode::E_Operator op ) noexcept;
|
|
||||||
P_ExpressionNode parseUnaryOp(
|
|
||||||
A_Node& parent ,
|
|
||||||
T_SRDList const& opList ,
|
|
||||||
T_UnaryOperatorNode::E_Operator op ) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
inline T_ParserImpl_::T_ParserImpl_(
|
|
||||||
T_Array< T_SRDError >* const errors ,
|
|
||||||
T_OwnPtr< T_RootNode >* const root ) noexcept
|
|
||||||
: root( *root ) , errors( *errors )
|
|
||||||
{ }
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
void T_ParserImpl_::parseTopLevel(
|
|
||||||
T_SRDList const& input ) noexcept
|
|
||||||
{
|
|
||||||
for ( auto const& t : input ) {
|
|
||||||
if ( t.type( ) == E_SRDTokenType::LIST && t.list( ).size( ) > 0 ) {
|
|
||||||
parseFunction( t.list( ) );
|
|
||||||
} else {
|
|
||||||
errors.addNew( "function, init or frame list expected" ,
|
|
||||||
t.location( ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !errors.empty( ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T_SRDLocation missingErrLoc( ([&input]() {
|
|
||||||
if ( input.size( ) != 0 ) {
|
|
||||||
return T_SRDLocation( input[ 0 ].location( ).source( ) , 1 , 1 );
|
|
||||||
}
|
|
||||||
return T_SRDLocation{};
|
|
||||||
})( ));
|
|
||||||
if ( !root->hasInit( ) ) {
|
|
||||||
errors.addNew( "no initialisation block" , missingErrLoc );
|
|
||||||
}
|
|
||||||
if ( !root->hasFrame( ) ) {
|
|
||||||
errors.addNew( "no initialisation block" , missingErrLoc );
|
|
||||||
}
|
|
||||||
if ( !errors.empty( ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
calls.clear( );
|
|
||||||
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( ) ) {
|
|
||||||
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;
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ) {
|
|
||||||
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;
|
|
||||||
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 ) {
|
|
||||||
T_StringBuilder esb;
|
|
||||||
esb << "duplicate argument '" << token.stringValue( )
|
|
||||||
<< "'; previous declaration: " << *rv;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define M_CASE_( NAME , FNAME ) case E_InstrType::NAME: parse##FNAME##Instruction( instructions , ilist ); break
|
|
||||||
switch ( *instrMap.get( iword ) ) {
|
|
||||||
M_CASE_( CALL , Call );
|
|
||||||
M_CASE_( IF , If );
|
|
||||||
M_CASE_( INPUT , Input );
|
|
||||||
M_CASE_( PIPELINE , Pipeline );
|
|
||||||
M_CASE_( PROFILE , Profile );
|
|
||||||
M_CASE_( PROGRAM , Program );
|
|
||||||
M_CASE_( SET , Set );
|
|
||||||
M_CASE_( TEXTURE , Texture );
|
|
||||||
M_CASE_( USE_FRAMEBUFFER , UseFramebuffer );
|
|
||||||
M_CASE_( USE_PIPELINE , UsePipeline );
|
|
||||||
M_CASE_( USE_PROGRAM , UseProgram );
|
|
||||||
M_CASE_( USE_TEXTURE , UseTexture );
|
|
||||||
M_CASE_( VIEWPORT , Viewport );
|
|
||||||
|
|
||||||
case E_InstrType::FULLSCREEN:
|
|
||||||
instructions.add< T_FullscreenInstrNode >( ).location( ) = iname.location( );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#undef M_CASE_
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
P_InstrListNode T_ParserImpl_::parseBlock(
|
|
||||||
A_Node& parent ,
|
|
||||||
T_SRDToken const& block ) noexcept
|
|
||||||
{
|
|
||||||
if ( block.type( ) != E_SRDTokenType::LIST ) {
|
|
||||||
errors.addNew( "block expected" , block.location( ) );
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
P_InstrListNode rv{ NewOwned< T_InstrListNode >( parent ) };
|
|
||||||
rv->location( ) = block.location( );
|
|
||||||
parseInstructions( *rv , block.list( ) , 0 );
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
#define M_INSTR_( NAME ) \
|
|
||||||
void T_ParserImpl_::parse##NAME##Instruction( \
|
|
||||||
T_InstrListNode& instructions , \
|
|
||||||
T_SRDList const& input ) noexcept
|
|
||||||
|
|
||||||
M_INSTR_( Call )
|
|
||||||
{
|
|
||||||
if ( input.size( ) == 1 || input[ 1 ].type( ) != E_SRDTokenType::WORD ) {
|
|
||||||
errors.addNew( "function identifier expected" ,
|
|
||||||
input[ input.size( ) == 1 ? 0 : 1 ].location( ) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
M_INSTR_( If )
|
|
||||||
{
|
|
||||||
if ( input.size( ) == 1 ) {
|
|
||||||
errors.addNew( "expression and 'then' block expected" ,
|
|
||||||
input[ 0 ].location( ) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
T_CondInstrNode& cond{ instructions.add< T_CondInstrNode >( ) };
|
|
||||||
cond.location( ) = input[ 0 ].location( );
|
|
||||||
cond.setExpression( parseExpression( cond , input[ 1 ] ) );
|
|
||||||
|
|
||||||
if ( input.size( ) == 2 ) {
|
|
||||||
errors.addNew( "'then' block expected" ,
|
|
||||||
input[ 0 ].location( ) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cond.setCase( 1 , parseBlock( cond , input[ 2 ] ) );
|
|
||||||
if ( input.size( ) > 3 ) {
|
|
||||||
cond.setDefaultCase( parseBlock( cond , input[ 3 ] ) );
|
|
||||||
if ( input.size( ) > 4 ) {
|
|
||||||
errors.addNew( "too many arguments" , input[ 4 ].location( ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
M_INSTR_( Input )
|
|
||||||
{
|
|
||||||
if ( input.size( ) < 2 || !input[ 1 ].isText( ) ) {
|
|
||||||
errors.addNew( "input identifier expected" ,
|
|
||||||
input[ input.size( ) < 2 ? 0 : 1 ].location( ) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( input.size( ) > 3 ) {
|
|
||||||
errors.addNew( "too many arguments" , input[ 3 ].location( ) );
|
|
||||||
}
|
|
||||||
if ( input.size( ) >= 3 && !input[ 2 ].isNumeric( ) ) {
|
|
||||||
errors.addNew( "default value expected" , input[ 2 ].location( ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool hasDefault( input.size( ) >= 3 && input[ 2 ].isNumeric( ) );
|
|
||||||
auto& instr( ([&]() -> T_InputInstrNode& {
|
|
||||||
if ( hasDefault ) {
|
|
||||||
return instructions.add< T_InputInstrNode >(
|
|
||||||
input[ 1 ] , input[ 2 ] );
|
|
||||||
}
|
|
||||||
return instructions.add< T_InputInstrNode >( input[ 1 ] );
|
|
||||||
})( ) );
|
|
||||||
instr.location( ) = input[ 0 ].location( );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
M_INSTR_( Pipeline )
|
|
||||||
{
|
|
||||||
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 ) {
|
|
||||||
T_StringBuilder esb;
|
|
||||||
esb << "duplicate program identifier; previous use: " << *dup;
|
|
||||||
errors.addNew( std::move( esb ) , tok.location( ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( input.size( ) > 8 ) {
|
|
||||||
errors.addNew( "too many arguments" , input[ 8 ].location( ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
M_INSTR_( Profile )
|
|
||||||
{
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
M_INSTR_( Program )
|
|
||||||
{
|
|
||||||
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( );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
M_INSTR_( Set )
|
|
||||||
{
|
|
||||||
bool ok{ true };
|
|
||||||
if ( input.size( ) == 1 ) {
|
|
||||||
errors.addNew( "identifier and expression required" ,
|
|
||||||
input[ 0 ].location( ) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( input[ 1 ].type( ) != E_SRDTokenType::WORD ) {
|
|
||||||
errors.addNew( "variable identifier expected" , input[ 1 ].location( ) );
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
if ( input.size( ) == 2 ) {
|
|
||||||
errors.addNew( "expression required" , input[ 0 ].location( ) );
|
|
||||||
}
|
|
||||||
if ( input.size( ) > 3 ) {
|
|
||||||
errors.addNew( "too many arguments" , input[ 3 ].location( ) );
|
|
||||||
}
|
|
||||||
if ( !ok ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
T_SetInstrNode& set{ instructions.add< T_SetInstrNode >( input[ 1 ] ) };
|
|
||||||
set.location( ) = input[ 0 ].location( );
|
|
||||||
if ( input.size( ) > 2 ) {
|
|
||||||
auto expr( parseExpression( set , input[ 2 ] ) );
|
|
||||||
if ( expr ) {
|
|
||||||
set.setExpression( std::move( expr ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
M_INSTR_( Texture )
|
|
||||||
{
|
|
||||||
if ( input.size( ) < 2 || input[ 1 ].type( ) != E_SRDTokenType::WORD ) {
|
|
||||||
errors.addNew( "texture identifier expected" ,
|
|
||||||
( input.size( ) < 2 ? input[ 0 ] : input[ 1 ] ).location( ) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( input.size( ) < 3 || input[ 2 ].type( ) != E_SRDTokenType::WORD ) {
|
|
||||||
errors.addNew( "texture type expected" ,
|
|
||||||
( input.size( ) < 3 ? input[ 0 ] : input[ 2 ] ).location( ) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const* const ttt( texTypeMap.get( input[ 2 ].stringValue( ) ) );
|
|
||||||
if ( !ttt ) {
|
|
||||||
errors.addNew( "invalid texture type" ,
|
|
||||||
( input.size( ) < 3 ? input[ 0 ] : input[ 2 ] ).location( ) );
|
|
||||||
}
|
|
||||||
const auto tt( ttt ? *ttt : E_TexType::RGB8 );
|
|
||||||
|
|
||||||
auto& instr{ instructions.add< T_TextureInstrNode >( input[ 1 ] , tt ) };
|
|
||||||
instr.location( ) = input[ 0 ].location( );
|
|
||||||
if ( input.size( ) > 4 ) {
|
|
||||||
instr.setWidth( parseExpression( instr , input[ 3 ] ) );
|
|
||||||
} else {
|
|
||||||
errors.addNew( "width expected" , input[ 0 ].location( ) );
|
|
||||||
}
|
|
||||||
if ( input.size( ) > 4 ) {
|
|
||||||
instr.setHeight( parseExpression( instr , input[ 3 ] ) );
|
|
||||||
} else {
|
|
||||||
errors.addNew( "height expected" , input[ 0 ].location( ) );
|
|
||||||
}
|
|
||||||
if ( input.size( ) > 5 ) {
|
|
||||||
errors.addNew( "too many arguments" , input[ 5 ].location( ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
M_INSTR_( UseFramebuffer )
|
|
||||||
{
|
|
||||||
parseUseCommon( instructions , input , T_UseInstrNode::FRAMEBUFFER );
|
|
||||||
}
|
|
||||||
|
|
||||||
M_INSTR_( UsePipeline )
|
|
||||||
{
|
|
||||||
parseUseCommon( instructions , input , T_UseInstrNode::PIPELINE );
|
|
||||||
}
|
|
||||||
|
|
||||||
M_INSTR_( UseProgram )
|
|
||||||
{
|
|
||||||
parseUseCommon( instructions , input , T_UseInstrNode::PROGRAM );
|
|
||||||
}
|
|
||||||
|
|
||||||
void T_ParserImpl_::parseUseCommon(
|
|
||||||
T_InstrListNode& instructions ,
|
|
||||||
T_SRDList const& input ,
|
|
||||||
T_UseInstrNode::E_Type type ) noexcept
|
|
||||||
{
|
|
||||||
if ( input.size( ) == 1 || input[ 1 ].type( ) != E_SRDTokenType::WORD ) {
|
|
||||||
errors.addNew( "resource identifier expected" ,
|
|
||||||
input[ input.size( ) == 1 ? 0 : 1 ].location( ) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( input.size( ) > 2 ) {
|
|
||||||
errors.addNew( "too many arguments" , input[ 2 ].location( ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& instr{ instructions.add< T_UseInstrNode >( type , input[ 1 ] ) };
|
|
||||||
instr.location( ) = input[ 0 ].location( );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
M_INSTR_( UseTexture )
|
|
||||||
{
|
|
||||||
if ( input.size( ) == 1 || !input[ 1 ].isInteger( ) ) {
|
|
||||||
errors.addNew( "bank number expected" ,
|
|
||||||
input[ input.size( ) == 1 ? 0 : 1 ].location( ) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( input[ 1 ].longValue( ) < 0 || input[ 1 ].longValue( ) > UINT32_MAX ) {
|
|
||||||
errors.addNew( "invalid bank number" , input[ 1 ].location( ) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( input.size( ) == 2 || input[ 2 ].type( ) != E_SRDTokenType::WORD ) {
|
|
||||||
errors.addNew( "texture identifier expected" ,
|
|
||||||
input[ input.size( ) == 2 ? 0 : 2 ].location( ) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( input.size( ) == 3 || input[ 3 ].type( ) != E_SRDTokenType::WORD ) {
|
|
||||||
errors.addNew( "sampler identifier expected" ,
|
|
||||||
input[ input.size( ) == 3 ? 0 : 3 ].location( ) );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( input.size( ) > 4 ) {
|
|
||||||
errors.addNew( "too many arguments" , input[ 4 ].location( ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& instr{ instructions.add< T_UseTextureInstrNode >(
|
|
||||||
input[ 1 ] , input[ 2 ] , input[ 3 ] ) };
|
|
||||||
instr.location( ) = input[ 0 ].location( );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
M_INSTR_( Viewport )
|
|
||||||
{
|
|
||||||
auto& instr{ instructions.add< T_ViewportInstrNode >( ) };
|
|
||||||
instr.location( ) = input[ 0 ].location( );
|
|
||||||
|
|
||||||
for ( auto i = 1u ; i < 5 ; i ++ ) {
|
|
||||||
T_ViewportInstrNode::E_Parameter p{
|
|
||||||
T_ViewportInstrNode::E_Parameter( i ) };
|
|
||||||
if ( input.size( ) < i ) {
|
|
||||||
T_StringBuilder sb;
|
|
||||||
sb << "missing ";
|
|
||||||
switch ( p ) {
|
|
||||||
case T_ViewportInstrNode::PX: sb << "X"; break;
|
|
||||||
case T_ViewportInstrNode::PY: sb << "Y"; break;
|
|
||||||
case T_ViewportInstrNode::PWIDTH: sb << "width"; break;
|
|
||||||
case T_ViewportInstrNode::PHEIGHT: sb << "height"; break;
|
|
||||||
}
|
|
||||||
sb << " parameter";
|
|
||||||
errors.addNew( std::move( sb ) , input[ 0 ].location( ) );
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
instr.setParameter( p , parseExpression( instr , input[ i ] ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
P_ExpressionNode T_ParserImpl_::parseExpression(
|
|
||||||
A_Node& parent ,
|
|
||||||
T_SRDToken const& token ) noexcept
|
|
||||||
{
|
|
||||||
if ( token.isNumeric( ) ) {
|
|
||||||
return NewOwned< T_ConstantExprNode >( parent , token );
|
|
||||||
}
|
|
||||||
if ( token.type( ) == E_SRDTokenType::WORD || token.type( ) == E_SRDTokenType::VAR ) {
|
|
||||||
return NewOwned< T_IdentifierExprNode >( parent , token );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( token.type( ) == E_SRDTokenType::LIST && !token.list( ).empty( ) ) {
|
|
||||||
return parseOperation( parent , token.list( ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
errors.addNew( "invalid expression" , token.location( ) );
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
P_ExpressionNode T_ParserImpl_::parseOperation(
|
|
||||||
A_Node& parent ,
|
|
||||||
T_SRDList const& opList ) noexcept
|
|
||||||
{
|
|
||||||
T_SRDToken const& opId( opList[ 0 ] );
|
|
||||||
if ( opId.type( ) != E_SRDTokenType::WORD ) {
|
|
||||||
errors.addNew( "operator expected" , opId.location( ) );
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( opId.stringValue( ) == "get-input" ) {
|
|
||||||
if ( opList.size( ) == 1 || !opList[ 1 ].isText( ) ) {
|
|
||||||
errors.addNew( "input identifier expected" ,
|
|
||||||
opList[ opList.size( ) == 1 ? 0 : 1 ].location( ) );
|
|
||||||
return { };
|
|
||||||
}
|
|
||||||
if ( opList.size( ) > 2 ) {
|
|
||||||
errors.addNew( "too many arguments" , opList[ 2 ].location( ) );
|
|
||||||
}
|
|
||||||
auto node{ NewOwned< T_InputExprNode >( parent , opList[ 1 ] ) };
|
|
||||||
node->location( ) = opList[ 0 ].location( );
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( binOpMap.contains( opId.stringValue( ) ) ) {
|
|
||||||
return parseBinOp( parent , opList ,
|
|
||||||
*binOpMap.get( opId.stringValue( ) ) );
|
|
||||||
}
|
|
||||||
if ( unaryOpMap.contains( opId.stringValue( ) ) ) {
|
|
||||||
return parseUnaryOp( parent , opList ,
|
|
||||||
*unaryOpMap.get( opId.stringValue( ) ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
errors.addNew( "unknown operator" , opId.location( ) );
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
P_ExpressionNode T_ParserImpl_::parseBinOp(
|
|
||||||
A_Node& parent ,
|
|
||||||
T_SRDList const& opList ,
|
|
||||||
T_BinaryOperatorNode::E_Operator op ) noexcept
|
|
||||||
{
|
|
||||||
if ( opList.size( ) < 3 ) {
|
|
||||||
errors.addNew( "not enough arguments" , opList[ 0 ].location( ) );
|
|
||||||
} else if ( opList.size( ) > 3 ) {
|
|
||||||
errors.addNew( "too many arguments" , opList[ 3 ].location( ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
T_OwnPtr< T_BinaryOperatorNode > opNode{
|
|
||||||
NewOwned< T_BinaryOperatorNode >( parent , op ) };
|
|
||||||
opNode->location( ) = opList[ 0 ].location( );
|
|
||||||
|
|
||||||
if ( opList.size( ) > 1 ) {
|
|
||||||
auto left{ parseExpression( *opNode , opList[ 1 ] ) };
|
|
||||||
if ( left ) {
|
|
||||||
opNode->setLeft( std::move( left ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( opList.size( ) > 2 ) {
|
|
||||||
auto right{ parseExpression( *opNode , opList[ 2 ] ) };
|
|
||||||
if ( right ) {
|
|
||||||
opNode->setRight( std::move( right ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return opNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
P_ExpressionNode T_ParserImpl_::parseUnaryOp(
|
|
||||||
A_Node& parent ,
|
|
||||||
T_SRDList const& opList ,
|
|
||||||
T_UnaryOperatorNode::E_Operator op ) noexcept
|
|
||||||
{
|
|
||||||
if ( opList.size( ) < 2 ) {
|
|
||||||
errors.addNew( "not enough arguments" , opList[ 0 ].location( ) );
|
|
||||||
} else if ( opList.size( ) > 2 ) {
|
|
||||||
errors.addNew( "too many arguments" , opList[ 2 ].location( ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
T_OwnPtr< T_UnaryOperatorNode > opNode{
|
|
||||||
NewOwned< T_UnaryOperatorNode >( parent , op ) };
|
|
||||||
opNode->location( ) = opList[ 0 ].location( );
|
|
||||||
|
|
||||||
if ( opList.size( ) > 1 ) {
|
|
||||||
auto argument{ parseExpression( *opNode , opList[ 1 ] ) };
|
|
||||||
if ( argument ) {
|
|
||||||
opNode->setArgument( std::move( argument ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return opNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // 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 >( );
|
|
||||||
p< T_ParserImpl_ >( ).parseTopLevel( input );
|
|
||||||
return errors_.empty( );
|
|
||||||
}
|
|
||||||
|
|
940
opparser.cc
Normal file
940
opparser.cc
Normal file
|
@ -0,0 +1,940 @@
|
||||||
|
#include "externals.hh"
|
||||||
|
#include "opast.hh"
|
||||||
|
#include <ebcl/Algorithms.hh>
|
||||||
|
|
||||||
|
using namespace ebcl;
|
||||||
|
using namespace opast;
|
||||||
|
|
||||||
|
|
||||||
|
/*= T_Parser =================================================================*/
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct T_ParserImpl_
|
||||||
|
{
|
||||||
|
enum class E_InstrType {
|
||||||
|
CALL ,
|
||||||
|
FULLSCREEN ,
|
||||||
|
IF ,
|
||||||
|
INPUT ,
|
||||||
|
PIPELINE ,
|
||||||
|
PROFILE ,
|
||||||
|
PROGRAM ,
|
||||||
|
SET ,
|
||||||
|
TEXTURE ,
|
||||||
|
USE_FRAMEBUFFER ,
|
||||||
|
USE_PIPELINE ,
|
||||||
|
USE_PROGRAM ,
|
||||||
|
USE_TEXTURE ,
|
||||||
|
VIEWPORT ,
|
||||||
|
};
|
||||||
|
|
||||||
|
const T_KeyValueTable< T_String , E_InstrType > instrMap{ ([]() {
|
||||||
|
T_KeyValueTable< T_String , E_InstrType > temp{ 256 , 64 , 64 };
|
||||||
|
const auto add{ [&temp]( char const* name , E_InstrType it ) {
|
||||||
|
temp.add( T_String::Pooled( name ) , it );
|
||||||
|
} };
|
||||||
|
|
||||||
|
add( "call" , E_InstrType::CALL );
|
||||||
|
add( "fullscreen" , E_InstrType::FULLSCREEN );
|
||||||
|
add( "if" , E_InstrType::IF );
|
||||||
|
add( "input" , E_InstrType::INPUT );
|
||||||
|
add( "pipeline" , E_InstrType::PIPELINE );
|
||||||
|
add( "profiling" , E_InstrType::PROFILE );
|
||||||
|
add( "program" , E_InstrType::PROGRAM );
|
||||||
|
add( "set" , E_InstrType::SET );
|
||||||
|
add( "texture" , E_InstrType::TEXTURE );
|
||||||
|
add( "use-framebuffer" , E_InstrType::USE_FRAMEBUFFER );
|
||||||
|
add( "use-pipeline" , E_InstrType::USE_PIPELINE );
|
||||||
|
add( "use-program" , E_InstrType::USE_PROGRAM );
|
||||||
|
add( "use-texture" , E_InstrType::USE_TEXTURE );
|
||||||
|
add( "viewport" , E_InstrType::VIEWPORT );
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
})( ) };
|
||||||
|
|
||||||
|
const T_KeyValueTable< T_String , T_UnaryOperatorNode::E_Operator > unaryOpMap{ ([]() {
|
||||||
|
T_KeyValueTable< T_String , T_UnaryOperatorNode::E_Operator > temp{ 64 , 32 , 32 };
|
||||||
|
const auto add{ [&temp]( char const* name ,
|
||||||
|
const T_UnaryOperatorNode::E_Operator it ) {
|
||||||
|
temp.add( T_String::Pooled( name ) , it );
|
||||||
|
} };
|
||||||
|
|
||||||
|
add( "neg" , T_UnaryOperatorNode::NEG );
|
||||||
|
add( "inv" , T_UnaryOperatorNode::INV );
|
||||||
|
add( "not" , T_UnaryOperatorNode::NOT );
|
||||||
|
add( "sin" , T_UnaryOperatorNode::SIN );
|
||||||
|
add( "cos" , T_UnaryOperatorNode::COS );
|
||||||
|
add( "tan" , T_UnaryOperatorNode::TAN );
|
||||||
|
add( "sqrt" , T_UnaryOperatorNode::SQRT );
|
||||||
|
add( "exp" , T_UnaryOperatorNode::EXP );
|
||||||
|
add( "ln" , T_UnaryOperatorNode::LN );
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
})( ) };
|
||||||
|
|
||||||
|
const T_KeyValueTable< T_String , E_TexType > texTypeMap{ ([]() {
|
||||||
|
T_KeyValueTable< T_String , E_TexType > temp{ 64 , 16 , 16 };
|
||||||
|
const auto add{ [&temp]( char const* name ,
|
||||||
|
const E_TexType it ) {
|
||||||
|
temp.add( T_String::Pooled( name ) , it );
|
||||||
|
} };
|
||||||
|
|
||||||
|
add( "rgba-nu8" , E_TexType::RGBA8 );
|
||||||
|
add( "rgba-f16" , E_TexType::RGBA16F );
|
||||||
|
add( "rgb-nu8" , E_TexType::RGB8 );
|
||||||
|
add( "rgb-f16" , E_TexType::RGB16F );
|
||||||
|
add( "r-nu8" , E_TexType::R8 );
|
||||||
|
add( "r-f16" , E_TexType::R16F );
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
})( ) };
|
||||||
|
|
||||||
|
const T_KeyValueTable< T_String , T_BinaryOperatorNode::E_Operator > binOpMap{ ([]() {
|
||||||
|
T_KeyValueTable< T_String , T_BinaryOperatorNode::E_Operator > temp{ 64 , 32 , 32 };
|
||||||
|
const auto add{ [&temp]( char const* name ,
|
||||||
|
const T_BinaryOperatorNode::E_Operator it ) {
|
||||||
|
temp.add( T_String::Pooled( name ) , it );
|
||||||
|
} };
|
||||||
|
|
||||||
|
add( "add" , T_BinaryOperatorNode::ADD );
|
||||||
|
add( "sub" , T_BinaryOperatorNode::SUB );
|
||||||
|
add( "mul" , T_BinaryOperatorNode::MUL );
|
||||||
|
add( "div" , T_BinaryOperatorNode::DIV );
|
||||||
|
add( "pow" , T_BinaryOperatorNode::POW );
|
||||||
|
add( "cmp-eq" , T_BinaryOperatorNode::CMP_EQ );
|
||||||
|
add( "cmp-ne" , T_BinaryOperatorNode::CMP_NE );
|
||||||
|
add( "cmp-gt" , T_BinaryOperatorNode::CMP_GT );
|
||||||
|
add( "cmp-ge" , T_BinaryOperatorNode::CMP_GE );
|
||||||
|
add( "cmp-lt" , T_BinaryOperatorNode::CMP_LT );
|
||||||
|
add( "cmp-le" , T_BinaryOperatorNode::CMP_LE );
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
})( ) };
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
T_OwnPtr< T_RootNode >& root;
|
||||||
|
T_Array< T_SRDError >& errors;
|
||||||
|
T_Visitor< A_Node > visitor{ opast::ASTVisitorBrowser };
|
||||||
|
T_MultiArray< uint32_t > calls;
|
||||||
|
T_Visitor< uint32_t , uint32_t > callGraphVisitor{
|
||||||
|
[this]( 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_ParserImpl_( T_Array< T_SRDError >* errors ,
|
||||||
|
T_OwnPtr< T_RootNode >* root ) noexcept;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
void parseTopLevel( T_SRDList const& list ) 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;
|
||||||
|
P_InstrListNode parseBlock(
|
||||||
|
A_Node& parent ,
|
||||||
|
T_SRDToken const& block ) noexcept;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define M_DPARSER_( NAME ) \
|
||||||
|
void parse##NAME##Instruction( \
|
||||||
|
T_InstrListNode& instructions , \
|
||||||
|
T_SRDList const& input ) noexcept
|
||||||
|
|
||||||
|
M_DPARSER_( Call );
|
||||||
|
M_DPARSER_( If );
|
||||||
|
M_DPARSER_( Input );
|
||||||
|
M_DPARSER_( Pipeline );
|
||||||
|
M_DPARSER_( Profile );
|
||||||
|
M_DPARSER_( Program );
|
||||||
|
M_DPARSER_( Set );
|
||||||
|
M_DPARSER_( Texture );
|
||||||
|
M_DPARSER_( UseFramebuffer );
|
||||||
|
M_DPARSER_( UsePipeline );
|
||||||
|
M_DPARSER_( UseProgram );
|
||||||
|
void parseUseCommon(
|
||||||
|
T_InstrListNode& instructions ,
|
||||||
|
T_SRDList const& input ,
|
||||||
|
T_UseInstrNode::E_Type type ) noexcept;
|
||||||
|
M_DPARSER_( UseTexture );
|
||||||
|
M_DPARSER_( Viewport );
|
||||||
|
|
||||||
|
#undef M_DPARSER_
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
P_ExpressionNode parseExpression(
|
||||||
|
A_Node& parent ,
|
||||||
|
T_SRDToken const& token ) noexcept;
|
||||||
|
P_ExpressionNode parseOperation(
|
||||||
|
A_Node& parent ,
|
||||||
|
T_SRDList const& opList ) noexcept;
|
||||||
|
P_ExpressionNode parseBinOp(
|
||||||
|
A_Node& parent ,
|
||||||
|
T_SRDList const& opList ,
|
||||||
|
T_BinaryOperatorNode::E_Operator op ) noexcept;
|
||||||
|
P_ExpressionNode parseUnaryOp(
|
||||||
|
A_Node& parent ,
|
||||||
|
T_SRDList const& opList ,
|
||||||
|
T_UnaryOperatorNode::E_Operator op ) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
inline T_ParserImpl_::T_ParserImpl_(
|
||||||
|
T_Array< T_SRDError >* const errors ,
|
||||||
|
T_OwnPtr< T_RootNode >* const root ) noexcept
|
||||||
|
: root( *root ) , errors( *errors )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void T_ParserImpl_::parseTopLevel(
|
||||||
|
T_SRDList const& input ) noexcept
|
||||||
|
{
|
||||||
|
for ( auto const& t : input ) {
|
||||||
|
if ( t.type( ) == E_SRDTokenType::LIST && t.list( ).size( ) > 0 ) {
|
||||||
|
parseFunction( t.list( ) );
|
||||||
|
} else {
|
||||||
|
errors.addNew( "function, init or frame list expected" ,
|
||||||
|
t.location( ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( !errors.empty( ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T_SRDLocation missingErrLoc( ([&input]() {
|
||||||
|
if ( input.size( ) != 0 ) {
|
||||||
|
return T_SRDLocation( input[ 0 ].location( ).source( ) , 1 , 1 );
|
||||||
|
}
|
||||||
|
return T_SRDLocation{};
|
||||||
|
})( ));
|
||||||
|
if ( !root->hasInit( ) ) {
|
||||||
|
errors.addNew( "no initialisation block" , missingErrLoc );
|
||||||
|
}
|
||||||
|
if ( !root->hasFrame( ) ) {
|
||||||
|
errors.addNew( "no initialisation block" , missingErrLoc );
|
||||||
|
}
|
||||||
|
if ( !errors.empty( ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
calls.clear( );
|
||||||
|
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( ) ) {
|
||||||
|
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;
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ) {
|
||||||
|
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;
|
||||||
|
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 ) {
|
||||||
|
T_StringBuilder esb;
|
||||||
|
esb << "duplicate argument '" << token.stringValue( )
|
||||||
|
<< "'; previous declaration: " << *rv;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define M_CASE_( NAME , FNAME ) case E_InstrType::NAME: parse##FNAME##Instruction( instructions , ilist ); break
|
||||||
|
switch ( *instrMap.get( iword ) ) {
|
||||||
|
M_CASE_( CALL , Call );
|
||||||
|
M_CASE_( IF , If );
|
||||||
|
M_CASE_( INPUT , Input );
|
||||||
|
M_CASE_( PIPELINE , Pipeline );
|
||||||
|
M_CASE_( PROFILE , Profile );
|
||||||
|
M_CASE_( PROGRAM , Program );
|
||||||
|
M_CASE_( SET , Set );
|
||||||
|
M_CASE_( TEXTURE , Texture );
|
||||||
|
M_CASE_( USE_FRAMEBUFFER , UseFramebuffer );
|
||||||
|
M_CASE_( USE_PIPELINE , UsePipeline );
|
||||||
|
M_CASE_( USE_PROGRAM , UseProgram );
|
||||||
|
M_CASE_( USE_TEXTURE , UseTexture );
|
||||||
|
M_CASE_( VIEWPORT , Viewport );
|
||||||
|
|
||||||
|
case E_InstrType::FULLSCREEN:
|
||||||
|
instructions.add< T_FullscreenInstrNode >( ).location( ) = iname.location( );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#undef M_CASE_
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
P_InstrListNode T_ParserImpl_::parseBlock(
|
||||||
|
A_Node& parent ,
|
||||||
|
T_SRDToken const& block ) noexcept
|
||||||
|
{
|
||||||
|
if ( block.type( ) != E_SRDTokenType::LIST ) {
|
||||||
|
errors.addNew( "block expected" , block.location( ) );
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
P_InstrListNode rv{ NewOwned< T_InstrListNode >( parent ) };
|
||||||
|
rv->location( ) = block.location( );
|
||||||
|
parseInstructions( *rv , block.list( ) , 0 );
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define M_INSTR_( NAME ) \
|
||||||
|
void T_ParserImpl_::parse##NAME##Instruction( \
|
||||||
|
T_InstrListNode& instructions , \
|
||||||
|
T_SRDList const& input ) noexcept
|
||||||
|
|
||||||
|
M_INSTR_( Call )
|
||||||
|
{
|
||||||
|
if ( input.size( ) == 1 || input[ 1 ].type( ) != E_SRDTokenType::WORD ) {
|
||||||
|
errors.addNew( "function identifier expected" ,
|
||||||
|
input[ input.size( ) == 1 ? 0 : 1 ].location( ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
M_INSTR_( If )
|
||||||
|
{
|
||||||
|
if ( input.size( ) == 1 ) {
|
||||||
|
errors.addNew( "expression and 'then' block expected" ,
|
||||||
|
input[ 0 ].location( ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
T_CondInstrNode& cond{ instructions.add< T_CondInstrNode >( ) };
|
||||||
|
cond.location( ) = input[ 0 ].location( );
|
||||||
|
cond.setExpression( parseExpression( cond , input[ 1 ] ) );
|
||||||
|
|
||||||
|
if ( input.size( ) == 2 ) {
|
||||||
|
errors.addNew( "'then' block expected" ,
|
||||||
|
input[ 0 ].location( ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cond.setCase( 1 , parseBlock( cond , input[ 2 ] ) );
|
||||||
|
if ( input.size( ) > 3 ) {
|
||||||
|
cond.setDefaultCase( parseBlock( cond , input[ 3 ] ) );
|
||||||
|
if ( input.size( ) > 4 ) {
|
||||||
|
errors.addNew( "too many arguments" , input[ 4 ].location( ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
M_INSTR_( Input )
|
||||||
|
{
|
||||||
|
if ( input.size( ) < 2 || !input[ 1 ].isText( ) ) {
|
||||||
|
errors.addNew( "input identifier expected" ,
|
||||||
|
input[ input.size( ) < 2 ? 0 : 1 ].location( ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( input.size( ) > 3 ) {
|
||||||
|
errors.addNew( "too many arguments" , input[ 3 ].location( ) );
|
||||||
|
}
|
||||||
|
if ( input.size( ) >= 3 && !input[ 2 ].isNumeric( ) ) {
|
||||||
|
errors.addNew( "default value expected" , input[ 2 ].location( ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool hasDefault( input.size( ) >= 3 && input[ 2 ].isNumeric( ) );
|
||||||
|
auto& instr( ([&]() -> T_InputInstrNode& {
|
||||||
|
if ( hasDefault ) {
|
||||||
|
return instructions.add< T_InputInstrNode >(
|
||||||
|
input[ 1 ] , input[ 2 ] );
|
||||||
|
}
|
||||||
|
return instructions.add< T_InputInstrNode >( input[ 1 ] );
|
||||||
|
})( ) );
|
||||||
|
instr.location( ) = input[ 0 ].location( );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
M_INSTR_( Pipeline )
|
||||||
|
{
|
||||||
|
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 ) {
|
||||||
|
T_StringBuilder esb;
|
||||||
|
esb << "duplicate program identifier; previous use: " << *dup;
|
||||||
|
errors.addNew( std::move( esb ) , tok.location( ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( input.size( ) > 8 ) {
|
||||||
|
errors.addNew( "too many arguments" , input[ 8 ].location( ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
M_INSTR_( Profile )
|
||||||
|
{
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
M_INSTR_( Program )
|
||||||
|
{
|
||||||
|
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( );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
M_INSTR_( Set )
|
||||||
|
{
|
||||||
|
bool ok{ true };
|
||||||
|
if ( input.size( ) == 1 ) {
|
||||||
|
errors.addNew( "identifier and expression required" ,
|
||||||
|
input[ 0 ].location( ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( input[ 1 ].type( ) != E_SRDTokenType::WORD ) {
|
||||||
|
errors.addNew( "variable identifier expected" , input[ 1 ].location( ) );
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
if ( input.size( ) == 2 ) {
|
||||||
|
errors.addNew( "expression required" , input[ 0 ].location( ) );
|
||||||
|
}
|
||||||
|
if ( input.size( ) > 3 ) {
|
||||||
|
errors.addNew( "too many arguments" , input[ 3 ].location( ) );
|
||||||
|
}
|
||||||
|
if ( !ok ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
T_SetInstrNode& set{ instructions.add< T_SetInstrNode >( input[ 1 ] ) };
|
||||||
|
set.location( ) = input[ 0 ].location( );
|
||||||
|
if ( input.size( ) > 2 ) {
|
||||||
|
auto expr( parseExpression( set , input[ 2 ] ) );
|
||||||
|
if ( expr ) {
|
||||||
|
set.setExpression( std::move( expr ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
M_INSTR_( Texture )
|
||||||
|
{
|
||||||
|
if ( input.size( ) < 2 || input[ 1 ].type( ) != E_SRDTokenType::WORD ) {
|
||||||
|
errors.addNew( "texture identifier expected" ,
|
||||||
|
( input.size( ) < 2 ? input[ 0 ] : input[ 1 ] ).location( ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( input.size( ) < 3 || input[ 2 ].type( ) != E_SRDTokenType::WORD ) {
|
||||||
|
errors.addNew( "texture type expected" ,
|
||||||
|
( input.size( ) < 3 ? input[ 0 ] : input[ 2 ] ).location( ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const* const ttt( texTypeMap.get( input[ 2 ].stringValue( ) ) );
|
||||||
|
if ( !ttt ) {
|
||||||
|
errors.addNew( "invalid texture type" ,
|
||||||
|
( input.size( ) < 3 ? input[ 0 ] : input[ 2 ] ).location( ) );
|
||||||
|
}
|
||||||
|
const auto tt( ttt ? *ttt : E_TexType::RGB8 );
|
||||||
|
|
||||||
|
auto& instr{ instructions.add< T_TextureInstrNode >( input[ 1 ] , tt ) };
|
||||||
|
instr.location( ) = input[ 0 ].location( );
|
||||||
|
if ( input.size( ) > 4 ) {
|
||||||
|
instr.setWidth( parseExpression( instr , input[ 3 ] ) );
|
||||||
|
} else {
|
||||||
|
errors.addNew( "width expected" , input[ 0 ].location( ) );
|
||||||
|
}
|
||||||
|
if ( input.size( ) > 4 ) {
|
||||||
|
instr.setHeight( parseExpression( instr , input[ 3 ] ) );
|
||||||
|
} else {
|
||||||
|
errors.addNew( "height expected" , input[ 0 ].location( ) );
|
||||||
|
}
|
||||||
|
if ( input.size( ) > 5 ) {
|
||||||
|
errors.addNew( "too many arguments" , input[ 5 ].location( ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
M_INSTR_( UseFramebuffer )
|
||||||
|
{
|
||||||
|
parseUseCommon( instructions , input , T_UseInstrNode::FRAMEBUFFER );
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INSTR_( UsePipeline )
|
||||||
|
{
|
||||||
|
parseUseCommon( instructions , input , T_UseInstrNode::PIPELINE );
|
||||||
|
}
|
||||||
|
|
||||||
|
M_INSTR_( UseProgram )
|
||||||
|
{
|
||||||
|
parseUseCommon( instructions , input , T_UseInstrNode::PROGRAM );
|
||||||
|
}
|
||||||
|
|
||||||
|
void T_ParserImpl_::parseUseCommon(
|
||||||
|
T_InstrListNode& instructions ,
|
||||||
|
T_SRDList const& input ,
|
||||||
|
T_UseInstrNode::E_Type type ) noexcept
|
||||||
|
{
|
||||||
|
if ( input.size( ) == 1 || input[ 1 ].type( ) != E_SRDTokenType::WORD ) {
|
||||||
|
errors.addNew( "resource identifier expected" ,
|
||||||
|
input[ input.size( ) == 1 ? 0 : 1 ].location( ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( input.size( ) > 2 ) {
|
||||||
|
errors.addNew( "too many arguments" , input[ 2 ].location( ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& instr{ instructions.add< T_UseInstrNode >( type , input[ 1 ] ) };
|
||||||
|
instr.location( ) = input[ 0 ].location( );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
M_INSTR_( UseTexture )
|
||||||
|
{
|
||||||
|
if ( input.size( ) == 1 || !input[ 1 ].isInteger( ) ) {
|
||||||
|
errors.addNew( "bank number expected" ,
|
||||||
|
input[ input.size( ) == 1 ? 0 : 1 ].location( ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( input[ 1 ].longValue( ) < 0 || input[ 1 ].longValue( ) > UINT32_MAX ) {
|
||||||
|
errors.addNew( "invalid bank number" , input[ 1 ].location( ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( input.size( ) == 2 || input[ 2 ].type( ) != E_SRDTokenType::WORD ) {
|
||||||
|
errors.addNew( "texture identifier expected" ,
|
||||||
|
input[ input.size( ) == 2 ? 0 : 2 ].location( ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( input.size( ) == 3 || input[ 3 ].type( ) != E_SRDTokenType::WORD ) {
|
||||||
|
errors.addNew( "sampler identifier expected" ,
|
||||||
|
input[ input.size( ) == 3 ? 0 : 3 ].location( ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( input.size( ) > 4 ) {
|
||||||
|
errors.addNew( "too many arguments" , input[ 4 ].location( ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& instr{ instructions.add< T_UseTextureInstrNode >(
|
||||||
|
input[ 1 ] , input[ 2 ] , input[ 3 ] ) };
|
||||||
|
instr.location( ) = input[ 0 ].location( );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
M_INSTR_( Viewport )
|
||||||
|
{
|
||||||
|
auto& instr{ instructions.add< T_ViewportInstrNode >( ) };
|
||||||
|
instr.location( ) = input[ 0 ].location( );
|
||||||
|
|
||||||
|
for ( auto i = 1u ; i < 5 ; i ++ ) {
|
||||||
|
T_ViewportInstrNode::E_Parameter p{
|
||||||
|
T_ViewportInstrNode::E_Parameter( i ) };
|
||||||
|
if ( input.size( ) < i ) {
|
||||||
|
T_StringBuilder sb;
|
||||||
|
sb << "missing ";
|
||||||
|
switch ( p ) {
|
||||||
|
case T_ViewportInstrNode::PX: sb << "X"; break;
|
||||||
|
case T_ViewportInstrNode::PY: sb << "Y"; break;
|
||||||
|
case T_ViewportInstrNode::PWIDTH: sb << "width"; break;
|
||||||
|
case T_ViewportInstrNode::PHEIGHT: sb << "height"; break;
|
||||||
|
}
|
||||||
|
sb << " parameter";
|
||||||
|
errors.addNew( std::move( sb ) , input[ 0 ].location( ) );
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
instr.setParameter( p , parseExpression( instr , input[ i ] ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
P_ExpressionNode T_ParserImpl_::parseExpression(
|
||||||
|
A_Node& parent ,
|
||||||
|
T_SRDToken const& token ) noexcept
|
||||||
|
{
|
||||||
|
if ( token.isNumeric( ) ) {
|
||||||
|
return NewOwned< T_ConstantExprNode >( parent , token );
|
||||||
|
}
|
||||||
|
if ( token.type( ) == E_SRDTokenType::WORD || token.type( ) == E_SRDTokenType::VAR ) {
|
||||||
|
return NewOwned< T_IdentifierExprNode >( parent , token );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( token.type( ) == E_SRDTokenType::LIST && !token.list( ).empty( ) ) {
|
||||||
|
return parseOperation( parent , token.list( ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
errors.addNew( "invalid expression" , token.location( ) );
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
P_ExpressionNode T_ParserImpl_::parseOperation(
|
||||||
|
A_Node& parent ,
|
||||||
|
T_SRDList const& opList ) noexcept
|
||||||
|
{
|
||||||
|
T_SRDToken const& opId( opList[ 0 ] );
|
||||||
|
if ( opId.type( ) != E_SRDTokenType::WORD ) {
|
||||||
|
errors.addNew( "operator expected" , opId.location( ) );
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( opId.stringValue( ) == "get-input" ) {
|
||||||
|
if ( opList.size( ) == 1 || !opList[ 1 ].isText( ) ) {
|
||||||
|
errors.addNew( "input identifier expected" ,
|
||||||
|
opList[ opList.size( ) == 1 ? 0 : 1 ].location( ) );
|
||||||
|
return { };
|
||||||
|
}
|
||||||
|
if ( opList.size( ) > 2 ) {
|
||||||
|
errors.addNew( "too many arguments" , opList[ 2 ].location( ) );
|
||||||
|
}
|
||||||
|
auto node{ NewOwned< T_InputExprNode >( parent , opList[ 1 ] ) };
|
||||||
|
node->location( ) = opList[ 0 ].location( );
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( binOpMap.contains( opId.stringValue( ) ) ) {
|
||||||
|
return parseBinOp( parent , opList ,
|
||||||
|
*binOpMap.get( opId.stringValue( ) ) );
|
||||||
|
}
|
||||||
|
if ( unaryOpMap.contains( opId.stringValue( ) ) ) {
|
||||||
|
return parseUnaryOp( parent , opList ,
|
||||||
|
*unaryOpMap.get( opId.stringValue( ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
errors.addNew( "unknown operator" , opId.location( ) );
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
P_ExpressionNode T_ParserImpl_::parseBinOp(
|
||||||
|
A_Node& parent ,
|
||||||
|
T_SRDList const& opList ,
|
||||||
|
T_BinaryOperatorNode::E_Operator op ) noexcept
|
||||||
|
{
|
||||||
|
if ( opList.size( ) < 3 ) {
|
||||||
|
errors.addNew( "not enough arguments" , opList[ 0 ].location( ) );
|
||||||
|
} else if ( opList.size( ) > 3 ) {
|
||||||
|
errors.addNew( "too many arguments" , opList[ 3 ].location( ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
T_OwnPtr< T_BinaryOperatorNode > opNode{
|
||||||
|
NewOwned< T_BinaryOperatorNode >( parent , op ) };
|
||||||
|
opNode->location( ) = opList[ 0 ].location( );
|
||||||
|
|
||||||
|
if ( opList.size( ) > 1 ) {
|
||||||
|
auto left{ parseExpression( *opNode , opList[ 1 ] ) };
|
||||||
|
if ( left ) {
|
||||||
|
opNode->setLeft( std::move( left ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( opList.size( ) > 2 ) {
|
||||||
|
auto right{ parseExpression( *opNode , opList[ 2 ] ) };
|
||||||
|
if ( right ) {
|
||||||
|
opNode->setRight( std::move( right ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return opNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
P_ExpressionNode T_ParserImpl_::parseUnaryOp(
|
||||||
|
A_Node& parent ,
|
||||||
|
T_SRDList const& opList ,
|
||||||
|
T_UnaryOperatorNode::E_Operator op ) noexcept
|
||||||
|
{
|
||||||
|
if ( opList.size( ) < 2 ) {
|
||||||
|
errors.addNew( "not enough arguments" , opList[ 0 ].location( ) );
|
||||||
|
} else if ( opList.size( ) > 2 ) {
|
||||||
|
errors.addNew( "too many arguments" , opList[ 2 ].location( ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
T_OwnPtr< T_UnaryOperatorNode > opNode{
|
||||||
|
NewOwned< T_UnaryOperatorNode >( parent , op ) };
|
||||||
|
opNode->location( ) = opList[ 0 ].location( );
|
||||||
|
|
||||||
|
if ( opList.size( ) > 1 ) {
|
||||||
|
auto argument{ parseExpression( *opNode , opList[ 1 ] ) };
|
||||||
|
if ( argument ) {
|
||||||
|
opNode->setArgument( std::move( argument ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return opNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // 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 >( );
|
||||||
|
p< T_ParserImpl_ >( ).parseTopLevel( input );
|
||||||
|
return errors_.empty( );
|
||||||
|
}
|
Loading…
Reference in a new issue