/******************************************************************************/ /* 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 ) ); } }