xo-interpreter: Object->TaggedPtr conversion (prep for primitives)

This commit is contained in:
Roland Conybeare 2025-11-26 20:15:03 -05:00
commit 4488117ac3
16 changed files with 371 additions and 26 deletions

View file

@ -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<Blob> 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 */

View file

@ -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>
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<Blob*>(this));
}
void
Blob::display(std::ostream & os) const
{
os << "<blob" << xtag("z", z_) << ">";
}
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<Blob> 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 */

View file

@ -9,6 +9,7 @@ set(SELF_SRCS
GcStatistics.cpp
ObjectStatistics.cpp
Object.cpp
Blob.cpp
Forwarding1.cpp
generation.cpp
)

View file

@ -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<FunctionExprInterface> from(bp<Expression> x) {
return bp<FunctionExprInterface>::from(x);
static bp<ProcedureExprInterface> from(bp<Expression> x) {
return bp<ProcedureExprInterface>::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 **/

View file

@ -6,7 +6,7 @@
#pragma once
#include "Expression.hpp"
#include "FunctionExprInterface.hpp"
#include "ProcedureExprInterface.hpp"
#include "Variable.hpp"
#include "LocalSymtab.hpp"
#include <map>
@ -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

View file

@ -5,19 +5,19 @@
#pragma once
#include "FunctionExprInterface.hpp"
#include "ProcedureExprInterface.hpp"
#include "llvmintrinsic.hpp"
#include <type_traits>
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<PrimitiveExprInterface> from(bp<Expression> x) {

View file

@ -0,0 +1,33 @@
/** @file FunctionExprInterface.hpp
*
* Author: Roland Conybeare
**/
#pragma once
#include "Expression.hpp"
//#include <cstdint>
namespace xo {
namespace scm {
class ProcedureExprInterface : public Expression {
public:
ProcedureExprInterface(exprtype extype, TypeDescr fn_type)
: Expression(extype, fn_type) {}
/** downcast from Expression **/
static bp<ProcedureExprInterface> from(bp<Expression> x) {
return bp<ProcedureExprInterface>::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 **/

View file

@ -259,7 +259,7 @@ namespace xo {
TypeDescr lambda_td,
const rp<LocalSymtab> & local_env,
const rp<Expression> & body)
: FunctionExprInterface(exprtype::lambda, lambda_td),
: ProcedureExprInterface(exprtype::lambda, lambda_td),
name_{name},
body_{body},
local_env_{local_env}

View file

@ -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;

View file

@ -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 */

View file

@ -18,10 +18,11 @@ namespace xo {
struct Converter {
using TaggedPtr = xo::reflect::TaggedPtr;
using ConvertToObjectFn = gp<Object> (*)(gc::IAlloc *, const TaggedPtr &);
using ConvertFromObjectFn = TaggedPtr (*)(gc::IAlloc *, gp<Object> 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 <typename T>
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 <typename T>
gp<Object> 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<Object> & obj, TypeId target_id, bool throw_flag);
private:
/** expandable type-driven conversion table.
**/
@ -84,7 +101,8 @@ namespace xo {
template <typename T>
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<T>();
Converter * cvt = cvt_.require(td);
*cvt = Converter(fn);
*cvt = Converter(to, from);
}
template <typename T>

View file

@ -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<double(double)>
**/
template <typename Fn>
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<Object> apply_nocheck(const CVector<gp<Object>> & args) override = 0;
protected:
Converter & converter_;
Fn impl_;
};
template <typename Fn>
class Primitive : public PrimitiveBase<Fn> {
};
template <Ret, Arg1, Arg2>
class Primitive<Ret (*)(Arg1, Arg2)> : public PrimitiveBase<Ret (*)(Arg1, Arg2)> {
public:
using Super = PrimitiveBase<Ret (*)(Arg1, Arg2)>;
using function_type = Ret (*)(Arg1, Arg2);
public:
explicit Primitive(Converter & cvt, function_type fn) : PrimitiveBase<Ret (*)(Arg1, Arg2)>{cvt, fn} {}
// inherited from Procedure..
virtual std::size_t n_args() const final override { return 2; }
virtual gp<Object> apply_nocheck(const CVector<gp<Object>> & 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 */

View file

@ -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<Object> apply_nocheck(const CVector<gp<Object>> & 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 */

View file

@ -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 <typename T>
TaggedPtr
object_to_int(IAlloc * mm, gp<Object> obj)
{
/* mm cannot be GC allocator!
* That's Object-only
*/
gp<Integer> 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<Blob> tmp = Blob::make(mm, sizeof(T));
T * p = reinterpret_cast<T *>(tmp->data());
*p = int_obj->value();
/* WARNING: retval invalidated when *mm cleared/recycled/collected */
return Reflect::make_tp(p);
}
template <typename T>
gp<Object>
float_to_object(IAlloc * mm, const TaggedPtr & src)
@ -36,6 +68,34 @@ namespace xo {
return Float::make(mm, *native);
}
template <typename T>
TaggedPtr
object_to_float(IAlloc * mm, gp<Object> obj)
{
/* mm cannot be GC allocator!
* That's Object-only
*/
gp<Float> 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<Blob> tmp = Blob::make(mm, sizeof(T));
T * p = reinterpret_cast<T *>(tmp->data());
*p = float_obj->value();
/* WARNING: retval invalidated when *mm cleared/recycled/collected */
return Reflect::make_tp(p);
}
gp<Object>
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<Object> obj)
{
static bool s_true = true;
static bool s_false = false;
gp<Boolean> 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<std::int32_t>(&int_to_object<std::int32_t>);
this->establish_conversion<std::int64_t>(&int_to_object<std::int64_t>);
this->establish_conversion<std::int32_t>(&int_to_object<std::int32_t>, &object_to_int<std::int32_t>);
this->establish_conversion<std::int64_t>(&int_to_object<std::int64_t>, &object_to_int<std::int64_t>);
this->establish_conversion<double>(&float_to_object<double>);
this->establish_conversion<double>(&float_to_object<double>, &object_to_float<double>);
this->establish_conversion<bool>(&bool_to_object);
this->establish_conversion<bool>(&bool_to_object, &object_to_bool);
}
gp<Object>
@ -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<Object> & 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();
}
}
}
}

View file

@ -144,7 +144,7 @@ namespace xo {
if (owner_ == owner::unique) {
std::byte * mem = reinterpret_cast<std::byte *>(chars_);
copy->chars_ = reinterpret_cast<char *>(Object::mm->alloc_gc_copy(z_chars_, mem));
copy->chars_ = reinterpret_cast<char *>(mm->alloc_gc_copy(z_chars_, mem));
strlcpy(copy->chars_, chars_, z_chars_);
}