xo-interpreter2: apply sequence now working in interpreter

This commit is contained in:
Roland Conybeare 2026-02-04 16:26:19 -05:00
commit 61d41a2298
8 changed files with 203 additions and 34 deletions

View file

@ -29,6 +29,8 @@ namespace xo {
obj<AProcedure> fn() const noexcept { return fn_; } obj<AProcedure> fn() const noexcept { return fn_; }
DArray * args() const noexcept { return args_; } DArray * args() const noexcept { return args_; }
void assign_fn(obj<AProcedure> x) { this->fn_ = x; }
std::size_t shallow_size() const noexcept; std::size_t shallow_size() const noexcept;
DVsmApplyFrame * shallow_copy(obj<AAllocator> mm) const noexcept; DVsmApplyFrame * shallow_copy(obj<AAllocator> mm) const noexcept;
std::size_t forward_children(obj<ACollector> gc) noexcept; std::size_t forward_children(obj<ACollector> gc) noexcept;

View file

@ -5,16 +5,18 @@
#pragma once #pragma once
#include "DVsmApplyFrame.hpp" #include "VsmApplyFrame.hpp"
#include <xo/expression2/DApplyExpr.hpp>
namespace xo { namespace xo {
namespace scm { namespace scm {
/** frame for executing an apply expression **/ /** frame for executing an apply expression **/
class DVsmEvalArgsFrame : public VsmFrame { class DVsmEvalArgsFrame {
public: public:
using ACollector = xo::mm::ACollector; using ACollector = xo::mm::ACollector;
using AAllocator = xo::mm::AAllocator; using AAllocator = xo::mm::AAllocator;
using AGCObject = xo::mm::AGCObject;
using ppindentinfo = xo::print::ppindentinfo; using ppindentinfo = xo::print::ppindentinfo;
public: public:
@ -24,12 +26,21 @@ namespace xo {
* old_cont = [xfer to called function] * old_cont = [xfer to called function]
* *
**/ **/
DVsmEvalArgsFrame(obj<AGCObject> old_parent, DVsmEvalArgsFrame(DVsmApplyFrame * parent,
VsmInstr old_cont); VsmInstr cont,
const DApplyExpr * apply_expr);
static DVsmEvalArgsFrame * make(obj<AAllocator> mm, static DVsmEvalArgsFrame * make(obj<AAllocator> mm,
obj<AGCObject> apply_frame, DVsmApplyFrame * apply_frame,
VsmInstr old_cont); VsmInstr old_cont,
const DApplyExpr * apply_expr);
DVsmApplyFrame * parent() const noexcept { return parent_; }
VsmInstr cont() const noexcept { return cont_; }
const DApplyExpr * apply_expr() const noexcept { return apply_expr_; }
int32_t i_arg() const noexcept { return i_arg_; }
int32_t increment_arg() { return ++i_arg_; }
std::size_t shallow_size() const noexcept; std::size_t shallow_size() const noexcept;
DVsmEvalArgsFrame * shallow_copy(obj<AAllocator> mm) const noexcept; DVsmEvalArgsFrame * shallow_copy(obj<AAllocator> mm) const noexcept;
@ -38,6 +49,14 @@ namespace xo {
bool pretty(const ppindentinfo & ppii) const; bool pretty(const ppindentinfo & ppii) const;
protected: protected:
/** parent stack frame **/
DVsmApplyFrame * parent_ = nullptr;
/** continuation after eval args completed (always VsmInstr::c_apply) **/
VsmInstr cont_;
/** expression being evaluated **/
const DApplyExpr * apply_expr_ = nullptr;
/** next argument to be evaluated. -1 means function head **/ /** next argument to be evaluated. -1 means function head **/
int32_t i_arg_ = -1; int32_t i_arg_ = -1;
}; };

View file

@ -147,7 +147,14 @@ namespace xo {
/** apply a function to evaluated arguments **/ /** apply a function to evaluated arguments **/
void _do_apply_op(); void _do_apply_op();
/** evaluate arguments on behalf of a function call **/ /** evaluate arguments on behalf of a function call
* Require:
* - expression value in @ref value_
* - stack:
* [0] VsmEvalArgsFrame
* [1] VsmApplyFrame
* ...
**/
void _do_evalargs_op(); void _do_evalargs_op();
private: private:
@ -159,6 +166,8 @@ namespace xo {
* Other registers are not preserved * Other registers are not preserved
* pc_ * pc_
* expr_ * expr_
* fn_
* args_
* value_ * value_
*/ */
@ -170,6 +179,11 @@ namespace xo {
**/ **/
box<AAllocator> mm_; box<AAllocator> mm_;
/** runtime context for this vsm.
* For example, provides allocator to primitives
**/
box<ARuntimeContext> rcx_;
// consider separate allocator (which _may_ turn out to be the same) // consider separate allocator (which _may_ turn out to be the same)
// for VM stack. Only works for code that doesn't rely on fancy // for VM stack. Only works for code that doesn't rely on fancy
// lexical scoping // lexical scoping
@ -186,6 +200,11 @@ namespace xo {
/** expression register **/ /** expression register **/
obj<AExpression> expr_; obj<AExpression> expr_;
/** function to call **/
obj<AProcedure> fn_;
/** evaluated argument list **/
DArray * args_;
/** result register **/ /** result register **/
VsmResult value_; VsmResult value_;

View file

@ -20,9 +20,7 @@ namespace xo {
VsmInstr cont) : parent_{parent}, cont_{cont} {} VsmInstr cont) : parent_{parent}, cont_{cont} {}
//obj<AGCObject> parent() const noexcept { return parent_; } //obj<AGCObject> parent() const noexcept { return parent_; }
VsmFrame * parent() const noexcept { obj<AGCObject> parent() const noexcept { return parent_; }
return reinterpret_cast<VsmFrame *>(parent_.data());
}
VsmInstr cont() const noexcept { return cont_; } VsmInstr cont() const noexcept { return cont_; }
protected: protected:

View file

@ -14,22 +14,26 @@ namespace xo {
// ----- VsmEvalArgsFrame ----- // ----- VsmEvalArgsFrame -----
DVsmEvalArgsFrame::DVsmEvalArgsFrame(obj<AGCObject> old_parent, DVsmEvalArgsFrame::DVsmEvalArgsFrame(DVsmApplyFrame * parent,
VsmInstr old_cont) VsmInstr cont,
: VsmFrame(old_parent, old_cont) const DApplyExpr * apply_expr)
: parent_{parent},
cont_{cont},
apply_expr_{apply_expr}
{} {}
DVsmEvalArgsFrame * DVsmEvalArgsFrame *
DVsmEvalArgsFrame::make(obj<AAllocator> mm, DVsmEvalArgsFrame::make(obj<AAllocator> mm,
obj<AGCObject> apply_frame, DVsmApplyFrame * apply_frame,
VsmInstr cont) VsmInstr cont,
const DApplyExpr * apply_expr)
{ {
DVsmEvalArgsFrame * result = nullptr; DVsmEvalArgsFrame * result = nullptr;
void * mem = mm.alloc(typeseq::id<DVsmEvalArgsFrame>(), void * mem = mm.alloc(typeseq::id<DVsmEvalArgsFrame>(),
sizeof(DVsmEvalArgsFrame)); sizeof(DVsmEvalArgsFrame));
result = new (mem) DVsmEvalArgsFrame(apply_frame, cont); result = new (mem) DVsmEvalArgsFrame(apply_frame, cont, apply_expr);
assert(result); assert(result);

View file

@ -8,6 +8,8 @@
#include "VsmEvalArgsFrame.hpp" #include "VsmEvalArgsFrame.hpp"
#include <xo/expression2/ApplyExpr.hpp> #include <xo/expression2/ApplyExpr.hpp>
#include <xo/expression2/Constant.hpp> #include <xo/expression2/Constant.hpp>
#include <xo/procedure2/RuntimeContext.hpp>
#include <xo/procedure2/SimpleRcx.hpp>
#include <xo/gc/DX1Collector.hpp> #include <xo/gc/DX1Collector.hpp>
#include <xo/gc/detail/IAllocator_DX1Collector.hpp> #include <xo/gc/detail/IAllocator_DX1Collector.hpp>
#include <xo/printable2/Printable.hpp> #include <xo/printable2/Printable.hpp>
@ -26,9 +28,13 @@ namespace xo {
namespace scm { namespace scm {
// NOTE: using heap for {DX1Collector, DSimpleRcx} instances
// (though allocation from explictly mmap'd memory)
//
VirtualSchematikaMachine::VirtualSchematikaMachine(const VsmConfig & config) VirtualSchematikaMachine::VirtualSchematikaMachine(const VsmConfig & config)
: config_{config}, : config_{config},
mm_(box<AAllocator,DX1Collector>(new DX1Collector(config.x1_config_))), mm_(box<AAllocator,DX1Collector>(new DX1Collector(config.x1_config_))),
rcx_(box<ARuntimeContext,DSimpleRcx>(new DSimpleRcx(mm_.to_op()))),
reader_{config.rdr_config_, mm_.to_op()} reader_{config.rdr_config_, mm_.to_op()}
{} {}
@ -211,21 +217,23 @@ namespace xo {
// assuming bump allocator: // assuming bump allocator:
// //
// DArray VsmApplyFrame VsmEvalArgsFrame // DArray VsmApplyFrame VsmEvalArgsFrame
// v v v // v v v
// +----------------------+-------+------+----+--------+-------+------+-------+ // +----------------------+-------+-------+----+--------+-------+-------+-------+
// | argument expressions | par x | cont | fn | args x | par x | cont | i_arg | // | argument expressions | par x | cont1 | fn | args x | par x | cont2 | i_arg |
// +----------------------+-----|-+------+----+------|-+-----|-+------+-------+ // +----------------------+-----|-+-------+----+------|-+-----|-+-------+-------+
// ^ ^ | | | // ^ ^ | | |
// | \----------------------------------/ // | \-----------------------------------/
// \ | / // \ | /
// \-----------------------------------------------/ // \------------------------------------------------/
// / // /
// <---------------------------/ // <---------------------------/
// //
// - VsmEvalArgsFrame: owned by VSM, state for evalargs loop // - VsmEvalArgsFrame: owned by VSM, state for evalargs loop
// - VsmApplyFrame: owned by VSM, state for transferring control to called function // - VsmApplyFrame: owned by VSM, state for transferring control to called function
// - DArray: contains evaluated args; owned by called primitive // - DArray: contains evaluated args; owned by called primitive
// - cont2: always c_apply
//
auto apply = obj<AExpression,DApplyExpr>::from(expr_); auto apply = obj<AExpression,DApplyExpr>::from(expr_);
@ -235,13 +243,13 @@ namespace xo {
// TODO: check function signature // TODO: check function signature
auto apply_frame DVsmApplyFrame * apply_frame
= obj<AGCObject,DVsmApplyFrame> = DVsmApplyFrame::make(mm_.to_op(), stack_, cont_, args);
(DVsmApplyFrame::make(mm_.to_op(), stack_, cont_, args));
auto evalargs_frame auto evalargs_frame
= obj<AGCObject,DVsmEvalArgsFrame> = obj<AGCObject,DVsmEvalArgsFrame>
(DVsmEvalArgsFrame::make(mm_.to_op(), apply_frame, VsmInstr::c_apply)); (DVsmEvalArgsFrame::make(mm_.to_op(),
apply_frame, VsmInstr::c_apply, apply.data()));
this->stack_ = evalargs_frame; this->stack_ = evalargs_frame;
@ -269,13 +277,131 @@ namespace xo {
void void
VirtualSchematikaMachine::_do_apply_op() VirtualSchematikaMachine::_do_apply_op()
{ {
// not implemented // rcx_ : runtime context
assert(false); // 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 void
VirtualSchematikaMachine::_do_evalargs_op() 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<AGCObject> 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<AGCObject,DVsmEvalArgsFrame>::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<AProcedure>();
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<int32_t>(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 // not implemented
assert(false); assert(false);
} }

View file

@ -166,10 +166,10 @@ namespace xo {
log && log(xtag("res.tseq", res.value()->_typeseq())); log && log(xtag("res.tseq", res.value()->_typeseq()));
auto x = obj<AGCObject,DInteger>::from(*res.value()); auto x = obj<AGCObject,DFloat>::from(*res.value());
REQUIRE(x); REQUIRE(x);
REQUIRE(x.data()->value() == 1011); REQUIRE(x.data()->value() == 1.570796325);
REQUIRE(res.remaining_.size() == 1); REQUIRE(res.remaining_.size() == 1);
REQUIRE(*res.remaining_.lo() == '\n'); REQUIRE(*res.remaining_.lo() == '\n');

View file

@ -68,7 +68,8 @@ namespace xo {
requires (std::same_as<Args, obj<AGCObject>> && ...) requires (std::same_as<Args, obj<AGCObject>> && ...)
static DArray * array(obj<AAllocator> mm, Args... args); static DArray * array(obj<AAllocator> mm, Args... args);
obj<AGCObject> operator[](size_type index) const noexcept { return elts_[index]; } const obj<AGCObject> & operator[](size_type index) const noexcept { return elts_[index]; }
obj<AGCObject> & operator[](size_type index) noexcept { return elts_[index]; }
///@} ///@}
/** @defgroup darray-access acecss methods **/ /** @defgroup darray-access acecss methods **/