From 4488117ac34367a17879bd95430018295d8c8fe6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 26 Nov 2025 20:15:03 -0500 Subject: [PATCH] xo-interpreter: Object->TaggedPtr conversion (prep for primitives) --- xo-alloc/include/xo/alloc/Blob.hpp | 40 +++++++ xo-alloc/src/alloc/Blob.cpp | 57 ++++++++++ xo-alloc/src/alloc/CMakeLists.txt | 1 + .../xo/expression/FunctionExprInterface.hpp | 12 +- .../include/xo/expression/Lambda.hpp | 4 +- .../xo/expression/PrimitiveExprInterface.hpp | 6 +- .../xo/expression/ProcedureExprInterface.hpp | 33 ++++++ xo-expression/src/expression/Lambda.cpp | 2 +- xo-jit/include/xo/jit/type2llvm.hpp | 2 +- .../include/xo/object}/CVector.hpp | 0 xo-object/include/xo/object/List.hpp | 8 +- .../include/xo/object/ObjectConverter.hpp | 26 ++++- xo-object/include/xo/object/Primitive.hpp | 70 ++++++++++++ xo-object/include/xo/object/Procedure.hpp | 31 ++++++ xo-object/src/object/ObjectConverter.cpp | 103 +++++++++++++++++- xo-object/src/object/String.cpp | 2 +- 16 files changed, 371 insertions(+), 26 deletions(-) create mode 100644 xo-alloc/include/xo/alloc/Blob.hpp create mode 100644 xo-alloc/src/alloc/Blob.cpp create mode 100644 xo-expression/include/xo/expression/ProcedureExprInterface.hpp rename {xo-interpreter/include/xo/interpreter => xo-object/include/xo/object}/CVector.hpp (100%) create mode 100644 xo-object/include/xo/object/Primitive.hpp create mode 100644 xo-object/include/xo/object/Procedure.hpp diff --git a/xo-alloc/include/xo/alloc/Blob.hpp b/xo-alloc/include/xo/alloc/Blob.hpp new file mode 100644 index 00000000..9e3ae44a --- /dev/null +++ b/xo-alloc/include/xo/alloc/Blob.hpp @@ -0,0 +1,40 @@ +/** @file Blob.hpp + * + * @author Roland Conybeare, Nov 2025 + **/ + +#pragma once + +#include "Object.hpp" +#include "IAlloc.hpp" + +namespace xo { + /** Use to allocate opaque binary data, + * with object header. + * + * Not sure if we want to bother implementing reflection for this... + **/ + class Blob : public Object { + public: + Blob(std::size_t z) : z_{z} {}; + + static gp make(gc::IAlloc * mm, std::size_t z); + + std::size_t size() const { return z_; } + std::byte * data() { return data_; } + + 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 * gc) const final override; + virtual std::size_t _forward_children(gc::IAlloc * gc) final override; + + private: + std::size_t z_ = 0; + /** flexible array, with @ref z_ bytes **/ + std::byte data_[]; + }; +} + +/* end Blob.hpp */ diff --git a/xo-alloc/src/alloc/Blob.cpp b/xo-alloc/src/alloc/Blob.cpp new file mode 100644 index 00000000..97932844 --- /dev/null +++ b/xo-alloc/src/alloc/Blob.cpp @@ -0,0 +1,57 @@ +/** @file Blob.cpp + * + * @author Roland Conybeare, Nov 2025 + **/ + +#include "Blob.hpp" +#include "xo/reflect/Reflect.hpp" +#include "xo/alloc/IAlloc.hpp" + +namespace xo { + using xo::reflect::Reflect; + using xo::reflect::TaggedPtr; + + gp + Blob::make(gc::IAlloc * mm, std::size_t z) { + std::byte * mem = mm->alloc(sizeof(Blob) + z); + + return new (mem) Blob(z); + } + + TaggedPtr + Blob::self_tp() const + { + return Reflect::make_tp(const_cast(this)); + } + + void + Blob::display(std::ostream & os) const + { + os << ""; + } + + std::size_t + Blob::_shallow_size() const { + return sizeof(Blob) + z_; + } + + Object * + Blob::_shallow_copy(gc::IAlloc * mm) const { + Cpof cpof(mm, this); + std::byte * cp_mem = mm->alloc_gc_copy(sizeof(Blob) + z_, this); + + gp copy = new (cp_mem) Blob(z_); + + ::memcpy(copy->data(), data_, z_); + + return copy.get(); + } + + std::size_t + Blob::_forward_children(gc::IAlloc *) + { + return this->_shallow_size(); + } +} + +/* end Blob.cpp */ diff --git a/xo-alloc/src/alloc/CMakeLists.txt b/xo-alloc/src/alloc/CMakeLists.txt index 6bbc2f32..4a9d1db4 100644 --- a/xo-alloc/src/alloc/CMakeLists.txt +++ b/xo-alloc/src/alloc/CMakeLists.txt @@ -9,6 +9,7 @@ set(SELF_SRCS GcStatistics.cpp ObjectStatistics.cpp Object.cpp + Blob.cpp Forwarding1.cpp generation.cpp ) diff --git a/xo-expression/include/xo/expression/FunctionExprInterface.hpp b/xo-expression/include/xo/expression/FunctionExprInterface.hpp index 62756654..340d3f11 100644 --- a/xo-expression/include/xo/expression/FunctionExprInterface.hpp +++ b/xo-expression/include/xo/expression/FunctionExprInterface.hpp @@ -1,4 +1,4 @@ -/** @file FunctionInterface.hpp +/** @file ProcedureExprInterface.hpp * * Author: Roland Conybeare **/ @@ -10,14 +10,14 @@ namespace xo { namespace scm { - class FunctionExprInterface : public Expression { + class ProcedureExprInterface : public Expression { public: - FunctionExprInterface(exprtype extype, TypeDescr fn_type) + ProcedureExprInterface(exprtype extype, TypeDescr fn_type) : Expression(extype, fn_type) {} /** downcast from Expression **/ - static bp from(bp x) { - return bp::from(x); + static bp from(bp x) { + return bp::from(x); } virtual const std::string & name() const = 0; @@ -30,4 +30,4 @@ namespace xo { } /*namespace scm*/ } /*namespace xo*/ -/** end FunctionInterface.hpp **/ +/** end ProcedureExprInterface.hpp **/ diff --git a/xo-expression/include/xo/expression/Lambda.hpp b/xo-expression/include/xo/expression/Lambda.hpp index e4d24cd6..e5b3aeb1 100644 --- a/xo-expression/include/xo/expression/Lambda.hpp +++ b/xo-expression/include/xo/expression/Lambda.hpp @@ -6,7 +6,7 @@ #pragma once #include "Expression.hpp" -#include "FunctionExprInterface.hpp" +#include "ProcedureExprInterface.hpp" #include "Variable.hpp" #include "LocalSymtab.hpp" #include @@ -19,7 +19,7 @@ namespace xo { * @brief abstract syntax tree for a function definition * **/ - class Lambda : public FunctionExprInterface { + class Lambda : public ProcedureExprInterface { public: /** * @p name. Name for this lambda -- must be unique diff --git a/xo-expression/include/xo/expression/PrimitiveExprInterface.hpp b/xo-expression/include/xo/expression/PrimitiveExprInterface.hpp index 1011e5dc..9db7be14 100644 --- a/xo-expression/include/xo/expression/PrimitiveExprInterface.hpp +++ b/xo-expression/include/xo/expression/PrimitiveExprInterface.hpp @@ -5,19 +5,19 @@ #pragma once -#include "FunctionExprInterface.hpp" +#include "ProcedureExprInterface.hpp" #include "llvmintrinsic.hpp" #include namespace xo { namespace scm { - class PrimitiveExprInterface : public FunctionExprInterface { + class PrimitiveExprInterface : public ProcedureExprInterface { public: using void_function_type = void (*)(); public: explicit PrimitiveExprInterface(TypeDescr fn_type) - : FunctionExprInterface(exprtype::primitive, fn_type) {} + : ProcedureExprInterface(exprtype::primitive, fn_type) {} /** downcast from Expression **/ static bp from(bp x) { diff --git a/xo-expression/include/xo/expression/ProcedureExprInterface.hpp b/xo-expression/include/xo/expression/ProcedureExprInterface.hpp new file mode 100644 index 00000000..62acf21a --- /dev/null +++ b/xo-expression/include/xo/expression/ProcedureExprInterface.hpp @@ -0,0 +1,33 @@ +/** @file FunctionExprInterface.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "Expression.hpp" +//#include + +namespace xo { + namespace scm { + class ProcedureExprInterface : public Expression { + public: + ProcedureExprInterface(exprtype extype, TypeDescr fn_type) + : Expression(extype, fn_type) {} + + /** downcast from Expression **/ + static bp from(bp x) { + return bp::from(x); + } + + virtual const std::string & name() const = 0; + virtual int n_arg() const = 0; // { return this->value_td()->n_fn_arg(); } + virtual TypeDescr fn_retval() const = 0; // { return this->value_td()->fn_retval(); } + virtual TypeDescr fn_arg(uint32_t i) const = 0; // { return this->value_td()->fn_arg(i); } + + private: + }; /*FunctionInterface*/ + } /*namespace scm*/ +} /*namespace xo*/ + +/** end FunctionExprInterface.hpp **/ diff --git a/xo-expression/src/expression/Lambda.cpp b/xo-expression/src/expression/Lambda.cpp index d944b690..0196f52e 100644 --- a/xo-expression/src/expression/Lambda.cpp +++ b/xo-expression/src/expression/Lambda.cpp @@ -259,7 +259,7 @@ namespace xo { TypeDescr lambda_td, const rp & local_env, const rp & body) - : FunctionExprInterface(exprtype::lambda, lambda_td), + : ProcedureExprInterface(exprtype::lambda, lambda_td), name_{name}, body_{body}, local_env_{local_env} diff --git a/xo-jit/include/xo/jit/type2llvm.hpp b/xo-jit/include/xo/jit/type2llvm.hpp index 5308b082..d19ce492 100644 --- a/xo-jit/include/xo/jit/type2llvm.hpp +++ b/xo-jit/include/xo/jit/type2llvm.hpp @@ -20,7 +20,7 @@ namespace xo { **/ struct type2llvm { public: - using FunctionInterface = xo::scm::FunctionExprInterface; + using FunctionInterface = xo::scm::ProcedureExprInterface; using Lambda = xo::scm::Lambda; using TypeDescr = xo::reflect::TypeDescr; diff --git a/xo-interpreter/include/xo/interpreter/CVector.hpp b/xo-object/include/xo/object/CVector.hpp similarity index 100% rename from xo-interpreter/include/xo/interpreter/CVector.hpp rename to xo-object/include/xo/object/CVector.hpp diff --git a/xo-object/include/xo/object/List.hpp b/xo-object/include/xo/object/List.hpp index f3618f0a..787940c6 100644 --- a/xo-object/include/xo/object/List.hpp +++ b/xo-object/include/xo/object/List.hpp @@ -1,7 +1,7 @@ -/* @file List.hpp +/** @file List.hpp * - * author: Roland Conybeare, Aug 2025 - */ + * @author: Roland Conybeare, Aug 2025 + **/ #pragma once @@ -70,3 +70,5 @@ namespace xo { }; } } /*namespace xo*/ + +/* end List.hpp */ diff --git a/xo-object/include/xo/object/ObjectConverter.hpp b/xo-object/include/xo/object/ObjectConverter.hpp index 67fe3dc0..a4b337fd 100644 --- a/xo-object/include/xo/object/ObjectConverter.hpp +++ b/xo-object/include/xo/object/ObjectConverter.hpp @@ -18,10 +18,11 @@ namespace xo { struct Converter { using TaggedPtr = xo::reflect::TaggedPtr; using ConvertToObjectFn = gp (*)(gc::IAlloc *, const TaggedPtr &); + using ConvertFromObjectFn = TaggedPtr (*)(gc::IAlloc *, gp obj); public: Converter() = default; - explicit Converter(ConvertToObjectFn f) : cvt_to_object_{f} {} + explicit Converter(ConvertToObjectFn to, ConvertFromObjectFn from) : cvt_to_object_{to}, cvt_from_object_{from} {} /** convert tagged pointer @p tp to new object, * allocated via @p mm. @@ -30,6 +31,14 @@ namespace xo { * see @ref ObjectConverter **/ ConvertToObjectFn cvt_to_object_ = nullptr; + + /** convert object to tagged pointer @p, + * allocated via @p mm. + * + * Conversion will typically be for some specific type; + * see @ref ObjectConverter + **/ + ConvertFromObjectFn cvt_from_object_ = nullptr; }; /** @class ObjectConverter @@ -53,6 +62,7 @@ namespace xo { class ObjectConverter { public: using TaggedPtr = xo::reflect::TaggedPtr; + using TypeId = xo::reflect::TypeId; using IAlloc = xo::gc::IAlloc; /** sets up standard conversions **/ @@ -60,7 +70,7 @@ namespace xo { /** establish conversion: use @p fn to convert values of type @tparam T. **/ template - void establish_conversion(Converter::ConvertToObjectFn fn); + void establish_conversion(Converter::ConvertToObjectFn to, Converter::ConvertFromObjectFn from); /** convert tagged poitner @p tp to object. allocates memory only from @p mm. * return nullptr if no converter available and @p throw_flag not set. @@ -76,6 +86,13 @@ namespace xo { template gp to_object(IAlloc * mm, const T & x, bool throw_flag); + /** convert object @p obj to tagged pointer, with typeid @target_id. + * Allocates memory only from @p mm. + * return null TaggedPtr if no converter available and @p throw_flag not set. + * Throw exception if no converter available and @p throw_flag set. + **/ + TaggedPtr tp_from_object(IAlloc * mm, gp & obj, TypeId target_id, bool throw_flag); + private: /** expandable type-driven conversion table. **/ @@ -84,7 +101,8 @@ namespace xo { template void - ObjectConverter::establish_conversion(Converter::ConvertToObjectFn fn) + ObjectConverter::establish_conversion(Converter::ConvertToObjectFn to, + Converter::ConvertFromObjectFn from) { using xo::reflect::TypeDescrW; using xo::reflect::Reflect; @@ -92,7 +110,7 @@ namespace xo { TypeDescrW td = Reflect::require(); Converter * cvt = cvt_.require(td); - *cvt = Converter(fn); + *cvt = Converter(to, from); } template diff --git a/xo-object/include/xo/object/Primitive.hpp b/xo-object/include/xo/object/Primitive.hpp new file mode 100644 index 00000000..64583e3d --- /dev/null +++ b/xo-object/include/xo/object/Primitive.hpp @@ -0,0 +1,70 @@ +/** @file Primitive.hpp + * + * @author Roland Conybeare, Nov 2025 + **/ + +#pragma once + +#include "Procedure.hpp" +#include "CVector.hpp" + +namespace xo { + namespace obj { + // TODO: consider PrimitiveInterface here + + /** @class Primitive + * @brief Procedure implemented natively, i.e. in c++ + * + * @tparam FunctionType can be: + * - a C-style function signature such as double(*)(double) + * - a std::function, such as std::function + **/ + template + class PrimitiveBase : public Procedure { + public: + using function_type = Fn; + + public: + explicit PrimitiveBase(Converter & cvt, Fn impl) : converter_{cvt}, impl_{std::move(impl)} {} + + // inherited from Procedure.. + + virtual std::size_t n_args() const override = 0; + virtual gp apply_nocheck(const CVector> & args) override = 0; + + protected: + Converter & converter_; + Fn impl_; + }; + + template + class Primitive : public PrimitiveBase { + }; + + template + class Primitive : public PrimitiveBase { + public: + using Super = PrimitiveBase; + using function_type = Ret (*)(Arg1, Arg2); + + public: + explicit Primitive(Converter & cvt, function_type fn) : PrimitiveBase{cvt, fn} {} + + // inherited from Procedure.. + + virtual std::size_t n_args() const final override { return 2; } + virtual gp apply_nocheck(const CVector> & args) final override { + /* note: args[0] will be this procedure. actual i'th function argument in args[i+1] + */ + + args[1] + } + + private: + + + }; + } +} + +/* end Primitive.hpp */ diff --git a/xo-object/include/xo/object/Procedure.hpp b/xo-object/include/xo/object/Procedure.hpp new file mode 100644 index 00000000..5a0cb824 --- /dev/null +++ b/xo-object/include/xo/object/Procedure.hpp @@ -0,0 +1,31 @@ +/** @file Procedure.hpp + * + * @author Roland Conybeare, Nov 2025 + **/ + +#pragma once + +#include "Object.hpp" + +namespace xo { + namespace obj { + /** @class ProcedureInterface + * @brief Interface to a dynamically-typed procedure + **/ + class Procedure : public Object { + public: + virtual std::size_t n_args() const = 0; + virtual gp apply_nocheck(const CVector> & args) = 0; + + // inherited from Object.. + + // virtual TaggedPtr self_tp() const override; + // virtual void display(std::ostream &) const override; + // virtual size_t _shallow_size() const override; + // virtual Object * _shallow_copy(gc::IAlloc *) const override; + // virtual size_t _forward_children(gc::IAlloc *) override; + }; + } +} + +/* end Procedure.hpp */ diff --git a/xo-object/src/object/ObjectConverter.cpp b/xo-object/src/object/ObjectConverter.cpp index 291c11de..2c15e594 100644 --- a/xo-object/src/object/ObjectConverter.cpp +++ b/xo-object/src/object/ObjectConverter.cpp @@ -7,9 +7,13 @@ #include "Integer.hpp" #include "Float.hpp" #include "Boolean.hpp" +#include "TaggedPtr.hpp" +#include "xo/alloc/Blob.hpp" namespace xo { + using xo::reflect::Reflect; using xo::reflect::TaggedPtr; + using xo::reflect::TypeId; using xo::gc::IAlloc; namespace obj { @@ -25,6 +29,34 @@ namespace xo { return Integer::make(mm, *native); } + template + TaggedPtr + object_to_int(IAlloc * mm, gp obj) + { + /* mm cannot be GC allocator! + * That's Object-only + */ + + gp int_obj = Integer::from(obj); + + if (!int_obj.get()) { + throw std::runtime_error(tostr("Object obj found where Integer expected", + xtag("obj", obj))); + } + + /* allocate a Blob wrapper to satsify GC memory layout demands */ + + gp tmp = Blob::make(mm, sizeof(T)); + + T * p = reinterpret_cast(tmp->data()); + + *p = int_obj->value(); + + /* WARNING: retval invalidated when *mm cleared/recycled/collected */ + + return Reflect::make_tp(p); + } + template gp float_to_object(IAlloc * mm, const TaggedPtr & src) @@ -36,6 +68,34 @@ namespace xo { return Float::make(mm, *native); } + template + TaggedPtr + object_to_float(IAlloc * mm, gp obj) + { + /* mm cannot be GC allocator! + * That's Object-only + */ + + gp float_obj = Float::from(obj); + + if (!float_obj.get()) { + throw std::runtime_error(tostr("Object obj found where Float expected", + xtag("obj", obj))); + } + + /* allocate a Blob wrapper to satsify GC memory layout demands */ + + gp tmp = Blob::make(mm, sizeof(T)); + + T * p = reinterpret_cast(tmp->data()); + + *p = float_obj->value(); + + /* WARNING: retval invalidated when *mm cleared/recycled/collected */ + + return Reflect::make_tp(p); + } + gp bool_to_object(IAlloc * /*mm*/, const TaggedPtr & src) { @@ -45,16 +105,32 @@ namespace xo { return Boolean::boolean_obj(*native); } + + TaggedPtr + object_to_bool(IAlloc * /*mm*/, gp obj) + { + static bool s_true = true; + static bool s_false = false; + + gp bool_obj = Boolean::from(obj); + + if (!bool_obj.get()) { + throw std::runtime_error(tostr("Object obj found where Boolean expected", + xtag("obj", obj))); + } + + return Reflect::make_tp(bool_obj->value() ? &s_true : &s_false); + } } ObjectConverter::ObjectConverter() { - this->establish_conversion(&int_to_object); - this->establish_conversion(&int_to_object); + this->establish_conversion(&int_to_object, &object_to_int); + this->establish_conversion(&int_to_object, &object_to_int); - this->establish_conversion(&float_to_object); + this->establish_conversion(&float_to_object, &object_to_float); - this->establish_conversion(&bool_to_object); + this->establish_conversion(&bool_to_object, &object_to_bool); } gp @@ -69,7 +145,7 @@ namespace xo { return (cvt->cvt_to_object_)(mm, x_tp); } else { if (throw_flag) { - throw std::runtime_error(tostr("no object-converter available for instance of type", + throw std::runtime_error(tostr("no to-object-converter available for instance of type", xtag("id", x_tp.td()->id()), xtag("name", x_tp.td()->short_name()))); } @@ -77,6 +153,23 @@ namespace xo { return nullptr; } } + + TaggedPtr + ObjectConverter::tp_from_object(IAlloc * mm, gp & obj, TypeId target_id, bool throw_flag) + { + const Converter * cvt = cvt_.lookup(target_id); + + if (cvt) { + return (cvt->cvt_from_object_)(mm, obj); + } else { + if (throw_flag) { + throw std::runtime_error(tostr("no from-object-converter available for instance of type", + xtag("id", target_id))); + } + + return TaggedPtr::universal_null(); + } + } } } diff --git a/xo-object/src/object/String.cpp b/xo-object/src/object/String.cpp index 154efa97..fdc3d3ba 100644 --- a/xo-object/src/object/String.cpp +++ b/xo-object/src/object/String.cpp @@ -144,7 +144,7 @@ namespace xo { if (owner_ == owner::unique) { std::byte * mem = reinterpret_cast(chars_); - copy->chars_ = reinterpret_cast(Object::mm->alloc_gc_copy(z_chars_, mem)); + copy->chars_ = reinterpret_cast(mm->alloc_gc_copy(z_chars_, mem)); strlcpy(copy->chars_, chars_, z_chars_); }