/** @file VirtualSchematikaMachine.hpp * * @author Roland Conybare, Jan 2026 **/ #pragma once #include "VsmConfig.hpp" #include "VsmInstr.hpp" #include "VsmFrame.hpp" #include #include #include #include namespace xo { namespace scm { struct EvaluationError { /** source location (in vsm implementation) at which error identified **/ std::string_view src_function_; /** error description (allocated from ErrorArena) **/ std::string_view error_description_; // TODO: info about location in schematika source }; /** similar to @ref xo::scm::ReaderResult **/ struct VsmResult { using AGCObject = xo::mm::AGCObject; using span_type = xo::mm::span; VsmResult() = default; VsmResult(obj value) : result_{value} {} VsmResult(TokenizerError err) : result_{err} {} bool is_value() const { return std::holds_alternative>(result_); } bool is_tk_error() const { return std::holds_alternative(result_); } bool is_eval_error() const { return std::holds_alternative(result_); } const obj * value() const { return std::get_if>(&result_); } /** result of evaluating first expression encountered in input **/ std::variant, TokenizerError, EvaluationError> result_; }; /** vsm result + reamining span **/ struct VsmResultExt : public VsmResult { using span_type = VsmResult::span_type; VsmResultExt() = default; VsmResultExt(const VsmResult & result, span_type rem) : VsmResult{result}, remaining_{rem} {} /** unconsumed portion of input **/ VsmResult::span_type remaining_; }; /** @class VirtualSchematikaMachine * @brief virtual machine for schematika **/ class VirtualSchematikaMachine { public: // will be DArenaVector> probably using Stack = void *; using AAllocator = xo::mm::AAllocator; using AGCObject = xo::mm::AGCObject; using MemorySizeVisitor = xo::mm::MemorySizeVisitor; using span_type = xo::mm::span; public: VirtualSchematikaMachine(const VsmConfig & config); /** visit vsm-owned memory pools; call visitor(info) for each **/ void visit_pools(const MemorySizeVisitor & visitor) const; /** begin interactive session. **/ void begin_interactive_session(); /** begin batch session **/ void begin_batch_session(); /** consume input @p input_cstr. * Require: must first start interactive/batch session **/ VsmResultExt read_eval_print(span_type input_span, bool eof); /** evaluate expression @p expr * Require: must first start interactive/batch session **/ VsmResult start_eval(obj expr); /** borrow calling thread to run indefinitely, * until halt instruction **/ void run(); /** execute vsm instruction in @ref pc_. * @retval instruction count. 1 unless pc_ is halt. **/ bool execute_one(); private: /** Require: * - expression in @ref expr_ **/ void _do_eval_op(); /** evaluate a constant expression * Require: * - expression in @ref expr_ **/ void _do_eval_constant_op(); /** evaluate a define-expression * Require: * - expression in @ref expr_ **/ void _do_eval_define_op(); /** evaluate a lambda expression * Require: * - expression in @ref expr_ **/ void _do_eval_lambda_op(); /** evaluate a variable expression * Require: * - expression in @ref expr_ **/ void _do_eval_variable_op(); /** evaluate an apply expression * Require: * - expression in @ref expr_ **/ void _do_eval_apply_op(); /** evaluate an if-else expression * Require: * - expression in @ref expr_ **/ void _do_eval_if_else_op(); /** evaluate a sequence expression * Require: * - expression in @ref expr_ **/ void _do_eval_sequence_op(); /** apply a function to evaluated arguments **/ void _do_apply_op(); /** evaluate arguments on behalf of a function call * Require: * - expression value in @ref value_ * - stack: * [0] VsmEvalArgsFrame * [1] VsmApplyFrame * ... **/ void _do_evalargs_op(); private: /* * Some registers are preserved by evaluation: * stack_ * cont_ * * Other registers are not preserved * pc_ * expr_ * fn_ * args_ * value_ */ /** configuration **/ VsmConfig config_; /** allocator (likely collector) for * expressions and values **/ box mm_; /** runtime context for this vsm. * For example, provides allocator to primitives **/ box rcx_; // 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; /** stack pointer **/ obj stack_; /** expression register **/ obj expr_; /** function to call **/ obj fn_; /** evaluated argument list **/ DArray * args_; /** result register **/ VsmResult value_; /** continuation register **/ VsmInstr cont_ = VsmInstr::c_halt; }; } /*namespace scm*/ } /*namespace xo*/ /* end VirtualSchematikaMachine.hpp */