133 lines
3.0 KiB
C++
133 lines
3.0 KiB
C++
static_assert( __cplusplus >= 2023'02 );
|
|
|
|
#pragma once
|
|
|
|
#include <Dillo/Dillo.h>
|
|
|
|
#include <iostream>
|
|
|
|
#include <map>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <variant>
|
|
#include <deque>
|
|
#include <list>
|
|
|
|
#include <boost/container/flat_map.hpp>
|
|
|
|
namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
|
{
|
|
inline namespace exports
|
|
{
|
|
class StackMachine;
|
|
}
|
|
|
|
/*!
|
|
* Machine state for JS4G (Java Script Forth Grinder, AKA: JavaScriptForge JS4G).
|
|
*
|
|
* This class represents the state of a JavaScript execution, post translation to
|
|
* the forth grinder format.
|
|
*/
|
|
class exports::StackMachine
|
|
{
|
|
private:
|
|
using Long= std::uint64_t;
|
|
using Integer= std::int32_t;
|
|
using Float= long double;
|
|
|
|
using Value= std::variant< std::string, Integer, Long >;
|
|
|
|
// The main runtime stack of the forth engine.
|
|
// It grows on the "back".
|
|
// A `std::deque` is used instead of `std::vector`, because
|
|
// it helps cut down on large allocations, by subdividing things.
|
|
std::vector< Value > stack;
|
|
|
|
// TODO: Should these tokens be binary?
|
|
// Upside: compressed.
|
|
// Downside: more memory management in the machine end?
|
|
|
|
// It's `void *` to break the cycle with itself
|
|
struct TokenHolder;
|
|
using Token= std::variant< std::string_view, TokenHolder *>;
|
|
|
|
std::vector< std::string > backing;
|
|
|
|
std::vector< Token > tokens;
|
|
|
|
struct Tokenizer
|
|
{
|
|
std::vector< Token >::const_iterator pos;
|
|
std::vector< Token >::const_iterator end;
|
|
|
|
explicit
|
|
Tokenizer( const std::vector< Token > &tokens )
|
|
: pos( tokens.begin() ), end( tokens.end() ) {}
|
|
|
|
bool hasNext() const { return pos != end; }
|
|
|
|
Token next() { return *pos++; }
|
|
};
|
|
|
|
std::vector< Tokenizer > tokenStack;
|
|
|
|
|
|
std::list< std::string > wordNames;
|
|
std::unordered_map< std::string_view, std::vector< Token > > words;
|
|
|
|
// Which side of the current conditional to take.
|
|
enum ConditionalState { If, Else, Skipped };
|
|
std::vector< ConditionalState > conditionals;
|
|
std::vector< ConditionalState > resumeConditionals;
|
|
|
|
ConditionalState currentState;
|
|
|
|
std::optional< std::string > definition;
|
|
|
|
std::ostream &output;
|
|
|
|
|
|
void runWord( std::string_view word );
|
|
|
|
Value pop();
|
|
Value &peek();
|
|
|
|
void push( std::string s );
|
|
void push( Integer s );
|
|
void push( Long s );
|
|
void push( Value s );
|
|
|
|
template< typename T >
|
|
T pop();
|
|
|
|
template< typename T >
|
|
void push( const T &t );
|
|
|
|
|
|
Token next();
|
|
|
|
void recurse( std::string_view func );
|
|
|
|
public:
|
|
StackMachine( std::ostream &output= std::cout );
|
|
|
|
void
|
|
loadProgram( std::vector< std::string > tokens )
|
|
{
|
|
backing= std::move( tokens );
|
|
this->tokens.clear();
|
|
std::copy( begin( backing ), end( backing ), back_inserter( this->tokens ) );
|
|
tokenStack.clear();
|
|
tokenStack.emplace_back( this->tokens );
|
|
}
|
|
|
|
void run();
|
|
};
|
|
}
|
|
|
|
namespace Dillo::Hydrogen::JavaScriptForge::inline exports::inline StackMachine_m
|
|
{
|
|
using namespace detail::StackMachine_m::exports;
|
|
}
|