xo-interpreter2 stack: define-expr's work at top-level

This commit is contained in:
Roland Conybeare 2026-02-17 14:42:17 -05:00
commit 6f3833d6fb
56 changed files with 1550 additions and 65 deletions

View file

@ -8,6 +8,10 @@ set(SELF_SRCS
VirtualSchematikaMachine.cpp
DVsmDefContFrame.cpp
IGCObject_DVsmDefContFrame.cpp
IPrintable_DVsmDefContFrame.cpp
DVsmEvalArgsFrame.cpp
IGCObject_DVsmEvalArgsFrame.cpp
IPrintable_DVsmEvalArgsFrame.cpp
@ -32,9 +36,13 @@ set(SELF_SRCS
IGCObject_DClosure.cpp
IPrintable_DClosure.cpp
DGlobalEnv.cpp
IGCObject_DGlobalEnv.cpp
IPrintable_DGlobalEnv.cpp
DLocalEnv.cpp
IGCObject_DLocalEnv.cpp
IPrintable_DLocalEnv.cpp
DLocalEnv.cpp
DVsmRcx.cpp
IRuntimeContext_DVsmRcx.cpp

View file

@ -0,0 +1,128 @@
/** @file DGlobalEnv.cpp
*
* @author Roland Conybeare, Feb 2026
**/
#include "GlobalEnv.hpp"
#include <xo/expression2/GlobalSymtab.hpp>
#include <xo/object2/Array.hpp>
namespace xo {
using xo::mm::AAllocator;
using xo::mm::AGCObject;
namespace scm {
DGlobalEnv::DGlobalEnv(DGlobalSymtab * symtab, DArray * values)
: symtab_{symtab}, values_{values}
{}
DGlobalEnv *
DGlobalEnv::_make(obj<AAllocator> mm,
DGlobalSymtab * symtab)
{
DArray * values = DArray::empty(mm, symtab->capacity());
void * mem = mm.alloc_for<DGlobalSymtab>();
return new (mem) DGlobalEnv(symtab, values);
}
obj<AGCObject>
DGlobalEnv::lookup_value(Binding ix) const noexcept
{
if (!ix.is_global()) {
assert(false);
return obj<AGCObject>();
}
if (ix.j_slot() >= static_cast<int32_t>(values_->size())) {
assert(false);
return obj<AGCObject>();
}
return (*values_)[ix.j_slot()];
}
void
DGlobalEnv::assign_value(obj<AAllocator> mm, Binding ix, obj<AGCObject> x)
{
scope log(XO_DEBUG(true),
xtag("ix.j_slot", ix.j_slot()),
xtag("values.cap", values_->capacity()));
assert(ix.is_global());
if (ix.j_slot() >= static_cast<int32_t>(values_->size())) {
// Control will come here in interpreter as new definitions are introduced.
// After seeing
// def foo = 1.2345;
// introducing new symbol foo:
// GlobalSymtab extends to include foo without this GlobalEnv
// knowing about it.
if (ix.j_slot() + 1 > static_cast<int32_t>(values_->capacity())) {
// realloc global array for more size
size_t cap_2x = 2 * values_->capacity();
while (cap_2x < static_cast<size_t>(ix.j_slot() + 1))
cap_2x = 2 * cap_2x;
DArray * values_2x = DArray::copy(mm, values_, cap_2x);
assert(values_2x);
if (values_2x) {
log && log("STUB: need write barrier for GC (also in GlobalSymtab!)");
this->values_ = values_2x;
} else {
return;
}
}
/** expand size sot that j_slot is valid **/
values_->resize(ix.j_slot() + 1);
}
log && log("STUB: need write barrier for GC here");
(*values_)[ix.j_slot()] = x;
}
// ----- AGCObject facet -----
std::size_t
DGlobalEnv::shallow_size() const noexcept
{
return sizeof(*this);
}
DGlobalEnv *
DGlobalEnv::shallow_copy(obj<AAllocator> mm) const noexcept
{
return mm.std_copy_for<DGlobalEnv>(this);
}
std::size_t
DGlobalEnv::forward_children(obj<ACollector> gc) noexcept
{
gc.forward_inplace(&symtab_);
gc.forward_inplace(&values_);
return this->shallow_size();
}
// ----- APrintable facet -----
bool
DGlobalEnv::pretty(const ppindentinfo & ppii) const
{
return ppii.pps()->pretty_struct
(ppii,
"DGlobalEnv",
refrtag("size", symtab_->size()));
}
} /*namespace scm*/
} /*namespace xo*/
/* end DGlobalEnv.cpp */

View file

@ -6,6 +6,7 @@
#include "LocalEnv.hpp"
#include <xo/object2/Array.hpp>
#include <xo/reflectutil/typeseq.hpp>
#include <xo/indentlog/scope.hpp>
namespace xo {
using xo::mm::AGCObject;
@ -64,6 +65,8 @@ namespace xo {
void
DLocalEnv::assign_value(Binding ix, obj<AGCObject> x)
{
scope log(XO_DEBUG(true));
assert(!ix.is_global());
const DLocalEnv * env = this;
@ -76,6 +79,7 @@ namespace xo {
auto j = ix.j_slot();
if (j < static_cast<decltype(j)>(env->size())) {
log && log("STUB: need write barrier for GC here");
(*(env->args_))[j] = x;
} else {
assert(false);

View file

@ -0,0 +1,68 @@
/** @file DVsmDefContFrame.cpp
*
* @author Roland Conybeare, Feb 2026
**/
#include "DVsmDefContFrame.hpp"
#include <xo/expression2/DefineExpr.hpp>
#include <xo/gc/Collector.hpp>
namespace xo {
namespace scm {
DVsmDefContFrame::DVsmDefContFrame(obj<AGCObject> parent,
VsmInstr cont,
DDefineExpr * def_expr)
: parent_{parent},
cont_{cont},
def_expr_{def_expr}
{}
DVsmDefContFrame *
DVsmDefContFrame::make(obj<AAllocator> mm,
obj<AGCObject> parent,
VsmInstr cont,
DDefineExpr * def_expr)
{
void * mem = mm.alloc_for<DVsmDefContFrame>();
return new (mem) DVsmDefContFrame(parent, cont, def_expr);
}
// gcobject facet
std::size_t
DVsmDefContFrame::shallow_size() const noexcept
{
return sizeof(*this);
}
DVsmDefContFrame *
DVsmDefContFrame::shallow_copy(obj<AAllocator> mm) const noexcept
{
return mm.std_copy_for<DVsmDefContFrame>(this);
}
std::size_t
DVsmDefContFrame::forward_children(obj<ACollector> gc) noexcept
{
gc.forward_inplace(&parent_);
gc.forward_inplace(&def_expr_);
return this->shallow_size();
}
// printable facet
bool
DVsmDefContFrame::pretty(const ppindentinfo & ppii) const noexcept
{
return ppii.pps()->pretty_struct(ppii,
"DVsmDefContFrame",
refrtag("cont", cont_));
}
} /*namespace scm*/
} /*namespace xo*/
/* end DVsmDefContFrame.cpp */

View file

@ -0,0 +1,39 @@
/** @file IGCObject_DGlobalEnv.cpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [xo-facet/codegen/genfacet]
* arguments:
* --input [idl/IGCObject_DGlobalEnv.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [iface_facet_any.hpp.j2]
* 3. idl for facet methods
* [idl/IGCObject_DGlobalEnv.json5]
**/
#include "env/IGCObject_DGlobalEnv.hpp"
namespace xo {
namespace scm {
auto
IGCObject_DGlobalEnv::shallow_size(const DGlobalEnv & self) noexcept -> size_type
{
return self.shallow_size();
}
auto
IGCObject_DGlobalEnv::shallow_copy(const DGlobalEnv & self, obj<AAllocator> mm) noexcept -> Opaque
{
return self.shallow_copy(mm);
}
auto
IGCObject_DGlobalEnv::forward_children(DGlobalEnv & self, obj<ACollector> gc) noexcept -> size_type
{
return self.forward_children(gc);
}
} /*namespace scm*/
} /*namespace xo*/
/* end IGCObject_DGlobalEnv.cpp */

View file

@ -11,7 +11,7 @@
* [idl/IGCObject_DLocalEnv.json5]
**/
#include "detail/IGCObject_DLocalEnv.hpp"
#include "env/IGCObject_DLocalEnv.hpp"
namespace xo {
namespace scm {

View file

@ -0,0 +1,39 @@
/** @file IGCObject_DVsmDefContFrame.cpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [xo-facet/codegen/genfacet]
* arguments:
* --input [idl/IGCObject_DVsmDefContFrame.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [iface_facet_any.hpp.j2]
* 3. idl for facet methods
* [idl/IGCObject_DVsmDefContFrame.json5]
**/
#include "define/IGCObject_DVsmDefContFrame.hpp"
namespace xo {
namespace scm {
auto
IGCObject_DVsmDefContFrame::shallow_size(const DVsmDefContFrame & self) noexcept -> size_type
{
return self.shallow_size();
}
auto
IGCObject_DVsmDefContFrame::shallow_copy(const DVsmDefContFrame & self, obj<AAllocator> mm) noexcept -> Opaque
{
return self.shallow_copy(mm);
}
auto
IGCObject_DVsmDefContFrame::forward_children(DVsmDefContFrame & self, obj<ACollector> gc) noexcept -> size_type
{
return self.forward_children(gc);
}
} /*namespace scm*/
} /*namespace xo*/
/* end IGCObject_DVsmDefContFrame.cpp */

View file

@ -0,0 +1,28 @@
/** @file IPrintable_DGlobalEnv.cpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [xo-facet/codegen/genfacet]
* arguments:
* --input [idl/IPrintable_DGlobalEnv.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [iface_facet_any.hpp.j2]
* 3. idl for facet methods
* [idl/IPrintable_DGlobalEnv.json5]
**/
#include "env/IPrintable_DGlobalEnv.hpp"
namespace xo {
namespace scm {
auto
IPrintable_DGlobalEnv::pretty(const DGlobalEnv & self, const ppindentinfo & ppii) -> bool
{
return self.pretty(ppii);
}
} /*namespace scm*/
} /*namespace xo*/
/* end IPrintable_DGlobalEnv.cpp */

View file

@ -11,7 +11,7 @@
* [idl/IPrintable_DLocalEnv.json5]
**/
#include "detail/IPrintable_DLocalEnv.hpp"
#include "env/IPrintable_DLocalEnv.hpp"
namespace xo {
namespace scm {

View file

@ -0,0 +1,28 @@
/** @file IPrintable_DVsmDefContFrame.cpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [xo-facet/codegen/genfacet]
* arguments:
* --input [idl/IPrintable_DVsmDefContFrame.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [iface_facet_any.hpp.j2]
* 3. idl for facet methods
* [idl/IPrintable_DVsmDefContFrame.json5]
**/
#include "define/IPrintable_DVsmDefContFrame.hpp"
namespace xo {
namespace scm {
auto
IPrintable_DVsmDefContFrame::pretty(const DVsmDefContFrame & self, const ppindentinfo & ppii) -> bool
{
return self.pretty(ppii);
}
} /*namespace scm*/
} /*namespace xo*/
/* end IPrintable_DVsmDefContFrame.cpp */

View file

@ -4,6 +4,7 @@
**/
#include "VirtualSchematikaMachine.hpp"
#include "VsmDefContFrame.hpp"
#include "VsmApplyFrame.hpp"
#include "VsmEvalArgsFrame.hpp"
#include "VsmApplyClosureFrame.hpp"
@ -11,10 +12,12 @@
#include "VsmSeqContFrame.hpp"
#include "VsmRcx.hpp"
#include "Closure.hpp"
#include <xo/expression2/DefineExpr.hpp>
#include <xo/expression2/ApplyExpr.hpp>
#include <xo/expression2/LambdaExpr.hpp>
#include <xo/expression2/Constant.hpp>
#include <xo/expression2/SequenceExpr.hpp>
#include <xo/expression2/UniqueString.hpp>
#include <xo/object2/Boolean.hpp>
#include <xo/procedure2/RuntimeContext.hpp>
//#include <xo/procedure2/SimpleRcx.hpp>
@ -65,10 +68,10 @@ namespace xo {
DArena * arena = new DArena(config_.error_config_);
assert(arena);
error_mm_.adopt(obj<AAllocator,DArena>(arena));
this->error_mm_.adopt(obj<AAllocator,DArena>(arena));
}
// TODO: allocate global_env
this->global_env_ = DGlobalEnv::_make(mm_.to_op(), reader_.global_symtab());
}
obj<AAllocator>
@ -166,17 +169,20 @@ namespace xo {
VirtualSchematikaMachine::execute_one()
{
scope log(XO_DEBUG(config_.debug_flag_));
log && log(xtag("pc", pc_),
xtag("cont", cont_));
obj<APrintable> stack_pr = stack_.to_facet<APrintable>();
// = (FacetRegistry::instance()
// .try_variant<APrintable,AGCObject>(stack_));
auto expr_pr = expr_.to_facet<APrintable>();
if (expr_pr)
log && log(xtag("expr", expr_pr));
auto stack_pr = stack_.to_facet<APrintable>();
if (stack_pr)
log && log(xtag("stack", stack_pr));
switch (pc_.opcode()) {
case vsm_opcode::sentinel:
case vsm_opcode::halt:
case vsm_opcode::N:
return false;
@ -189,6 +195,9 @@ namespace xo {
case vsm_opcode::evalargs:
_do_evalargs_op();
break;
case vsm_opcode::def_cont:
_do_def_cont_op();
break;
case vsm_opcode::apply_cont:
_do_apply_cont_op();
break;
@ -244,14 +253,84 @@ namespace xo {
= obj<AExpression,DConstant>::from(expr_);
this->value_ = VsmResult(expr.data()->value());
this->pc_ = this->cont_;
this->cont_ = VsmInstr::c_sentinel;
}
void
VirtualSchematikaMachine::_do_eval_define_op()
{
// not implemented
assert(false);
scope log(XO_DEBUG(true));
auto def_expr
= obj<AExpression,DDefineExpr>::from(expr_);
if (local_env_ == nullptr) {
// top-level define
// .stack_ --+
// |
// v
// +------DVsmDefContFrame------+
// | .parent x | .cont | .def x |
// +---------|-+-------+------|-+
// | |
// ParserStack* <-----/ |
// |
// v
// DDefineExpr
/* stack frame for nested continuation
* (to perform assignment)
*/
auto defcont_frame
= obj<AGCObject,DVsmDefContFrame>
(DVsmDefContFrame::make(mm_.to_op(),
this->stack_ /*saved stack*/,
this->cont_ /*saved cont*/,
def_expr.data() /*saved expr*/));
this->stack_ = defcont_frame;
// setup evaluation of rhs
this->expr_ = def_expr->rhs();
this->pc_ = VsmInstr::c_eval;
this->cont_ = VsmInstr::c_def_cont;
} else {
// nested defines implemented by rewriting,
// so this branch should be unreachable
assert(false);
}
}
void
VirtualSchematikaMachine::_do_def_cont_op()
{
// see DVsmDefContFrame
auto frame = obj<AGCObject,DVsmDefContFrame>::from(stack_);
assert(frame);
assert(value_.is_value());
// TODO: verify that value satisfies expected type ?
DVariable * lhs = frame->def_expr()->lhs();
obj<AGCObject> rhs = *value_.value();
assert(lhs->path().is_global());
global_env_->assign_value(mm_.to_op(), lhs->path(), rhs);
// TODO: unfortunate const_cast here, because obj<> doesn't support const DRepr yet
this->value_ = VsmResult(obj<AGCObject,DUniqueString>(const_cast<DUniqueString*>(lhs->name())));
this->stack_ = frame->parent();
this->pc_ = frame->cont();
this->cont_ = VsmInstr::c_sentinel;
}
void
@ -294,7 +373,9 @@ namespace xo {
this->value_
= VsmResult(obj<AGCObject>(obj<AGCObject,DClosure>(closure)));
this->pc_ = this->cont_;
this->cont_ = VsmInstr::c_sentinel;
}
void
@ -320,7 +401,9 @@ namespace xo {
if (value) {
this->value_ = VsmResult(value);
this->pc_ = this->cont_;
this->cont_ = VsmInstr::c_sentinel;
return;
}
@ -338,6 +421,7 @@ namespace xo {
// 3. have every vsm instruction check inputs for errors
this->pc_ = VsmInstr::c_halt;
this->cont_ = VsmInstr::c_sentinel;
}
void
@ -385,9 +469,9 @@ namespace xo {
// Setup evaluation of first argument. No new stack for this.
this->cont_ = VsmInstr::c_evalargs;
this->expr_ = apply->fn();
this->pc_ = VsmInstr::c_eval;
this->cont_ = VsmInstr::c_evalargs;
}
void
@ -404,9 +488,9 @@ namespace xo {
stack_, cont_, ifelse_expr.data()));
this->stack_ = ifelse_frame;
this->cont_ = VsmInstr::c_ifelse_cont;
this->expr_ = ifelse_expr->test();
this->pc_ = VsmInstr::c_eval;
this->cont_ = VsmInstr::c_ifelse_cont;
}
void
@ -445,9 +529,9 @@ namespace xo {
// Setup evaluation of first sequence element
this->cont_ = VsmInstr::c_seq_cont;
this->expr_ = (*seq_expr.data())[0];
this->pc_ = VsmInstr::c_eval;
this->cont_ = VsmInstr::c_seq_cont;
}
void
@ -514,6 +598,7 @@ namespace xo {
this->local_env_ = local_env;
this->expr_ = lambda->body_expr();
this->pc_ = VsmInstr::c_eval;
// cont_ already established
}
void
@ -523,6 +608,7 @@ namespace xo {
this->value_ = VsmResult(fn.apply_nocheck(rcx_.to_op(), args_));
this->pc_ = cont_;
this->cont_ = VsmInstr::c_sentinel;
}
void
@ -537,6 +623,7 @@ namespace xo {
log && log("error in apply -> terminating app");
this->pc_ = VsmInstr::c_halt;
this->cont_ = VsmInstr::c_sentinel;
return;
}
@ -595,7 +682,7 @@ namespace xo {
this->expr_ = apply_expr->arg(i_arg);
this->pc_ = VsmInstr::c_eval;
//this->cont_ = VsmInstra::c_evalargs; // redundant, since preserved
this->cont_ = VsmInstr::c_evalargs;
return;
} else {
@ -633,7 +720,7 @@ namespace xo {
} else {
this->expr_ = apply_expr->arg(i_arg);
this->pc_ = VsmInstr::c_eval;
//this->cont_ = VsmInstra::c_evalargs; // redundant, since preserved
this->cont_ = VsmInstr::c_evalargs;
return;
}
@ -655,6 +742,7 @@ namespace xo {
this->stack_ = frame->parent();
this->local_env_ = frame->local_env();
this->pc_ = frame->cont();
this->cont_ = VsmInstr::c_sentinel;
}
void
@ -682,9 +770,9 @@ namespace xo {
}
this->stack_ = frame->parent();
this->cont_ = frame->cont();
this->expr_ = next_expr;
this->pc_ = VsmInstr::c_eval;
this->cont_ = frame->cont();
} else {
auto error = DRuntimeError::make(mm_.to_op(),
"_do_ifelse_cont_op",
@ -698,6 +786,7 @@ namespace xo {
// 3. have every vsm instruction check inputs for errors
this->pc_ = VsmInstr::c_halt;
this->cont_ = VsmInstr::c_sentinel;
}
}
@ -722,13 +811,16 @@ namespace xo {
this->stack_ = frame->parent();
this->pc_ = frame->cont();
this->cont_ = VsmInstr::c_sentinel;
return;
} else {
frame->incr_i_seq();
this->cont_ = VsmInstr::c_seq_cont;
this->expr_ = (*seq_expr)[i_seq];
this->pc_ = VsmInstr::c_eval;
this->cont_ = VsmInstr::c_seq_cont;
return;
}
}

View file

@ -11,10 +11,12 @@ namespace xo {
vsm_opcode_descr(vsm_opcode x)
{
switch (x) {
case vsm_opcode::sentinel: return "sentinel";
case vsm_opcode::halt: return "halt";
case vsm_opcode::eval: return "eval";
case vsm_opcode::apply: return "apply";
case vsm_opcode::evalargs: return "evalargs";
case vsm_opcode::def_cont: return "def_cont";
case vsm_opcode::apply_cont: return "apply_cont";
case vsm_opcode::ifelse_cont: return "ifelse_cont";
case vsm_opcode::seq_cont: return "seq_cont";
@ -25,6 +27,9 @@ namespace xo {
return "opcode?";
}
VsmInstr
VsmInstr::c_sentinel = VsmInstr(vsm_opcode::sentinel);
VsmInstr
VsmInstr::c_halt = VsmInstr(vsm_opcode::halt);
@ -37,12 +42,15 @@ namespace xo {
VsmInstr
VsmInstr::c_evalargs = VsmInstr(vsm_opcode::evalargs);
VsmInstr
VsmInstr::c_def_cont = VsmInstr(vsm_opcode::def_cont);
VsmInstr
VsmInstr::c_apply_cont = VsmInstr(vsm_opcode::apply_cont);
VsmInstr
VsmInstr::c_ifelse_cont = VsmInstr(vsm_opcode::ifelse_cont);
VsmInstr
VsmInstr::c_seq_cont = VsmInstr(vsm_opcode::seq_cont);
} /*namespace scm*/

View file

@ -6,6 +6,7 @@
#include "interpreter2_register_facets.hpp"
#include "DPrimitive_gco_2_gco_gco.hpp"
#include "VsmDefContFrame.hpp"
#include "VsmApplyFrame.hpp"
#include "VsmEvalArgsFrame.hpp"
#include "VsmApplyClosureFrame.hpp"
@ -13,6 +14,7 @@
#include "VsmSeqContFrame.hpp"
#include "Primitive_gco_2_gco_gco.hpp"
#include "Closure.hpp"
#include "GlobalEnv.hpp"
#include "LocalEnv.hpp"
#include "VsmRcx.hpp"
@ -39,6 +41,7 @@ namespace xo {
// +- VsmApplyFrame
// +- VsmEvalArgsFrame
// +- VsmApplyClosureFrame
// +- VsmDefContFrame
// +- VsmIfElseContFrame
// \- VsmSeqContFrame
@ -51,12 +54,20 @@ namespace xo {
FacetRegistry::register_impl<AGCObject, DVsmApplyClosureFrame>();
FacetRegistry::register_impl<APrintable, DVsmApplyClosureFrame>();
FacetRegistry::register_impl<AGCObject, DVsmDefContFrame>();
FacetRegistry::register_impl<APrintable, DVsmDefContFrame>();
FacetRegistry::register_impl<AGCObject, DVsmIfElseContFrame>();
FacetRegistry::register_impl<APrintable, DVsmIfElseContFrame>();
FacetRegistry::register_impl<AGCObject, DVsmSeqContFrame>();
FacetRegistry::register_impl<APrintable, DVsmSeqContFrame>();
// GlobalEnv
FacetRegistry::register_impl<AGCObject, DGlobalEnv>();
FacetRegistry::register_impl<APrintable, DGlobalEnv>();
// LocalEnv
FacetRegistry::register_impl<AGCObject, DLocalEnv>();
@ -84,8 +95,12 @@ namespace xo {
log && log(xtag("DVsmApplyFrame.tseq", typeseq::id<DVsmApplyFrame>()));
log && log(xtag("DVsmEvalArgsFrame.tseq", typeseq::id<DVsmEvalArgsFrame>()));
log && log(xtag("DVsmApplyClosureFrame.tseq", typeseq::id<DVsmApplyClosureFrame>()));
log && log(xtag("DVsmDefContFrame.tseq", typeseq::id<DVsmDefContFrame>()));
log && log(xtag("DVsmDefContFrame.tseq", typeseq::id<DVsmDefContFrame>()));
log && log(xtag("DVsmIfElseContFrame.tseq", typeseq::id<DVsmIfElseContFrame>()));
log && log(xtag("DVsmSeqContFrame.tseq", typeseq::id<DVsmSeqContFrame>()));
log && log(xtag("DClosure.tseq", typeseq::id<DClosure>()));
log && log(xtag("DGlobalEnv.tseq", typeseq::id<DGlobalEnv>()));
log && log(xtag("DLocalEnv.tseq", typeseq::id<DLocalEnv>()));
log && log(xtag("DVsmRcx.tseq", typeseq::id<DVsmRcx>()));