xo-interpreter2 stack: refactor + bugfix operator expr

This commit is contained in:
Roland Conybeare 2026-03-12 20:26:08 -05:00
commit 6c7216ed7d
7 changed files with 195 additions and 200 deletions

View file

@ -6,8 +6,8 @@ include(CMakeFindDependencyMacro)
# must coordinate with xo_dependency() calls # must coordinate with xo_dependency() calls
# in CMakeLists.txt # in CMakeLists.txt
# #
find_dependency(xo_type)
find_dependency(xo_object2) find_dependency(xo_object2)
#find_dependency(xo_gc)
find_dependency(subsys) find_dependency(subsys)
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")

View file

@ -55,6 +55,18 @@ namespace xo {
} }
/** @brief Schematika primitive with fixed number of arguments /** @brief Schematika primitive with fixed number of arguments
*
* Distinction between type-constructor (@ref type_) and
* type-description (@ref fn_td_): type-constructor is narrower;
* it may lift into schematika type system some information about function
* behavior. For example
* @pre
* cons :: (T x list<T>) -> list<T>
* @endpre
* but implementation has signature:
* @pre
* (obj<AGCobject> x obj<AGCObject,DList>) -> obj<AGCObject,DList>
* @endpre
**/ **/
template <typename Fn> template <typename Fn>
class Primitive { class Primitive {
@ -70,13 +82,28 @@ namespace xo {
using ppindentinfo = xo::print::ppindentinfo; using ppindentinfo = xo::print::ppindentinfo;
public: public:
Primitive(std::string_view name, Fn fn) : name_{name}, /** @defgroup scm-primitive-ctors constructors **/
fn_td_{Reflect::require<Fn>()}, ///@{
fn_{fn} {}
Primitive(std::string_view name, Fn fn)
: name_{name},
fn_td_{Reflect::require<Fn>()},
fn_{fn} {}
static Primitive * _make(obj<AAllocator> mm, std::string_view name, Fn fn) {
void * mem = mm.alloc_for<Primitive>();
return new (mem) Primitive(name, fn);
}
///@}
/** @defgroup scm-primitive-methods general methods **/
///@{
TypeDescr fn_td() const noexcept { return fn_td_; } TypeDescr fn_td() const noexcept { return fn_td_; }
std::string_view name() const noexcept { return name_; } std::string_view name() const noexcept { return name_; }
static constexpr std::int32_t n_args() noexcept { return Traits::n_args; } static constexpr std::int32_t n_args() noexcept { return Traits::n_args; }
bool is_nary() const noexcept { return false; } bool is_nary() const noexcept { return false; }
@ -86,6 +113,7 @@ namespace xo {
std::make_index_sequence<Traits::n_args>{}); std::make_index_sequence<Traits::n_args>{});
} }
///@}
/** @defgroup scm-primitive-printable-facet **/ /** @defgroup scm-primitive-printable-facet **/
///@{ ///@{
@ -166,8 +194,12 @@ namespace xo {
template <typename Fn> template <typename Fn>
std::size_t std::size_t
Primitive<Fn>::forward_children(obj<ACollector>) noexcept { Primitive<Fn>::forward_children(obj<ACollector> gc) noexcept {
// Primitive holds no GC refs (just string_view + function pointer) {
auto e = type_.to_facet<AGCObject>(); // FacetRegistry dep
gc.forward_inplace(e.iface(), (void **)&(type_.data_));
}
return this->shallow_size(); return this->shallow_size();
} }

View file

