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}