// Copyright (C) 2016-2018 T. Zachary Laine // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_YAP_DETAIL_TRANSFORM_HPP_INCLUDED #define BOOST_YAP_DETAIL_TRANSFORM_HPP_INCLUDED #include #include #include namespace boost { namespace yap { namespace detail { template struct nth_element_impl { using type = typename nth_element_impl::type; }; template struct nth_element_impl<0, T, Ts...> { using type = T; }; template using nth_element = typename nth_element_impl::type; template::value> struct rvalue_ref_to_value; template struct rvalue_ref_to_value { using type = typename std::remove_reference::type; }; template struct rvalue_ref_to_value { using type = T; }; template using rvalue_ref_to_value_t = typename rvalue_ref_to_value::type; template struct rvalue_mover { template constexpr decltype(auto) operator()(T && t) const { return static_cast(t); } }; template<> struct rvalue_mover { template constexpr std::remove_reference_t operator()(T && t) const { return std::move(t); } }; template struct placeholder_transform_t { using tuple_t = hana::tuple...>; placeholder_transform_t(PlaceholderArgs &&... args) : placeholder_args_(static_cast(args)...) {} template constexpr decltype(auto) operator()(expr_tag, boost::yap::placeholder) const { static_assert( I <= decltype(hana::size(std::declval()))::value, "Out of range placeholder index,"); using nth_type = nth_element; return as_expr( rvalue_mover::value>::value( placeholder_args_[hana::llong{}])); } tuple_t placeholder_args_; }; template struct evaluation_transform_t { using tuple_t = hana::tuple...>; evaluation_transform_t(PlaceholderArgs &&... args) : placeholder_args_(static_cast(args)...) {} template constexpr decltype(auto) operator()(expr_tag, boost::yap::placeholder) const { static_assert( I <= decltype(hana::size(std::declval()))::value, "Out of range placeholder index,"); using nth_type = nth_element; return rvalue_mover::value>{}( placeholder_args_[hana::llong{}]); } template constexpr decltype(auto) operator()(expr_tag, T && t) const { return static_cast(t); } #define BOOST_YAP_UNARY_OPERATOR_CASE(op, op_name) \ template \ constexpr decltype(auto) operator()(expr_tag, T && t) const \ { \ return op transform( \ as_expr(static_cast(t)), *this); \ } BOOST_YAP_UNARY_OPERATOR_CASE(+, unary_plus) BOOST_YAP_UNARY_OPERATOR_CASE(-, negate) BOOST_YAP_UNARY_OPERATOR_CASE(*, dereference) BOOST_YAP_UNARY_OPERATOR_CASE(~, complement) BOOST_YAP_UNARY_OPERATOR_CASE(&, address_of) BOOST_YAP_UNARY_OPERATOR_CASE(!, logical_not) BOOST_YAP_UNARY_OPERATOR_CASE(++, pre_inc) BOOST_YAP_UNARY_OPERATOR_CASE(--, pre_dec) template constexpr decltype(auto) operator()(expr_tag, T && t) const { return transform( as_expr(static_cast(t)), *this)++; } template constexpr decltype(auto) operator()(expr_tag, T && t) const { return transform( as_expr(static_cast(t)), *this)--; } #undef BOOST_YAP_UNARY_OPERATOR_CASE #define BOOST_YAP_BINARY_OPERATOR_CASE(op, op_name) \ template \ constexpr decltype(auto) operator()(expr_tag, T && t, U && u) const \ { \ return transform(as_expr(static_cast(t)), *this) \ op transform(as_expr(static_cast(u)), *this); \ } BOOST_YAP_BINARY_OPERATOR_CASE(<<, shift_left) BOOST_YAP_BINARY_OPERATOR_CASE(>>, shift_right) BOOST_YAP_BINARY_OPERATOR_CASE(*, multiplies) BOOST_YAP_BINARY_OPERATOR_CASE(/, divides) BOOST_YAP_BINARY_OPERATOR_CASE(%, modulus) BOOST_YAP_BINARY_OPERATOR_CASE(+, plus) BOOST_YAP_BINARY_OPERATOR_CASE(-, minus) BOOST_YAP_BINARY_OPERATOR_CASE(<, less) BOOST_YAP_BINARY_OPERATOR_CASE(>, greater) BOOST_YAP_BINARY_OPERATOR_CASE(<=, less_equal) BOOST_YAP_BINARY_OPERATOR_CASE(>=, greater_equal) BOOST_YAP_BINARY_OPERATOR_CASE(==, equal_to) BOOST_YAP_BINARY_OPERATOR_CASE(!=, not_equal_to) BOOST_YAP_BINARY_OPERATOR_CASE(||, logical_or) BOOST_YAP_BINARY_OPERATOR_CASE(&&, logical_and) BOOST_YAP_BINARY_OPERATOR_CASE(&, bitwise_and) BOOST_YAP_BINARY_OPERATOR_CASE(|, bitwise_or) BOOST_YAP_BINARY_OPERATOR_CASE (^, bitwise_xor) // clang-format off //[ evaluation_transform_comma template constexpr decltype(auto) operator()(expr_tag, T && t, U && u) const { return transform( as_expr(static_cast(t)), *this), transform( as_expr(static_cast(u)), *this); } //] // clang-format on BOOST_YAP_BINARY_OPERATOR_CASE(->*, mem_ptr) BOOST_YAP_BINARY_OPERATOR_CASE(=, assign) BOOST_YAP_BINARY_OPERATOR_CASE(<<=, shift_left_assign) BOOST_YAP_BINARY_OPERATOR_CASE(>>=, shift_right_assign) BOOST_YAP_BINARY_OPERATOR_CASE(*=, multiplies_assign) BOOST_YAP_BINARY_OPERATOR_CASE(/=, divides_assign) BOOST_YAP_BINARY_OPERATOR_CASE(%=, modulus_assign) BOOST_YAP_BINARY_OPERATOR_CASE(+=, plus_assign) BOOST_YAP_BINARY_OPERATOR_CASE(-=, minus_assign) BOOST_YAP_BINARY_OPERATOR_CASE(&=, bitwise_and_assign) BOOST_YAP_BINARY_OPERATOR_CASE(|=, bitwise_or_assign) BOOST_YAP_BINARY_OPERATOR_CASE(^=, bitwise_xor_assign) template constexpr decltype(auto) operator()(expr_tag, T && t, U && u) const { return transform( as_expr(static_cast(t)), *this)[transform( as_expr(static_cast(u)), *this)]; } #undef BOOST_YAP_BINARY_OPERATOR_CASE template constexpr decltype(auto) operator()(expr_tag, T && t, U && u, V && v) const { return transform(as_expr(static_cast(t)), *this) ? transform( as_expr(static_cast(u)), *this) : transform( as_expr(static_cast(v)), *this); } // clang-format off //[ evaluation_transform_call template constexpr decltype(auto) operator()( expr_tag, Callable && callable, Args &&... args) const { return transform(as_expr(static_cast(callable)), *this)( transform(as_expr(static_cast(args)), *this)... ); } //] // clang-format on tuple_t placeholder_args_; }; template struct transform_impl; template< bool Strict, typename Expr, typename TransformTuple, int I, expr_arity Arity, typename = void_t<>> struct transform_expression_tag; // Forward terminals/recurively transform noterminasl; attempted last. template struct default_transform { template constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const { return static_cast(expr); } }; template struct default_transform { struct incomplete; // If you're getting an error because this function is uncallable, // that's by design. You called yap::transform_strict(expr, xfrom) // and one or more subexpression of 'expr' are not callable with any // overload in 'xform'. template constexpr incomplete operator()(Expr && expr, TransformTuple transforms) const; }; template< expr_kind Kind, template class ExprTemplate, typename OldTuple, typename NewTuple> constexpr auto make_expr_from_tuple( ExprTemplate const & expr, NewTuple && tuple) { return ExprTemplate{std::move(tuple)}; } template constexpr auto make_expr_from_tuple(Expr const & expr, NewTuple && tuple) { return minimal_expr{std::move(tuple)}; } template constexpr decltype(auto) transform_nonterminal( Expr const & expr, Tuple && tuple, TransformTuple transforms) { auto transformed_tuple = hana::transform(static_cast(tuple), [&](auto && element) { using element_t = decltype(element); auto const kind = remove_cv_ref_t::kind; ::boost::yap::detail:: transform_impl xform; return xform(static_cast(element), transforms); }); auto const kind = remove_cv_ref_t::kind; return make_expr_from_tuple(expr, std::move(transformed_tuple)); } template<> struct default_transform { template constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const { return transform_nonterminal(expr, expr.elements, transforms); } }; template<> struct default_transform { template decltype(auto) operator()(Expr && expr, TransformTuple transforms) const { return transform_nonterminal( expr, std::move(expr.elements), transforms); } }; // Dispatch to the next transform, or to the default transform if there is // no next transform. template< bool Strict, typename Expr, typename TransformTuple, int I, bool NextTransformExists> struct next_or_default_transform { constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const { // Use the next transform. constexpr expr_kind kind = remove_cv_ref_t::kind; return detail:: transform_impl{}( static_cast(expr), transforms); } }; template struct next_or_default_transform { constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const { // No next transform exists; use the default transform. constexpr expr_kind kind = remove_cv_ref_t::kind; return default_transform< std::is_lvalue_reference::value, kind == expr_kind::terminal, Strict>{}(static_cast(expr), transforms); } }; // Expression-matching; attempted second. template< bool Strict, typename Expr, typename TransformTuple, int I, typename = detail::void_t<>> struct transform_expression_expr { constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const { // No expr-matching succeeded; use the next or default transform. return next_or_default_transform< Strict, Expr, TransformTuple, I, I + 1 < decltype(hana::size( std::declval()))::value>{}( static_cast(expr), transforms); } }; template struct transform_expression_expr< Strict, Expr, TransformTuple, I, void_t()[hana::llong{}])( std::declval()))>> { constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const { return (*transforms[hana::llong{}])(static_cast(expr)); } }; // Tag-matching; attempted first. template< bool Strict, typename Expr, typename TransformTuple, int I, expr_arity Arity, typename> struct transform_expression_tag { constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const { // No tag-matching succeeded; try expr-matching. return transform_expression_expr{}( static_cast(expr), transforms); } }; template decltype(auto) terminal_value(T && x) { return value_impl(static_cast(x)); } template struct transform_expression_tag< Strict, Expr, TransformTuple, I, expr_arity::one, void_t()[hana::llong{}])( expr_tag::kind>{}, terminal_value(::boost::yap::value(std::declval()))))>> { constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const { return (*transforms[hana::llong{}])( expr_tag::kind>{}, terminal_value( ::boost::yap::value(static_cast(expr)))); } }; template struct transform_expression_tag< Strict, Expr, TransformTuple, I, expr_arity::two, void_t()[hana::llong{}])( expr_tag::kind>{}, terminal_value(::boost::yap::left(std::declval())), terminal_value(::boost::yap::right(std::declval()))))>> { constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const { return (*transforms[hana::llong{}])( expr_tag::kind>{}, terminal_value(::boost::yap::left(static_cast(expr))), terminal_value( ::boost::yap::right(static_cast(expr)))); } }; template struct transform_expression_tag< Strict, Expr, TransformTuple, I, expr_arity::three, void_t()[hana::llong{}])( expr_tag::kind>{}, terminal_value(::boost::yap::cond(std::declval())), terminal_value(::boost::yap::then(std::declval())), terminal_value(::boost::yap::else_(std::declval()))))>> { constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const { return (*transforms[hana::llong{}])( expr_tag::kind>{}, terminal_value(::boost::yap::cond(static_cast(expr))), terminal_value(::boost::yap::then(static_cast(expr))), terminal_value( ::boost::yap::else_(static_cast(expr)))); } }; template struct transform_call_unpacker { template constexpr auto operator()( Expr && expr, Transform & transform, std::integer_sequence) const -> decltype(transform( expr_tag{}, terminal_value(::boost::yap::get( static_cast(expr), hana::llong_c))...)) { return transform( expr_tag{}, terminal_value(::boost::yap::get( static_cast(expr), hana::llong_c))...); } }; template constexpr auto indices_for(Expr const & expr) { constexpr long long size = decltype(hana::size(expr.elements))::value; return std::make_integer_sequence(); } template struct transform_expression_tag< Strict, Expr, TransformTuple, I, expr_arity::n, void_t()[hana::llong{}])>{}( std::declval(), *std::declval()[hana::llong{}], indices_for(std::declval())))>> { constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const { using transform_t = decltype(*transforms[hana::llong{}]); return transform_call_unpacker{}( static_cast(expr), *transforms[hana::llong{}], indices_for(expr)); } }; template struct transform_impl { template constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const { constexpr expr_kind kind = detail::remove_cv_ref_t::kind; return detail::transform_expression_tag< Strict, Expr, TransformTuple, I, detail::arity_of()>{}( static_cast(expr), transforms); } }; template struct transform_impl { template constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const { return detail::transform_impl{}( ::boost::yap::deref(static_cast(expr)), transforms); } }; }}} #endif