Files
clice/include/Test/TExpr.h
2025-08-16 23:09:13 +08:00

112 lines
4.4 KiB
C++

#pragma once
#include <string>
#include <format>
#include <algorithm>
#include <functional>
#include "Support/Compare.h"
#include "Support/Ranges.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
namespace clice::testing {
template <typename T>
concept is_expr_v = requires { typename T::expr_tag; };
template <typename Derived>
struct default_formatter : std::formatter<std::string_view> {
using Base = std::formatter<std::string_view>;
template <typename FormatContext>
auto format(const auto& value, FormatContext& ctx) const {
llvm::SmallString<256> buffer;
static_cast<const Derived*>(this)->format_to(std::back_inserter(buffer), value);
return Base::format(std::string_view(buffer), ctx);
}
};
template <typename Expr>
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 <typename LHS, typename RHS> \
struct name { \
const LHS& lhs; \
const RHS& rhs; \
\
using expr_tag = int; \
\
auto operator() () const { \
return name##_impl(compute(lhs), compute(rhs)); \
} \
}; \
\
template <typename LHS, typename RHS> \
name(const LHS&, const RHS&) -> name<LHS, RHS>; \
} \
\
template <typename LHS, typename RHS> \
struct std::formatter<clice::testing::name<LHS, RHS>> : \
clice::testing::default_formatter<std::formatter<clice::testing::name<LHS, RHS>>> { \
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