#pragma once #include #include #include #include #include "Support/Compare.h" #include "Support/Ranges.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" namespace clice::testing { template concept is_expr_v = requires { typename T::expr_tag; }; template struct default_formatter : std::formatter { using Base = std::formatter; template auto format(const auto& value, FormatContext& ctx) const { llvm::SmallString<256> buffer; static_cast(this)->format_to(std::back_inserter(buffer), value); return Base::format(std::string_view(buffer), ctx); } }; template decltype(auto) compute(const Expr& expr) { if constexpr(requires { typename Expr::expr_tag; }) { return expr(); } else { return expr; } } } // namespace clice::testing #define BINARY_PREDICATE(name, op) \ namespace clice::testing { \ decltype(auto) name##_impl(auto&& lhs, auto&& rhs); \ \ template \ struct name { \ const LHS& lhs; \ const RHS& rhs; \ \ using expr_tag = int; \ \ auto operator() () const { \ return name##_impl(compute(lhs), compute(rhs)); \ } \ }; \ \ template \ name(const LHS&, const RHS&) -> name; \ } \ \ template \ struct std::formatter> : \ clice::testing::default_formatter>> { \ void format_to(auto&& inserter, const auto& expr) const { \ std::format_to(inserter, "{} " #op " {}", expr.lhs, expr.rhs); \ } \ }; \ \ decltype(auto) clice::testing::name##_impl(auto&& lhs, auto&& rhs) BINARY_PREDICATE(add, +) { return lhs + rhs; }; BINARY_PREDICATE(sub, -) { return lhs - rhs; } BINARY_PREDICATE(mul, *) { return lhs * rhs; } BINARY_PREDICATE(eq, ==) { return refl::equal(lhs, rhs); } BINARY_PREDICATE(ne, !=) { return !refl::equal(lhs, rhs); } BINARY_PREDICATE(lt, <) { return refl::less(lhs, rhs); } BINARY_PREDICATE(le, <=) { return refl::less_equal(lhs, rhs); } BINARY_PREDICATE(gt, >) { return refl::less(rhs, lhs); } BINARY_PREDICATE(ge, >=) { return refl::less_equal(rhs, lhs); } BINARY_PREDICATE(has, has) { return ranges::contains(lhs, rhs); } #undef BINARY_PREDICATE