#include "expression_fwd.hpp" #include "operators.hpp" #include "detail/expression.hpp" #define BOOST_PROTO17_STREAM_OPERATORS // TODO: For testing. #include "print.hpp" #include #include #include #include #include // TODO: For testing. // TODO: Verbose debugging mode for matching. // TODO: Proto-style "Fuzzy and Exact Matches of Terminals". namespace boost::proto17 { namespace detail { template auto default_eval_expr (expression const & expr, Tuple && tuple) { using namespace hana::literals; if constexpr (Kind == expr_kind::terminal) { static_assert(sizeof...(T) == 1); return expr.elements[0_c]; } else if constexpr (Kind == expr_kind::placeholder) { static_assert(sizeof...(T) == 1); return tuple[expr.elements[0_c]]; } else if constexpr (Kind == expr_kind::plus) { return eval_plus( default_eval_expr(expr.elements[0_c], static_cast(tuple)), default_eval_expr(expr.elements[1_c], static_cast(tuple)) ); } else if constexpr (Kind == expr_kind::minus) { return eval_minus( default_eval_expr(expr.elements[0_c], static_cast(tuple)), default_eval_expr(expr.elements[1_c], static_cast(tuple)) ); } else { assert(false && "Unhandled expr_kind in default_evaluate!"); return; } } } // TODO: Customization point. // TODO: static assert/SFINAE std::is_callable<> // TODO: static assert/SFINAE no placeholders template R evaluate_expression_as (expression const & expr) { return static_cast(detail::default_eval_expr(expr, hana::tuple<>())); } // TODO: static assert/SFINAE std::is_callable<> template auto evaluate (Expr const & expr, T && ...t) { return detail::default_eval_expr(expr, hana::make_tuple(static_cast(t)...)); } template struct expression { using this_type = expression; using tuple_type = hana::tuple; static const expr_kind kind = Kind; expression (T && ... t) : elements (static_cast(t)...) {} expression (hana::tuple const & rhs) : elements (rhs) {} expression (hana::tuple && rhs) : elements (std::move(rhs)) {} expression & operator= (hana::tuple const & rhs) { elements = rhs.elements; } expression & operator= (hana::tuple && rhs) { elements = std::move(rhs.elements); } tuple_type elements; template operator R () { return evaluate_expression_as(*this); } template auto operator+ (U && rhs) const & { using rhs_type = typename detail::rhs_type::type; return expression{ hana::tuple{*this, rhs_type{static_cast(rhs)}} }; } template auto operator+ (U && rhs) && { using rhs_type = typename detail::rhs_type::type; return expression{ hana::tuple{std::move(*this), rhs_type{static_cast(rhs)}} }; } template auto operator- (U && rhs) const & { using rhs_type = typename detail::rhs_type::type; return expression{ hana::tuple{*this, rhs_type{static_cast(rhs)}} }; } template auto operator- (U && rhs) && { using rhs_type = typename detail::rhs_type::type; return expression{ hana::tuple{std::move(*this), rhs_type{static_cast(rhs)}} }; } }; namespace literals { template constexpr auto operator"" _p () { using i = hana::llong({c...})>; return expression(i{}); } } namespace match { template struct expression { KindMatches kind_matches; hana::tuple elements; }; // TODO: Use this to detect identical types within a match. template struct placeholder : hana::llong {}; } template constexpr bool is_match_expression (hana::basic_type) { return false; } template constexpr bool is_match_expression (hana::basic_type>) { return true; } namespace literals { template constexpr auto operator"" _T () { return match::placeholder({c...})>{}; } } namespace detail { inline bool matches (...) { return false; } template bool matches (MatchExpr const & match_subtree, TreeExpr const & subtree); template void recursive_match_impl (MatchExpr const & match_subtree, TreeExpr const & subtree, bool & result) { if (!matches(match_subtree[hana::size_c], subtree[hana::size_c])) { result = false; } if constexpr (0 < I) { recursive_match_impl(match_subtree, subtree, result); } } template bool matches (MatchExpr const & match_subtree, TreeExpr const & subtree) { static_assert(is_match_expression(hana::typeid_(match_subtree)), "Attempted to use a non-tree as a match expression."); static_assert(is_expression(hana::typeid_(subtree)), "Attempted to use find a match in a non-tree."); // TODO: Verbose mode. if (!match_subtree.kind_matches(subtree)) { return false; } else { auto constexpr subtree_size = hana::size(subtree.elements); if constexpr (hana::size(match_subtree.elements) != subtree_size) { // TODO: Verbose mode. return false; } else { bool children_match = true; recursive_match_impl( match_subtree.elements, subtree.elements, children_match ); return children_match; } } } template auto mutate_subtrees_of ( Matcher const & match_subtree, expression & tree, Callable && mutation ) { // TODO: Process children first. if (matches(match_subtree, tree)) { return mutation(tree); } else if (Kind == expr_kind::terminal) { return tree; } else { auto mutate_child = [&match_subtree, &mutation] (auto & t) { return mutate_subtrees_of(match_subtree, t, static_cast(mutation)); }; auto return_elements = hana::transform(tree.elements, mutate_child); return make_expression(Kind, std::move(return_elements)); } } } } #include template using term = boost::proto17::terminal; namespace bp17 = boost::proto17; using namespace std::string_literals; void term_plus_x () { // char const * string { term unity{1.0}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + "3"; } // std::string temporary { term unity{1.0}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + "3"s; } // arrays { term unity{1.0}; int ints[] = {1, 2}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + ints; } { term unity{1.0}; int const ints[] = {1, 2}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + ints; } { term unity{1.0}; int ints[] = {1, 2}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + std::move(ints); } // pointers { term unity{1.0}; int ints[] = {1, 2}; int * int_ptr = ints; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + int_ptr; } { term unity{1.0}; int const ints[] = {1, 2}; int const * int_ptr = ints; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + int_ptr; } { term unity{1.0}; int ints[] = {1, 2}; int * int_ptr = ints; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + std::move(int_ptr); } // const pointers { term unity{1.0}; int ints[] = {1, 2}; int * const int_ptr = ints; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + int_ptr; } { term unity{1.0}; int const ints[] = {1, 2}; int const * const int_ptr = ints; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + int_ptr; } { term unity{1.0}; int ints[] = {1, 2}; int * const int_ptr = ints; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + std::move(int_ptr); } // values { term unity{1.0}; int i = 1; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + i; } { term unity{1.0}; int const i = 1; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + i; } { term unity{1.0}; int i = 1; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + std::move(i); } } void term_plus_x_this_ref_overloads() { { term unity{1.0}; int i = 1; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + i; } { term const unity{1.0}; int i = 1; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + i; } { term unity{1.0}; int i = 1; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = std::move(unity) + i; } } void term_plus_term () { // char const * string { term unity{1.0}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + term{"3"}; } // std::string temporary { term unity{1.0}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + term{"3"s}; } // pointers { term unity{1.0}; int ints_[] = {1, 2}; term ints = {ints_}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + ints; } { term unity{1.0}; int const ints_[] = {1, 2}; term ints = {ints_}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + ints; } { term unity{1.0}; int ints_[] = {1, 2}; term ints = {ints_}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + std::move(ints); } // const pointers { term unity{1.0}; int ints[] = {1, 2}; term int_ptr = {ints}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + int_ptr; } { term unity{1.0}; int const ints[] = {1, 2}; term int_ptr = {ints}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + int_ptr; } { term unity{1.0}; int ints[] = {1, 2}; term int_ptr = {ints}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + std::move(int_ptr); } // values { term unity{1.0}; term i = {1}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + i; } { term unity{1.0}; term i = {1}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + i; } { term unity{1.0}; term i = {1}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + std::move(i); } // const value terminals { term unity{1.0}; term const i = {1}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + i; } { term unity{1.0}; term const i = {1}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + i; } // lvalue refs { term unity{1.0}; int i_ = 1; term i{i_}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + i; } { term unity{1.0}; int i_ = 1; term i{i_}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + i; } { term unity{1.0}; int i_ = 1; term i{i_}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + std::move(i); } // rvalue refs { term unity{1.0}; int i_ = 1; term i{std::move(i_)}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + std::move(i); } { term unity{1.0}; int i_ = 1; term i{std::move(i_)}; bp17::expression< bp17::expr_kind::plus, term, term > unevaluated_expr = unity + std::move(i); } } void term_plus_expr () { // values { term unity{1.0}; term i = {1}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + i; bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + expr; } { term unity{1.0}; term i = {1}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + i; bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + expr; } { term unity{1.0}; term i = {1}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + std::move(i); bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + expr; } // const value terminals/expressions { term unity{1.0}; term const i = {1}; bp17::expression< bp17::expr_kind::plus, term, term > const expr = unity + i; bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + expr; } { term unity{1.0}; term i = {1}; bp17::expression< bp17::expr_kind::plus, term, term > const expr = unity + i; bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + expr; } // lvalue refs { term unity{1.0}; int i_ = 1; term i{i_}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + i; bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + expr; } { term unity{1.0}; int i_ = 1; term i{i_}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + i; bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + expr; } { term unity{1.0}; int i_ = 1; term i{i_}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + std::move(i); bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + expr; } { term unity{1.0}; int i_ = 1; term i{i_}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + i; bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + std::move(expr); } { term unity{1.0}; int i_ = 1; term i{i_}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + i; bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + std::move(expr); } { term unity{1.0}; int i_ = 1; term i{i_}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + std::move(i); bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + std::move(expr); } // rvalue refs { term unity{1.0}; int i_ = 1; term i{std::move(i_)}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + std::move(i); bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + std::move(expr); } { term unity{1.0}; int i_ = 1; term i{std::move(i_)}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + std::move(i); bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + std::move(expr); } { term unity{1.0}; int i_ = 1; term i{std::move(i_)}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + std::move(i); bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + std::move(expr); } { term unity{1.0}; int i_ = 1; term i{std::move(i_)}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + std::move(i); bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + std::move(expr); } } void placeholders () { using namespace boost::proto17::literals; { bp17::placeholder<0> p0 = 0_p; } { bp17::placeholder<0> p0 = 0_p; term unity{1.0}; bp17::expression< bp17::expr_kind::plus, bp17::placeholder<0>, term > expr = p0 + unity; } { bp17::placeholder<0> p0 = 0_p; bp17::expression< bp17::expr_kind::plus, bp17::placeholder<0>, bp17::placeholder<1> > expr = p0 + 1_p; } } void const_term_expr () { { term unity{1.0}; int i_ = 42; term i{std::move(i_)}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + std::move(i); bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + std::move(expr); } { term const unity{1.0}; int i_ = 42; term i{std::move(i_)}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + std::move(i); bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + std::move(expr); } { term unity{1.0}; int i_ = 42; term i{i_}; bp17::expression< bp17::expr_kind::plus, term, term > const expr = unity + std::move(i); bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + std::move(expr); } } void print () { term unity{1.0}; int i_ = 42; term i{std::move(i_)}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + std::move(i); bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + std::move(expr); std::cout << "================================================================================\n"; bp17::print(std::cout, unity); std::cout << "================================================================================\n"; bp17::print(std::cout, expr); std::cout << "================================================================================\n"; bp17::print(std::cout, unevaluated_expr); struct thing {}; term a_thing(thing{}); std::cout << "================================================================================\n"; bp17::print(std::cout, a_thing); std::cout << "\n"; std::cout << "================================================================================\n"; std::cout << "================================================================================\n"; { using namespace boost::proto17::literals; std::cout << (0_p + unity); std::cout << (2_p + 3_p); std::cout << (unity + 1_p); } #if defined(BOOST_PROTO17_STREAM_OPERATORS) std::cout << "\n"; std::cout << "================================================================================\n"; std::cout << "================================================================================\n"; std::cout << unity; std::cout << "================================================================================\n"; std::cout << expr; std::cout << "================================================================================\n"; std::cout << unevaluated_expr; std::cout << "================================================================================\n"; std::cout << a_thing; #endif } void default_eval () { term unity{1.0}; int i_ = 42; term i{std::move(i_)}; bp17::expression< bp17::expr_kind::minus, term, term > expr = unity - std::move(i); bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::minus, term, term > > unevaluated_expr = unity + std::move(expr); { double result = unity; std::cout << "unity=" << result << "\n"; // 1 } { double result = expr; std::cout << "expr=" << result << "\n"; // -41 } { double result = unevaluated_expr; std::cout << "unevaluated_expr=" << result << "\n"; // -40 } { double result = evaluate(unity, boost::hana::make_tuple(5, 6, 7)); std::cout << "evaluate(unity)=" << result << "\n"; // 1 } { double result = evaluate(expr, boost::hana::make_tuple()); std::cout << "evaluate(expr)=" << result << "\n"; // -41 } { double result = evaluate(unevaluated_expr, boost::hana::make_tuple(std::string("15"))); std::cout << "evaluate(unevaluated_expr)=" << result << "\n"; // -40 } } namespace test { struct number { explicit operator double () const { return value; } double value; }; // User-defined binary-plus! With weird semantics! inline auto eval_plus (number a, number b) { return number{a.value - b.value}; } } void user_operator_eval () { term unity{{1.0}}; double d_ = 42.0; term i{{d_}}; bp17::expression< bp17::expr_kind::plus, term, term > expr = unity + std::move(i); bp17::expression< bp17::expr_kind::plus, term, bp17::expression< bp17::expr_kind::plus, term, term > > unevaluated_expr = unity + std::move(expr); { double result = unity; std::cout << "unity=" << result << "\n"; // 1 } { double result = expr; std::cout << "expr=" << result << "\n"; // -41 } { double result = unevaluated_expr; std::cout << "unevaluated_expr=" << result << "\n"; // 42 } { double result = (double)evaluate(unity, boost::hana::make_tuple(5, 6, 7)); std::cout << "evaluate(unity)=" << result << "\n"; // 1 } { double result = (double)evaluate(expr, boost::hana::make_tuple()); std::cout << "evaluate(expr)=" << result << "\n"; // -41 } { double result = (double)evaluate(unevaluated_expr, boost::hana::make_tuple(std::string("15"))); std::cout << "evaluate(unevaluated_expr)=" << result << "\n"; // 42 } } void placeholder_eval () { using namespace boost::proto17::literals; bp17::placeholder<2> p2 = 2_p; int i_ = 42; term i{std::move(i_)}; bp17::expression< bp17::expr_kind::plus, bp17::placeholder<2>, term > expr = p2 + std::move(i); bp17::expression< bp17::expr_kind::plus, bp17::placeholder<2>, bp17::expression< bp17::expr_kind::plus, bp17::placeholder<2>, term > > unevaluated_expr = p2 + std::move(expr); { double result = evaluate(p2, 5, 6, 7); std::cout << "evaluate(p2)=" << result << "\n"; // 7 } { double result = evaluate(expr, std::string("15"), 3, 1); std::cout << "evaluate(expr)=" << result << "\n"; // 43 } { double result = evaluate(unevaluated_expr, std::string("15"), 2, 3); std::cout << "evaluate(unevaluated_expr)=" << result << "\n"; // 48 } } int main () { term_plus_x(); term_plus_x_this_ref_overloads(); term_plus_term(); term_plus_expr(); placeholders(); const_term_expr(); print(); default_eval(); user_operator_eval(); placeholder_eval(); #if 0 // TODO { bp17::terminal unity{1.0}; auto unevaluated_expr = unity + "3"; auto mutated_expr = mutate(unevaluated_expr, match_expr, mutation); auto result = bp17::eval(unevaluated_expr); } { bp17::terminal a{1.0}; bp17::terminal x{2.0}; bp17::terminal b{3}; auto unevaluated_expr = a * x + b; auto match_expr = 0_T * 0_T + 1_T; auto mutated_expr = mutate(unevaluated_expr, match_expr, mutation); auto result = bp17::eval(mutated_expr); } { bp17::terminal a{1.0}; bp17::terminal x{2.0}; bp17::terminal b{3}; auto match_double_2 = [] (auto && terminal) { if constexpr (hana::typeid_(terminal) == hana::type{}) return terminal == 2.0; else return false; }; auto unevaluated_expr = a * x + b; auto match_expr = bp17::match * match_double_2 + 0_T; auto mutated_expr = mutate(unevaluated_expr, match_expr, mutation); auto result = bp17::eval(mutated_expr); } #endif }