From 2d94fd51bf98907192db09dce5048197d17f2d1f Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 13 Jun 2024 15:20:36 -0400 Subject: [PATCH 01/58] xo-expression: initial commit (constant + primitive) --- .gitignore | 8 ++ CMakeLists.txt | 36 ++++++++ LICENSE | 29 +++++++ cmake/xo-bootstrap-macros.cmake | 35 ++++++++ cmake/xo_expressionConfig.cmake.in | 6 ++ example/CMakeLists.txt | 1 + example/ex1/CMakeLists.txt | 12 +++ example/ex1/ex1.cpp | 14 +++ include/xo/expression/Apply.hpp | 32 +++++++ include/xo/expression/Constant.hpp | 78 +++++++++++++++++ include/xo/expression/ConstantInterface.hpp | 41 +++++++++ include/xo/expression/Expression.hpp | 48 +++++++++++ include/xo/expression/Primitive.hpp | 89 ++++++++++++++++++++ include/xo/expression/PrimitiveInterface.hpp | 35 ++++++++ include/xo/expression/exprtype.hpp | 51 +++++++++++ src/expression/CMakeLists.txt | 14 +++ src/expression/Expression.cpp | 15 ++++ 17 files changed, 544 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100755 cmake/xo-bootstrap-macros.cmake create mode 100644 cmake/xo_expressionConfig.cmake.in create mode 100644 example/CMakeLists.txt create mode 100644 example/ex1/CMakeLists.txt create mode 100644 example/ex1/ex1.cpp create mode 100644 include/xo/expression/Apply.hpp create mode 100644 include/xo/expression/Constant.hpp create mode 100644 include/xo/expression/ConstantInterface.hpp create mode 100644 include/xo/expression/Expression.hpp create mode 100644 include/xo/expression/Primitive.hpp create mode 100644 include/xo/expression/PrimitiveInterface.hpp create mode 100644 include/xo/expression/exprtype.hpp create mode 100644 src/expression/CMakeLists.txt create mode 100644 src/expression/Expression.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f3b23fc3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# emacs projectile config +.projectile +# clangd working space (see emacs+lsp) +.cache +# typical cmake build directory (source-tree-nephew) +.build* +# symlink to builddir/compile_commands.json; should be set manually in dev sandbox +compile_commands.json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..468778de --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,36 @@ +# expression/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(xo_expression VERSION 0.1) + +include(GNUInstallDirs) +include(cmake/xo-bootstrap-macros.cmake) + +xo_cxx_toplevel_options3() + +# ---------------------------------------------------------------- +# c++ settings + +set(PROJECT_CXX_FLAGS "") +#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") # gcc-only! +add_definitions(${PROJECT_CXX_FLAGS}) + +# ---------------------------------------------------------------- + +# must complete definition of expression lib before configuring examples +add_subdirectory(src/expression) + +xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) + +# ---------------------------------------------------------------- + +add_subdirectory(example) +#add_subdirectory(utest) + +# ---------------------------------------------------------------- +# docs targets depend on all the other library/utest targets +# +#add_subdirectory(docs) + +# end CMakeLists.txt diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..cae3cb5d --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2024 Roland Conybeare , All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Please also refer to the file .github/CONTRIBUTING.md, which clarifies licensing of +external contributions to this project including patches, pull requests, etc. diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake new file mode 100755 index 00000000..aba31169 --- /dev/null +++ b/cmake/xo-bootstrap-macros.cmake @@ -0,0 +1,35 @@ +# ---------------------------------------------------------------- +# for example: +# $ PREFIX=/usr/local # for example +# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build +# +# will get +# CMAKE_MODULE_PATH +# from xo-cmake-config --cmake-module-path +# +# and expect .cmake macros in +# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake +# ---------------------------------------------------------------- + +find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED) + +if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND") + message(FATAL "could not find xo-cmake-config executable") +endif() + +message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") + +if (NOT XO_SUBMODULE_BUILD) + if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix)) + # default to typical install location for xo-project-macros + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH) + message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + endif() +endif() + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo_cxx) + +xo_cxx_bootstrap_message() diff --git a/cmake/xo_expressionConfig.cmake.in b/cmake/xo_expressionConfig.cmake.in new file mode 100644 index 00000000..e7f7f1be --- /dev/null +++ b/cmake/xo_expressionConfig.cmake.in @@ -0,0 +1,6 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(reflect) +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 00000000..4151ec21 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(ex1) diff --git a/example/ex1/CMakeLists.txt b/example/ex1/CMakeLists.txt new file mode 100644 index 00000000..d7e8b3b6 --- /dev/null +++ b/example/ex1/CMakeLists.txt @@ -0,0 +1,12 @@ +# xo-expression/example/ex1/CMakeLists.txt + +set(SELF_EXE xo_expression_ex1) +set(SELF_SRCS ex1.cpp) + +if (XO_ENABLE_EXAMPLES) + xo_add_executable(${SELF_EXE} ${SELF_SRCS}) + xo_self_dependency(${SELF_EXE} xo_expression) + xo_dependency(${SELF_EXE} refcnt) +endif() + +# end CMakeLists.txt diff --git a/example/ex1/ex1.cpp b/example/ex1/ex1.cpp new file mode 100644 index 00000000..19dddc82 --- /dev/null +++ b/example/ex1/ex1.cpp @@ -0,0 +1,14 @@ +/** @file ex1.cpp **/ + +#include "xo/expression/Constant.hpp" +#include + +int +main() { + using xo::ast::make_constant; + + auto expr = make_constant(7); +} + + +/** end ex1.cpp **/ diff --git a/include/xo/expression/Apply.hpp b/include/xo/expression/Apply.hpp new file mode 100644 index 00000000..518415f0 --- /dev/null +++ b/include/xo/expression/Apply.hpp @@ -0,0 +1,32 @@ +/** @file Apply.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "Expression.hpp" + +//#include + +namespace xo { + namespace ast { + /** @class Apply + * @brief syntax for a function call. + * + * In general we don't know function to be invoked + * until runtime, depending on the nature of Expression. + * + * For first cut, we'll just handle the case of a Constant + * that refers to a known function + **/ + class Apply : public Expression { + ref::rp fn_; + std::vector> args_; + }; + } /*namespace ast*/ + +} /*namespace xo*/ + + +/** end Apply.hpp **/ diff --git a/include/xo/expression/Constant.hpp b/include/xo/expression/Constant.hpp new file mode 100644 index 00000000..2741881f --- /dev/null +++ b/include/xo/expression/Constant.hpp @@ -0,0 +1,78 @@ +/** @file Constant.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "ConstantInterface.hpp" +#include + +namespace xo { + namespace ast { + /** @class Constant + * @brief syntax for a literal constant. + * + * Require: + * 1. T must be a POD type (plain old data) + * We need this to be true so that we can generate + * code for constructing a T instance by memcopying + * @ref value_ + * + * @tp T type of captured literal. + **/ + template + class Constant : public ConstantInterface { + public: + using Reflect = xo::reflect::Reflect; + using TaggedPtr = xo::reflect::TaggedPtr; + using TypeDescr = xo::reflect::TypeDescr; + + public: + explicit Constant(const T & x) + : ConstantInterface(exprtype::constant), + value_td_{Reflect::require()}, + value_(x) + { + static_assert(std::is_standard_layout_v && std::is_trivial_v); + } + + const T & value() const { return value_; } + + // ----- ConstantInterface ----- + + virtual TypeDescr value_td() const override { return value_td_; } + virtual TaggedPtr value_tp() const override { + /* note: idk why, but need to spell this out in two steps with gcc 13.2 */ + const void * erased_cptr = &value_; + void * erased_ptr = const_cast(erased_cptr); + + return TaggedPtr(value_td_, erased_ptr); + } + + // ----- Expression ----- + + virtual void display(std::ostream & os) const override { + os << "short_name()) + << xtag("value", value_) + << ">"; + } + + private: + /** type description for T **/ + TypeDescr value_td_; + /** value of this constant **/ + T value_; + }; /*Constant*/ + + template + ref::rp>> + make_constant(const T & x) { + return new Constant(x); + } + + } /*namespace ast*/ +} /*namespace xo*/ + +/** end Constant.hpp **/ diff --git a/include/xo/expression/ConstantInterface.hpp b/include/xo/expression/ConstantInterface.hpp new file mode 100644 index 00000000..9bd38383 --- /dev/null +++ b/include/xo/expression/ConstantInterface.hpp @@ -0,0 +1,41 @@ +/** @file ConstantInterface.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "Expression.hpp" +#include "xo/reflect/Reflect.hpp" +#include "xo/reflect/TypeDescr.hpp" +#include + +namespace xo { + namespace ast { + /** @class ConstantInterface + * @brief syntax for a literal constant. + **/ + class ConstantInterface : public Expression { + public: + using TaggedPtr = xo::reflect::TaggedPtr; + using TypeDescr = xo::reflect::TypeDescr; + + public: + /** @p extype sets expression-type; could be constant|primitive **/ + ConstantInterface(exprtype extype) : Expression{extype} {} + + /** downcast from Expression **/ + static ref::brw from(ref::brw x) { + return ref::brw::from(x); + } + + /** type description for representation of literal value **/ + virtual TypeDescr value_td() const = 0; + /** reflection-tagged pointer to literal value of this constant **/ + virtual TaggedPtr value_tp() const = 0; + }; /*ConstantInterface*/ + + } /*namespace ast*/ +} /*namespace xo*/ + +/** end ConstantInterface.hpp **/ diff --git a/include/xo/expression/Expression.hpp b/include/xo/expression/Expression.hpp new file mode 100644 index 00000000..cd23df53 --- /dev/null +++ b/include/xo/expression/Expression.hpp @@ -0,0 +1,48 @@ +/** @file Expression.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "xo/refcnt/Refcounted.hpp" +#include "exprtype.hpp" + +namespace xo { + namespace ast { + /** @class Expression + * @brief abstract syntax tree for an EGAD program + * + * (Expression Graph with Automagic Derivation) + * + * Things you can do with an Expression: + * - evaluate it using an interpreter + * - execute it on a VM + * - compile using LLVM + * see xo-jit/ + **/ + class Expression : public ref::Refcount { + public: + explicit Expression(exprtype extype) : extype_{extype} {} + + exprtype extype() const { return extype_; } + + /** write human-readable representation to stream **/ + virtual void display(std::ostream & os) const = 0; + /** human-readable string representation **/ + virtual std::string display_string() const; + + private: + /** expression type (constant | apply | ..) for this expression **/ + exprtype extype_ = exprtype::invalid; + }; /*Expression*/ + + inline std::ostream & + operator<<(std::ostream & os, const Expression & x) { + x.display(os); + return os; + } + } /*namespace ast*/ +} /*namespace xo*/ + +/** end Expression.hpp **/ diff --git a/include/xo/expression/Primitive.hpp b/include/xo/expression/Primitive.hpp new file mode 100644 index 00000000..e0de42d7 --- /dev/null +++ b/include/xo/expression/Primitive.hpp @@ -0,0 +1,89 @@ +/** @file Primitive.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "PrimitiveInterface.hpp" +//#include + +namespace xo { + namespace ast { + /** @class Primitive + * @brief syntax for a constant that refers to a known function. + * + * Two cases here: + * 1. primitive refers to a function that is supported directly in llvm + * (e.g. floating-point addition) + * 2. primitive refers to a compiled (C/C++) function that we can invoke at runtime + * + * In any case, a primitive serves as both declaration and definition + * (May be possible to relax this to declaration-only using null value_ as sentinel..?) + **/ + template + class Primitive: public PrimitiveInterface { + public: + using Reflect = xo::reflect::Reflect; + using TaggedPtr = xo::reflect::TaggedPtr; + using TypeDescr = xo::reflect::TypeDescr; + + public: + Primitive(const std::string & name, + FunctionPointer fnptr) + : PrimitiveInterface(), + name_{name}, + value_td_{Reflect::require()}, + value_{fnptr} + {} + + FunctionPointer value() const { return value_; } + + // ----- PrimitiveInterface ----- + + virtual std::string const & name() const { return name_; } + + // ----- ConstantInterface ----- + + virtual TypeDescr value_td() const override { return value_td_; } + virtual TaggedPtr value_tp() const override { + /* note: idk why, but need to spell this out in two steps with gcc 13.2 */ + const void * erased_cptr = &value_; + void * erased_ptr = const_cast(erased_cptr); + + return TaggedPtr(value_td_, erased_ptr); + } + + // ----- Expression ----- + + virtual void display(std::ostream & os) const override { + os << "value_td()->short_name()) + << xtag("value", this->value()) + << ">"; + } + + private: + // from Expression: + // exprtype extype_ + + /** name of this primitive, e.g. '+', 'sqrt' **/ + std::string name_; + /** type description for FunctionPointer **/ + TypeDescr value_td_; + /** address of executable function **/ + FunctionPointer value_; + }; /*Primitive*/ + + /** adopt function @p x as a callable primitive function named @p name **/ + template + ref::rp> + make_primitive(const std::string & name, FunctionPointer x) { + return new Primitive(name, x); + } + } /*namespace ast*/ +} /*namespace xo*/ + + +/** end Primitive.hpp **/ diff --git a/include/xo/expression/PrimitiveInterface.hpp b/include/xo/expression/PrimitiveInterface.hpp new file mode 100644 index 00000000..54e51451 --- /dev/null +++ b/include/xo/expression/PrimitiveInterface.hpp @@ -0,0 +1,35 @@ +/** @file PrimitiveInterface.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "ConstantInterface.hpp" + +//#include + +#include +namespace xo { + namespace ast { + class PrimitiveInterface : public ConstantInterface { + public: + PrimitiveInterface() : ConstantInterface(exprtype::primitive) {} + + /** downcast from Expression **/ + static ref::brw from(ref::brw x) { + return ref::brw::from(x); + } + + virtual const std::string & name() const = 0; + + //virtual TypeDescr value_td() const override = 0; + //virtual TaggedPtr value_tp() const override = 0; + + private: + }; /*PrimitiveInterface*/ + } /*namespace ast*/ +} /*namespace xo*/ + + +/** end PrimitiveInterface.hpp **/ diff --git a/include/xo/expression/exprtype.hpp b/include/xo/expression/exprtype.hpp new file mode 100644 index 00000000..fe810bb7 --- /dev/null +++ b/include/xo/expression/exprtype.hpp @@ -0,0 +1,51 @@ +/** @file exprtype.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include + +namespace xo { + namespace ast { + /** @enum exprtype + * @brief enum to identify subclasses of xo::ast::Expression. + * + **/ + enum class exprtype { + /** sentinel value **/ + invalid = -1, + + /** literal constant. must satisfy both standard_layout_type + trivial **/ + constant, + /** a literal constant that refers to a linkable named function **/ + primitive, + /** function call **/ + apply, + + /** not an expression. comes last, counts entries **/ + n_expr + }; + + inline const char * + expr2str(exprtype x) + { + switch(x) { + case exprtype::invalid: return "?exprtype"; + case exprtype::constant: return "constant"; + case exprtype::primitive: return "primitive"; + case exprtype::apply: return "apply"; + default: break; + } + + return "???exprtype???"; + } + + /** @brief number of built-in expression types, repr convenient for array sizing **/ + static constexpr std::size_t n_exprtype = static_cast(exprtype::n_expr); + } /*namespace ast*/ +} /*namespace xo*/ + + +/** end exprtype.hpp **/ diff --git a/src/expression/CMakeLists.txt b/src/expression/CMakeLists.txt new file mode 100644 index 00000000..94e74d0c --- /dev/null +++ b/src/expression/CMakeLists.txt @@ -0,0 +1,14 @@ +# expression/CMakeLists.txt + +set(SELF_LIB xo_expression) +set(SELF_SRCS + Expression.cpp + #init_reflect.cpp +) + +xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) +xo_dependency(${SELF_LIB} reflect) +#xo_dependency(${SELF_LIB} indentlog) +#xo_dependency(${SELF_LIB} subsys) + +# end CMakeLists.txt diff --git a/src/expression/Expression.cpp b/src/expression/Expression.cpp new file mode 100644 index 00000000..ecebe8b4 --- /dev/null +++ b/src/expression/Expression.cpp @@ -0,0 +1,15 @@ +/* @file Expression.cpp */ + +#include "Expression.hpp" + +namespace xo { + namespace ast { + std::string + Expression::display_string() const { + return tostr(*this); + } + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end Expression.cpp */ From d6371ee3694a37aeea9f6b8f2e91d23e87d409f8 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 13 Jun 2024 16:20:53 -0400 Subject: [PATCH 02/58] xo-expression: + Apply expressions --- include/xo/expression/Apply.hpp | 40 ++++++++++++++++---- include/xo/expression/Primitive.hpp | 2 + include/xo/expression/PrimitiveInterface.hpp | 1 + include/xo/expression/exprtype.hpp | 9 +++++ src/expression/Apply.cpp | 19 ++++++++++ src/expression/CMakeLists.txt | 1 + 6 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 src/expression/Apply.cpp diff --git a/include/xo/expression/Apply.hpp b/include/xo/expression/Apply.hpp index 518415f0..56f6b886 100644 --- a/include/xo/expression/Apply.hpp +++ b/include/xo/expression/Apply.hpp @@ -11,21 +11,47 @@ namespace xo { namespace ast { + /** @class Apply * @brief syntax for a function call. * * In general we don't know function to be invoked * until runtime, depending on the nature of Expression. - * - * For first cut, we'll just handle the case of a Constant - * that refers to a known function **/ class Apply : public Expression { - ref::rp fn_; - std::vector> args_; - }; - } /*namespace ast*/ + public: + Apply(const ref::rp & fn, + const std::vector> & argv) + : Expression(exprtype::apply), fn_{fn}, argv_(argv) + {} + /** downcast from Expression **/ + static ref::brw from(ref::brw x) { + return ref::brw::from(x); + } + + const ref::rp & fn() const { return fn_; } + const std::vector> & argv() const { return argv_; } + + virtual void display(std::ostream & os) const; + + private: + /** function to invoke **/ + ref::rp fn_; + /** argument expressions, in l-to-r order **/ + std::vector> argv_; + }; /*Apply*/ + + inline ref::rp + make_apply(const ref::rp & fn, + const ref::rp & arg1) { + std::vector> argv; + argv.push_back(arg1); + + return new Apply(fn, argv); + } /*make_apply*/ + + } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/Primitive.hpp b/include/xo/expression/Primitive.hpp index e0de42d7..afae94a9 100644 --- a/include/xo/expression/Primitive.hpp +++ b/include/xo/expression/Primitive.hpp @@ -42,6 +42,8 @@ namespace xo { // ----- PrimitiveInterface ----- virtual std::string const & name() const { return name_; } + /** FIXME for now **/ + virtual int n_arg() const { return 1; } // ----- ConstantInterface ----- diff --git a/include/xo/expression/PrimitiveInterface.hpp b/include/xo/expression/PrimitiveInterface.hpp index 54e51451..c294a440 100644 --- a/include/xo/expression/PrimitiveInterface.hpp +++ b/include/xo/expression/PrimitiveInterface.hpp @@ -22,6 +22,7 @@ namespace xo { } virtual const std::string & name() const = 0; + virtual int n_arg() const = 0; //virtual TypeDescr value_td() const override = 0; //virtual TaggedPtr value_tp() const override = 0; diff --git a/include/xo/expression/exprtype.hpp b/include/xo/expression/exprtype.hpp index fe810bb7..1988f7e9 100644 --- a/include/xo/expression/exprtype.hpp +++ b/include/xo/expression/exprtype.hpp @@ -5,6 +5,7 @@ #pragma once +#include #include namespace xo { @@ -44,6 +45,14 @@ namespace xo { /** @brief number of built-in expression types, repr convenient for array sizing **/ static constexpr std::size_t n_exprtype = static_cast(exprtype::n_expr); + + inline std::ostream & + operator<<(std::ostream & os, + exprtype x) + { + os << expr2str(x); + return os; + } } /*namespace ast*/ } /*namespace xo*/ diff --git a/src/expression/Apply.cpp b/src/expression/Apply.cpp new file mode 100644 index 00000000..781ec170 --- /dev/null +++ b/src/expression/Apply.cpp @@ -0,0 +1,19 @@ +/* @file Apply.cpp */ + +#include "Apply.hpp" +#include "xo/indentlog/print/vector.hpp" + +namespace xo { + namespace ast { + void + Apply::display(std::ostream & os) const { + os << ""; + } + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end Apply.cpp */ diff --git a/src/expression/CMakeLists.txt b/src/expression/CMakeLists.txt index 94e74d0c..59e754a5 100644 --- a/src/expression/CMakeLists.txt +++ b/src/expression/CMakeLists.txt @@ -3,6 +3,7 @@ set(SELF_LIB xo_expression) set(SELF_SRCS Expression.cpp + Apply.cpp #init_reflect.cpp ) From 20b23b50fc7c584f386361e0d794b3a9bc5f3b80 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 13 Jun 2024 17:05:38 -0400 Subject: [PATCH 03/58] xo-expression: + Lambda (function definitions) --- include/xo/expression/Lambda.hpp | 44 ++++++++++++++++++++++++++++++ include/xo/expression/exprtype.hpp | 3 ++ src/expression/CMakeLists.txt | 1 + src/expression/Lambda.cpp | 19 +++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 include/xo/expression/Lambda.hpp create mode 100644 src/expression/Lambda.cpp diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp new file mode 100644 index 00000000..581ba274 --- /dev/null +++ b/include/xo/expression/Lambda.hpp @@ -0,0 +1,44 @@ +/** @file Lambda.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "Expression.hpp" +#include +#include +//#include + +namespace xo { + namespace ast { + /** @class Lambda + * @brief abstract syntax tree for a function definition + * + **/ + class Lambda : public Expression { + public: + /** @p argv Formal parameters, in left-to-right order + * @p body Expression for body of this function + **/ + Lambda(const std::vector & argv, + const ref::rp & body) + : Expression(exprtype::lambda), argv_{argv}, body_{body} {} + + const std::vector & argv() const { return argv_; } + const ref::rp & body() const { return body_; } + + // ----- Expression ----- + + virtual void display(std::ostream & os) const override; + + private: + /** formal argument names **/ + std::vector argv_; + /** function body **/ + ref::rp body_; + }; /*Lambda*/ + } /*namespace ast*/ +} /*namespace xo*/ + +/** end Lambda.hpp **/ diff --git a/include/xo/expression/exprtype.hpp b/include/xo/expression/exprtype.hpp index 1988f7e9..7afa8ff3 100644 --- a/include/xo/expression/exprtype.hpp +++ b/include/xo/expression/exprtype.hpp @@ -24,6 +24,8 @@ namespace xo { primitive, /** function call **/ apply, + /** function definition **/ + lambda, /** not an expression. comes last, counts entries **/ n_expr @@ -37,6 +39,7 @@ namespace xo { case exprtype::constant: return "constant"; case exprtype::primitive: return "primitive"; case exprtype::apply: return "apply"; + case exprtype::lambda: return "lambda"; default: break; } diff --git a/src/expression/CMakeLists.txt b/src/expression/CMakeLists.txt index 59e754a5..5db806b2 100644 --- a/src/expression/CMakeLists.txt +++ b/src/expression/CMakeLists.txt @@ -4,6 +4,7 @@ set(SELF_LIB xo_expression) set(SELF_SRCS Expression.cpp Apply.cpp + Lambda.cpp #init_reflect.cpp ) diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp new file mode 100644 index 00000000..d228bb2c --- /dev/null +++ b/src/expression/Lambda.cpp @@ -0,0 +1,19 @@ +/* @file Lambda.cpp */ + +#include "Lambda.hpp" +#include "xo/indentlog/print/vector.hpp" + +namespace xo { + namespace ast { + void + Lambda::display(std::ostream & os) const { + os << ""; + } /*display*/ + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end Lambda.cpp */ From 9d2b0f17f84975e51235d87514ad8c2d33e2724d Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 13 Jun 2024 17:59:51 -0400 Subject: [PATCH 04/58] xo-expression: + Variable + apply/lambda fixes --- include/xo/expression/Apply.hpp | 2 +- include/xo/expression/Lambda.hpp | 27 +++++++++++++++++-- include/xo/expression/Variable.hpp | 42 ++++++++++++++++++++++++++++++ include/xo/expression/exprtype.hpp | 3 +++ src/expression/CMakeLists.txt | 1 + src/expression/Lambda.cpp | 1 + src/expression/Variable.cpp | 17 ++++++++++++ 7 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 include/xo/expression/Variable.hpp create mode 100644 src/expression/Variable.cpp diff --git a/include/xo/expression/Apply.hpp b/include/xo/expression/Apply.hpp index 56f6b886..1f2b9a8f 100644 --- a/include/xo/expression/Apply.hpp +++ b/include/xo/expression/Apply.hpp @@ -44,7 +44,7 @@ namespace xo { inline ref::rp make_apply(const ref::rp & fn, - const ref::rp & arg1) { + const ref::rp & arg1) { std::vector> argv; argv.push_back(arg1); diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 581ba274..7f6434f8 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -21,10 +21,17 @@ namespace xo { /** @p argv Formal parameters, in left-to-right order * @p body Expression for body of this function **/ - Lambda(const std::vector & argv, + Lambda(const std::string & name, + const std::vector & argv, const ref::rp & body) - : Expression(exprtype::lambda), argv_{argv}, body_{body} {} + : Expression(exprtype::lambda), name_{name}, argv_{argv}, body_{body} {} + /** downcast from Expression **/ + static ref::brw from(ref::brw x) { + return ref::brw::from(x); + } + + const std::string & name() const { return name_; } const std::vector & argv() const { return argv_; } const ref::rp & body() const { return body_; } @@ -33,11 +40,27 @@ namespace xo { virtual void display(std::ostream & os) const override; private: + /** lambda name. Initially supporting only form like + * (define (foo x y z) + * (+ (* x x) (* y y) (* z z))) + * + * In any case need to supply names for distinct things-for-which-code-is-generated + * so that they can be linked etc. + **/ + std::string name_; /** formal argument names **/ std::vector argv_; /** function body **/ ref::rp body_; }; /*Lambda*/ + + inline ref::rp + make_lambda(const std::string & name, + const std::vector & argv, + const ref::rp & body) + { + return new Lambda(name, argv, body); + } } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/Variable.hpp b/include/xo/expression/Variable.hpp new file mode 100644 index 00000000..fc565236 --- /dev/null +++ b/include/xo/expression/Variable.hpp @@ -0,0 +1,42 @@ +/** @file Variable.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "Expression.hpp" + +namespace xo { + namespace ast { + + /** @class Variable + * @brief syntax for a variable reference + **/ + class Variable : public Expression { + public: + Variable(const std::string & name) : Expression(exprtype::variable), name_{name} {} + + /** downcast from Expression **/ + static ref::brw from(ref::brw x) { + return ref::brw::from(x); + } + + const std::string & name() const { return name_; } + + virtual void display(std::ostream & os) const; + + private: + /** variable name **/ + std::string name_; + }; /*Variable*/ + + inline ref::rp + make_var(const std::string & name) { + return new Variable(name); + } + } /*namespace ast*/ +} /*namespace xo*/ + + +/** end Variable.hpp **/ diff --git a/include/xo/expression/exprtype.hpp b/include/xo/expression/exprtype.hpp index 7afa8ff3..91bcfbb5 100644 --- a/include/xo/expression/exprtype.hpp +++ b/include/xo/expression/exprtype.hpp @@ -26,6 +26,8 @@ namespace xo { apply, /** function definition **/ lambda, + /** variable reference **/ + variable, /** not an expression. comes last, counts entries **/ n_expr @@ -40,6 +42,7 @@ namespace xo { case exprtype::primitive: return "primitive"; case exprtype::apply: return "apply"; case exprtype::lambda: return "lambda"; + case exprtype::variable: return "variable"; default: break; } diff --git a/src/expression/CMakeLists.txt b/src/expression/CMakeLists.txt index 5db806b2..0ef351e1 100644 --- a/src/expression/CMakeLists.txt +++ b/src/expression/CMakeLists.txt @@ -5,6 +5,7 @@ set(SELF_SRCS Expression.cpp Apply.cpp Lambda.cpp + Variable.cpp #init_reflect.cpp ) diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index d228bb2c..87f70759 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -8,6 +8,7 @@ namespace xo { void Lambda::display(std::ostream & os) const { os << ""; diff --git a/src/expression/Variable.cpp b/src/expression/Variable.cpp new file mode 100644 index 00000000..c8ab19fc --- /dev/null +++ b/src/expression/Variable.cpp @@ -0,0 +1,17 @@ +/* @file Variable.cpp */ + +#include "Variable.hpp" + +namespace xo { + namespace ast { + void + Variable::display(std::ostream & os) const { + os << ""; + } /*display*/ + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end Variable.cpp */ From f5b08526e67b72854cca4ebb4f4675e504558a5f Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 15:35:08 -0400 Subject: [PATCH 05/58] xo-expression: + README.md --- README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..7279b3fa --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# xo-expression library + +A library for representing abstract syntax trees for EGAD (a small expression-based language). + +## Getting Started + +### build + install `xo-cmake` dependency + +- [github/Rconybea/xo-cmake](https://github.com/Rconybea/xo-cmake) + +Installs a few cmake ingredients, along with a build assistant `xo-build` for XO projects such as this one. + +### build + install other XO dependencies +``` +$ xo-build --clone --configure --build --install xo-indentlog +$ xo-build --clone --configure --build --install xo-refnct +$ xo-build --clone --configure --build --install xo-subsys +$ xo-build --clone --configure --build --install xo-reflect +``` + +Note: can use `xo-build -n` to dry-run here + +### copy `xo-expression` repository locally +``` +$ xo-build --clone xo-expression +``` + +or eqivalently +``` +$ git clone git@github.com:Rconybea/xo-expression.git +``` + +### build + install xo-expression +``` +$ xo-build --configure --build --install xo-expression +``` + +or equivalently: +``` +$ PREFIX=/usr/local # or wherever you prefer +$ cmake -DCMAKE_INSTALL_PREFIX=${PREFIX} -S xo-expression -B xo-reflect/.build +$ cmake --build xo-expression/.build +$ cmake --install xo-expression/.build +``` + +### build for unit test coverage +``` +$ cmake -DCMAKE_BUILD_TYPE=coverage -DCMAKE_INSTALL_PREFIX=$PREFIX xo-expression/.build-ccov +$ cmake --build xo-expression/.build-ccov +``` + +### LSP support +``` +$ cd xo-expression +$ ln -s build/compile_commands.json # lsp will look for compile_commands.json in the root of the source tree +``` From 2b32aa83c7a05fd665cdafc69ab9aee99e73545d Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 15:36:47 -0400 Subject: [PATCH 06/58] xo-expression: README: fix copypasta --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7279b3fa..205ba405 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ $ xo-build --configure --build --install xo-expression or equivalently: ``` $ PREFIX=/usr/local # or wherever you prefer -$ cmake -DCMAKE_INSTALL_PREFIX=${PREFIX} -S xo-expression -B xo-reflect/.build +$ cmake -DCMAKE_INSTALL_PREFIX=${PREFIX} -S xo-expression -B xo-jit/.build $ cmake --build xo-expression/.build $ cmake --install xo-expression/.build ``` From 6783cb7e8cc6823e021ab82b6ff336b87992f125 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 17 Jun 2024 12:25:12 -0400 Subject: [PATCH 07/58] xo-expression: use Reflect::require_function() for precision --- include/xo/expression/Lambda.hpp | 3 +++ include/xo/expression/Primitive.hpp | 13 ++++++++----- include/xo/expression/PrimitiveInterface.hpp | 4 +++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 7f6434f8..7f5ebab4 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -35,6 +35,9 @@ namespace xo { const std::vector & argv() const { return argv_; } const ref::rp & body() const { return body_; } + /** return number of arguments expected by this function **/ + int n_arg() const { return argv_.size(); } + // ----- Expression ----- virtual void display(std::ostream & os) const override; diff --git a/include/xo/expression/Primitive.hpp b/include/xo/expression/Primitive.hpp index afae94a9..f4fc21b7 100644 --- a/include/xo/expression/Primitive.hpp +++ b/include/xo/expression/Primitive.hpp @@ -33,17 +33,20 @@ namespace xo { FunctionPointer fnptr) : PrimitiveInterface(), name_{name}, - value_td_{Reflect::require()}, + value_td_{Reflect::require_function()}, value_{fnptr} - {} + { + if (!value_td_->is_function()) + throw std::runtime_error("Primitive: expected function pointer"); + if (!value_td_->fn_retval()) + throw std::runtime_error("Primitive: expected non-null function return value"); + } FunctionPointer value() const { return value_; } // ----- PrimitiveInterface ----- - virtual std::string const & name() const { return name_; } - /** FIXME for now **/ - virtual int n_arg() const { return 1; } + virtual std::string const & name() const override { return name_; } // ----- ConstantInterface ----- diff --git a/include/xo/expression/PrimitiveInterface.hpp b/include/xo/expression/PrimitiveInterface.hpp index c294a440..3274195f 100644 --- a/include/xo/expression/PrimitiveInterface.hpp +++ b/include/xo/expression/PrimitiveInterface.hpp @@ -22,7 +22,9 @@ namespace xo { } virtual const std::string & name() const = 0; - virtual int n_arg() const = 0; + int n_arg() const { return this->value_td()->n_fn_arg(); } + TypeDescr fn_retval() const { return this->value_td()->fn_retval(); } + TypeDescr fn_arg(uint32_t i) const { return this->value_td()->fn_arg(i); } //virtual TypeDescr value_td() const override = 0; //virtual TaggedPtr value_tp() const override = 0; From 6b0f49970ae59cf8cd42f56c1052f5bee6331b99 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 17 Jun 2024 12:25:46 -0400 Subject: [PATCH 08/58] xo-expression: ++ example to build primitive --- example/ex1/ex1.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/example/ex1/ex1.cpp b/example/ex1/ex1.cpp index 19dddc82..5c12a4b1 100644 --- a/example/ex1/ex1.cpp +++ b/example/ex1/ex1.cpp @@ -1,14 +1,34 @@ /** @file ex1.cpp **/ #include "xo/expression/Constant.hpp" +#include "xo/expression/Primitive.hpp" #include +#include int main() { using xo::ast::make_constant; + using xo::ast::make_primitive; + using std::cout; + using std::endl; - auto expr = make_constant(7); + { + auto expr = make_constant(7); + } + + { + auto expr = make_primitive("sqrt", &::sqrt); + + auto expr_td = expr->value_td(); + + cout << "expr_td: " << expr_td->short_name() << endl; + cout << "expr_td->is_function(): " << expr_td->is_function() << endl; + cout << "expr_td->fn_retval(): " << expr_td->fn_retval()->short_name() << endl; + cout << "expr_td->n_fn_arg(): " << expr_td->n_fn_arg() << endl; + for (uint32_t i = 0; i < expr_td->n_fn_arg(); ++i) + cout << "expr_td->fn_arg(" << i << "): " << expr_td->fn_arg(i)->short_name() << endl; + cout << "expr_td->fn_is_noexcept(): " << expr_td->fn_is_noexcept() << endl; + } } - /** end ex1.cpp **/ From 012597b112bf2498f63e028d1eaf1044146562d2 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 17 Jun 2024 12:26:24 -0400 Subject: [PATCH 09/58] xo-expression: + if-expressions --- include/xo/expression/IfExpr.hpp | 67 ++++++++++++++++++++++++++++++ include/xo/expression/exprtype.hpp | 3 ++ src/expression/CMakeLists.txt | 2 +- src/expression/IfExpr.cpp | 20 +++++++++ 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 include/xo/expression/IfExpr.hpp create mode 100644 src/expression/IfExpr.cpp diff --git a/include/xo/expression/IfExpr.hpp b/include/xo/expression/IfExpr.hpp new file mode 100644 index 00000000..d60ab1d3 --- /dev/null +++ b/include/xo/expression/IfExpr.hpp @@ -0,0 +1,67 @@ +/** @file IfExpr.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "Expression.hpp" +#include +#include +//#include + +namespace xo { + namespace ast { + /** @class IfExpr + * @brief abstract syntax tree for a function definition + * + **/ + class IfExpr : public Expression { + public: + /** @p test test-expression; always execute + * @p when_true then-branch; executes only when test succeeds + * @p when_false else-branch; executes only when test fails + **/ + IfExpr(const ref::rp & test, + const ref::rp & when_true, + const ref::rp & when_false) + : Expression(exprtype::if_expr), + test_{test}, + when_true_{when_true}, + when_false_{when_false} {} + + /** downcast from Expression **/ + static ref::brw from(ref::brw x) { + return ref::brw::from(x); + } + + const ref::rp & test() const { return test_; } + const ref::rp & when_true() const { return when_true_; } + const ref::rp & when_false() const { return when_false_; } + + // ----- Expression ----- + + virtual void display(std::ostream & os) const override; + + private: + /** if: + * (if x y z) + * + * executes x; if true execute y; otherwise execute z + **/ + ref::rp test_; + ref::rp when_true_; + ref::rp when_false_; + }; /*IfExpr*/ + + inline ref::rp + make_if_expr(const ref::rp & test, + const ref::rp & when_true, + const ref::rp & when_false) + { + return new IfExpr(test, when_true, when_false); + } + } /*namespace ast*/ +} /*namespace xo*/ + +/** end IfExpr.hpp **/ diff --git a/include/xo/expression/exprtype.hpp b/include/xo/expression/exprtype.hpp index 91bcfbb5..ee33190e 100644 --- a/include/xo/expression/exprtype.hpp +++ b/include/xo/expression/exprtype.hpp @@ -28,6 +28,8 @@ namespace xo { lambda, /** variable reference **/ variable, + /** if-then-else **/ + if_expr, /** not an expression. comes last, counts entries **/ n_expr @@ -43,6 +45,7 @@ namespace xo { case exprtype::apply: return "apply"; case exprtype::lambda: return "lambda"; case exprtype::variable: return "variable"; + case exprtype::if_expr: return "if_expr"; default: break; } diff --git a/src/expression/CMakeLists.txt b/src/expression/CMakeLists.txt index 0ef351e1..ef5cf6f2 100644 --- a/src/expression/CMakeLists.txt +++ b/src/expression/CMakeLists.txt @@ -6,7 +6,7 @@ set(SELF_SRCS Apply.cpp Lambda.cpp Variable.cpp - #init_reflect.cpp + IfExpr.cpp ) xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) diff --git a/src/expression/IfExpr.cpp b/src/expression/IfExpr.cpp new file mode 100644 index 00000000..a12fbdb7 --- /dev/null +++ b/src/expression/IfExpr.cpp @@ -0,0 +1,20 @@ +/* @file IfExpr.cpp */ + +#include "IfExpr.hpp" +#include "xo/indentlog/print/vector.hpp" + +namespace xo { + namespace ast { + void + IfExpr::display(std::ostream & os) const { + os << ""; + } /*display*/ + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end IfExpr.cpp */ From fe2053a7bec7bb63028380e734d5c96af0680bac Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 17 Jun 2024 12:26:35 -0400 Subject: [PATCH 10/58] xo-expresion: doc: + links --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 205ba405..151c0493 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,13 @@ # xo-expression library A library for representing abstract syntax trees for EGAD (a small expression-based language). +See also +- [github/Rconybea/xo-jit](https://github.com/Rconybea/xo-jit) + EGAD code generation via LLVM +- [github/Rconybea/xo-pyexpression](https://github.com/Rconybea/xo-pyexpression) + build EGAD expressions from a python session +- [github/Rconybea/xo-pyjit](https://github.com/Rconybea/xo-pyjit) + compile + run EGAD expressions from a python session ## Getting Started From 78605a758d20745d6f4fb7464300fcbf598651f1 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 17 Jun 2024 13:43:21 -0400 Subject: [PATCH 11/58] xo-expression: minor refactor: if_expr -> ifexpr --- include/xo/expression/Expression.hpp | 3 +++ include/xo/expression/IfExpr.hpp | 8 ++++---- include/xo/expression/exprtype.hpp | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/xo/expression/Expression.hpp b/include/xo/expression/Expression.hpp index cd23df53..c9c4e0c0 100644 --- a/include/xo/expression/Expression.hpp +++ b/include/xo/expression/Expression.hpp @@ -20,6 +20,9 @@ namespace xo { * - execute it on a VM * - compile using LLVM * see xo-jit/ + * + * Expressions are immutable. This means they can resused + * across jit interactions **/ class Expression : public ref::Refcount { public: diff --git a/include/xo/expression/IfExpr.hpp b/include/xo/expression/IfExpr.hpp index d60ab1d3..592c585c 100644 --- a/include/xo/expression/IfExpr.hpp +++ b/include/xo/expression/IfExpr.hpp @@ -25,7 +25,7 @@ namespace xo { IfExpr(const ref::rp & test, const ref::rp & when_true, const ref::rp & when_false) - : Expression(exprtype::if_expr), + : Expression(exprtype::ifexpr), test_{test}, when_true_{when_true}, when_false_{when_false} {} @@ -55,9 +55,9 @@ namespace xo { }; /*IfExpr*/ inline ref::rp - make_if_expr(const ref::rp & test, - const ref::rp & when_true, - const ref::rp & when_false) + make_ifexpr(const ref::rp & test, + const ref::rp & when_true, + const ref::rp & when_false) { return new IfExpr(test, when_true, when_false); } diff --git a/include/xo/expression/exprtype.hpp b/include/xo/expression/exprtype.hpp index ee33190e..d8e66a61 100644 --- a/include/xo/expression/exprtype.hpp +++ b/include/xo/expression/exprtype.hpp @@ -29,7 +29,7 @@ namespace xo { /** variable reference **/ variable, /** if-then-else **/ - if_expr, + ifexpr, /** not an expression. comes last, counts entries **/ n_expr @@ -45,7 +45,7 @@ namespace xo { case exprtype::apply: return "apply"; case exprtype::lambda: return "lambda"; case exprtype::variable: return "variable"; - case exprtype::if_expr: return "if_expr"; + case exprtype::ifexpr: return "if_expr"; default: break; } From a36b6f5c75dc0945c1f26cf4c482db5d01d839c9 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 17 Jun 2024 13:43:41 -0400 Subject: [PATCH 12/58] xo-expression: Apply::make() takes vector of argument expressions --- include/xo/expression/Apply.hpp | 48 ++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/include/xo/expression/Apply.hpp b/include/xo/expression/Apply.hpp index 1f2b9a8f..24f4849d 100644 --- a/include/xo/expression/Apply.hpp +++ b/include/xo/expression/Apply.hpp @@ -20,10 +20,11 @@ namespace xo { **/ class Apply : public Expression { public: - Apply(const ref::rp & fn, - const std::vector> & argv) - : Expression(exprtype::apply), fn_{fn}, argv_(argv) - {} + static ref::rp make(const ref::rp & fn, + const std::vector> & argv) + { + return new Apply(fn, argv); + } /** downcast from Expression **/ static ref::brw from(ref::brw x) { @@ -35,6 +36,12 @@ namespace xo { virtual void display(std::ostream & os) const; + private: + Apply(const ref::rp & fn, + const std::vector> & argv) + : Expression(exprtype::apply), fn_{fn}, argv_(argv) + {} + private: /** function to invoke **/ ref::rp fn_; @@ -42,13 +49,36 @@ namespace xo { std::vector> argv_; }; /*Apply*/ + namespace detail { + /** Use: + ** std::vector> + **/ + + template + struct apply_push_args; + + template <> + struct apply_push_args<> { + static void p9ush_all(std::vector> * /*p_argv*/) {} + }; + + template + struct apply_push_args { + static void push_all(std::vector> * p_argv, + const ref::rp & x, Rest... rest) + { + p_argv->push_back(x); + apply_push_args::push_all(p_argv, rest...); + }; + }; + } + + /* reminder: initializer-lists are compile-time only */ inline ref::rp make_apply(const ref::rp & fn, - const ref::rp & arg1) { - std::vector> argv; - argv.push_back(arg1); - - return new Apply(fn, argv); + const std::initializer_list> args) { + std::vector> argv(args); + return Apply::make(fn, argv); } /*make_apply*/ } /*namespace ast*/ From f71cb12831b86bc4074103a8156d1e573443204d Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 17 Jun 2024 13:45:13 -0400 Subject: [PATCH 13/58] xo-expression: disable unused detail::apply_push_args --- include/xo/expression/Apply.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/xo/expression/Apply.hpp b/include/xo/expression/Apply.hpp index 24f4849d..acb27a5f 100644 --- a/include/xo/expression/Apply.hpp +++ b/include/xo/expression/Apply.hpp @@ -49,6 +49,7 @@ namespace xo { std::vector> argv_; }; /*Apply*/ +#ifdef NOT_USING namespace detail { /** Use: ** std::vector> @@ -59,7 +60,7 @@ namespace xo { template <> struct apply_push_args<> { - static void p9ush_all(std::vector> * /*p_argv*/) {} + static void push_all(std::vector> * /*p_argv*/) {} }; template @@ -72,6 +73,7 @@ namespace xo { }; }; } +#endif /* reminder: initializer-lists are compile-time only */ inline ref::rp From b18de1b0ce51288254a75615f98a50b6a3b1dfd4 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 17 Jun 2024 17:00:16 -0400 Subject: [PATCH 14/58] xo-expression: + Lambda::type_str --- include/xo/expression/Lambda.hpp | 8 ++++++-- src/expression/Lambda.cpp | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 7f5ebab4..06d5857f 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -23,8 +23,7 @@ namespace xo { **/ Lambda(const std::string & name, const std::vector & argv, - const ref::rp & body) - : Expression(exprtype::lambda), name_{name}, argv_{argv}, body_{body} {} + const ref::rp & body); /** downcast from Expression **/ static ref::brw from(ref::brw x) { @@ -32,6 +31,7 @@ namespace xo { } const std::string & name() const { return name_; } + const std::string & type_str() const { return type_str_; } const std::vector & argv() const { return argv_; } const ref::rp & body() const { return body_; } @@ -51,6 +51,10 @@ namespace xo { * so that they can be linked etc. **/ std::string name_; + /** e.g. + * "double(double,double)" for function of two doubles that returns a double + **/ + std::string type_str_; /** formal argument names **/ std::vector argv_; /** function body **/ diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index 87f70759..cae46f6f 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -4,7 +4,30 @@ #include "xo/indentlog/print/vector.hpp" namespace xo { + using std::stringstream; + namespace ast { + Lambda::Lambda(const std::string & name, + const std::vector & argv, + const ref::rp & body) + : Expression(exprtype::lambda), + name_{name}, + argv_{argv}, + body_{body} + { + stringstream ss; + ss << "double"; + ss << "("; + for (std::size_t i = 0; i < argv.size(); ++i) { + if (i > 0) + ss << ","; + ss << "double"; + } + ss << ")"; + + type_str_ = ss.str(); + } /*ctor*/ + void Lambda::display(std::ostream & os) const { os << " Date: Tue, 18 Jun 2024 16:55:46 -0400 Subject: [PATCH 15/58] xo-expression: add explicit types to all Expressions --- include/xo/expression/Apply.hpp | 16 +++++--- include/xo/expression/Constant.hpp | 24 +++++++---- include/xo/expression/ConstantInterface.hpp | 2 +- include/xo/expression/Expression.hpp | 14 ++++++- include/xo/expression/IfExpr.hpp | 39 +++++++++++++----- include/xo/expression/Lambda.hpp | 27 ++++++++---- include/xo/expression/Primitive.hpp | 40 ++++++++++++------ include/xo/expression/PrimitiveInterface.hpp | 3 +- include/xo/expression/Variable.hpp | 20 +++++++-- src/expression/Apply.cpp | 22 ++++++++++ src/expression/IfExpr.cpp | 27 ++++++++++++ src/expression/Lambda.cpp | 43 +++++++++++++++++++- 12 files changed, 223 insertions(+), 54 deletions(-) diff --git a/include/xo/expression/Apply.hpp b/include/xo/expression/Apply.hpp index acb27a5f..775c31f9 100644 --- a/include/xo/expression/Apply.hpp +++ b/include/xo/expression/Apply.hpp @@ -20,11 +20,13 @@ namespace xo { **/ class Apply : public Expression { public: + using TypeDescr = xo::reflect::TypeDescr; + + public: + /** create new apply-expression instance + **/ static ref::rp make(const ref::rp & fn, - const std::vector> & argv) - { - return new Apply(fn, argv); - } + const std::vector> & argv); /** downcast from Expression **/ static ref::brw from(ref::brw x) { @@ -37,9 +39,11 @@ namespace xo { virtual void display(std::ostream & os) const; private: - Apply(const ref::rp & fn, + Apply(TypeDescr apply_valuetype, + const ref::rp & fn, const std::vector> & argv) - : Expression(exprtype::apply), fn_{fn}, argv_(argv) + : Expression(exprtype::apply, apply_valuetype), + fn_{fn}, argv_(argv) {} private: diff --git a/include/xo/expression/Constant.hpp b/include/xo/expression/Constant.hpp index 2741881f..17d8aa3b 100644 --- a/include/xo/expression/Constant.hpp +++ b/include/xo/expression/Constant.hpp @@ -29,13 +29,12 @@ namespace xo { using TypeDescr = xo::reflect::TypeDescr; public: - explicit Constant(const T & x) - : ConstantInterface(exprtype::constant), - value_td_{Reflect::require()}, - value_(x) - { - static_assert(std::is_standard_layout_v && std::is_trivial_v); - } + /** create constant expression representing literal value x **/ + static ref::rp make(const T & x) { + TypeDescr x_valuetype = Reflect::require(); + + return new Constant(x_valuetype, x); + } const T & value() const { return value_; } @@ -59,6 +58,15 @@ namespace xo { << ">"; } + private: + explicit Constant(TypeDescr x_type, const T & x) + : ConstantInterface(exprtype::constant, x_type), + value_td_{Reflect::require()}, + value_(x) + { + static_assert(std::is_standard_layout_v && std::is_trivial_v); + } + private: /** type description for T **/ TypeDescr value_td_; @@ -69,7 +77,7 @@ namespace xo { template ref::rp>> make_constant(const T & x) { - return new Constant(x); + return Constant::make(x); } } /*namespace ast*/ diff --git a/include/xo/expression/ConstantInterface.hpp b/include/xo/expression/ConstantInterface.hpp index 9bd38383..586322c7 100644 --- a/include/xo/expression/ConstantInterface.hpp +++ b/include/xo/expression/ConstantInterface.hpp @@ -22,7 +22,7 @@ namespace xo { public: /** @p extype sets expression-type; could be constant|primitive **/ - ConstantInterface(exprtype extype) : Expression{extype} {} + ConstantInterface(exprtype extype, TypeDescr valuetype) : Expression{extype, valuetype} {} /** downcast from Expression **/ static ref::brw from(ref::brw x) { diff --git a/include/xo/expression/Expression.hpp b/include/xo/expression/Expression.hpp index c9c4e0c0..22bd8d4a 100644 --- a/include/xo/expression/Expression.hpp +++ b/include/xo/expression/Expression.hpp @@ -5,6 +5,7 @@ #pragma once +#include "xo/reflect/TypeDescr.hpp" #include "xo/refcnt/Refcounted.hpp" #include "exprtype.hpp" @@ -23,12 +24,19 @@ namespace xo { * * Expressions are immutable. This means they can resused * across jit interactions + * + * Every expression evaluates to a value with a particular type **/ class Expression : public ref::Refcount { public: - explicit Expression(exprtype extype) : extype_{extype} {} + using TypeDescr = xo::reflect::TypeDescr; + + public: + explicit Expression(exprtype extype, TypeDescr valuetype) + : extype_{extype}, valuetype_{valuetype}{} exprtype extype() const { return extype_; } + TypeDescr valuetype() const { return valuetype_; } /** write human-readable representation to stream **/ virtual void display(std::ostream & os) const = 0; @@ -38,6 +46,10 @@ namespace xo { private: /** expression type (constant | apply | ..) for this expression **/ exprtype extype_ = exprtype::invalid; + /** type information (when available) for values produced by this + * expression. + **/ + TypeDescr valuetype_ = nullptr; }; /*Expression*/ inline std::ostream & diff --git a/include/xo/expression/IfExpr.hpp b/include/xo/expression/IfExpr.hpp index 592c585c..ff9c3e57 100644 --- a/include/xo/expression/IfExpr.hpp +++ b/include/xo/expression/IfExpr.hpp @@ -18,17 +18,16 @@ namespace xo { **/ class IfExpr : public Expression { public: - /** @p test test-expression; always execute - * @p when_true then-branch; executes only when test succeeds - * @p when_false else-branch; executes only when test fails + using TypeDescr = xo::reflect::TypeDescr; + + public: + /** create expression for conditional execution of + * @p when_true or @p when_false, depending on result + * of evaluating expression @p test **/ - IfExpr(const ref::rp & test, - const ref::rp & when_true, - const ref::rp & when_false) - : Expression(exprtype::ifexpr), - test_{test}, - when_true_{when_true}, - when_false_{when_false} {} + static ref::rp make(const ref::rp & test, + const ref::rp & when_true, + const ref::rp & when_false); /** downcast from Expression **/ static ref::brw from(ref::brw x) { @@ -43,6 +42,24 @@ namespace xo { virtual void display(std::ostream & os) const override; + private: + /** + * @p ifexpr_type type for value produced by if-expression. + * same as both when_true->valuetype() and + * when_false->valuetype(). + * @p test test-expression; always execute + * @p when_true then-branch; executes only when test succeeds + * @p when_false else-branch; executes only when test fails + **/ + IfExpr(TypeDescr ifexpr_type, + const ref::rp & test, + const ref::rp & when_true, + const ref::rp & when_false) + : Expression(exprtype::ifexpr, ifexpr_type), + test_{test}, + when_true_{when_true}, + when_false_{when_false} {} + private: /** if: * (if x y z) @@ -59,7 +76,7 @@ namespace xo { const ref::rp & when_true, const ref::rp & when_false) { - return new IfExpr(test, when_true, when_false); + return IfExpr::make(test, when_true, when_false); } } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 06d5857f..ed88b44e 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -18,12 +18,14 @@ namespace xo { **/ class Lambda : public Expression { public: - /** @p argv Formal parameters, in left-to-right order + /** + * @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 **/ - Lambda(const std::string & name, - const std::vector & argv, - const ref::rp & body); + static ref::rp make(const std::string & name, + const std::vector> & argv, + const ref::rp & body); /** downcast from Expression **/ static ref::brw from(ref::brw x) { @@ -32,7 +34,7 @@ namespace xo { const std::string & name() const { return name_; } const std::string & type_str() const { return type_str_; } - const std::vector & argv() const { return argv_; } + const std::vector> & argv() const { return argv_; } const ref::rp & body() const { return body_; } /** return number of arguments expected by this function **/ @@ -42,6 +44,15 @@ namespace xo { virtual void display(std::ostream & os) const override; + private: + /** @param lambda_type. function type for this lambda. + * We arbitrarily choose the form "Retval(*)(Args...)" + **/ + Lambda(const std::string & name, + TypeDescr lambda_type, + const std::vector> & argv, + const ref::rp & body); + private: /** lambda name. Initially supporting only form like * (define (foo x y z) @@ -56,17 +67,17 @@ namespace xo { **/ std::string type_str_; /** formal argument names **/ - std::vector argv_; + std::vector> argv_; /** function body **/ ref::rp body_; }; /*Lambda*/ inline ref::rp make_lambda(const std::string & name, - const std::vector & argv, + const std::vector> & argv, const ref::rp & body) { - return new Lambda(name, argv, body); + return Lambda::make(name, argv, body); } } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/Primitive.hpp b/include/xo/expression/Primitive.hpp index f4fc21b7..507bee8e 100644 --- a/include/xo/expression/Primitive.hpp +++ b/include/xo/expression/Primitive.hpp @@ -20,6 +20,10 @@ namespace xo { * * In any case, a primitive serves as both declaration and definition * (May be possible to relax this to declaration-only using null value_ as sentinel..?) + * + * @tparam FunctionPointer a function-pointer type, e.g. double(*)(double). + * Must be in this "canonical form". std::function + * won't work here. **/ template class Primitive: public PrimitiveInterface { @@ -29,18 +33,12 @@ namespace xo { using TypeDescr = xo::reflect::TypeDescr; public: - Primitive(const std::string & name, - FunctionPointer fnptr) - : PrimitiveInterface(), - name_{name}, - value_td_{Reflect::require_function()}, - value_{fnptr} - { - if (!value_td_->is_function()) - throw std::runtime_error("Primitive: expected function pointer"); - if (!value_td_->fn_retval()) - throw std::runtime_error("Primitive: expected non-null function return value"); - } + static ref::rp make(const std::string & name, + FunctionPointer fnptr) { + TypeDescr fn_type = Reflect::require(); + + return new Primitive(fn_type, name, fnptr); + } FunctionPointer value() const { return value_; } @@ -69,6 +67,22 @@ namespace xo { << ">"; } + private: + Primitive(TypeDescr fn_type, + const std::string & name, + FunctionPointer fnptr) + : PrimitiveInterface(fn_type), + name_{name}, + value_td_{Reflect::require_function()}, + value_{fnptr} + { + if (!value_td_->is_function()) + throw std::runtime_error("Primitive: expected function pointer"); + if (!value_td_->fn_retval()) + throw std::runtime_error("Primitive: expected non-null function return value"); + } + + private: // from Expression: // exprtype extype_ @@ -85,7 +99,7 @@ namespace xo { template ref::rp> make_primitive(const std::string & name, FunctionPointer x) { - return new Primitive(name, x); + return Primitive::make(name, x); } } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/PrimitiveInterface.hpp b/include/xo/expression/PrimitiveInterface.hpp index 3274195f..3798c329 100644 --- a/include/xo/expression/PrimitiveInterface.hpp +++ b/include/xo/expression/PrimitiveInterface.hpp @@ -14,7 +14,8 @@ namespace xo { namespace ast { class PrimitiveInterface : public ConstantInterface { public: - PrimitiveInterface() : ConstantInterface(exprtype::primitive) {} + PrimitiveInterface(TypeDescr fn_type) + : ConstantInterface(exprtype::primitive, fn_type) {} /** downcast from Expression **/ static ref::brw from(ref::brw x) { diff --git a/include/xo/expression/Variable.hpp b/include/xo/expression/Variable.hpp index fc565236..908efbf9 100644 --- a/include/xo/expression/Variable.hpp +++ b/include/xo/expression/Variable.hpp @@ -15,7 +15,14 @@ namespace xo { **/ class Variable : public Expression { public: - Variable(const std::string & name) : Expression(exprtype::variable), name_{name} {} + /** create expression representing a variable + * identified by @p name, that can take on values + * described by @p var_type. + **/ + static ref::rp make(const std::string & name, + TypeDescr var_type) { + return new Variable(name, var_type); + } /** downcast from Expression **/ static ref::brw from(ref::brw x) { @@ -26,14 +33,21 @@ namespace xo { virtual void display(std::ostream & os) const; + private: + Variable(const std::string & name, + TypeDescr var_type) + : Expression(exprtype::variable, var_type), + name_{name} {} + private: /** variable name **/ std::string name_; }; /*Variable*/ inline ref::rp - make_var(const std::string & name) { - return new Variable(name); + make_var(const std::string & name, + reflect::TypeDescr var_type) { + return Variable::make(name, var_type); } } /*namespace ast*/ } /*namespace xo*/ diff --git a/src/expression/Apply.cpp b/src/expression/Apply.cpp index 781ec170..12926ddb 100644 --- a/src/expression/Apply.cpp +++ b/src/expression/Apply.cpp @@ -4,7 +4,29 @@ #include "xo/indentlog/print/vector.hpp" namespace xo { + using xo::ref::rp; + namespace ast { + rp + Apply::make(const rp & fn, + const std::vector> & argv) + { + /* extract result type from function type */ + TypeDescr fn_valuetype = fn->valuetype(); + + if (!fn_valuetype->is_function()) { + throw std::runtime_error + (tostr("Apply::make: found expression F in function position," + " with value-type FT where a function type expected", + xtag("FT", fn_valuetype->short_name()), + xtag("F", fn_valuetype))); + } + + TypeDescr fn_retval_type = fn_valuetype->fn_retval(); + + return new Apply(fn_retval_type, fn, argv); + } + void Apply::display(std::ostream & os) const { os << " + IfExpr::make(const rp & test, + const rp & when_true, + const rp & when_false) + { + /** TODO: verify test returns _boolean_ type **/ + + if (when_true->valuetype() != when_false->valuetype()) { + throw std::runtime_error + (tostr("IfExpr::make:" + " types {T1,T2} found for branches of if-expr" + " where equal types expected", + xtag("T1", when_true->valuetype()->canonical_name()), + xtag("T2", when_false->valuetype()->canonical_name()))); + } + + /* arbitrary choice here */ + auto ifexpr_type = when_true->valuetype(); + + return new IfExpr(ifexpr_type, + test, + when_true, + when_false); + } /*make*/ + void IfExpr::display(std::ostream & os) const { os << " + Lambda::make(const std::string & name, + const std::vector> & argv, + const ref::rp & body) + { + using xo::reflect::FunctionTdx; + + /** assemble function type. + * + * NOTE: need this to be unique! + **/ + + std::vector arg_td_v; + arg_td_v.reserve(argv.size()); + + for (const auto & arg : argv) { + arg_td_v.push_back(arg->valuetype()); + } + + auto function_info + = FunctionTdxInfo(body->valuetype(), + arg_td_v, + false /*!is_noexcept*/); + + TypeDescr lambda_td + = TypeDescrBase::require_by_fn_info(function_info); + + return new Lambda(name, + lambda_td, + argv, + body); + } /*make*/ + Lambda::Lambda(const std::string & name, - const std::vector & argv, + TypeDescr lambda_type, + const std::vector> & argv, const ref::rp & body) - : Expression(exprtype::lambda), + : Expression(exprtype::lambda, lambda_type), name_{name}, argv_{argv}, body_{body} From 20eab4e5d3152210e27ca4546a7b2cf1967f9eb7 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 18 Jun 2024 16:56:14 -0400 Subject: [PATCH 16/58] xo-expression: ++ docs --- LESSONS | 9 +++++++++ TODO | 3 +++ 2 files changed, 12 insertions(+) create mode 100644 LESSONS create mode 100644 TODO diff --git a/LESSONS b/LESSONS new file mode 100644 index 00000000..c719f02e --- /dev/null +++ b/LESSONS @@ -0,0 +1,9 @@ +* want reflection to work without requiring std::type_info, + so that in xo-expression we can create function types that + C++ compiler has never encountered. + +** This means we need the ability to construct the canonical name + for a type without c++ compiler's assistance. + +** need lookup using function ingredients (return types + arg types + noexcept) + to intern lambda types diff --git a/TODO b/TODO new file mode 100644 index 00000000..c2a91ce8 --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ +- In xo-reflect, TypeDescr objects are immortal. + In original context rationale was recording information for already-established C++ types. + Maybe want to revisit that choice when associating TypeDescr objects with expressions? From 0595c7c74e3806f5e1783e9d6dc361543aee3c63 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 18 Jun 2024 17:23:32 -0400 Subject: [PATCH 17/58] xo-expression: refactor: enforce Lambda.argv must contain Variables --- include/xo/expression/Lambda.hpp | 11 ++++++----- src/expression/Lambda.cpp | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index ed88b44e..cdf72ed9 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -6,6 +6,7 @@ #pragma once #include "Expression.hpp" +#include "Variable.hpp" #include #include //#include @@ -24,7 +25,7 @@ namespace xo { * @p body Expression for body of this function **/ static ref::rp make(const std::string & name, - const std::vector> & argv, + const std::vector> & argv, const ref::rp & body); /** downcast from Expression **/ @@ -34,7 +35,7 @@ namespace xo { const std::string & name() const { return name_; } const std::string & type_str() const { return type_str_; } - const std::vector> & argv() const { return argv_; } + const std::vector> & argv() const { return argv_; } const ref::rp & body() const { return body_; } /** return number of arguments expected by this function **/ @@ -50,7 +51,7 @@ namespace xo { **/ Lambda(const std::string & name, TypeDescr lambda_type, - const std::vector> & argv, + const std::vector> & argv, const ref::rp & body); private: @@ -67,14 +68,14 @@ namespace xo { **/ std::string type_str_; /** formal argument names **/ - std::vector> argv_; + std::vector> argv_; /** function body **/ ref::rp body_; }; /*Lambda*/ inline ref::rp make_lambda(const std::string & name, - const std::vector> & argv, + const std::vector> & argv, const ref::rp & body) { return Lambda::make(name, argv, body); diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index acc40cbe..d92ac801 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -14,7 +14,7 @@ namespace xo { namespace ast { rp Lambda::make(const std::string & name, - const std::vector> & argv, + const std::vector> & argv, const ref::rp & body) { using xo::reflect::FunctionTdx; @@ -47,7 +47,7 @@ namespace xo { Lambda::Lambda(const std::string & name, TypeDescr lambda_type, - const std::vector> & argv, + const std::vector> & argv, const ref::rp & body) : Expression(exprtype::lambda, lambda_type), name_{name}, From 5c4b062cf85eadc1cafdc7dd728a6b602fb8d73c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 19 Jun 2024 10:53:04 -0400 Subject: [PATCH 18/58] xo-expression: refactor: Lambda/Primitive share FunctionInterface --- include/xo/expression/FunctionInterface.hpp | 34 ++++++++++++++++++++ include/xo/expression/Lambda.hpp | 11 +++++-- include/xo/expression/Primitive.hpp | 20 +++++++----- include/xo/expression/PrimitiveInterface.hpp | 17 ++++------ src/expression/Lambda.cpp | 2 +- src/expression/Variable.cpp | 1 + 6 files changed, 63 insertions(+), 22 deletions(-) create mode 100644 include/xo/expression/FunctionInterface.hpp diff --git a/include/xo/expression/FunctionInterface.hpp b/include/xo/expression/FunctionInterface.hpp new file mode 100644 index 00000000..a51dc69b --- /dev/null +++ b/include/xo/expression/FunctionInterface.hpp @@ -0,0 +1,34 @@ +/** @file FunctionInterface.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "Expression.hpp" +//#include + +namespace xo { + namespace ast { + class FunctionInterface : public Expression { + public: + FunctionInterface(exprtype extype, TypeDescr fn_type) + : Expression(extype, fn_type) {} + + /** downcast from Expression **/ + static ref::brw from(ref::brw x) { + return ref::brw::from(x); + } + + virtual const std::string & name() const = 0; + virtual int n_arg() const = 0; // { return this->value_td()->n_fn_arg(); } + virtual TypeDescr fn_retval() const = 0; // { return this->value_td()->fn_retval(); } + virtual TypeDescr fn_arg(uint32_t i) const = 0; // { return this->value_td()->fn_arg(i); } + + private: + }; /*FunctionInterface*/ + } /*namespace ast*/ +} /*namespace xo*/ + + +/** end FunctionInterface.hpp **/ diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index cdf72ed9..ce63860d 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -6,6 +6,7 @@ #pragma once #include "Expression.hpp" +#include "FunctionInterface.hpp" #include "Variable.hpp" #include #include @@ -17,7 +18,7 @@ namespace xo { * @brief abstract syntax tree for a function definition * **/ - class Lambda : public Expression { + class Lambda : public FunctionInterface { public: /** * @p name Name for this lambda -- must be unique @@ -33,13 +34,17 @@ namespace xo { return ref::brw::from(x); } - const std::string & name() const { return name_; } const std::string & type_str() const { return type_str_; } const std::vector> & argv() const { return argv_; } const ref::rp & body() const { return body_; } + // ----- FunctionInterface ----- + + virtual const std::string & name() const override { return name_; } /** return number of arguments expected by this function **/ - int n_arg() const { return argv_.size(); } + virtual int n_arg() const override { return argv_.size(); } + virtual TypeDescr fn_retval() const override { return body_->valuetype(); } + virtual TypeDescr fn_arg(uint32_t i) const override { return argv_[i]->valuetype(); } // ----- Expression ----- diff --git a/include/xo/expression/Primitive.hpp b/include/xo/expression/Primitive.hpp index 507bee8e..f251465d 100644 --- a/include/xo/expression/Primitive.hpp +++ b/include/xo/expression/Primitive.hpp @@ -6,6 +6,7 @@ #pragma once #include "PrimitiveInterface.hpp" +#include "xo/reflect/Reflect.hpp" //#include namespace xo { @@ -42,14 +43,8 @@ namespace xo { FunctionPointer value() const { return value_; } - // ----- PrimitiveInterface ----- - - virtual std::string const & name() const override { return name_; } - - // ----- ConstantInterface ----- - - virtual TypeDescr value_td() const override { return value_td_; } - virtual TaggedPtr value_tp() const override { + TypeDescr value_td() const { return value_td_; } + TaggedPtr value_tp() const { /* note: idk why, but need to spell this out in two steps with gcc 13.2 */ const void * erased_cptr = &value_; void * erased_ptr = const_cast(erased_cptr); @@ -57,6 +52,15 @@ namespace xo { return TaggedPtr(value_td_, erased_ptr); } + // ----- PrimitiveInterface ----- + + // ----- FunctionInterface ----- + + virtual std::string const & name() const override { return name_; } + virtual int n_arg() const override { return this->value_td()->n_fn_arg(); } + virtual TypeDescr fn_retval() const override { return this->value_td()->fn_retval(); } + virtual TypeDescr fn_arg(uint32_t i) const override { return this->value_td()->fn_arg(i); } + // ----- Expression ----- virtual void display(std::ostream & os) const override { diff --git a/include/xo/expression/PrimitiveInterface.hpp b/include/xo/expression/PrimitiveInterface.hpp index 3798c329..685d04ae 100644 --- a/include/xo/expression/PrimitiveInterface.hpp +++ b/include/xo/expression/PrimitiveInterface.hpp @@ -5,30 +5,27 @@ #pragma once -#include "ConstantInterface.hpp" +#include "FunctionInterface.hpp" //#include #include namespace xo { namespace ast { - class PrimitiveInterface : public ConstantInterface { + class PrimitiveInterface : public FunctionInterface { public: PrimitiveInterface(TypeDescr fn_type) - : ConstantInterface(exprtype::primitive, fn_type) {} + : FunctionInterface(exprtype::primitive, fn_type) {} /** downcast from Expression **/ static ref::brw from(ref::brw x) { return ref::brw::from(x); } - virtual const std::string & name() const = 0; - int n_arg() const { return this->value_td()->n_fn_arg(); } - TypeDescr fn_retval() const { return this->value_td()->fn_retval(); } - TypeDescr fn_arg(uint32_t i) const { return this->value_td()->fn_arg(i); } - - //virtual TypeDescr value_td() const override = 0; - //virtual TaggedPtr value_tp() const override = 0; + // virtual const std::string & name() const; + // virtual int n_arg() const; + // virtual TypeDescr fn_retval() const; + // virtual TypeDescr fn_arg(uint32_t i) const; private: }; /*PrimitiveInterface*/ diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index d92ac801..4cd25da9 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -49,7 +49,7 @@ namespace xo { TypeDescr lambda_type, const std::vector> & argv, const ref::rp & body) - : Expression(exprtype::lambda, lambda_type), + : FunctionInterface(exprtype::lambda, lambda_type), name_{name}, argv_{argv}, body_{body} diff --git a/src/expression/Variable.cpp b/src/expression/Variable.cpp index c8ab19fc..52890ccc 100644 --- a/src/expression/Variable.cpp +++ b/src/expression/Variable.cpp @@ -8,6 +8,7 @@ namespace xo { Variable::display(std::ostream & os) const { os << "valuetype()->short_name()) << ">"; } /*display*/ } /*namespace ast*/ From 37b2bc85a41fac6ff5cdeb0d9fb397d43a9391ae Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 19 Jun 2024 18:14:02 -0400 Subject: [PATCH 19/58] xo-expression: + explicit_symbol_def feature in PrimitiveInterface --- example/ex1/ex1.cpp | 4 ++- include/xo/expression/Primitive.hpp | 26 +++++++++++++++----- include/xo/expression/PrimitiveInterface.hpp | 16 ++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/example/ex1/ex1.cpp b/example/ex1/ex1.cpp index 5c12a4b1..be606918 100644 --- a/example/ex1/ex1.cpp +++ b/example/ex1/ex1.cpp @@ -17,7 +17,9 @@ main() { } { - auto expr = make_primitive("sqrt", &::sqrt); + auto expr = make_primitive("sqrt", + &::sqrt, + false /*!explicit_symbol_def*/); auto expr_td = expr->value_td(); diff --git a/include/xo/expression/Primitive.hpp b/include/xo/expression/Primitive.hpp index f251465d..caf7f1bf 100644 --- a/include/xo/expression/Primitive.hpp +++ b/include/xo/expression/Primitive.hpp @@ -35,10 +35,11 @@ namespace xo { public: static ref::rp make(const std::string & name, - FunctionPointer fnptr) { + FunctionPointer fnptr, + bool explicit_symbol_def) { TypeDescr fn_type = Reflect::require(); - return new Primitive(fn_type, name, fnptr); + return new Primitive(fn_type, name, fnptr, explicit_symbol_def); } FunctionPointer value() const { return value_; } @@ -54,6 +55,9 @@ namespace xo { // ----- PrimitiveInterface ----- + virtual bool explicit_symbol_def() const override { return explicit_symbol_def_; } + virtual void_function_type function_address() const override { return reinterpret_cast(value_); } + // ----- FunctionInterface ----- virtual std::string const & name() const override { return name_; } @@ -74,11 +78,13 @@ namespace xo { private: Primitive(TypeDescr fn_type, const std::string & name, - FunctionPointer fnptr) + FunctionPointer fnptr, + bool explicit_symbol_def) : PrimitiveInterface(fn_type), name_{name}, value_td_{Reflect::require_function()}, - value_{fnptr} + value_{fnptr}, + explicit_symbol_def_{explicit_symbol_def} { if (!value_td_->is_function()) throw std::runtime_error("Primitive: expected function pointer"); @@ -97,13 +103,21 @@ namespace xo { TypeDescr value_td_; /** address of executable function **/ FunctionPointer value_; + /** if true, use Jit.intern_symbol() to provide explicit binding. + * Currently mystified as to what's distinguishes functions like ::sin(), ::sqrt() + * (which work do not require this) from symbols like ::mul_i32(), which do) + **/ + bool explicit_symbol_def_ = false; }; /*Primitive*/ /** adopt function @p x as a callable primitive function named @p name **/ template ref::rp> - make_primitive(const std::string & name, FunctionPointer x) { - return Primitive::make(name, x); + make_primitive(const std::string & name, + FunctionPointer x, + bool explicit_symbol_def) + { + return Primitive::make(name, x, explicit_symbol_def); } } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/PrimitiveInterface.hpp b/include/xo/expression/PrimitiveInterface.hpp index 685d04ae..0b42b252 100644 --- a/include/xo/expression/PrimitiveInterface.hpp +++ b/include/xo/expression/PrimitiveInterface.hpp @@ -13,6 +13,9 @@ namespace xo { namespace ast { class PrimitiveInterface : public FunctionInterface { + public: + using void_function_type = void (*)(); + public: PrimitiveInterface(TypeDescr fn_type) : FunctionInterface(exprtype::primitive, fn_type) {} @@ -22,6 +25,19 @@ namespace xo { return ref::brw::from(x); } + /** if true, Jit will try to explicitly symbol for this primitive + * (instead of looking it up in host process). + * Don't know if this works. + * Do know it's not needed for ::sin(), ::sqrt(). + * Do know that my extern "C" functions like ::mul_i32(), ::mul_f64() + * need something else. + **/ + virtual bool explicit_symbol_def() const = 0; + + /** function address for this primitive + **/ + virtual void_function_type function_address() const = 0; + // virtual const std::string & name() const; // virtual int n_arg() const; // virtual TypeDescr fn_retval() const; From c7c21969e889f9b15827cb8c4d2da818df8cb596 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 21 Jun 2024 14:05:26 -0400 Subject: [PATCH 20/58] xo-expression: + llvmintrinsics enum --- include/xo/expression/llvmintrinsic.hpp | 92 +++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 include/xo/expression/llvmintrinsic.hpp diff --git a/include/xo/expression/llvmintrinsic.hpp b/include/xo/expression/llvmintrinsic.hpp new file mode 100644 index 00000000..37f231fe --- /dev/null +++ b/include/xo/expression/llvmintrinsic.hpp @@ -0,0 +1,92 @@ +/** @file llvmintrinsic.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +//#include + +namespace xo { + namespace ast { + /** @enum llvminstrinsic + * @brief enum to identify an LLVM instrinsic, e.g. @c IRBuilder::CreateFAdd + * + * Associate an @c llvminstrinsic with an AST @c Primitive p. + * Later, in @c xo::jit::IrPipeline + * - when generating code for @c xo::ast::Apply + * - with *p* is in the function-call position + * can use the associated llvm instrinsic instead of generating a function call + * @c Primitive::value + * + * @note llvm will still need to use @c Primitive::value, for example + * when handling an @c xo::ast::Apply instance where the function position + * is a computed function. + * @endnote + * + * @note + * NSW stands for 'no signed wrap' -> poison value if overflow (costs more) + * NUW stands for 'no unsigned wrap' -> poison value if overflow (costs more) + * @endnote + **/ + enum class llvmintrinsic { + /** sentinel value **/ + invalid = -1, + + /** -> IRBuilder::CreateAdd (add 2 integers, overflow silently) **/ + i_add, + + /** -> IRBuilder::CreateMul (multiply 2 integers, overflow silently) **/ + i_mul, + + /** -> IRBuilder::CreateFadd (add 2 floating-point numbers) **/ + fp_add, + + /** -> IRBuilder::CreateFmul (multiply 2 floating-point numbers) **/ + fp_mul, + + /** + * want to do whatever llvm IR @c llvm.sqrt.f64 and friends do. + * Not sure if that's an always-available function of something else + **/ + fp_sqrt, + + /** WIP **/ + fp_pow, + + /** WIP **/ + fp_sin, + + /** WIP **/ + fp_cos, + + /** WIP **/ + fp_tan, + + /** not an intrinsic. comes last, counts entries **/ + n_intrinsic + }; + + inline const char * + llvmintrinsic2str(llvmintrinsic x) + { + switch(x) { + case llvmintrinsic::invalid: return "?llvminstrinsic"; + case llvmintrinsic::i_add: return "i_add"; + case llvmintrinsic::i_mul: return "i_mul"; + case llvmintrinsic::fp_add: return "fp_add"; + case llvmintrinsic::fp_mul: return "fp_mul"; + case llvmintrinsic::fp_sqrt: return "fp_sqrt"; + case llvmintrinsic::fp_pow: return "fp_pow"; + case llvmintrinsic::fp_sin: return "fp_sin"; + case llvmintrinsic::fp_cos: return "fp_cos"; + case llvmintrinsic::fp_tan: return "fp_tan"; + default: break; + } + + return "???llvmintrinsic???"; + } /*llvmintrinsic2str*/ + } /*namespace ast*/ +} /*namespace xo*/ + +/** end llvmintrinsic.hpp **/ From 4d486c0e5cac8c5153674c9ceacd46084d096423 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 21 Jun 2024 14:05:49 -0400 Subject: [PATCH 21/58] xo-expression: + Primitive::intrinsic --- example/ex1/ex1.cpp | 5 ++- include/xo/expression/Primitive.hpp | 37 +++++++++++++------- include/xo/expression/PrimitiveInterface.hpp | 7 ++-- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/example/ex1/ex1.cpp b/example/ex1/ex1.cpp index be606918..5dd91fe2 100644 --- a/example/ex1/ex1.cpp +++ b/example/ex1/ex1.cpp @@ -2,6 +2,7 @@ #include "xo/expression/Constant.hpp" #include "xo/expression/Primitive.hpp" +#include "xo/expression/llvmintrinsic.hpp" #include #include @@ -9,6 +10,7 @@ int main() { using xo::ast::make_constant; using xo::ast::make_primitive; + using xo::ast::llvmintrinsic; using std::cout; using std::endl; @@ -19,7 +21,8 @@ main() { { auto expr = make_primitive("sqrt", &::sqrt, - false /*!explicit_symbol_def*/); + false /*!explicit_symbol_def*/, + llvmintrinsic::fp_sqrt); auto expr_td = expr->value_td(); diff --git a/include/xo/expression/Primitive.hpp b/include/xo/expression/Primitive.hpp index caf7f1bf..49246d8b 100644 --- a/include/xo/expression/Primitive.hpp +++ b/include/xo/expression/Primitive.hpp @@ -6,6 +6,7 @@ #pragma once #include "PrimitiveInterface.hpp" +#include "llvmintrinsic.hpp" #include "xo/reflect/Reflect.hpp" //#include @@ -15,9 +16,11 @@ namespace xo { * @brief syntax for a constant that refers to a known function. * * Two cases here: - * 1. primitive refers to a function that is supported directly in llvm - * (e.g. floating-point addition) - * 2. primitive refers to a compiled (C/C++) function that we can invoke at runtime + * 1. (always) primitive refers to a compiled (C/C++) function that we can invoke at runtime + * 2. (sometimes) primitive also refers to a function that is supported directly in llvm + * (e.g. floating-point addition). In that case @ref intrinsic_ + * identifies that direct support, provided it knows at codegen time which primitive + * is being invoked * * In any case, a primitive serves as both declaration and definition * (May be possible to relax this to declaration-only using null value_ as sentinel..?) @@ -36,13 +39,15 @@ namespace xo { public: static ref::rp make(const std::string & name, FunctionPointer fnptr, - bool explicit_symbol_def) { + bool explicit_symbol_def, + llvmintrinsic intrinsic) { TypeDescr fn_type = Reflect::require(); - return new Primitive(fn_type, name, fnptr, explicit_symbol_def); + return new Primitive(fn_type, name, fnptr, explicit_symbol_def, intrinsic); } FunctionPointer value() const { return value_; } + llvmintrinsic intrinsic() const { return intrinsic_; } TypeDescr value_td() const { return value_td_; } TaggedPtr value_tp() const { @@ -79,12 +84,14 @@ namespace xo { Primitive(TypeDescr fn_type, const std::string & name, FunctionPointer fnptr, - bool explicit_symbol_def) + bool explicit_symbol_def, + llvmintrinsic intrinsic) : PrimitiveInterface(fn_type), name_{name}, value_td_{Reflect::require_function()}, value_{fnptr}, - explicit_symbol_def_{explicit_symbol_def} + explicit_symbol_def_{explicit_symbol_def}, + intrinsic_{intrinsic} { if (!value_td_->is_function()) throw std::runtime_error("Primitive: expected function pointer"); @@ -103,11 +110,16 @@ namespace xo { TypeDescr value_td_; /** address of executable function **/ FunctionPointer value_; - /** if true, use Jit.intern_symbol() to provide explicit binding. - * Currently mystified as to what's distinguishes functions like ::sin(), ::sqrt() - * (which work do not require this) from symbols like ::mul_i32(), which do) + /** for LLVM: if true, use Jit.intern_symbol() to provide explicit binding. + * + * Not obvious what distinguishes functions like ::sin(), ::sqrt() + * (which work without this) from symbols like ::mul_i32(), which do. **/ bool explicit_symbol_def_ = false; + /** invalid: generate call (IRBuilder::CreateCall) + * all others: generate direct use of LLVM intrinsic + **/ + llvmintrinsic intrinsic_; }; /*Primitive*/ /** adopt function @p x as a callable primitive function named @p name **/ @@ -115,9 +127,10 @@ namespace xo { ref::rp> make_primitive(const std::string & name, FunctionPointer x, - bool explicit_symbol_def) + bool explicit_symbol_def, + llvmintrinsic intrinsic) { - return Primitive::make(name, x, explicit_symbol_def); + return Primitive::make(name, x, explicit_symbol_def, intrinsic); } } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/PrimitiveInterface.hpp b/include/xo/expression/PrimitiveInterface.hpp index 0b42b252..629772e0 100644 --- a/include/xo/expression/PrimitiveInterface.hpp +++ b/include/xo/expression/PrimitiveInterface.hpp @@ -6,6 +6,7 @@ #pragma once #include "FunctionInterface.hpp" +#include "llvmintrinsic.hpp" //#include @@ -34,10 +35,12 @@ namespace xo { **/ virtual bool explicit_symbol_def() const = 0; - /** function address for this primitive - **/ + /** function address for this primitive **/ virtual void_function_type function_address() const = 0; + /** get llvm intrinsic hint for this primitive **/ + virtual llvmintrinsic intrinsic() const = 0; + // virtual const std::string & name() const; // virtual int n_arg() const; // virtual TypeDescr fn_retval() const; From d94e16eecdef5f6112b7cf0f1e42233a8ba92342 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 24 Jun 2024 15:09:27 -0400 Subject: [PATCH 22/58] xo-expression: + several arithmetic intrinsics --- include/xo/expression/llvmintrinsic.hpp | 37 +++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/include/xo/expression/llvmintrinsic.hpp b/include/xo/expression/llvmintrinsic.hpp index 37f231fe..052baa9e 100644 --- a/include/xo/expression/llvmintrinsic.hpp +++ b/include/xo/expression/llvmintrinsic.hpp @@ -19,9 +19,18 @@ namespace xo { * can use the associated llvm instrinsic instead of generating a function call * @c Primitive::value * - * @note llvm will still need to use @c Primitive::value, for example - * when handling an @c xo::ast::Apply instance where the function position - * is a computed function. + * @note llvm will still sometimes need to use + * @c Primitive::value (and generate a function call sequence), + * for example when handling an @c xo::ast::Apply instance + * where the function position is a computed function. + * @endnote + * + * @note + * LLVM requires separate intrinsics for {ints, floats}. + * It does not need separate intrinsics for different sizes. + * For example IRBuilder::CreateAdd works for + * {8-bit, 16-bit, 32-bit, 64-bit, 128-bit} x {signed, unsigned} integers + * Integer division is an exception; need to choose between i_sdiv and i_udiv * @endnote * * @note @@ -30,21 +39,38 @@ namespace xo { * @endnote **/ enum class llvmintrinsic { + // see /nix/store/x5yz...llvm-18.1.5-dev/include/llvm/IR/IRBuilder.h + /** sentinel value **/ invalid = -1, + /** -> IRBuilder::CreateNeg (negate 1 integer) **/ + i_neg, + /** -> IRBuilder::CreateAdd (add 2 integers, overflow silently) **/ i_add, + /** -> IRBuilder::CreateSub (subtract 2 integers, overflow silently) **/ + i_sub, + /** -> IRBuilder::CreateMul (multiply 2 integers, overflow silently) **/ i_mul, + /** -> IRBuilder::CreateSdiv (divide 2 signed integers) **/ + i_sdiv, + + /** -> IRBuilder::CreateUdiv (divide 2 unsigned integers) **/ + i_udiv, + /** -> IRBuilder::CreateFadd (add 2 floating-point numbers) **/ fp_add, /** -> IRBuilder::CreateFmul (multiply 2 floating-point numbers) **/ fp_mul, + /** -> IRBuilder::CreateFdiv (divide 2 floating-point numbers) **/ + fp_div, + /** * want to do whatever llvm IR @c llvm.sqrt.f64 and friends do. * Not sure if that's an always-available function of something else @@ -72,10 +98,15 @@ namespace xo { { switch(x) { case llvmintrinsic::invalid: return "?llvminstrinsic"; + case llvmintrinsic::i_neg: return "i_neg"; case llvmintrinsic::i_add: return "i_add"; + case llvmintrinsic::i_sub: return "i_sub"; case llvmintrinsic::i_mul: return "i_mul"; + case llvmintrinsic::i_sdiv: return "i_sdiv"; + case llvmintrinsic::i_udiv: return "i_udiv"; case llvmintrinsic::fp_add: return "fp_add"; case llvmintrinsic::fp_mul: return "fp_mul"; + case llvmintrinsic::fp_div: return "fp_div"; case llvmintrinsic::fp_sqrt: return "fp_sqrt"; case llvmintrinsic::fp_pow: return "fp_pow"; case llvmintrinsic::fp_sin: return "fp_sin"; From 829bffd007e7e594115361d87c7c1215b116e3f5 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 24 Jun 2024 15:10:06 -0400 Subject: [PATCH 23/58] xo-expression: + Expression::visit_preorder() --- include/xo/expression/Apply.hpp | 15 ++++++++++++++- include/xo/expression/Constant.hpp | 5 +++++ include/xo/expression/Expression.hpp | 9 +++++++++ include/xo/expression/IfExpr.hpp | 11 +++++++++++ include/xo/expression/Lambda.hpp | 13 +++++++++++++ include/xo/expression/PrimitiveInterface.hpp | 7 +++++++ include/xo/expression/Variable.hpp | 7 ++++++- 7 files changed, 65 insertions(+), 2 deletions(-) diff --git a/include/xo/expression/Apply.hpp b/include/xo/expression/Apply.hpp index 775c31f9..b7cb8c31 100644 --- a/include/xo/expression/Apply.hpp +++ b/include/xo/expression/Apply.hpp @@ -36,7 +36,20 @@ namespace xo { const ref::rp & fn() const { return fn_; } const std::vector> & argv() const { return argv_; } - virtual void display(std::ostream & os) const; + virtual std::size_t visit_preorder(VisitFn visitor_fn) override { + std::size_t n = 1; + + visitor_fn(this); + + n += fn_->visit_preorder(visitor_fn); + + for (const auto & arg : argv_) + n += arg->visit_preorder(visitor_fn); + + return n; + } + + virtual void display(std::ostream & os) const override; private: Apply(TypeDescr apply_valuetype, diff --git a/include/xo/expression/Constant.hpp b/include/xo/expression/Constant.hpp index 17d8aa3b..50f4c2ba 100644 --- a/include/xo/expression/Constant.hpp +++ b/include/xo/expression/Constant.hpp @@ -51,6 +51,11 @@ namespace xo { // ----- Expression ----- + virtual std::size_t visit_preorder(VisitFn visitor_fn) override { + visitor_fn(this); + return 1; + } + virtual void display(std::ostream & os) const override { os << "short_name()) diff --git a/include/xo/expression/Expression.hpp b/include/xo/expression/Expression.hpp index 22bd8d4a..b38eecbd 100644 --- a/include/xo/expression/Expression.hpp +++ b/include/xo/expression/Expression.hpp @@ -8,6 +8,7 @@ #include "xo/reflect/TypeDescr.hpp" #include "xo/refcnt/Refcounted.hpp" #include "exprtype.hpp" +#include namespace xo { namespace ast { @@ -29,6 +30,7 @@ namespace xo { **/ class Expression : public ref::Refcount { public: + using VisitFn = std::function)>; using TypeDescr = xo::reflect::TypeDescr; public: @@ -38,6 +40,13 @@ namespace xo { exprtype extype() const { return extype_; } TypeDescr valuetype() const { return valuetype_; } + /** visit each Expression node in this AST, + * and invoke @p fn for each. + * Returns the number of nodes visited. + * Preorder: call @p fn for a node before visiting children + **/ + virtual std::size_t visit_preorder(VisitFn visitor_fn) = 0; + /** write human-readable representation to stream **/ virtual void display(std::ostream & os) const = 0; /** human-readable string representation **/ diff --git a/include/xo/expression/IfExpr.hpp b/include/xo/expression/IfExpr.hpp index ff9c3e57..6f397b3d 100644 --- a/include/xo/expression/IfExpr.hpp +++ b/include/xo/expression/IfExpr.hpp @@ -40,6 +40,17 @@ namespace xo { // ----- Expression ----- + virtual std::size_t visit_preorder(VisitFn visitor_fn) override { + std::size_t n = 1; + + visitor_fn(this); + + n += this->when_true_->visit_preorder(visitor_fn); + n += this->when_false_->visit_preorder(visitor_fn); + + return n; + } + virtual void display(std::ostream & os) const override; private: diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index ce63860d..20c4425b 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -48,6 +48,19 @@ namespace xo { // ----- Expression ----- + virtual std::size_t visit_preorder(VisitFn visitor_fn) override { + std::size_t n = 1; + + visitor_fn(this); + + for (const auto & arg : argv_) + n += arg->visit_preorder(visitor_fn); + + n += body_->visit_preorder(visitor_fn); + + return n; + } + virtual void display(std::ostream & os) const override; private: diff --git a/include/xo/expression/PrimitiveInterface.hpp b/include/xo/expression/PrimitiveInterface.hpp index 629772e0..907aef2d 100644 --- a/include/xo/expression/PrimitiveInterface.hpp +++ b/include/xo/expression/PrimitiveInterface.hpp @@ -46,6 +46,13 @@ namespace xo { // virtual TypeDescr fn_retval() const; // virtual TypeDescr fn_arg(uint32_t i) const; + // ----- Expression ----- + + virtual std::size_t visit_preorder(VisitFn visitor_fn) override { + visitor_fn(this); + return 1; + } + private: }; /*PrimitiveInterface*/ } /*namespace ast*/ diff --git a/include/xo/expression/Variable.hpp b/include/xo/expression/Variable.hpp index 908efbf9..13e46a98 100644 --- a/include/xo/expression/Variable.hpp +++ b/include/xo/expression/Variable.hpp @@ -31,7 +31,12 @@ namespace xo { const std::string & name() const { return name_; } - virtual void display(std::ostream & os) const; + virtual std::size_t visit_preorder(VisitFn visitor_fn) override { + visitor_fn(this); + return 1; + } + + virtual void display(std::ostream & os) const override; private: Variable(const std::string & name, From d836f13b889d1b28230e0e3f312bdcc563dae00e Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 30 Jun 2024 19:10:56 -0400 Subject: [PATCH 24/58] xo-expression: + environment implementation --- include/xo/expression/Environment.hpp | 23 +++++++++ include/xo/expression/GlobalEnv.hpp | 46 ++++++++++++++++++ include/xo/expression/LocalEnv.hpp | 70 +++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 include/xo/expression/Environment.hpp create mode 100644 include/xo/expression/GlobalEnv.hpp create mode 100644 include/xo/expression/LocalEnv.hpp diff --git a/include/xo/expression/Environment.hpp b/include/xo/expression/Environment.hpp new file mode 100644 index 00000000..834366c7 --- /dev/null +++ b/include/xo/expression/Environment.hpp @@ -0,0 +1,23 @@ +/* file Environment.hpp + * + * author: Roland Conybeare, Jun 2024 + */ + +#pragma once + +#include "xo/refcnt/Refcounted.hpp" +#include "Variable.hpp" + +namespace xo { + namespace ast { + class Environment : public ref::Refcount { + public: + /** lookup variable-expression @p vname in this environment + **/ + virtual ref::brw lookup_var(const std::string & vname) const = 0; + }; + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end Environment.hpp */ diff --git a/include/xo/expression/GlobalEnv.hpp b/include/xo/expression/GlobalEnv.hpp new file mode 100644 index 00000000..223fc9df --- /dev/null +++ b/include/xo/expression/GlobalEnv.hpp @@ -0,0 +1,46 @@ +/* file GlobalEnv.hpp + * + * author: Roland Conybeare, Jun 2024 + */ + +#pragma once + +#include "Environment.hpp" +#include + +namespace xo { + namespace ast { + class GlobalEnv : public Environment { + public: + ref::brw require_global(ref::brw var) { + const std::string & vname = var->name(); + + auto ix = var_map_.find(vname); + + if (ix == var_map_.end()) { + var_map_[vname] = var.get(); + return var; + } else { + return ix->second; + } + } /*require_global*/ + + // ----- Environment ----- + + virtual ref::brw lookup_var(const std::string & vname) const { + auto ix = var_map_.find(vname); + + if (ix == var_map_.end()) + return ref::brw::from_native(nullptr); + + return ix->second; + } + + private: + std::map> var_map_; + }; + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end GlobalEnv.hpp */ diff --git a/include/xo/expression/LocalEnv.hpp b/include/xo/expression/LocalEnv.hpp new file mode 100644 index 00000000..7f5f12dd --- /dev/null +++ b/include/xo/expression/LocalEnv.hpp @@ -0,0 +1,70 @@ +/* file LocalEnv.hpp + * + * author: Roland Conybeare, Jun 2024 + */ + +#pragma once + +#include "Environment.hpp" + +namespace xo { + namespace ast { + /** @brief LocalEnv + * + * @class Local environment for a lambda. + * Lists the Variables corresponding to this lambda's formal + * parameters, but also links to @ref Environment for + * innermost enclosing @ref Lambda. + **/ + class LocalEnv : public Environment { + public: + using TypeDescr = xo::reflect::TypeDescr; + + public: + /** named ctor idiom. Create instance with local variables per @p argv **/ + static ref::rp make(const std::vector> & argv) { + return new LocalEnv(argv); + } + + const std::vector> & argv() const { return argv_; } + int n_arg() const { return argv_.size(); } + TypeDescr fn_arg(uint32_t i) const { return argv_[i]->valuetype(); } + + /** single-assign this environment's parent **/ + void assign_parent(ref::brw p) { + assert(parent_env_.get() == nullptr); + parent_env_ = p.get(); + } + + // ----- Environment ----- + + virtual ref::brw lookup_var(const std::string & target) const override { + for (const auto & arg : argv_) { + if (arg->name() == target) + return arg; + } + + /* here: target not found in local vars, + * delegate to innermost ancestor + */ + return parent_env_->lookup_var(target); + } + + private: + LocalEnv(const std::vector> & argv) + : argv_(argv) {} + + private: + /** formal argument names **/ + std::vector> argv_; + + /** parent environment. Free variable in this lambda's + * body, will be resolved by referring them to @ref parent_env_. + **/ + ref::rp parent_env_; + }; + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end LocalEnv.hpp */ From a877af562ae1981214c8aca79df2137ea3347273 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 30 Jun 2024 19:11:40 -0400 Subject: [PATCH 25/58] xo-expression: + Expression::attach_envs() --- include/xo/expression/Apply.hpp | 7 ++++++ include/xo/expression/ConstantInterface.hpp | 6 +++++ include/xo/expression/Expression.hpp | 21 ++++++++++++++++ include/xo/expression/IfExpr.hpp | 14 +++++++++++ include/xo/expression/Lambda.hpp | 25 ++++++++++++++------ include/xo/expression/PrimitiveInterface.hpp | 2 ++ include/xo/expression/Variable.hpp | 7 ++++++ src/expression/Lambda.cpp | 14 ++++++----- 8 files changed, 83 insertions(+), 13 deletions(-) diff --git a/include/xo/expression/Apply.hpp b/include/xo/expression/Apply.hpp index b7cb8c31..666ca5c7 100644 --- a/include/xo/expression/Apply.hpp +++ b/include/xo/expression/Apply.hpp @@ -49,6 +49,13 @@ namespace xo { return n; } + virtual void attach_envs(ref::brw p) override { + fn_->attach_envs(p); + + for (const auto & arg : argv_) + arg->attach_envs(p); + } + virtual void display(std::ostream & os) const override; private: diff --git a/include/xo/expression/ConstantInterface.hpp b/include/xo/expression/ConstantInterface.hpp index 586322c7..cb38f50d 100644 --- a/include/xo/expression/ConstantInterface.hpp +++ b/include/xo/expression/ConstantInterface.hpp @@ -33,6 +33,12 @@ namespace xo { virtual TypeDescr value_td() const = 0; /** reflection-tagged pointer to literal value of this constant **/ virtual TaggedPtr value_tp() const = 0; + + // ----- Expression ----- + + virtual void attach_envs(ref::brw /*p*/) override {} + + }; /*ConstantInterface*/ } /*namespace ast*/ diff --git a/include/xo/expression/Expression.hpp b/include/xo/expression/Expression.hpp index b38eecbd..403c2403 100644 --- a/include/xo/expression/Expression.hpp +++ b/include/xo/expression/Expression.hpp @@ -9,9 +9,14 @@ #include "xo/refcnt/Refcounted.hpp" #include "exprtype.hpp" #include +#include namespace xo { namespace ast { + class Variable; /* see Variable.hpp */ + class Lambda; /* see Lamnbda.hpp */ + class Environment; /* see Environment.hpp */ + /** @class Expression * @brief abstract syntax tree for an EGAD program * @@ -47,6 +52,22 @@ namespace xo { **/ virtual std::size_t visit_preorder(VisitFn visitor_fn) = 0; + /** attach an environment to each lambda expression X in this subtree, + * that will: + * - resolve names matching X's arguments (formal parameters) to + * from @p X.argv + * - resolve free variables from @p parent + **/ + virtual void attach_envs(ref::brw parent) = 0; + + /** append to *p_set the set of free variables in this expression. + * returns the number of free variables introduced + * + * @param env stack of lexcically-enclosing lamnbda expressions, + * in nesting order, i.e. outermost first, innertmost last + **/ + //virtual std::int32_t find_free_vars(std::vector> env) = 0; + /** write human-readable representation to stream **/ virtual void display(std::ostream & os) const = 0; /** human-readable string representation **/ diff --git a/include/xo/expression/IfExpr.hpp b/include/xo/expression/IfExpr.hpp index 6f397b3d..f962c905 100644 --- a/include/xo/expression/IfExpr.hpp +++ b/include/xo/expression/IfExpr.hpp @@ -51,6 +51,20 @@ namespace xo { return n; } + virtual void attach_envs(ref::brw p) override { + test_->attach_envs(p); + when_true_->attach_envs(p); + when_false_->attach_envs(p); + } + +#ifdef NOT_USING + virtual std::int32_t find_free_vars(std::set> * p_set) override { + return (test_->find_free_vars(p_set) + + when_true_->find_free_vars(p_set) + + when_false_->find_free_vars(p_set)); + } +#endif + virtual void display(std::ostream & os) const override; private: diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 20c4425b..791f3548 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -8,6 +8,7 @@ #include "Expression.hpp" #include "FunctionInterface.hpp" #include "Variable.hpp" +#include "LocalEnv.hpp" #include #include //#include @@ -35,16 +36,16 @@ namespace xo { } const std::string & type_str() const { return type_str_; } - const std::vector> & argv() const { return argv_; } + const std::vector> & argv() const { return local_env_->argv(); } const ref::rp & body() const { return body_; } // ----- FunctionInterface ----- virtual const std::string & name() const override { return name_; } /** return number of arguments expected by this function **/ - virtual int n_arg() const override { return argv_.size(); } + virtual int n_arg() const override { return local_env_->n_arg(); } virtual TypeDescr fn_retval() const override { return body_->valuetype(); } - virtual TypeDescr fn_arg(uint32_t i) const override { return argv_[i]->valuetype(); } + virtual TypeDescr fn_arg(uint32_t i) const override { return local_env_->fn_arg(i); } // ----- Expression ----- @@ -53,7 +54,7 @@ namespace xo { visitor_fn(this); - for (const auto & arg : argv_) + for (const auto & arg : local_env_->argv()) n += arg->visit_preorder(visitor_fn); n += body_->visit_preorder(visitor_fn); @@ -61,6 +62,10 @@ namespace xo { return n; } + virtual void attach_envs(ref::brw p) override { + local_env_->assign_parent(p); + } + virtual void display(std::ostream & os) const override; private: @@ -69,7 +74,7 @@ namespace xo { **/ Lambda(const std::string & name, TypeDescr lambda_type, - const std::vector> & argv, + const ref::rp & local_env, const ref::rp & body); private: @@ -85,10 +90,16 @@ namespace xo { * "double(double,double)" for function of two doubles that returns a double **/ std::string type_str_; - /** formal argument names **/ - std::vector> argv_; /** function body **/ ref::rp body_; + + /** established (once) by @ref attach_envs. + * + * @note data dependency on ancestor expressions that don't exist yet + * when Lambda constructor runs, so we need to assign @ref local_env_ + * later. + **/ + ref::rp local_env_; }; /*Lambda*/ inline ref::rp diff --git a/include/xo/expression/PrimitiveInterface.hpp b/include/xo/expression/PrimitiveInterface.hpp index 907aef2d..184a70d8 100644 --- a/include/xo/expression/PrimitiveInterface.hpp +++ b/include/xo/expression/PrimitiveInterface.hpp @@ -53,6 +53,8 @@ namespace xo { return 1; } + virtual void attach_envs(ref::brw /*p*/) override {} + private: }; /*PrimitiveInterface*/ } /*namespace ast*/ diff --git a/include/xo/expression/Variable.hpp b/include/xo/expression/Variable.hpp index 13e46a98..c4a2eb9a 100644 --- a/include/xo/expression/Variable.hpp +++ b/include/xo/expression/Variable.hpp @@ -36,6 +36,13 @@ namespace xo { return 1; } + virtual void attach_envs(ref::brw /*p*/) override {} + +#ifdef NOT_USING + virtual std::int32_t find_free_vars(std::set> * p_set) override { + } +#endif + virtual void display(std::ostream & os) const override; private: diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index 4cd25da9..7ad70ceb 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -41,23 +41,23 @@ namespace xo { return new Lambda(name, lambda_td, - argv, + LocalEnv::make(argv), body); } /*make*/ Lambda::Lambda(const std::string & name, TypeDescr lambda_type, - const std::vector> & argv, + const rp & local_env, const ref::rp & body) : FunctionInterface(exprtype::lambda, lambda_type), name_{name}, - argv_{argv}, - body_{body} + body_{body}, + local_env_{local_env} { stringstream ss; ss << "double"; ss << "("; - for (std::size_t i = 0; i < argv.size(); ++i) { + for (std::size_t i = 0, n = this->n_arg(); i < n; ++i) { if (i > 0) ss << ","; ss << "double"; @@ -65,13 +65,15 @@ namespace xo { ss << ")"; type_str_ = ss.str(); + + body_->attach_envs(local_env_); } /*ctor*/ void Lambda::display(std::ostream & os) const { os << "argv()) << xtag("body", body_) << ">"; } /*display*/ From 28884e1f4f97ed8cdde0c7ebcc1f20698c923963 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 30 Jun 2024 19:33:18 -0400 Subject: [PATCH 26/58] xo-expression: refactor: use GlobalEnv for MachPipeline::global_env --- include/xo/expression/Environment.hpp | 5 ++-- include/xo/expression/GlobalEnv.hpp | 34 +++++++++++++++------------ include/xo/expression/LocalEnv.hpp | 2 +- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/include/xo/expression/Environment.hpp b/include/xo/expression/Environment.hpp index 834366c7..6fec9b32 100644 --- a/include/xo/expression/Environment.hpp +++ b/include/xo/expression/Environment.hpp @@ -12,9 +12,10 @@ namespace xo { namespace ast { class Environment : public ref::Refcount { public: - /** lookup variable-expression @p vname in this environment + /** lookup variable-expression @p vname in this environment. + * returns llvm::Value representing code that produces a value for vname **/ - virtual ref::brw lookup_var(const std::string & vname) const = 0; + virtual ref::brw lookup_var(const std::string & vname) const = 0; }; } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/GlobalEnv.hpp b/include/xo/expression/GlobalEnv.hpp index 223fc9df..fbc9ef9c 100644 --- a/include/xo/expression/GlobalEnv.hpp +++ b/include/xo/expression/GlobalEnv.hpp @@ -12,32 +12,36 @@ namespace xo { namespace ast { class GlobalEnv : public Environment { public: - ref::brw require_global(ref::brw var) { - const std::string & vname = var->name(); + /** create instance. Probably only need one of these **/ + static ref::rp make() { return new GlobalEnv(); } - auto ix = var_map_.find(vname); - - if (ix == var_map_.end()) { - var_map_[vname] = var.get(); - return var; - } else { - return ix->second; - } + ref::brw require_global(const std::string & vname, + ref::brw expr) { + global_map_[vname] = expr.get(); + return expr; } /*require_global*/ // ----- Environment ----- - virtual ref::brw lookup_var(const std::string & vname) const { - auto ix = var_map_.find(vname); + virtual ref::brw lookup_var(const std::string & vname) const { + auto ix = global_map_.find(vname); - if (ix == var_map_.end()) - return ref::brw::from_native(nullptr); + if (ix == global_map_.end()) { + /* not found */ + return ref::brw::from_native(nullptr); + } return ix->second; } private: - std::map> var_map_; + GlobalEnv() = default; + + private: + /* for assignable globals, need to allocate memory + * addresses for these. + */ + std::map> global_map_; }; } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/LocalEnv.hpp b/include/xo/expression/LocalEnv.hpp index 7f5f12dd..b206bddf 100644 --- a/include/xo/expression/LocalEnv.hpp +++ b/include/xo/expression/LocalEnv.hpp @@ -38,7 +38,7 @@ namespace xo { // ----- Environment ----- - virtual ref::brw lookup_var(const std::string & target) const override { + virtual ref::brw lookup_var(const std::string & target) const override { for (const auto & arg : argv_) { if (arg->name() == target) return arg; From b7db7c54549d6ad52ecbd1b4ac5feee8b9eeaac3 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 1 Jul 2024 16:50:17 -0400 Subject: [PATCH 27/58] xo-expression: + Expression::get_free_variables() --- include/xo/expression/Apply.hpp | 14 ++++++++++++++ include/xo/expression/ConstantInterface.hpp | 4 ++++ include/xo/expression/Expression.hpp | 6 ++++++ include/xo/expression/IfExpr.hpp | 15 +++++++++++++++ include/xo/expression/Lambda.hpp | 10 ++++++++++ include/xo/expression/PrimitiveInterface.hpp | 4 ++++ include/xo/expression/Variable.hpp | 6 ++++++ 7 files changed, 59 insertions(+) diff --git a/include/xo/expression/Apply.hpp b/include/xo/expression/Apply.hpp index 666ca5c7..b6db3a0c 100644 --- a/include/xo/expression/Apply.hpp +++ b/include/xo/expression/Apply.hpp @@ -36,6 +36,20 @@ namespace xo { const ref::rp & fn() const { return fn_; } const std::vector> & argv() const { return argv_; } + virtual std::set get_free_variables() const override { + std::set retval = fn_->get_free_variables(); + + for (const auto & arg : argv_) { + std::set arg_free_set + = arg->get_free_variables(); + + for (const auto & name : arg_free_set) + retval.insert(name); + } + + return retval; + } + virtual std::size_t visit_preorder(VisitFn visitor_fn) override { std::size_t n = 1; diff --git a/include/xo/expression/ConstantInterface.hpp b/include/xo/expression/ConstantInterface.hpp index cb38f50d..0b10ec41 100644 --- a/include/xo/expression/ConstantInterface.hpp +++ b/include/xo/expression/ConstantInterface.hpp @@ -36,6 +36,10 @@ namespace xo { // ----- Expression ----- + virtual std::set get_free_variables() const override { + return std::set(); + } + virtual void attach_envs(ref::brw /*p*/) override {} diff --git a/include/xo/expression/Expression.hpp b/include/xo/expression/Expression.hpp index 403c2403..d23a412e 100644 --- a/include/xo/expression/Expression.hpp +++ b/include/xo/expression/Expression.hpp @@ -45,6 +45,12 @@ namespace xo { exprtype extype() const { return extype_; } TypeDescr valuetype() const { return valuetype_; } + /** find free named variables in this expression. + * comprises the set of names that don't match formal parameters in + * enclosing lambdas. + **/ + virtual std::set get_free_variables() const = 0; + /** visit each Expression node in this AST, * and invoke @p fn for each. * Returns the number of nodes visited. diff --git a/include/xo/expression/IfExpr.hpp b/include/xo/expression/IfExpr.hpp index f962c905..fc8204a7 100644 --- a/include/xo/expression/IfExpr.hpp +++ b/include/xo/expression/IfExpr.hpp @@ -40,6 +40,21 @@ namespace xo { // ----- Expression ----- + virtual std::set get_free_variables() const override { + std::set retval = test_->get_free_variables(); + + std::set free_vars; + free_vars = when_true_->get_free_variables(); + for (const auto & s : free_vars) + retval.insert(s); + + free_vars = when_false_->get_free_variables(); + for (const auto & s : free_vars) + retval.insert(s); + + return retval; + } + virtual std::size_t visit_preorder(VisitFn visitor_fn) override { std::size_t n = 1; diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 791f3548..a7f7529d 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -49,6 +49,16 @@ namespace xo { // ----- Expression ----- + virtual std::set get_free_variables() const override { + std::set retval = body_->get_free_variables(); + + /* but remove formals. */ + for (const auto & var : local_env_->argv()) + retval.erase(var->name()); + + return retval; + } + virtual std::size_t visit_preorder(VisitFn visitor_fn) override { std::size_t n = 1; diff --git a/include/xo/expression/PrimitiveInterface.hpp b/include/xo/expression/PrimitiveInterface.hpp index 184a70d8..c52eb979 100644 --- a/include/xo/expression/PrimitiveInterface.hpp +++ b/include/xo/expression/PrimitiveInterface.hpp @@ -48,6 +48,10 @@ namespace xo { // ----- Expression ----- + virtual std::set get_free_variables() const override { + return std::set(); + } + virtual std::size_t visit_preorder(VisitFn visitor_fn) override { visitor_fn(this); return 1; diff --git a/include/xo/expression/Variable.hpp b/include/xo/expression/Variable.hpp index c4a2eb9a..e8181a8c 100644 --- a/include/xo/expression/Variable.hpp +++ b/include/xo/expression/Variable.hpp @@ -31,6 +31,12 @@ namespace xo { const std::string & name() const { return name_; } + virtual std::set get_free_variables() const override { + std::set retval; + retval.insert(this->name_); + return retval; + } + virtual std::size_t visit_preorder(VisitFn visitor_fn) override { visitor_fn(this); return 1; From 97264b726f2ec295bc548676ed186848d5013dbb Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 1 Jul 2024 20:53:22 -0400 Subject: [PATCH 28/58] xo-expression: Lambda caches free var set + ::needs_closure() method --- include/xo/expression/Lambda.hpp | 16 +++++++++------- src/expression/Lambda.cpp | 16 +++++++++++++++- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index a7f7529d..619913a1 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -39,6 +39,8 @@ namespace xo { const std::vector> & argv() const { return local_env_->argv(); } const ref::rp & body() const { return body_; } + bool needs_closure_flag() const { return !free_var_set_.empty(); } + // ----- FunctionInterface ----- virtual const std::string & name() const override { return name_; } @@ -50,13 +52,7 @@ namespace xo { // ----- Expression ----- virtual std::set get_free_variables() const override { - std::set retval = body_->get_free_variables(); - - /* but remove formals. */ - for (const auto & var : local_env_->argv()) - retval.erase(var->name()); - - return retval; + return this->free_var_set_; } virtual std::size_t visit_preorder(VisitFn visitor_fn) override { @@ -87,6 +83,9 @@ namespace xo { const ref::rp & local_env, const ref::rp & body); + /** compute free-variable set for this lambda **/ + std::set calc_free_variables() const; + private: /** lambda name. Initially supporting only form like * (define (foo x y z) @@ -103,6 +102,9 @@ namespace xo { /** function body **/ ref::rp body_; + /** free variables for this lambda **/ + std::set free_var_set_; + /** established (once) by @ref attach_envs. * * @note data dependency on ancestor expressions that don't exist yet diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index 7ad70ceb..d2fe8222 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -45,6 +45,19 @@ namespace xo { body); } /*make*/ + std::set + Lambda::calc_free_variables() const + { + std::set retval + = body_->get_free_variables(); + + /* but remove formals. */ + for (const auto & var : local_env_->argv()) + retval.erase(var->name()); + + return retval; + } /*calc_free_variables*/ + Lambda::Lambda(const std::string & name, TypeDescr lambda_type, const rp & local_env, @@ -64,7 +77,8 @@ namespace xo { } ss << ")"; - type_str_ = ss.str(); + this->type_str_ = ss.str(); + this->free_var_set_ = this->calc_free_variables(); body_->attach_envs(local_env_); } /*ctor*/ From bf60c704da575c98faf0275ae401e0b1019a3141 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 2 Jul 2024 14:19:57 -0400 Subject: [PATCH 29/58] xo-expression: + ptr to originating lambda --- include/xo/expression/LocalEnv.hpp | 22 ++++++++++++++++++++-- include/xo/expression/Variable.hpp | 5 ----- src/expression/Lambda.cpp | 16 ++++++++++++---- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/include/xo/expression/LocalEnv.hpp b/include/xo/expression/LocalEnv.hpp index b206bddf..2a7a3045 100644 --- a/include/xo/expression/LocalEnv.hpp +++ b/include/xo/expression/LocalEnv.hpp @@ -9,6 +9,8 @@ namespace xo { namespace ast { + class Lambda; + /** @brief LocalEnv * * @class Local environment for a lambda. @@ -26,10 +28,17 @@ namespace xo { return new LocalEnv(argv); } + Lambda * owner() const { return owner_; } const std::vector> & argv() const { return argv_; } int n_arg() const { return argv_.size(); } TypeDescr fn_arg(uint32_t i) const { return argv_[i]->valuetype(); } + /** single-assign this environment's owner **/ + void assign_owner(Lambda * p) { + assert(owner_ == nullptr); + owner_ = p; + } + /** single-assign this environment's parent **/ void assign_parent(ref::brw p) { assert(parent_env_.get() == nullptr); @@ -55,11 +64,20 @@ namespace xo { : argv_(argv) {} private: + /** Lambnda for which this environment created. + * + * Invariant: + * @code + * owner_->local_env_ == this + * @endcode + **/ + Lambda * owner_ = nullptr; + /** formal argument names **/ std::vector> argv_; - /** parent environment. Free variable in this lambda's - * body, will be resolved by referring them to @ref parent_env_. + /** parent environment. A free variable in this lambda's + * body will be resolved by referring them to @ref parent_env_. **/ ref::rp parent_env_; }; diff --git a/include/xo/expression/Variable.hpp b/include/xo/expression/Variable.hpp index e8181a8c..471eb873 100644 --- a/include/xo/expression/Variable.hpp +++ b/include/xo/expression/Variable.hpp @@ -44,11 +44,6 @@ namespace xo { virtual void attach_envs(ref::brw /*p*/) override {} -#ifdef NOT_USING - virtual std::int32_t find_free_vars(std::set> * p_set) override { - } -#endif - virtual void display(std::ostream & os) const override; private: diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index d2fe8222..6c8afafd 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -39,10 +39,18 @@ namespace xo { TypeDescr lambda_td = TypeDescrBase::require_by_fn_info(function_info); - return new Lambda(name, - lambda_td, - LocalEnv::make(argv), - body); + rp env = LocalEnv::make(argv); + + rp retval + = new Lambda(name, + lambda_td, + env, + body); + + /* need two-phase construction b/c pointer cycle */ + env->assign_owner(retval.get()); + + return retval; } /*make*/ std::set From a94c55304bbf57c4e9e89d09c532691207211b19 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 2 Jul 2024 14:22:11 -0400 Subject: [PATCH 30/58] xo-expression: refactor: LocalEnv::owner -> origin --- include/xo/expression/LocalEnv.hpp | 14 +++++++------- src/expression/Lambda.cpp | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/xo/expression/LocalEnv.hpp b/include/xo/expression/LocalEnv.hpp index 2a7a3045..42a36f2e 100644 --- a/include/xo/expression/LocalEnv.hpp +++ b/include/xo/expression/LocalEnv.hpp @@ -28,15 +28,15 @@ namespace xo { return new LocalEnv(argv); } - Lambda * owner() const { return owner_; } + Lambda * origin() const { return origin_; } const std::vector> & argv() const { return argv_; } int n_arg() const { return argv_.size(); } TypeDescr fn_arg(uint32_t i) const { return argv_[i]->valuetype(); } - /** single-assign this environment's owner **/ - void assign_owner(Lambda * p) { - assert(owner_ == nullptr); - owner_ = p; + /** single-assign this environment's origin **/ + void assign_origin(Lambda * p) { + assert(origin_ == nullptr); + origin_ = p; } /** single-assign this environment's parent **/ @@ -68,10 +68,10 @@ namespace xo { * * Invariant: * @code - * owner_->local_env_ == this + * origin_->local_env_ == this * @endcode **/ - Lambda * owner_ = nullptr; + Lambda * origin_ = nullptr; /** formal argument names **/ std::vector> argv_; diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index 6c8afafd..2f84b178 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -48,7 +48,7 @@ namespace xo { body); /* need two-phase construction b/c pointer cycle */ - env->assign_owner(retval.get()); + env->assign_origin(retval.get()); return retval; } /*make*/ From 14796663b1d91a1593ee30c221281c635d2d0822 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 2 Jul 2024 16:57:07 -0400 Subject: [PATCH 31/58] xo-expression: + binding_path + assoc w/ each Variable --- include/xo/expression/Environment.hpp | 15 +++++++++++++ include/xo/expression/GlobalEnv.hpp | 12 +++++++++- include/xo/expression/LocalEnv.hpp | 6 ++++- include/xo/expression/Variable.hpp | 7 +++++- include/xo/expression/binding_path.hpp | 29 ++++++++++++++++++++++++ src/expression/CMakeLists.txt | 1 + src/expression/LocalEnv.cpp | 31 ++++++++++++++++++++++++++ src/expression/Variable.cpp | 7 ++++++ 8 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 include/xo/expression/binding_path.hpp create mode 100644 src/expression/LocalEnv.cpp diff --git a/include/xo/expression/Environment.hpp b/include/xo/expression/Environment.hpp index 6fec9b32..6baded6d 100644 --- a/include/xo/expression/Environment.hpp +++ b/include/xo/expression/Environment.hpp @@ -12,6 +12,21 @@ namespace xo { namespace ast { class Environment : public ref::Refcount { public: + /** true if this is toplevel (global) environment. + * Toplevel environment doesn't have slot numbers. + * + * Variables that bind in the global environment have unique + * names, which we rely on instead of slot numbers. + **/ + virtual bool is_global_env() const = 0; + + /** lookup binding path for @p vname in this environment. + * + * Reports ingredients needed to address variable at runtime, + * in runtime analog of this environment + **/ + virtual binding_path lookup_binding(const std::string & vname) const = 0; + /** lookup variable-expression @p vname in this environment. * returns llvm::Value representing code that produces a value for vname **/ diff --git a/include/xo/expression/GlobalEnv.hpp b/include/xo/expression/GlobalEnv.hpp index fbc9ef9c..08eb9355 100644 --- a/include/xo/expression/GlobalEnv.hpp +++ b/include/xo/expression/GlobalEnv.hpp @@ -7,6 +7,7 @@ #include "Environment.hpp" #include +#include namespace xo { namespace ast { @@ -23,7 +24,16 @@ namespace xo { // ----- Environment ----- - virtual ref::brw lookup_var(const std::string & vname) const { + virtual bool is_global_env() const override { return true; } + + virtual binding_path lookup_binding(const std::string & vname) const override { + /* i_link: -1 for global environment + * j_slot: not used + */ + return { -1, 0 }; + } + + virtual ref::brw lookup_var(const std::string & vname) const override { auto ix = global_map_.find(vname); if (ix == global_map_.end()) { diff --git a/include/xo/expression/LocalEnv.hpp b/include/xo/expression/LocalEnv.hpp index 42a36f2e..d8fe2178 100644 --- a/include/xo/expression/LocalEnv.hpp +++ b/include/xo/expression/LocalEnv.hpp @@ -47,6 +47,10 @@ namespace xo { // ----- Environment ----- + virtual bool is_global_env() const override { return false; } + + virtual binding_path lookup_binding(const std::string & vname) const override; + virtual ref::brw lookup_var(const std::string & target) const override { for (const auto & arg : argv_) { if (arg->name() == target) @@ -61,7 +65,7 @@ namespace xo { private: LocalEnv(const std::vector> & argv) - : argv_(argv) {} + : origin_{nullptr}, argv_(argv) {} private: /** Lambnda for which this environment created. diff --git a/include/xo/expression/Variable.hpp b/include/xo/expression/Variable.hpp index 471eb873..3f45677e 100644 --- a/include/xo/expression/Variable.hpp +++ b/include/xo/expression/Variable.hpp @@ -6,6 +6,7 @@ #pragma once #include "Expression.hpp" +#include "binding_path.hpp" namespace xo { namespace ast { @@ -42,7 +43,7 @@ namespace xo { return 1; } - virtual void attach_envs(ref::brw /*p*/) override {} + virtual void attach_envs(ref::brw /*p*/) override; virtual void display(std::ostream & os) const override; @@ -55,6 +56,10 @@ namespace xo { private: /** variable name **/ std::string name_; + /** navigate environment via this path to find runtime memory + * location for this variable + **/ + binding_path path_; }; /*Variable*/ inline ref::rp diff --git a/include/xo/expression/binding_path.hpp b/include/xo/expression/binding_path.hpp new file mode 100644 index 00000000..a3692ed1 --- /dev/null +++ b/include/xo/expression/binding_path.hpp @@ -0,0 +1,29 @@ +/* file binding_path.hpp + * + * author: Roland Conybeare, Jul 2024 + */ + +#pragma once + +namespace xo { + namespace ast { + /** @class path + * + * @brief path from the *use* of a variable to the environment + * providing its location. + **/ + struct binding_path { + /** @of parent links to traverse. -1 if global **/ + int i_link_ = -1; + /** for variables bound in some local environment: + * slot# within that environment. + * + * Ignored if @ref i_link_ is -1 + **/ + int j_slot_ = 0; + }; /*binding_path*/ + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end binding_path.hpp */ diff --git a/src/expression/CMakeLists.txt b/src/expression/CMakeLists.txt index ef5cf6f2..50edec01 100644 --- a/src/expression/CMakeLists.txt +++ b/src/expression/CMakeLists.txt @@ -7,6 +7,7 @@ set(SELF_SRCS Lambda.cpp Variable.cpp IfExpr.cpp + LocalEnv.cpp ) xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) diff --git a/src/expression/LocalEnv.cpp b/src/expression/LocalEnv.cpp new file mode 100644 index 00000000..fb724aeb --- /dev/null +++ b/src/expression/LocalEnv.cpp @@ -0,0 +1,31 @@ +/* file LocalEnv.cpp + * + * author: Roland Conybeare + */ + +#include "LocalEnv.hpp" + +namespace xo { + namespace ast { + binding_path + LocalEnv::lookup_binding(const std::string & vname) const + { + int j_slot = 0; + for (const auto & arg : argv_) { + if (arg->name() == vname) + return { 0 /*i_link*/, j_slot }; + ++j_slot; + } + + auto tmp = parent_env_->lookup_binding(vname); + + if (tmp.i_link_ == -1) + return tmp; + else + return { tmp.i_link_ + 1, tmp.j_slot_ }; + } /*lookup_binding*/ + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end LocalEnv.cpp */ diff --git a/src/expression/Variable.cpp b/src/expression/Variable.cpp index 52890ccc..71d47655 100644 --- a/src/expression/Variable.cpp +++ b/src/expression/Variable.cpp @@ -1,9 +1,16 @@ /* @file Variable.cpp */ #include "Variable.hpp" +#include "Environment.hpp" namespace xo { namespace ast { + void + Variable::attach_envs(ref::brw e) { + /** e makes accessible all enclosing lexical scopes **/ + this->path_ = e->lookup_binding(this->name_); + } /*attach_envs*/ + void Variable::display(std::ostream & os) const { os << " Date: Wed, 3 Jul 2024 14:11:02 -0400 Subject: [PATCH 32/58] xo-exprssion: + Expression::xform_layer() --- include/xo/expression/Apply.hpp | 9 ++++++ include/xo/expression/Constant.hpp | 4 +++ include/xo/expression/Expression.hpp | 8 ++++- include/xo/expression/IfExpr.hpp | 9 ++++++ include/xo/expression/Lambda.hpp | 5 +++ include/xo/expression/PrimitiveInterface.hpp | 4 +++ include/xo/expression/Variable.hpp | 4 +++ src/expression/Lambda.cpp | 33 ++++++++++++++++++-- 8 files changed, 72 insertions(+), 4 deletions(-) diff --git a/include/xo/expression/Apply.hpp b/include/xo/expression/Apply.hpp index b6db3a0c..1ed06e6b 100644 --- a/include/xo/expression/Apply.hpp +++ b/include/xo/expression/Apply.hpp @@ -63,6 +63,15 @@ namespace xo { return n; } + virtual ref::rp xform_layer(TransformFn xform_fn) override { + this->fn_ = fn_->xform_layer(xform_fn); + + for (auto & arg : argv_) + arg = arg->xform_layer(xform_fn); + + return xform_fn(this); + } + virtual void attach_envs(ref::brw p) override { fn_->attach_envs(p); diff --git a/include/xo/expression/Constant.hpp b/include/xo/expression/Constant.hpp index 50f4c2ba..ffc15967 100644 --- a/include/xo/expression/Constant.hpp +++ b/include/xo/expression/Constant.hpp @@ -56,6 +56,10 @@ namespace xo { return 1; } + virtual ref::rp xform_layer(TransformFn xform_fn) override { + return xform_fn(this); + } + virtual void display(std::ostream & os) const override { os << "short_name()) diff --git a/include/xo/expression/Expression.hpp b/include/xo/expression/Expression.hpp index d23a412e..84248555 100644 --- a/include/xo/expression/Expression.hpp +++ b/include/xo/expression/Expression.hpp @@ -35,7 +35,10 @@ namespace xo { **/ class Expression : public ref::Refcount { public: - using VisitFn = std::function)>; + using VisitFn = std::function + )>; + using TransformFn = std::function + (ref::brw)>; using TypeDescr = xo::reflect::TypeDescr; public: @@ -58,6 +61,9 @@ namespace xo { **/ virtual std::size_t visit_preorder(VisitFn visitor_fn) = 0; + /** traverse ast @ref visit_preorder but do not visit Lambdas **/ + virtual ref::rp xform_layer(TransformFn visitor_fn) = 0; + /** attach an environment to each lambda expression X in this subtree, * that will: * - resolve names matching X's arguments (formal parameters) to diff --git a/include/xo/expression/IfExpr.hpp b/include/xo/expression/IfExpr.hpp index fc8204a7..8091481e 100644 --- a/include/xo/expression/IfExpr.hpp +++ b/include/xo/expression/IfExpr.hpp @@ -60,12 +60,21 @@ namespace xo { visitor_fn(this); + n += this->test_->visit_preorder(visitor_fn); n += this->when_true_->visit_preorder(visitor_fn); n += this->when_false_->visit_preorder(visitor_fn); return n; } + virtual ref::rp xform_layer(TransformFn xform_fn) override { + this->test_ = this->test_->xform_layer(xform_fn); + this->when_true_ = this->when_true_->xform_layer(xform_fn); + this->when_false_= this->when_false_->xform_layer(xform_fn); + + return xform_fn(this); + } + virtual void attach_envs(ref::brw p) override { test_->attach_envs(p); when_true_->attach_envs(p); diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 619913a1..18039982 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -68,6 +68,11 @@ namespace xo { return n; } + virtual ref::rp xform_layer(TransformFn /*xform_fn*/) override { + /* a layer is bounded by lambdas, don't enter them */ + return this; + } + virtual void attach_envs(ref::brw p) override { local_env_->assign_parent(p); } diff --git a/include/xo/expression/PrimitiveInterface.hpp b/include/xo/expression/PrimitiveInterface.hpp index c52eb979..70080607 100644 --- a/include/xo/expression/PrimitiveInterface.hpp +++ b/include/xo/expression/PrimitiveInterface.hpp @@ -57,6 +57,10 @@ namespace xo { return 1; } + virtual ref::rp xform_layer(TransformFn xform_fn) override { + return xform_fn(this); + } + virtual void attach_envs(ref::brw /*p*/) override {} private: diff --git a/include/xo/expression/Variable.hpp b/include/xo/expression/Variable.hpp index 3f45677e..713da66f 100644 --- a/include/xo/expression/Variable.hpp +++ b/include/xo/expression/Variable.hpp @@ -43,6 +43,10 @@ namespace xo { return 1; } + virtual ref::rp xform_layer(TransformFn xform_fn) override { + return xform_fn(this); + } + virtual void attach_envs(ref::brw /*p*/) override; virtual void display(std::ostream & os) const override; diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index 2f84b178..b2e4d1f3 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -4,6 +4,7 @@ #include "xo/reflect/TypeDescr.hpp" #include "xo/reflect/function/FunctionTdx.hpp" #include "xo/indentlog/print/vector.hpp" +#include namespace xo { using xo::reflect::TypeDescrBase; @@ -25,10 +26,12 @@ namespace xo { **/ std::vector arg_td_v; - arg_td_v.reserve(argv.size()); + { + arg_td_v.reserve(argv.size()); - for (const auto & arg : argv) { - arg_td_v.push_back(arg->valuetype()); + for (const auto & arg : argv) { + arg_td_v.push_back(arg->valuetype()); + } } auto function_info @@ -85,6 +88,30 @@ namespace xo { } ss << ")"; + /* regularize local_env+body: make sure exactly one instance + * (i.e. with object identity) of a Variable appears + * within one layer of a lambda body. + * + * Here 'layer' means excluding appearance in any nested lambdas + * (i.e. whether or not such appearance would resolve to the same + * memory location). + * + * Motivation is to unify Variables that would use the same + * binding_path to resolve their runtime location. + */ + { + std::map> var_map; + + for (const auto & arg : local_env_->argv()) { + /* each arg name can appear at most once + * in a particular lambda's parameter list + */ + assert(var_map.find(arg->name()) == var_map.end()); + + var_map[arg->name()] = arg; + } + } + this->type_str_ = ss.str(); this->free_var_set_ = this->calc_free_variables(); From fdfe2e7270e32c735b6ad8e8d657807f81fc4a8d Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 3 Jul 2024 14:31:47 -0400 Subject: [PATCH 33/58] xo-expression: unify variables within each lambda layer --- src/expression/Lambda.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index b2e4d1f3..30b579cf 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -110,6 +110,28 @@ namespace xo { var_map[arg->name()] = arg; } + + body_ = body_->xform_layer + ([&var_map](ref::brw x) -> ref::rp + { + if (x->extype() == exprtype::variable) { + ref::brw var = Variable::from(x); + + auto ix = var_map.find(var->name()); + if (ix == var_map.end()) { + /* add to var_map */ + + var_map[var->name()] = var.get(); + + return var.get(); + } else { + /* substitute already-encountered var_map[] member */ + return ix->second; + } + } else { + return x.get(); + } + }); } this->type_str_ = ss.str(); From a76c8354774ea9929b1b68ee74b0c8e034c88712 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 3 Jul 2024 14:39:32 -0400 Subject: [PATCH 34/58] xo-expression: refactor: xtract -> method regularize_layer_vars() --- include/xo/expression/Lambda.hpp | 8 ++++++ src/expression/Lambda.cpp | 44 ++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 18039982..d4ec9c54 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -91,6 +91,14 @@ namespace xo { /** compute free-variable set for this lambda **/ std::set calc_free_variables() const; + /** ensure at most one Variable instance with a particular name + * in this lambda, but ignore nested lambdas. + * + * Goal is to unify variables that can use the same binding + * path to determine memory location at runtime. + **/ + void regularize_layer_vars(); + private: /** lambda name. Initially supporting only form like * (define (foo x y z) diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index 30b579cf..833c998f 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -69,25 +69,9 @@ namespace xo { return retval; } /*calc_free_variables*/ - Lambda::Lambda(const std::string & name, - TypeDescr lambda_type, - const rp & local_env, - const ref::rp & body) - : FunctionInterface(exprtype::lambda, lambda_type), - name_{name}, - body_{body}, - local_env_{local_env} + void + Lambda::regularize_layer_vars() { - stringstream ss; - ss << "double"; - ss << "("; - for (std::size_t i = 0, n = this->n_arg(); i < n; ++i) { - if (i > 0) - ss << ","; - ss << "double"; - } - ss << ")"; - /* regularize local_env+body: make sure exactly one instance * (i.e. with object identity) of a Variable appears * within one layer of a lambda body. @@ -133,11 +117,33 @@ namespace xo { } }); } + } /*regularize_layer_vars*/ + + Lambda::Lambda(const std::string & name, + TypeDescr lambda_type, + const rp & local_env, + const ref::rp & body) + : FunctionInterface(exprtype::lambda, lambda_type), + name_{name}, + body_{body}, + local_env_{local_env} + { + stringstream ss; + ss << "double"; + ss << "("; + for (std::size_t i = 0, n = this->n_arg(); i < n; ++i) { + if (i > 0) + ss << ","; + ss << "double"; + } + ss << ")"; this->type_str_ = ss.str(); this->free_var_set_ = this->calc_free_variables(); - body_->attach_envs(local_env_); + this->regularize_layer_vars(); + + this->body_->attach_envs(local_env_); } /*ctor*/ void From f18c33b24937f66d857fe098887bb08991bea721 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 3 Jul 2024 14:43:34 -0400 Subject: [PATCH 35/58] xo-expression: bugfix: ensure in-layer uniqueness of vars --- include/xo/expression/Variable.hpp | 5 +++++ src/expression/Lambda.cpp | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/xo/expression/Variable.hpp b/include/xo/expression/Variable.hpp index 713da66f..8ca2cb2b 100644 --- a/include/xo/expression/Variable.hpp +++ b/include/xo/expression/Variable.hpp @@ -25,6 +25,11 @@ namespace xo { return new Variable(name, var_type); } + /** return copy of x: same var, different object identity **/ + static ref::rp copy(ref::brw x) { + return new Variable(x->name(), x->valuetype()); + } + /** downcast from Expression **/ static ref::brw from(ref::brw x) { return ref::brw::from(x); diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index 833c998f..72459fe5 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -103,9 +103,11 @@ namespace xo { auto ix = var_map.find(var->name()); if (ix == var_map.end()) { - /* add to var_map */ + /* add to var_map, copy to ensure Variable + * is unique to layer + */ - var_map[var->name()] = var.get(); + var_map[var->name()] = Variable::copy(var); return var.get(); } else { From 89043b0d46880440d3c51a8b19ca4e0dd7014bd9 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 3 Jul 2024 15:10:34 -0400 Subject: [PATCH 36/58] xo-expression: + Lambda::layer_var_map --- include/xo/expression/Lambda.hpp | 17 +++++-- src/expression/Lambda.cpp | 81 ++++++++++++++++++-------------- 2 files changed, 58 insertions(+), 40 deletions(-) diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index d4ec9c54..231e2043 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -9,6 +9,7 @@ #include "FunctionInterface.hpp" #include "Variable.hpp" #include "LocalEnv.hpp" +#include #include #include //#include @@ -73,9 +74,7 @@ namespace xo { return this; } - virtual void attach_envs(ref::brw p) override { - local_env_->assign_parent(p); - } + virtual void attach_envs(ref::brw p) override; virtual void display(std::ostream & os) const override; @@ -97,7 +96,7 @@ namespace xo { * Goal is to unify variables that can use the same binding * path to determine memory location at runtime. **/ - void regularize_layer_vars(); + std::map> regularize_layer_vars(); private: /** lambda name. Initially supporting only form like @@ -118,6 +117,16 @@ namespace xo { /** free variables for this lambda **/ std::set free_var_set_; + /** map giving unique identity to each variable appearing in this layer. + * includes: + * - formal parameters + * - free variables in @ref body_ + * excludes: + * - any variables appearing in nested lambdas + * (whether formals or free variables) + **/ + std::map> layer_var_map_; + /** established (once) by @ref attach_envs. * * @note data dependency on ancestor expressions that don't exist yet diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index 72459fe5..85f8bbcf 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -69,7 +69,7 @@ namespace xo { return retval; } /*calc_free_variables*/ - void + std::map> Lambda::regularize_layer_vars() { /* regularize local_env+body: make sure exactly one instance @@ -83,42 +83,43 @@ namespace xo { * Motivation is to unify Variables that would use the same * binding_path to resolve their runtime location. */ - { - std::map> var_map; + std::map> var_map; - for (const auto & arg : local_env_->argv()) { - /* each arg name can appear at most once - * in a particular lambda's parameter list - */ - assert(var_map.find(arg->name()) == var_map.end()); + for (const auto & arg : local_env_->argv()) { + /* each arg name can appear at most once + * in a particular lambda's parameter list + */ + assert(var_map.find(arg->name()) == var_map.end()); - var_map[arg->name()] = arg; - } - - body_ = body_->xform_layer - ([&var_map](ref::brw x) -> ref::rp - { - if (x->extype() == exprtype::variable) { - ref::brw var = Variable::from(x); - - auto ix = var_map.find(var->name()); - if (ix == var_map.end()) { - /* add to var_map, copy to ensure Variable - * is unique to layer - */ - - var_map[var->name()] = Variable::copy(var); - - return var.get(); - } else { - /* substitute already-encountered var_map[] member */ - return ix->second; - } - } else { - return x.get(); - } - }); + var_map[arg->name()] = arg; } + + this->body_ + = (body_->xform_layer + ([&var_map](ref::brw x) -> ref::rp + { + if (x->extype() == exprtype::variable) { + ref::brw var = Variable::from(x); + + auto ix = var_map.find(var->name()); + if (ix == var_map.end()) { + /* add to var_map, copy to ensure Variable + * not shared with any other layer + */ + + var_map[var->name()] = Variable::copy(var); + + return var.get(); + } else { + /* substitute already-encountered var_map[] member */ + return ix->second; + } + } else { + return x.get(); + } + })); + + return var_map; } /*regularize_layer_vars*/ Lambda::Lambda(const std::string & name, @@ -141,13 +142,21 @@ namespace xo { ss << ")"; this->type_str_ = ss.str(); - this->free_var_set_ = this->calc_free_variables(); - this->regularize_layer_vars(); + /* ensure variables are unique within layer for this lambda */ + this->layer_var_map_ = this->regularize_layer_vars(); + + this->free_var_set_ = this->calc_free_variables(); this->body_->attach_envs(local_env_); } /*ctor*/ + void + Lambda::attach_envs(ref::brw p) { + local_env_->assign_parent(p); + + } + void Lambda::display(std::ostream & os) const { os << " Date: Wed, 3 Jul 2024 16:18:26 -0400 Subject: [PATCH 37/58] xo-expression: + Expression::nested_layer() --- include/xo/expression/Apply.hpp | 13 +++++++++++++ include/xo/expression/Constant.hpp | 5 +++++ include/xo/expression/Environment.hpp | 4 +++- include/xo/expression/Expression.hpp | 7 +++++++ include/xo/expression/IfExpr.hpp | 12 ++++++++++++ include/xo/expression/Lambda.hpp | 8 ++++++++ include/xo/expression/LocalEnv.hpp | 2 ++ include/xo/expression/PrimitiveInterface.hpp | 5 +++++ include/xo/expression/Variable.hpp | 5 +++++ src/expression/Lambda.cpp | 2 +- 10 files changed, 61 insertions(+), 2 deletions(-) diff --git a/include/xo/expression/Apply.hpp b/include/xo/expression/Apply.hpp index 1ed06e6b..e6de3ac4 100644 --- a/include/xo/expression/Apply.hpp +++ b/include/xo/expression/Apply.hpp @@ -63,6 +63,19 @@ namespace xo { return n; } + virtual std::size_t visit_layer(VisitFn visitor_fn) override { + std::size_t n = 1; + + visitor_fn(this); + + n += fn_->visit_layer(visitor_fn); + + for (const auto & arg : argv_) + n += arg->visit_layer(visitor_fn); + + return n; + } + virtual ref::rp xform_layer(TransformFn xform_fn) override { this->fn_ = fn_->xform_layer(xform_fn); diff --git a/include/xo/expression/Constant.hpp b/include/xo/expression/Constant.hpp index ffc15967..3ecf1fe3 100644 --- a/include/xo/expression/Constant.hpp +++ b/include/xo/expression/Constant.hpp @@ -56,6 +56,11 @@ namespace xo { return 1; } + virtual std::size_t visit_layer(VisitFn visitor_fn) override { + visitor_fn(this); + return 1; + } + virtual ref::rp xform_layer(TransformFn xform_fn) override { return xform_fn(this); } diff --git a/include/xo/expression/Environment.hpp b/include/xo/expression/Environment.hpp index 6baded6d..b827a575 100644 --- a/include/xo/expression/Environment.hpp +++ b/include/xo/expression/Environment.hpp @@ -6,10 +6,12 @@ #pragma once #include "xo/refcnt/Refcounted.hpp" -#include "Variable.hpp" +#include "binding_path.hpp" namespace xo { namespace ast { + class Expression; + class Environment : public ref::Refcount { public: /** true if this is toplevel (global) environment. diff --git a/include/xo/expression/Expression.hpp b/include/xo/expression/Expression.hpp index 84248555..89a876d4 100644 --- a/include/xo/expression/Expression.hpp +++ b/include/xo/expression/Expression.hpp @@ -61,6 +61,13 @@ namespace xo { **/ virtual std::size_t visit_preorder(VisitFn visitor_fn) = 0; + /** visit each Expression node in this AST, + * including immediately-nested Lambda nodes; + * but do not recurse into the params/body of such nested Lambdas. + * Returns the number of nodes visited + **/ + virtual std::size_t visit_layer(VisitFn visitor_fn) = 0; + /** traverse ast @ref visit_preorder but do not visit Lambdas **/ virtual ref::rp xform_layer(TransformFn visitor_fn) = 0; diff --git a/include/xo/expression/IfExpr.hpp b/include/xo/expression/IfExpr.hpp index 8091481e..bfe1e116 100644 --- a/include/xo/expression/IfExpr.hpp +++ b/include/xo/expression/IfExpr.hpp @@ -67,6 +67,18 @@ namespace xo { return n; } + virtual std::size_t visit_layer(VisitFn visitor_fn) override { + std::size_t n = 1; + + visitor_fn(this); + + n += this->test_->visit_layer(visitor_fn); + n += this->when_true_->visit_layer(visitor_fn); + n += this->when_false_->visit_layer(visitor_fn); + + return n; + } + virtual ref::rp xform_layer(TransformFn xform_fn) override { this->test_ = this->test_->xform_layer(xform_fn); this->when_true_ = this->when_true_->xform_layer(xform_fn); diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 231e2043..3a3de6ac 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -69,6 +69,14 @@ namespace xo { return n; } + virtual std::size_t visit_layer(VisitFn visitor_fn) override { + std::size_t n = 1; + + visitor_fn(this); + + return n; + } + virtual ref::rp xform_layer(TransformFn /*xform_fn*/) override { /* a layer is bounded by lambdas, don't enter them */ return this; diff --git a/include/xo/expression/LocalEnv.hpp b/include/xo/expression/LocalEnv.hpp index d8fe2178..3106dc6c 100644 --- a/include/xo/expression/LocalEnv.hpp +++ b/include/xo/expression/LocalEnv.hpp @@ -6,6 +6,8 @@ #pragma once #include "Environment.hpp" +#include "Variable.hpp" +#include "xo/reflect/TypeDescr.hpp" namespace xo { namespace ast { diff --git a/include/xo/expression/PrimitiveInterface.hpp b/include/xo/expression/PrimitiveInterface.hpp index 70080607..12d4c4a7 100644 --- a/include/xo/expression/PrimitiveInterface.hpp +++ b/include/xo/expression/PrimitiveInterface.hpp @@ -57,6 +57,11 @@ namespace xo { return 1; } + virtual std::size_t visit_layer(VisitFn visitor_fn) override { + visitor_fn(this); + return 1; + } + virtual ref::rp xform_layer(TransformFn xform_fn) override { return xform_fn(this); } diff --git a/include/xo/expression/Variable.hpp b/include/xo/expression/Variable.hpp index 8ca2cb2b..7d965dea 100644 --- a/include/xo/expression/Variable.hpp +++ b/include/xo/expression/Variable.hpp @@ -48,6 +48,11 @@ namespace xo { return 1; } + virtual std::size_t visit_layer(VisitFn visitor_fn) override { + visitor_fn(this); + return 1; + } + virtual ref::rp xform_layer(TransformFn xform_fn) override { return xform_fn(this); } diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index 85f8bbcf..fba94694 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -112,7 +112,7 @@ namespace xo { return var.get(); } else { /* substitute already-encountered var_map[] member */ - return ix->second; + return ix->second.get(); } } else { return x.get(); From 4b0a2cff2a7413c1662ec8d3d20404347d107538 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 3 Jul 2024 16:20:18 -0400 Subject: [PATCH 38/58] xo-expression: + Lambda::nested_lambda_map --- include/xo/expression/GlobalEnv.hpp | 2 +- include/xo/expression/Lambda.hpp | 3 +++ include/xo/expression/binding_path.hpp | 4 ++-- src/expression/Lambda.cpp | 21 ++++++++++++++++++++- src/expression/Variable.cpp | 6 +++++- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/include/xo/expression/GlobalEnv.hpp b/include/xo/expression/GlobalEnv.hpp index 08eb9355..010dc296 100644 --- a/include/xo/expression/GlobalEnv.hpp +++ b/include/xo/expression/GlobalEnv.hpp @@ -26,7 +26,7 @@ namespace xo { virtual bool is_global_env() const override { return true; } - virtual binding_path lookup_binding(const std::string & vname) const override { + virtual binding_path lookup_binding(const std::string & /*vname*/) const override { /* i_link: -1 for global environment * j_slot: not used */ diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 3a3de6ac..91366ca9 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -135,6 +135,9 @@ namespace xo { **/ std::map> layer_var_map_; + /** all lambdas nested once inside this lambda's body **/ + std::map> nested_lambda_map_; + /** established (once) by @ref attach_envs. * * @note data dependency on ancestor expressions that don't exist yet diff --git a/include/xo/expression/binding_path.hpp b/include/xo/expression/binding_path.hpp index a3692ed1..7aef8c51 100644 --- a/include/xo/expression/binding_path.hpp +++ b/include/xo/expression/binding_path.hpp @@ -13,8 +13,8 @@ namespace xo { * providing its location. **/ struct binding_path { - /** @of parent links to traverse. -1 if global **/ - int i_link_ = -1; + /** @of parent links to traverse. -1 if global. -2 if sentinel **/ + int i_link_ = -2; /** for variables bound in some local environment: * slot# within that environment. * diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index fba94694..320e1eda 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -83,7 +83,7 @@ namespace xo { * Motivation is to unify Variables that would use the same * binding_path to resolve their runtime location. */ - std::map> var_map; + std::map> var_map; for (const auto & arg : local_env_->argv()) { /* each arg name can appear at most once @@ -148,6 +148,24 @@ namespace xo { this->free_var_set_ = this->calc_free_variables(); + std::map> nested_lambda_map; + + this->body_->visit_layer + ([&nested_lambda_map] + (ref::brw expr) + { + if (expr->extype() == exprtype::lambda) { + ref::brw lm = Lambda::from(expr); + + nested_lambda_map[lm->name()] = lm.get(); + } + }); + + this->nested_lambda_map_ = std::move(nested_lambda_map); + + /* in particular: + * - establish binding path for each variable + */ this->body_->attach_envs(local_env_); } /*ctor*/ @@ -155,6 +173,7 @@ namespace xo { Lambda::attach_envs(ref::brw p) { local_env_->assign_parent(p); + /** establish a binding path for each variable **/ } void diff --git a/src/expression/Variable.cpp b/src/expression/Variable.cpp index 71d47655..766e0c67 100644 --- a/src/expression/Variable.cpp +++ b/src/expression/Variable.cpp @@ -8,7 +8,11 @@ namespace xo { void Variable::attach_envs(ref::brw e) { /** e makes accessible all enclosing lexical scopes **/ - this->path_ = e->lookup_binding(this->name_); + if (this->path_.i_link_ == -2 /*sentinel*/) { + this->path_ = e->lookup_binding(this->name_); + } else { + /* have already established binding for this Variable */ + } } /*attach_envs*/ void From 8cf89f5eeffb3f2cb52a2863ee964569b3db276a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 3 Jul 2024 16:37:06 -0400 Subject: [PATCH 39/58] xo-expression: + LocalEnv::lookup_local_binding() --- include/xo/expression/LocalEnv.hpp | 5 +++++ src/expression/LocalEnv.cpp | 23 ++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/include/xo/expression/LocalEnv.hpp b/include/xo/expression/LocalEnv.hpp index 3106dc6c..ec3d4c81 100644 --- a/include/xo/expression/LocalEnv.hpp +++ b/include/xo/expression/LocalEnv.hpp @@ -35,6 +35,11 @@ namespace xo { int n_arg() const { return argv_.size(); } TypeDescr fn_arg(uint32_t i) const { return argv_[i]->valuetype(); } + /** report binding path for a formal parameter. + * Returns sentinel if @p vname doesn't appear in @ref argv_ + **/ + binding_path lookup_local_binding(const std::string & vname) const; + /** single-assign this environment's origin **/ void assign_origin(Lambda * p) { assert(origin_ == nullptr); diff --git a/src/expression/LocalEnv.cpp b/src/expression/LocalEnv.cpp index fb724aeb..8b834278 100644 --- a/src/expression/LocalEnv.cpp +++ b/src/expression/LocalEnv.cpp @@ -8,7 +8,7 @@ namespace xo { namespace ast { binding_path - LocalEnv::lookup_binding(const std::string & vname) const + LocalEnv::lookup_local_binding(const std::string & vname) const { int j_slot = 0; for (const auto & arg : argv_) { @@ -17,12 +17,25 @@ namespace xo { ++j_slot; } - auto tmp = parent_env_->lookup_binding(vname); + return { -2 /*i_link: sentinel*/, 0 }; + } /*lookup_local_binding*/ - if (tmp.i_link_ == -1) - return tmp; + binding_path + LocalEnv::lookup_binding(const std::string & vname) const + { + { + auto local = this->lookup_local_binding(vname); + + if (local.i_link_ == 0) + return local; + } + + auto free = parent_env_->lookup_binding(vname); + + if (free.i_link_ == -1) + return free; else - return { tmp.i_link_ + 1, tmp.j_slot_ }; + return { free.i_link_ + 1, free.j_slot_ }; } /*lookup_binding*/ } /*namespace ast*/ } /*namespace xo*/ From cdb4dd8427d46f5db12e65be4817383b8c18b1a6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 3 Jul 2024 16:37:29 -0400 Subject: [PATCH 40/58] xo-expression: + Lambda::captured_var_set; assigned in ctor --- include/xo/expression/Lambda.hpp | 3 ++ src/expression/Lambda.cpp | 50 ++++++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 91366ca9..40083875 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -125,6 +125,9 @@ namespace xo { /** free variables for this lambda **/ std::set free_var_set_; + /** variables that appear free in some nested lambda **/ + std::set captured_var_set_; + /** map giving unique identity to each variable appearing in this layer. * includes: * - formal parameters diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index 320e1eda..cf9112cd 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -149,24 +149,50 @@ namespace xo { this->free_var_set_ = this->calc_free_variables(); std::map> nested_lambda_map; + { + this->body_->visit_layer + ([&nested_lambda_map] + (ref::brw expr) + { + if (expr->extype() == exprtype::lambda) { + ref::brw lm = Lambda::from(expr); - this->body_->visit_layer - ([&nested_lambda_map] - (ref::brw expr) - { - if (expr->extype() == exprtype::lambda) { - ref::brw lm = Lambda::from(expr); - - nested_lambda_map[lm->name()] = lm.get(); - } - }); - + nested_lambda_map[lm->name()] = lm.get(); + } + }); + } this->nested_lambda_map_ = std::move(nested_lambda_map); + /* establish the set of captured local vars. + * These are any formal parameters that appear free in + * any layer of a nested lambda. + */ + std::set captured_var_set; + { + for (const auto & ix : nested_lambda_map_) { + std::set nested_free_var_set + = ix.second->get_free_variables(); + + for (const auto & jx : nested_free_var_set) { + /* check whether variable *jx is one of this lambda's formals */ + auto bind = this->local_env_->lookup_local_binding(jx); + + if (bind.i_link_ == 0) { + /* yup, it's a formal parameter of this lambda */ + captured_var_set.insert(jx); + } + } + } + } + + this->captured_var_set_ = std::move(captured_var_set); + /* in particular: - * - establish binding path for each variable + * - establish binding path (intrusively) for each variable + * assigns Variable::path_ */ this->body_->attach_envs(local_env_); + } /*ctor*/ void From ca19c65b02b2e4bd14438b38df9b45b0992701ae Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 5 Aug 2024 15:04:34 -0400 Subject: [PATCH 41/58] xo-expression: fix: ns xo::ref::rp -> xo::rp --- include/xo/expression/Apply.hpp | 26 ++++++------- include/xo/expression/Constant.hpp | 6 +-- include/xo/expression/Expression.hpp | 4 +- include/xo/expression/GlobalEnv.hpp | 4 +- include/xo/expression/IfExpr.hpp | 40 ++++++++++---------- include/xo/expression/Lambda.hpp | 30 +++++++-------- include/xo/expression/LocalEnv.hpp | 10 ++--- include/xo/expression/PrimitiveInterface.hpp | 2 +- include/xo/expression/Variable.hpp | 10 ++--- src/expression/Apply.cpp | 2 - src/expression/IfExpr.cpp | 2 - src/expression/Lambda.cpp | 9 ++--- 12 files changed, 70 insertions(+), 75 deletions(-) diff --git a/include/xo/expression/Apply.hpp b/include/xo/expression/Apply.hpp index e6de3ac4..f06b8b8b 100644 --- a/include/xo/expression/Apply.hpp +++ b/include/xo/expression/Apply.hpp @@ -25,16 +25,16 @@ namespace xo { public: /** create new apply-expression instance **/ - static ref::rp make(const ref::rp & fn, - const std::vector> & argv); + static rp make(const rp & fn, + const std::vector> & argv); /** downcast from Expression **/ static ref::brw from(ref::brw x) { return ref::brw::from(x); } - const ref::rp & fn() const { return fn_; } - const std::vector> & argv() const { return argv_; } + const rp & fn() const { return fn_; } + const std::vector> & argv() const { return argv_; } virtual std::set get_free_variables() const override { std::set retval = fn_->get_free_variables(); @@ -76,7 +76,7 @@ namespace xo { return n; } - virtual ref::rp xform_layer(TransformFn xform_fn) override { + virtual rp xform_layer(TransformFn xform_fn) override { this->fn_ = fn_->xform_layer(xform_fn); for (auto & arg : argv_) @@ -96,17 +96,17 @@ namespace xo { private: Apply(TypeDescr apply_valuetype, - const ref::rp & fn, - const std::vector> & argv) + const rp & fn, + const std::vector> & argv) : Expression(exprtype::apply, apply_valuetype), fn_{fn}, argv_(argv) {} private: /** function to invoke **/ - ref::rp fn_; + rp fn_; /** argument expressions, in l-to-r order **/ - std::vector> argv_; + std::vector> argv_; }; /*Apply*/ #ifdef NOT_USING @@ -136,10 +136,10 @@ namespace xo { #endif /* reminder: initializer-lists are compile-time only */ - inline ref::rp - make_apply(const ref::rp & fn, - const std::initializer_list> args) { - std::vector> argv(args); + inline rp + make_apply(const rp & fn, + const std::initializer_list> args) { + std::vector> argv(args); return Apply::make(fn, argv); } /*make_apply*/ diff --git a/include/xo/expression/Constant.hpp b/include/xo/expression/Constant.hpp index 3ecf1fe3..77098a5b 100644 --- a/include/xo/expression/Constant.hpp +++ b/include/xo/expression/Constant.hpp @@ -30,7 +30,7 @@ namespace xo { public: /** create constant expression representing literal value x **/ - static ref::rp make(const T & x) { + static rp make(const T & x) { TypeDescr x_valuetype = Reflect::require(); return new Constant(x_valuetype, x); @@ -61,7 +61,7 @@ namespace xo { return 1; } - virtual ref::rp xform_layer(TransformFn xform_fn) override { + virtual rp xform_layer(TransformFn xform_fn) override { return xform_fn(this); } @@ -89,7 +89,7 @@ namespace xo { }; /*Constant*/ template - ref::rp>> + rp>> make_constant(const T & x) { return Constant::make(x); } diff --git a/include/xo/expression/Expression.hpp b/include/xo/expression/Expression.hpp index 89a876d4..cbea3a35 100644 --- a/include/xo/expression/Expression.hpp +++ b/include/xo/expression/Expression.hpp @@ -38,7 +38,7 @@ namespace xo { using VisitFn = std::function )>; using TransformFn = std::function - (ref::brw)>; + (ref::brw)>; using TypeDescr = xo::reflect::TypeDescr; public: @@ -69,7 +69,7 @@ namespace xo { virtual std::size_t visit_layer(VisitFn visitor_fn) = 0; /** traverse ast @ref visit_preorder but do not visit Lambdas **/ - virtual ref::rp xform_layer(TransformFn visitor_fn) = 0; + virtual rp xform_layer(TransformFn visitor_fn) = 0; /** attach an environment to each lambda expression X in this subtree, * that will: diff --git a/include/xo/expression/GlobalEnv.hpp b/include/xo/expression/GlobalEnv.hpp index 010dc296..9cbf328f 100644 --- a/include/xo/expression/GlobalEnv.hpp +++ b/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 ref::rp make() { return new GlobalEnv(); } + static rp make() { return new GlobalEnv(); } ref::brw require_global(const std::string & vname, ref::brw expr) { @@ -51,7 +51,7 @@ namespace xo { /* for assignable globals, need to allocate memory * addresses for these. */ - std::map> global_map_; + std::map> global_map_; }; } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/IfExpr.hpp b/include/xo/expression/IfExpr.hpp index bfe1e116..0f62f86d 100644 --- a/include/xo/expression/IfExpr.hpp +++ b/include/xo/expression/IfExpr.hpp @@ -25,18 +25,18 @@ namespace xo { * @p when_true or @p when_false, depending on result * of evaluating expression @p test **/ - static ref::rp make(const ref::rp & test, - const ref::rp & when_true, - const ref::rp & when_false); + static rp make(const rp & test, + const rp & when_true, + const rp & when_false); /** downcast from Expression **/ static ref::brw from(ref::brw x) { return ref::brw::from(x); } - const ref::rp & test() const { return test_; } - const ref::rp & when_true() const { return when_true_; } - const ref::rp & when_false() const { return when_false_; } + const rp & test() const { return test_; } + const rp & when_true() const { return when_true_; } + const rp & when_false() const { return when_false_; } // ----- Expression ----- @@ -79,7 +79,7 @@ namespace xo { return n; } - virtual ref::rp xform_layer(TransformFn xform_fn) override { + virtual rp xform_layer(TransformFn xform_fn) override { this->test_ = this->test_->xform_layer(xform_fn); this->when_true_ = this->when_true_->xform_layer(xform_fn); this->when_false_= this->when_false_->xform_layer(xform_fn); @@ -113,13 +113,13 @@ namespace xo { * @p when_false else-branch; executes only when test fails **/ IfExpr(TypeDescr ifexpr_type, - const ref::rp & test, - const ref::rp & when_true, - const ref::rp & when_false) + rp test, + rp when_true, + rp when_false) : Expression(exprtype::ifexpr, ifexpr_type), - test_{test}, - when_true_{when_true}, - when_false_{when_false} {} + test_{std::move(test)}, + when_true_{std::move(when_true)}, + when_false_{std::move(when_false)} {} private: /** if: @@ -127,15 +127,15 @@ namespace xo { * * executes x; if true execute y; otherwise execute z **/ - ref::rp test_; - ref::rp when_true_; - ref::rp when_false_; + rp test_; + rp when_true_; + rp when_false_; }; /*IfExpr*/ - inline ref::rp - make_ifexpr(const ref::rp & test, - const ref::rp & when_true, - const ref::rp & when_false) + inline rp + make_ifexpr(const rp & test, + const rp & when_true, + const rp & when_false) { return IfExpr::make(test, when_true, when_false); } diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 40083875..e5d4a84e 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -27,9 +27,9 @@ namespace xo { * @p argv Formal parameters, in left-to-right order * @p body Expression for body of this function **/ - static ref::rp make(const std::string & name, - const std::vector> & argv, - const ref::rp & body); + static rp make(const std::string & name, + const std::vector> & argv, + const rp & body); /** downcast from Expression **/ static ref::brw from(ref::brw x) { @@ -37,8 +37,8 @@ namespace xo { } const std::string & type_str() const { return type_str_; } - const std::vector> & argv() const { return local_env_->argv(); } - const ref::rp & body() const { return body_; } + const std::vector> & argv() const { return local_env_->argv(); } + const rp & body() const { return body_; } bool needs_closure_flag() const { return !free_var_set_.empty(); } @@ -77,7 +77,7 @@ namespace xo { return n; } - virtual ref::rp xform_layer(TransformFn /*xform_fn*/) override { + virtual rp xform_layer(TransformFn /*xform_fn*/) override { /* a layer is bounded by lambdas, don't enter them */ return this; } @@ -92,8 +92,8 @@ namespace xo { **/ Lambda(const std::string & name, TypeDescr lambda_type, - const ref::rp & local_env, - const ref::rp & body); + const rp & local_env, + const rp & body); /** compute free-variable set for this lambda **/ std::set calc_free_variables() const; @@ -104,7 +104,7 @@ namespace xo { * Goal is to unify variables that can use the same binding * path to determine memory location at runtime. **/ - std::map> regularize_layer_vars(); + std::map> regularize_layer_vars(); private: /** lambda name. Initially supporting only form like @@ -120,7 +120,7 @@ namespace xo { **/ std::string type_str_; /** function body **/ - ref::rp body_; + rp body_; /** free variables for this lambda **/ std::set free_var_set_; @@ -136,7 +136,7 @@ namespace xo { * - any variables appearing in nested lambdas * (whether formals or free variables) **/ - std::map> layer_var_map_; + std::map> layer_var_map_; /** all lambdas nested once inside this lambda's body **/ std::map> nested_lambda_map_; @@ -147,13 +147,13 @@ namespace xo { * when Lambda constructor runs, so we need to assign @ref local_env_ * later. **/ - ref::rp local_env_; + rp local_env_; }; /*Lambda*/ - inline ref::rp + inline rp make_lambda(const std::string & name, - const std::vector> & argv, - const ref::rp & body) + const std::vector> & argv, + const rp & body) { return Lambda::make(name, argv, body); } diff --git a/include/xo/expression/LocalEnv.hpp b/include/xo/expression/LocalEnv.hpp index ec3d4c81..d4facdba 100644 --- a/include/xo/expression/LocalEnv.hpp +++ b/include/xo/expression/LocalEnv.hpp @@ -26,12 +26,12 @@ namespace xo { public: /** named ctor idiom. Create instance with local variables per @p argv **/ - static ref::rp make(const std::vector> & argv) { + static rp make(const std::vector> & argv) { return new LocalEnv(argv); } Lambda * origin() const { return origin_; } - const std::vector> & argv() const { return argv_; } + const std::vector> & argv() const { return argv_; } int n_arg() const { return argv_.size(); } TypeDescr fn_arg(uint32_t i) const { return argv_[i]->valuetype(); } @@ -71,7 +71,7 @@ namespace xo { } private: - LocalEnv(const std::vector> & argv) + LocalEnv(const std::vector> & argv) : origin_{nullptr}, argv_(argv) {} private: @@ -85,12 +85,12 @@ namespace xo { Lambda * origin_ = nullptr; /** formal argument names **/ - std::vector> argv_; + std::vector> argv_; /** parent environment. A free variable in this lambda's * body will be resolved by referring them to @ref parent_env_. **/ - ref::rp parent_env_; + rp parent_env_; }; } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/PrimitiveInterface.hpp b/include/xo/expression/PrimitiveInterface.hpp index 12d4c4a7..c8b0758b 100644 --- a/include/xo/expression/PrimitiveInterface.hpp +++ b/include/xo/expression/PrimitiveInterface.hpp @@ -62,7 +62,7 @@ namespace xo { return 1; } - virtual ref::rp xform_layer(TransformFn xform_fn) override { + virtual rp xform_layer(TransformFn xform_fn) override { return xform_fn(this); } diff --git a/include/xo/expression/Variable.hpp b/include/xo/expression/Variable.hpp index 7d965dea..43936f70 100644 --- a/include/xo/expression/Variable.hpp +++ b/include/xo/expression/Variable.hpp @@ -20,13 +20,13 @@ namespace xo { * identified by @p name, that can take on values * described by @p var_type. **/ - static ref::rp make(const std::string & name, - TypeDescr var_type) { + static rp make(const std::string & name, + TypeDescr var_type) { return new Variable(name, var_type); } /** return copy of x: same var, different object identity **/ - static ref::rp copy(ref::brw x) { + static rp copy(ref::brw x) { return new Variable(x->name(), x->valuetype()); } @@ -53,7 +53,7 @@ namespace xo { return 1; } - virtual ref::rp xform_layer(TransformFn xform_fn) override { + virtual rp xform_layer(TransformFn xform_fn) override { return xform_fn(this); } @@ -76,7 +76,7 @@ namespace xo { binding_path path_; }; /*Variable*/ - inline ref::rp + inline rp make_var(const std::string & name, reflect::TypeDescr var_type) { return Variable::make(name, var_type); diff --git a/src/expression/Apply.cpp b/src/expression/Apply.cpp index 12926ddb..36e2428a 100644 --- a/src/expression/Apply.cpp +++ b/src/expression/Apply.cpp @@ -4,8 +4,6 @@ #include "xo/indentlog/print/vector.hpp" namespace xo { - using xo::ref::rp; - namespace ast { rp Apply::make(const rp & fn, diff --git a/src/expression/IfExpr.cpp b/src/expression/IfExpr.cpp index 31389b60..bd5d231f 100644 --- a/src/expression/IfExpr.cpp +++ b/src/expression/IfExpr.cpp @@ -4,8 +4,6 @@ #include "xo/indentlog/print/vector.hpp" namespace xo { - using xo::ref::rp; - namespace ast { rp IfExpr::make(const rp & test, diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index cf9112cd..d18468cd 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -9,14 +9,13 @@ namespace xo { using xo::reflect::TypeDescrBase; using xo::reflect::FunctionTdxInfo; - using xo::ref::rp; using std::stringstream; namespace ast { rp Lambda::make(const std::string & name, const std::vector> & argv, - const ref::rp & body) + const rp & body) { using xo::reflect::FunctionTdx; @@ -69,7 +68,7 @@ namespace xo { return retval; } /*calc_free_variables*/ - std::map> + std::map> Lambda::regularize_layer_vars() { /* regularize local_env+body: make sure exactly one instance @@ -83,7 +82,7 @@ namespace xo { * Motivation is to unify Variables that would use the same * binding_path to resolve their runtime location. */ - std::map> var_map; + std::map> var_map; for (const auto & arg : local_env_->argv()) { /* each arg name can appear at most once @@ -96,7 +95,7 @@ namespace xo { this->body_ = (body_->xform_layer - ([&var_map](ref::brw x) -> ref::rp + ([&var_map](ref::brw x) -> rp { if (x->extype() == exprtype::variable) { ref::brw var = Variable::from(x); From 3964f72cea7d1d22e94c9d68c1ad69c03190524f Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 5 Aug 2024 15:10:14 -0400 Subject: [PATCH 42/58] xo-expression: fix: straggler ns fix in Lambda ctor --- src/expression/Lambda.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index d18468cd..039e1e1e 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -124,7 +124,7 @@ namespace xo { Lambda::Lambda(const std::string & name, TypeDescr lambda_type, const rp & local_env, - const ref::rp & body) + const rp & body) : FunctionInterface(exprtype::lambda, lambda_type), name_{name}, body_{body}, From 4738ff66b428b3f585ff9a862e208da90a8fdf36 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 5 Aug 2024 15:12:15 -0400 Subject: [PATCH 43/58] xo-expression: straggler: ns fix in Primitive::make --- include/xo/expression/Primitive.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/xo/expression/Primitive.hpp b/include/xo/expression/Primitive.hpp index 49246d8b..653a3a83 100644 --- a/include/xo/expression/Primitive.hpp +++ b/include/xo/expression/Primitive.hpp @@ -37,10 +37,10 @@ namespace xo { using TypeDescr = xo::reflect::TypeDescr; public: - static ref::rp make(const std::string & name, - FunctionPointer fnptr, - bool explicit_symbol_def, - llvmintrinsic intrinsic) { + static rp make(const std::string & name, + FunctionPointer fnptr, + bool explicit_symbol_def, + llvmintrinsic intrinsic) { TypeDescr fn_type = Reflect::require(); return new Primitive(fn_type, name, fnptr, explicit_symbol_def, intrinsic); From e8a590b0d46eef35f4dc260424811c64b5904cb9 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 5 Aug 2024 15:14:28 -0400 Subject: [PATCH 44/58] xo-expression: fix: ns fix in Primitive make_primitive() helper --- include/xo/expression/Primitive.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/xo/expression/Primitive.hpp b/include/xo/expression/Primitive.hpp index 653a3a83..5b1d46ac 100644 --- a/include/xo/expression/Primitive.hpp +++ b/include/xo/expression/Primitive.hpp @@ -124,7 +124,7 @@ namespace xo { /** adopt function @p x as a callable primitive function named @p name **/ template - ref::rp> + rp> make_primitive(const std::string & name, FunctionPointer x, bool explicit_symbol_def, From 9a1a419aef300afc86cc4bac86590639e81353f3 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 6 Aug 2024 03:20:19 -0400 Subject: [PATCH 45/58] xo-exprssion: + DefineExpr --- include/xo/expression/DefineExpr.hpp | 128 +++++++++++++++++++++++++++ src/expression/CMakeLists.txt | 1 + src/expression/DefineExpr.cpp | 99 +++++++++++++++++++++ 3 files changed, 228 insertions(+) create mode 100644 include/xo/expression/DefineExpr.hpp create mode 100644 src/expression/DefineExpr.cpp diff --git a/include/xo/expression/DefineExpr.hpp b/include/xo/expression/DefineExpr.hpp new file mode 100644 index 00000000..e7ffce65 --- /dev/null +++ b/include/xo/expression/DefineExpr.hpp @@ -0,0 +1,128 @@ +/* file DefineExpr.hpp + * + * author: Roland Conybeare, Jul 2024 + */ + +#pragma once + +#include "Expression.hpp" + +namespace xo { + namespace ast { + /** @class DefineExpr + * @brief Provide definition for a constant, variable or function + * + * At toplevel, introduces a new global variable. + * In a nested context, + * + * def foo = rhsexpr + * body... + * + * is equivalent to + * + * (lambda (foo) body...)(rhsexpr) + **/ + class DefineExpr : public Expression { + public: + static rp make(std::string name, + rp value); + + + static ref::brw from(ref::brw x) { + return ref::brw::from(x); + } + + const std::string & lhs_name() const { return lhs_name_; } + const rp & rhs() const { return rhs_; } + + std::set calc_free_variables() const; + + // ----- Expression ----- + + virtual std::set get_free_variables() const override { + return this->free_var_set_; + } + + virtual std::size_t visit_preorder(VisitFn visitor_fn) override { + std::size_t n = 1; + + visitor_fn(this); + + n += rhs_->visit_preorder(visitor_fn); + + return n; + } + + virtual std::size_t visit_layer(VisitFn visitor_fn) override { + std::size_t n = 1; + + visitor_fn(this); + + n += this->rhs_->visit_layer(visitor_fn); + + return n; + } + + virtual rp xform_layer(TransformFn xform_fn) override { + this->rhs_ = this->rhs_->xform_layer(xform_fn); + + return xform_fn(this); + } + + virtual void attach_envs(ref::brw p) override { + rhs_->attach_envs(p); + } + + virtual void display(std::ostream & os) const override; + + protected: + /** + * + **/ + DefineExpr(TypeDescr rhs_valuetype, + std::string lhs_name, + rp rhs); + + protected: + /** symbol name for this definition **/ + std::string lhs_name_; + /** right-hand side of definition **/ + rp rhs_; + + /** free variables for this definition **/ + std::set free_var_set_; + }; + + /** @class DefineExprAccess + * @brief DefineExpr with writeable members. + * + * Convenient when scaffolding a parser, + * e.g. see xo-parser + **/ + class DefineExprAccess : public DefineExpr { + public: + static rp make(std::string lhs_name, + rp rhs); + static rp make_empty(); + + void assign_lhs_name(const std::string & x) { + this->lhs_name_ = x; + } + + void assign_rhs(const rp & x); + + private: + DefineExprAccess(TypeDescr rhs_valuetype, + std::string lhs_name, + rp rhs) + : DefineExpr(rhs_valuetype, + std::move(lhs_name), + std::move(rhs)) + {} + }; + + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end DefineExpr.hpp */ diff --git a/src/expression/CMakeLists.txt b/src/expression/CMakeLists.txt index 50edec01..32520c85 100644 --- a/src/expression/CMakeLists.txt +++ b/src/expression/CMakeLists.txt @@ -3,6 +3,7 @@ set(SELF_LIB xo_expression) set(SELF_SRCS Expression.cpp + DefineExpr.cpp Apply.cpp Lambda.cpp Variable.cpp diff --git a/src/expression/DefineExpr.cpp b/src/expression/DefineExpr.cpp new file mode 100644 index 00000000..1c67188c --- /dev/null +++ b/src/expression/DefineExpr.cpp @@ -0,0 +1,99 @@ +/* file DefineExpr.cpp + * + * author: Roland Conybeare + */ + +#include "DefineExpr.hpp" + +namespace xo { + namespace ast { + rp + DefineExpr::make(std::string lhs_name, + rp rhs) + { + TypeDescr rhs_valuetype = nullptr; + + if (rhs) + rhs_valuetype = rhs->valuetype(); + + return new DefineExpr(rhs_valuetype, + std::move(lhs_name), + std::move(rhs)); + } /*make*/ + + DefineExpr::DefineExpr(TypeDescr rhs_valuetype, + std::string lhs_name, + rp rhs) + : Expression(exprtype::define, rhs_valuetype), + lhs_name_{std::move(lhs_name)}, + rhs_{std::move(rhs)} + { + this->free_var_set_ = this->calc_free_variables(); + } + + std::set + DefineExpr::calc_free_variables() const + { + std::set retval; + + if (rhs_) + retval = rhs_->get_free_variables(); + + /* but remove this variable */ + if (!this->lhs_name().empty()) + retval.erase(this->lhs_name()); + + return retval; + } /*calc_free_variables*/ + + void + DefineExpr::display(std::ostream & os) const { + os << ""; + } /*display*/ + + // ----- DefineExprAccess ----- + + rp + DefineExprAccess::make(std::string lhs_name, + rp rhs) + { + TypeDescr rhs_valuetype = nullptr; + + if (rhs) + rhs_valuetype = rhs->valuetype(); + + return new DefineExprAccess(rhs_valuetype, + std::move(lhs_name), + std::move(rhs)); + } + + rp + DefineExprAccess::make_empty() + { + return new DefineExprAccess(nullptr /*rhs_valuetype*/, + "" /*lhs_name*/, + nullptr /*rhs*/); + } + + void + DefineExprAccess::assign_rhs(const rp & x) + { + assert(x); + + this->rhs_ = x; + + if (x) { + this->assign_valuetype(x->valuetype()); + } + + this->free_var_set_ = this->calc_free_variables(); + } + + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end DefineExpr.cpp */ From 0708bc75693525cd137a458c3942a4440761c8ae Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 6 Aug 2024 03:20:38 -0400 Subject: [PATCH 46/58] xo-expression: + ConvertExpr --- include/xo/expression/ConvertExpr.hpp | 109 ++++++++++++++++++++++++++ src/expression/CMakeLists.txt | 1 + src/expression/ConvertExpr.cpp | 53 +++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 include/xo/expression/ConvertExpr.hpp create mode 100644 src/expression/ConvertExpr.cpp diff --git a/include/xo/expression/ConvertExpr.hpp b/include/xo/expression/ConvertExpr.hpp new file mode 100644 index 00000000..8c5f9b70 --- /dev/null +++ b/include/xo/expression/ConvertExpr.hpp @@ -0,0 +1,109 @@ +/* file ConvertExpr.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "Expression.hpp" + +namespace xo { + namespace ast { + /** @class Convertexpr + * @brief Convenience for automatic type conversion + * + * This is equivalent to calling a built-in primitive + * that performs the conversion. + * + * We rely on this for convenience, for example to parse + * code like + * + * def foo : i16 = 0 + **/ + class ConvertExpr : public Expression { + public: + static rp make(TypeDescr dest_type, + rp arg); + + static ref::brw from(ref::brw x) { + return ref::brw::from(x); + } + + const rp & arg() const { return arg_; } + + // ----- Expression ----- + + virtual std::set get_free_variables() const override; + + virtual std::size_t visit_preorder(VisitFn visitor_fn) override { + std::size_t n = 1; + + visitor_fn(this); + + n += arg_->visit_preorder(visitor_fn); + + return n; + } + + virtual std::size_t visit_layer(VisitFn visitor_fn) override { + std::size_t n = 1; + + visitor_fn(this); + + n += this->arg_->visit_layer(visitor_fn); + + return n; + } + + virtual rp xform_layer(TransformFn xform_fn) override { + this->arg_ = this->arg_->xform_layer(xform_fn); + + return xform_fn(this); + } + + virtual void attach_envs(ref::brw p) override { + arg_->attach_envs(p); + } + + virtual void display(std::ostream & os) const override; + + protected: + ConvertExpr(TypeDescr dest_type, + rp arg) + : Expression(exprtype::convert, dest_type), + arg_{std::move(arg)} + {} + + protected: + /** source expression. Convert + * @c arg_->valuetype() to @c dest_type_ + **/ + rp arg_; + }; + + /** @class ConvertExprAccess + * @brief ConvertExpr with writeable members. + * + * Convenient when scaffolding a parser, + * e.g. see xo-parser + **/ + class ConvertExprAccess : public ConvertExpr { + public: + static rp make(TypeDescr dest_type, + rp arg); + static rp make_empty(); + + void assign_arg(rp arg) { this->arg_ = std::move(arg); } + + private: + ConvertExprAccess(TypeDescr dest_type, + rp arg) + : ConvertExpr(dest_type, + std::move(arg)) + {} + }; + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end ConvertExpr.hpp */ diff --git a/src/expression/CMakeLists.txt b/src/expression/CMakeLists.txt index 32520c85..eb0f6d64 100644 --- a/src/expression/CMakeLists.txt +++ b/src/expression/CMakeLists.txt @@ -9,6 +9,7 @@ set(SELF_SRCS Variable.cpp IfExpr.cpp LocalEnv.cpp + ConvertExpr.cpp ) xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) diff --git a/src/expression/ConvertExpr.cpp b/src/expression/ConvertExpr.cpp new file mode 100644 index 00000000..2e741f1b --- /dev/null +++ b/src/expression/ConvertExpr.cpp @@ -0,0 +1,53 @@ +/* file ConvertExpr.cpp + * + * author: Roland Conybeare + */ + +#include "ConvertExpr.hpp" + +namespace xo { + namespace ast { + rp + ConvertExpr::make(TypeDescr dest_type, + rp arg) + { + return new ConvertExpr(dest_type, + std::move(arg)); + } + + std::set + ConvertExpr::get_free_variables() const { + if (this->arg_) + return this->arg_->get_free_variables(); + else + return std::set(); + } + + void + ConvertExpr::display(std::ostream & os) const { + os << "valuetype()->short_name()) + << xtag("arg", arg_) + << ">"; + } + + // ----- ConvertExprAccess ----- + + rp + ConvertExprAccess::make(TypeDescr dest_type, + rp arg) + { + return new ConvertExprAccess(dest_type, + std::move(arg)); + } + + rp + ConvertExprAccess::make_empty() { + return new ConvertExprAccess(nullptr /*dest_type*/, + nullptr /*arg*/); + } + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end ConvertExpr.cpp */ From cad31397ec7b341b2dde9ff6de259e6377832663 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 6 Aug 2024 03:22:31 -0400 Subject: [PATCH 47/58] xo-expression: straggler: exprtype::define --- include/xo/expression/exprtype.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/xo/expression/exprtype.hpp b/include/xo/expression/exprtype.hpp index d8e66a61..c108bb2d 100644 --- a/include/xo/expression/exprtype.hpp +++ b/include/xo/expression/exprtype.hpp @@ -22,6 +22,8 @@ namespace xo { constant, /** a literal constant that refers to a linkable named function **/ primitive, + /** variable/function definition **/ + define, /** function call **/ apply, /** function definition **/ @@ -42,6 +44,7 @@ namespace xo { case exprtype::invalid: return "?exprtype"; case exprtype::constant: return "constant"; case exprtype::primitive: return "primitive"; + case exprtype::define: return "define"; case exprtype::apply: return "apply"; case exprtype::lambda: return "lambda"; case exprtype::variable: return "variable"; From 2d0608de52bc4c534005e2032fb46b0ff1d1b33c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 6 Aug 2024 03:23:07 -0400 Subject: [PATCH 48/58] xo-epression: straggler: exprtype::convert --- include/xo/expression/exprtype.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/xo/expression/exprtype.hpp b/include/xo/expression/exprtype.hpp index c108bb2d..49745d52 100644 --- a/include/xo/expression/exprtype.hpp +++ b/include/xo/expression/exprtype.hpp @@ -6,7 +6,7 @@ #pragma once #include -#include +//#include namespace xo { namespace ast { @@ -32,6 +32,8 @@ namespace xo { variable, /** if-then-else **/ ifexpr, + /** type conversion **/ + convert, /** not an expression. comes last, counts entries **/ n_expr @@ -49,6 +51,7 @@ namespace xo { case exprtype::lambda: return "lambda"; case exprtype::variable: return "variable"; case exprtype::ifexpr: return "if_expr"; + case exprtype::convert: return "convert"; default: break; } From fce0fea1cbdd86df5a27892e14044ad13a4179bd Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 6 Aug 2024 04:35:26 -0400 Subject: [PATCH 49/58] xo-expression: + Expression.assign_valuetype() --- include/xo/expression/Expression.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/xo/expression/Expression.hpp b/include/xo/expression/Expression.hpp index cbea3a35..3e9137c4 100644 --- a/include/xo/expression/Expression.hpp +++ b/include/xo/expression/Expression.hpp @@ -92,6 +92,10 @@ namespace xo { /** human-readable string representation **/ virtual std::string display_string() const; + protected: + /** useful when scaffolding expressions in a parser **/ + void assign_valuetype(TypeDescr x) { valuetype_ = x; } + private: /** expression type (constant | apply | ..) for this expression **/ exprtype extype_ = exprtype::invalid; From d4fd55b8ed79591dba390ba54190cc3e52e6caf6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 19 Aug 2024 18:45:02 -0400 Subject: [PATCH 50/58] xo-expression: arithmetic expression support --- include/xo/expression/Apply.hpp | 13 ++++ include/xo/expression/Primitive.hpp | 29 +++++++- include/xo/expression/llvmintrinsic.hpp | 4 ++ src/expression/Apply.cpp | 33 +++++++++ src/expression/CMakeLists.txt | 1 + src/expression/Primitive.cpp | 89 +++++++++++++++++++++++++ src/expression/intrinsics.cpp | 14 ++++ 7 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 src/expression/Primitive.cpp create mode 100644 src/expression/intrinsics.cpp diff --git a/include/xo/expression/Apply.hpp b/include/xo/expression/Apply.hpp index f06b8b8b..e95dd76a 100644 --- a/include/xo/expression/Apply.hpp +++ b/include/xo/expression/Apply.hpp @@ -28,6 +28,19 @@ namespace xo { static rp make(const rp & fn, const std::vector> & argv); + /** create apply-expression to add two 64-bit floating-point numbers **/ + static rp make_add2_f64(const rp & lhs, + const rp & rhs); + /** create apply-expression to subtract two 64-bit floating-point numbers **/ + static rp make_sub2_f64(const rp & lhs, + const rp & rhs); + /** create apply-expression to multiply two 64-bit floating-point numbers **/ + static rp make_mul2_f64(const rp & lhs, + const rp & rhs); + /** create apply-expression to divide two 64-bit floating-point numbers **/ + static rp make_div2_f64(const rp & lhs, + const rp & rhs); + /** downcast from Expression **/ static ref::brw from(ref::brw x) { return ref::brw::from(x); diff --git a/include/xo/expression/Primitive.hpp b/include/xo/expression/Primitive.hpp index 5b1d46ac..2305bfe4 100644 --- a/include/xo/expression/Primitive.hpp +++ b/include/xo/expression/Primitive.hpp @@ -10,6 +10,13 @@ #include "xo/reflect/Reflect.hpp" //#include +extern "C" { + /* these symbols needed to link primitives */ + + /* see Primitive_f64::make() */ + double add2_f64(double x, double y); +}; + namespace xo { namespace ast { /** @class Primitive @@ -35,6 +42,7 @@ namespace xo { using Reflect = xo::reflect::Reflect; using TaggedPtr = xo::reflect::TaggedPtr; using TypeDescr = xo::reflect::TypeDescr; + using fptr_type = FunctionPointer; public: static rp make(const std::string & name, @@ -46,8 +54,9 @@ namespace xo { return new Primitive(fn_type, name, fnptr, explicit_symbol_def, intrinsic); } + /** see classes below for intrinsics **/ + FunctionPointer value() const { return value_; } - llvmintrinsic intrinsic() const { return intrinsic_; } TypeDescr value_td() const { return value_td_; } TaggedPtr value_tp() const { @@ -60,6 +69,7 @@ namespace xo { // ----- PrimitiveInterface ----- + virtual llvmintrinsic intrinsic() const override { return intrinsic_; } virtual bool explicit_symbol_def() const override { return explicit_symbol_def_; } virtual void_function_type function_address() const override { return reinterpret_cast(value_); } @@ -113,7 +123,7 @@ namespace xo { /** for LLVM: if true, use Jit.intern_symbol() to provide explicit binding. * * Not obvious what distinguishes functions like ::sin(), ::sqrt() - * (which work without this) from symbols like ::mul_i32(), which do. + * (which work without this) from symbols like ::mul_i32(), which require it. **/ bool explicit_symbol_def_ = false; /** invalid: generate call (IRBuilder::CreateCall) @@ -132,6 +142,21 @@ namespace xo { { return Primitive::make(name, x, explicit_symbol_def, intrinsic); } + + class Primitive_f64 : public Primitive { + public: + using PrimitiveType = Primitive; + + public: + /** add2_f64: add two 64-bit floating-point numbers **/ + static rp make_add2_f64(); + /** sub2_f64: subtract two 64-bit floating-point numbers **/ + static rp make_sub2_f64(); + /** mul2_f64: multiply two 64-bit floating-point numbers **/ + static rp make_mul2_f64(); + /** div2_f64: divide two 64-bit floating-point numbers **/ + static rp make_div2_f64(); + }; } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/llvmintrinsic.hpp b/include/xo/expression/llvmintrinsic.hpp index 052baa9e..bc484640 100644 --- a/include/xo/expression/llvmintrinsic.hpp +++ b/include/xo/expression/llvmintrinsic.hpp @@ -65,6 +65,9 @@ namespace xo { /** -> IRBuilder::CreateFadd (add 2 floating-point numbers) **/ fp_add, + /** -> IRBuilder::CreateFsub (subtract 2 floating-pointer numbers) **/ + fp_sub, + /** -> IRBuilder::CreateFmul (multiply 2 floating-point numbers) **/ fp_mul, @@ -105,6 +108,7 @@ namespace xo { case llvmintrinsic::i_sdiv: return "i_sdiv"; case llvmintrinsic::i_udiv: return "i_udiv"; case llvmintrinsic::fp_add: return "fp_add"; + case llvmintrinsic::fp_sub: return "fp_sub"; case llvmintrinsic::fp_mul: return "fp_mul"; case llvmintrinsic::fp_div: return "fp_div"; case llvmintrinsic::fp_sqrt: return "fp_sqrt"; diff --git a/src/expression/Apply.cpp b/src/expression/Apply.cpp index 36e2428a..497565b1 100644 --- a/src/expression/Apply.cpp +++ b/src/expression/Apply.cpp @@ -1,6 +1,7 @@ /* @file Apply.cpp */ #include "Apply.hpp" +#include "Primitive.hpp" #include "xo/indentlog/print/vector.hpp" namespace xo { @@ -25,6 +26,38 @@ namespace xo { return new Apply(fn_retval_type, fn, argv); } + rp + Apply::make_add2_f64(const rp & lhs, + const rp & rhs) + { + return Apply::make(Primitive_f64::make_add2_f64(), + {lhs, rhs}); + } + + rp + Apply::make_sub2_f64(const rp & lhs, + const rp & rhs) + { + return Apply::make(Primitive_f64::make_sub2_f64(), + {lhs, rhs}); + } + + rp + Apply::make_mul2_f64(const rp & lhs, + const rp & rhs) + { + return Apply::make(Primitive_f64::make_mul2_f64(), + {lhs, rhs}); + } + + rp + Apply::make_div2_f64(const rp & lhs, + const rp & rhs) + { + return Apply::make(Primitive_f64::make_div2_f64(), + {lhs, rhs}); + } + void Apply::display(std::ostream & os) const { os << " rp + { + static rp s_retval; + + if (!s_retval) + s_retval = Primitive::make("add2_f64", + &add2_f64, + true /*explicit_symbol_def*/, + llvmintrinsic::fp_add); + + return s_retval; + } + + auto + Primitive_f64::make_sub2_f64() -> rp + { + static rp s_retval; + + if (!s_retval) + s_retval = Primitive::make("sub2_f64", + &sub2_f64, + true /*explicit_symbol_def*/, + llvmintrinsic::fp_sub); + + return s_retval; + } + + auto + Primitive_f64::make_mul2_f64() -> rp + { + static rp s_retval; + + if (!s_retval) + s_retval = Primitive::make("mul2_f64", + &mul2_f64, + true /*explicit_symbol_def*/, + llvmintrinsic::fp_mul); + + return s_retval; + } + + auto + Primitive_f64::make_div2_f64() -> rp + { + static rp s_retval; + + if (!s_retval) + s_retval = Primitive::make("div2_f64", + &div2_f64, + true /*explicit_symbol_def*/, + llvmintrinsic::fp_div); + + return s_retval; + } + + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end Primitive.cpp */ diff --git a/src/expression/intrinsics.cpp b/src/expression/intrinsics.cpp new file mode 100644 index 00000000..27dcd83a --- /dev/null +++ b/src/expression/intrinsics.cpp @@ -0,0 +1,14 @@ +/* @file intrinsics.cpp */ + +#include "intrinsics.hpp" + +/* FIXME: don't know how to mangle symbols yet, + * so putting functions invoked from jit into global namespace + */ +extern "C" +int32_t +mul_i32(int32_t x, int32_t y) { + return x * y; +} + +/* end intrinsics.cpp */ From 5038045bdc9749cf4c1a025e696f65db1538cca6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 20 Aug 2024 13:31:17 -0400 Subject: [PATCH 51/58] lambda stuff [wip] --- include/xo/expression/Expression.hpp | 32 +------- .../xo/expression/GeneralizedExpression.hpp | 60 ++++++++++++++ include/xo/expression/Lambda.hpp | 31 ++++++++ include/xo/expression/Sequence.hpp | 52 +++++++++++++ include/xo/expression/exprtype.hpp | 3 + src/expression/CMakeLists.txt | 2 + src/expression/Expression.cpp | 4 - src/expression/GeneralizedExpression.cpp | 14 ++++ src/expression/Lambda.cpp | 74 ++++++++++++++++-- src/expression/Sequence.cpp | 78 +++++++++++++++++++ 10 files changed, 309 insertions(+), 41 deletions(-) create mode 100644 include/xo/expression/GeneralizedExpression.hpp create mode 100644 include/xo/expression/Sequence.hpp create mode 100644 src/expression/GeneralizedExpression.cpp create mode 100644 src/expression/Sequence.cpp diff --git a/include/xo/expression/Expression.hpp b/include/xo/expression/Expression.hpp index 3e9137c4..1b71ffcd 100644 --- a/include/xo/expression/Expression.hpp +++ b/include/xo/expression/Expression.hpp @@ -5,9 +5,7 @@ #pragma once -#include "xo/reflect/TypeDescr.hpp" -#include "xo/refcnt/Refcounted.hpp" -#include "exprtype.hpp" +#include "GeneralizedExpression.hpp" #include #include @@ -33,7 +31,7 @@ namespace xo { * * Every expression evaluates to a value with a particular type **/ - class Expression : public ref::Refcount { + class Expression : public GeneralizedExpression { public: using VisitFn = std::function )>; @@ -43,10 +41,7 @@ namespace xo { public: explicit Expression(exprtype extype, TypeDescr valuetype) - : extype_{extype}, valuetype_{valuetype}{} - - exprtype extype() const { return extype_; } - TypeDescr valuetype() const { return valuetype_; } + : GeneralizedExpression(extype, valuetype) {} /** find free named variables in this expression. * comprises the set of names that don't match formal parameters in @@ -87,29 +82,8 @@ namespace xo { **/ //virtual std::int32_t find_free_vars(std::vector> env) = 0; - /** write human-readable representation to stream **/ - virtual void display(std::ostream & os) const = 0; - /** human-readable string representation **/ - virtual std::string display_string() const; - - protected: - /** useful when scaffolding expressions in a parser **/ - void assign_valuetype(TypeDescr x) { valuetype_ = x; } - - private: - /** expression type (constant | apply | ..) for this expression **/ - exprtype extype_ = exprtype::invalid; - /** type information (when available) for values produced by this - * expression. - **/ - TypeDescr valuetype_ = nullptr; }; /*Expression*/ - inline std::ostream & - operator<<(std::ostream & os, const Expression & x) { - x.display(os); - return os; - } } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/GeneralizedExpression.hpp b/include/xo/expression/GeneralizedExpression.hpp new file mode 100644 index 00000000..7866284e --- /dev/null +++ b/include/xo/expression/GeneralizedExpression.hpp @@ -0,0 +1,60 @@ +/** @file GeneralizedExpression.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "xo/refcnt/Refcounted.hpp" +#include "xo/reflect/TypeDescr.hpp" +#include "exprtype.hpp" +//#include + +namespace xo { + namespace ast { + /** @class GeneralizedExpression + * @brief abstract syntax tree (non-executable) for schematica + * + * 'Generalized' because it includes both kernel and macro expressions. + * Every macro expression automatically translates to an equivalent kernel expression. + * Kernel expressions are directly executable. + **/ + class GeneralizedExpression : public ref::Refcount { + public: + using TypeDescr = xo::reflect::TypeDescr; + + public: + GeneralizedExpression(exprtype extype, TypeDescr valuetype) + : extype_{extype}, valuetype_{valuetype}{} + + exprtype extype() const { return extype_; } + TypeDescr valuetype() const { return valuetype_; } + + /** write human-readable representation to stream **/ + virtual void display(std::ostream & os) const = 0; + /** human-readable string representation **/ + virtual std::string display_string() const; + + protected: + /** useful when scaffolding expressions in a parser **/ + void assign_valuetype(TypeDescr x) { valuetype_ = x; } + + private: + /** expression type (constant | apply | ..) for this expression **/ + exprtype extype_ = exprtype::invalid; + /** type information (when available) for values produced by this + * expression. + **/ + TypeDescr valuetype_ = nullptr; + }; + + inline std::ostream & + operator<<(std::ostream & os, const GeneralizedExpression & x) { + x.display(os); + return os; + } + } /*namespace ast*/ +} /*namespace xo*/ + + +/** end GeneralizedExpression.hpp **/ diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index e5d4a84e..be4d833d 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -86,6 +86,18 @@ namespace xo { virtual void display(std::ostream & os) const override; + protected: + /** create type description for lambda with arguments @p argv + * and body expression @p body + **/ + static TypeDescr assemble_lambda_td(const std::vector> & argv, + const rp & body); + + /** create string description for function signature, + * consistent with c++ expectation + **/ + static std::string assemble_type_str(TypeDescr lambda_td); + private: /** @param lambda_type. function type for this lambda. * We arbitrarily choose the form "Retval(*)(Args...)" @@ -157,6 +169,25 @@ namespace xo { { return Lambda::make(name, argv, body); } + + class LambdaAccess : public Lambda { + public: + static rp make(const std::string & name, + const std::vector> & argv, + const rp & body); + static rp make_empty(); + + private: + /** lambda_type, body can be null here, + * in which case fill in with assign methods + **/ + LambdaAccess(const std::string & name, + TypeDescr lambda_type, + const rp & local_env, + const rp & body); + + + }; } /*namespace ast*/ } /*namespace xo*/ diff --git a/include/xo/expression/Sequence.hpp b/include/xo/expression/Sequence.hpp new file mode 100644 index 00000000..af2fb0e3 --- /dev/null +++ b/include/xo/expression/Sequence.hpp @@ -0,0 +1,52 @@ +/** @file Sequence.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "Expression.hpp" +//#include + +namespace xo { + namespace ast { + class Sequence : public Expression { + public: + Sequence(const std::vector> & xv) + : Expression(exprtype::sequence, + xv[xv.size() - 1]->valuetype()), + expr_v_(xv) {} + + static rp make(const std::vector> & xv) { return new Sequence(xv); } + + std::size_t size() const { return expr_v_.size(); } + const rp & operator[](std::size_t i) const { return expr_v_[i]; } + + // ----- from Expression ----- + + /** note: broken if .expr_v_ contains any def-exprs + * (will treat references to so-defined vars as free). + * must rewrite these first + **/ + virtual std::set get_free_variables() const override; + virtual std::size_t visit_preorder(VisitFn visitor_fn) override; + /** note: borken if .expr_v_ contains any def-exprs **/ + virtual std::size_t visit_layer(VisitFn visitor_fn) override; + virtual rp xform_layer(TransformFn visitor_fn) override; + virtual void attach_envs(ref::brw parent) override; + + // ----- from GeneralizedExpression ---- + + virtual void display(std::ostream & os) const override; + + private: + /** sequence of expressions; evaluate in left-to-right order. + **/ + std::vector> expr_v_; + }; + } /*namespace ast*/ + +} /*namespace xo*/ + + +/** end Sequence.hpp **/ diff --git a/include/xo/expression/exprtype.hpp b/include/xo/expression/exprtype.hpp index 49745d52..ae764021 100644 --- a/include/xo/expression/exprtype.hpp +++ b/include/xo/expression/exprtype.hpp @@ -32,6 +32,8 @@ namespace xo { variable, /** if-then-else **/ ifexpr, + /** sequence **/ + sequence, /** type conversion **/ convert, @@ -51,6 +53,7 @@ namespace xo { case exprtype::lambda: return "lambda"; case exprtype::variable: return "variable"; case exprtype::ifexpr: return "if_expr"; + case exprtype::sequence: return "sequence"; case exprtype::convert: return "convert"; default: break; } diff --git a/src/expression/CMakeLists.txt b/src/expression/CMakeLists.txt index 08c4302f..99a818db 100644 --- a/src/expression/CMakeLists.txt +++ b/src/expression/CMakeLists.txt @@ -2,12 +2,14 @@ set(SELF_LIB xo_expression) set(SELF_SRCS + GeneralizedExpression.cpp Expression.cpp DefineExpr.cpp Apply.cpp Lambda.cpp Variable.cpp IfExpr.cpp + Sequence.cpp LocalEnv.cpp ConvertExpr.cpp Primitive.cpp diff --git a/src/expression/Expression.cpp b/src/expression/Expression.cpp index ecebe8b4..e9f02932 100644 --- a/src/expression/Expression.cpp +++ b/src/expression/Expression.cpp @@ -4,10 +4,6 @@ namespace xo { namespace ast { - std::string - Expression::display_string() const { - return tostr(*this); - } } /*namespace ast*/ } /*namespace xo*/ diff --git a/src/expression/GeneralizedExpression.cpp b/src/expression/GeneralizedExpression.cpp new file mode 100644 index 00000000..6239f700 --- /dev/null +++ b/src/expression/GeneralizedExpression.cpp @@ -0,0 +1,14 @@ +/* @file GeneralizedExpression.cpp */ + +#include "GeneralizedExpression.hpp" + +namespace xo { + namespace ast { + std::string + GeneralizedExpression::display_string() const { + return tostr(*this); + } + } /*namespace ast*/ +} /*namespace xo*/ + +/* end GeneralizedExpression.cpp */ diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index 039e1e1e..98f69e3a 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -5,19 +5,21 @@ #include "xo/reflect/function/FunctionTdx.hpp" #include "xo/indentlog/print/vector.hpp" #include +#include namespace xo { + using xo::reflect::TypeDescr; using xo::reflect::TypeDescrBase; using xo::reflect::FunctionTdxInfo; using std::stringstream; namespace ast { - rp - Lambda::make(const std::string & name, - const std::vector> & argv, - const rp & body) + TypeDescr + Lambda::assemble_lambda_td(const std::vector> & argv, + const rp & body) { - using xo::reflect::FunctionTdx; + if (!body) + return nullptr; /** assemble function type. * @@ -41,8 +43,39 @@ namespace xo { TypeDescr lambda_td = TypeDescrBase::require_by_fn_info(function_info); + return lambda_td; + } + + std::string + Lambda::assemble_type_str(TypeDescr lambda_td) { + assert(lambda_td); + + std::stringstream ss; + + ss << lambda_td->fn_retval()->short_name() + << "("; + + for (std::size_t i = 0, n = lambda_td->n_fn_arg(); i < n; ++i) { + if (i > 0) + ss << ","; + ss << lambda_td->fn_arg(i)->short_name(); + } + ss << ")"; + + return ss.str(); + } + + rp + Lambda::make(const std::string & name, + const std::vector> & argv, + const rp & body) + { + using xo::reflect::FunctionTdx; + rp env = LocalEnv::make(argv); + TypeDescr lambda_td = assemble_lambda_td(argv, body); + rp retval = new Lambda(name, lambda_td, @@ -122,14 +155,15 @@ namespace xo { } /*regularize_layer_vars*/ Lambda::Lambda(const std::string & name, - TypeDescr lambda_type, + TypeDescr lambda_td, const rp & local_env, const rp & body) - : FunctionInterface(exprtype::lambda, lambda_type), + : FunctionInterface(exprtype::lambda, lambda_td), name_{name}, body_{body}, local_env_{local_env} { +#ifdef OBSOLETE stringstream ss; ss << "double"; ss << "("; @@ -139,8 +173,10 @@ namespace xo { ss << "double"; } ss << ")"; +#endif - this->type_str_ = ss.str(); + if (lambda_td) + this->type_str_ = assemble_type_str(lambda_td); /* ensure variables are unique within layer for this lambda */ this->layer_var_map_ = this->regularize_layer_vars(); @@ -209,6 +245,28 @@ namespace xo { << xtag("body", body_) << ">"; } /*display*/ + + // ----- Lambda Access ----- + + rp + LambdaAccess::make(const std::string & name, + const std::vector> & argv, + const rp & body) + { + TypeDescr lambda_td = + + TypeDescr body_valuetype = nullptr; + + } + + rp + LambdaAccess::make_empty() + { + return new LambdaAccess("" /*name*/, + nullptr /*lambda_td*/, + nullptr /*local_env*/, + nullptr /*body*/); + } } /*namespace ast*/ } /*namespace xo*/ diff --git a/src/expression/Sequence.cpp b/src/expression/Sequence.cpp new file mode 100644 index 00000000..98269e2c --- /dev/null +++ b/src/expression/Sequence.cpp @@ -0,0 +1,78 @@ +/* @file Sequence.cpp */ + +#include "Sequence.hpp" +#include + +namespace xo { + namespace ast { + std::set + Sequence::get_free_variables() const { + std::set retval; + + for (const auto & x : expr_v_) { + std::set free_vars; + free_vars = x->get_free_variables(); + + for (const auto & y : free_vars) + retval.insert(y); + } + + return retval; + } + + std::size_t + Sequence::visit_preorder(VisitFn visitor_fn) { + std::size_t n = 1; + + visitor_fn(this); + + for (const auto & x : expr_v_) + n += x->visit_preorder(visitor_fn); + + return n; + } + + std::size_t + Sequence::visit_layer(VisitFn visitor_fn) { + std::size_t n = 1; + + visitor_fn(this); + + for (const auto & x : expr_v_) + n += x->visit_layer(visitor_fn); + + return n; + } + + rp + Sequence::xform_layer(TransformFn xform_fn) { + for (std::size_t i = 0, n = expr_v_.size(); i < n; ++i) { + expr_v_[i] = expr_v_[i]->xform_layer(xform_fn); + } + + return xform_fn(this); + } + + void + Sequence::attach_envs(ref::brw p) { + for (const auto & x : expr_v_) + x->attach_envs(p); + } + + void + Sequence::display(std::ostream & os) const { + os << ""; + } + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end Sequence.cpp */ From a0e921e9ee5a4e510d107fd64f89e5956664986a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 21 Aug 2024 14:35:21 -0400 Subject: [PATCH 52/58] xo-expression: LambdaAccess [wip] --- include/xo/expression/Lambda.hpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index be4d833d..7ba7cdd2 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -98,7 +98,6 @@ namespace xo { **/ static std::string assemble_type_str(TypeDescr lambda_td); - private: /** @param lambda_type. function type for this lambda. * We arbitrarily choose the form "Retval(*)(Args...)" **/ @@ -107,6 +106,7 @@ namespace xo { const rp & local_env, const rp & body); + protected: /** compute free-variable set for this lambda **/ std::set calc_free_variables() const; @@ -118,17 +118,17 @@ namespace xo { **/ std::map> regularize_layer_vars(); - private: /** lambda name. Initially supporting only form like * (define (foo x y z) * (+ (* x x) (* y y) (* z z))) * - * In any case need to supply names for distinct things-for-which-code-is-generated - * so that they can be linked etc. + * In any case need to supply names for distinct + * things-for-which-code-is-generated so that they can be linked etc. **/ std::string name_; /** e.g. - * "double(double,double)" for function of two doubles that returns a double + * "double(double,double)" for function of two doubles that + * returns a double **/ std::string type_str_; /** function body **/ @@ -177,6 +177,11 @@ namespace xo { const rp & body); static rp make_empty(); + /* TODO: make sure Lambda members that depend on non-emtpy body + * get calc'd + */ + void assign_body(const rp & body); + private: /** lambda_type, body can be null here, * in which case fill in with assign methods From 3e28f8b42c1521d25225c089c3ae0b59823bffa0 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 21 Aug 2024 14:35:50 -0400 Subject: [PATCH 53/58] missed .cpp --- src/expression/Lambda.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index 98f69e3a..c21df94b 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -253,10 +253,19 @@ namespace xo { const std::vector> & argv, const rp & body) { - TypeDescr lambda_td = + TypeDescr lambda_td = assemble_lambda_td(argv, body); + rp env = LocalEnv::make(argv); - TypeDescr body_valuetype = nullptr; + rp retval + = new LambdaAccess(name, + lambda_td, + env, + body); + /* need two-phase construction b/c pointer cycle */ + env->assign_origin(retval.get()); + + return retval; } rp @@ -267,6 +276,14 @@ namespace xo { nullptr /*local_env*/, nullptr /*body*/); } + + LambdaAccess::LambdaAccess(const std::string & name, + TypeDescr lambda_td, + const rp & local_env, + const rp & body) + : Lambda(name, lambda_td, local_env, body) + {} + } /*namespace ast*/ } /*namespace xo*/ From b906fdfa5ac51d679240cd95a28860efe8732f12 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 22 Aug 2024 15:45:31 -0400 Subject: [PATCH 54/58] xo-expression: + Lambda.complete_assembly_from_body() --- include/xo/expression/Lambda.hpp | 15 +++++-- src/expression/Lambda.cpp | 72 +++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 5 deletions(-) diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 7ba7cdd2..668d1303 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -106,7 +106,6 @@ namespace xo { const rp & local_env, const rp & body); - protected: /** compute free-variable set for this lambda **/ std::set calc_free_variables() const; @@ -118,6 +117,14 @@ namespace xo { **/ std::map> regularize_layer_vars(); + /** compute derived members + * (type_str_, free_var_set_, captured_var_set_, layer_var_map_, + * nested_lambda_map_) + * once .body_ is established + **/ + void complete_assembly_from_body(); + + protected: /** lambda name. Initially supporting only form like * (define (foo x y z) * (+ (* x x) (* y y) (* z z))) @@ -177,9 +184,9 @@ namespace xo { const rp & body); static rp make_empty(); - /* TODO: make sure Lambda members that depend on non-emtpy body - * get calc'd - */ + /** assign body + compute derived members + * (see complete_assembly_from_body()) + **/ void assign_body(const rp & body); private: diff --git a/src/expression/Lambda.cpp b/src/expression/Lambda.cpp index c21df94b..5eae7973 100644 --- a/src/expression/Lambda.cpp +++ b/src/expression/Lambda.cpp @@ -154,6 +154,68 @@ namespace xo { return var_map; } /*regularize_layer_vars*/ + void + Lambda::complete_assembly_from_body() { + if (body_) { + TypeDescr lambda_td + = assemble_lambda_td(this->local_env_->argv(), body_); + + if (lambda_td) + this->type_str_ = assemble_type_str(lambda_td); + + this->layer_var_map_ = this->regularize_layer_vars(); + + this->free_var_set_ = this->calc_free_variables(); + + std::map> nested_lambda_map; + { + this->body_->visit_layer + ([&nested_lambda_map] + (ref::brw expr) + { + if (expr->extype() == exprtype::lambda) { + ref::brw lm = Lambda::from(expr); + + nested_lambda_map[lm->name()] = lm.get(); + } + }); + } + this->nested_lambda_map_ = std::move(nested_lambda_map); + + /* establish the set of captured local vars. + * These are any formal parameters that appear free in + * any layer of a nested lambda. + */ + std::set captured_var_set; + { + for (const auto & ix : nested_lambda_map_) { + std::set nested_free_var_set + = ix.second->get_free_variables(); + + for (const auto & jx : nested_free_var_set) { + /* check whether variable *jx is one of this lambda's + * formals + */ + auto bind = this->local_env_->lookup_local_binding(jx); + + if (bind.i_link_ == 0) { + /* yup, it's a formal parameter of this lambda */ + captured_var_set.insert(jx); + } + } + } + } + + this->captured_var_set_ = std::move(captured_var_set); + + /* in particular: + * - establish binding path (intrusively) for each variable + * assigns Variable::path_ + */ + this->body_->attach_envs(local_env_); + } + } + Lambda::Lambda(const std::string & name, TypeDescr lambda_td, const rp & local_env, @@ -209,7 +271,9 @@ namespace xo { = ix.second->get_free_variables(); for (const auto & jx : nested_free_var_set) { - /* check whether variable *jx is one of this lambda's formals */ + /* check whether variable *jx is one of this lambda's + * formals + */ auto bind = this->local_env_->lookup_local_binding(jx); if (bind.i_link_ == 0) { @@ -284,6 +348,12 @@ namespace xo { : Lambda(name, lambda_td, local_env, body) {} + void + LambdaAccess::assign_body(const rp & body) { + this->body_ = body; + + this->complete_assembly_from_body(); + } } /*namespace ast*/ } /*namespace xo*/ From d0b28e3cd4a4ecf15f85a95d720ac978917fa20c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 23 Aug 2024 11:35:10 -0400 Subject: [PATCH 55/58] xo-expression: + AssignExpr --- include/xo/expression/AssignExpr.hpp | 61 ++++++++++++++++++ include/xo/expression/exprtype.hpp | 3 + src/expression/AssignExpr.cpp | 93 ++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 include/xo/expression/AssignExpr.hpp create mode 100644 src/expression/AssignExpr.cpp diff --git a/include/xo/expression/AssignExpr.hpp b/include/xo/expression/AssignExpr.hpp new file mode 100644 index 00000000..0e7d5e5a --- /dev/null +++ b/include/xo/expression/AssignExpr.hpp @@ -0,0 +1,61 @@ +/* file AssignExpr.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "Expression.hpp" +#include "Variable.hpp" + +namespace xo { + namespace ast { + /** @class AssignExpr + * @brief Provide expression for assigning to a variable + * + * def pi = 3.14159265; + * def foo = 0.0; + * foo := pi / 2; + **/ + class AssignExpr : public Expression { + public: + static rp make(const rp & lhs, + const rp & rhs); + + static ref::brw from(ref::brw x) { + return ref::brw::from(x); + } + + const rp & lhs() const { return lhs_; } + const rp & rhs() const { return rhs_; } + + std::set calc_free_variables() const; + + // ----- inherited from Expression ----- + + virtual std::set get_free_variables() const override; + virtual std::size_t visit_preorder(VisitFn visitor_fn) override; + virtual std::size_t visit_layer(VisitFn visitor_fn) override; + virtual rp xform_layer(TransformFn xform_fn) override; + virtual void attach_envs(ref::brw p) override; + + virtual void display(std::ostream & os) const override; + + private: + AssignExpr(const rp & lhs, + const rp & rhs); + + private: + /** assign to this variable. **/ + rp lhs_; + /** assign value of this expression to variable @p lhs **/ + rp rhs_; + + /** free variables for this assignment **/ + std::set free_var_set_; + }; + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end AssignExpr.hpp */ diff --git a/include/xo/expression/exprtype.hpp b/include/xo/expression/exprtype.hpp index ae764021..e9b8393c 100644 --- a/include/xo/expression/exprtype.hpp +++ b/include/xo/expression/exprtype.hpp @@ -24,6 +24,8 @@ namespace xo { primitive, /** variable/function definition **/ define, + /** variable assignment **/ + assign, /** function call **/ apply, /** function definition **/ @@ -49,6 +51,7 @@ namespace xo { case exprtype::constant: return "constant"; case exprtype::primitive: return "primitive"; case exprtype::define: return "define"; + case exprtype::assign: return "assign"; case exprtype::apply: return "apply"; case exprtype::lambda: return "lambda"; case exprtype::variable: return "variable"; diff --git a/src/expression/AssignExpr.cpp b/src/expression/AssignExpr.cpp new file mode 100644 index 00000000..8d56a058 --- /dev/null +++ b/src/expression/AssignExpr.cpp @@ -0,0 +1,93 @@ +/* file AssignExpr.cpp + * + * author: Roland Conybeare + */ + +#include "AssignExpr.hpp" +#include "xo/indentlog/print/tag.hpp" + +namespace xo { + namespace ast { + rp + AssignExpr::make(const rp & lhs, + const rp & rhs) + { + return new AssignExpr(lhs, rhs); + } + + AssignExpr::AssignExpr(const rp & lhs, + const rp & rhs) + : Expression(exprtype::assign, rhs->valuetype()), + lhs_{lhs}, rhs_{rhs} + { + this->free_var_set_ = this->calc_free_variables(); + } + + std::set + AssignExpr::calc_free_variables() const + { + std::set retval = lhs_->get_free_variables(); + + std::set tmp = rhs_->get_free_variables(); + + for (const auto & name : tmp) + retval.insert(name); + + return retval; + } + + std::set + AssignExpr::get_free_variables() const { + return free_var_set_; + } + + std::size_t + AssignExpr::visit_preorder(VisitFn visitor_fn) { + std::size_t n = 1; + + visitor_fn(this); + + n += lhs_->visit_preorder(visitor_fn); + n += rhs_->visit_preorder(visitor_fn); + + return n; + } + + std::size_t + AssignExpr::visit_layer(VisitFn visitor_fn) { + std::size_t n = 1; + + visitor_fn(this); + + n += lhs_->visit_layer(visitor_fn); + n += rhs_->visit_layer(visitor_fn); + + return n; + } + + rp + AssignExpr::xform_layer(TransformFn xform_fn) { + this->lhs_ = Variable::from(lhs_->xform_layer(xform_fn)).promote(); + this->rhs_ = rhs_->xform_layer(xform_fn); + + return xform_fn(this); + } + + void + AssignExpr::attach_envs(ref::brw p) { + lhs_->attach_envs(p); + rhs_->attach_envs(p); + } + + void + AssignExpr::display(std::ostream & os) const { + os << ""; + } + } /*namespace ast*/ +} /*namespace xo*/ + + +/* end AssignExpr.cpp */ From 47a5e7ccaa3fd7907c60d2b65a442846eba2b2b3 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 23 Aug 2024 11:35:45 -0400 Subject: [PATCH 56/58] xo-expression: build: + AssignExpr --- src/expression/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/expression/CMakeLists.txt b/src/expression/CMakeLists.txt index 99a818db..0241ec67 100644 --- a/src/expression/CMakeLists.txt +++ b/src/expression/CMakeLists.txt @@ -5,6 +5,7 @@ set(SELF_SRCS GeneralizedExpression.cpp Expression.cpp DefineExpr.cpp + AssignExpr.cpp Apply.cpp Lambda.cpp Variable.cpp From 24d9d504b513f23cdc25bd8fe3354786c7b375c8 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 28 Aug 2024 00:57:17 -0400 Subject: [PATCH 57/58] xo-expression: minor print logging fixes --- include/xo/expression/Constant.hpp | 11 +++++++---- src/expression/Variable.cpp | 9 ++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/xo/expression/Constant.hpp b/include/xo/expression/Constant.hpp index 77098a5b..b2c9f71d 100644 --- a/include/xo/expression/Constant.hpp +++ b/include/xo/expression/Constant.hpp @@ -66,10 +66,13 @@ namespace xo { } virtual void display(std::ostream & os) const override { - os << "short_name()) - << xtag("value", value_) - << ">"; + os << "short_name()); + else + os << xtag("type", "nullptr");; + os << xtag("value", value_); + os << ">"; } private: diff --git a/src/expression/Variable.cpp b/src/expression/Variable.cpp index 766e0c67..18434afb 100644 --- a/src/expression/Variable.cpp +++ b/src/expression/Variable.cpp @@ -18,9 +18,12 @@ namespace xo { void Variable::display(std::ostream & os) const { os << "valuetype()->short_name()) - << ">"; + << xtag("name", name_); + if (this->valuetype()) + os << xtag("type", this->valuetype()->short_name()); + else + os << xtag("type", "nullptr"); + os << ">"; } /*display*/ } /*namespace ast*/ } /*namespace xo*/ From 5ac3c03a0ccfa8e4e320955b2589423a17c5e5ba Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 9 May 2025 00:03:08 -0500 Subject: [PATCH 58/58] xo-expression: + captured-var methods --- include/xo/expression/Lambda.hpp | 2 ++ include/xo/expression/LocalEnv.hpp | 1 + 2 files changed, 3 insertions(+) diff --git a/include/xo/expression/Lambda.hpp b/include/xo/expression/Lambda.hpp index 668d1303..452cafc9 100644 --- a/include/xo/expression/Lambda.hpp +++ b/include/xo/expression/Lambda.hpp @@ -40,7 +40,9 @@ namespace xo { const std::vector> & argv() const { return local_env_->argv(); } const rp & body() const { return body_; } + const std::string& i_argname(int i_arg) const { return local_env_->lookup_arg(i_arg)->name(); } bool needs_closure_flag() const { return !free_var_set_.empty(); } + bool is_captured(const std::string& var) const { return (captured_var_set_.find(var) != captured_var_set_.end()); } // ----- FunctionInterface ----- diff --git a/include/xo/expression/LocalEnv.hpp b/include/xo/expression/LocalEnv.hpp index d4facdba..603bbfda 100644 --- a/include/xo/expression/LocalEnv.hpp +++ b/include/xo/expression/LocalEnv.hpp @@ -32,6 +32,7 @@ namespace xo { Lambda * origin() const { return origin_; } const std::vector> & argv() const { return argv_; } + const rp& lookup_arg(int i) const { return argv_[i]; } int n_arg() const { return argv_.size(); } TypeDescr fn_arg(uint32_t i) const { return argv_[i]->valuetype(); }