@ -0,0 +1,89 @@
/** @file PrimitiveRegistry.hpp
*
* @author Roland Conybeare, Mar 2026
**/
#pragma once
#include "Procedure.hpp"
#include <xo/alloc2/Allocator.hpp>
#include <xo/reflect/TypeDescr.hpp>
#include <functional>
namespace xo {
namespace scm {
/** partition primitives based on scope.
* Each primitive associates with exactly one flag value.
* Flags also operate as bitmasks when calling
* @ref PrimitiveRegistry::install_primitives()
**/
enum class InstallFlags {
f_none = 0x0,
/** mandatory primitives, necessary for operator support,
* e.g. _add needed to implement infix +
**/
f_essential = 0x1,
f_generalpurpose = 0x2,
/** all primitives **/
f_all = f_essential | f_generalpurpose,
};
inline InstallFlags operator&(InstallFlags x, InstallFlags y) {
return InstallFlags(static_cast<uint64_t>(x) & static_cast<uint64_t>(y));
}
/** provided by VSM to receive created primitives.
* InstallSink(pm) adopts a primitive
**/
using InstallSink = std::function<bool (std::string_view name,
xo::reflect::TypeDescr fn_td,
obj<AProcedure> pm,
InstallFlags flags)>;
/** @class PrimitiveRegistry
*
* @brief Runtime registry for primitives
*
* Singleton to remember setup code for known primitives.
* Use to gather primitives for installation in global
* environment for a virtual schematika machine (VSM)
**/
class PrimitiveRegistry {
public:
using AAllocator = xo::mm::AAllocator;
/** provided by a subsystem that provides primitives.
* Allocates primitives using memory from mm, delivering them
* to InstallSink sink.
**/
using InstallSource = std::function<bool (obj<AAllocator> mm,
InstallSink sink,
InstallFlags flags)>;
public:
/** singleton instance **/
static PrimitiveRegistry & instance();
/** remember primitive-factory @p source_fn **/
void register_primitives(InstallSource source_fn);
/** create primitives using memory from @p mm,
* delivering each primitive to @p sink.
**/
bool install_primitives(obj<AAllocator> mm,
InstallSink sink,
InstallFlags flags);
private:
/** a set of factories that create primitives **/
std::vector<InstallSource> init_seq_v_;
};
} /*namespace scm*/
} /*namespace xo*/
/* end PrimitiveRegistry.hpp */

View file

@ -0,0 +1,18 @@
/** @file procedure2_register_primitives.hpp
*
* @author Roland Conybeare, Mar 2026
**/
#pragma once
#include "PrimitiveRegistry.hpp"
#include <xo/alloc2/Collector.hpp>
namespace xo {
namespace scm {
/** Register gc-aware (AGCObject,DRepr) combinations with garbage collector @p gc **/
bool procedure2_register_primitives(obj<xo::mm::AAllocator> gc, InstallSink sink);
}
}
/* end procedure2_register_primitives.hpp */

View file

@ -34,10 +34,9 @@ xo_install_include_tree3(include/xo/procedure2)
# NOTE: dependency set here must be kept consistent with # NOTE: dependency set here must be kept consistent with
# xo-procedure2/cmake/xo_procedure2Config.cmake.in # xo-procedure2/cmake/xo_procedure2Config.cmake.in
xo_dependency(${SELF_LIB} xo_type)
xo_dependency(${SELF_LIB} xo_object2) xo_dependency(${SELF_LIB} xo_object2)
#xo_dependency(${SELF_LIB} xo_gc)
xo_dependency(${SELF_LIB} subsys) xo_dependency(${SELF_LIB} subsys)
#xo_dependency(${SELF_LIB} xo_indentlog)
xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets)

View file

@ -0,0 +1,49 @@
/** @file PrimitiveRegistry.cpp
*
* @author Roland Conybeare, Mar 2026
**/
#include "PrimitiveRegistry.hpp"
#include <xo/indentlog/scope.hpp>
namespace xo {
namespace scm {
PrimitiveRegistry &
PrimitiveRegistry::instance()
{
static PrimitiveRegistry s_instance;
return s_instance;
}
void
PrimitiveRegistry::register_primitives(InstallSource factory)
{
scope log(XO_DEBUG(true));
init_seq_v_.push_back(factory);
}
bool
PrimitiveRegistry::install_primitives(obj<AAllocator> mm,
InstallSink sink,
InstallFlags flags)
{
scope log(XO_DEBUG(true));
bool ok = true;
size_t i = 0;
size_t n = init_seq_v_.size();
log && log("run n init steps", xtag("n", n));
for (const auto & fn : init_seq_v_) {
log && log("do install fn (", i+1, "/", n, ")");
ok = ok & fn(mm, sink, flags);
}
return ok;
}
}
} /*namespace xo*/

