xo-interpreter: handle define-expressions.

This commit is contained in:
Roland Conybeare 2025-11-24 18:01:24 -05:00
commit fb5c43dc85
16 changed files with 523 additions and 53 deletions

View file

@ -8,6 +8,8 @@ set(SELF_SRCS
GlobalEnv.cpp
VirtualSchematikaMachine.cpp
VsmInstr.cpp
VsmStackFrame.cpp
ExpressionBoxed.cpp
)
find_package(Threads REQUIRED)

View file

@ -0,0 +1,59 @@
/** @file ExpressionBoxed.cpp
*
* @author Roland Conybeare, Nov 2025
**/
#include "ExpressionBoxed.hpp"
#include "xo/reflect/Reflect.hpp"
namespace xo {
using xo::reflect::Reflect;
using xo::reflect::TaggedPtr;
namespace scm {
ExpressionBoxed::ExpressionBoxed(bp<Expression> c) : contents_{c.promote()}
{}
gp<ExpressionBoxed>
ExpressionBoxed::make(gc::IAlloc * mm,
bp<Expression> c)
{
return new (MMPtr(mm)) ExpressionBoxed(c);
}
TaggedPtr
ExpressionBoxed::self_tp() const
{
return Reflect::make_tp(const_cast<ExpressionBoxed *>(this));
}
void
ExpressionBoxed::display(std::ostream & os) const
{
os << contents_;
}
std::size_t
ExpressionBoxed::_shallow_size() const
{
return sizeof(ExpressionBoxed);
}
Object *
ExpressionBoxed::_shallow_copy(gc::IAlloc * mm) const
{
Cpof cpof(mm, this);
return new (cpof) ExpressionBoxed(*this);
}
std::size_t
ExpressionBoxed::_forward_children(gc::IAlloc *)
{
return _shallow_size();
}
} /*namespace scm*/
} /*namespace xo*/
/* end ExpressionBoxed.cpp */

View file

