From 38d18bf325b0736a4513f2327405b182bbe3962f Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 12 Jan 2026 21:50:17 -0500 Subject: [PATCH] xo-expression2: + Expression facet --- CMakeLists.txt | 1 + xo-expression2/CMakeLists.txt | 15 +++- .../cmake/xo_expression2Config.cmake.in | 6 +- xo-expression2/idl/Expression.json5 | 48 ++++++++++ .../include/xo/expression2/DConstant.hpp | 37 ++++++++ .../include/xo/expression2/Expression.hpp | 21 +++++ .../include/xo/expression2/TypeRef.hpp | 60 +++++++++++++ .../xo/expression2/detail/AExpression.hpp | 72 +++++++++++++++ .../xo/expression2/detail/IExpression_Any.hpp | 87 +++++++++++++++++++ .../expression2/detail/IExpression_Xfer.hpp | 86 ++++++++++++++++++ .../xo/expression2/detail/RExpression.hpp | 81 +++++++++++++++++ xo-expression2/src/expression2/CMakeLists.txt | 16 ++++ .../src/expression2/IExpression_Any.cpp | 38 ++++++++ xo-expression2/src/expression2/TypeRef.cpp | 60 +++++++++++++ .../expression2_register_facets.cpp | 11 +++ xo-object2/include/xo/object2/DList.hpp | 1 + 16 files changed, 636 insertions(+), 4 deletions(-) create mode 100644 xo-expression2/idl/Expression.json5 create mode 100644 xo-expression2/include/xo/expression2/DConstant.hpp create mode 100644 xo-expression2/include/xo/expression2/Expression.hpp create mode 100644 xo-expression2/include/xo/expression2/TypeRef.hpp create mode 100644 xo-expression2/include/xo/expression2/detail/AExpression.hpp create mode 100644 xo-expression2/include/xo/expression2/detail/IExpression_Any.hpp create mode 100644 xo-expression2/include/xo/expression2/detail/IExpression_Xfer.hpp create mode 100644 xo-expression2/include/xo/expression2/detail/RExpression.hpp create mode 100644 xo-expression2/src/expression2/CMakeLists.txt create mode 100644 xo-expression2/src/expression2/IExpression_Any.cpp create mode 100644 xo-expression2/src/expression2/TypeRef.cpp create mode 100644 xo-expression2/src/expression2/expression2_register_facets.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 58299244..07f57b8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,7 @@ add_subdirectory(xo-object2) # experiment w/ facet object model add_subdirectory(xo-ordinaltree) # add_subdirectory(xo-tokenizer2) # schematika tokenizer (fomo) +add_subdirectory(xo-expression2) # schematika expressions (fomo) # add_subdirectory(xo-webutil) add_subdirectory(xo-pywebutil) diff --git a/xo-expression2/CMakeLists.txt b/xo-expression2/CMakeLists.txt index 1ecb48b3..3adeccfa 100644 --- a/xo-expression2/CMakeLists.txt +++ b/xo-expression2/CMakeLists.txt @@ -22,12 +22,21 @@ add_definitions(${PROJECT_CXX_FLAGS}) #add_subdirectory(utest) +# note: manual target; generated code committed to git +xo_add_genfacet( + TARGET xo-expression2-facet-expression + FACET Expression + INPUT idl/Expression.json5 + OUTPUT_HPP_DIR include/xo/expression2 + OUTPUT_IMPL_SUBDIR detail + OUTPUT_CPP_DIR src/expression2 + ) + # ---------------------------------------------------------------- # header-only library -set(SELF_LIB xo_expression2) -xo_add_headeronly_library(${SELF_LIB}) -xo_install_library4(${SELF_LIB} ${PROJECT_NAME}Targets) +add_subdirectory(src/expression2) + xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) # ---------------------------------------------------------------- diff --git a/xo-expression2/cmake/xo_expression2Config.cmake.in b/xo-expression2/cmake/xo_expression2Config.cmake.in index b5c3cd5c..ee1852c8 100644 --- a/xo-expression2/cmake/xo_expression2Config.cmake.in +++ b/xo-expression2/cmake/xo_expression2Config.cmake.in @@ -6,7 +6,11 @@ include(CMakeFindDependencyMacro) # must coordinate with xo_dependency() calls # in CMakeLists.txt # -#find_dependency(xo_flatstring) +find_dependency(xo_gc) +find_dependency(reflect) +find_dependency(xo_printable2) +find_dependency(xo_flatstring) +find_dependency(indentlog) include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") check_required_components("@PROJECT_NAME@") diff --git a/xo-expression2/idl/Expression.json5 b/xo-expression2/idl/Expression.json5 new file mode 100644 index 00000000..9daca006 --- /dev/null +++ b/xo-expression2/idl/Expression.json5 @@ -0,0 +1,48 @@ +{ + mode: "facet", + includes: ["\"TypeRef.hpp\""], + namespace1: "xo", + namespace2: "scm", + facet: "Expression", + detail_subdir: "detail", + brief: "a schematika expression", + using_doxygen: true, + doc: [ + "Representation for executable Schematika expressions" + ], + types: [], + const_methods: [ + { + name: "typeref", + doc: ["placeholder for type giving possible values for this expression"], + return_type: "TypeRef", + args: [], + const: true, + noexcept: true, + attributes: [], + }, + { + name: "valuetype", + doc: ["type giving possible values for this expression. Maybe null before typecheck"], + return_type: "TypeDescr", + args: [], + const: true, + noexcept: true, + attributes: [], + }, + ], + nonconst_methods: [ + { + name: "assign_valuetype", + doc: ["assing to valuetype member. Useful when scaffolding expressions"], + return_type: "void", + args: [ + // void assign_valuetype(TypeDescr td) + {type: "TypeDescr", name: "td"}, + ], + const: false, + noexcept: true, + attributes: [], + } + ], +} diff --git a/xo-expression2/include/xo/expression2/DConstant.hpp b/xo-expression2/include/xo/expression2/DConstant.hpp new file mode 100644 index 00000000..b7cc5cce --- /dev/null +++ b/xo-expression2/include/xo/expression2/DConstant.hpp @@ -0,0 +1,37 @@ +/** @file DConstant.hpp + * + * @author Roland Conybeare, Jan 2026 + **/ + +#include + +namespace xo { + namespace scm { + /** @class DConstant + * @brief Schematika expression respresenting a literal constant + **/ + struct DConstant { + public: + using TypeDescr = xo::reflect::TypeDescr; + + public: + DConstant(TypeDescr td, void * value); + + TypeDescr value_td() const { return value_td; } + + TypeRef typeref() const noexcept; + TypeDescr valuetype() const noexcept; + void assign_valuetype(TypeDescr td) noexcept; + + private: + /** type for value of this expression **/ + TypeRef type_ref_; + /** type description for destination *value_ **/ + TypeDescr valuetype_; + /** literal value **/ + void * value_; + }; + } /*namespace scm*/ +} /*namespace xo*/ + +/* end DConstant.hpp */ diff --git a/xo-expression2/include/xo/expression2/Expression.hpp b/xo-expression2/include/xo/expression2/Expression.hpp new file mode 100644 index 00000000..311b627d --- /dev/null +++ b/xo-expression2/include/xo/expression2/Expression.hpp @@ -0,0 +1,21 @@ +/** @file Expression.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [/home/roland/proj/xo-umbrella2-claude1/xo-facet/codegen/genfacet] + * arguments: + * --input [idl/Expression.json5] + * 2. jinja2 template for facet .hpp file: + * [facet.hpp.j2] + * 3. idl for facet methods + * [idl/Expression.json5] + **/ + +#pragma once + +#include "detail/AExpression.hpp" +#include "detail/IExpression_Any.hpp" +#include "detail/IExpression_Xfer.hpp" +#include "detail/RExpression.hpp" + +/* end Expression.hpp */ \ No newline at end of file diff --git a/xo-expression2/include/xo/expression2/TypeRef.hpp b/xo-expression2/include/xo/expression2/TypeRef.hpp new file mode 100644 index 00000000..8ee08716 --- /dev/null +++ b/xo-expression2/include/xo/expression2/TypeRef.hpp @@ -0,0 +1,60 @@ +/** @file TypeRef.hpp + * + * @author Roland Conybeare, Jan 2026 + **/ + +#pragma once + +#include +#include + +namespace xo { + namespace scm { + /** @class TypeRef + * @brief name and (when established) resolution for type associate with an expression + * + * Type inference / unification operates on + * @ref xo::scm::TypeBlueprint instances. See also! + **/ + class TypeRef { + public: + using TypeDescr = xo::reflect::TypeDescr; + using type_var = flatstring<20>; + using prefix_type = flatstring<8>; + + public: + TypeRef() = default; + TypeRef(const type_var & id, TypeDescr td); + + /** if @p td is non-null + * -> type is already resolved + * + * if type is not determined (i.e. @p td is nullptr): + * -> generate and store type variable name. + **/ + static TypeRef dwim(prefix_type prefix, TypeDescr td); + + /** generate a unique type-variable name, + * that begins with @p prefix + **/ + static type_var generate_unique(prefix_type prefix); + + const type_var & id() const noexcept { return id_; } + TypeDescr td() const noexcept { return td_; } + + /** true iff type at this location has been resolved **/ + bool is_concrete() const noexcept; + + private: + /** unique (probably generated) name for type at this location **/ + type_var id_; + /** Description for concrete type, once resolved. + * May be null when this TypeRef created, + * but expected to be immutable once established. + **/ + TypeDescr td_; + }; + } /*namespace scm*/ +} /*namespace xo*/ + +/* end TypeRef.hpp */ diff --git a/xo-expression2/include/xo/expression2/detail/AExpression.hpp b/xo-expression2/include/xo/expression2/detail/AExpression.hpp new file mode 100644 index 00000000..942c940b --- /dev/null +++ b/xo-expression2/include/xo/expression2/detail/AExpression.hpp @@ -0,0 +1,72 @@ +/** @file AExpression.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [/home/roland/proj/xo-umbrella2-claude1/xo-facet/codegen/genfacet] + * arguments: + * --input [idl/Expression.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [abstract_facet.hpp.j2] + * 3. idl for facet methods + * [idl/Expression.json5] + **/ + +#pragma once + +// includes (via {facet_includes}) +#include "TypeRef.hpp" +#include +#include +#include + +namespace xo { +namespace scm { + +using Copaque = const void *; +using Opaque = void *; + +/** +Representation for executable Schematika expressions +**/ +class AExpression { +public: + /** @defgroup scm-expression-type-traits **/ + ///@{ + // types + /** integer identifying a type **/ + using typeseq = xo::facet::typeseq; + ///@} + + /** @defgroup scm-expression-methods **/ + ///@{ + // const methods + /** RTTI: unique id# for actual runtime data representation **/ + virtual typeseq _typeseq() const noexcept = 0; + /** placeholder for type giving possible values for this expression **/ + virtual TypeRef typeref(Copaque data) const noexcept = 0; + /** type giving possible values for this expression. Maybe null before typecheck **/ + virtual TypeDescr valuetype(Copaque data) const noexcept = 0; + + // nonconst methods + /** assing to valuetype member. Useful when scaffolding expressions **/ + virtual void assign_valuetype(Opaque data, TypeDescr td) noexcept = 0; + ///@} +}; /*AExpression*/ + +/** Implementation IExpression_DRepr of AExpression for state DRepr + * should provide a specialization: + * + * template <> + * struct xo::facet::FacetImplementation { + * using Impltype = IExpression_DRepr; + * }; + * + * then IExpression_ImplType --> IExpression_DRepr + **/ +template +using IExpression_ImplType = xo::facet::FacetImplType; + +} /*namespace scm*/ +} /*namespace xo*/ + +/* */ \ No newline at end of file diff --git a/xo-expression2/include/xo/expression2/detail/IExpression_Any.hpp b/xo-expression2/include/xo/expression2/detail/IExpression_Any.hpp new file mode 100644 index 00000000..0fac3289 --- /dev/null +++ b/xo-expression2/include/xo/expression2/detail/IExpression_Any.hpp @@ -0,0 +1,87 @@ +/** @file IExpression_Any.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [/home/roland/proj/xo-umbrella2-claude1/xo-facet/codegen/genfacet] + * arguments: + * --input [idl/Expression.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [iface_facet_any.hpp.j2] + * 3. idl for facet methods + * [idl/Expression.json5] + **/ + +#pragma once + +#include "AExpression.hpp" +#include + +namespace xo { namespace scm { class IExpression_Any; } } + +namespace xo { +namespace facet { + +template <> +struct FacetImplementation +{ + using ImplType = xo::scm::IExpression_Any; +}; + +} +} + +namespace xo { +namespace scm { + + /** @class IExpression_Any + * @brief AExpression implementation for empty variant instance + **/ + class IExpression_Any : public AExpression { + public: + /** @defgroup scm-expression-any-type-traits **/ + ///@{ + + /** integer identifying a type **/ + using typeseq = xo::facet::typeseq; + + ///@} + /** @defgroup scm-expression-any-methods **/ + ///@{ + + const AExpression * iface() const { return std::launder(this); } + + // from AExpression + + // const methods + typeseq _typeseq() const noexcept override { return s_typeseq; } + [[noreturn]] TypeRef typeref(Copaque) const noexcept override { _fatal(); } + [[noreturn]] TypeDescr valuetype(Copaque) const noexcept override { _fatal(); } + + // nonconst methods + [[noreturn]] void assign_valuetype(Opaque, TypeDescr) noexcept override { _fatal(); } + + ///@} + + private: + /** @defgraoup scm-expression-any-private-methods **/ + ///@{ + + [[noreturn]] static void _fatal(); + + ///@} + + public: + /** @defgroup scm-expression-any-member-vars **/ + ///@{ + + static typeseq s_typeseq; + static bool _valid; + + ///@} + }; + +} /*namespace scm */ +} /*namespace xo */ + +/* IExpression_Any.hpp */ \ No newline at end of file diff --git a/xo-expression2/include/xo/expression2/detail/IExpression_Xfer.hpp b/xo-expression2/include/xo/expression2/detail/IExpression_Xfer.hpp new file mode 100644 index 00000000..55abfd05 --- /dev/null +++ b/xo-expression2/include/xo/expression2/detail/IExpression_Xfer.hpp @@ -0,0 +1,86 @@ +/** @file IExpression_Xfer.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [/home/roland/proj/xo-umbrella2-claude1/xo-facet/codegen/genfacet] + * arguments: + * --input [idl/Expression.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [iface_facet_any.hpp.j2] + * 3. idl for facet methods + * [idl/Expression.json5] + **/ + +#pragma once + +#include "TypeRef.hpp" + +namespace xo { +namespace scm { + /** @class IExpression_Xfer + **/ + template + class IExpression_Xfer : public AExpression { + public: + /** @defgroup scm-expression-xfer-type-traits **/ + ///@{ + /** actual implementation (not generated; often delegates to DRepr) **/ + using Impl = IExpression_DRepr; + /** integer identifying a type **/ + using typeseq = AExpression::typeseq; + ///@} + + /** @defgroup scm-expression-xfer-methods **/ + ///@{ + + static const DRepr & _dcast(Copaque d) { return *(const DRepr *)d; } + static DRepr & _dcast(Opaque d) { return *(DRepr *)d; } + + // from AExpression + + // const methods + typeseq _typeseq() const noexcept override { return s_typeseq; } + TypeRef typeref(Copaque data) const noexcept override { + return I::typeref(_dcast(data)); + } + TypeDescr valuetype(Copaque data) const noexcept override { + return I::valuetype(_dcast(data)); + } + + // non-const methods + void assign_valuetype(Opaque data, TypeDescr td) noexcept override { + return I::assign_valuetype(_dcast(data), td); + } + + ///@} + + private: + using I = Impl; + + public: + /** @defgroup scm-expression-xfer-member-vars **/ + ///@{ + + /** typeseq for template parameter DRepr **/ + static typeseq s_typeseq; + /** true iff satisfies facet implementation **/ + static bool _valid; + + ///@} + }; + + template + xo::facet::typeseq + IExpression_Xfer::s_typeseq + = xo::facet::typeseq::id(); + + template + bool + IExpression_Xfer::_valid + = xo::facet::valid_facet_implementation(); + +} /*namespace scm */ +} /*namespace xo*/ + +/* end IExpression_Xfer.hpp */ \ No newline at end of file diff --git a/xo-expression2/include/xo/expression2/detail/RExpression.hpp b/xo-expression2/include/xo/expression2/detail/RExpression.hpp new file mode 100644 index 00000000..e9b431e3 --- /dev/null +++ b/xo-expression2/include/xo/expression2/detail/RExpression.hpp @@ -0,0 +1,81 @@ +/** @file RExpression.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [/home/roland/proj/xo-umbrella2-claude1/xo-facet/codegen/genfacet] + * arguments: + * --input [idl/Expression.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [iface_facet_any.hpp.j2] + * 3. idl for facet methods + * [idl/Expression.json5] + **/ + +#pragma once + +#include "AExpression.hpp" + +namespace xo { +namespace scm { + +/** @class RExpression + **/ +template +class RExpression : public Object { +private: + using O = Object; + +public: + /** @defgroup scm-expression-router-type-traits **/ + ///@{ + using ObjectType = Object; + using DataPtr = Object::DataPtr; + using typeseq = xo::reflect::typeseq; + ///@} + + /** @defgroup scm-expression-router-ctors **/ + ///@{ + RExpression() {} + RExpression(Object::DataPtr data) : Object{std::move(data)} {} + + ///@} + /** @defgroup scm-expression-router-methods **/ + ///@{ + + // const methods + typeseq _typeseq() const noexcept { return O::iface()->_typeseq(); } + TypeRef typeref() const noexcept override { + return O::iface()->typeref(O::data()); + } + TypeDescr valuetype() const noexcept override { + return O::iface()->valuetype(O::data()); + } + + // non-const methods + // << do something for non-const methods >> + // + + ///@} + /** @defgroup scm-expression-member-vars **/ + ///@{ + + static bool _valid; + + ///@} +}; + +template +bool +RExpression::_valid = xo::facet::valid_object_router(); + +} /*namespace scm*/ +} /*namespace xo*/ + +namespace xo { namespace facet { + template + struct RoutingFor { + using RoutingType = xo::scm::RExpression; + }; +} } + +/* end RExpression.hpp */ \ No newline at end of file diff --git a/xo-expression2/src/expression2/CMakeLists.txt b/xo-expression2/src/expression2/CMakeLists.txt new file mode 100644 index 00000000..c0c794ff --- /dev/null +++ b/xo-expression2/src/expression2/CMakeLists.txt @@ -0,0 +1,16 @@ +# expression2/CMakeLists.txt + +set(SELF_LIB xo_expression2) +set(SELF_SRCS + TypeRef.cpp + #IExpression_Any.cpp + expression2_register_facets.cpp + ) + +xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) +# note: deps here must also appear in cmake/xo_expression2Config.cmake.in +xo_dependency(${SELF_LIB} xo_gc) +xo_dependency(${SELF_LIB} reflect) +xo_dependency(${SELF_LIB} xo_printable2) +xo_dependency(${SELF_LIB} xo_flatstring) +xo_dependency(${SELF_LIB} indentlog) diff --git a/xo-expression2/src/expression2/IExpression_Any.cpp b/xo-expression2/src/expression2/IExpression_Any.cpp new file mode 100644 index 00000000..a27a68bc --- /dev/null +++ b/xo-expression2/src/expression2/IExpression_Any.cpp @@ -0,0 +1,38 @@ +/** @file IExpression_Any.cpp + * + **/ + +#include "detail/IExpression_Any.hpp" +#include + +namespace xo { +namespace scm { + +using xo::facet::DVariantPlaceholder; +using xo::facet::typeseq; +using xo::facet::valid_facet_implementation; + +void +IExpression_Any::_fatal() +{ + /* control here on uninitialized IAllocator_Any. + * Initialized instance will have specific implementation type + */ + std::cerr << "fatal" + << ": attempt to call uninitialized" + << " IExpression_Any method" + << std::endl; + std::terminate(); +} + +typeseq +IExpression_Any::s_typeseq = typeseq::id(); + +bool +IExpression_Any::_valid + = valid_facet_implementation(); + +} /*namespace scm*/ +} /*namespace xo*/ + +/* end IExpression_Any.cpp */ \ No newline at end of file diff --git a/xo-expression2/src/expression2/TypeRef.cpp b/xo-expression2/src/expression2/TypeRef.cpp new file mode 100644 index 00000000..2ceb6132 --- /dev/null +++ b/xo-expression2/src/expression2/TypeRef.cpp @@ -0,0 +1,60 @@ +/** @file TypeRef.cpp + * + * @author Roland Conybeare, Jan 2026 + **/ + +#include "TypeRef.hpp" + +namespace xo { + namespace scm { + TypeRef::TypeRef(const type_var & id, TypeDescr td) + : id_{id}, td_{td} + {} + + TypeRef + TypeRef::dwim(prefix_type prefix, TypeDescr td) + { + if (td) { + /* type already resolved + * -> we don't need a type variable name + */ + type_var null; + return TypeRef(null, td); + } else { + /* type is not resolved yet. + * -> give it a unique name, + * to seed unification + */ + return TypeRef(generate_unique(prefix), td); + } + } + + auto + TypeRef::generate_unique(prefix_type prefix) -> type_var + { + static uint32_t s_counter = 0; + + s_counter = (1 + s_counter) % 1000000000; + + char buf[type_var::fixed_capacity]; + int n = snprintf(buf, sizeof(buf), "%s:%u", prefix.c_str(), s_counter); + (void)n;; + + assert(n < static_cast(type_var::fixed_capacity)); + + // not necessary, but to remove all doubt. + buf [sizeof(buf) - 1] = '\0'; + + return type_var(buf); + } + + bool + TypeRef::is_concrete() const noexcept + { + return (td_ != nullptr); + } + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end TypeRef.cpp */ diff --git a/xo-expression2/src/expression2/expression2_register_facets.cpp b/xo-expression2/src/expression2/expression2_register_facets.cpp new file mode 100644 index 00000000..49edb80c --- /dev/null +++ b/xo-expression2/src/expression2/expression2_register_facets.cpp @@ -0,0 +1,11 @@ +/** @file expression2_register_facets.cpp +* + * @author Roland Conybeare, Jan 2026 + **/ + +namespace xo { + namespace scm { + } +} /*namesapce xo*/ + +/* end expression2_register_facets.cpp */ diff --git a/xo-object2/include/xo/object2/DList.hpp b/xo-object2/include/xo/object2/DList.hpp index d20fadca..58de0750 100644 --- a/xo-object2/include/xo/object2/DList.hpp +++ b/xo-object2/include/xo/object2/DList.hpp @@ -21,6 +21,7 @@ namespace xo { using AAllocator = xo::mm::AAllocator; using ppindentinfo = xo::print::ppindentinfo; + public: DList(xo::obj h, DList * r) : head_{h}, rest_{r} {}