diff --git a/xo-interpreter/include/xo/interpreter/SchematikaError.hpp b/xo-interpreter/include/xo/interpreter/SchematikaError.hpp new file mode 100644 index 00000000..6baabad3 --- /dev/null +++ b/xo-interpreter/include/xo/interpreter/SchematikaError.hpp @@ -0,0 +1,28 @@ +/** @file SchematikaError.hpp + * + * @author Roland Conybeare, Nov 2025 + **/ + +#pragma once + +#include + +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 */ diff --git a/xo-interpreter/include/xo/interpreter/VirtualSchematikaMachine.hpp b/xo-interpreter/include/xo/interpreter/VirtualSchematikaMachine.hpp index edcd9706..069233a0 100644 --- a/xo-interpreter/include/xo/interpreter/VirtualSchematikaMachine.hpp +++ b/xo-interpreter/include/xo/interpreter/VirtualSchematikaMachine.hpp @@ -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, SchematikaError> eval(bp 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 expr_; + /** non-error result value from eval() / apply() **/ + gp value_; + + /** error result value from eval() / apply() **/ + SchematikaError error_; + + /** continuation + * (Perhaps replace with VsmInstr::Opcode ?) + **/ + const VsmInstr * cont_ = nullptr; }; } /*namespace scm*/ diff --git a/xo-interpreter/include/xo/interpreter/VsmInstr.hpp b/xo-interpreter/include/xo/interpreter/VsmInstr.hpp index cf677d72..6bf350af 100644 --- a/xo-interpreter/include/xo/interpreter/VsmInstr.hpp +++ b/xo-interpreter/include/xo/interpreter/VsmInstr.hpp @@ -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_; }; diff --git a/xo-interpreter/src/interpreter/CMakeLists.txt b/xo-interpreter/src/interpreter/CMakeLists.txt index 1cc1a277..2c18bbb4 100644 --- a/xo-interpreter/src/interpreter/CMakeLists.txt +++ b/xo-interpreter/src/interpreter/CMakeLists.txt @@ -7,6 +7,7 @@ set(SELF_SRCS LocalEnv.cpp GlobalEnv.cpp VirtualSchematikaMachine.cpp + VsmInstr.cpp ) find_package(Threads REQUIRED) diff --git a/xo-interpreter/src/interpreter/Schematika.cpp b/xo-interpreter/src/interpreter/Schematika.cpp index 1c5b4361..9e36d433 100644 --- a/xo-interpreter/src/interpreter/Schematika.cpp +++ b/xo-interpreter/src/interpreter/Schematika.cpp @@ -4,6 +4,7 @@ **/ #include "Schematika.hpp" +#include "VirtualSchematikaMachine.hpp" #include "xo/reader/reader.hpp" #include #include @@ -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); diff --git a/xo-interpreter/src/interpreter/VirtualSchematikaMachine.cpp b/xo-interpreter/src/interpreter/VirtualSchematikaMachine.cpp index d46b6433..4df7c4d5 100644 --- a/xo-interpreter/src/interpreter/VirtualSchematikaMachine.cpp +++ b/xo-interpreter/src/interpreter/VirtualSchematikaMachine.cpp @@ -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, + SchematikaError> + VirtualSchematikaMachine::eval(bp 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 */ diff --git a/xo-interpreter/src/interpreter/VsmInstr.cpp b/xo-interpreter/src/interpreter/VsmInstr.cpp new file mode 100644 index 00000000..3ca138d9 --- /dev/null +++ b/xo-interpreter/src/interpreter/VsmInstr.cpp @@ -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 */