xo-interpreter: scaffold interpreter read-eval-print loop

This commit is contained in:
Roland Conybeare 2025-11-19 16:46:55 -05:00
commit 3956635920
7 changed files with 222 additions and 4 deletions

View file

@ -0,0 +1,28 @@
/** @file SchematikaError.hpp
*
* @author Roland Conybeare, Nov 2025
**/
#pragma once
#include <string>
namespace xo {
namespace scm {
class SchematikaError {
public:
SchematikaError() = default;
explicit SchematikaError(std::string x) : what_{std::move(x)} {}
const std::string & what() const { return what_; }
bool is_error() const { return !what_.empty(); }
bool is_not_an_error() const { return what_.empty(); }
private:
std::string what_;
};
}
}
/* end SchematikaError.hpp */

View file

@ -3,6 +3,9 @@
#pragma once
#include "VsmInstr.hpp"
#include "SchematikaError.hpp"
#include "xo/expression/Expression.hpp"
#include "xo/alloc/Object.hpp"
namespace xo {
namespace scm {
@ -14,12 +17,47 @@ namespace xo {
public:
VirtualSchematikaMachine();
/** evaluate expression @p expr.
* borrows calling thread until completion
* return [value, error]. error ignored unless value is nullptr.
* conversely when value is nullptr, error gives details of original
* error.
**/
std::pair<gp<Object>, SchematikaError> eval(bp<Expression> expr);
private:
/** program counter **/
/** borrow calling thread to run schematika machine
* indefinitely, or until null continuation
**/
void run();
/** execute vsm instruction @p pc
* Note: may possibly be able to replace with just opcode
**/
void execute_vsm(const VsmInstr * pc);
/** implementation class; contains instruction implementations **/
friend class VsmOps;
private:
/** program counter.
* (Perhaps replace with VsmInstr::Opcode ?)
**/
const VsmInstr * pc_ = nullptr;
/** expression **/
rp<Expression> expr_;
/** non-error result value from eval() / apply() **/
gp<Object> value_;
/** error result value from eval() / apply() **/
SchematikaError error_;
/** continuation
* (Perhaps replace with VsmInstr::Opcode ?)
**/
const VsmInstr * cont_ = nullptr;
};
} /*namespace scm*/

View file

@ -16,9 +16,29 @@ namespace xo {
class VsmInstr
{
public:
using ActionFn = void (*)(VirtualSchematikaMachine * vm);
enum class Opcode {
/** Halt virtual schematika machine **/
halt,
/** Evaluate a schematika expression.
* See VirtualSchematikaMachine::eval()
**/
eval,
N_Opcode
};
//using ActionFn = void (*)(VirtualSchematikaMachine * vm);
public:
VsmInstr(Opcode opcode, std::string_view name);
Opcode opcode() const { return opcode_; }
private:
/** unique opcode for this instruction **/
Opcode opcode_;
/** **/
std::string_view name_;
//ActionFn action_;
};

View file

@ -7,6 +7,7 @@ set(SELF_SRCS
LocalEnv.cpp
GlobalEnv.cpp
VirtualSchematikaMachine.cpp
VsmInstr.cpp
)
find_package(Threads REQUIRED)

View file

@ -4,6 +4,7 @@
**/
#include "Schematika.hpp"
#include "VirtualSchematikaMachine.hpp"
#include "xo/reader/reader.hpp"
#include <replxx.hxx>
#include <ostream>
@ -35,6 +36,8 @@ namespace xo {
private:
/** configuration **/
Config config_;
/** schematika interpreter **/
VirtualSchematikaMachine vsm_;
};
void
@ -151,7 +154,23 @@ namespace xo {
ppconfig ppc;
ppstate_standalone pps(&cout, 0, &ppc);
pps.prettyn(expr);
//pps.prettyn(expr);
// TODO:
auto [ value, scm_error ] = this->vsm_.eval(expr);
if (scm_error.is_error()) {
/* print error */
cout << "scm error: " << scm_error.what() << endl;
cout << "top-level expression: " << expr << endl;
} else {
/* print value */
cout << "scm result:" << endl;
pps.pretty(value);
}
} else if (error.is_error()) {
cout << "parsing error (detected in " << error.src_function() << "): " << endl << endl;
error.report(cout);

View file

@ -4,9 +4,105 @@
#include "VsmInstr.hpp"
/** continue after completing a VSM instruction;
* achieve by jumping to continuation.
**/
//#define VSM_CONTINUE() this->pc_ = this->cont_; return;
namespace xo {
namespace scm {
struct VsmOps {
/** halt virtual scheme machine.
* This will cause innermost run() to return to its caller
**/
static VsmInstr halt_op;
/** evaluate an expression.
* - opcode is Opcode::eval
* - expression in register @ref expr_
**/
static VsmInstr eval_op;
};
VsmInstr
VsmOps::halt_op{VsmInstr::Opcode::halt, "halt"};
VsmInstr
VsmOps::eval_op{VsmInstr::Opcode::eval, "eval"};
VirtualSchematikaMachine::VirtualSchematikaMachine()
{}
void
VirtualSchematikaMachine::run()
{
for (const VsmInstr * pc = pc_; pc; pc = pc_)
this->execute_vsm(pc);
}
void
VirtualSchematikaMachine::execute_vsm(const VsmInstr * instr)
{
using Opcode = VsmInstr::Opcode;
switch (instr->opcode()) {
case Opcode::halt:
{
this->pc_ = nullptr;
this->cont_ = nullptr;
break;
}
case Opcode::eval:
{
/* generally speaking: opcode will be 1:1 with extypes */
switch (expr_->extype()) {
case exprtype::invalid:
case exprtype::constant:
case exprtype::primitive:
case exprtype::define:
case exprtype::assign:
case exprtype::apply:
case exprtype::lambda:
case exprtype::variable:
case exprtype::ifexpr:
case exprtype::sequence:
case exprtype::convert:
case exprtype::n_expr:
this->pc_ = nullptr;
this->value_ = nullptr;
this->error_ = SchematikaError(tostr("execute_vsm: not implmented",
xtag("extype", expr_->extype())));
this->cont_ = nullptr;
break;
}
}
break;
case Opcode::N_Opcode:
assert(false);
break;
}
}
std::pair<gp<Object>,
SchematikaError>
VirtualSchematikaMachine::eval(bp<Expression> expr)
{
this->pc_ = &VsmOps::eval_op;
this->expr_ = expr.promote();
this->cont_ = &VsmOps::halt_op;
this->run();
return std::make_pair(this->value_, this->error_);
}
} /*namespace scm*/
} /*namespace xo*/
/* end VirtualSchematikaMachine.hpp */
/* end VirtualSchematikaMachine.cpp */

View file

@ -0,0 +1,16 @@
/** @file VsmInstr.cpp
*
* @author Roland Conybeare, Nov 2025
**/
#include "VsmInstr.hpp"
namespace xo {
namespace scm {
VsmInstr::VsmInstr(Opcode opcode,
std::string_view name) : opcode_{opcode}, name_{name}
{}
}
}
/* end VsmInstr.cpp */