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

@ -0,0 +1,50 @@
/** @file CVector.hpp
*
* @author Roland Conybeare, Nov 2025
**/
#pragma once
#include "xo/alloc/IAlloc.hpp"
#include <cstdint>
namespace xo {
namespace scm {
/** gc-only vector.
* Used in both LocalEnv and VsmStackFrame
**/
template <typename ElementType>
class CVector {
public:
using value_type = ElementType;
public:
CVector(gc::IAlloc * mm, std::size_t n)
: n_{n}, v_{nullptr}
{
if (n_ > 0) {
std::byte * mem = mm->alloc(n_ * sizeof(ElementType));
this->v_ = new (mem) ElementType[n];
}
}
std::size_t size() const { return n_; }
ElementType operator[](std::size_t i) const { return v_[i]; }
ElementType & operator[](std::size_t i) { return v_[i]; }
friend class LocalEnv;
friend class VsmStackFrame;
private:
/** number of elements in @ref v_ **/
std::size_t n_ = 0;
/** contiguous array of pointers **/
ElementType * v_ = nullptr;
};
} /*namespace scm*/
} /*namespace xo*/
/* end CVector.hpp */

View file

@ -0,0 +1,43 @@
/** @file ExpressionBoxed.hpp
*
* @author Roland Conybeare, Nov 2025
**/
#pragma once
#include "xo/alloc/Object.hpp"
#include "xo/expression/Expression.hpp"
namespace xo {
namespace scm {
/** @class ExpressionBoxed
* @brief xo::scm::Expression, adapted to xo::Object interface
**/
class ExpressionBoxed : public Object {
public:
explicit ExpressionBoxed(bp<Expression> c);
/** create boxed version of @p c, using allocator @p mm **/
static gp<ExpressionBoxed> make(gc::IAlloc * mm,
bp<Expression> c);
const rp<Expression> & contents() const { return contents_; }
// inherited from Object
virtual TaggedPtr self_tp() const final override;
virtual void display(std::ostream & os) const final override;
virtual std::size_t _shallow_size() const final override;
virtual Object * _shallow_copy(gc::IAlloc * mm) const final override;
virtual std::size_t _forward_children(gc::IAlloc * /*gc*/) final override;
private:
/** reference-counted Expression pointer
*
* NOTE correctness requires finalization support in xo::gc::GC
**/
rp<Expression> contents_;
};
} /*namespace scm*/
} /*namespace xo*/
/* end ExpressionBoxed.hpp */

View file

