From 8c11c108ca26efd447a7faff469d710d0e881b72 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 20 Nov 2025 21:26:18 -0500 Subject: [PATCH] xo-interpreter: setting up for gc in interactive interpreter --- xo-alloc/include/xo/alloc/GC.hpp | 4 +- xo-alloc/src/alloc/GC.cpp | 2 +- .../include/xo/interpreter/Schematika.hpp | 9 ++- .../interpreter/VirtualSchematikaMachine.hpp | 24 ++++++-- xo-interpreter/src/interpreter/CMakeLists.txt | 2 +- xo-interpreter/src/interpreter/Schematika.cpp | 33 +++++++++-- .../interpreter/VirtualSchematikaMachine.cpp | 55 ++++++++++++++++--- .../include/xo/object/ObjectConverter.hpp | 26 ++++----- xo-object/src/object/ObjectConverter.cpp | 22 ++++++++ xo-reader/src/reader/reader.cpp | 6 +- .../include/xo/tokenizer/input_state.hpp | 6 +- .../include/xo/tokenizer/tokenizer.hpp | 5 +- 12 files changed, 153 insertions(+), 41 deletions(-) diff --git a/xo-alloc/include/xo/alloc/GC.hpp b/xo-alloc/include/xo/alloc/GC.hpp index abbb9657..ce7b0f85 100644 --- a/xo-alloc/include/xo/alloc/GC.hpp +++ b/xo-alloc/include/xo/alloc/GC.hpp @@ -37,14 +37,14 @@ namespace xo { * pages are committed on demand. * Initial committment will be up to @ref incr_gc_threshold_ **/ - std::size_t initial_nursery_z_ = 0; + std::size_t initial_nursery_z_ = 64*1024*1024; /** initial size in bytes for oldest (Tenured) generation. * GC allocates two tenured spaces of this size. * This number represents reserved address space. * pages are committed on demand. * Initial committment will be up to @ref full_gc_threshold_ **/ - std::size_t initial_tenured_z_ = 0; + std::size_t initial_tenured_z_ = 128*1024*1024; /** trigger incremental GC after this many bytes allocated in nursery **/ std::size_t incr_gc_threshold_ = 64*1024; /** trigger full GC after this many bytes promoted to tenured **/ diff --git a/xo-alloc/src/alloc/GC.cpp b/xo-alloc/src/alloc/GC.cpp index 8181cbb1..22654700 100644 --- a/xo-alloc/src/alloc/GC.cpp +++ b/xo-alloc/src/alloc/GC.cpp @@ -73,7 +73,7 @@ namespace xo { } if (nursery_size + config_.full_gc_threshold_ > tenured_size) { - throw std::runtime_error(tostr("GC::ctor: expected nursery size + tennured gc threshold < tenured size", + throw std::runtime_error(tostr("GC::ctor: expected nursery size + tenured gc threshold < tenured size", xtag("nursery-size", nursery_size), xtag("tenured-size", tenured_size), xtag("full-gc-threshold", config_.full_gc_threshold_) diff --git a/xo-interpreter/include/xo/interpreter/Schematika.hpp b/xo-interpreter/include/xo/interpreter/Schematika.hpp index f79167bf..e53c4459 100644 --- a/xo-interpreter/include/xo/interpreter/Schematika.hpp +++ b/xo-interpreter/include/xo/interpreter/Schematika.hpp @@ -5,7 +5,7 @@ #pragma once -#include "xo/alloc/IAlloc.hpp" +#include "xo/alloc/GC.hpp" namespace xo { namespace scm { @@ -27,8 +27,13 @@ namespace xo { std::string history_file = "scm_history.txt"; /** when true enable console logging for repl internals **/ bool debug_flag = false; + + /** garbage collector configuration **/ + gc::Config gc_config_; }; + using IAlloc = xo::gc::IAlloc; + public: ~Schematika(); @@ -51,7 +56,7 @@ namespace xo { void interactive_repl(); private: - Schematika(const Config & cfg); + explicit Schematika(const Config & cfg); private: up p_impl_; diff --git a/xo-interpreter/include/xo/interpreter/VirtualSchematikaMachine.hpp b/xo-interpreter/include/xo/interpreter/VirtualSchematikaMachine.hpp index 5ce33e29..da582e2e 100644 --- a/xo-interpreter/include/xo/interpreter/VirtualSchematikaMachine.hpp +++ b/xo-interpreter/include/xo/interpreter/VirtualSchematikaMachine.hpp @@ -5,14 +5,19 @@ #include "VsmInstr.hpp" #include "SchematikaError.hpp" #include "xo/expression/Expression.hpp" +#include "xo/object/ObjectConverter.hpp" #include "xo/alloc/Object.hpp" namespace xo { namespace scm { /** @brief state that may be shared across VirtualSchematikaMachine instances **/ struct VirtualSchematikaMachineFlyweight { + explicit VirtualSchematikaMachineFlyweight(gc::IAlloc * mm); + + /** memory allocator for interpreter operation. **/ + gc::IAlloc * object_mm_ = nullptr; /** convert TaggedPtr->Object **/ - ObjectConverter object_converter_; + xo::obj::ObjectConverter object_converter_; }; /** @class VirtualSchematikaMachine @@ -21,7 +26,10 @@ namespace xo { **/ class VirtualSchematikaMachine { public: - VirtualSchematikaMachine(); + using IAlloc = xo::gc::IAlloc; + + public: + VirtualSchematikaMachine(IAlloc * mm); /** evaluate expression @p expr. * borrows calling thread until completion @@ -37,10 +45,16 @@ namespace xo { **/ void run(); - /** execute vsm instruction @p pc + /** execute vsm instruction in program counter. * Note: may possibly be able to replace with just opcode **/ - void execute_one(const VsmInstr * pc); + void execute_one(); + + /** interpret literal constant expression **/ + void constant_op(); + + /** goto error state with message @p err **/ + void report_error(const std::string & err); /** implementation class; contains instruction implementations **/ friend class VsmOps; @@ -66,7 +80,7 @@ namespace xo { const VsmInstr * cont_ = nullptr; /** possibly-shared data **/ - VirtualSchemtikaMachineFlyweight flyweight_; + VirtualSchematikaMachineFlyweight flyweight_; }; } /*namespace scm*/ diff --git a/xo-interpreter/src/interpreter/CMakeLists.txt b/xo-interpreter/src/interpreter/CMakeLists.txt index 2c18bbb4..ca2dc98d 100644 --- a/xo-interpreter/src/interpreter/CMakeLists.txt +++ b/xo-interpreter/src/interpreter/CMakeLists.txt @@ -13,7 +13,7 @@ set(SELF_SRCS find_package(Threads REQUIRED) xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) -xo_dependency(${SELF_LIB} xo_alloc) +xo_dependency(${SELF_LIB} xo_object) xo_dependency(${SELF_LIB} xo_expression) xo_dependency(${SELF_LIB} xo_reader) xo_external_target_dependency(${SELF_LIB} replxx replxx::replxx) diff --git a/xo-interpreter/src/interpreter/Schematika.cpp b/xo-interpreter/src/interpreter/Schematika.cpp index 9e36d433..870d9e7a 100644 --- a/xo-interpreter/src/interpreter/Schematika.cpp +++ b/xo-interpreter/src/interpreter/Schematika.cpp @@ -10,6 +10,8 @@ #include namespace xo { + using xo::gc::IAlloc; + using xo::gc::GC; using xo::print::ppconfig; using xo::print::ppstate_standalone; using replxx::Replxx; @@ -19,7 +21,14 @@ namespace xo { class Schematika::Impl { public: - Impl(const Config & config) : config_{config} {} + /** note: choosing to have Schemtika::Impl + * rather than VirtualSchematikaMachine to own allocator + * to preserve option to share it + **/ + Impl(const Config & config, up mm) : config_{config}, vsm_{mm.get()}, mm_{std::move(mm)} {} + + /** create instance + allocator **/ + static up make(const Config & cfg); /** borrow calling thread to run interactive read-eval-print loop; * input from stdin, output to stdout. @@ -38,8 +47,21 @@ namespace xo { Config config_; /** schematika interpreter **/ VirtualSchematikaMachine vsm_; + /** ownership for memory allocator / garbage collector; + * @ref vsm_ holds naked pointer, so this could in principle be nullptr + * in case want to maintain allocator ownership from outside. + **/ + up mm_; }; + up + Schematika::Impl::make(const Config & cfg) + { + up mm = GC::make(cfg.gc_config_); + + return std::make_unique(cfg, std::move(mm)); + } + void Schematika::Impl::welcome(std::ostream & os) { @@ -106,7 +128,8 @@ namespace xo { // rx.bind_key_internal(Replxx::KEY::control('p'), "history_previous"); // rx.bind_key_internal(Replxx::KEY::control('n'), "history_next"); - reader rdr(config_.debug_flag); + //reader rdr(config_.debug_flag); + reader rdr(true); rdr.begin_interactive_session(); string input_str; @@ -168,7 +191,8 @@ namespace xo { /* print value */ cout << "scm result:" << endl; - pps.pretty(value); + cout << value << endl; + //pps.pretty(value); } } else if (error.is_error()) { @@ -207,8 +231,7 @@ namespace xo { // ----- Schematika ----- - Schematika::Schematika(const Config & cfg) : - p_impl_{new Impl(cfg)} + Schematika::Schematika(const Config & cfg) : p_impl_{std::move(Impl::make(cfg))} {} Schematika::~Schematika() diff --git a/xo-interpreter/src/interpreter/VirtualSchematikaMachine.cpp b/xo-interpreter/src/interpreter/VirtualSchematikaMachine.cpp index a00eb988..818deb13 100644 --- a/xo-interpreter/src/interpreter/VirtualSchematikaMachine.cpp +++ b/xo-interpreter/src/interpreter/VirtualSchematikaMachine.cpp @@ -1,14 +1,17 @@ /** @file VirtualSchematikaMachine.cpp **/ #include "VirtualSchematikaMachine.hpp" - #include "VsmInstr.hpp" +#include "xo/expression/ConstantInterface.hpp" /** continue after completing a VSM instruction; * achieve by jumping to continuation. **/ #define VSM_CONTINUE() this->pc_ = this->cont_; return; +/** report error and terminate VSM execution + **/ +#define VSM_ERROR(msg) report_error(msg); return; namespace xo { namespace scm { @@ -31,7 +34,15 @@ namespace xo { VsmInstr VsmOps::eval_op{VsmInstr::Opcode::eval, "eval"}; - VirtualSchematikaMachine::VirtualSchematikaMachine() + // ----- VirtualSchematikaMachineFlyweight ----- + + VirtualSchematikaMachineFlyweight::VirtualSchematikaMachineFlyweight(gc::IAlloc * mm) : + object_mm_{mm} + {} + + // ----- VirtualSchematikaMachine ----- + + VirtualSchematikaMachine::VirtualSchematikaMachine(gc::IAlloc * mm) : flyweight_{mm} {} std::pair, @@ -50,16 +61,16 @@ namespace xo { void VirtualSchematikaMachine::run() { - for (const VsmInstr * pc = pc_; pc; pc = pc_) - this->execute_one(pc); + while(pc_) + this->execute_one(); } void - VirtualSchematikaMachine::execute_one(const VsmInstr * instr) + VirtualSchematikaMachine::execute_one() { using Opcode = VsmInstr::Opcode; - switch (instr->opcode()) { + switch (pc_->opcode()) { case Opcode::halt: { @@ -104,10 +115,40 @@ namespace xo { } } + void + VirtualSchematikaMachine::report_error(const std::string & err) + { + /* error short-circuits vsm operation */ + + this->pc_ = nullptr; + this->value_ = nullptr; + this->error_ = SchematikaError(err); + this->cont_ = nullptr; + } + void VirtualSchematikaMachine::constant_op() { - this->value_ = expr_->value_tp(); + scope log(XO_DEBUG(true)); + + using xo::scm::ConstantInterface; + + bp expr = ConstantInterface::from(expr_); + + assert(expr); + + this->value_ = flyweight_.object_converter_.tp_to_object(flyweight_.object_mm_, + expr->value_tp(), + false); + if (this->value_.ptr()) { + log && log("got object: ", xtag("value", value_)); + + VSM_CONTINUE(); + } else { + VSM_ERROR(tostr("constant_op: unable to convert native value to object", + xtag("id", expr->value_tp().td()->id()), + xtag("short_name", expr->value_tp().td()->short_name()))); + } } } /*namespace scm*/ diff --git a/xo-object/include/xo/object/ObjectConverter.hpp b/xo-object/include/xo/object/ObjectConverter.hpp index 1ab2a2e5..67fe3dc0 100644 --- a/xo-object/include/xo/object/ObjectConverter.hpp +++ b/xo-object/include/xo/object/ObjectConverter.hpp @@ -12,11 +12,15 @@ namespace xo { namespace obj { + /* Convert between xo::reflect::TaggedPtr and xo::Object for + * a particular wrapped c++ type + */ struct Converter { using TaggedPtr = xo::reflect::TaggedPtr; using ConvertToObjectFn = gp (*)(gc::IAlloc *, const TaggedPtr &); public: + Converter() = default; explicit Converter(ConvertToObjectFn f) : cvt_to_object_{f} {} /** convert tagged pointer @p tp to new object, @@ -25,7 +29,7 @@ namespace xo { * Conversion will typically be for some specific type; * see @ref ObjectConverter **/ - ConvertToObjectFn cvt_to_object_; + ConvertToObjectFn cvt_to_object_ = nullptr; }; /** @class ObjectConverter @@ -48,6 +52,7 @@ namespace xo { **/ class ObjectConverter { public: + using TaggedPtr = xo::reflect::TaggedPtr; using IAlloc = xo::gc::IAlloc; /** sets up standard conversions **/ @@ -57,6 +62,12 @@ namespace xo { template void establish_conversion(Converter::ConvertToObjectFn fn); + /** 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. + * throw exception if no converter available and @p throw_flag set. + **/ + gp tp_to_object(IAlloc * mm, const TaggedPtr & tp, bool throw_flag); + /** convert @p x to object. * return converted object; if allocated, using only memory from @p mm. * return nullptr if no converter available, and @p throw_flag not set. @@ -92,19 +103,8 @@ namespace xo { using xo::reflect::TaggedPtr; TaggedPtr x_tp = Reflect::make_tp(&x); - Converter * cvt = cvt_.lookup(x_tp.td()); - if (cvt) { - 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", - xtag("id", x_tp.td()->id()), - xtag("name", x_tp.td()->short_name()))); - } - - return nullptr; - } + return tp_to_object(mm, x_tp, throw_flag); } } } diff --git a/xo-object/src/object/ObjectConverter.cpp b/xo-object/src/object/ObjectConverter.cpp index b6f4cf26..85ea6e98 100644 --- a/xo-object/src/object/ObjectConverter.cpp +++ b/xo-object/src/object/ObjectConverter.cpp @@ -27,6 +27,28 @@ namespace xo { ObjectConverter::ObjectConverter() { this->establish_conversion(&int_to_object); + this->establish_conversion(&int_to_object); + } + + gp + ObjectConverter::tp_to_object(IAlloc * mm, const TaggedPtr & x_tp, bool throw_flag) + { + using xo::reflect::Reflect; + using xo::reflect::TaggedPtr; + + const Converter * cvt = cvt_.lookup(x_tp.td()); + + if (cvt) { + 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", + xtag("id", x_tp.td()->id()), + xtag("name", x_tp.td()->short_name()))); + } + + return nullptr; + } } } } diff --git a/xo-reader/src/reader/reader.cpp b/xo-reader/src/reader/reader.cpp index 2fb59d72..6931cd5f 100644 --- a/xo-reader/src/reader/reader.cpp +++ b/xo-reader/src/reader/reader.cpp @@ -4,8 +4,8 @@ namespace xo { namespace scm { - reader::reader(bool debug_flag) - : parser_{debug_flag} + reader::reader(bool debug_flag) : + tokenizer_{debug_flag}, parser_{debug_flag} {} void @@ -118,7 +118,7 @@ namespace xo { } } - /* control here: eithero + /* control here: either * 1. input.empty (perhaps ate some whitespace, ok) * 2. missing or incomplete token (ok unless eof) */ diff --git a/xo-tokenizer/include/xo/tokenizer/input_state.hpp b/xo-tokenizer/include/xo/tokenizer/input_state.hpp index f7f7293f..8e73321f 100644 --- a/xo-tokenizer/include/xo/tokenizer/input_state.hpp +++ b/xo-tokenizer/include/xo/tokenizer/input_state.hpp @@ -177,14 +177,18 @@ namespace xo { ++eol; this->current_line_ = span_type(sol, eol); + this->current_pos_ = 0; - log && log(xtag("current_line", print::printspan(current_line_))); + log && log(xtag("current_line", print::printspan(current_line_)), + xtag("current_pos", current_pos_)); } template const CharT * input_state::skip_leading_whitespace(const span_type & input) { + scope log(XO_DEBUG(debug_flag_)); + const CharT * ix = input.lo(); if (this->current_line().is_null()) { diff --git a/xo-tokenizer/include/xo/tokenizer/tokenizer.hpp b/xo-tokenizer/include/xo/tokenizer/tokenizer.hpp index 7984126c..8d6ac215 100644 --- a/xo-tokenizer/include/xo/tokenizer/tokenizer.hpp +++ b/xo-tokenizer/include/xo/tokenizer/tokenizer.hpp @@ -166,7 +166,10 @@ namespace xo { /** @defgroup tokenizer-instance-vars tokenizer instance variables **/ ///@{ - /** track input state (line#,pos,..) for error messages **/ + /** track input state (line#,pos,..) for error messages. + * There's an ordering problem here: + * 1. input_state_.skip_leading_whitespace() advances current line when it sees newline. + **/ input_state_type input_state_; /** Accumulate partial token here. * This will happen if input sent to @ref tokenizer::scan