corelib/src/SRDPPCommands.cc

2518 lines
63 KiB
C++

/******************************************************************************/
/* SRD - PREPROCESSOR COMMANDS ************************************************/
/******************************************************************************/
#include <lw/lib/MemoryStreams.hh>
#include <lw/lib/SRDBinary.hh>
#include <lw/lib/SRDPPCommands.hh>
#include <lw/lib/SRDText.hh>
#include <lw/lib/VFS.hh>
#include <cmath>
using namespace lw;
/*= BUILTIN COMMANDS INITIALIZER =============================================*/
#define M_ADDCMD_( NAME , ARGS... ) \
add< T_SRDPPCMD_ ## NAME >( ARGS )
void T_SRDPreprocessorConfig::addBuiltinCommands( ) noexcept
{
M_ADDCMD_( Add );
M_ADDCMD_( And );
M_ADDCMD_( Bless );
M_ADDCMD_( Break );
M_ADDCMD_( BwAnd );
M_ADDCMD_( BwNot );
M_ADDCMD_( BwOr );
M_ADDCMD_( BwXor );
M_ADDCMD_( Call );
M_ADDCMD_( CastString );
M_ADDCMD_( CastWord );
M_ADDCMD_( CastInt );
M_ADDCMD_( CastLong );
M_ADDCMD_( CastBestInt );
M_ADDCMD_( CastReal );
M_ADDCMD_( CastVar );
M_ADDCMD_( CastList );
M_ADDCMD_( ClearScope );
M_ADDCMD_( Cmp );
M_ADDCMD_( CmpEq );
M_ADDCMD_( CmpNe );
M_ADDCMD_( CmpLt );
M_ADDCMD_( CmpGt );
M_ADDCMD_( CmpLe );
M_ADDCMD_( CmpGe );
M_ADDCMD_( Concat );
M_ADDCMD_( Div );
M_ADDCMD_( EndsWith );
M_ADDCMD_( Eval );
M_ADDCMD_( Error );
M_ADDCMD_( FromSource );
M_ADDCMD_( FromSRB );
M_ADDCMD_( Get );
M_ADDCMD_( If );
M_ADDCMD_( Ignore );
M_ADDCMD_( IsBlessed );
M_ADDCMD_( IsMacro );
M_ADDCMD_( IsSet );
M_ADDCMD_( Length );
M_ADDCMD_( ListMacros );
M_ADDCMD_( ListVariables );
M_ADDCMD_( Mod );
M_ADDCMD_( Mul );
M_ADDCMD_( Neg );
M_ADDCMD_( Not );
M_ADDCMD_( Or );
M_ADDCMD_( Output );
M_ADDCMD_( Raw );
M_ADDCMD_( Rethrow );
M_ADDCMD_( Scope );
M_ADDCMD_( Set );
M_ADDCMD_( SetMacro );
M_ADDCMD_( StartsWith );
M_ADDCMD_( StrFind );
M_ADDCMD_( StrSplit );
M_ADDCMD_( Sub );
M_ADDCMD_( Substr );
M_ADDCMD_( ToSource );
M_ADDCMD_( Try );
M_ADDCMD_( TypeOf );
M_ADDCMD_( Unset );
M_ADDCMD_( UnsetMacro );
M_ADDCMD_( Unwrap );
M_ADDCMD_( Xor );
}
void T_SRDPreprocessorConfig::addVFSCommands(
T_VFS& vfs ) noexcept
{
M_ADDCMD_( VFSList , vfs );
M_ADDCMD_( VFSLoad , vfs );
M_ADDCMD_( VFSType , vfs );
}
/*= HELPER FOR MATH COMMANDS =================================================*/
typedef std::function< int64_t( int64_t , int64_t ) > F_LongOp_;
typedef std::function< double ( double , double ) > F_RealOp_;
namespace {
void DoMathOp_( T_SRDPreprocessorState& state , F_LongOp_ longOp , F_RealOp_ realOp , bool nonZero = false )
{
auto& input( state.output( ) );
auto& output( state.data( ).top( ).output( ) );
auto const& location( *state.initialLocation( ) );
const uint32_t nInputs( input.size( ) );
if ( nInputs < 2 ) {
state.data( ).addError( location , "not enough arguments" );
if ( nInputs == 0 ) {
T_SRDToken r( T_SRDToken::Integer( 0 ) );
r.location( location );
output.add( std::move( r ) );
return;
}
}
const E_SRDTokenType type( input[ 0 ].type( ) );
if ( type != E_SRDTokenType::INT && type != E_SRDTokenType::LONG && type != E_SRDTokenType::FLOAT ) {
state.data( ).addError( input[ 0 ] , "numeric argument expected" );
}
bool hadReal( type == E_SRDTokenType::FLOAT );
int64_t rl( input[ 0 ].longValue( ) );
double rr( input[ 0 ].floatValue( ) );
for ( uint32_t i = 1 ; i < nInputs ; i ++ ) {
auto& tok( input[ i ] );
const auto tt( tok.type( ) );
if ( tt != E_SRDTokenType::INT && tt != E_SRDTokenType::LONG &&
tt != E_SRDTokenType::FLOAT ) {
state.data( ).addError( tok , "numeric argument expected" );
continue;
}
if ( tok.floatValue( ) == 0 && nonZero ) {
state.data( ).addError( tok , "non-zero argument expected" );
continue;
}
hadReal = hadReal || tt == E_SRDTokenType::FLOAT;
if ( !hadReal ) {
rl = longOp( rl , tok.longValue( ) );
}
rr = realOp( rr , tok.floatValue( ) );
}
T_SRDToken result( hadReal ? T_SRDToken::Float( rr ) : T_SRDToken::AutoInteger( rl ) );
result.location( location );
output.add( std::move( result ) );
}
void DoLogicOp_( T_SRDPreprocessorState& state , F_LongOp_ op )
{
auto& input( state.output( ) );
auto& output( state.data( ).top( ).output( ) );
auto const& location( *state.initialLocation( ) );
const uint32_t nInputs( input.size( ) );
if ( nInputs < 2 ) {
state.data( ).addError( location , "not enough arguments" );
if ( nInputs == 0 ) {
T_SRDToken r( T_SRDToken::Integer( 0 ) );
r.location( location );
output.add( std::move( r ) );
return;
}
}
if ( !input[ 0 ].isInteger( ) ) {
state.data( ).addError( input[ 0 ] , "integer argument expected" );
}
int64_t rl( input[ 0 ].longValue( ) );
for ( uint32_t i = 1 ; i < nInputs ; i ++ ) {
auto& tok( input[ i ] );
if ( !tok.isInteger( ) ) {
state.data( ).addError( tok , "integer argument expected" );
continue;
}
rl = op( rl , tok.longValue( ) );
}
T_SRDToken result( T_SRDToken::AutoInteger( rl ) );
result.location( location );
output.add( std::move( result ) );
}
}
/*= HELPER FOR COMPARISONS ===================================================*/
namespace {
bool CompareInput_( T_SRDPreprocessorState & state , int & result )
{
auto& data( state.data( ) );
auto& input( state.output( ) );
auto& loc( *state.initialLocation( ) );
if ( input.size( ) < 2 ) {
data.addError( loc , "not enough arguments" );
return false;
}
if ( input.size( ) > 2 ) {
data.addError( loc , "too many arguments" );
return false;
}
result = input[ 0 ].compare( input[ 1 ] );
return true;
}
}
/*= COMMANDS =================================================================*/
M_SRDPP_COMMAND_EXEC( Add )
{
DoMathOp_( state ,
[]( auto a , auto b ) {
return a + b;
} ,
[]( auto a , auto b ) {
return a + b;
} );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( And )
{
DoLogicOp_( state ,
[]( auto a , auto b ) {
return ( a && b ) ? 1 : 0;
} );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Bless )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
const auto n( input.size( ) );
if ( n == 0 ) {
data.addError( loc , "not enough arguments" );
return;
}
for ( uint32_t i = 0 ; i < n ; i ++ ) {
auto const& ntok( input[ i ] );
if ( ntok.type( ) != E_SRDTokenType::WORD ) {
data.addError( ntok , "word expected" );
continue;
}
auto const& vn( ntok.stringValue( ) );
auto& s( data.scopes( ) );
if ( s.isBlessed( vn ) ) {
continue;
}
RPC_SRDList var( s.get( false , vn ) );
if ( var == nullptr ) {
data.addError( ntok , "unknown variable" );
continue;
}
if ( T_SRDPreprocessor::isValidFunction( *var ) ) {
s.bless( vn );
} else {
data.addError( ntok , "variable does not contain a valid function" );
}
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Break )
{
if ( state.output( ).size( ) ) {
state.data().addError( *state.initialLocation( ) , "too many arguments" );
}
state.data( ).interrupt( T_SRDPreprocessorState::C_CALL );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( BwAnd )
{
DoLogicOp_( state ,
[]( auto a , auto b ) {
return a & b;
} );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( BwNot )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
bool useLong( false );
int64_t iv( 0 );
if ( input.size( ) == 0 ) {
data.addError( loc , "not enough arguments" );
} else if ( input.size( ) != 1 ) {
data.addError( loc , "too many arguments" );
} else if ( !input[ 0 ].isInteger( ) ) {
data.addError( input[ 0 ] , "integer argument expected" );
} else {
iv = ~input[ 0 ].longValue( );
useLong = ( input[ 0 ].type( ) == E_SRDTokenType::LONG );
if ( !useLong ) {
iv = iv & 0xffffffff;
}
}
T_SRDToken output( useLong ? T_SRDToken::Long( iv ) : T_SRDToken::Integer( iv ) );
output.location( loc );
state.data( ).top( ).output( ).add( std::move( output ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( BwOr )
{
DoLogicOp_( state ,
[]( auto a , auto b ) {
return a | b;
} );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( BwXor )
{
DoLogicOp_( state ,
[]( auto a , auto b ) {
return a ^ b;
} );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Call )
{
auto& data( state.data( ) );
auto& input( state.output( ) );
auto const& loc( *state.initialLocation( ) );
if ( input.size( ) < 1 ) {
data.addError( loc , "not enough arguments" );
return;
}
auto& func( input[ 0 ] );
if ( !T_SRDPreprocessor::isValidFunction( func ) ) {
data.addError( func , "invalid function" );
return;
}
auto const& argsTok( func.list( )[ 0 ] );
auto const& args( argsTok.list( ) );
const bool hasOptArgs( args.size( ) > 0
&& args[ args.size( ) - 1 ].type( ) == E_SRDTokenType::LIST );
const uint32_t reqArgs( args.size( ) - ( hasOptArgs ? 1 : 0 ) );
const auto nValues( input.size( ) - 1 );
if ( nValues < reqArgs ) {
data.addError( loc , "not enough function arguments" );
return;
}
if ( nValues > reqArgs && !hasOptArgs ) {
data.addError( loc , "too many function arguments" );
return;
}
data.scopes( ).setupCallScope( args , input , 1 );
data.push( T_SRDPreprocessorState::C_CALL ,
NewShared< T_SRDList >( func.list( ).moveRange( 1 ) ) ,
data.top( ).outputPointer( ) ,
[]( T_SRDPreprocessorState& ns ) {
ns.data( ).scopes( ).exit( );
} );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( CastString )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto& output( data.top( ).output( ) );
const auto size( input.size( ) );
for ( uint32_t i = 0 ; i < size ; i ++ ) {
auto& token( input[ i ] );
T_String outString;
switch ( token.type( ) ) {
case E_SRDTokenType::STRING:
output.add( std::move( token ) );
continue;
case E_SRDTokenType::WORD:
outString = token.stringValue( );
break;
case E_SRDTokenType::INT:
case E_SRDTokenType::LONG:
{
T_StringBuilder sb;
sb << token.longValue( );
outString = std::move( sb );
break;
}
case E_SRDTokenType::FLOAT:
{
T_StringBuilder sb;
sb << token.floatValue( );
outString = std::move( sb );
break;
}
case E_SRDTokenType::VAR:
{
T_StringBuilder sb;
sb << '$' << token.stringValue( );
outString = std::move( sb );
break;
}
case E_SRDTokenType::BINARY:
{
auto const& buffer( token.binary( ) );
outString = T_String( (char const*) buffer.data( ) ,
buffer.bytes( ) );
if ( !outString.valid( ) ) {
outString = T_String( );
data.addError( token , "invalid UTF-8 data" );
}
break;
}
default:
data.addError( token , "unsupported type conversion" );
continue;
}
T_SRDToken otok( T_SRDToken::String( std::move( outString ) ) );
otok.copyLocationOf( token );
output.add( std::move( otok ) );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( CastWord )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto& output( data.top( ).output( ) );
const auto size( input.size( ) );
for ( uint32_t i = 0 ; i < size ; i ++ ) {
auto& token( input[ i ] );
T_String outString;
switch ( token.type( ) ) {
case E_SRDTokenType::STRING:
outString = token.stringValue( );
if ( !T_SRDToken::IsWord( outString ) ) {
data.addError( token , "invalid word" );
continue;
}
break;
case E_SRDTokenType::WORD:
output.add( std::move( token ) );
continue;
case E_SRDTokenType::VAR:
outString = token.stringValue( );
break;
default:
data.addError( token , "unsupported type conversion" );
continue;
}
T_SRDToken otok( T_SRDToken::Word( std::move( outString ) ) );
otok.copyLocationOf( token );
output.add( std::move( otok ) );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( CastInt )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto& output( data.top( ).output( ) );
const auto size( input.size( ) );
for ( uint32_t i = 0 ; i < size ; i ++ ) {
auto& token( input[ i ] );
int64_t value;
switch ( token.type( ) ) {
case E_SRDTokenType::INT:
output.add( std::move( token ) );
continue;
case E_SRDTokenType::STRING:
{
bool ok;
value = token.stringValue( ).toInteger( &ok );
if ( !ok ) {
data.addError( token , "invalid integer value" );
continue;
}
break;
}
case E_SRDTokenType::LONG:
value = token.longValue( );
break;
case E_SRDTokenType::FLOAT:
value = token.floatValue( );
break;
default:
data.addError( token , "unsupported type conversion" );
continue;
}
if ( value < INT32_MIN || value > INT32_MAX ) {
data.addError( token , "value out of range" );
continue;
}
T_SRDToken otok( T_SRDToken::Integer( value ) );
otok.copyLocationOf( token );
output.add( std::move( otok ) );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( CastLong )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto& output( data.top( ).output( ) );
const auto size( input.size( ) );
for ( uint32_t i = 0 ; i < size ; i ++ ) {
auto& token( input[ i ] );
int64_t value;
switch ( token.type( ) ) {
case E_SRDTokenType::LONG:
output.add( std::move( token ) );
continue;
case E_SRDTokenType::STRING:
{
bool ok;
value = token.stringValue( ).toInteger( &ok );
if ( !ok ) {
data.addError( token , "invalid integer value" );
continue;
}
break;
}
case E_SRDTokenType::INT:
value = token.longValue( );
break;
case E_SRDTokenType::FLOAT:
value = token.floatValue( );
break;
default:
data.addError( token , "unsupported type conversion" );
continue;
}
T_SRDToken otok( T_SRDToken::Long( value ) );
otok.copyLocationOf( token );
output.add( std::move( otok ) );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( CastBestInt )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto& output( data.top( ).output( ) );
const auto size( input.size( ) );
for ( uint32_t i = 0 ; i < size ; i ++ ) {
auto& token( input[ i ] );
int64_t value;
switch ( token.type( ) ) {
case E_SRDTokenType::INT:
output.add( std::move( token ) );
continue;
case E_SRDTokenType::STRING:
{
bool ok;
value = token.stringValue( ).toInteger( &ok );
if ( !ok ) {
data.addError( token , "invalid integer value" );
continue;
}
break;
}
case E_SRDTokenType::LONG:
value = token.longValue( );
break;
case E_SRDTokenType::FLOAT:
value = token.floatValue( );
break;
default:
data.addError( token , "unsupported type conversion" );
continue;
}
T_SRDToken otok( T_SRDToken::AutoInteger( value ) );
otok.copyLocationOf( token );
output.add( std::move( otok ) );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( CastReal )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto& output( data.top( ).output( ) );
const auto size( input.size( ) );
for ( uint32_t i = 0 ; i < size ; i ++ ) {
auto& token( input[ i ] );
double value;
switch ( token.type( ) ) {
case E_SRDTokenType::FLOAT:
output.add( std::move( token ) );
continue;
case E_SRDTokenType::STRING:
{
bool ok;
value = token.stringValue( ).toDouble( &ok );
if ( !ok ) {
data.addError( token , "invalid real value" );
continue;
}
break;
}
case E_SRDTokenType::INT:
case E_SRDTokenType::LONG:
value = token.longValue( );
break;
default:
data.addError( token , "unsupported type conversion" );
continue;
}
T_SRDToken otok( T_SRDToken::Float( value ) );
otok.copyLocationOf( token );
output.add( std::move( otok ) );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( CastVar )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto& output( data.top( ).output( ) );
const auto size( input.size( ) );
for ( uint32_t i = 0 ; i < size ; i ++ ) {
auto& token( input[ i ] );
T_String outString;
switch ( token.type( ) ) {
case E_SRDTokenType::STRING:
outString = token.stringValue( );
if ( outString[ 0 ] == '$' ) {
outString = outString.substr( 1 );
}
if ( !T_SRDToken::IsWord( outString ) || outString[ 0 ] == '-' ) {
data.addError( token , "invalid variable name" );
continue;
}
break;
case E_SRDTokenType::WORD:
outString = token.stringValue( );
if ( outString[ 0 ] == '-' ) {
data.addError( token , "invalid variable name" );
continue;
}
break;
case E_SRDTokenType::VAR:
output.add( std::move( token ) );
continue;
default:
data.addError( token , "unsupported type conversion" );
continue;
}
T_SRDToken otok( T_SRDToken::Variable( std::move( outString ) ) );
otok.copyLocationOf( token );
output.add( std::move( otok ) );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( CastList )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto& output( data.top( ).output( ) );
const auto size( input.size( ) );
for ( uint32_t i = 0 ; i < size ; i ++ ) {
auto& token( input[ i ] );
switch ( token.type( ) ) {
case E_SRDTokenType::STRING:
case E_SRDTokenType::WORD:
{
T_StringIterator it( token.stringValue( ) );
T_StringBuilder sb;
while ( !it.atEnd( ) ) {
T_Character c( it );
it.next( );
sb << c;
T_SRDToken ctok( T_SRDToken::String( sb ) );
ctok.copyLocationOf( token );
output.add( std::move( ctok ) );
sb.clear( );
}
break;
}
case E_SRDTokenType::BINARY:
{
auto const& buffer( token.binary( ) );
const auto bs( buffer.size( ) );
for ( auto i = 0u ; i < bs ; i ++ ) {
T_SRDToken vtok( T_SRDToken::Integer( buffer[ i ] ) );
vtok.copyLocationOf( token );
output.add( std::move( vtok ) );
}
break;
}
case E_SRDTokenType::LIST:
{
auto const& l( token.list( ) );
const auto ls( l.size( ) );
for ( auto i = 0u ; i < ls ; i ++ ) {
output.add( std::move( l[ i ] ) );
}
break;
}
default:
output.add( std::move( token ) );
continue;
}
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( ClearScope )
{
if ( state.output( ).size( ) != 0 ) {
auto& data( state.data( ) );
data.scopes( ).enter( );
data.scopes( ).clear( );
data.push( T_SRDPreprocessorState::C_INNER ,
state.outputPointer( ) , data.top( ).outputPointer( ) ,
[]( T_SRDPreprocessorState & ns ) {
ns.data( ).scopes( ).exit( );
} );
} else {
state.data( ).scopes( ).clear( );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Cmp )
{
int result;
bool success( CompareInput_( state , result ) );
T_SRDToken token( T_SRDToken::Integer( success ? result : 0 ) );
token.location( *state.initialLocation( ) );
state.data( ).top( ).output( ).add( std::move( token ) );
}
M_SRDPP_COMMAND_EXEC( CmpEq )
{
int result;
bool success( CompareInput_( state , result ) );
T_SRDToken token( T_SRDToken::Integer( ( success && result == 0 ) ? 1 : 0 ) );
token.location( *state.initialLocation( ) );
state.data( ).top( ).output( ).add( std::move( token ) );
}
M_SRDPP_COMMAND_EXEC( CmpNe )
{
int result;
bool success( CompareInput_( state , result ) );
T_SRDToken token( T_SRDToken::Integer( ( success && result != 0 ) ? 1 : 0 ) );
token.location( *state.initialLocation( ) );
state.data( ).top( ).output( ).add( std::move( token ) );
}
M_SRDPP_COMMAND_EXEC( CmpLt )
{
int result;
bool success( CompareInput_( state , result ) );
T_SRDToken token( T_SRDToken::Integer( ( success && result < 0 ) ? 1 : 0 ) );
token.location( *state.initialLocation( ) );
state.data( ).top( ).output( ).add( std::move( token ) );
}
M_SRDPP_COMMAND_EXEC( CmpGt )
{
int result;
bool success( CompareInput_( state , result ) );
T_SRDToken token( T_SRDToken::Integer( ( success && result > 0 ) ? 1 : 0 ) );
token.location( *state.initialLocation( ) );
state.data( ).top( ).output( ).add( std::move( token ) );
}
M_SRDPP_COMMAND_EXEC( CmpLe )
{
int result;
bool success( CompareInput_( state , result ) );
T_SRDToken token( T_SRDToken::Integer( ( success && result <= 0 ) ? 1 : 0 ) );
token.location( *state.initialLocation( ) );
state.data( ).top( ).output( ).add( std::move( token ) );
}
M_SRDPP_COMMAND_EXEC( CmpGe )
{
int result;
bool success( CompareInput_( state , result ) );
T_SRDToken token( T_SRDToken::Integer( ( success && result >= 0 ) ? 1 : 0 ) );
token.location( *state.initialLocation( ) );
state.data( ).top( ).output( ).add( std::move( token ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Concat )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto& output( data.top( ).output( ) );
auto const& loc( *state.initialLocation( ) );
const auto size( input.size( ) );
if ( size == 0 ) {
data.addError( loc , "not enough arguments" );
T_SRDToken otok( T_SRDToken::String( T_String( "" ) ) );
otok.location( loc );
output.add( std::move( otok ) );
return;
}
// Find out whether this is a string concatenation or a binary
// concatenation.
uint32_t binLength = 0;
bool isBinary = true;
for ( uint32_t i = 0 ; i < size ; i ++ ) {
auto const& token( input[ i ] );
if ( token.type( ) != E_SRDTokenType::BINARY ) {
isBinary = false;
break;
}
binLength += token.binary( ).size( );
}
// Binary concat
if ( isBinary ) {
auto ob( NewShared< T_Buffer< uint8_t > >( binLength ) );
binLength = 0;
for ( uint32_t i = 0 ; i < size ; i ++ ) {
auto const& bin( input[ i ].binary( ) );
memcpy( ob->data( ) + binLength ,
bin.data( ) , bin.size( ) );
binLength += bin.size( );
}
T_SRDToken otok( T_SRDToken::Binary( ob ) );
otok.location( loc );
output.add( std::move( otok ) );
return;
}
// String concat
T_StringBuilder sb;
for ( uint32_t i = 0 ; i < size ; i ++ ) {
auto const& token( input[ i ] );
if ( !( token.isText( ) || token.isNumeric( ) ) ) {
data.addError( token , "expected text or numeric argument" );
continue;
}
switch ( token.type( ) ) {
case E_SRDTokenType::STRING:
case E_SRDTokenType::WORD:
sb << token.stringValue( );
continue;
case E_SRDTokenType::INT:
case E_SRDTokenType::LONG:
sb << token.longValue( );
break;
case E_SRDTokenType::FLOAT:
sb << token.floatValue( );
break;
default:
assert( 0 && "we shouldn't be here" );
continue;
}
}
T_SRDToken otok( T_SRDToken::String( std::move( sb ) ) );
otok.location( loc );
output.add( std::move( otok ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Div )
{
DoMathOp_( state ,
[]( auto a , auto b ) {
return a / b;
} ,
[]( auto a , auto b ) {
return a / b;
} , true );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( EndsWith )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
uint32_t value = 0;
if ( input.size( ) < 2 ) {
data.addError( loc , "not enough arguments" );
} else if ( input.size( ) > 2 ) {
data.addError( loc , "too many arguments" );
} else {
if ( input[ 0 ].isText( ) && input[ 1 ].isText( ) ) {
value = input[ 1 ].stringValue( ).endsWith(
input[ 0 ].stringValue( ) );
} else if ( input[ 0 ].type( ) == E_SRDTokenType::BINARY
&& input[ 1 ].type( ) == E_SRDTokenType::BINARY ) {
auto const& needle( input[ 0 ].binary( ) );
auto const& haystack( input[ 1 ].binary( ) );
auto const nsz( needle.size( ) );
auto const hsz( haystack.size( ) );
value = ( nsz <= hsz
&& !( nsz && memcmp( needle.data( ) ,
haystack.data( ) + hsz - nsz ,
nsz ) ) );
} else {
data.addError( loc , "invalid arguments" );
}
}
auto tok( T_SRDToken::Integer( value ) );
tok.location( loc );
data.top( ).output( ).add( std::move( tok ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Eval )
{
if ( state.output( ).size( ) == 0 ) {
return;
}
auto& data( state.data( ) );
// data.push( T_SRDPreprocessorState::C_CALL , state.outputPointer( ) ,
// data.top( ).outputPointer( ) );
auto& input( state.output( ) );
const auto is( input.size( ) );
if ( is ) {
const SP_SRDLocation evalLocation( state.createChainedLocation( ) );
SP_SRDList updated( NewShared< T_SRDList >( is ) );
for ( size_t i = 0 ; i < is ; i ++ ) {
T_SRDToken token( std::move( input[ i ] ) );
auto& loc( token.location( ) );
SP_SRDLocation el( ( i == is - 1 )
? evalLocation
: NewShared< T_SRDLocation >( *evalLocation ) );
if ( loc.isChained( ) ) {
auto const& lc( loc.chaining( ) );
el->chain( lc.circumstances , lc.location );
}
loc.chain( E_SRDLocationChaining::EVALUATED , el );
updated->add( std::move( token ) );
}
data.push( T_SRDPreprocessorState::C_UNSPECIFIED , updated ,
data.top( ).outputPointer( ) );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Error )
{
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
auto& input( state.output( ) );
const auto nTokens( input.size( ) );
if ( nTokens == 0 ) {
data.addError( loc , "user error" );
} else {
T_StringBuilder sb;
for ( uint32_t i = 0 ; i < nTokens ; i ++ ) {
if ( i > 0 ) {
sb << ' ';
}
auto& tok( input[ i ] );
switch( tok.type( ) ) {
case E_SRDTokenType::VAR:
sb << '$';
case E_SRDTokenType::STRING:
case E_SRDTokenType::WORD:
sb << tok.stringValue( );
break;
case E_SRDTokenType::INT:
case E_SRDTokenType::LONG:
sb << tok.longValue( );
break;
case E_SRDTokenType::FLOAT:
sb << tok.floatValue( );
break;
case E_SRDTokenType::LIST:
tok.generateFullText( );
sb << tok.fullText( );
break;
default:
sb << "(INTERNAL ERROR)";
break;
}
}
data.addError( loc , sb );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( FromSource )
{
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
auto& input( state.output( ) );
const auto is( input.size( ) );
if ( is == 0 ) {
data.addError( loc , "not enough arguments" );
return;
}
if ( is > 2 ) {
data.addError( loc , "too many arguments" );
}
if ( input[ 0 ].type( ) != E_SRDTokenType::STRING ) {
data.addError( input[ 0 ] , "string expected" );
return;
}
if ( is == 2 && !input[ 1 ].isText( ) ) {
data.addError( input[ 1 ] , "word or string expected" );
}
const T_String inputName( ( is == 2 && input[ 1 ].isText( ) )
? input[ 1 ].stringValue( )
: T_String::Pooled( "(-from-source)" ) );
T_SRDErrors& errors( data.errorContext( ) );
T_SRDMemoryTarget tgt( true );
T_SRDLexer lexer( inputName , errors , tgt );
tgt.start( errors );
T_StringIterator it( input[ 0 ].stringValue( ) );
while ( !it.atEnd( ) ) {
lexer.processCharacter( it );
it.next( );
}
lexer.processEnd( );
auto const& generated( tgt.list( ) );
auto& output( data.top( ).output( ) );
const auto gs( generated.size( ) );
const SP_SRDLocation gloc( state.createChainedLocation( ) );
for ( size_t i = 0 ; i < gs ; i ++ ) {
T_SRDToken t( generated[ i ] );
t.location( ).chain( E_SRDLocationChaining::GENERATED , gloc );
output.add( std::move( t ) );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( FromSRB )
{
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
auto& input( state.output( ) );
const auto is( input.size( ) );
if ( is == 0 ) {
data.addError( loc , "not enough arguments" );
return;
}
if ( is > 2 ) {
data.addError( loc , "too many arguments" );
}
if ( input[ 0 ].type( ) != E_SRDTokenType::BINARY ) {
data.addError( input[ 0 ] , "binary array expected" );
return;
}
if ( is == 2 && !input[ 1 ].isText( ) ) {
data.addError( input[ 1 ] , "word or string expected" );
}
const T_String inputName( ( is == 2 && input[ 1 ].isText( ) )
? input[ 1 ].stringValue( )
: T_String::Pooled( "(-from-srb)" ) );
T_SRDErrors& errors( data.errorContext( ) );
T_SRDMemoryTarget tgt( true );
T_SRDBinaryReader reader( tgt );
T_MemoryInputStream istream( input[ 0 ].binary( ) );
try {
reader.read( inputName , istream );
} catch ( X_SRDErrors const& srbErrors ) {
errors.addAll( srbErrors.errors );
}
data.top( ).output( ).addAll( tgt.list( ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Get )
{
auto& data( state.data( ) );
auto& input( state.output( ) );
const auto sz( input.size( ) );
auto& output( state.data( ).top( ).output( ) );
for ( uint32_t i = 0 ; i < sz ; i ++ ) {
auto const& vtok( input[ i ] );
if ( vtok.type( ) != E_SRDTokenType::WORD ) {
data.addError( vtok , "word expected" );
continue;
}
auto var( data.scopes( ).get( false , vtok.stringValue( ) ) );
if ( var == nullptr ) {
data.addError( vtok , "unknown variable" );
} else {
output.addAll( *var );
}
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( If )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
const auto sz( input.size( ) );
if ( sz < 2 ) {
data.addError( loc , "not enough arguments" );
return;
}
if ( sz > 3 ) {
data.addError( loc , "too many arguments" );
return;
}
auto const& cdTok( input[ 0 ] );
if ( cdTok.type( ) != E_SRDTokenType::INT && cdTok.type( ) != E_SRDTokenType::LONG ) {
data.addError( cdTok , "integer expected" );
return;
}
if ( input[ 1 ].type( ) != E_SRDTokenType::LIST ) {
data.addError( input[ 1 ] , "list expected" );
return;
}
if ( sz == 3 && input[ 2 ].type( ) != E_SRDTokenType::LIST ) {
data.addError( input[ 2 ] , "list expected" );
return;
}
const bool cond( cdTok.longValue( ) != 0 );
if ( sz == 2 && !cond ) {
return;
}
data.push( T_SRDPreprocessorState::C_INNER ,
NewShared< T_SRDList >( input[ cond ? 1 : 2 ].list( ) ) ,
data.top( ).outputPointer( ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Ignore )
{
(void) state;
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( IsBlessed )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
bool blessed;
if ( input.size( ) == 0 ) {
data.addError( loc , "not enough arguments" );
blessed = false;
} else if ( input.size( ) > 1 ) {
data.addError( loc , "too many arguments" );
blessed = false;
} else if ( input[ 0 ].type( ) != E_SRDTokenType::WORD ) {
data.addError( input[ 0 ] , "word expected" );
blessed = false;
} else {
blessed = data.scopes( ).isBlessed( input[ 0 ].stringValue( ) );
}
T_SRDToken output( T_SRDToken::Integer( blessed ? 1 : 0 ) );
output.location( loc );
state.data( ).top( ).output( ).add( std::move( output ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( IsMacro )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
bool set;
if ( input.size( ) == 0 ) {
data.addError( loc , "not enough arguments" );
set = false;
} else if ( input.size( ) > 1 ) {
data.addError( loc , "too many arguments" );
set = false;
} else if ( input[ 0 ].type( ) != E_SRDTokenType::WORD ) {
data.addError( input[ 0 ] , "word expected" );
set = false;
} else {
set = ( data.scopes( ).get( true , input[ 0 ].stringValue( ) ) != nullptr );
}
T_SRDToken output( T_SRDToken::Integer( set ? 1 : 0 ) );
output.location( loc );
state.data( ).top( ).output( ).add( std::move( output ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( IsSet )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
bool set;
if ( input.size( ) == 0 ) {
data.addError( loc , "not enough arguments" );
set = false;
} else if ( input.size( ) > 1 ) {
data.addError( loc , "too many arguments" );
set = false;
} else if ( input[ 0 ].type( ) != E_SRDTokenType::WORD ) {
data.addError( input[ 0 ] , "word expected" );
set = false;
} else {
set = ( data.scopes( ).get( false , input[ 0 ].stringValue( ) ) != nullptr );
}
T_SRDToken output( T_SRDToken::Integer( set ? 1 : 0 ) );
output.location( loc );
state.data( ).top( ).output( ).add( std::move( output ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Length )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
int64_t length;
if ( input.size( ) == 0 ) {
data.addError( loc , "not enough arguments" );
length = -1;
} else if ( input.size( ) > 1 ) {
data.addError( loc , "too many arguments" );
length = -1;
} else if ( input[ 0 ].type( ) == E_SRDTokenType::LIST ) {
length = input[ 0 ].list( ).size( );
} else if ( input[ 0 ].type( ) == E_SRDTokenType::BINARY ) {
length = input[ 0 ].binary( ).size( );
} else if ( input[ 0 ].isText( ) ) {
length = input[ 0 ].stringValue( ).length( );
} else {
data.addError( input[ 0 ] , "list, word, string or binary array expected" );
length = -1;
}
T_SRDToken output( T_SRDToken::AutoInteger( length ) );
output.location( loc );
state.data( ).top( ).output( ).add( std::move( output ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( ListMacros )
{
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
if ( state.output( ).size( ) != 0 ) {
data.addError( loc , "too many arguments" );
}
auto & output( data.top( ).output( ) );
T_Array< T_String > macroNames( data.scopes( ).list( true ) );
const auto nMacros( macroNames.size( ) );
for ( uint32_t i = 0 ; i < nMacros ; i ++ ) {
T_SRDToken tok( T_SRDToken::Word( macroNames[ i ] ) );
tok.location( loc );
output.add( std::move( tok ) );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( ListVariables )
{
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
if ( state.output( ).size( ) != 0 ) {
data.addError( loc , "too many arguments" );
}
auto & output( data.top( ).output( ) );
T_Array< T_String > varNames( data.scopes( ).list( false ) );
const auto nVars( varNames.size( ) );
for ( uint32_t i = 0 ; i < nVars ; i ++ ) {
T_SRDToken tok( T_SRDToken::Word( varNames[ i ] ) );
tok.location( loc );
output.add( std::move( tok ) );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Mod )
{
DoMathOp_( state ,
[]( auto a , auto b ) {
return a % b;
} ,
[]( auto a , auto b ) {
return std::fmod( a , b );
} , true );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Mul )
{
DoMathOp_( state ,
[]( auto a , auto b ) {
return a * b;
} ,
[]( auto a , auto b ) {
return a * b;
} );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Neg )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
bool of( false );
int64_t iv( 0 );
double fv( 0 );
if ( input.size( ) == 0 ) {
data.addError( loc , "not enough arguments" );
} else if ( input.size( ) != 1 ) {
data.addError( loc , "too many arguments" );
} else if ( !input[ 0 ].isNumeric( ) ) {
data.addError( input[ 0 ] , "numeric argument expected" );
} else if ( input[ 0 ].isInteger( ) ) {
iv = -input[ 0 ].longValue( );
} else {
fv = -input[ 0 ].floatValue( );
of = true;
}
T_SRDToken output( of ? T_SRDToken::Float( fv ) : T_SRDToken::AutoInteger( iv ) );
output.location( loc );
state.data( ).top( ).output( ).add( std::move( output ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Not )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
int64_t iv( 0 );
if ( input.size( ) == 0 ) {
data.addError( loc , "not enough arguments" );
} else if ( input.size( ) != 1 ) {
data.addError( loc , "too many arguments" );
} else if ( !input[ 0 ].isInteger( ) ) {
data.addError( input[ 0 ] , "integer argument expected" );
} else {
iv = input[ 0 ].longValue( ) ? 0 : 1;
}
T_SRDToken output( T_SRDToken::Integer( iv ) );
output.location( loc );
state.data( ).top( ).output( ).add( std::move( output ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Or )
{
DoLogicOp_( state ,
[]( auto a , auto b ) {
return ( a || b ) ? 1 : 0;
} );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Output )
{
auto& out( state.output( ) );
const auto n( out.size( ) );
if ( n ) {
auto& data( state.data( ) );
auto& pp( data.preprocessor( ) );
auto& ec( data.errorContext( ) );
for ( uint32_t i = 0 ; i < n ; i ++ ) {
pp.sendOutput( ec , out[ i ] );
}
pp.target( ).push( ec , T_SRDToken::Flush( ) );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Raw )
{
state.data( ).top( ).output( ).addAll( state.output( ) );
}
/*----------------------------------------------------------------------------*/
namespace {
void RethrowError_( T_SRDPreprocessorData& data , T_SRDList const& error )
{
auto const& esTok( error[ 0 ] );
if ( esTok.type( ) != E_SRDTokenType::STRING ) {
data.addError( esTok , "string expected" );
return;
}
const auto elSize( error.size( ) );
SP_SRDLocation location;
for ( uint32_t i = elSize - 1 ; i > 0 ; i -- ) {
auto const& locEntry( error[ i ] );
if ( locEntry.type( ) != E_SRDTokenType::LIST ) {
data.addError( locEntry , "list expected" );
return;
}
auto const& locList( locEntry.list( ) );
const bool hasChaining( i != elSize - 1 );
const uint32_t expectedLength( hasChaining ? 4 : 3 );
if ( locList.size( ) != expectedLength ) {
data.addError( locEntry , "invalid location entry" );
return;
}
auto const& locName( locList[ 0 ] );
if ( locName.type( ) != E_SRDTokenType::STRING ) {
data.addError( locName , "string expected" );
return;
}
auto const& locLine( locList[ 1 ] );
if ( !locLine.isInteger( ) ) {
data.addError( locLine , "integer expected" );
return;
}
const int64_t line( locLine.longValue( ) );
if ( line < 0 || line > UINT32_MAX ) {
data.addError( locLine , "invalid line number" );
return;
}
auto const& locChar( locList[ 2 ] );
if ( !locChar.isInteger( ) ) {
data.addError( locChar , "integer expected" );
return;
}
const int64_t character( locChar.longValue( ) );
if ( character < 0 || character > UINT32_MAX ) {
data.addError( locChar , "invalid character/byte number" );
return;
}
E_SRDLocationChaining chaining;
if ( hasChaining ) {
if ( locList[ 3 ].type( ) != E_SRDTokenType::WORD ) {
data.addError( locList[ 3 ] , "word expected" );
return;
}
T_String const& chainWord( locList[ 3 ].stringValue( ) );
if ( chainWord == "generated" ) {
chaining = E_SRDLocationChaining::GENERATED;
} else if ( chainWord == "included" ) {
chaining = E_SRDLocationChaining::INCLUDED;
} else if ( chainWord == "loaded" ) {
chaining = E_SRDLocationChaining::LOADED;
} else if ( chainWord == "called" ) {
chaining = E_SRDLocationChaining::CALLED;
} else if ( chainWord == "evaluated" ) {
chaining = E_SRDLocationChaining::EVALUATED;
} else if ( chainWord == "expanded" ) {
chaining = E_SRDLocationChaining::EXPANDED;
} else if ( chainWord == "substituted" ) {
chaining = E_SRDLocationChaining::SUBSTITUTED;
} else {
data.addError( locList[ 3 ] , "invalid chaining" );
return;
}
} else {
// Compiler stfu
chaining = E_SRDLocationChaining( 0 );
}
T_String const& src( locName.stringValue( ) );
SP_SRDLocation nLocation( line > 0
? NewShared< T_SRDLocation >( src , line , character )
: NewShared< T_SRDLocation >( src , character ) );
if ( hasChaining ) {
nLocation->chain( chaining , location );
}
location = nLocation;
}
data.addError( *location , esTok.stringValue( ) );
}
void DoRethrow_( T_SRDPreprocessorData& data , T_SRDList const& errors )
{
const auto nErrors( errors.size( ) );
for ( uint32_t i = 0 ; i < nErrors ; i ++ ) {
auto const& errTok( errors[ i ] );
if ( errTok.type( ) != E_SRDTokenType::LIST ) {
data.addError( errTok , "list expected" );
continue;
}
if ( errTok.list( ).size( ) < 2 ) {
data.addError( errTok , "invalid error record" );
continue;
}
RethrowError_( data , errTok.list( ) );
}
}
}
M_SRDPP_COMMAND_EXEC( Rethrow )
{
auto const& input( state.output( ) );
if ( input.size( ) == 0 ) {
return;
}
if ( input.size( ) == 1 && input[ 0 ].type( ) == E_SRDTokenType::LIST ) {
auto const& innerList( input[ 0 ].list( ) );
if ( innerList.size( ) == 0 ) {
return;
}
if ( innerList[ 0 ].type( ) == E_SRDTokenType::LIST ) {
DoRethrow_( state.data( ) , innerList );
return;
}
}
DoRethrow_( state.data( ) , input );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Scope )
{
if ( state.output( ).size( ) == 0 ) {
return;
}
auto& data( state.data( ) );
data.scopes( ).enter( );
data.push( T_SRDPreprocessorState::C_INNER ,
state.outputPointer( ) , data.top( ).outputPointer( ) ,
[]( T_SRDPreprocessorState & ns ) {
ns.data( ).scopes( ).exit( );
} );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Set )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
if ( input.size( ) == 0 ) {
data.addError( loc , "not enough arguments" );
return;
}
auto const& names( input[ 0 ] );
auto& s( data.scopes( ) );
if ( names.type( ) == E_SRDTokenType::WORD ) {
auto const& vn( names.stringValue( ) );
if ( !s.canSet( false , vn ) ) {
data.addError( names , "duplicate variable name" );
return;
}
s.set( false , vn , input , 1 );
} else if ( names.type( ) == E_SRDTokenType::LIST ) {
auto const& list( names.list( ) );
const auto n( list.size( ) );
if ( n == 0 ) {
data.addError( names , "non-empty list expected" );
return;
}
bool ok( true );
for ( uint32_t i = 0 ; i < n ; i ++ ) {
auto const& name( list[ i ] );
if ( name.type( ) != E_SRDTokenType::WORD ) {
data.addError( name , "word expected" );
ok = false;
continue;
}
auto const& vn( name.stringValue( ) );
for ( uint32_t j = 0 ; j < i ; j ++ ) {
if ( list[ j ].type( ) != E_SRDTokenType::WORD ) {
continue;
}
if ( list[ j ].stringValue( ) == vn ) {
data.addError( name , "duplicate variable name" );
ok = false;
goto next;
}
}
if ( !s.canSet( false , vn ) ) {
data.addError( name , "duplicate variable name" );
ok = false;
}
next: ;
}
if ( !ok ) {
return;
}
for ( uint32_t i = 0 ; i + 1 < n ; i ++ ) {
s.set( false , list[ i ].stringValue( ) , input , 1 + i , 1 + i );
}
s.set( false , list[ n - 1 ].stringValue( ) , input , n );
} else {
data.addError( names , "word or list expected" );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( SetMacro )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
if ( input.size( ) < 2 ) {
data.addError( loc , "not enough arguments" );
return;
}
if ( input.size( ) > 2 ) {
data.addError( loc , "too many arguments" );
return;
}
auto const& name( input[ 0 ] );
if ( name.type( ) != E_SRDTokenType::WORD ) {
data.addError( name , "word expected" );
return;
}
auto const& mn( name.stringValue( ) );
auto& s( data.scopes( ) );
if ( !s.canSet( true , mn ) ) {
data.addError( name , "duplicate macro name" );
return;
}
auto const& value( input[ 1 ] );
if ( !T_SRDPreprocessor::isValidFunction( value ) ) {
data.addError( value , "invalid macro body" );
return;
}
s.set( true , mn , value.list( ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( StartsWith )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
uint32_t value = 0;
if ( input.size( ) < 2 ) {
data.addError( loc , "not enough arguments" );
} else if ( input.size( ) > 2 ) {
data.addError( loc , "too many arguments" );
} else {
if ( input[ 0 ].isText( ) && input[ 1 ].isText( ) ) {
value = input[ 1 ].stringValue( ).startsWith(
input[ 0 ].stringValue( ) );
} else if ( input[ 0 ].type( ) == E_SRDTokenType::BINARY
&& input[ 1 ].type( ) == E_SRDTokenType::BINARY ) {
auto const& needle( input[ 0 ].binary( ) );
auto const& haystack( input[ 1 ].binary( ) );
auto const nsz( needle.size( ) );
auto const hsz( haystack.size( ) );
value = ( nsz <= hsz
&& !( nsz && memcmp( needle.data( ) ,
haystack.data( ) , nsz ) ) );
} else {
data.addError( loc , "invalid arguments" );
}
}
auto tok( T_SRDToken::Integer( value ) );
tok.location( loc );
data.top( ).output( ).add( std::move( tok ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( StrFind )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
int32_t value = -1;
if ( input.size( ) < 2 ) {
data.addError( loc , "not enough arguments" );
} else if ( input.size( ) > 2 ) {
data.addError( loc , "too many arguments" );
} else {
if ( !input[ 0 ].isText( ) ) {
data.addError( input[ 0 ] , "text expected" );
}
if ( !input[ 1 ].isText( ) ) {
data.addError( input[ 1 ] , "text expected" );
}
if ( input[ 0 ].isText( ) && input[ 1 ].isText( ) ) {
value = input[ 1 ].stringValue( ).find(
input[ 0 ].stringValue( ) );
}
}
auto tok( T_SRDToken::Integer( value ) );
tok.location( loc );
data.top( ).output( ).add( std::move( tok ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( StrSplit )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
auto& output( data.top( ).output( ) );
if ( input.size( ) < 2 ) {
data.addError( loc , "not enough arguments" );
return;
}
if ( input.size( ) > 3 ) {
data.addError( loc , "too many arguments" );
return;
}
bool argsOk = true;
if ( !input[ 0 ].isText( ) ) {
data.addError( input[ 0 ] , "text expected" );
argsOk = false;
} else if ( !input[ 0 ].stringValue( ) ) {
data.addError( input[ 0 ] , "invalid argument value" );
argsOk = false;
}
if ( !input[ 1 ].isText( ) ) {
data.addError( input[ 1 ] , "text expected" );
argsOk = false;
}
if ( input.size( ) == 3
&& input[ 2 ].type( ) != E_SRDTokenType::INT
&& input[ 2 ].type( ) != E_SRDTokenType::LONG ) {
data.addError( input[ 2 ] , "integer expected" );
argsOk = false;
}
if ( !argsOk ) {
return;
}
uint32_t maxOutput;
if ( input.size( ) == 3 ) {
int64_t n( input[ 2 ].longValue( ) );
if ( n < 0 || n > UINT32_MAX ) {
data.addError( input[ 2 ] , "invalid argument value" );
maxOutput = UINT32_MAX;
} else {
maxOutput = n;
}
} else {
maxOutput = UINT32_MAX;
}
int32_t nextSearch( 0 );
auto const& needle( input[ 0 ].stringValue( ) );
auto const& haystack( input[ 1 ].stringValue( ) );
while ( nextSearch != -1 && maxOutput != 0 ) {
const int32_t sepIndex( haystack.find( needle , nextSearch ) );
const T_String os( sepIndex == -1
? haystack.substr( nextSearch )
: ( sepIndex == 0
? T_String( )
: haystack.range( nextSearch , sepIndex - 1 ) ) );
T_SRDToken otok( T_SRDToken::String( os ) );
otok.location( loc );
output.add( std::move( otok ) );
if ( sepIndex == -1 ) {
nextSearch = -1;
} else {
nextSearch = sepIndex + needle.length( );
}
maxOutput --;
}
if ( maxOutput == 0 && nextSearch != -1 ) {
T_SRDToken otok( T_SRDToken::String( haystack.substr( nextSearch ) ) );
otok.location( loc );
output.add( std::move( otok ) );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Sub )
{
DoMathOp_( state ,
[]( auto a , auto b ) {
return a - b;
} ,
[]( auto a , auto b ) {
return a - b;
} );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Substr )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
const auto is( input.size( ) );
if ( is < 2 ) {
data.addError( loc , "not enough arguments" );
} else if ( is > 4 ) {
data.addError( loc , "too many arguments" );
}
if ( is == 0 ) {
T_SRDToken otok( T_SRDToken::String( T_String( ) ) );
otok.location( loc );
data.top( ).output( ).add( std::move( otok ) );
return;
}
const bool isBinary( input[ 0 ].type( ) == E_SRDTokenType::BINARY );
if ( is < 2 || is > 4 ) {
T_SRDToken otok( isBinary
? T_SRDToken::Binary( (uint8_t const*) nullptr , 0 )
: T_SRDToken::String( T_String( ) ) );
otok.location( loc );
data.top( ).output( ).add( std::move( otok ) );
return;
}
const bool isRange = ( is == 4 );
bool argsOk = true;
uint32_t start( 0 );
uint32_t end( UINT32_MAX );
if ( !( input[ 0 ].isText( ) || isBinary ) ) {
data.addError( input[ 0 ] , "binary or text expected" );
argsOk = false;
}
if ( isRange && ( input[ 2 ].type( ) != E_SRDTokenType::WORD
|| input[ 2 ].stringValue( ) != "to" ) ) {
data.addError( input[ 2 ] , "'to' expected" );
argsOk = false;
}
if ( !input[ 1 ].isNumeric( ) || input[ 1 ].type( ) == E_SRDTokenType::FLOAT ) {
data.addError( input[ 1 ] , "integer expected" );
argsOk = false;
} else {
const int64_t v( input[ 1 ].longValue( ) );
if ( v < 0 || v > UINT32_MAX ) {
data.addError( input[ 1 ] , "invalid argument value" );
argsOk = false;
} else {
start = v;
}
}
if ( is > 2 ) {
auto const& next( input[ isRange ? 3 : 2 ] );
if ( !next.isNumeric( ) || next.type( ) == E_SRDTokenType::FLOAT ) {
data.addError( next , "integer expected" );
argsOk = false;
} else {
const int64_t v( next.longValue( ) );
if ( v < 0 || v > UINT32_MAX ) {
data.addError( next , "invalid argument value" );
argsOk = false;
} else {
end = v;
}
}
}
if ( !argsOk ) {
T_SRDToken otok( isBinary
? T_SRDToken::Binary( (uint8_t const*) nullptr , 0 )
: T_SRDToken::String( T_String( ) ) );
otok.location( loc );
data.top( ).output( ).add( std::move( otok ) );
return;
}
if ( !isBinary ) {
auto const& str( input[ 0 ].stringValue( ) );
T_SRDToken otok( T_SRDToken::String( isRange
? str.range( start , end )
: str.substr( start , end ) ) );
otok.location( loc );
data.top( ).output( ).add( std::move( otok ) );
return;
}
const uint32_t oriLen( input[ 0 ].binary( ).size( ) );
const uint32_t realEnd( std::min( oriLen , end ) );
const uint32_t lengthMax( isRange
? ( realEnd < start ? 0 : realEnd - start + 1 )
: realEnd );
const uint32_t length( start >= oriLen
? 0 : ( std::min( oriLen , start + lengthMax ) - start ) );
auto buffer( NewShared< T_Buffer< uint8_t > >( length ) );
if ( length ) {
memcpy( buffer->data( ) , input[ 0 ].binary( ).data( ) , length );
}
T_SRDToken otok( T_SRDToken::Binary( buffer ) );
otok.location( loc );
data.top( ).output( ).add( std::move( otok ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( ToSource )
{
auto& input( state.output( ) );
const auto n( input.size( ) );
T_StringBuilder sb;
for ( uint32_t i = 0 ; i < n ; i ++ ) {
if ( i > 0 ) {
sb << ' ';
}
auto& token( input[ i ] );
token.generateFullText( );
sb << token.fullText( );
}
T_SRDToken otok( T_SRDToken::String( std::move( sb ) ) );
otok.location( *state.initialLocation( ) );
state.data( ).top( ).output( ).add( std::move( otok ) );
}
/*----------------------------------------------------------------------------*/
namespace {
void ErrorsToList_( T_SRDLocation const& location , T_SRDErrors const& errors , T_SRDList & output )
{
const auto nErrors( errors.size( ) );
for ( uint32_t i = 0 ; i < nErrors ; i ++ ) {
auto const& error( errors[ i ] );
auto& errToken( output[ output.add( T_SRDToken::List( ) ) ] );
auto& etl( errToken.list( ) );
errToken.location( location );
{
T_SRDToken msg( T_SRDToken::String( error.error( ) ) );
msg.location( location );
etl.add( std::move( msg ) );
}
RPC_SRDLocation errLoc( &error.location( ) );
RPC_SRDLocationChaining chaining( nullptr );
while ( errLoc != nullptr ) {
auto& locToken( etl[ etl.add( T_SRDToken::List( ) ) ] );
auto& ll( locToken.list( ) );
locToken.location( location );
{
T_SRDToken src( T_SRDToken::String( errLoc->source( ) ) );
src.location( location );
ll.add( std::move( src ) );
}{
T_SRDToken ln( T_SRDToken::Integer( errLoc->line( ) ) );
ln.location( location );
ll.add( std::move( ln ) );
}{
T_SRDToken cn( T_SRDToken::Integer( errLoc->character( ) ) );
cn.location( location );
ll.add( std::move( cn ) );
}
if ( chaining ) {
T_String chain( T_String::Pooled( ( []( RPC_SRDLocationChaining c ) -> char const* {
switch ( c->circumstances ) {
case E_SRDLocationChaining::INCLUDED:
return "included";
case E_SRDLocationChaining::LOADED:
return "loaded";
case E_SRDLocationChaining::CALLED:
return "called";
case E_SRDLocationChaining::GENERATED:
return "generated";
case E_SRDLocationChaining::SUBSTITUTED:
return "substituted";
case E_SRDLocationChaining::EVALUATED:
return "evaluated";
case E_SRDLocationChaining::EXPANDED:
return "expanded";
}
return "unknown";
} )( chaining ) ) );
T_SRDToken ctok( T_SRDToken::Word( std::move( chain ) ) );
ctok.location( location );
ll.add( std::move( ctok ) );
}
if ( errLoc->isChained( ) ) {
chaining = &errLoc->chaining( );
errLoc = RPC_SRDLocation( chaining->location );
} else {
errLoc = nullptr;
}
}
}
}
}
M_SRDPP_COMMAND_EXEC( Try )
{
auto& data( state.data( ) );
auto& output( data.top( ).output( ) );
T_SRDToken otok( T_SRDToken::List( ) );
otok.location( *state.initialLocation( ) );
if ( state.output( ).size( ) == 0 ) {
// No input -> empty list
output.add( std::move( otok ) );
return;
}
const SP_SRDList fullOutput( NewShared< T_SRDList >( 16 ) );
fullOutput->add( std::move( otok ) );
data.pushErrorContext( );
data.push( T_SRDPreprocessorState::C_TRY , state.outputPointer( ) , fullOutput ,
[]( T_SRDPreprocessorState & ns ) {
const RP_SRDErrors errors( ns.data( ).popErrorContext( ) );
auto& errTok( ns.output( )[ 0 ] );
ErrorsToList_( errTok.location( ) , *errors , errTok.list( ) );
delete errors;
ns.data( ).top( ).output( ).addAll( std::move( ns.output( ) ) );
} );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( TypeOf )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
char const* type;
if ( input.size( ) == 0 ) {
data.addError( loc , "not enough arguments" );
type = "none";
} else if ( input.size( ) > 1 ) {
data.addError( loc , "too many arguments" );
type = "none";
} else {
switch ( input[ 0 ].type( ) ) {
case E_SRDTokenType::LIST:
type = "list";
break;
case E_SRDTokenType::WORD:
type = "word";
break;
case E_SRDTokenType::STRING:
type = "string";
break;
case E_SRDTokenType::INT:
type = "int";
break;
case E_SRDTokenType::LONG:
type = "long";
break;
case E_SRDTokenType::FLOAT:
type = "real";
break;
case E_SRDTokenType::VAR:
type = "var";
break;
case E_SRDTokenType::BINARY:
type = "binary";
break;
case E_SRDTokenType::COMMENT:
type = "comment";
break;
default:
type = "none";
data.addError( input[ 0 ] , "internal error" );
break;
}
}
T_SRDToken output( T_SRDToken::Word( T_String::Pooled( type ) ) );
output.location( loc );
state.data( ).top( ).output( ).add( std::move( output ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Unset )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
if ( input.size( ) == 0 ) {
data.addError( loc , "not enough arguments" );
return;
}
const uint32_t n( input.size( ) );
for ( uint32_t i = 0 ; i < n ; i ++ ) {
auto const& name( input[ i ] );
if ( name.type( ) != E_SRDTokenType::WORD ) {
data.addError( name , "word expected" );
} else {
data.scopes( ).unset( false , name.stringValue( ) );
}
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( UnsetMacro )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
if ( input.size( ) == 0 ) {
data.addError( loc , "not enough arguments" );
return;
}
const uint32_t n( input.size( ) );
for ( uint32_t i = 0 ; i < n ; i ++ ) {
auto const& name( input[ i ] );
if ( name.type( ) != E_SRDTokenType::WORD ) {
data.addError( name , "word expected" );
} else {
data.scopes( ).unset( true , name.stringValue( ) );
}
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Unwrap )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
if ( input.size( ) == 0 ) {
data.addError( loc , "not enough arguments" );
} else if ( input.size( ) > 1 ) {
data.addError( loc , "too many arguments" );
} else if ( input[ 0 ].type( ) != E_SRDTokenType::LIST ) {
data.addError( input[ 0 ] , "list expected" );
} else {
data.top( ).output( ).addAll( std::move( input[ 0 ].list( ) ) );
}
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( Xor )
{
DoLogicOp_( state ,
[]( auto a , auto b ) {
return ( a || b ) && !( a && b );
} );
}
/*= VFS COMMANDS =============================================================*/
M_SRDPP_COMMAND_EXEC( VFSList )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
if ( input.size( ) == 0 ) {
data.addError( loc , "not enough arguments" );
return;
}
if ( input.size( ) > 1 ) {
data.addError( loc , "too many arguments" );
return;
}
if ( input[ 0 ].type( ) != E_SRDTokenType::STRING ) {
data.addError( input[ 0 ] , "string expected" );
return;
}
const T_VFSPath path( input[ 0 ].stringValue( ) );
if ( path.type( ) != E_VFSPathType::ABSOLUTE ) {
data.addError( input[ 0 ] , "absolute VFS path expected" );
return;
}
T_Array< T_VFSPath > list( 32 );
if ( !vfs_.list( path , list ) ) {
return;
}
const uint32_t n( list.size( ) );
T_SRDList output( std::max( 1u , n ) );
output.ensureCapacity( n );
for ( uint32_t i = 0 ; i < n ; i ++ ) {
output.add( T_SRDToken::String( T_String( list[ i ] ) ) );
}
auto otok( T_SRDToken::List( std::move( output ) ) );
otok.location( loc );
data.top( ).output( ).add( std::move( otok ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( VFSLoad )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
auto const& loc( *state.initialLocation( ) );
if ( input.size( ) == 0 ) {
data.addError( loc , "not enough arguments" );
return;
}
if ( input.size( ) > 1 ) {
data.addError( loc , "too many arguments" );
return;
}
if ( input[ 0 ].type( ) != E_SRDTokenType::STRING ) {
data.addError( input[ 0 ] , "string expected" );
return;
}
const T_VFSPath path( input[ 0 ].stringValue( ) );
if ( path.type( ) != E_VFSPathType::ABSOLUTE ) {
data.addError( input[ 0 ] , "absolute VFS path expected" );
return;
}
const OP_InputStream istream( vfs_.read( path ) );
if ( !istream ) {
T_StringBuilder sb( "error while loading '" );
sb << T_String( path ) << "': file not found";
data.addError( input[ 0 ] , std::move( sb ) );
return;
}
T_SharedPtr< T_Buffer< uint8_t > > buffer;
try {
if ( istream->isSizeKnown( ) ) {
const auto sz( istream->size( ) );
buffer = NewShared< T_Buffer< uint8_t > >( sz );
if ( sz ) {
istream->read( buffer->data( ) , sz );
}
} else {
uint8_t temp[ 4096 ];
buffer = NewShared< T_Buffer< uint8_t > >( );
try {
while ( 1 ) {
const auto nRead( istream->read( temp , sizeof( temp ) ) );
const auto prevSize( buffer->size( ) );
assert( nRead > 0 );
buffer->resize( prevSize + nRead );
memcpy( buffer->data( ) + prevSize , temp , nRead );
}
} catch ( X_StreamError const& error ) {
if ( error.code( ) != E_StreamError::END ) {
throw;
}
}
}
} catch ( X_StreamError const& error ) {
T_StringBuilder sb( "error while loading '" );
sb << T_String( path ) << "': " << error.what( );
data.addError( input[ 0 ] , std::move( sb ) );
}
auto otok( T_SRDToken::Binary( buffer ) );
otok.location( loc );
data.top( ).output( ).add( std::move( otok ) );
}
/*----------------------------------------------------------------------------*/
M_SRDPP_COMMAND_EXEC( VFSType )
{
auto& input( state.output( ) );
auto& data( state.data( ) );
const auto is( input.size( ) );
if ( is == 0 ) {
return;
}
T_Array< T_VFSPath > paths( 4 );
paths.ensureCapacity( is );
for ( uint32_t i = 0 ; i < is ; i ++ ) {
if ( input[ i ].type( ) != E_SRDTokenType::STRING ) {
data.addError( input[ i ] , "string expected" );
return;
}
T_VFSPath const& path( paths.addNew( input[ i ].stringValue( ) ) );
if ( path.type( ) != E_VFSPathType::ABSOLUTE ) {
data.addError( input[ i ] , "absolute VFS path expected" );
return;
}
}
auto& out( data.top( ).output( ) );
for ( uint32_t i = 0 ; i < is ; i ++ ) {
const E_VFSEntryType et( vfs_.typeOf( paths[ i ] ) );
T_SRDToken token( ( []( E_VFSEntryType et ) {
switch ( et ) {
case E_VFSEntryType::NONE:
return T_SRDToken::Word( T_String::Pooled( "none" ) );
case E_VFSEntryType::FILE:
return T_SRDToken::Word( T_String::Pooled( "file" ) );
case E_VFSEntryType::DIRECTORY:
return T_SRDToken::Word( T_String::Pooled( "directory" ) );
case E_VFSEntryType::OTHER:
return T_SRDToken::Word( T_String::Pooled( "other" ) );
}
return T_SRDToken::Word( T_String::Pooled( "unknown" ) );
})( et ) );
token.copyLocationOf( input[ i ] );
out.add( std::move( token ) );
}
}