@ -35,7 +35,7 @@ namespace xo {
virtual void display(std::ostream & os) const final override;
virtual std::size_t _shallow_size() const final override;
virtual Object * _shallow_copy(gc::IAlloc * mm) const final override;
virtual std::size_t _forward_children(gc::IAlloc * /*gc*/) final override;
virtual std::size_t _forward_children(gc::IAlloc * mm) final override;
private:
GlobalEnv(const GlobalEnv & x);

View file

@ -1,6 +1,7 @@
/** @file LocalEnv.hpp **/
#include "Env.hpp"
#include "CVector.hpp"
#include "xo/alloc/IAlloc.hpp"
#include "xo/expression/LocalSymtab.hpp"
#include <cstddef>
@ -8,36 +9,6 @@
namespace xo {
namespace scm {
/** gc-only vector
**/
template <typename ElementType>
class CVector {
public:
using value_type = ElementType;
public:
CVector(gc::IAlloc * mm, std::size_t n)
: n_{n}, v_{nullptr}
{
if (n_ > 0) {
std::byte * mem = mm->alloc(n_ * sizeof(ElementType));
this->v_ = new (mem) ElementType[n];
}
}
std::size_t size() const { return n_; }
ElementType operator[](std::size_t i) const { return v_[i]; }
ElementType & operator[](std::size_t i) { return v_[i]; }
friend class LocalEnv;
private:
/** number of elements in @ref v_ **/
std::size_t n_ = 0;
/** contiguous array of pointers **/
ElementType * v_ = nullptr;
};
/** @class LocalEnv
* @brief Represent a single runtime stack frame for a Schematika function
*

View file

@ -30,6 +30,9 @@ namespace xo {
/** garbage collector configuration **/
gc::Config gc_config_;
/** control schematika vsm logging **/
log_level vsm_log_level_;
};
using IAlloc = xo::gc::IAlloc;

View file

@ -3,6 +3,7 @@
#pragma once
#include "VsmInstr.hpp"
#include "VsmStackFrame.hpp"
#include "SchematikaError.hpp"
#include "Env.hpp"
#include "xo/expression/Expression.hpp"
@ -14,7 +15,8 @@ namespace xo {
/** @brief state that may be shared across VirtualSchematikaMachine instances **/
struct VirtualSchematikaMachineFlyweight {
explicit VirtualSchematikaMachineFlyweight(gc::IAlloc * mm,
gp<Env> env);
gp<Env> env,
log_level log_level);
/** memory allocator for interpreter operation. **/
gc::IAlloc * object_mm_ = nullptr;
@ -22,6 +24,8 @@ namespace xo {
gp<Env> toplevel_env_;
/** convert TaggedPtr->Object **/
xo::obj::ObjectConverter object_converter_;
/** control logging level. higher values -> more logging **/
log_level log_level_;
};
/** @class VirtualSchematikaMachine
@ -33,7 +37,7 @@ namespace xo {
using IAlloc = xo::gc::IAlloc;
public:
VirtualSchematikaMachine(IAlloc * mm, gp<Env> toplevel_env);
VirtualSchematikaMachine(IAlloc * mm, gp<Env> toplevel_env, log_level log_level);
~VirtualSchematikaMachine();
/** evaluate expression @p expr.
@ -64,12 +68,25 @@ namespace xo {
/** execute vsm instruction in program counter.
* Note: may possibly be able to replace with just opcode
*
* Registers:
* - expr_ input, caller saves
* - env_ input, caller saves
* - cont_ input, caller saves
* - value_ output
* - error_ output
**/
void execute_one();
/** interpret literal constant expression **/
void eval_constant_op();
/** interpret define expression **/
void eval_define_op();
/** continue after establishing value fo rhs of define exprsssion **/
void do_defexpr_assign_op();
/** goto error state with message @p err **/
void report_error(const std::string & err);
@ -82,19 +99,38 @@ namespace xo {
**/
const VsmInstr * pc_ = nullptr;
/** expression **/
/** register to hold Schematika expression to drive @ref execute_one.
*
* caller saves!
**/
rp<Expression> expr_;
/** holds bindings for all schematika variables **/
/** holds bindings for all schematika variables, to drive @ref execute_one.
* execute_one will not save this
*
* caller saves!
**/
gp<Env> env_;
/** non-error result value from eval() / apply() **/
/** vsm stack. callee saves!
**/
gp<VsmStackFrame> stack_;
/** non-error result value from eval() / apply()
*
* output register: caller must save
**/
gp<Object> value_;
/** error result value from eval() / apply() **/
/** error result value from eval() / apply()
*
* output regisetr: caller must save
**/
SchematikaError error_;
/** continuation
* (Perhaps replace with VsmInstr::Opcode ?)
*
* input register: callee saves!
**/
const VsmInstr * cont_ = nullptr;

View file

@ -25,6 +25,15 @@ namespace xo {
**/
eval,
/** assign to variable + continue
*
* stack: frame with:
* [0] lhs : variable to assign
* [1] cont : continuation after assignment
* ... maybe other vsm state that must be saved
**/
defexpr_assign,
N_Opcode
};

View file

@ -0,0 +1,75 @@
/** @file VsmStackFrame.hpp
*
* @author Roland Conybeare, Nov 2025
**/
#pragma once
#include "VsmInstr.hpp"
#include "CVector.hpp"
#include "xo/alloc/Object.hpp"
namespace xo {
namespace scm {
/** @class VsmStackFrame
* @brief Virtual Schematika Machine stack frame
*
* Intending to use the "cheney on the MTA" strategy,
* i.e. allocate frames using GC's bump allocator.
*
* Parallels LocalEnv, but VSM implementation isn't reflected
**/
class VsmStackFrame : public Object {
public:
VsmStackFrame(gc::IAlloc * mm, gp<VsmStackFrame> p, std::size_t n, const VsmInstr * cont);
#ifdef NOT_YET
/** create frame using allocator @p mm,
* with parent @p p and exactly @p n_slot object pointers.
**/
static gp<VsmStackFrame> make(gc::IAlloc * mm,
gp<VsmStackFrame> p,
const VsmInstr * cont,
std::size_t n_slot);
#endif
/** create new stack frame using allocator @p mm,
* with parent frame @p p; new frame contains values @p s0, @p s1
**/
static gp<VsmStackFrame> push1(gc::IAlloc * mm,
gp<VsmStackFrame> p,
gp<Object> s0,
const VsmInstr * cont);
/** reflect VsmStackFrame object representation **/
static void reflect_self();
gp<VsmStackFrame> parent() const { return parent_; }
std::size_t size() const { return slot_v_.size(); }
const VsmInstr * continuation() const { return cont_; }
gp<Object> operator[](std::size_t i) const { return slot_v_[i]; }
gp<Object> & operator[](std::size_t i) { return slot_v_[i]; }
// inherited from Object..
virtual TaggedPtr self_tp() const final override;
virtual void display(std::ostream & os) const final override;
virtual std::size_t _shallow_size() const final override;
virtual Object * _shallow_copy(gc::IAlloc *) const final override;
virtual std::size_t _forward_children(gc::IAlloc *) final override;
private:
/** parent stack frame **/
gp<VsmStackFrame> parent_;
/** stored state **/
CVector<gp<Object>> slot_v_;
/** proceed to this continuation when popping this frame **/
const VsmInstr * cont_ = nullptr;
};
} /*namespace scm*/
} /*namespace xo*/
/* end VsmStackFrame.hpp */