Files
flenser/js4g/StackMachine.cc

306 lines
7.0 KiB
C++

static_assert( __cplusplus >= 2023'02 );
#include "StackMachine.h"
#include <boost/lexical_cast.hpp>
namespace
{
using namespace std::literals::string_literals;
using namespace std::literals::string_view_literals;
namespace C
{
constexpr bool debug= false;
}
}
namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
{
StackMachine::StackMachine( std::ostream &output )
: output( output )
{
}
void
StackMachine::run()
{
while( not tokenStack.empty() and tokenStack.back().hasNext() )
{
runWord( std::get< std::string_view >( next() ) );
if( C::debug ) std::cerr << "After processing stack is now: " << std::endl;
if( C::debug ) for( const auto &element: stack )
{
std::visit
(
[&]( const auto &thing ) { std::cerr << thing << std::endl; },
element
);
}
}
if( C::debug ) std::cerr << "Run done with stack at size: " << stack.size() << std::endl;
}
StackMachine::Token
StackMachine::next()
{
if( tokenStack.empty() )
{
throw std::runtime_error{ "FATAL: Token required, no more tokens left." };
}
const auto rv= tokenStack.back().next();
while( not tokenStack.empty() and not tokenStack.back().hasNext() ) tokenStack.pop_back();
return rv;
}
void
StackMachine::runWord( const std::string_view word )
{
if( C::debug ) std::cerr << "Processing token: " << word << std::endl;
if( C::debug ) std::cerr << "Conditional depth is: " << conditionals.size() << std::endl;
if( C::debug )
{
std::cerr << "The following cached named functions exist: " << std::endl;
for( const auto &name: wordNames )
{
std::cerr << name << std::endl;
}
std::cerr << "The following named functions exist: " << std::endl;
for( const auto &[ name, _ ]: words )
{
std::cerr << name << std::endl;
}
}
const bool inConditional= not conditionals.empty();
if( false );
else if( word.empty() );
else if( word == "}"sv )
{
if( not definition.has_value() )
{
throw std::runtime_error{ "Stray end of function definition" };
}
if( C::debug ) std::cerr << "Definition of " << definition.value() << " is done. It is: " << std::endl;
//if( C::debug ) std::copy( begin( words.at( definition.value() ) ), end( words.at( definition.value() ) ),
//std::ostream_iterator< std::string >{ std::cout, "\n" } );
definition= std::nullopt;
}
else if( definition.has_value() )
{
if( C::debug ) std::cerr << "Adding word: " << word << " to function definition: " << definition.value() << std::endl;
words[ definition.value() ].emplace_back( word );
}
else if( inConditional and word == "@else"sv and currentState != Else )
{
currentState= Else;
}
else if( inConditional and word == "@endif"sv )
{
conditionals.pop_back();
currentState= resumeConditionals.back();
resumeConditionals.pop_back();
}
else if( inConditional and currentState != conditionals.back() )
{
if( word == "@if"sv )
{
conditionals.push_back( Skipped );
resumeConditionals.push_back( currentState );
currentState= If;
}
else ; // We skip the statement.
}
else if( word == "@else"sv )
{
throw std::runtime_error{ "Syntax error: unexpected `@else`" };
}
else if( word == "@endif"sv )
{
throw std::runtime_error{ "Syntax error: unexpected `@endif`" };
}
else if( word == "@dup"sv )
{
auto val= peek();
push( std::move( val ) );
}
else if( word == "@swap"sv )
{
auto a= pop();
auto b= pop();
push( std::move( a ) );
push( std::move( b ) );
}
else if( word == "@drop"sv )
{
std::ignore= pop();
}
else if( word == "@not"sv )
{
auto val= pop< Integer >();
push( val == 0 ? 1 : 0 );
}
else if( word == "@i_inc"sv )
{
push( pop< Integer >() + 1 );
}
else if( word == "@i_dec"sv )
{
push( pop< Integer >() - 1 );
}
else if( word == "@i_add"sv )
{
const auto lhs= pop< Integer >();
const auto rhs= pop< Integer >();
push( lhs + rhs );
}
else if( word == "@i_sub"sv )
{
const auto lhs= pop< Integer >();
const auto rhs= pop< Integer >();
push( lhs - rhs );
}
else if( word == "@i_mul"sv )
{
const auto lhs= pop< Integer >();
const auto rhs= pop< Integer >();
push( lhs * rhs );
}
else if( word == "@i_div"sv )
{
const auto lhs= pop< Integer >();
const auto rhs= pop< Integer >();
push( lhs / rhs );
}
else if( word == "@print"sv )
{
output << pop< std::string >() << std::endl;
}
else if( word == "@if"sv )
{
const auto val= pop< Integer >();
conditionals.push_back( val ? If : Else );
resumeConditionals.push_back( currentState );
currentState= If;
}
else if( word == "{"sv )
{
if( definition.has_value() ) throw std::runtime_error{ "Nested definitions not supported." };
wordNames.push_back( pop< std::string >() );
definition= wordNames.back();
words[ wordNames.back() ]= {};
if( C::debug ) std::cerr << "Starting function definition for " << definition.value() << std::endl;
}
else if( word == "0"sv ) push( 0 );
else if( word == "1"sv ) push( 1 );
else if( word.at( 0 ) == '@' )
{
const auto invoke= word.substr( 1 );
if( not words.contains( invoke ) )
{
throw std::runtime_error{ "Unknown keyword: `"s + std::string{ word }
+ "`" };
}
const auto &def= words.at( invoke );
tokenStack.emplace_back( def );
}
else
{
push( word );
}
}
template< typename T >
T
StackMachine::pop()
{
if( std::holds_alternative< T >( peek() ) ) return std::get< T >( pop() );
else if( std::holds_alternative< Integer >( peek() ) ) return boost::lexical_cast< T >( std::get< Integer >( pop() ) );
else if( std::holds_alternative< Long >( peek() ) ) return boost::lexical_cast< T >( std::get< Long >( pop() ) );
else return boost::lexical_cast< T >( std::get< std::string >( pop() ) );
}
StackMachine::Value
StackMachine::pop()
{
const auto rv= std::move( peek() );
stack.pop_back();
return rv;
}
StackMachine::Value &
StackMachine::peek()
{
if( stack.empty() ) throw std::runtime_error{ "FATAL: No more elements in stack." };
return stack.back();
}
void
StackMachine::push( Value element )
{
stack.push_back( std::move( element ) );
}
void
StackMachine::push( std::string element )
{
stack.push_back( std::move( element ) );
}
void
StackMachine::push( Integer element )
{
stack.push_back( std::move( element ) );
}
void
StackMachine::push( Long element )
{
stack.push_back( std::move( element ) );
}
template< typename T >
void
StackMachine::push( const T &t )
{
if constexpr( std::is_same_v< Integer, T > ) push( t );
else if constexpr( std::is_same_v< Long, T > ) push( t );
else if constexpr( std::is_same_v< std::string, T > ) push( t );
else push( boost::lexical_cast< std::string >( t ) );
}
}
int
main()
{
std::vector< std::string > program;
while( std::cin )
{
std::string token;
std::cin >> token;
program.push_back( std::move( token ) );
}
using Dillo::JavaScriptForge::StackMachine;
StackMachine machine;
machine.loadProgram( std::move( program ) );
machine.run();
}