xo-expression: initial commit (constant + primitive)

This commit is contained in:
Roland Conybeare 2024-06-13 15:20:36 -04:00
commit 2d94fd51bf
17 changed files with 544 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -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

36
CMakeLists.txt Normal file
View file

@ -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

29
LICENSE Normal file
View file

@ -0,0 +1,29 @@
Copyright (c) 2024 Roland Conybeare <git3ub@nym.hush.com>, 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.

35
cmake/xo-bootstrap-macros.cmake Executable file
View file

@ -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()

View file

@ -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@")

1
example/CMakeLists.txt Normal file
View file

@ -0,0 +1 @@
add_subdirectory(ex1)

View file

@ -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

14
example/ex1/ex1.cpp Normal file
View file

@ -0,0 +1,14 @@
/** @file ex1.cpp **/
#include "xo/expression/Constant.hpp"
#include <iostream>
int
main() {
using xo::ast::make_constant;
auto expr = make_constant(7);
}
/** end ex1.cpp **/

View file

@ -0,0 +1,32 @@
/** @file Apply.hpp
*
* Author: Roland Conybeare
**/
#pragma once
#include "Expression.hpp"
//#include <cstdint>
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<Expression> fn_;
std::vector<ref::rp<Expression>> args_;
};
} /*namespace ast*/
} /*namespace xo*/
/** end Apply.hpp **/

View file

@ -0,0 +1,78 @@
/** @file Constant.hpp
*
* Author: Roland Conybeare
**/
#pragma once
#include "ConstantInterface.hpp"
#include <type_traits>
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 <typename T>
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<T>()},
value_(x)
{
static_assert(std::is_standard_layout_v<T> && std::is_trivial_v<T>);
}
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<void*>(erased_cptr);
return TaggedPtr(value_td_, erased_ptr);
}
// ----- Expression -----
virtual void display(std::ostream & os) const override {
os << "<Constant"
<< xtag("type", value_td_->short_name())
<< xtag("value", value_)
<< ">";
}
private:
/** type description for T **/
TypeDescr value_td_;
/** value of this constant **/
T value_;
}; /*Constant*/
template <typename T>
ref::rp<Constant<std::remove_reference_t<T>>>
make_constant(const T & x) {
return new Constant(x);
}
} /*namespace ast*/
} /*namespace xo*/
/** end Constant.hpp **/

View file

@ -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 <type_traits>
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<ConstantInterface> from(ref::brw<Expression> x) {
return ref::brw<ConstantInterface>::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 **/

View file

@ -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 **/

View file

@ -0,0 +1,89 @@
/** @file Primitive.hpp
*
* Author: Roland Conybeare
**/
#pragma once
#include "PrimitiveInterface.hpp"
//#include <cstdint>
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 <typename FunctionPointer>
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<FunctionPointer>()},
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<void*>(erased_cptr);
return TaggedPtr(value_td_, erased_ptr);
}
// ----- Expression -----
virtual void display(std::ostream & os) const override {
os << "<Primitive"
<< xtag("name", name_)
<< xtag("type", this->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 <typename FunctionPointer>
ref::rp<Primitive<FunctionPointer>>
make_primitive(const std::string & name, FunctionPointer x) {
return new Primitive(name, x);
}
} /*namespace ast*/
} /*namespace xo*/
/** end Primitive.hpp **/

View file

@ -0,0 +1,35 @@
/** @file PrimitiveInterface.hpp
*
* Author: Roland Conybeare
**/
#pragma once
#include "ConstantInterface.hpp"
//#include <cstdint>
#include <type_traits>
namespace xo {
namespace ast {
class PrimitiveInterface : public ConstantInterface {
public:
PrimitiveInterface() : ConstantInterface(exprtype::primitive) {}
/** downcast from Expression **/
static ref::brw<PrimitiveInterface> from(ref::brw<Expression> x) {
return ref::brw<PrimitiveInterface>::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 **/

View file

@ -0,0 +1,51 @@
/** @file exprtype.hpp
*
* Author: Roland Conybeare
**/
#pragma once
#include <cstdint>
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<std::size_t>(exprtype::n_expr);
} /*namespace ast*/
} /*namespace xo*/
/** end exprtype.hpp **/

View file

@ -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

View file

@ -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 */