From eadf26bb9c2ce70149d2316baa1a6eba42e26d31 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 24 Dec 2025 19:40:48 -0500 Subject: [PATCH] xo-facet: facet template [WIP] --- xo-facet/codegen/abstract_facet.hpp.j2 | 40 +++++++ xo-facet/codegen/genfacet.py | 115 ++++++++++++++++++++ xo-gc/include/xo/gc/detail/AGCObject.hpp | 53 +++++++++ xo-object2/idl/Sequence.json5 | 47 ++++++++ xo-object2/include/xo/object2/ASequence.hpp | 43 ++++++++ 5 files changed, 298 insertions(+) create mode 100644 xo-facet/codegen/abstract_facet.hpp.j2 create mode 100755 xo-facet/codegen/genfacet.py create mode 100644 xo-gc/include/xo/gc/detail/AGCObject.hpp create mode 100644 xo-object2/idl/Sequence.json5 create mode 100644 xo-object2/include/xo/object2/ASequence.hpp diff --git a/xo-facet/codegen/abstract_facet.hpp.j2 b/xo-facet/codegen/abstract_facet.hpp.j2 new file mode 100644 index 00000000..ff9c61fb --- /dev/null +++ b/xo-facet/codegen/abstract_facet.hpp.j2 @@ -0,0 +1,40 @@ +/** @file {{ abstract_facet_fname }} + * + * Generated automagically from ingredients: + * 1. code generator: + * [{{genfacet}}] + * arguments: + * --input [{{genfacet_input}}] + * 2. jinja2 template for abstract facet .hpp file: + * [{{ abstract_facet_hpp_j2 }}] + * 3. idl for facet methods + * [{{ idl_fname }}] + **/ + +#pragma once + +// includes (via {facet_includes}) +{% for include_fname in facet_includes %} +#include {{include_fname}} +{% endfor %} + +namespace {{facet_ns1}} { +namespace {{facet_ns2}} { + +/** +{{abstract_facet_doc}} +**/ +class {{abstract_facet}} { +public: + {% for method in methods %} + + /** {{method.doc}} **/ + virtual {{method.return_type}} {{method.name}}({{method.args | args}}) {{method | qualifiers}} = 0; + + {% endfor %} +}; /*{{abstract_facet}}*/ + +template + +} /*namespace {{facet_ns2}}*/ +} /*namespace {{facet_ns1}}*/ diff --git a/xo-facet/codegen/genfacet.py b/xo-facet/codegen/genfacet.py new file mode 100755 index 00000000..aab098fa --- /dev/null +++ b/xo-facet/codegen/genfacet.py @@ -0,0 +1,115 @@ +#! /usr/bin/env python3 +# +# genfacet.py + +import json5 +import argparse +from pathlib import Path +from jinja2 import Environment, FileSystemLoader + +def load_idl(path): + with open(path) as f: + return json5.load(f) + +def format_method_qualifiers(method): + """ Build qualifier string: const noexcept + """ + quals = [] + if method.get('const', False): + quals.append('const') + if method.get('noexcept', False): + quals.append('noexcept') + + return ' '.join(quals) + +def format_args(args, include_names=True): + """ Format argument list for a method + """ + if not args: + return '' + if include_names: + return ', '.join(f"{p['type']} {p['name']}" for p in args) + else: + return ', '.join(p['type'] for p in args) + +def format_arg_names(args): + """ Format argument names, for forwarding + """ + return ', '.join(p['name'] for p in args) + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--input', required=True, help='input IDL JSON5 file') + parser.add_argument('--output', required=True, help='output directory') + args = parser.parse_args() + + idl_fname = args.input + idl = load_idl(idl_fname) + output_dir = Path(args.output) + output_dir.mkdir(parents=False, exist_ok=True) + + # setup jinja2 + template_dir = Path(__file__).parent + #template_dir = Path(__file__).parent / 'codegen' + + print(f'template_dir: [{template_dir}]') + + env = Environment(loader = FileSystemLoader(template_dir), + trim_blocks = True, + lstrip_blocks = True) + + # custom filters + env.filters['qualifiers'] = format_method_qualifiers + env.filters['args'] = format_args + env.filters['argnames'] = format_arg_names + + facet_includes = idl['includes'] + facet_ns1 = idl['namespace1'] + facet_ns2 = idl['namespace2'] + facet_name = idl['facet'] # e.g. Sequence + facet_brief = idl['brief'] + facet_doc = '\n'.join(idl['doc']) + methods = idl['methods'] + for method in methods: + method['doc'] = '\n'.join(method['doc']) + + abstract_facet = f'A{facet_name}' + abstract_facet_fname = f'{abstract_facet}.hpp' + + context = { + 'genfacet': __file__, + 'genfacet_input': args.input, + # + 'facet_includes': facet_includes, + 'facet_ns1': facet_ns1, + 'facet_ns2': facet_ns2, + #'name': facet_name, + 'idl_fname': idl_fname, + # + 'abstract_facet_hpp_j2': 'abstract_facet.hpp.j2', + 'abstract_facet': abstract_facet, + 'abstract_facet_fname': abstract_facet_fname, + 'abstract_facet_doc': facet_doc, + # + 'methods': methods + } + + # generate .hpp files + + templates = {} + templates[abstract_facet_fname] = context['abstract_facet_hpp_j2'] + + for output_file, template_name in templates.items(): + print(f'output_file: [{output_file}]') + print(f'template_name: [{template_name}]') + + template = env.get_template(template_name) + content = template.render(**context) + + (output_dir / output_file).write_text(content) + print(f"Generated {output_dir}/{output_file}") + + +if __name__ == '__main__': + main() + diff --git a/xo-gc/include/xo/gc/detail/AGCObject.hpp b/xo-gc/include/xo/gc/detail/AGCObject.hpp new file mode 100644 index 00000000..1e0a6d45 --- /dev/null +++ b/xo-gc/include/xo/gc/detail/AGCObject.hpp @@ -0,0 +1,53 @@ +/** @file AGCObject.hpp + * + * @author Roland Conybeare, Dec 2025 + **/ + +#pragma once + +#include "Allocator.hpp" +#include "xo/facet/facet_implementation.hpp" +#include "xo/facet/typeseq.hpp" +#include "xo/facet/obj.hpp" // for obj in shallow_copy +#include +#include + +namespace xo { + namespace mm { + using Copaque = const void *; + using Opaque = void *; + + /** @class AObject + * @brief Abstract facet for collector-eligible data + * + * Data that supports AGCObject can have memory managed + * by ACollector + **/ + struct AGCObject { + using size_type = std::size_t; + + /** RTTI: unique id# for actual runtime data representation **/ + virtual int32_t _typeseq() const noexcept = 0; + + virtual size_type shallow_size(Copaque d) const noexcept = 0; + virtual Opaque * shallow_copy(Copaque d, + obj mm) const noexcept = 0; + virtual size_type forward_children(Opaque d) const noexcept = 0; + }; + + // implementation IGCObject_DRepr of AGCObject for state DRepr + // should provide a specialization: + // + // template <> + // struct xo::facet::FacetImplementation { + // using ImplType = IGCObject_DRepr; + // }; + // + // then IGCObject_ImplType --> IGCObject_DRepr + // + template + using IGCObject_ImplType = xo::facet::FacetImplType; + } /*namespace mm*/ +} /*namespace xo*/ + +/* end AGCObject.hpp */ diff --git a/xo-object2/idl/Sequence.json5 b/xo-object2/idl/Sequence.json5 new file mode 100644 index 00000000..a0dc0011 --- /dev/null +++ b/xo-object2/idl/Sequence.json5 @@ -0,0 +1,47 @@ +{ + includes: [""], + namespace1: "xo", + namespace2: "scm", + facet: "Sequence", + brief: "an ordered collection of variants", + doc: [ + "Elements appear in some determinstic order.", + "Sequence is GC-aware --> elements must be GC-aware" + ], + methods: [ + // bool is_empty() const noexcept + { + name: "is_empty", + doc: ["true iff sequence is empty"], + return_type: "bool", + args: [], + const: true, + noexcept: true, + attributes: [], + }, + + // bool is_finite() const noexcept + { + name: "is_finite", + doc: ["true iff sequence is finite"], + return_type: "bool", + args: [], + const: true, + noexcept: true, + attributes: [], + }, + + // obj at(size_type index) const; + { + name: "at", + doc: ["return element @p index of this sequence"], + return_type: "obj", + args: [ + {type: "size_type", name: "index"}, + ], + const: true, + noexcept: false, + attributes: [], + }, + ], +} diff --git a/xo-object2/include/xo/object2/ASequence.hpp b/xo-object2/include/xo/object2/ASequence.hpp new file mode 100644 index 00000000..db12e6bd --- /dev/null +++ b/xo-object2/include/xo/object2/ASequence.hpp @@ -0,0 +1,43 @@ +/** @file ASequence.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [/home/roland/proj/xo-umbrella2/xo-object2/../xo-facet/codegen/genfacet.py] + * arguments: + * --input [./idl/Sequence.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [abstract_facet.hpp.j2] + * 3. idl for facet methods + * [./idl/Sequence.json5] + **/ + +#pragma once + +// includes (via {facet_includes}) +#include + +namespace xo { +namespace scm { + +/** +Elements appear in some determinstic order. +Sequence is GC-aware --> elements must be GC-aware +**/ +class ASequence { +public: + + /** true iff sequence is empty **/ + virtual bool is_empty() const noexcept = 0; + + + /** true iff sequence is finite **/ + virtual bool is_finite() const noexcept = 0; + + + /** return element @p index of this sequence **/ + virtual obj at(size_type index) const = 0; + +}; /*ASequence*/ + +} /*namespace scm*/ +} /*namespace xo*/ \ No newline at end of file