/** @file VirtualSchematikaMachine.cpp * * @author Roland Conybeare, Jan 2026 **/ #include "VirtualSchematikaMachine.hpp" #include "VsmApplyFrame.hpp" #include "VsmEvalArgsFrame.hpp" #include "Closure.hpp" #include #include #include #include #include #include #include #include #include #include namespace xo { using xo::print::APrintable; using xo::print::ppconfig; using xo::print::ppstate_standalone; using xo::mm::AGCObject; //using xo::mm::MemorySizeInfo; // not used yet using xo::mm::DX1Collector; using xo::facet::FacetRegistry; using std::cout; namespace scm { // NOTE: using heap for {DX1Collector, DSimpleRcx} instances // (though allocation from explictly mmap'd memory) // VirtualSchematikaMachine::VirtualSchematikaMachine(const VsmConfig & config) : config_{config}, mm_(box(new DX1Collector(config.x1_config_))), rcx_(box(new DSimpleRcx(mm_.to_op()))), reader_{config.rdr_config_, mm_.to_op()} { // TODO: allocate global_env } void VirtualSchematikaMachine::visit_pools(const MemorySizeVisitor & visitor) const { mm_.visit_pools(visitor); reader_.visit_pools(visitor); } void VirtualSchematikaMachine::begin_interactive_session() { reader_.begin_interactive_session(); } void VirtualSchematikaMachine::begin_batch_session() { reader_.begin_batch_session(); } VsmResultExt VirtualSchematikaMachine::read_eval_print(span_type input, bool eof) { if (input.empty()) { return VsmResultExt(); } auto [expr, remaining, error1] = reader_.read_expr(input, eof); if (!expr) { /* tokenizer error */ return VsmResultExt(VsmResult(error1), remaining); } VsmResult evalresult = this->start_eval(expr); if (evalresult.is_eval_error() || evalresult.is_tk_error()) { return VsmResultExt(evalresult, remaining); } assert(evalresult.is_value()); obj * p_value = std::get_if>(&(evalresult.result_)); assert(p_value); obj value_pr = FacetRegistry::instance().variant(*p_value); // pretty_toplevel(value_pr, &cout, ppconfig()); ppconfig ppc; ppstate_standalone pps(&cout, 0, &ppc); pps.prettyn(value_pr); return VsmResultExt(VsmResult(*p_value), remaining); } VsmResult VirtualSchematikaMachine::start_eval(obj expr) { this->pc_ = VsmInstr::c_eval; this->expr_ = expr; this->value_ = obj(); this->cont_ = VsmInstr::c_halt; this->run(); return value_; } void VirtualSchematikaMachine::run() { while (this->execute_one()) ; } bool VirtualSchematikaMachine::execute_one() { scope log(XO_DEBUG(true)); log && log(xtag("pc", pc_), xtag("cont", cont_)); obj stack_pr = (FacetRegistry::instance() .try_variant(stack_)); if (stack_pr) log && log(xtag("stack", stack_pr)); switch (pc_.opcode()) { case vsm_opcode::halt: case vsm_opcode::N: 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; } void VirtualSchematikaMachine::_do_eval_op() { switch(expr_.extype()) { case exprtype::invalid: case exprtype::N: break; case exprtype::constant: _do_eval_constant_op(); break; case exprtype::define: _do_eval_define_op(); break; case exprtype::lambda: _do_eval_lambda_op(); break; case exprtype::variable: _do_eval_variable_op(); break; case exprtype::varref: _do_eval_varref_op(); break; case exprtype::apply: _do_eval_apply_op(); break; case exprtype::ifexpr: _do_eval_if_else_op(); break; case exprtype::sequence: _do_eval_sequence_op(); break; } } void VirtualSchematikaMachine::_do_eval_constant_op() { auto expr = obj::from(expr_); this->value_ = expr.data()->value(); this->pc_ = this->cont_; } void VirtualSchematikaMachine::_do_eval_define_op() { // not implemented assert(false); } void VirtualSchematikaMachine::_do_eval_lambda_op() { // assuming bump allocator // // +----------- DArray---------+ +-------------DLocalEnv-----------+ +-----DClosure-------+ // | .cap |.size | .elts_[]... |h| .parent x | .symtab x | .args x |h| .lambda x | .env x | // +------+------+-------------+ +---------|-+---------|-+-------|-+ +---------|-+------|-+ // ^ ^ | | | | | // \-----------------------------|---------|-----------|---------/ | | // | | | | | // \---------|-----------|-----------------------|--------/ // | | | // <--------------------------------------/ | | // | | // v v // DLocalSymtab DLambdaExpr // // DClosure runtime procedure (created below) // DArray bound non-local variables (established by VSM) // DLocalEnv local environment (copy ref from VSM state) // h alloc header // DLocalSymtab local symbol table (created by parser) // DLambdaExpr lambda expression (created by parser) // // will create DClosure with local_env_ // local_env_ // global_env_ auto lambda = obj::from(expr_); DClosure * closure = DClosure::make(mm_.to_op(), lambda.data(), local_env_); this->value_ = obj(obj(closure)); this->pc_ = this->cont_; } void VirtualSchematikaMachine::_do_eval_variable_op() { // not implemented assert(false); } void VirtualSchematikaMachine::_do_eval_varref_op() { // not implemented assert(false); } void VirtualSchematikaMachine::_do_eval_apply_op() { // ApplyExpr in expr_ register // assuming bump allocator: // // DArray VsmApplyFrame VsmEvalArgsFrame // v v v // +----------------------+-------+-------+----+--------+-------+-------+-------+ // | argument expressions | par x | cont1 | fn | args x | par x | cont2 | 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 // - cont2: always c_apply // auto apply = obj::from(expr_); // evaluated arguments DArray * args = DArray::empty(mm_.to_op(), apply->n_args()); // TODO: check function signature DVsmApplyFrame * apply_frame = DVsmApplyFrame::make(mm_.to_op(), stack_, cont_, args); auto evalargs_frame = obj (DVsmEvalArgsFrame::make(mm_.to_op(), apply_frame, VsmInstr::c_apply, apply.data())); 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 VirtualSchematikaMachine::_do_eval_if_else_op() { // not implemented assert(false); } void VirtualSchematikaMachine::_do_eval_sequence_op() { // not implemented assert(false); } void VirtualSchematikaMachine::_do_apply_op() { // rcx_ : runtime context // fn_ : function to call // args_ : array of arguments // TODO: check argument types this->value_ = fn_.apply_nocheck(rcx_.to_op(), args_); this->pc_ = cont_; return; } void VirtualSchematikaMachine::_do_evalargs_op() { scope log(XO_DEBUG(false)); if (!value_.is_value()) { // error while evaluating function arg log.retroactively_enable(); log && log("error in apply -> terminating app"); this->pc_ = VsmInstr::c_halt; return; } // here: nested evaluation succeeded // value of one of {fn, arg(i), ..} in fn(arg0 .. arg(n-1)) // obj value = *(value_.value()); // value_ in [i_arg] value_ // . (if i_arg >= 0) . (if i_arg = -1) // . . // DArray . VsmApplyFrame . VsmEvalArgsFrame // v v v v v // +----------------------+-------+-------+----+--------+-------+-------+--------+-------+ // | argument expressions | par o | cont1 | fn | args x | par o | cont2 | applyx | 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 // - i_arg // if -1: value_ register holds function // if >=0: value_ register holds i'th function argument // auto evalargs_frame = obj::from(stack_); assert(evalargs_frame); int32_t i_arg = evalargs_frame->i_arg(); DVsmApplyFrame * apply_frame = evalargs_frame->parent(); const DApplyExpr * apply_expr = evalargs_frame->apply_expr(); if (i_arg == -1) { auto fn = value.to_facet(); if (fn) { apply_frame->assign_fn(fn); i_arg = evalargs_frame->increment_arg(); // now i_arg is 0 -> evaluate that argument this->expr_ = apply_expr->arg(i_arg); this->pc_ = VsmInstr::c_eval; //this->cont_ = VsmInstra::c_evalargs; // redundant, since preserved return; } else { // error - function position must deliver something with AProcedure? // or DClosure, but we'll get to that. log.retroactively_enable(); log("expected procedure in function position -> terminate"); assert(false); } } else { DArray * args = apply_frame->args(); log && log(xtag("i_arg", i_arg), xtag("n_arg", args->size()), xtag("cap", args->capacity())); args->push_back(value); i_arg = evalargs_frame->increment_arg(); if (i_arg == static_cast(apply_expr->n_args())) { // all apply-arguments have been evaluated // -> done with VsmEvalArgsFrame // this->fn_ = apply_frame->fn(); this->args_ = apply_frame->args(); this->stack_ = apply_frame->parent(); this->pc_ = VsmInstr::c_apply; this->cont_ = apply_frame->cont(); return; } else { this->expr_ = apply_expr->arg(i_arg); this->pc_ = VsmInstr::c_eval; //this->cont_ = VsmInstra::c_evalargs; // redundant, since preserved return; } } // not implemented assert(false); } } /*namespace scm*/ } /*namespace xo*/ /* end VirtualSchematikaMachine.hpp */