mirror of
https://github.com/boostorg/yap.git
synced 2026-02-22 03:52:18 +00:00
Add specialization of default_transform_expression for expr_arity::three; add cond(), then(), else_() accessors.
This commit is contained in:
@@ -234,7 +234,7 @@ namespace boost::yap {
|
||||
struct default_transform_expression<
|
||||
Expr,
|
||||
Transform,
|
||||
expr_arity::two, // TODO: Add an overload for ::three!
|
||||
expr_arity::two,
|
||||
std::void_t<decltype(
|
||||
std::declval<Transform>()(
|
||||
detail::tag_for<remove_cv_ref_t<Expr>::kind>(),
|
||||
@@ -254,6 +254,32 @@ namespace boost::yap {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Expr, typename Transform>
|
||||
struct default_transform_expression<
|
||||
Expr,
|
||||
Transform,
|
||||
expr_arity::three,
|
||||
std::void_t<decltype(
|
||||
std::declval<Transform>()(
|
||||
detail::tag_for<remove_cv_ref_t<Expr>::kind>(),
|
||||
::boost::yap::value(::boost::yap::cond(std::declval<Expr>())),
|
||||
::boost::yap::value(::boost::yap::then(std::declval<Expr>())),
|
||||
::boost::yap::value(::boost::yap::else_(std::declval<Expr>()))
|
||||
)
|
||||
)>
|
||||
>
|
||||
{
|
||||
decltype(auto) operator() (Expr && expr, Transform && transform)
|
||||
{
|
||||
return static_cast<Transform &&>(transform)(
|
||||
detail::tag_for<remove_cv_ref_t<Expr>::kind>(),
|
||||
::boost::yap::value(::boost::yap::cond(static_cast<Expr &&>(expr))),
|
||||
::boost::yap::value(::boost::yap::then(static_cast<Expr &&>(expr))),
|
||||
::boost::yap::value(::boost::yap::else_(static_cast<Expr &&>(expr)))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Expr, typename Transform>
|
||||
struct transform_call_unpacker
|
||||
{
|
||||
|
||||
@@ -324,9 +324,10 @@ namespace boost::yap {
|
||||
if constexpr (detail::is_expr<T>::value) {
|
||||
using namespace hana::literals;
|
||||
constexpr expr_kind kind = detail::remove_cv_ref_t<T>::kind;
|
||||
constexpr detail::expr_arity arity = detail::arity_of<kind>();
|
||||
if constexpr (kind == expr_kind::expr_ref) {
|
||||
return ::boost::yap::value(::boost::yap::deref(static_cast<T &&>(x)));
|
||||
} else if constexpr (kind == expr_kind::terminal || kind == expr_kind::placeholder) {
|
||||
} else if constexpr (arity == detail::expr_arity::one) {
|
||||
if constexpr (std::is_lvalue_reference<T>{}) {
|
||||
return x.elements[0_c];
|
||||
} else {
|
||||
@@ -340,69 +341,112 @@ namespace boost::yap {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Expr>
|
||||
decltype(auto) left (Expr && expr)
|
||||
template <long long I, typename Expr>
|
||||
decltype(auto) get (Expr && expr, hana::llong<I> i)
|
||||
{
|
||||
static_assert(
|
||||
detail::is_expr<Expr>::value,
|
||||
"left() is only defined for expressions."
|
||||
"get() is only defined for expressions."
|
||||
);
|
||||
|
||||
using namespace hana::literals;
|
||||
constexpr expr_kind kind = detail::remove_cv_ref_t<Expr>::kind;
|
||||
if constexpr (kind == expr_kind::expr_ref) {
|
||||
return ::boost::yap::left(::boost::yap::deref(static_cast<Expr &&>(expr)));
|
||||
return ::boost::yap::get(::boost::yap::deref(static_cast<Expr &&>(expr)), i);
|
||||
} else {
|
||||
static_assert(
|
||||
0 <= I && I < decltype(hana::size(expr.elements))::value,
|
||||
"In get(expr, I), I must be nonnegative, and less "
|
||||
"than hana::size(expr.elements)."
|
||||
);
|
||||
if constexpr (std::is_lvalue_reference<Expr>{}) {
|
||||
return expr.elements[i];
|
||||
} else {
|
||||
return std::move(expr.elements[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <long long I, typename Expr>
|
||||
decltype(auto) get_c (Expr && expr)
|
||||
{ return ::boost::yap::get(static_cast<Expr &&>(expr), hana::llong_c<I>); }
|
||||
|
||||
template <typename Expr>
|
||||
decltype(auto) left (Expr && expr)
|
||||
{
|
||||
using namespace hana::literals;
|
||||
return ::boost::yap::get(static_cast<Expr &&>(expr), 0_c);
|
||||
constexpr expr_kind kind = detail::remove_cv_ref_t<Expr>::kind;
|
||||
if constexpr (kind != expr_kind::expr_ref) {
|
||||
static_assert(
|
||||
detail::arity_of<kind>() == detail::expr_arity::two,
|
||||
"left() is only defined for binary expressions."
|
||||
);
|
||||
if constexpr (std::is_lvalue_reference<Expr>{}) {
|
||||
return expr.elements[0_c];
|
||||
} else {
|
||||
return std::move(expr.elements[0_c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Expr>
|
||||
decltype(auto) right (Expr && expr)
|
||||
{
|
||||
static_assert(
|
||||
detail::is_expr<Expr>::value,
|
||||
"right() is only defined for expressions."
|
||||
);
|
||||
|
||||
using namespace hana::literals;
|
||||
return ::boost::yap::get(static_cast<Expr &&>(expr), 1_c);
|
||||
constexpr expr_kind kind = detail::remove_cv_ref_t<Expr>::kind;
|
||||
if constexpr (kind == expr_kind::expr_ref) {
|
||||
return ::boost::yap::right(::boost::yap::deref(static_cast<Expr &&>(expr)));
|
||||
} else {
|
||||
if constexpr (kind != expr_kind::expr_ref) {
|
||||
static_assert(
|
||||
detail::arity_of<kind>() == detail::expr_arity::two,
|
||||
"right() is only defined for binary expressions."
|
||||
);
|
||||
if constexpr (std::is_lvalue_reference<Expr>{}) {
|
||||
return expr.elements[1_c];
|
||||
} else {
|
||||
return std::move(expr.elements[1_c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Expr>
|
||||
decltype(auto) cond (Expr && expr)
|
||||
{
|
||||
using namespace hana::literals;
|
||||
return ::boost::yap::get(static_cast<Expr &&>(expr), 0_c);
|
||||
constexpr expr_kind kind = detail::remove_cv_ref_t<Expr>::kind;
|
||||
if constexpr (kind != expr_kind::expr_ref) {
|
||||
static_assert(
|
||||
kind == expr_kind::if_else,
|
||||
"cond() is only defined for if_else expressions."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Expr>
|
||||
decltype(auto) then (Expr && expr)
|
||||
{
|
||||
using namespace hana::literals;
|
||||
return ::boost::yap::get(static_cast<Expr &&>(expr), 1_c);
|
||||
constexpr expr_kind kind = detail::remove_cv_ref_t<Expr>::kind;
|
||||
if constexpr (kind != expr_kind::expr_ref) {
|
||||
static_assert(
|
||||
kind == expr_kind::if_else,
|
||||
"then() is only defined for if_else expressions."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Expr>
|
||||
decltype(auto) else_ (Expr && expr)
|
||||
{
|
||||
using namespace hana::literals;
|
||||
return ::boost::yap::get(static_cast<Expr &&>(expr), 2_c);
|
||||
constexpr expr_kind kind = detail::remove_cv_ref_t<Expr>::kind;
|
||||
if constexpr (kind != expr_kind::expr_ref) {
|
||||
static_assert(
|
||||
kind == expr_kind::if_else,
|
||||
"else_() is only defined for if_else expressions."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
template <long long I, typename Expr>
|
||||
decltype(auto) argument (Expr && expr, hana::llong<I> i)
|
||||
{
|
||||
static_assert(
|
||||
detail::is_expr<Expr>::value,
|
||||
"argument() is only defined for expressions."
|
||||
);
|
||||
|
||||
using namespace hana::literals;
|
||||
return ::boost::yap::get(static_cast<Expr &&>(expr), i);
|
||||
constexpr expr_kind kind = detail::remove_cv_ref_t<Expr>::kind;
|
||||
if constexpr (kind == expr_kind::expr_ref) {
|
||||
return ::boost::yap::argument(::boost::yap::deref(static_cast<Expr &&>(expr)), i);
|
||||
} else {
|
||||
if constexpr (kind != expr_kind::expr_ref) {
|
||||
static_assert(
|
||||
detail::arity_of<kind>() == detail::expr_arity::n,
|
||||
"argument() is only defined for call expressions."
|
||||
@@ -412,11 +456,6 @@ namespace boost::yap {
|
||||
"In argument(expr, I), I must be nonnegative, and less "
|
||||
"than hana::size(expr.elements)."
|
||||
);
|
||||
if constexpr (std::is_lvalue_reference<Expr>{}) {
|
||||
return expr.elements[i];
|
||||
} else {
|
||||
return std::move(expr.elements[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,15 @@ namespace user {
|
||||
|
||||
friend number operator* (number lhs, number rhs)
|
||||
{ return number{lhs.value * rhs.value}; }
|
||||
|
||||
friend number operator- (number n)
|
||||
{ return number{-n.value}; }
|
||||
|
||||
friend bool operator< (number lhs, double rhs)
|
||||
{ return lhs.value < rhs; }
|
||||
|
||||
friend bool operator< (double lhs, number rhs)
|
||||
{ return lhs < rhs.value; }
|
||||
};
|
||||
|
||||
number naxpy (number a, number x, number y)
|
||||
@@ -167,6 +176,74 @@ namespace user {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// unary transforms
|
||||
|
||||
struct disable_negate_xform_tag
|
||||
{
|
||||
decltype(auto) operator() (yap::negate_tag, user::number const & value)
|
||||
{ return yap::make_terminal(value); }
|
||||
|
||||
template <typename Expr>
|
||||
decltype(auto) operator() (yap::negate_tag, Expr const & expr)
|
||||
{ return expr; }
|
||||
};
|
||||
|
||||
struct disable_negate_xform_expr
|
||||
{
|
||||
template <typename Expr>
|
||||
decltype(auto) operator() (yap::expression<yap::expr_kind::negate, bh::tuple<Expr>> const & expr)
|
||||
{ return ::boost::yap::value(expr); }
|
||||
};
|
||||
|
||||
struct disable_negate_xform_both
|
||||
{
|
||||
decltype(auto) operator() (yap::negate_tag, user::number const & value)
|
||||
{ return yap::make_terminal(value); }
|
||||
|
||||
template <typename Expr>
|
||||
decltype(auto) operator() (yap::negate_tag, Expr const & expr)
|
||||
{ return expr; }
|
||||
|
||||
template <typename Expr>
|
||||
decltype(auto) operator() (yap::expression<yap::expr_kind::negate, bh::tuple<Expr>> const & expr)
|
||||
{
|
||||
throw std::logic_error("Oops! Picked the wrong overload!");
|
||||
return ::boost::yap::value(expr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// ternary transforms
|
||||
|
||||
struct ternary_to_else_xform_tag
|
||||
{
|
||||
template <typename Expr>
|
||||
decltype(auto) operator() (yap::if_else_tag, Expr const & cond, user::number const & then, user::number const & else_)
|
||||
{ return yap::make_terminal(else_); }
|
||||
};
|
||||
|
||||
struct ternary_to_else_xform_expr
|
||||
{
|
||||
template <typename Cond, typename Then, typename Else>
|
||||
decltype(auto) operator() (yap::expression<yap::expr_kind::if_else, bh::tuple<Cond, Then, Else>> const & expr)
|
||||
{ return ::boost::yap::else_(expr); }
|
||||
};
|
||||
|
||||
struct ternary_to_else_xform_both
|
||||
{
|
||||
template <typename Expr>
|
||||
decltype(auto) operator() (yap::if_else_tag, Expr const & cond, user::number const & then, user::number const & else_)
|
||||
{ return yap::make_terminal(else_); }
|
||||
|
||||
template <typename Cond, typename Then, typename Else>
|
||||
decltype(auto) operator() (yap::expression<yap::expr_kind::if_else, bh::tuple<Cond, Then, Else>> const & expr)
|
||||
{
|
||||
throw std::logic_error("Oops! Picked the wrong overload!");
|
||||
return ::boost::yap::else_(expr);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
TEST(user_expression_transform_3, test_user_expression_transform_3)
|
||||
@@ -341,3 +418,147 @@ TEST(move_only, test_user_expression_transform_3)
|
||||
|
||||
transform(std::move(transformed_expr), check_unique_ptrs_equal_7);
|
||||
}
|
||||
|
||||
TEST(unary_transforms, test_user_expression_transform_3)
|
||||
{
|
||||
term<user::number> a{{1.0}};
|
||||
term<user::number> x{{42.0}};
|
||||
term<user::number> y{{3.0}};
|
||||
|
||||
{
|
||||
auto expr = -x;
|
||||
{
|
||||
user::number result = evaluate(expr);
|
||||
EXPECT_EQ(result.value, -42);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::disable_negate_xform_tag{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 42);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::disable_negate_xform_expr{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 42);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::disable_negate_xform_both{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 42);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto expr = a * -x + y;
|
||||
{
|
||||
user::number result = evaluate(expr);
|
||||
EXPECT_EQ(result.value, -39);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::disable_negate_xform_tag{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 45);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::disable_negate_xform_expr{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 45);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::disable_negate_xform_both{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 45);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto expr = -(x + y);
|
||||
{
|
||||
user::number result = evaluate(expr);
|
||||
EXPECT_EQ(result.value, -45);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::disable_negate_xform_tag{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 45);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::disable_negate_xform_expr{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 45);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::disable_negate_xform_both{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 45);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ternary_transforms, test_user_expression_transform_3)
|
||||
{
|
||||
term<user::number> a{{1.0}};
|
||||
term<user::number> x{{42.0}};
|
||||
term<user::number> y{{3.0}};
|
||||
|
||||
{
|
||||
auto expr = if_else(0 < a, x, y);
|
||||
{
|
||||
user::number result = evaluate(expr);
|
||||
EXPECT_EQ(result.value, 42);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::ternary_to_else_xform_tag{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 3);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::ternary_to_else_xform_expr{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 3);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::ternary_to_else_xform_both{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 3);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto expr = y * if_else(0 < a, x, y) + user::number{0.0};
|
||||
{
|
||||
user::number result = evaluate(expr);
|
||||
EXPECT_EQ(result.value, 126);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::ternary_to_else_xform_tag{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 9);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::ternary_to_else_xform_expr{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 9);
|
||||
}
|
||||
|
||||
{
|
||||
auto transformed_expr = transform(expr, user::ternary_to_else_xform_both{});
|
||||
user::number result = evaluate(transformed_expr);
|
||||
EXPECT_EQ(result.value, 9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user