2518 lines
63 KiB
C++
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 ) );
|
|
}
|
|
}
|