xo-interpreter2: ifelse expressions working + utest

This commit is contained in:
Roland Conybeare 2026-02-14 11:15:38 -05:00
commit e7e9d226dd
18 changed files with 508 additions and 7 deletions

View file

@ -103,6 +103,32 @@ xo_add_genfacetimpl(
# ----------------------------------------------------------------
# note: manual target; generated code committed to git
xo_add_genfacetimpl(
TARGET xo-interpreter2-facetimpl-gcobject-vsmifelsecontframe
FACET_PKG xo_gc
FACET GCObject
REPR VsmIfElseContFrame
INPUT idl/IGCObject_DVsmIfElseContFrame.json5
OUTPUT_HPP_DIR include/xo/interpreter2
OUTPUT_IMPL_SUBDIR ifelse
OUTPUT_CPP_DIR src/interpreter2
)
# note: manual target; generated code committed to git
xo_add_genfacetimpl(
TARGET xo-interpreter2-facetimpl-printable-vsmifelsecontframe
FACET_PKG xo_printable2
FACET Printable
REPR VsmIfElseContFrame
INPUT idl/IPrintable_DVsmIfElseContFrame.json5
OUTPUT_HPP_DIR include/xo/interpreter2
OUTPUT_IMPL_SUBDIR ifelse
OUTPUT_CPP_DIR src/interpreter2
)
# ----------------------------------------------------------------
# note: manual target; generated code committed to git
xo_add_genfacetimpl(
TARGET xo-interpreter2-facetimpl-gcobject-vsmseqcontframe

View file

@ -0,0 +1,15 @@
{
mode: "implementation",
includes: [
"<xo/gc/GCObject.hpp>",
"<xo/alloc2/Allocator.hpp>"
],
local_types: [ ],
namespace1: "xo",
namespace2: "scm",
facet_idl: "idl/GCObject.json5",
brief: "provide AGCObject interface for DVsmIfElseContFrame",
using_doxygen: true,
repr: "DVsmIfElseContFrame",
doc: [ "implement AGCObject for DVsmIfElseContFrame" ],
}

View file

@ -0,0 +1,13 @@
{
mode: "implementation",
includes: [ "<xo/printable2/Printable.hpp>",
"<xo/printable2/detail/IPrintable_Xfer.hpp>" ],
local_types: [ ],
namespace1: "xo",
namespace2: "scm",
facet_idl: "idl/Printable.json5",
brief: "provide APrintable interface for DVsmIfElseContFrame",
using_doxygen: true,
repr: "DVsmIfElseContFrame",
doc: [ "implement APrintable for DVsmIfElseContFrame" ],
}

View file

@ -0,0 +1,82 @@
/** @file DVsmIfElseContFrame.hpp
*
* @author Roland Conybeare, Feb 2026
**/
#pragma once
#include "VsmInstr.hpp"
#include <xo/expression2/IfElseExpr.hpp>
#include <xo/gc/GCObject.hpp>
namespace xo {
namespace scm {
/** @brief saved VSM state during evaluation of a SequenceExpr
**/
class DVsmIfElseContFrame {
public:
using ACollector = xo::mm::ACollector;
using AAllocator = xo::mm::AAllocator;
using AGCObject = xo::mm::AGCObject;
using ppindentinfo = xo::print::ppindentinfo;
public:
/** @defgroup scm-vsmevalsequenceframe-ctors constructors **/
///@{
DVsmIfElseContFrame(obj<AGCObject> parent,
VsmInstr cont,
DIfElseExpr * ifelse_expr);
/** create instance using memory from allocator @p mm **/
static DVsmIfElseContFrame * make(obj<AAllocator> mm,
obj<AGCObject> parent,
VsmInstr cont,
DIfElseExpr * ifelse_expr);
///@}
/** @defgroup scm-vsmevalsequenceframe-access-methods access methods **/
///@{
obj<AGCObject> parent() const noexcept { return parent_; }
VsmInstr cont() const noexcept { return cont_; }
DIfElseExpr * ifelse_expr() const noexcept { return ifelse_expr_; }
///@}
/** @defgroup scm-vsmevalsequenceframe-general-methods general methods **/
///@{
///@}
/** @defgroup scm-vsmevalsequenceframe-gcobject-facet gcobject facet **/
///@{
std::size_t shallow_size() const noexcept;
DVsmIfElseContFrame * shallow_copy(obj<AAllocator> mm) const noexcept;
std::size_t forward_children(obj<ACollector> gc) noexcept;
///@}
/** @defgrouop scm-vsmseqcontframe-printable-facet printable facet **/
///@{
bool pretty(const ppindentinfo & ppii) const noexcept;
///@}
private:
/** @defgroup scm-vsmevalsequenceframe-members member variables **/
///@{
/** saved VSM stack; restore when this frame consumed **/
obj<AGCObject> parent_;
/** saved continuation; restore when this frame consumed **/
VsmInstr cont_;
/** saved expr. evaluate elements of this sequence in order **/
DIfElseExpr * ifelse_expr_ = nullptr;
///@}
};
} /*namespace scm*/
} /*namespace xo*/
/* end DVsmIfElseContFrame.hpp */

View file

@ -187,6 +187,11 @@ namespace xo {
**/
void _do_apply_cont_op();
/** proceed with if- or else- branch of an if-else expression
* after evaluating test condition
**/
void _do_ifelse_cont_op();
/** loop continuation after evaluating element of a SequenceExpr **/
void _do_seq_cont_op();

View file

@ -0,0 +1,12 @@
/** @file VsmIfElseContFrame.hpp
*
* @author Roland Conybeare, Feb 2026
**/
#pragma once
#include "DVsmIfElseContFrame.hpp"
#include "ifelse/IGCObject_DVsmIfElseContFrame.hpp"
#include "ifelse/IPrintable_DVsmIfElseContFrame.hpp"
/* end VsmIfElseContFrame.hpp */

View file

@ -13,14 +13,21 @@ namespace xo {
public:
explicit VsmInstr(vsm_opcode oc) : opcode_{oc} {}
// instructions
static VsmInstr c_halt;
static VsmInstr c_eval;
static VsmInstr c_apply;
static VsmInstr c_evalargs;
/** proceed to continuation after an ApplyExpr **/
/** restore VSM state for continuation of an apply expression **/
static VsmInstr c_apply_cont;
/** proceed to branch of if-else expression after evaluating
* test condition
**/
static VsmInstr c_ifelse_cont;
/** loop to evaluate members of a SequenceExpr **/
static VsmInstr c_seq_cont;

View file

@ -28,11 +28,14 @@ namespace xo {
**/
evalargs,
/** Coda to restore vsm registers (local_env, stack, cont)
/** continuation to restore vsm registers (local_env, stack, cont)
* after invoking a closure
**/
apply_cont,
/** continuation to act on a branch **/
ifelse_cont,
/** Loop over elements of a SequenceExpr **/
seq_cont,
@ -55,3 +58,4 @@ namespace xo {
} /*namespace xo*/
/* end VsmOpcode.hpp */

View file

@ -0,0 +1,67 @@
/** @file IGCObject_DVsmIfElseContFrame.hpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [xo-facet/codegen/genfacet]
* arguments:
* --input [idl/IGCObject_DVsmIfElseContFrame.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [iface_facet_repr.hpp.j2]
* 3. idl for facet methods
* [idl/IGCObject_DVsmIfElseContFrame.json5]
**/
#pragma once
#include "GCObject.hpp"
#include <xo/gc/GCObject.hpp>
#include <xo/alloc2/Allocator.hpp>
#include "DVsmIfElseContFrame.hpp"
namespace xo { namespace scm { class IGCObject_DVsmIfElseContFrame; } }
namespace xo {
namespace facet {
template <>
struct FacetImplementation<xo::mm::AGCObject,
xo::scm::DVsmIfElseContFrame>
{
using ImplType = xo::mm::IGCObject_Xfer
<xo::scm::DVsmIfElseContFrame,
xo::scm::IGCObject_DVsmIfElseContFrame>;
};
}
}
namespace xo {
namespace scm {
/** @class IGCObject_DVsmIfElseContFrame
**/
class IGCObject_DVsmIfElseContFrame {
public:
/** @defgroup scm-gcobject-dvsmifelsecontframe-type-traits **/
///@{
using size_type = xo::mm::AGCObject::size_type;
using AAllocator = xo::mm::AGCObject::AAllocator;
using ACollector = xo::mm::AGCObject::ACollector;
using Copaque = xo::mm::AGCObject::Copaque;
using Opaque = xo::mm::AGCObject::Opaque;
///@}
/** @defgroup scm-gcobject-dvsmifelsecontframe-methods **/
///@{
// const methods
/** memory consumption for this instance **/
static size_type shallow_size(const DVsmIfElseContFrame & self) noexcept;
/** copy instance using allocator **/
static Opaque shallow_copy(const DVsmIfElseContFrame & self, obj<AAllocator> mm) noexcept;
// non-const methods
/** during GC: forward immdiate children **/
static size_type forward_children(DVsmIfElseContFrame & self, obj<ACollector> gc) noexcept;
///@}
};
} /*namespace scm*/
} /*namespace xo*/
/* end */

View file

@ -0,0 +1,62 @@
/** @file IPrintable_DVsmIfElseContFrame.hpp
*
* Generated automagically from ingredients:
* 1. code generator:
* [xo-facet/codegen/genfacet]
* arguments:
* --input [idl/IPrintable_DVsmIfElseContFrame.json5]
* 2. jinja2 template for abstract facet .hpp file:
* [iface_facet_repr.hpp.j2]
* 3. idl for facet methods
* [idl/IPrintable_DVsmIfElseContFrame.json5]
**/
#pragma once
#include "Printable.hpp"
#include <xo/printable2/Printable.hpp>
#include <xo/printable2/detail/IPrintable_Xfer.hpp>
#include "DVsmIfElseContFrame.hpp"
namespace xo { namespace scm { class IPrintable_DVsmIfElseContFrame; } }
namespace xo {
namespace facet {
template <>
struct FacetImplementation<xo::print::APrintable,
xo::scm::DVsmIfElseContFrame>
{
using ImplType = xo::print::IPrintable_Xfer
<xo::scm::DVsmIfElseContFrame,
xo::scm::IPrintable_DVsmIfElseContFrame>;
};
}
}
namespace xo {
namespace scm {
/** @class IPrintable_DVsmIfElseContFrame
**/
class IPrintable_DVsmIfElseContFrame {
public:
/** @defgroup scm-printable-dvsmifelsecontframe-type-traits **/
///@{
using ppindentinfo = xo::print::APrintable::ppindentinfo;
using Copaque = xo::print::APrintable::Copaque;
using Opaque = xo::print::APrintable::Opaque;
///@}
/** @defgroup scm-printable-dvsmifelsecontframe-methods **/
///@{
// const methods
/** Pretty-printing support for this object.
See [xo-indentlog/xo/indentlog/pretty.hpp] **/
static bool pretty(const DVsmIfElseContFrame & self, const ppindentinfo & ppii);
// non-const methods
///@}
};
} /*namespace scm*/
} /*namespace xo*/
/* end */

View file

@ -20,6 +20,10 @@ set(SELF_SRCS
IGCObject_DVsmApplyClosureFrame.cpp
IPrintable_DVsmApplyClosureFrame.cpp
DVsmIfElseContFrame.cpp
IGCObject_DVsmIfElseContFrame.cpp
IPrintable_DVsmIfElseContFrame.cpp
DVsmSeqContFrame.cpp
IGCObject_DVsmSeqContFrame.cpp
IPrintable_DVsmSeqContFrame.cpp

View file

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

View file

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

View file

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

View file

@ -7,6 +7,7 @@
#include "VsmApplyFrame.hpp"
#include "VsmEvalArgsFrame.hpp"
#include "VsmApplyClosureFrame.hpp"
#include "VsmIfElseContFrame.hpp"
#include "VsmSeqContFrame.hpp"
#include "VsmRcx.hpp"
#include "Closure.hpp"
@ -14,6 +15,7 @@
#include <xo/expression2/LambdaExpr.hpp>
#include <xo/expression2/Constant.hpp>
#include <xo/expression2/SequenceExpr.hpp>
#include <xo/object2/Boolean.hpp>
#include <xo/procedure2/RuntimeContext.hpp>
//#include <xo/procedure2/SimpleRcx.hpp>
#include <xo/gc/DX1Collector.hpp>
@ -190,6 +192,9 @@ namespace xo {
case vsm_opcode::apply_cont:
_do_apply_cont_op();
break;
case vsm_opcode::ifelse_cont:
_do_ifelse_cont_op();
break;
case vsm_opcode::seq_cont:
_do_seq_cont_op();
break;
@ -388,14 +393,26 @@ namespace xo {
void
VirtualSchematikaMachine::_do_eval_if_else_op()
{
// not implemented
assert(false);
// control:
// self -> eval(test) -> ifelse_cont -> eval(when_true)
// -> eval(when_false)
auto ifelse_expr = obj<AExpression,DIfElseExpr>::from(expr_);
obj<AGCObject,DVsmIfElseContFrame> ifelse_frame
(DVsmIfElseContFrame::make(mm_.to_op(),
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;
}
void
VirtualSchematikaMachine::_do_eval_sequence_op()
{
// assuming bump allocator:
// stack:
//
// VsmEvalSequence
// v
@ -640,6 +657,50 @@ namespace xo {
this->pc_ = frame->cont();
}
void
VirtualSchematikaMachine::_do_ifelse_cont_op()
{
// pre: result of evaluating test condition in value_ register
auto frame = obj<AGCObject,DVsmIfElseContFrame>::from(stack_);
assert(frame);
assert(value_.is_value());
auto flag = obj<AGCObject,DBoolean>::from(*value_.value());
if (flag.data()) {
obj<AExpression> next_expr;
{
if (flag->value()) {
// proceed with if-branch
next_expr = frame->ifelse_expr()->when_true();
} else {
// proceed with else-branch
next_expr = frame->ifelse_expr()->when_false();
}
}
this->stack_ = frame->parent();
this->cont_ = frame->cont();
this->expr_ = next_expr;
this->pc_ = VsmInstr::c_eval;
} else {
auto error = DRuntimeError::make(mm_.to_op(),
"_do_ifelse_cont_op",
"expected boolean for test condition");
this->value_ = VsmResult(error);
// for now: halt VSM execution
// TODO: some combination of
// 1. emit stack trace
// 2. go to debugger
// 3. have every vsm instruction check inputs for errors
this->pc_ = VsmInstr::c_halt;
}
}
void
VirtualSchematikaMachine::_do_seq_cont_op()
{

View file

@ -16,6 +16,7 @@ namespace xo {
case vsm_opcode::apply: return "apply";
case vsm_opcode::evalargs: return "evalargs";
case vsm_opcode::apply_cont: return "apply_cont";
case vsm_opcode::ifelse_cont: return "ifelse_cont";
case vsm_opcode::seq_cont: return "seq_cont";
case vsm_opcode::N:
break;
@ -39,6 +40,9 @@ namespace xo {
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

@ -9,6 +9,7 @@
#include "VsmApplyFrame.hpp"
#include "VsmEvalArgsFrame.hpp"
#include "VsmApplyClosureFrame.hpp"
#include "VsmIfElseContFrame.hpp"
#include "VsmSeqContFrame.hpp"
#include "Primitive_gco_2_gco_gco.hpp"
#include "Closure.hpp"
@ -38,6 +39,7 @@ namespace xo {
// +- VsmApplyFrame
// +- VsmEvalArgsFrame
// +- VsmApplyClosureFrame
// +- VsmIfElseContFrame
// \- VsmSeqContFrame
FacetRegistry::register_impl<AGCObject, DVsmApplyFrame>();
@ -49,6 +51,9 @@ namespace xo {
FacetRegistry::register_impl<AGCObject, DVsmApplyClosureFrame>();
FacetRegistry::register_impl<APrintable, DVsmApplyClosureFrame>();
FacetRegistry::register_impl<AGCObject, DVsmIfElseContFrame>();
FacetRegistry::register_impl<APrintable, DVsmIfElseContFrame>();
FacetRegistry::register_impl<AGCObject, DVsmSeqContFrame>();
FacetRegistry::register_impl<APrintable, DVsmSeqContFrame>();

View file

@ -30,6 +30,7 @@ namespace xo {
using xo::scm::VsmConfig;
using xo::scm::VsmResultExt;
using xo::scm::DClosure;
using xo::scm::DString;
using xo::scm::DFloat;
using xo::scm::DBoolean;
using xo::scm::DInteger;
@ -258,10 +259,10 @@ namespace xo {
log && log(xtag("res.tseq", res.value()->_typeseq()));
auto x = obj<AGCObject,DBoolean>::from(*res.value());
auto x = obj<AGCObject,DString>::from(*res.value());
REQUIRE(x);
REQUIRE(x.data()->value() == true);
REQUIRE(strcmp(x.data()->chars(), "equal") == 0);
REQUIRE(res.remaining_.size() == 1);
REQUIRE(*res.remaining_.lo() == '\n');