From 771a0e640c3ea7f403eeddd2b7a7b1e6582eb0e1 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 3 Feb 2026 21:44:40 -0500 Subject: [PATCH] xo-interpreter: + VSM work in progress --- .../include/xo/interpreter2/DApplyFrame.hpp | 20 +++++ .../interpreter2/VirtualSchematikaMachine.hpp | 15 +++- .../include/xo/interpreter2/VsmFrame.hpp | 86 +++++++++++++++++++ .../include/xo/interpreter2/VsmInstr.hpp | 3 + .../include/xo/interpreter2/VsmOpcode.hpp | 9 ++ .../src/interpreter2/CMakeLists.txt | 1 + .../interpreter2/VirtualSchematikaMachine.cpp | 69 ++++++++++++++- xo-interpreter2/src/interpreter2/VsmFrame.cpp | 68 +++++++++++++++ xo-interpreter2/src/interpreter2/VsmInstr.cpp | 6 ++ .../utest/VirtualSchematikaMachine.test.cpp | 37 ++++++++ 10 files changed, 307 insertions(+), 7 deletions(-) create mode 100644 xo-interpreter2/include/xo/interpreter2/DApplyFrame.hpp create mode 100644 xo-interpreter2/include/xo/interpreter2/VsmFrame.hpp create mode 100644 xo-interpreter2/src/interpreter2/VsmFrame.cpp diff --git a/xo-interpreter2/include/xo/interpreter2/DApplyFrame.hpp b/xo-interpreter2/include/xo/interpreter2/DApplyFrame.hpp new file mode 100644 index 00000000..d1c81c2b --- /dev/null +++ b/xo-interpreter2/include/xo/interpreter2/DApplyFrame.hpp @@ -0,0 +1,20 @@ +/** @file DApplyFrame.hpp + * + * @author Roland Conyberae, Feb 2026 + **/ + +#pragma once + +namespace xo { + namespace scm { + /** In virtual schematika machine (VSM): + * stack frame for interpreted apply expression + * (@ref DApplyExpr) + **/ + class DApplyFrame { + obj + }; + } +} + +/* end DApplyFrame.hpp */ diff --git a/xo-interpreter2/include/xo/interpreter2/VirtualSchematikaMachine.hpp b/xo-interpreter2/include/xo/interpreter2/VirtualSchematikaMachine.hpp index 5aecbc55..26366d50 100644 --- a/xo-interpreter2/include/xo/interpreter2/VirtualSchematikaMachine.hpp +++ b/xo-interpreter2/include/xo/interpreter2/VirtualSchematikaMachine.hpp @@ -7,6 +7,7 @@ #include "VsmConfig.hpp" #include "VsmInstr.hpp" +#include "VsmFrame.hpp" #include #include #include @@ -143,6 +144,12 @@ namespace xo { **/ void _do_eval_sequence_op(); + /** apply a function to evaluated arguments **/ + void _do_apply_op(); + + /** evaluate arguments on behalf of a function call **/ + void _do_evalargs_op(); + private: /* * Some registers are preserved by evaluation: @@ -163,16 +170,18 @@ namespace xo { **/ box mm_; + // consider separate allocator (which _may_ turn out to be the same) + // for VM stack. Only works for code that doesn't rely on fancy + // lexical scoping + /** reader: text -> expression **/ SchematikaReader reader_; /** program counter **/ VsmInstr pc_ = VsmInstr::c_halt; -#ifdef NOT_YET /** stack pointer **/ - Stack stack_; -#endif + VsmFrame * stack_ = nullptr; /** expression register **/ obj expr_; diff --git a/xo-interpreter2/include/xo/interpreter2/VsmFrame.hpp b/xo-interpreter2/include/xo/interpreter2/VsmFrame.hpp new file mode 100644 index 00000000..09b542a8 --- /dev/null +++ b/xo-interpreter2/include/xo/interpreter2/VsmFrame.hpp @@ -0,0 +1,86 @@ +/** @file VsmFrame.hpp + * + * @author Roland Conybeare, Feb 2026 + **/ + +#pragma once + +#include "VsmInstr.hpp" +#include +#include + +namespace xo { + namespace scm { + class VsmFrame { + public: + VsmFrame(VsmFrame * parent, VsmInstr cont) : parent_{parent}, cont_{cont} {} + + VsmFrame * parent() const noexcept { return parent_; } + VsmInstr cont() const noexcept { return cont_; } + + protected: + /** saved VSM stack; restore when this frame consumed **/ + VsmFrame * parent_ = nullptr; + /** saved continuation; restore when this frame consumed **/ + VsmInstr cont_; + }; + + class VsmApplyFrame : public VsmFrame { + public: + using AProcedure = xo::scm::AProcedure; + using AAllocator = xo::mm::AAllocator; + + public: + VsmApplyFrame(VsmFrame * old_parent, + VsmInstr old_cont, + DArray * args); + + static VsmApplyFrame * make(obj mm, + VsmFrame * old_parent, + VsmInstr old_cont, + DArray * args); + + obj fn() const noexcept { return fn_; } + DArray * args() const noexcept { return args_; } + + private: + /** evaluated target procedure. + * + * note: when initially created, this will be unpopulated; + * don't know correct value until we evaluate + * expression in head position + **/ + obj fn_; + /** evaluated arguments (to target procedure) **/ + DArray * args_; + }; + + /** frame for executing an apply expression **/ + class VsmEvalArgsFrame : public VsmFrame { + public: + using AAllocator = xo::mm::AAllocator; + + public: + /** see picture in VirtualSchematikaMachine._do_eval_apply_op() + * + * old_parent = [apply frame] + * old_cont = [xfer to called function] + * + **/ + VsmEvalArgsFrame(VsmApplyFrame * old_parent, + VsmInstr old_cont); + + static VsmEvalArgsFrame * make(obj mm, + VsmApplyFrame * apply_frame, + VsmInstr old_cont); + + private: + /** next argument to be evaluated. -1 means function head **/ + int32_t i_arg_ = -1; + }; + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end VsmFrame.hpp */ + diff --git a/xo-interpreter2/include/xo/interpreter2/VsmInstr.hpp b/xo-interpreter2/include/xo/interpreter2/VsmInstr.hpp index c956c607..a94ee625 100644 --- a/xo-interpreter2/include/xo/interpreter2/VsmInstr.hpp +++ b/xo-interpreter2/include/xo/interpreter2/VsmInstr.hpp @@ -16,6 +16,9 @@ namespace xo { static VsmInstr c_halt; static VsmInstr c_eval; + static VsmInstr c_apply; + static VsmInstr c_evalargs; + vsm_opcode opcode() const noexcept { return opcode_; } private: diff --git a/xo-interpreter2/include/xo/interpreter2/VsmOpcode.hpp b/xo-interpreter2/include/xo/interpreter2/VsmOpcode.hpp index 3638fa29..6e41c5c1 100644 --- a/xo-interpreter2/include/xo/interpreter2/VsmOpcode.hpp +++ b/xo-interpreter2/include/xo/interpreter2/VsmOpcode.hpp @@ -18,6 +18,15 @@ namespace xo { /** Evaluate expression in expr register **/ eval, + /** Apply function in stack frame + * See diagram in VirtualSchematikaMachine::_do_eval_apply_op + **/ + apply, + /** Eval arguments to function. + * See diagram in VirtualSchematikaMachine::_do_eval_apply_op + **/ + evalargs, + /** sentinel, counts number of opcodes **/ N, }; diff --git a/xo-interpreter2/src/interpreter2/CMakeLists.txt b/xo-interpreter2/src/interpreter2/CMakeLists.txt index 93ecd47e..8ce214ba 100644 --- a/xo-interpreter2/src/interpreter2/CMakeLists.txt +++ b/xo-interpreter2/src/interpreter2/CMakeLists.txt @@ -4,6 +4,7 @@ set(SELF_LIB xo_interpreter2) set(SELF_SRCS init_interpreter2.cpp VirtualSchematikaMachine.cpp + VsmFrame.cpp VsmInstr.cpp #IExpression_Any.cpp #interpreter2_register_facets.cpp diff --git a/xo-interpreter2/src/interpreter2/VirtualSchematikaMachine.cpp b/xo-interpreter2/src/interpreter2/VirtualSchematikaMachine.cpp index 7c4873fb..dc876b44 100644 --- a/xo-interpreter2/src/interpreter2/VirtualSchematikaMachine.cpp +++ b/xo-interpreter2/src/interpreter2/VirtualSchematikaMachine.cpp @@ -4,8 +4,8 @@ **/ #include "VirtualSchematikaMachine.hpp" -#include -#include +#include +#include #include #include #include @@ -117,6 +117,13 @@ namespace xo { return false; case vsm_opcode::eval: _do_eval_op(); + break; + case vsm_opcode::apply: + _do_apply_op(); + break; + case vsm_opcode::evalargs: + _do_evalargs_op(); + break; } return true; @@ -187,8 +194,47 @@ namespace xo { void VirtualSchematikaMachine::_do_eval_apply_op() { - // not implemented - assert(false); + // ApplyExpr in expr_ register + + // assuming bump allocator: + // + // DArray VsmApplyFrame VsmEvalArgsFrame + // v v v + // +----------------------+-------+------+----+--------+-------+------+-------+ + // | argument expressions | par x | cont | fn | args x | par x | cont | i_arg | + // +----------------------+-----|-+------+----+------|-+-----|-+------+-------+ + // ^ ^ | | | + // | \----------------------------------/ + // \ | / + // \-----------------------------------------------/ + // / + // <---------------------------/ + // + // - VsmEvalArgsFrame: owned by VSM, state for evalargs loop + // - VsmApplyFrame: owned by VSM, state for transferring control to called function + // - DArray: contains evaluated args; owned by called primitive + + auto apply = obj::from(expr_); + + // evaluated arguments + DArray * args = DArray::empty(mm_.to_op(), + apply->n_args()); + + // TODO: check function signature + + VsmApplyFrame * apply_frame + = VsmApplyFrame::make(mm_.to_op(), stack_, cont_, args); + + VsmEvalArgsFrame * evalargs_frame + = VsmEvalArgsFrame::make(mm_.to_op(), apply_frame, VsmInstr::c_apply); + + this->stack_ = evalargs_frame; + + // Setup evaluation of first argument. No new stack for this. + + this->cont_ = VsmInstr::c_evalargs; + this->expr_ = apply->fn(); + this->pc_ = VsmInstr::c_eval; } void @@ -204,6 +250,21 @@ namespace xo { // not implemented assert(false); } + + void + VirtualSchematikaMachine::_do_apply_op() + { + // not implemented + assert(false); + } + + void + VirtualSchematikaMachine::_do_evalargs_op() + { + // not implemented + assert(false); + } + } /*namespace scm*/ } /*namespace xo*/ diff --git a/xo-interpreter2/src/interpreter2/VsmFrame.cpp b/xo-interpreter2/src/interpreter2/VsmFrame.cpp new file mode 100644 index 00000000..2f262431 --- /dev/null +++ b/xo-interpreter2/src/interpreter2/VsmFrame.cpp @@ -0,0 +1,68 @@ +/** @file VsmFrame.cpp + * + * @author Roland Conybeare, Feb 2026 + **/ + +#include "VsmFrame.hpp" + +namespace xo { + using xo::facet::typeseq; + + namespace scm { + + VsmApplyFrame::VsmApplyFrame(VsmFrame * old_parent, + VsmInstr old_cont, + DArray * args) + : VsmFrame(old_parent, old_cont), + args_{args} + {} + + VsmApplyFrame * + VsmApplyFrame::make(obj mm, + VsmFrame * old_parent, + VsmInstr old_cont, + DArray * args) + { + VsmApplyFrame * result = nullptr; + + void * mem = mm.alloc(typeseq::id(), + sizeof(VsmApplyFrame)); + + result = new (mem) VsmApplyFrame(old_parent, + old_cont, + args); + + assert(result); + + return result; + } + + // ----- VsmEvalArgsFrame ----- + + VsmEvalArgsFrame::VsmEvalArgsFrame(VsmApplyFrame * old_parent, + VsmInstr old_cont) + : VsmFrame(old_parent, old_cont) + {} + + VsmEvalArgsFrame * + VsmEvalArgsFrame::make(obj mm, + VsmApplyFrame * apply_frame, + VsmInstr cont) + { + VsmEvalArgsFrame * result = nullptr; + + void * mem = mm.alloc(typeseq::id(), + sizeof(VsmEvalArgsFrame)); + + result = new (mem) VsmEvalArgsFrame(apply_frame, cont); + + assert(result); + + return result; + } + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end VsmFrame.cpp */ + diff --git a/xo-interpreter2/src/interpreter2/VsmInstr.cpp b/xo-interpreter2/src/interpreter2/VsmInstr.cpp index 95a0ef74..8d0907fc 100644 --- a/xo-interpreter2/src/interpreter2/VsmInstr.cpp +++ b/xo-interpreter2/src/interpreter2/VsmInstr.cpp @@ -12,6 +12,12 @@ namespace xo { VsmInstr VsmInstr::c_eval = VsmInstr(vsm_opcode::eval); + + VsmInstr + VsmInstr::c_apply = VsmInstr(vsm_opcode::apply); + + VsmInstr + VsmInstr::c_evalargs = VsmInstr(vsm_opcode::evalargs); } /*namespace scm*/ } /*namespace xo*/ diff --git a/xo-interpreter2/utest/VirtualSchematikaMachine.test.cpp b/xo-interpreter2/utest/VirtualSchematikaMachine.test.cpp index 0a2ba92f..59104860 100644 --- a/xo-interpreter2/utest/VirtualSchematikaMachine.test.cpp +++ b/xo-interpreter2/utest/VirtualSchematikaMachine.test.cpp @@ -149,6 +149,43 @@ namespace xo { vsm.visit_pools(visitor); } + TEST_CASE("VirtualSchematikaMachine-arith1", "[interpreter2][VSM]") + { + scope log(XO_DEBUG(true)); + + VsmConfig cfg; + VirtualSchematikaMachine vsm(cfg); + + bool eof_flag = false; + + vsm.begin_interactive_session(); + VsmResultExt res = vsm.read_eval_print(span_type::from_cstr("3.14159265 * 0.5;"), eof_flag); + + REQUIRE(res.is_value()); + REQUIRE(res.value()); + + log && log(xtag("res.tseq", res.value()->_typeseq())); + + auto x = obj::from(*res.value()); + + REQUIRE(x); + REQUIRE(x.data()->value() == 1011); + + REQUIRE(res.remaining_.size() == 1); + REQUIRE(*res.remaining_.lo() == '\n'); + + auto visitor = [&log](const MemorySizeInfo & info) { + log && log(xtag("resource", info.resource_name_), + xtag("used", info.used_), + xtag("alloc", info.allocated_), + xtag("commit", info.committed_), + xtag("resv", info.reserved_)); + }; + + FacetRegistry::instance().visit_pools(visitor); + vsm.visit_pools(visitor); + } + } /*namespace ut*/ } /*namespace xo*/