View file

@ -37,187 +37,6 @@ namespace xo {
} }
#endif #endif
#ifdef OBSOLETE // see xo-numeric/xo/numeric/NumericPrimitives.cpp
obj<AGCObject>
mul_gco_gco(obj<ARuntimeContext> rcx,
obj<AGCObject> x_gco,
obj<AGCObject> y_gco)
{
using xo::reflect::typeseq;
obj<AAllocator> mm = rcx.allocator();
// PLACEHOLDER
// TODO:
// 1. move this to xo-numeric2/ when available
// 2. at that point will require polymorphic dispatch
// on argument representations, analogous to dispatch
// in FacetRegistry
typeseq x_tseq = x_gco._typeseq();
typeseq y_tseq = y_gco._typeseq();
// FOR NOW: just test runtime values
//
if (x_tseq == typeseq::id<DInteger>()) {
// i64 * ..
long x = GCObjectConversion<long>::from_gco(mm, x_gco);
if (y_tseq == typeseq::id<DInteger>()) {
// i64 * i64
long y = GCObjectConversion<long>::from_gco(mm, y_gco);
return DInteger::box<AGCObject>(mm, x * y);
} else if (y_tseq == typeseq::id<DFloat>()) {
// i64 * f64
double y = GCObjectConversion<double>::from_gco(mm, y_gco);
return DFloat::box<AGCObject>(mm, x * y);
}
} else if (x_tseq == typeseq::id<DFloat>()) {
if (y_tseq == typeseq::id<DInteger>()) {
// f64 * i64.
double x = GCObjectConversion<double>::from_gco(mm, x_gco);
long y = GCObjectConversion<long>::from_gco(mm, y_gco);
return DFloat::box<AGCObject>(mm, x * y);
} else if (y_tseq == typeseq::id<DFloat>()) {
// f64 * f64.
double x = GCObjectConversion<double>::from_gco(mm, x_gco);
double y = GCObjectConversion<double>::from_gco(mm, y_gco);
return DFloat::box<AGCObject>(mm, x * y);
}
}
// here: error
throw std::runtime_error(tostr("mul_gco_gco: unexpected argument types xt,yt",
xtag("x.tseq", x_tseq),
xtag("y.tseq", y_tseq)));
return obj<AGCObject>();
}
obj<AGCObject>
sub_gco_gco(obj<ARuntimeContext> rcx,
obj<AGCObject> x_gco,
obj<AGCObject> y_gco)
{
using xo::reflect::typeseq;
obj<AAllocator> mm = rcx.allocator();
// PLACEHOLDER
// TODO:
// 1. move this to xo-numeric2/ when available
// 2. at that point will require polymorphic dispatch
// on argument representations, analogous to dispatch
// in FacetRegistry
typeseq x_tseq = x_gco._typeseq();
typeseq y_tseq = y_gco._typeseq();
// FOR NOW: just test runtime values
//
if (x_tseq == typeseq::id<DInteger>()) {
// i64 * ..
long x = GCObjectConversion<long>::from_gco(mm, x_gco);
if (y_tseq == typeseq::id<DInteger>()) {
// i64 * i64
long y = GCObjectConversion<long>::from_gco(mm, y_gco);
return DInteger::box<AGCObject>(mm, x - y);
} else if (y_tseq == typeseq::id<DFloat>()) {
// i64 * f64
double y = GCObjectConversion<double>::from_gco(mm, y_gco);
return DFloat::box<AGCObject>(mm, x - y);
}
} else if (x_tseq == typeseq::id<DFloat>()) {
if (y_tseq == typeseq::id<DInteger>()) {
// f64 * i64.
double x = GCObjectConversion<double>::from_gco(mm, x_gco);
long y = GCObjectConversion<long>::from_gco(mm, y_gco);
return DFloat::box<AGCObject>(mm, x - y);
} else if (y_tseq == typeseq::id<DFloat>()) {
// f64 * f64.
double x = GCObjectConversion<double>::from_gco(mm, x_gco);
double y = GCObjectConversion<double>::from_gco(mm, y_gco);
return DFloat::box<AGCObject>(mm, x - y);
}
}
// here: error
throw std::runtime_error(tostr("sub_gco_gco: unexpected argument types xt,yt",
xtag("x.tseq", x_tseq),
xtag("y.tseq", y_tseq)));
return obj<AGCObject>();
}
obj<AGCObject>
equal_gco_gco(obj<ARuntimeContext> rcx,
obj<AGCObject> x_gco,
obj<AGCObject> y_gco)
{
using xo::reflect::typeseq;
obj<AAllocator> mm = rcx.allocator();
// PLACEHOLDER
// TODO
// 1. move this to xo-numeric2/ when available
// 2. at that point will require polymorphic dispatch on argument representations.
//
typeseq x_tseq = x_gco._typeseq();
typeseq y_tseq = y_gco._typeseq();
// FOR NOW: just test runtime values
//
if (x_tseq == typeseq::id<DInteger>()) {
// i64 * ..
long x = GCObjectConversion<long>::from_gco(mm, x_gco);
if (y_tseq == typeseq::id<DInteger>()) {
// i64 == i64
long y = GCObjectConversion<long>::from_gco(mm, y_gco);
return DBoolean::box<AGCObject>(mm, x == y);
} else if (y_tseq == typeseq::id<DFloat>()) {
// i64 == f64
double y = GCObjectConversion<double>::from_gco(mm, y_gco);
return DFloat::box<AGCObject>(mm, static_cast<double>(x) == y);
}
} else if (x_tseq == typeseq::id<DFloat>()) {
if (y_tseq == typeseq::id<DInteger>()) {
// f64 == i64.
double x = GCObjectConversion<double>::from_gco(mm, x_gco);
long y = GCObjectConversion<long>::from_gco(mm, y_gco);
return DFloat::box<AGCObject>(mm, x == static_cast<double>(y));
} else if (y_tseq == typeseq::id<DFloat>()) {
// f64 * f64.
double x = GCObjectConversion<double>::from_gco(mm, x_gco);
double y = GCObjectConversion<double>::from_gco(mm, y_gco);
return DFloat::box<AGCObject>(mm, x == y);
}
}
// here: error
throw std::runtime_error(tostr("mul_gco_gco: unexpected argument types xt,yt",
xtag("x.tseq", x_tseq),
xtag("y.tseq", y_tseq)));
return obj<AGCObject>();
}
#endif
#ifdef NOT_YET #ifdef NOT_YET
double double
mul_f64_f64(double x, double y) { mul_f64_f64(double x, double y) {
@ -255,17 +74,6 @@ namespace xo {
} }
#endif #endif
#ifdef OSOLETE
DPrimitive_gco_2_gco_gco
Primitives::s_mul_gco_gco_pm("_mul", &mul_gco_gco);
DPrimitive_gco_2_gco_gco
Primitives::s_sub_gco_gco_pm("_sub", &sub_gco_gco);
DPrimitive_gco_2_gco_gco
Primitives::s_equal_gco_gco_pm("_equal", &equal_gco_gco);
#endif
#ifdef NOT_YET #ifdef NOT_YET
Primitive_f64_1_f64 Primitive_f64_1_f64
Primitives::s_neg_f64_pm("_neg_d", Primitives::s_neg_f64_pm("_neg_d",