From f164a3ab67725df5db8e0ae7e6196cbf322a8116 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 6 Jul 2025 14:13:44 -0500 Subject: [PATCH] xo-reader xo-expression: nested lambdas working properly + docs --- include/xo/expression/Apply.hpp | 7 +-- include/xo/expression/Environment.hpp | 8 +++ include/xo/expression/GlobalEnv.hpp | 9 ++- include/xo/expression/Lambda.hpp | 21 +++++-- include/xo/expression/LocalEnv.hpp | 27 ++++++--- include/xo/expression/Variable.hpp | 5 ++ src/expression/Apply.cpp | 8 +++ src/expression/CMakeLists.txt | 1 + src/expression/GlobalEnv.cpp | 26 ++++++++ src/expression/Lambda.cpp | 28 +++++---- src/expression/LocalEnv.cpp | 85 +++++++++++++++++++++++++-- src/expression/Variable.cpp | 12 ++++ 12 files changed, 198 insertions(+), 39 deletions(-) create mode 100644 src/expression/GlobalEnv.cpp diff --git a/include/xo/expression/Apply.hpp b/include/xo/expression/Apply.hpp index 465b0ca0..00e418da 100644 --- a/include/xo/expression/Apply.hpp +++ b/include/xo/expression/Apply.hpp @@ -98,12 +98,7 @@ namespace xo { return xform_fn(this); } - virtual void attach_envs(bp p) override { - fn_->attach_envs(p); - - for (const auto & arg : argv_) - arg->attach_envs(p); - } + virtual void attach_envs(bp p) override; virtual void display(std::ostream & os) const override; diff --git a/include/xo/expression/Environment.hpp b/include/xo/expression/Environment.hpp index f846196f..cd5ee5af 100644 --- a/include/xo/expression/Environment.hpp +++ b/include/xo/expression/Environment.hpp @@ -33,7 +33,15 @@ namespace xo { * returns llvm::Value representing code that produces a value for vname **/ virtual bp lookup_var(const std::string & vname) const = 0; + + virtual void print(std::ostream & os) const = 0; }; + + inline std::ostream & + operator<< (std::ostream & os, const Environment & x) { + x.print(os); + return os; + } } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/GlobalEnv.hpp b/include/xo/expression/GlobalEnv.hpp index 4e10d53e..e05e88ff 100644 --- a/include/xo/expression/GlobalEnv.hpp +++ b/include/xo/expression/GlobalEnv.hpp @@ -17,10 +17,7 @@ namespace xo { static rp make() { return new GlobalEnv(); } bp require_global(const std::string & vname, - bp expr) { - global_map_[vname] = expr.get(); - return expr; - } /*require_global*/ + bp expr); // ----- Environment ----- @@ -44,8 +41,10 @@ namespace xo { return ix->second; } + virtual void print(std::ostream & os) const override; + private: - GlobalEnv() = default; + GlobalEnv(); private: /* for assignable globals, need to allocate memory diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 4dd120bb..41a5fdce 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -26,10 +26,21 @@ namespace xo { * @p name Name for this lambda -- must be unique * @p argv Formal parameters, in left-to-right order * @p body Expression for body of this function + * @p parent_env Environment for enclosing lexical scope **/ static rp make(const std::string & name, const std::vector> & argv, - const rp & body); + const rp & body, + const rp & parent_env); + + /** + * @p name Name for this lambda -- must be unique + * @p env Environment with {name,type} for each formal parameter + * @p body Expression for body of function + **/ + static rp make_from_env(const std::string & name, + const rp & env, + const rp & body); /** downcast from Expression **/ static bp from(bp x) { @@ -174,16 +185,18 @@ namespace xo { inline rp make_lambda(const std::string & name, const std::vector> & argv, - const rp & body) + const rp & body, + const rp & parent_env) { - return Lambda::make(name, argv, body); + return Lambda::make(name, argv, body, parent_env); } class LambdaAccess : public Lambda { public: static rp make(const std::string & name, const std::vector> & argv, - const rp & body); + const rp & body, + const rp & parent_env); static rp make_empty(); /** assign body + compute derived members diff --git a/include/xo/expression/LocalEnv.hpp b/include/xo/expression/LocalEnv.hpp index 8c02f623..a66efa49 100644 --- a/include/xo/expression/LocalEnv.hpp +++ b/include/xo/expression/LocalEnv.hpp @@ -25,10 +25,13 @@ namespace xo { using TypeDescr = xo::reflect::TypeDescr; public: + static rp make_empty(); /** named ctor idiom. Create instance with local variables per @p argv **/ - static rp make(const std::vector> & argv) { - return new LocalEnv(argv); - } + static rp make(const std::vector> & argv, + const rp & parent_env); + /** Create instance with single local variable @ap argv1 **/ + static rp make1(const rp & arg1, + const rp & parent_env); Lambda * origin() const { return origin_; } const std::vector> & argv() const { return argv_; } @@ -48,10 +51,14 @@ namespace xo { } /** single-assign this environment's parent **/ - void assign_parent(bp p) { - assert(parent_env_.get() == nullptr); - parent_env_ = p.get(); - } + 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 ----- @@ -71,9 +78,10 @@ namespace xo { return parent_env_->lookup_var(target); } + virtual void print(std::ostream & os) const override; + private: - LocalEnv(const std::vector> & argv) - : origin_{nullptr}, argv_(argv) {} + LocalEnv(const std::vector> & argv, const rp & parent_env); private: /** Lambnda for which this environment created. @@ -93,6 +101,7 @@ namespace xo { **/ rp parent_env_; }; + } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/Variable.hpp b/include/xo/expression/Variable.hpp index 2d602b21..9228660a 100644 --- a/include/xo/expression/Variable.hpp +++ b/include/xo/expression/Variable.hpp @@ -16,6 +16,11 @@ namespace xo { **/ class Variable : public Expression { public: + /** Generate unique symbol-name beginning with @p prefix. + * Relies on static counter + **/ + static std::string gensym(const std::string & prefix); + /** create expression representing a variable * identified by @p name, that can take on values * described by @p var_type. diff --git a/src/expression/Apply.cpp b/src/expression/Apply.cpp index 497565b1..631a2693 100644 --- a/src/expression/Apply.cpp +++ b/src/expression/Apply.cpp @@ -58,6 +58,14 @@ namespace xo { {lhs, rhs}); } + void + Apply::attach_envs(bp p) { + fn_->attach_envs(p); + + for (const auto & arg : argv_) + arg->attach_envs(p); + } + void Apply::display(std::ostream & os) const { os << " + GlobalEnv::require_global(const std::string & vname, + bp expr) + { + this->global_map_[vname] = expr.get(); + return expr; + } /*require_global*/ + + void + GlobalEnv::print(std::ostream & os) const { + os << ""; + } + } /*namespace ast*/ +} /*namespace xo*/ diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index 7c5875e2..67cfd545 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -66,15 +66,11 @@ namespace xo { } rp - Lambda::make(const std::string & name, - const std::vector> & argv, - const rp & body) + Lambda::make_from_env(const std::string & name, + const rp & env, + const rp & body) { - using xo::reflect::FunctionTdx; - - rp env = LocalEnv::make(argv); - - TypeDescr lambda_td = assemble_lambda_td(argv, body); + TypeDescr lambda_td = assemble_lambda_td(env->argv(), body); rp retval = new Lambda(name, @@ -86,6 +82,17 @@ namespace xo { env->assign_origin(retval.get()); return retval; + } + + rp + Lambda::make(const std::string & name, + const std::vector> & argv, + const rp & body, + const rp & parent_env) + { + rp env = LocalEnv::make(argv, parent_env); + + return make_from_env(name, env, body); } /*make*/ std::set @@ -315,10 +322,11 @@ namespace xo { rp LambdaAccess::make(const std::string & name, const std::vector> & argv, - const rp & body) + const rp & body, + const rp & parent_env) { TypeDescr lambda_td = assemble_lambda_td(argv, body); - rp env = LocalEnv::make(argv); + rp env = LocalEnv::make(argv, parent_env); rp retval = new LambdaAccess(name, diff --git a/src/expression/LocalEnv.cpp b/src/expression/LocalEnv.cpp index 8b834278..1b27e577 100644 --- a/src/expression/LocalEnv.cpp +++ b/src/expression/LocalEnv.cpp @@ -4,12 +4,43 @@ */ #include "LocalEnv.hpp" +#include "xo/indentlog/print/vector.hpp" namespace xo { namespace ast { - binding_path - LocalEnv::lookup_local_binding(const std::string & vname) const + rp + LocalEnv::make_empty() { + return new LocalEnv(std::vector>(), nullptr); + } + + rp + LocalEnv::make(const std::vector> & argv, + const rp & parent_env) { + return new LocalEnv(argv, parent_env); + } + + rp + LocalEnv::make1(const rp & arg1, + const rp & parent_env) + { + std::vector> argv = { arg1 }; + + return make(argv, parent_env); + } + + LocalEnv::LocalEnv(const std::vector> & argv, + const rp & parent_env) + : origin_{nullptr}, + argv_(argv), + parent_env_{parent_env} + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag), xtag("this", (void*)this), xtag("argv", argv_)); + } + + binding_path + LocalEnv::lookup_local_binding(const std::string & vname) const { int j_slot = 0; for (const auto & arg : argv_) { if (arg->name() == vname) @@ -21,11 +52,9 @@ namespace xo { } /*lookup_local_binding*/ binding_path - LocalEnv::lookup_binding(const std::string & vname) const - { + LocalEnv::lookup_binding(const std::string & vname) const { { auto local = this->lookup_local_binding(vname); - if (local.i_link_ == 0) return local; } @@ -37,6 +66,52 @@ namespace xo { else return { free.i_link_ + 1, free.j_slot_ }; } /*lookup_binding*/ + + bp + 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())) { + throw std::runtime_error(tostr("LocalEnv::assign_parent(P2): already have established parent P1", + xtag("P1", parent_env_), + xtag("P2", p))); + + assert(false); + } + + parent_env_ = p.promote(); + } + + void + LocalEnv::upsert_local(bp target) { + for (auto & var : this->argv_) { + if (var->name() == target->name()) { + /* replace existing variable. May change its type */ + var = target.promote(); + return; + } + } + + /* control here: target not already present in this frame -> append */ + + this->argv_.push_back(target.promote()); + } + + void + LocalEnv::print(std::ostream& os) const { + os << ""; + } } /*namespace ast*/ } /*namespace xo*/ diff --git a/src/expression/Variable.cpp b/src/expression/Variable.cpp index e2fbed35..af862cfa 100644 --- a/src/expression/Variable.cpp +++ b/src/expression/Variable.cpp @@ -5,6 +5,18 @@ namespace xo { namespace ast { + std::string + Variable::gensym(const std::string & prefix) { + static std::size_t s_counter = 0; + + ++s_counter; + + char buf[32]; + snprintf(buf, sizeof(buf), "%ld", s_counter); + + return prefix + std::string(buf); + } + void Variable::attach_envs(bp e) { /** e makes accessible all enclosing lexical scopes **/