xo-interpreter2/src/skrepl/skreplxx.cpp

244 lines
6.9 KiB
C++

/** @file skreplxx.cpp
*
* @author Roland Conybeare, Feb 2026
**/
#include <xo/interpreter2/init_interpreter2.hpp>
#include <xo/interpreter2/VirtualSchematikaMachine.hpp>
#include <xo/alloc2/Arena.hpp>
#include <xo/facet/FacetRegistry.hpp>
#include <replxx.hxx>
#include <iostream>
#ifdef __APPLE__
#include <unistd.h> // for STDIN_FILENO on OSX
#endif
namespace xo {
using xo::scm::VirtualSchematikaMachine;
using xo::scm::VsmResultExt;
using xo::mm::AAllocator;
using xo::mm::ArenaConfig;
using xo::mm::DArena;
using span_type = xo::mm::span<const char>;
using xo::facet::FacetRegistry;
using xo::facet::TypeRegistry;
using std::cerr;
// presumeably replxx assumes input is a tty anyway?
//
bool replxx_getline(bool interactive,
bool is_at_toplevel,
replxx::Replxx & rx,
span_type * p_input
//const char ** p_input
)
{
using namespace std;
char const * prompt = "";
if (interactive) {
prompt = ((is_at_toplevel) ? "> " : ". ");
}
const char * input_cstr = rx.input(prompt);
bool retval = (input_cstr != nullptr);
if (retval)
*p_input = span_type::from_cstr(input_cstr);
if (input_cstr)
rx.history_add(input_cstr);
return retval;
}
void
welcome(std::ostream & os)
{
using namespace std;
os << "schematika repl" << endl;
os << " ctrl-a/ctrl-e beginning/end of line" << endl;
os << " ctrl-u delete entire line" << endl;
os << " ctrl-k delete to end of line" << endl;
os << " meta-<bs> backward delete word" << endl;
os << " <up>|meta-p previous command from history" << endl;
os << " <down>|meta-n next command from history" << endl;
os << " <pgup>/<pgdown> page through history faster" << endl;
os << " ctrl-s/ctrl-r forward/backward history search" << endl;
os << endl;
}
struct ReplConfig {
ReplConfig() = default;
std::size_t max_history_size_ = 1000;
std::string repl_history_fname_ = "skrepl_history.txt";
bool debug_flag_ = false;
};
struct AppConfig {
using VsmConfig = xo::scm::VsmConfig;
//using ReaderConfig = xo::scm::ReaderConfig;
//using X1CollectorConfig = xo::mm::X1CollectorConfig;
//using ArenaConfig = xo::mm::ArenaConfig;
AppConfig(const ReplConfig & repl_cfg = ReplConfig(),
const ArenaConfig & app_arena_cfg = ArenaConfig().with_name("skreplxx").with_size(16 * 1024),
const VsmConfig & vsm_cfg = VsmConfig())
: repl_config_{repl_cfg}, app_arena_config_{app_arena_cfg}, vsm_config_{vsm_cfg}
{
//rdr_config_.reader_debug_flag_ = true;
//rdr_config.parser_debug_flag_ = true;
//rdr_config.tk_debug_flag_ = true;
}
ReplConfig repl_config_;
ArenaConfig app_arena_config_;
VsmConfig vsm_config_;
//ReaderConfig rdr_config_;
//X1CollectorConfig x1_config_ = (X1CollectorConfig().with_name("gc").with_size(4*1024*1024));
//ArenaConfig fixed_config_ = (ArenaConfig().with_name("fixed").with_size(4*1024));
};
struct App {
//using AAllocator = xo::mm::AAllocator;
//using DX1Collector = xo::mm::DX1Collector;
//using X1CollectorConfig = xo::mm::X1CollectorConfig;
//using DArena = xo::mm::DArena;
//using ArenaConfig = xo::mm::ArenaConfig;
using Replxx = replxx::Replxx;
using span_type = VirtualSchematikaMachine::span_type;
App(const AppConfig & cfg = AppConfig())
: repl_config_{cfg.repl_config_},
app_arena_{cfg.app_arena_config_},
vsm_{cfg.vsm_config_, obj<AAllocator,DArena>(&app_arena_)}
{
this->interactive_ = isatty(STDIN_FILENO);
rx_.set_max_history_size(repl_config_.max_history_size_);
rx_.history_load(repl_config_.repl_history_fname_);
// rx.bind_key_internal(Replxx::KEY::control('p'), "history_previous");
// rx.bind_key_internal(Replxx::KEY::control('n'), "history_next");
}
/** borrows calling thread to run application **/
void run();
private:
void _init();
void _start();
void _repl();
bool _read_eval_print(span_type * p_input, bool eof);
private:
InitEvidence init_evidence_ = 0;
ReplConfig repl_config_;
bool interactive_ = false;
Replxx rx_;
///** collector/allocator for schematika expressions **/
//DX1Collector x1_;
///** e.g. for DArenaHashMap within global symtab **/
//DArena fixed_;
/** arena with same lifetime as this application **/
DArena app_arena_;
/** schematika virtual machine **/
VirtualSchematikaMachine vsm_;
};
void
App::run()
{
this->_init();
this->_start();
this->_repl();
}
void
App::_init()
{
// window to contorl size of registries ends as soon as we init other subsystems
TypeRegistry::instance(1024);
FacetRegistry::instance(1024);
InitEvidence init_evidence_ = (InitSubsys<S_interpreter2_tag>::require());
Subsystem::initialize_all();
}
void
App::_start()
{
welcome(cerr);
vsm_.begin_interactive_session();
}
void
App::_repl()
{
bool eof = false;
span_type input;
// outer loop: fetch one line of interactive input
while (replxx_getline(interactive_, vsm_.is_at_toplevel(), rx_, &input)) {
// inner loop: consume up to one expression at a time.
while (!input.empty() && this->_read_eval_print(&input, false /*eof*/))
{
;
}
/* here: either:
* 1. input.empty() or
* 2. error encountered
*/
}
/* reminder: eof can complete at most one token */
this->_read_eval_print(&input, true /*eof*/);
}
/** body of read-parse-print loop
*
* true -> no errors;
* false -> reader encountered error
**/
bool
App::_read_eval_print(span_type * p_input,
bool eof)
{
scope log(XO_DEBUG(repl_config_.debug_flag_));
if (!p_input || p_input->empty())
return true;
VsmResultExt res = vsm_.read_eval_print(*p_input, eof);
*p_input = res.remaining_;
return !res.is_tk_error() && !res.is_eval_error();
}
} /*namespace xo*/
int
main (int argc, char * argv[])
{
using xo::AppConfig;
using xo::App;
AppConfig cfg;
// [cmdline options here]
App app(cfg);
app.run();
} /*main*/
/* end skreplxx.cpp */