From 5c7b756bd1698a1cf4d0a101255465daa95af1ed Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 28 Jul 2025 13:16:09 -0400 Subject: [PATCH] xo-expression: generalize envframestack + handle explicit lm retturn --- .../include/xo/expression/Environment.hpp | 16 ++++++++ .../include/xo/expression/GlobalEnv.hpp | 10 ++++- .../include/xo/expression/LocalEnv.hpp | 37 ++++++++++++------- xo-expression/src/expression/GlobalEnv.cpp | 8 ++++ xo-expression/src/expression/LocalEnv.cpp | 15 +------- xo-jit/src/jit/MachPipeline.cpp | 2 +- xo-reader/include/xo/reader/define_xs.hpp | 1 + xo-reader/include/xo/reader/envframestack.hpp | 16 ++++---- xo-reader/include/xo/reader/parser.hpp | 6 +++ .../include/xo/reader/parserstatemachine.hpp | 6 +-- xo-reader/src/reader/envframestack.cpp | 10 ++--- xo-reader/src/reader/lambda_xs.cpp | 11 ++++-- xo-reader/src/reader/parser.cpp | 12 ++++-- xo-reader/src/reader/parserstatemachine.cpp | 32 ++++++---------- xo-reader/src/reader/reader.cpp | 4 ++ 15 files changed, 112 insertions(+), 74 deletions(-) diff --git a/xo-expression/include/xo/expression/Environment.hpp b/xo-expression/include/xo/expression/Environment.hpp index 51746d99..f3f482e4 100644 --- a/xo-expression/include/xo/expression/Environment.hpp +++ b/xo-expression/include/xo/expression/Environment.hpp @@ -6,6 +6,7 @@ #pragma once #include "xo/refcnt/Refcounted.hpp" +#include "Variable.hpp" #include "binding_path.hpp" #include "xo/indentlog/print/pretty.hpp" @@ -14,6 +15,13 @@ namespace xo { namespace scm { class Expression; + /** @class Environment + * @brief Abstract interface for tracking variable bindings + * + * When parsing (see xo-reader): rhs will always be a variable. + * When generating code (see xo-jit): rhs can be any expression, + * for example a Lambda. + **/ class Environment : public ref::Refcount { public: /** true if this is toplevel (global) environment. @@ -36,6 +44,14 @@ namespace xo { **/ virtual bp lookup_var(const std::string & vname) const = 0; + /** like @ref lookup_var but do not delegate to parent environment **/ + virtual bp lookup_local(const std::string & vname) const = 0; + + /** create/replace local variable @p target. + * Narrow use case: intended for when Environment represents a top-level session environment + **/ + virtual void upsert_local(bp target) = 0; + virtual void print(std::ostream & os) const = 0; virtual std::uint32_t pretty_print(const xo::print::ppindentinfo & ppii) const = 0; }; diff --git a/xo-expression/include/xo/expression/GlobalEnv.hpp b/xo-expression/include/xo/expression/GlobalEnv.hpp index b9cc026e..2670f929 100644 --- a/xo-expression/include/xo/expression/GlobalEnv.hpp +++ b/xo-expression/include/xo/expression/GlobalEnv.hpp @@ -14,7 +14,7 @@ namespace xo { class GlobalEnv : public Environment { public: /** create instance. Probably only need one of these **/ - static rp make() { return new GlobalEnv(); } + static rp make_empty() { return new GlobalEnv(); } bp require_global(const std::string & vname, bp expr); @@ -31,16 +31,22 @@ namespace xo { } virtual bp lookup_var(const std::string & vname) const override { + return this->lookup_local(vname); + } + + virtual bp lookup_local(const std::string & vname) const override { auto ix = global_map_.find(vname); if (ix == global_map_.end()) { /* not found */ - return bp::from_native(nullptr); + return bp::from_native(nullptr); } return ix->second; } + virtual void upsert_local(bp target) override; + virtual void print(std::ostream & os) const override; virtual std::uint32_t pretty_print(const xo::print::ppindentinfo & ppii) const override; diff --git a/xo-expression/include/xo/expression/LocalEnv.hpp b/xo-expression/include/xo/expression/LocalEnv.hpp index ea4e7e0a..dc500f37 100644 --- a/xo-expression/include/xo/expression/LocalEnv.hpp +++ b/xo-expression/include/xo/expression/LocalEnv.hpp @@ -32,6 +32,8 @@ namespace xo { /** Create instance with single local variable @ap argv1 **/ static rp make1(const rp & arg1, const rp & parent_env); + /** runtime downcast. nullptr if @p x is not a LocalEnv instance **/ + static bp from(const bp & x) { return bp::from(x); } Lambda * origin() const { return origin_; } const std::vector> & argv() const { return argv_; } @@ -53,31 +55,38 @@ namespace xo { /** single-assign this environment's parent **/ void assign_parent(bp p); - /** create/replace local variable @p target. - * Narrow use case: intended for when LocalEnv represents a top-level session environment - **/ - void upsert_local(bp target); - - bp lookup_local(const std::string & vname) const; - // ----- Environment ----- virtual bool is_global_env() const override { return false; } virtual binding_path lookup_binding(const std::string & vname) const override; - virtual bp lookup_var(const std::string & target) const override { - for (const auto & arg : argv_) { - if (arg->name() == target) - return arg; - } + virtual bp lookup_var(const std::string & vname) const override { + bp retval = this->lookup_local(vname); + + if (retval) + return retval; /* here: target not found in local vars, * delegate to innermost ancestor */ - return parent_env_->lookup_var(target); + return parent_env_->lookup_var(vname); } + virtual bp lookup_local(const std::string & vname) const override { + for (const auto & arg : argv_) { + if (arg->name() == vname) + return arg; + } + + return bp(); + } + + /** create/replace local variable @p target. + * Narrow use case: intended for when LocalEnv represents a top-level session environment. + **/ + virtual void upsert_local(bp target) override; + virtual void print(std::ostream & os) const override; virtual std::uint32_t pretty_print(const print::ppindentinfo & ppii) const override; @@ -85,7 +94,7 @@ namespace xo { LocalEnv(const std::vector> & argv, const rp & parent_env); private: - /** Lambnda for which this environment created. + /** Lambda for which this environment created. * * Invariant: * @code diff --git a/xo-expression/src/expression/GlobalEnv.cpp b/xo-expression/src/expression/GlobalEnv.cpp index 23962fd0..14e9a4e8 100644 --- a/xo-expression/src/expression/GlobalEnv.cpp +++ b/xo-expression/src/expression/GlobalEnv.cpp @@ -16,9 +16,17 @@ namespace xo { bp expr) { this->global_map_[vname] = expr.get(); + return expr; } /*require_global*/ + void + GlobalEnv::upsert_local(bp target) { + // in practice: paraphrase of .require_global() + + this->global_map_[target->name()] = target.promote(); + } + void GlobalEnv::print(std::ostream & os) const { os << " - LocalEnv::lookup_local(const std::string & vname) const { - for (const auto & var : this->argv_) { - if (var->name() == vname) - return var; - } - - return bp::from_native(nullptr); - } - void LocalEnv::assign_parent(bp p) { if ((parent_env_.get() != nullptr) && (parent_env_.get() != p.get())) { @@ -110,8 +100,7 @@ namespace xo { void LocalEnv::print(std::ostream& os) const { - os << ""; } @@ -125,8 +114,6 @@ namespace xo { if (ppii.upto()) { if (!pps->print_upto("print_upto_tag("this", (void*)this)) - return false; if (!pps->print_upto_tag("argv", argv_)) return false; pps->write(">"); diff --git a/xo-jit/src/jit/MachPipeline.cpp b/xo-jit/src/jit/MachPipeline.cpp index 462ed4a4..be1c78bf 100644 --- a/xo-jit/src/jit/MachPipeline.cpp +++ b/xo-jit/src/jit/MachPipeline.cpp @@ -75,7 +75,7 @@ namespace xo { MachPipeline::MachPipeline(std::unique_ptr jit) : jit_{std::move(jit)}, - global_env_{GlobalEnv::make()} + global_env_{GlobalEnv::make_empty()} { this->recreate_llvm_ir_pipeline(); } diff --git a/xo-reader/include/xo/reader/define_xs.hpp b/xo-reader/include/xo/reader/define_xs.hpp index 47a4712d..d10a080f 100644 --- a/xo-reader/include/xo/reader/define_xs.hpp +++ b/xo-reader/include/xo/reader/define_xs.hpp @@ -79,6 +79,7 @@ namespace xo { static void start(parserstatemachine * p_psm); defexprstatetype defxs_type() const { return defxs_type_; } + bp lhs_variable() const { return def_expr_->lhs_variable(); } /** @return expected input in current state **/ virtual const char * get_expect_str() const override; diff --git a/xo-reader/include/xo/reader/envframestack.hpp b/xo-reader/include/xo/reader/envframestack.hpp index 81de3ec3..56063924 100644 --- a/xo-reader/include/xo/reader/envframestack.hpp +++ b/xo-reader/include/xo/reader/envframestack.hpp @@ -14,8 +14,6 @@ namespace xo { **/ class envframestack { public: - using LocalEnv = xo::scm::LocalEnv; - using Variable = xo::scm::Variable; using ppstate = xo::print::ppstate; using ppindentinfo = xo::print::ppindentinfo; @@ -29,23 +27,23 @@ namespace xo { * Visit frames in fifo order, report first match; * nullptr if no matches. **/ - bp lookup(const std::string & x) const; + bp lookup(const std::string & x) const; /** update/replace binding for variable @p target. * New binding may have a different type. **/ void upsert(bp target); - bp top_envframe() const; - void push_envframe(const rp & x); - rp pop_envframe(); + bp top_envframe() const; + void push_envframe(const rp & x); + rp pop_envframe(); void reset_to_toplevel() { stack_.resize(1); } /** relative to top-of-stack. * 0 -> top (last in), z-1 -> bottom (first in) **/ - bp operator[](std::size_t i) { + bp operator[](std::size_t i) { std::size_t z = stack_.size(); assert(i < z); @@ -53,7 +51,7 @@ namespace xo { return stack_[z - i - 1].get(); } - bp operator[](std::size_t i) const { + bp operator[](std::size_t i) const { std::size_t z = stack_.size(); assert(i < z); @@ -65,7 +63,7 @@ namespace xo { bool pretty_print(const ppindentinfo & ppii) const; private: - std::vector> stack_; + std::vector> stack_; }; inline std::ostream & diff --git a/xo-reader/include/xo/reader/parser.hpp b/xo-reader/include/xo/reader/parser.hpp index 6f6b459b..d13f36ed 100644 --- a/xo-reader/include/xo/reader/parser.hpp +++ b/xo-reader/include/xo/reader/parser.hpp @@ -219,6 +219,12 @@ namespace xo { **/ const parser_result & include_token(const token_type & tk); + /** reset parsed result expression; use using return value from + * @ref include_token. Complicating api here to avoid copying parser_result + * on each token + **/ + void reset_result(); + /** reset to starting parsing state. * use this after encountering an error, to avoid cascade of * spurious secondary errors.. particularly important when diff --git a/xo-reader/include/xo/reader/parserstatemachine.hpp b/xo-reader/include/xo/reader/parserstatemachine.hpp index cecf5c95..76c4e03c 100644 --- a/xo-reader/include/xo/reader/parserstatemachine.hpp +++ b/xo-reader/include/xo/reader/parserstatemachine.hpp @@ -59,13 +59,13 @@ namespace xo { void upsert_var(bp x); /** @return available variable bindings in current parsing state **/ - bp top_envframe() const; + bp top_envframe() const; /** @return frame @p i levels from the top **/ - bp lookup_envframe(std::size_t i) const; + bp lookup_envframe(std::size_t i) const; /** push frame @p x (with new variable bindings) onto environment stack **/ void push_envframe(const rp & x); /** @return pop innermost environment frame and return it **/ - rp pop_envframe(); + rp pop_envframe(); /** @return number of stacked environment frames **/ size_t env_stack_size() const { return env_stack_.size(); } diff --git a/xo-reader/src/reader/envframestack.cpp b/xo-reader/src/reader/envframestack.cpp index 3dff60da..350bab48 100644 --- a/xo-reader/src/reader/envframestack.cpp +++ b/xo-reader/src/reader/envframestack.cpp @@ -12,7 +12,7 @@ namespace xo { using xo::scm::Variable; namespace scm { - bp + bp envframestack::top_envframe() const { std::size_t z = stack_.size(); @@ -25,7 +25,7 @@ namespace xo { } void - envframestack::push_envframe(const rp & frame) + envframestack::push_envframe(const rp & frame) { constexpr bool c_debug_flag = true; scope log(XO_DEBUG(c_debug_flag), @@ -38,7 +38,7 @@ namespace xo { stack_[z] = frame; } - rp + rp envframestack::pop_envframe() { constexpr bool c_debug_flag = true; scope log(XO_DEBUG(c_debug_flag)); @@ -48,7 +48,7 @@ namespace xo { if (z > 0) { //std::unique_ptr top = std::move(stack_[z-1]); - rp retval = stack_.at(z-1); + rp retval = stack_.at(z-1); stack_.resize(z-1); @@ -58,7 +58,7 @@ namespace xo { } } - bp + bp envframestack::lookup(const std::string & x) const { for (std::size_t i = 0, z = this->size(); i < z; ++i) { const auto & frame = (*this)[i]; diff --git a/xo-reader/src/reader/lambda_xs.cpp b/xo-reader/src/reader/lambda_xs.cpp index c2a2ea0f..89e2f972 100644 --- a/xo-reader/src/reader/lambda_xs.cpp +++ b/xo-reader/src/reader/lambda_xs.cpp @@ -156,6 +156,7 @@ namespace xo { parserstatemachine * p_psm) { constexpr const char * c_self_name = "lambda_xs::on_typedescr"; + scope log(XO_DEBUG(p_psm->debug_flag())); assert(td); @@ -189,13 +190,17 @@ namespace xo { && (p_psm->env_stack_size() >= 2) ) { - bp def_env = p_psm->lookup_envframe(1); + const define_xs * def_xs = dynamic_cast(&(p_psm->lookup_exprstate(2))); - assert(def_env->n_arg() == 1); + assert(def_xs); - bp def_var = def_env->lookup_arg(0).get(); + bp def_var = def_xs->lhs_variable(); if (def_var->valuetype() == nullptr) { + log && log("assign discovered lambda type T to enclosing define", + xtag("lhs", def_var), + xtag("T", print::unq(this->lambda_td_->canonical_name()))); + def_var->assign_valuetype(lambda_td_); } else { /* don't need to unify here. if def already hasa a type, diff --git a/xo-reader/src/reader/parser.cpp b/xo-reader/src/reader/parser.cpp index e06eb4d0..41eeae96 100644 --- a/xo-reader/src/reader/parser.cpp +++ b/xo-reader/src/reader/parser.cpp @@ -11,8 +11,8 @@ #include "xo/expression/DefineExpr.hpp" #include "xo/expression/Constant.hpp" #include "xo/expression/ConvertExpr.hpp" -//#include "xo/expression/GlobalEnv.hpp" -#include "xo/expression/LocalEnv.hpp" +#include "xo/expression/GlobalEnv.hpp" +//#include "xo/expression/LocalEnv.hpp" //#include #include @@ -28,7 +28,7 @@ namespace xo { : psm_{debug_flag} { /* top-level environment. initially empty */ - rp toplevel_env = LocalEnv::make_empty(); + rp toplevel_env = GlobalEnv::make_empty(); this->psm_.env_stack_.push_envframe(toplevel_env); } @@ -70,6 +70,12 @@ namespace xo { return psm_.result_; } /*include_token*/ + void + parser::reset_result() + { + psm_.result_ = parser_result::none(); + } + void parser::reset_to_idle_toplevel() { diff --git a/xo-reader/src/reader/parserstatemachine.cpp b/xo-reader/src/reader/parserstatemachine.cpp index a7c97f79..f85a0ae0 100644 --- a/xo-reader/src/reader/parserstatemachine.cpp +++ b/xo-reader/src/reader/parserstatemachine.cpp @@ -17,7 +17,7 @@ namespace xo { namespace scm { bp parserstatemachine::lookup_var(const std::string & x) const { - return env_stack_.lookup(x); + return Variable::from(env_stack_.lookup(x)); } void @@ -45,30 +45,28 @@ namespace xo { xs_stack_.push_exprstate(std::move(x)); } - bp + bp parserstatemachine::top_envframe() const { return env_stack_.top_envframe(); } - bp + bp parserstatemachine::lookup_envframe(std::size_t i) const { return env_stack_[i]; } void parserstatemachine::push_envframe(const rp & x) { - constexpr bool c_debug_flag = true; - scope log(XO_DEBUG(c_debug_flag)); + scope log(XO_DEBUG(debug_flag_)); log && log(xtag("frame", x)); env_stack_.push_envframe(x); } - rp + rp parserstatemachine::pop_envframe() { - constexpr bool c_debug_flag = true; - scope log(XO_DEBUG(c_debug_flag)); + scope log(XO_DEBUG(debug_flag_)); return env_stack_.pop_envframe(); } @@ -76,8 +74,7 @@ namespace xo { void parserstatemachine::on_expr(bp x) { - constexpr bool c_debug_flag = true; - scope log(XO_DEBUG(c_debug_flag)); + scope log(XO_DEBUG(debug_flag_)); log && log(xtag("x", x), xtag("psm", this)); @@ -88,8 +85,7 @@ namespace xo { void parserstatemachine::on_expr_with_semicolon(bp x) { - constexpr bool c_debug_flag = true; - scope log(XO_DEBUG(c_debug_flag)); + scope log(XO_DEBUG(debug_flag_)); log && log(xtag("x", x), xtag("psm", this)); @@ -102,8 +98,7 @@ namespace xo { void parserstatemachine::on_symbol(const std::string & x) { - constexpr bool c_debug_flag = true; - scope log(XO_DEBUG(c_debug_flag)); + scope log(XO_DEBUG(debug_flag_)); log && log(xtag("x", x), xtag("psm", this)); @@ -114,8 +109,7 @@ namespace xo { void parserstatemachine::on_semicolon_token(const token_type & tk) { - constexpr bool c_debug_flag = true; - scope log(XO_DEBUG(c_debug_flag)); + scope log(XO_DEBUG(debug_flag_)); log && log(xtag("tk", tk), xtag("psm", this)); @@ -126,8 +120,7 @@ namespace xo { void parserstatemachine::on_operator_token(const token_type & tk) { - constexpr bool c_debug_flag = true; - scope log(XO_DEBUG(c_debug_flag)); + scope log(XO_DEBUG(debug_flag_)); log && log(xtag("tk", tk), xtag("psm", this)); @@ -138,8 +131,7 @@ namespace xo { void parserstatemachine::on_leftbrace_token(const token_type & tk) { - constexpr bool c_debug_flag = true; - scope log(XO_DEBUG(c_debug_flag)); + scope log(XO_DEBUG(debug_flag_)); log && log(xtag("tk", tk), xtag("psm", this)); diff --git a/xo-reader/src/reader/reader.cpp b/xo-reader/src/reader/reader.cpp index 37a64d6b..2a615fd9 100644 --- a/xo-reader/src/reader/reader.cpp +++ b/xo-reader/src/reader/reader.cpp @@ -63,6 +63,10 @@ namespace xo { log && log(xtag("outcome", "victory!"), xtag("expr", parser_result.result_expr())); + rp result_expr = parser_result.result_expr(); + + this->parser_.reset_result(); + /* token completes an expression -> victory */ return reader_result(parser_result.result_expr(), expr_span, parser_.stack_size(), reader_error());