@ -101,7 +101,7 @@ namespace xo {
{
Cpof cpof(mm, this);
size_t z = size();
size_t z = this->size();
LocalEnv * copy = new (cpof) LocalEnv(cpof.mm_, parent_, symtab_, z);

View file

@ -28,7 +28,9 @@ namespace xo {
* to preserve option to share it
**/
Impl(const Config & config, up<IAlloc> mm, gp<Env> toplevel_env) :
config_{config}, vsm_{mm.get(), toplevel_env}, mm_{std::move(mm)} {}
config_{config},
vsm_{mm.get(), toplevel_env, config.vsm_log_level_},
mm_{std::move(mm)} {}
/** create instance + allocator **/
static up<Impl> make(const Config & cfg);

View file

@ -2,7 +2,10 @@
#include "VirtualSchematikaMachine.hpp"
#include "VsmInstr.hpp"
#include "ExpressionBoxed.hpp"
#include "xo/expression/ConstantInterface.hpp"
#include "xo/expression/DefineExpr.hpp"
#include "xo/expression/Variable.hpp"
#include "xo/alloc/GC.hpp"
/** continue after completing a VSM instruction;
@ -14,6 +17,8 @@
**/
#define VSM_ERROR(msg) report_error(msg); return;
namespace xo {
using xo::gc::GC;
@ -29,6 +34,12 @@ namespace xo {
* - expression in register @ref expr_
**/
static VsmInstr eval_op;
/** assign variable after evaluating rhs of a define-expression
* - opcode is Opcode::defexpr_assign
* - top stack frame contains {lhs, cont}
*/
static VsmInstr defexpr_assign_op;
};
VsmInstr
@ -37,18 +48,27 @@ namespace xo {
VsmInstr
VsmOps::eval_op{VsmInstr::Opcode::eval, "eval"};
VsmInstr
VsmOps::defexpr_assign_op{VsmInstr::Opcode::defexpr_assign, "defexpr-assign"};
// ----- VirtualSchematikaMachineFlyweight -----
VirtualSchematikaMachineFlyweight::VirtualSchematikaMachineFlyweight(gc::IAlloc * mm,
gp<Env> env) :
gp<Env> env,
log_level ll) :
object_mm_{mm},
toplevel_env_{env}
toplevel_env_{env},
log_level_{ll}
{}
// ----- VirtualSchematikaMachine -----
VirtualSchematikaMachine::VirtualSchematikaMachine(gc::IAlloc * mm, gp<Env> env) : flyweight_{mm, env}
VirtualSchematikaMachine::VirtualSchematikaMachine(gc::IAlloc * mm,
gp<Env> env,
log_level ll) : flyweight_{mm, env, ll}
{
this->env_ = env;
// gc roots
gc::GC * gc = GC::from(mm);
@ -90,10 +110,13 @@ namespace xo {
SchematikaError>
VirtualSchematikaMachine::eval(bp<Expression> expr, gp<Env> env)
{
this->pc_ = &VsmOps::eval_op;
this->expr_ = expr.promote();
this->env_ = env;
this->cont_ = &VsmOps::halt_op;
this->pc_ = &VsmOps::eval_op;
this->expr_ = expr.promote();
this->env_ = env;
this->stack_ = nullptr;
this->value_ = nullptr;
this->error_ = SchematikaError();
this->cont_ = &VsmOps::halt_op;
this->run();
@ -110,6 +133,9 @@ namespace xo {
void
VirtualSchematikaMachine::execute_one()
{
scope log(XO_DEBUG(true));
log && log("stack", stack_);
using Opcode = VsmInstr::Opcode;
switch (pc_->opcode()) {
@ -123,6 +149,8 @@ namespace xo {
case Opcode::eval:
{
log && log("eval");
/* generally speaking: opcode will be 1:1 with extypes */
switch (expr_->extype()) {
@ -130,9 +158,14 @@ namespace xo {
this->eval_constant_op();
break;
case exprtype::define:
this->eval_define_op();
break;
case exprtype::invalid:
case exprtype::primitive:
case exprtype::define:
case exprtype::assign:
case exprtype::apply:
case exprtype::lambda:
@ -151,6 +184,10 @@ namespace xo {
}
break;
case Opcode::defexpr_assign:
this->do_defexpr_assign_op();
break;
case Opcode::N_Opcode:
assert(false);
break;
@ -197,29 +234,60 @@ namespace xo {
// placeholder: primitive_op
#ifdef NOT_YET
void
VirtualSchematikaMachine::define_op()
VirtualSchematikaMachine::eval_define_op()
{
using xo::scm::DefineExpr;
scope log(XO_DEBUG(true));
auto mm = flyweight_.object_mm_;
bp<DefineExpr> expr = DefineExpr::from(expr_);
assert(expr);
assert(env_.get());
// note: expecting nested define to have expanded iteself into
// applying a lambda
// note: establish lhs_var first, to allow for recursion, for example:
// def fact(n: i64) { if (n == 0) then 1; else n * fact(n-1); }
/** remembers promised variable type **/
env_->establish_var(expr->lhs_variable());
this->env_->establish_var(expr->lhs_variable());
/** must promote rp<Expression> -> gp<ExpressionBoxed> **/
gp<ExpressionBoxed> lhs_0 = ExpressionBoxed::make(mm, expr->lhs_variable());
this->pc_ = &VsmOps::eval_op;
this->expr_ = expr->rhs();
/* lhs_var
* rhs
/* when control arrives at .cont_, will have:
* .value_ -> result of evaluating expr->rhs()
*/
this->stack_ = VsmStackFrame::push1(mm, this->stack_, lhs_0, cont_);
this->cont_ = &VsmOps::defexpr_assign_op;
}
void
VirtualSchematikaMachine::do_defexpr_assign_op()
{
/*
* - value: contains result of evaluating rhs of define
* - stack: top frame has 1 slot, holds variable to receive assignment
*/
assert(value_.get());
assert(stack_.get());
gp<VsmStackFrame> sp0 = this->stack_;
//this->value_ = this->value_; // preserve value from rhs of defexpr
this->stack_ = sp0->parent();
this->pc_ = this->cont_ = sp0->continuation();
}
#endif
} /*namespace scm*/
} /*namespace xo*/

View file

@ -0,0 +1,148 @@
/** @file VsmStackFrame.cpp
*
* @author Roland Conybeare, Nov 2025
**/
#include "VsmStackFrame.hpp"
#include "xo/reflect/Reflect.hpp"
#include "xo/reflect/StructReflector.hpp"
namespace xo {
using xo::reflect::Reflect;
using xo::reflect::StructReflector;
using xo::reflect::TypeDescrW;
using xo::reflect::TaggedPtr;
using xo::reflect::TypeDescrExtra;
using xo::reflect::EstablishTypeDescr;
using xo::reflect::StlVectorTdx;
namespace scm {
namespace {
// TOOD: move into CVector
std::size_t
slot_array_size(std::size_t n) {
return n * sizeof(gp<Object>);
}
}
VsmStackFrame::VsmStackFrame(gc::IAlloc * mm,
gp<VsmStackFrame> p,
std::size_t n,
const VsmInstr * cont) : parent_{p},
slot_v_{mm, n},
cont_{cont}
{}
gp<VsmStackFrame>
VsmStackFrame::push1(gc::IAlloc * mm,
gp<VsmStackFrame> p,
gp<Object> s0,
const VsmInstr * cont)
{
gp<VsmStackFrame> retval = new (MMPtr(mm)) VsmStackFrame(mm, p, 1, cont);
(*retval)[0] = s0;
return retval;
}
TaggedPtr
VsmStackFrame::self_tp() const
{
return Reflect::make_tp(const_cast<VsmStackFrame *>(this));
}
void
VsmStackFrame::display(std::ostream & os) const
{
os << "<vsm-stack-frame"
<< xtag("n", slot_v_.size());
#ifdef NOT_YET
for (std::size_t i = 0, n = n_slot(); i < n; ++i) {
char buf[24];
snprintf(buf, sizeof(buf), "v[%lu]", i);
os << xtag(buf, lookup(i));
}
#endif
os << ">";
}
std::size_t
VsmStackFrame::_shallow_size() const
{
std::size_t retval = sizeof(VsmStackFrame);
retval += gc::IAlloc::with_padding(slot_array_size(slot_v_.size()));
return retval;
}
Object *
VsmStackFrame::_shallow_copy(gc::IAlloc * mm) const
{
Cpof cpof(mm, this);
size_t n = this->size();
VsmStackFrame * copy = new (cpof) VsmStackFrame(cpof.mm_, parent_, n, cont_);
void * v_dest = copy->slot_v_.v_;
if (slot_v_.v_) {
::memcpy(v_dest, slot_v_.v_, slot_array_size(n));
}
#ifdef OBSOLETE
for (size_t i = 0, n = n_slot_; i < n; ++i) {
copy->v_[i] = v_[i];
}
#endif
return copy;
}
std::size_t
VsmStackFrame::_forward_children(gc::IAlloc * gc)
{
Object::_forward_inplace(parent_, gc);
for (std::size_t i = 0, n = slot_v_.size(); i < n; ++i) {
Object::_forward_inplace((*this)[i], gc);
}
return _shallow_size();
}
void
VsmStackFrame::reflect_self()
{
StructReflector<VsmStackFrame> sr;
if (sr.is_incomplete() ) {
/* reflect CVector<gp<Object>>.
* duplicates similar code in LocalEnv::reflect_self()
*/
using VectorType = CVector<gp<Object>>;
/* custom reflection for array of Object pointers.
* Can use StlVectorTdx here, treating CVector<T> as a vector
* via .size() and .operator[] members
*/
std::unique_ptr<TypeDescrExtra> tdx1
= std::make_unique<StlVectorTdx<VectorType>>();
TypeDescrW td1
= EstablishTypeDescr::establish<VectorType>();
td1->assign_tdextra(Reflect::get_final_invoker<VectorType>(),
std::move(tdx1));
REFLECT_MEMBER(sr, parent);
REFLECT_MEMBER(sr, slot_v);
}
}
} /*namespace scm*/
} /*namespace xo*/
/* end VsmStackFrame.cpp */