From 2d94fd51bf98907192db09dce5048197d17f2d1f Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 13 Jun 2024 15:20:36 -0400 Subject: [PATCH] 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 */