draconisplusplus/include/matchit.hpp
2025-05-09 23:37:04 -04:00

1793 lines
No EOL
62 KiB
C++

/*
* Copyright (c) 2021-2022 Bowen Fu
* Distributed Under The Apache-2.0 License
*/
#pragma once
#include <algorithm>
#include <any>
#include <array>
#include <cassert>
#include <cstdint>
#include <optional>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#include "src/util/defs.hpp"
// NOLINTBEGIN(readability-identifier-*, cppcoreguidelines-special-member-functions)
namespace matchit {
namespace impl {
template <typename Value, bool byRef>
class ValueType {
public:
using ValueT = Value;
};
template <typename Value>
class ValueType<Value, true> {
public:
using ValueT = Value&&;
};
template <typename Value, typename... Patterns>
constexpr fn matchPatterns(Value&& value, const Patterns&... patterns);
template <typename Value, bool byRef>
class MatchHelper {
private:
using ValueT = typename ValueType<Value, byRef>::ValueT;
ValueT mValue;
using ValueRefT = ValueT&&;
public:
template <typename V>
constexpr explicit MatchHelper(V&& value)
: mValue { std::forward<V>(value) } {}
template <typename... PatternPair>
constexpr fn operator()(const PatternPair&... patterns) {
return matchPatterns(std::forward<ValueRefT>(mValue), patterns...);
}
MatchHelper(const MatchHelper&) = delete;
MatchHelper(MatchHelper&&) = delete;
};
template <typename Value>
constexpr fn match(Value&& value) {
return MatchHelper<Value, true> { std::forward<Value>(value) };
}
template <typename First, typename... Values>
constexpr fn match(First&& first, Values&&... values) {
auto result = std::forward_as_tuple(std::forward<First>(first), std::forward<Values>(values)...);
return MatchHelper<decltype(result), false> { std::forward<decltype(result)>(result) };
}
template <typename T>
class Nullary : public T {
public:
using T::operator();
};
template <typename T>
constexpr fn nullary(const T& t) {
return Nullary<T> { t };
}
template <typename T>
class Id;
template <typename T>
constexpr fn expr(Id<T>& id) {
return nullary([&] { return *id; });
}
template <typename T>
constexpr fn expr(const T& v) {
return nullary([&] { return v; });
}
template <typename T>
constexpr fn toNullary(T&& v) {
if constexpr (std::is_invocable_v<std::decay_t<T>>)
return std::forward<T>(v);
else
return expr(std::forward<T>(v));
}
// for constant
template <typename T>
class EvalTraits {
public:
template <typename... Args>
constexpr static fn evalImpl(const T& v, const Args&... /*unused*/) -> decltype(auto) {
return v;
}
};
template <typename T>
class EvalTraits<Nullary<T>> {
public:
constexpr static fn evalImpl(const Nullary<T>& e) -> decltype(auto) {
return e();
}
};
// Only allowed in nullary
template <typename T>
class EvalTraits<Id<T>> {
public:
constexpr static fn evalImpl(const Id<T>& id) -> decltype(auto) {
return *id;
}
};
template <typename Pred>
class Meet;
// Unary is an alias of Meet.
template <typename T>
using Unary = Meet<T>;
template <typename T>
class EvalTraits<Unary<T>> {
public:
template <typename Arg>
constexpr static fn evalImpl(const Unary<T>& e, const Arg& arg) -> decltype(auto) {
return e(arg);
}
};
class Wildcard;
template <>
class EvalTraits<Wildcard> {
public:
template <typename Arg>
constexpr static fn evalImpl(const Wildcard& /*unused*/, const Arg& arg) -> decltype(auto) {
return arg;
}
};
template <typename T, typename... Args>
constexpr fn evaluate_(const T& t, const Args&... args) -> decltype(auto) {
return EvalTraits<T>::evalImpl(t, args...);
}
template <typename T>
class IsNullaryOrId : public std::false_type {};
template <typename T>
class IsNullaryOrId<Id<T>> : public std::true_type {};
template <typename T>
class IsNullaryOrId<Nullary<T>> : public std::true_type {};
template <typename T>
constexpr bool isNullaryOrIdV = IsNullaryOrId<std::decay_t<T>>::value;
#define UN_OP_FOR_NULLARY(op) \
template <typename T> \
constexpr fn operator op(T const& t) \
requires isNullaryOrIdV<T> \
{ \
return nullary([&] { return op evaluate_(t); }); \
}
#define BIN_OP_FOR_NULLARY(op) \
template <typename T, typename U> \
constexpr fn operator op(T const& t, U const& u) \
requires isNullaryOrIdV<T> || isNullaryOrIdV<U> \
{ \
return nullary([&] { return evaluate_(t) op evaluate_(u); }); \
}
// ADL will find these operators.
UN_OP_FOR_NULLARY(!)
UN_OP_FOR_NULLARY(-)
#undef UN_OP_FOR_NULLARY
BIN_OP_FOR_NULLARY(+)
BIN_OP_FOR_NULLARY(-)
BIN_OP_FOR_NULLARY(*)
BIN_OP_FOR_NULLARY(/)
BIN_OP_FOR_NULLARY(%)
BIN_OP_FOR_NULLARY(<)
BIN_OP_FOR_NULLARY(<=)
BIN_OP_FOR_NULLARY(==)
BIN_OP_FOR_NULLARY(!=)
BIN_OP_FOR_NULLARY(>=)
BIN_OP_FOR_NULLARY(>)
BIN_OP_FOR_NULLARY(||)
BIN_OP_FOR_NULLARY(&&)
BIN_OP_FOR_NULLARY(^)
#undef BIN_OP_FOR_NULLARY
// Unary
template <typename T>
class IsUnaryOrWildcard : public std::false_type {};
template <>
class IsUnaryOrWildcard<Wildcard> : public std::true_type {};
template <typename T>
class IsUnaryOrWildcard<Unary<T>> : public std::true_type {};
template <typename T>
constexpr bool isUnaryOrWildcardV = IsUnaryOrWildcard<std::decay_t<T>>::value;
// unary is an alias of meet.
template <typename T>
constexpr fn unary(T&& t) {
return meet(std::forward<T>(t));
}
#define UN_OP_FOR_UNARY(op) \
template <typename T> \
constexpr fn operator op(T const& t) \
requires isUnaryOrWildcardV<T> \
{ \
return unary([&](auto&& arg) constexpr { return op evaluate_(t, arg); }); \
}
#define BIN_OP_FOR_UNARY(op) \
template <typename T, typename U> \
constexpr fn operator op(T const& t, U const& u) \
requires isUnaryOrWildcardV<T> || isUnaryOrWildcardV<U> \
{ \
return unary([&](auto&& arg) constexpr { return evaluate_(t, arg) op evaluate_(u, arg); }); \
}
UN_OP_FOR_UNARY(!)
UN_OP_FOR_UNARY(-)
#undef UN_OP_FOR_UNARY
BIN_OP_FOR_UNARY(+)
BIN_OP_FOR_UNARY(-)
BIN_OP_FOR_UNARY(*)
BIN_OP_FOR_UNARY(/)
BIN_OP_FOR_UNARY(%)
BIN_OP_FOR_UNARY(<)
BIN_OP_FOR_UNARY(<=)
BIN_OP_FOR_UNARY(==)
BIN_OP_FOR_UNARY(!=)
BIN_OP_FOR_UNARY(>=)
BIN_OP_FOR_UNARY(>)
BIN_OP_FOR_UNARY(||)
BIN_OP_FOR_UNARY(&&)
BIN_OP_FOR_UNARY(^)
#undef BIN_OP_FOR_UNARY
template <typename I, typename S = I>
class Subrange {
I mBegin;
S mEnd;
public:
constexpr Subrange(const I begin, const S end)
: mBegin { begin }, mEnd { end } {}
constexpr Subrange(const Subrange& other)
: mBegin { other.begin() }, mEnd { other.end() } {}
fn operator=(const Subrange& other)->Subrange& {
if (this == &other)
return *this;
mBegin = other.begin();
mEnd = other.end();
return *this;
}
[[nodiscard]] fn size() const -> size_t {
return static_cast<size_t>(std::distance(mBegin, mEnd));
}
[[nodiscard]] fn begin() const {
return mBegin;
}
[[nodiscard]] fn end() const {
return mEnd;
}
};
template <typename I, typename S>
constexpr fn makeSubrange(I begin, S end) {
return Subrange<I, S> { begin, end };
}
template <typename RangeType>
class IterUnderlyingType {
public:
using beginT = decltype(std::begin(std::declval<RangeType&>()));
using endT = decltype(std::end(std::declval<RangeType&>()));
};
// force array iterators fallback to pointers.
template <typename ElemT, size_t size>
class IterUnderlyingType<std::array<ElemT, size>> {
public:
using beginT = decltype(&*std::begin(std::declval<std::array<ElemT, size>&>()));
using endT = beginT;
};
// force array iterators fallback to pointers.
template <typename ElemT, size_t size>
class IterUnderlyingType<const std::array<ElemT, size>> {
public:
using beginT = decltype(&*std::begin(std::declval<const std::array<ElemT, size>&>()));
using endT = beginT;
};
template <typename RangeType>
using SubrangeT =
Subrange<typename IterUnderlyingType<RangeType>::beginT, typename IterUnderlyingType<RangeType>::endT>;
template <typename I, typename S>
fn operator==(const Subrange<I, S>& lhs, const Subrange<I, S>& rhs)->bool {
using std::operator==;
return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
}
template <typename K1, typename V1, typename K2, typename V2>
constexpr fn operator==(const std::pair<K1, V1>& t, const std::pair<K2, V2>& u) {
return t.first == u.first && t.second == u.second;
}
template <typename T, typename... Ts>
class WithinTypes {
public:
constexpr static bool value = (std::is_same_v<T, Ts> || ...);
};
template <typename T, typename Tuple>
class PrependUnique;
template <typename T, typename... Ts>
class PrependUnique<T, std::tuple<Ts...>> {
constexpr static bool unique = !WithinTypes<T, Ts...>::value;
public:
using type = std::conditional_t<unique, std::tuple<T, Ts...>, std::tuple<Ts...>>;
};
template <typename T, typename Tuple>
using PrependUniqueT = typename PrependUnique<T, Tuple>::type;
template <typename Tuple>
class Unique;
template <typename... Ts>
using UniqueT = typename Unique<std::tuple<Ts...>>::type;
template <>
class Unique<std::tuple<>> {
public:
using type = std::tuple<>;
};
template <typename T, typename... Ts>
class Unique<std::tuple<T, Ts...>> {
public:
using type = PrependUniqueT<T, UniqueT<Ts...>>;
};
static_assert(std::is_same_v<std::tuple<int32_t>, UniqueT<int32_t, int32_t>>);
static_assert(std::is_same_v<std::tuple<std::tuple<>, int32_t>, UniqueT<int32_t, std::tuple<>, int32_t>>);
using std::get;
namespace detail {
template <std::size_t start, class Tuple, std::size_t... I>
constexpr fn subtupleImpl(Tuple&& t, std::index_sequence<I...> /*unused*/) -> decltype(auto) {
return std::forward_as_tuple(get<start + I>(std::forward<Tuple>(t))...);
}
} // namespace detail
// [start, end)
template <std::size_t start, std::size_t end, class Tuple>
constexpr fn subtuple(Tuple&& t) -> decltype(auto) {
constexpr size_t tupleSize = std::tuple_size_v<std::remove_reference_t<Tuple>>;
static_assert(start <= end);
static_assert(end <= tupleSize);
return detail::subtupleImpl<start>(std::forward<Tuple>(t), std::make_index_sequence<end - start> {});
}
template <std::size_t start, class Tuple>
constexpr fn drop(Tuple&& t) -> decltype(auto) {
constexpr size_t tupleSize = std::tuple_size_v<std::remove_reference_t<Tuple>>;
static_assert(start <= tupleSize);
return subtuple<start, tupleSize>(std::forward<Tuple>(t));
}
template <std::size_t len, class Tuple>
constexpr fn take(Tuple&& t) -> decltype(auto) {
constexpr size_t tupleSize = std::tuple_size_v<std::remove_reference_t<Tuple>>;
static_assert(len <= tupleSize);
return subtuple<0, len>(std::forward<Tuple>(t));
}
template <class F, class Tuple>
constexpr fn apply_(F&& f, Tuple&& t) -> decltype(auto) {
return std::apply(std::forward<F>(f), drop<0>(std::forward<Tuple>(t)));
}
// as constexpr
template <class F, class... Args>
constexpr fn invoke_(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_v<F, Args...>) -> std::invoke_result_t<F, Args...> {
return std::apply(std::forward<F>(f), std::forward_as_tuple(std::forward<Args>(args)...));
}
template <class T>
struct decayArray {
private:
using U = std::remove_reference_t<T>;
public:
using type = std::conditional_t<std::is_array_v<U>, std::remove_extent_t<U>*, T>;
};
template <class T>
using decayArrayT = typename decayArray<T>::type;
static_assert(std::is_same_v<decayArrayT<int32_t*>, int32_t*>);
static_assert(std::is_same_v<decayArrayT<const int32_t*>, const int32_t*>);
static_assert(std::is_same_v<decayArrayT<const int32_t&>, const int32_t&>);
template <typename T>
struct AddConstToPointer {
using type =
std::conditional_t<!std::is_pointer_v<T>, T, std::add_pointer_t<std::add_const_t<std::remove_pointer_t<T>>>>;
};
template <typename T>
using AddConstToPointerT = typename AddConstToPointer<T>::type;
static_assert(std::is_same_v<AddConstToPointerT<void*>, const void*>);
static_assert(std::is_same_v<AddConstToPointerT<int32_t>, int32_t>);
template <typename Pattern>
using InternalPatternT = std::remove_reference_t<AddConstToPointerT<decayArrayT<Pattern>>>;
template <typename Pattern>
class PatternTraits;
template <typename... PatternPairs>
class PatternPairsRetType {
public:
using RetType = std::common_type_t<typename PatternPairs::RetType...>;
};
enum class IdProcess : uint8_t {
kCANCEL,
kCONFIRM,
};
template <typename Pattern>
constexpr void processId(const Pattern& pattern, int32_t depth, IdProcess idProcess) {
PatternTraits<Pattern>::processIdImpl(pattern, depth, idProcess);
}
template <typename Tuple>
class Variant;
template <typename T, typename... Ts>
class Variant<std::tuple<T, Ts...>> {
public:
using type = std::variant<std::monostate, T, Ts...>;
};
template <typename... Ts>
using UniqVariant = typename Variant<UniqueT<Ts...>>::type;
template <typename... Ts>
class Context {
using ElementT = UniqVariant<Ts...>;
using ContainerT = std::array<ElementT, sizeof...(Ts)>;
ContainerT mMemHolder;
size_t mSize = 0;
public:
template <typename T>
constexpr fn emplace_back(T&& t) -> void {
mMemHolder[mSize] = std::forward<T>(t);
++mSize;
}
constexpr fn back() -> ElementT& {
return mMemHolder[mSize - 1];
}
};
template <>
class Context<> {};
template <typename T>
class ContextTrait;
template <typename... Ts>
class ContextTrait<std::tuple<Ts...>> {
public:
using ContextT = Context<Ts...>;
};
template <typename Value, typename Pattern, typename ConctextT>
constexpr fn matchPattern(Value&& value, const Pattern& pattern, int32_t depth, ConctextT& context) {
const auto result = PatternTraits<Pattern>::matchPatternImpl(std::forward<Value>(value), pattern, depth, context);
const auto process = result ? IdProcess::kCONFIRM : IdProcess::kCANCEL;
processId(pattern, depth, process);
return result;
}
template <typename Pattern, typename Func>
class PatternPair {
public:
using RetType = std::invoke_result_t<Func>;
using PatternT = Pattern;
constexpr PatternPair(const Pattern& pattern, const Func& func)
: mPattern { pattern }, mHandler { func } {}
template <typename Value, typename ContextT>
constexpr fn matchValue(Value&& value, ContextT& context) const -> bool {
return matchPattern(std::forward<Value>(value), mPattern, /*depth*/ 0, context);
}
constexpr fn execute() const {
return mHandler();
}
private:
Pattern mPattern;
std::conditional_t<std::is_function_v<Func>, const Func&, const Func> mHandler;
};
template <typename Pattern, typename Pred>
class PostCheck;
template <typename Pred>
class When {
public:
Pred mPred;
};
template <typename Pred>
constexpr fn when(Pred&& pred) {
auto p = toNullary(std::forward<Pred>(pred));
return When<decltype(p)> { p };
}
template <typename Pattern>
class PatternHelper {
public:
constexpr explicit PatternHelper(const Pattern& pattern)
: mPattern { pattern } {}
template <typename Func>
constexpr fn operator=(Func&& func) { // NOLINT(misc-unconventional-assign-operator, cppcoreguidelines-c-copy-assignment-signature)
auto f = toNullary(std::forward<Func>(func));
return PatternPair<Pattern, decltype(f)> { mPattern, f };
}
template <typename Pred>
constexpr fn operator|(const When<Pred>& w) {
return PatternHelper<PostCheck<Pattern, Pred>>(PostCheck(mPattern, w.mPred));
}
private:
Pattern mPattern;
};
template <typename... Patterns>
class Ds;
template <typename... Patterns>
constexpr fn ds(const Patterns&... patterns) -> Ds<Patterns...>;
template <typename Pattern>
class OooBinder;
class PatternPipable {
public:
template <typename Pattern>
// ReSharper disable once CppDFAUnreachableFunctionCall
constexpr fn operator|(const Pattern& p) const->PatternHelper<Pattern> {
return PatternHelper<Pattern> { p };
}
template <typename T>
constexpr fn operator|(const T* p) const->PatternHelper<const T*> {
return PatternHelper<const T*> { p };
}
template <typename Pattern>
constexpr fn operator|(const OooBinder<Pattern>& p) const->PatternHelper<Pattern> {
return operator|(ds(p));
}
};
constexpr PatternPipable is {};
template <typename Pattern>
class PatternTraits {
public:
template <typename Value>
using AppResultTuple = std::tuple<>;
constexpr static int nbIdV = 0;
template <typename Value, typename ContextT>
constexpr static fn matchPatternImpl(Value&& value, const Pattern& pattern, int32_t /* depth */, ContextT& /*context*/) -> bool {
return pattern == std::forward<Value>(value);
}
constexpr static void processIdImpl(const Pattern& /*unused*/, int32_t /*depth*/, IdProcess /*unused*/) {}
};
class Wildcard {};
constexpr Wildcard _;
template <>
class PatternTraits<Wildcard> {
using Pattern = Wildcard;
public:
template <typename Value>
using AppResultTuple = std::tuple<>;
constexpr static int nbIdV = 0;
template <typename Value, typename ContextT>
constexpr static fn matchPatternImpl(Value&& /*unused*/, const Pattern& /*unused*/, int32_t /*unused*/, ContextT& /*unused*/) -> bool {
return true;
}
constexpr static fn processIdImpl(const Pattern& /*unused*/, int32_t /*depth*/, IdProcess /*unused*/) -> void {}
};
template <typename... Patterns>
class Or {
public:
constexpr explicit Or(const Patterns&... patterns)
: mPatterns { patterns... } {}
constexpr fn patterns() const -> const std::tuple<InternalPatternT<Patterns>...>& {
return mPatterns;
}
private:
std::tuple<InternalPatternT<Patterns>...> mPatterns;
};
template <typename... Patterns>
constexpr fn or_(const Patterns&... patterns) -> Or<Patterns...> {
return Or<Patterns...> { patterns... };
}
template <typename... Patterns>
class PatternTraits<Or<Patterns...>> {
public:
template <typename Value>
using AppResultTuple =
decltype(std::tuple_cat(typename PatternTraits<Patterns>::template AppResultTuple<Value> {}...));
constexpr static int nbIdV = (PatternTraits<Patterns>::nbIdV + ... + 0);
template <typename Value, typename ContextT>
constexpr static fn matchPatternImpl(Value&& value, const Or<Patterns...>& orPat, int32_t depth, ContextT& context) -> bool {
constexpr uint64_t patSize = sizeof...(Patterns);
return std::apply(
[&value, depth, &context](const auto&... patterns) {
return (matchPattern(value, patterns, depth + 1, context) || ...);
},
take<patSize - 1>(orPat.patterns())
) ||
matchPattern(std::forward<Value>(value), get<patSize - 1>(orPat.patterns()), depth + 1, context);
}
constexpr static void processIdImpl(const Or<Patterns...>& orPat, int32_t depth, IdProcess idProcess) {
return std::apply(
[depth, idProcess](const Patterns&... patterns) { return (processId(patterns, depth, idProcess), ...); },
orPat.patterns()
);
}
};
template <typename Pred>
class Meet : public Pred {
public:
using Pred::operator();
};
template <typename Pred>
constexpr fn meet(const Pred& pred) -> Meet<Pred> {
return Meet<Pred> { pred };
}
template <typename Pred>
class PatternTraits<Meet<Pred>> {
public:
template <typename Value>
using AppResultTuple = std::tuple<>;
constexpr static int nbIdV = 0;
template <typename Value, typename ContextT>
constexpr static fn matchPatternImpl(Value&& value, const Meet<Pred>& meetPat, int32_t /* depth */, ContextT& /*unused*/) -> bool {
return meetPat(std::forward<Value>(value));
}
constexpr static fn processIdImpl(const Meet<Pred>& /*unused*/, int32_t /*depth*/, IdProcess /*unused*/) -> void {}
};
template <typename Unary, typename Pattern>
class App {
public:
constexpr App(Unary&& unary, const Pattern& pattern) : mUnary { std::move(unary) }, mPattern { pattern } {}
[[nodiscard]] constexpr fn unary() const -> const Unary& {
return mUnary;
}
[[nodiscard]] constexpr fn pattern() const -> const InternalPatternT<Pattern>& {
return mPattern;
}
private:
std::decay_t<Unary> mUnary;
InternalPatternT<Pattern> mPattern;
};
template <typename Unary, typename Pattern>
constexpr fn app(Unary&& unary, const Pattern& pattern) -> App<Unary, Pattern> {
return { std::forward<Unary>(unary), pattern };
}
constexpr int y = 1;
static_assert(std::holds_alternative<const int32_t*>(std::variant<std::monostate, const int32_t*> { &y }));
template <typename Unary, typename Pattern>
class PatternTraits<App<Unary, Pattern>> {
public:
template <typename Value>
using AppResult = std::invoke_result_t<Unary, Value>;
// We store value for scalar types in Id and they can not be moved. So to
// support constexpr.
template <typename Value>
using AppResultCurTuple = std::conditional_t<
std::is_lvalue_reference_v<AppResult<Value>> || std::is_scalar_v<AppResult<Value>>,
std::tuple<>,
std::tuple<std::decay_t<AppResult<Value>>>>;
template <typename Value>
using AppResultTuple = decltype(std::tuple_cat(
std::declval<AppResultCurTuple<Value>>(),
std::declval<typename PatternTraits<Pattern>::template AppResultTuple<AppResult<Value>>>()
));
constexpr static int nbIdV = PatternTraits<Pattern>::nbIdV;
template <typename Value, typename ContextT>
constexpr static fn matchPatternImpl(Value&& value, const App<Unary, Pattern>& appPat, const int32_t depth, ContextT& context) -> bool {
if constexpr (std::is_same_v<AppResultCurTuple<Value>, std::tuple<>>) {
return matchPattern(
std::forward<AppResult<Value>>(invoke_(appPat.unary(), std::forward<Value>(value))), appPat.pattern(), depth + 1, context
);
} else {
context.emplace_back(invoke_(appPat.unary(), std::forward<Value>(value)));
decltype(auto) result = get<std::decay_t<AppResult<Value>>>(context.back());
return matchPattern(std::forward<decltype(result)>(result), appPat.pattern(), depth + 1, context);
}
}
constexpr static fn processIdImpl(const App<Unary, Pattern>& appPat, int32_t depth, IdProcess idProcess) -> void {
return processId(appPat.pattern(), depth, idProcess);
}
};
template <typename... Patterns>
class And {
public:
constexpr explicit And(const Patterns&... patterns)
: mPatterns { patterns... } {}
constexpr fn patterns() const -> const std::tuple<InternalPatternT<Patterns>...>& {
return mPatterns;
}
private:
std::tuple<InternalPatternT<Patterns>...> mPatterns;
};
template <typename... Patterns>
constexpr fn and_(const Patterns&... patterns) -> And<Patterns...> {
return { patterns... };
}
template <typename Tuple>
class NbIdInTuple;
template <typename... Patterns>
class NbIdInTuple<std::tuple<Patterns...>> {
public:
constexpr static int nbIdV = (PatternTraits<std::decay_t<Patterns>>::nbIdV + ... + 0);
};
template <typename... Patterns>
class PatternTraits<And<Patterns...>> {
public:
template <typename Value>
using AppResultTuple =
decltype(std::tuple_cat(std::declval<typename PatternTraits<Patterns>::template AppResultTuple<Value>>()...));
constexpr static int nbIdV = (PatternTraits<Patterns>::nbIdV + ... + 0);
template <typename Value, typename ContextT>
constexpr static fn matchPatternImpl(Value&& value, const And<Patterns...>& andPat, int32_t depth, ContextT& context) -> bool {
constexpr uint64_t patSize = sizeof...(Patterns);
const auto exceptLast = std::apply(
[&value, depth, &context](const auto&... patterns) {
return (matchPattern(value, patterns, depth + 1, context) && ...);
},
take<patSize - 1>(andPat.patterns())
);
// No Id in patterns except the last one.
if constexpr (NbIdInTuple<std::decay_t<decltype(take<patSize - 1>(andPat.patterns()))>>::nbIdV == 0) {
return exceptLast &&
matchPattern(std::forward<Value>(value), get<patSize - 1>(andPat.patterns()), depth + 1, context);
} else {
return exceptLast && matchPattern(value, get<patSize - 1>(andPat.patterns()), depth + 1, context);
}
}
constexpr static void processIdImpl(const And<Patterns...>& andPat, int32_t depth, IdProcess idProcess) {
return std::apply(
[depth, idProcess](const Patterns&... patterns) { return (processId(patterns, depth, idProcess), ...); },
andPat.patterns()
);
}
};
template <typename Pattern>
class Not {
public:
explicit Not(const Pattern& pattern)
: mPattern { pattern } {}
[[nodiscard]] constexpr fn pattern() const -> const InternalPatternT<Pattern>& {
return mPattern;
}
private:
InternalPatternT<Pattern> mPattern;
};
template <typename Pattern>
constexpr fn not_(const Pattern& pattern) -> Not<Pattern> {
return { pattern };
}
template <typename Pattern>
class PatternTraits<Not<Pattern>> {
public:
template <typename Value>
using AppResultTuple = typename PatternTraits<Pattern>::template AppResultTuple<Value>;
constexpr static int nbIdV = PatternTraits<Pattern>::nbIdV;
template <typename Value, typename ContextT>
constexpr static fn matchPatternImpl(Value&& value, const Not<Pattern>& notPat, const int32_t depth, ContextT& context) -> bool {
return !matchPattern(std::forward<Value>(value), notPat.pattern(), depth + 1, context);
}
constexpr static void processIdImpl(const Not<Pattern>& notPat, int32_t depth, IdProcess idProcess) {
processId(notPat.pattern(), depth, idProcess);
}
};
template <typename Ptr, typename Value, typename = std::void_t<>>
struct StorePointer : std::false_type {};
template <typename Type>
using ValueVariant = std::conditional_t<
std::is_lvalue_reference_v<Type>,
UniqVariant<std::remove_reference_t<Type>*>,
std::conditional_t<
std::is_rvalue_reference_v<Type>,
UniqVariant<std::remove_reference_t<Type>, std::remove_reference_t<Type>*>,
std::conditional_t<
std::is_abstract_v<std::remove_reference_t<Type>>,
UniqVariant<std::remove_reference_t<Type>*, const std::remove_reference_t<Type>*>,
UniqVariant<
std::remove_reference_t<Type>,
std::remove_reference_t<Type>*,
const std::remove_reference_t<Type>*>>>>;
template <typename Type, typename Value>
struct StorePointer<Type, Value, std::void_t<decltype(std::declval<ValueVariant<Type>&>() = &std::declval<Value>())>>
: std::is_reference<Value> {};
static_assert(!StorePointer<char, char>::value);
static_assert(StorePointer<char, char&>::value);
static_assert(StorePointer<const char, const char&>::value);
static_assert(StorePointer<const char, char&>::value);
static_assert(StorePointer<const std::tuple<int32_t&, int32_t&>, const std::tuple<int32_t&, int32_t&>&>::value);
template <typename... Ts>
class Overload : public Ts... {
public:
using Ts::operator()...;
};
template <typename... Ts>
constexpr fn overload(Ts&&... ts) -> Overload<Ts...> {
return { std::forward<Ts>(ts)... };
}
template <typename Pattern>
class OooBinder;
class Ooo;
template <typename Type>
class IdTraits {
public:
constexpr static fn equal(const Type& lhs, const Type& rhs) -> bool {
return lhs == rhs;
}
};
template <typename Type>
class IdBlockBase {
int32_t mDepth {};
private:
ValueVariant<Type> mVariant {};
public:
constexpr IdBlockBase() = default;
[[nodiscard]] constexpr fn variant() -> ValueVariant<Type>& {
return mVariant;
}
constexpr void reset(const int32_t depth) {
if (mDepth - depth >= 0) {
mVariant = {};
mDepth = depth;
}
}
constexpr void confirm(const int32_t depth) {
if (mDepth > depth || mDepth == 0) {
assert(depth == mDepth - 1 || depth == mDepth || mDepth == 0);
mDepth = depth;
}
}
};
constexpr IdBlockBase<int> dummy;
template <typename Type>
class IdBlock : public IdBlockBase<Type> {
public:
[[nodiscard]] constexpr fn hasValue() const -> bool {
return std::visit(
overload(
[](const Type&) { return true; },
[](const Type*) { return true; },
[](const std::monostate&) { return false; }
),
IdBlockBase<Type>::mVariant
);
}
[[nodiscard]] constexpr fn get() const -> decltype(auto) {
return std::visit(
overload(
[](const Type& v) -> Type { return v; },
[](const Type* p) -> const Type& { return *p; },
[](Type* p) -> const Type& { return *p; },
[](const std::monostate&) -> const Type& { throw std::logic_error("invalid state!"); }
),
IdBlockBase<Type>::mVariant
);
}
};
template <typename Type>
class IdBlock<const Type&> : public IdBlock<Type> {};
template <typename Type>
class IdBlock<Type&> : public IdBlockBase<Type&> {
public:
[[nodiscard]] constexpr fn hasValue() const -> bool {
return std::visit(
overload([](Type*) { return true; }, [](const std::monostate&) { return false; }),
IdBlockBase<Type&>::mVariant
);
}
[[nodiscard]] constexpr fn get() -> decltype(auto) {
return std::visit(
overload(
[](Type* v) -> Type& {
if (v == nullptr) {
throw std::logic_error("Trying to dereference a nullptr!");
}
return *v;
},
[](std::monostate&) -> Type& { throw std::logic_error("Invalid state!"); }
),
IdBlockBase<Type&>::mVariant
);
}
};
template <typename Type>
class IdBlock<Type&&> : public IdBlockBase<Type&&> {
public:
[[nodiscard]] constexpr fn hasValue() const -> bool {
return std::visit(
overload(
[](const Type&) { return true; }, [](Type*) { return true; }, [](const std::monostate&) { return false; }
),
IdBlockBase<Type&&>::mVariant
);
}
[[nodiscard]] constexpr fn get() -> decltype(auto) {
return std::visit(
overload(
[](Type& v) -> Type& { return v; },
[](Type* v) -> Type& {
if (v == nullptr) {
throw std::logic_error("Trying to dereference a nullptr!");
}
return *v;
},
[](std::monostate&) -> Type& { throw std::logic_error("Invalid state!"); }
),
IdBlockBase<Type&&>::mVariant
);
}
};
template <typename Type>
class IdUtil {
public:
template <typename Value>
constexpr static fn bindValue(ValueVariant<Type>& v, Value&& value, std::false_type /* StorePointer */) {
// for constexpr
v = ValueVariant<Type> { std::forward<Value>(value) };
}
template <typename Value>
constexpr static fn bindValue(ValueVariant<Type>& v, Value&& value, std::true_type /* StorePointer */) {
v = ValueVariant<Type> { &std::forward<Value>(value) };
}
};
template <typename Type>
class Id {
private:
using BlockT = IdBlock<Type>;
using BlockVT = std::variant<BlockT, BlockT*>;
BlockVT mBlock = BlockT {};
[[nodiscard]] constexpr fn internalValue() const -> decltype(auto) {
return block().get();
}
public:
constexpr Id() = default;
constexpr Id(const Id& id) : mBlock(BlockVT { &id.block() }) {}
// non-const to inform users not to mark Id as const.
template <typename Pattern>
constexpr fn at(Pattern&& pattern) -> decltype(auto) {
return and_(std::forward<Pattern>(pattern), *this);
}
// non-const to inform users not to mark Id as const.
constexpr fn at(const Ooo& /*unused*/) -> OooBinder<Type> {
return OooBinder<Type> { *this };
}
[[nodiscard]] constexpr fn block() const -> BlockT& {
return std::visit(
overload([](BlockT& v) -> BlockT& { return v; }, [](BlockT* p) -> BlockT& { return *p; }),
mBlock
);
}
template <typename Value>
constexpr fn matchValue(Value&& v) const -> bool {
if (hasValue())
return IdTraits<std::decay_t<Type>>::equal(internalValue(), v);
IdUtil<Type>::bindValue(block().variant(), std::forward<Value>(v), StorePointer<Type, Value> {});
return true;
}
constexpr fn reset(int32_t depth) const -> void {
return block().reset(depth);
}
constexpr fn confirm(int32_t depth) const -> void {
return block().confirm(depth);
}
[[nodiscard]] constexpr fn hasValue() const -> bool {
return block().hasValue();
}
// non-const to inform users not to mark Id as const.
[[nodiscard]] constexpr fn get() -> decltype(auto) {
return block().get();
}
// non-const to inform users not to mark Id as const.
[[nodiscard]] constexpr fn operator*()->decltype(auto) {
return get();
}
};
template <typename Type>
class PatternTraits<Id<Type>> {
public:
template <typename Value>
using AppResultTuple = std::tuple<>;
constexpr static bool nbIdV = true;
template <typename Value, typename ContextT>
constexpr static fn matchPatternImpl(Value&& value, const Id<Type>& idPat, int32_t /* depth */, ContextT& /*unused*/) -> bool {
return idPat.matchValue(std::forward<Value>(value));
}
constexpr static fn processIdImpl(const Id<Type>& idPat, int32_t depth, const IdProcess idProcess) -> void {
switch (idProcess) {
case IdProcess::kCANCEL: idPat.reset(depth); break;
case IdProcess::kCONFIRM: idPat.confirm(depth); break;
}
}
};
template <typename... Patterns>
class Ds {
public:
constexpr explicit Ds(const Patterns&... patterns)
: mPatterns { patterns... } {}
[[nodiscard]] constexpr fn patterns() const -> const auto& {
return mPatterns;
}
using Type = std::tuple<InternalPatternT<Patterns>...>;
private:
Type mPatterns;
};
template <typename... Patterns>
constexpr fn ds(const Patterns&... patterns) -> Ds<Patterns...> {
return Ds<Patterns...> { patterns... };
}
template <typename T>
class OooBinder {
Id<T> mId;
public:
explicit OooBinder(const Id<T>& id)
: mId { id } {}
[[nodiscard]] constexpr fn binder() const -> decltype(auto) {
return mId;
}
};
class Ooo {
public:
template <typename T>
constexpr fn operator()(Id<T> id) const->OooBinder<T> {
return OooBinder<T> { id };
}
};
constexpr Ooo ooo;
template <>
class PatternTraits<Ooo> {
public:
template <typename Value>
using AppResultTuple = std::tuple<>;
constexpr static bool nbIdV = false;
template <typename Value, typename ContextT>
constexpr static fn matchPatternImpl(Value&& /*unused*/, Ooo /*unused*/, int32_t /*depth*/, ContextT& /*unused*/) -> bool {
return true;
}
constexpr static fn processIdImpl(Ooo /*unused*/, int32_t /*depth*/, IdProcess /*unused*/) -> void {}
};
template <typename Pattern>
class PatternTraits<OooBinder<Pattern>> {
public:
template <typename Value>
using AppResultTuple = typename PatternTraits<Pattern>::template AppResultTuple<Value>;
constexpr static bool nbIdV = PatternTraits<Pattern>::nbIdV;
template <typename Value, typename ContextT>
constexpr static fn matchPatternImpl(Value&& value, const OooBinder<Pattern>& oooBinderPat, const int32_t depth, ContextT& context) {
return matchPattern(std::forward<Value>(value), oooBinderPat.binder(), depth + 1, context);
}
constexpr static fn processIdImpl(const OooBinder<Pattern>& oooBinderPat, int32_t depth, IdProcess idProcess) -> void {
processId(oooBinderPat.binder(), depth, idProcess);
}
};
template <typename T>
class IsOoo : public std::false_type {};
template <>
class IsOoo<Ooo> : public std::true_type {};
template <typename T>
class IsOooBinder : public std::false_type {};
template <typename T>
class IsOooBinder<OooBinder<T>> : public std::true_type {};
template <typename T>
constexpr bool isOooBinderV = IsOooBinder<std::decay_t<T>>::value;
template <typename T>
constexpr bool isOooOrBinderV = IsOoo<std::decay_t<T>>::value || isOooBinderV<T>;
template <typename... Patterns>
constexpr size_t nbOooOrBinderV = ((isOooOrBinderV<Patterns> ? 1 : 0) + ... + 0);
static_assert(nbOooOrBinderV<int32_t&, const Ooo&, const char*, Wildcard, const Ooo> == 2);
template <typename Tuple, std::size_t... I>
constexpr fn findOooIdxImpl(std::index_sequence<I...> /*unused*/) -> std::size_t {
return ((isOooOrBinderV<decltype(get<I>(std::declval<Tuple>()))> ? I : 0) + ...);
}
template <typename Tuple>
constexpr fn findOooIdx() -> std::size_t {
return findOooIdxImpl<Tuple>(std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>> {});
}
static_assert(isOooOrBinderV<Ooo>);
static_assert(isOooOrBinderV<OooBinder<int32_t>>);
static_assert(findOooIdx<std::tuple<int32_t, OooBinder<int32_t>, const char*>>() == 1);
static_assert(findOooIdx<std::tuple<int32_t, Ooo, const char*>>() == 1);
using std::get;
template <std::size_t valueStartIdx, std::size_t patternStartIdx, std::size_t... I, typename ValueTuple, typename PatternTuple, typename ContextT>
constexpr fn matchPatternMultipleImpl(ValueTuple&& valueTuple, PatternTuple&& patternTuple, const int32_t depth, ContextT& context, std::index_sequence<I...> /*unused*/) -> decltype(auto) {
const fn func = [&]<typename T>(T&& value, auto&& pattern) { return matchPattern(std::forward<T>(value), pattern, depth + 1, context); };
static_cast<void>(func);
return (func(get<I + valueStartIdx>(std::forward<ValueTuple>(valueTuple)), std::get<I + patternStartIdx>(patternTuple)) && ...);
}
template <std::size_t valueStartIdx, std::size_t patternStartIdx, std::size_t size, typename ValueTuple, typename PatternTuple, typename ContextT>
constexpr fn matchPatternMultiple(ValueTuple&& valueTuple, PatternTuple&& patternTuple, int32_t depth, ContextT& context) -> decltype(auto) {
return matchPatternMultipleImpl<valueStartIdx, patternStartIdx>(
std::forward<ValueTuple>(valueTuple), std::forward<PatternTuple>(patternTuple), depth, context, std::make_index_sequence<size> {}
);
}
template <std::size_t patternStartIdx, std::size_t... I, typename RangeBegin, typename PatternTuple, typename ContextT>
constexpr fn matchPatternRangeImpl(RangeBegin&& rangeBegin, PatternTuple&& patternTuple, const int32_t depth, ContextT& context, std::index_sequence<I...> /*unused*/) -> decltype(auto) {
const fn func = [&]<typename T>(T&& value, auto&& pattern) { return matchPattern(std::forward<T>(value), pattern, depth + 1, context); };
static_cast<void>(func);
auto it = rangeBegin;
bool result = true;
((result = result && func(*it, std::get<I + patternStartIdx>(patternTuple)), ++it), ...);
return result;
}
template <std::size_t patternStartIdx, std::size_t size, typename ValueRangeBegin, typename PatternTuple, typename ContextT>
constexpr fn matchPatternRange(
ValueRangeBegin&& valueRangeBegin,
PatternTuple&& patternTuple,
int32_t depth,
ContextT& context
) {
return matchPatternRangeImpl<patternStartIdx>(
std::forward<ValueRangeBegin>(valueRangeBegin), std::forward<PatternTuple>(patternTuple), depth, context, std::make_index_sequence<size> {}
);
}
template <std::size_t start, typename Indices, typename Tuple>
class IndexedTypes;
template <typename Tuple, std::size_t start, std::size_t... I>
class IndexedTypes<start, std::index_sequence<I...>, Tuple> {
public:
using type = std::tuple<std::decay_t<decltype(std::get<start + I>(std::declval<Tuple>()))>...>;
};
template <std::size_t start, std::size_t end, class Tuple>
class SubTypes {
constexpr static size_t tupleSize = std::tuple_size_v<std::remove_reference_t<Tuple>>;
static_assert(start <= end);
static_assert(end <= tupleSize);
using Indices = std::make_index_sequence<end - start>;
public:
using type = typename IndexedTypes<start, Indices, Tuple>::type;
};
template <std::size_t start, std::size_t end, class Tuple>
using SubTypesT = typename SubTypes<start, end, Tuple>::type;
static_assert(std::is_same_v<std::tuple<std::nullptr_t>, SubTypesT<3, 4, std::tuple<char, bool, int32_t, std::nullptr_t>>>);
static_assert(std::is_same_v<std::tuple<char>, SubTypesT<0, 1, std::tuple<char, bool, int32_t, std::nullptr_t>>>);
static_assert(std::is_same_v<std::tuple<>, SubTypesT<1, 1, std::tuple<char, bool, int32_t, std::nullptr_t>>>);
static_assert(std::is_same_v<std::tuple<int32_t, std::nullptr_t>, SubTypesT<2, 4, std::tuple<char, bool, int32_t, std::nullptr_t>>>);
template <typename ValueTuple>
class IsArray : public std::false_type {};
template <typename T, size_t s>
class IsArray<std::array<T, s>> : public std::true_type {};
template <typename ValueTuple>
constexpr bool isArrayV = IsArray<std::decay_t<ValueTuple>>::value;
template <typename Value, typename = std::void_t<>>
struct IsTupleLike : std::false_type {};
template <typename Value>
struct IsTupleLike<Value, std::void_t<decltype(std::tuple_size<Value>::value)>> : std::true_type {};
template <typename ValueTuple>
constexpr bool isTupleLikeV = IsTupleLike<std::decay_t<ValueTuple>>::value;
static_assert(isTupleLikeV<std::pair<int32_t, char>>);
static_assert(!isTupleLikeV<bool>);
template <typename Value, typename = std::void_t<>>
struct IsRange : std::false_type {};
template <typename Value>
struct IsRange<Value, std::void_t<decltype(std::begin(std::declval<Value>())), decltype(std::end(std::declval<Value>()))>>
: std::true_type {};
template <typename ValueTuple>
constexpr bool isRangeV = IsRange<std::decay_t<ValueTuple>>::value;
static_assert(!isRangeV<std::pair<int32_t, char>>);
static_assert(isRangeV<const std::array<int32_t, 5>>);
template <typename... Patterns>
class PatternTraits<Ds<Patterns...>> {
constexpr static size_t nbOooOrBinder = nbOooOrBinderV<Patterns...>;
static_assert(nbOooOrBinder == 0 || nbOooOrBinder == 1);
public:
template <typename PsTuple, typename VsTuple>
class PairPV;
template <typename... Ps, typename... Vs>
class PairPV<std::tuple<Ps...>, std::tuple<Vs...>> {
public:
using type = decltype(std::tuple_cat(std::declval<typename PatternTraits<Ps>::template AppResultTuple<Vs>>()...));
};
template <std::size_t nbOoos, typename ValueTuple>
class AppResultForTupleHelper;
template <typename... Values>
class AppResultForTupleHelper<0, std::tuple<Values...>> {
public:
using type = decltype(std::tuple_cat(std::declval<typename PatternTraits<Patterns>::template AppResultTuple<Values>>()...));
};
template <typename... Values>
class AppResultForTupleHelper<1, std::tuple<Values...>> {
constexpr static size_t idxOoo = findOooIdx<typename Ds<Patterns...>::Type>();
using Ps0 = SubTypesT<0, idxOoo, std::tuple<Patterns...>>;
using Vs0 = SubTypesT<0, idxOoo, std::tuple<Values...>>;
constexpr static bool isBinder = isOooBinderV<std::tuple_element_t<idxOoo, std::tuple<Patterns...>>>;
// <0, ...int32_t> to workaround compile failure for std::tuple<>.
using ElemT = std::tuple_element_t<0, std::tuple<std::remove_reference_t<Values>..., int32_t>>;
constexpr static int64_t diff = static_cast<int64_t>(sizeof...(Values) - sizeof...(Patterns));
constexpr static size_t clippedDiff = static_cast<size_t>(diff > 0 ? diff : 0);
using OooResultTuple =
std::conditional_t<isBinder, std::tuple<SubrangeT<std::array<ElemT, clippedDiff>>>, std::tuple<>>;
using FirstHalfTuple = typename PairPV<Ps0, Vs0>::type;
using Ps1 = SubTypesT<idxOoo + 1, sizeof...(Patterns), std::tuple<Patterns...>>;
constexpr static size_t vs1Start = static_cast<size_t>(static_cast<int64_t>(idxOoo) + 1 + diff);
using Vs1 = SubTypesT<vs1Start, sizeof...(Values), std::tuple<Values...>>;
using SecondHalfTuple = typename PairPV<Ps1, Vs1>::type;
public:
using type = decltype(std::tuple_cat(
std::declval<FirstHalfTuple>(),
std::declval<OooResultTuple>(),
std::declval<SecondHalfTuple>()
));
};
template <typename Tuple>
using AppResultForTuple = typename AppResultForTupleHelper<nbOooOrBinder, decltype(drop<0>(std::declval<Tuple>()))>::type;
template <typename RangeType>
using RangeTuple = std::conditional_t<nbOooOrBinder == 1, std::tuple<SubrangeT<RangeType>>, std::tuple<>>;
template <typename RangeType>
using AppResultForRangeType = decltype(std::tuple_cat(
std::declval<RangeTuple<RangeType>>(),
std::declval<typename PatternTraits<Patterns>::template AppResultTuple<decltype(*std::begin(std::declval<RangeType>()))>>()...
));
template <typename Value, typename = std::void_t<>>
class AppResultHelper;
template <typename Value>
class AppResultHelper<Value, std::enable_if_t<isTupleLikeV<Value>>> {
public:
using type = AppResultForTuple<Value>;
};
template <typename RangeType>
class AppResultHelper<RangeType, std::enable_if_t<!isTupleLikeV<RangeType> && isRangeV<RangeType>>> {
public:
using type = AppResultForRangeType<RangeType>;
};
template <typename Value>
using AppResultTuple = typename AppResultHelper<Value>::type;
constexpr static size_t nbIdV = (PatternTraits<Patterns>::nbIdV + ... + 0);
template <typename ValueTuple, typename ContextT>
constexpr static fn matchPatternImpl(ValueTuple&& valueTuple, const Ds<Patterns...>& dsPat, int32_t depth, ContextT& context)
-> bool
requires(isTupleLikeV<ValueTuple>)
{
if constexpr (nbOooOrBinder == 0) {
return std::apply(
[&valueTuple, depth, &context](const auto&... patterns) {
return apply_(
[depth, &context, &patterns...]<typename... T>(T&&... values) constexpr {
static_assert(sizeof...(patterns) == sizeof...(values));
return (matchPattern(std::forward<T>(values), patterns, depth + 1, context) && ...);
},
valueTuple
);
},
dsPat.patterns()
);
} else if constexpr (nbOooOrBinder == 1) {
constexpr size_t idxOoo = findOooIdx<typename Ds<Patterns...>::Type>();
constexpr bool isBinder = isOooBinderV<std::tuple_element_t<idxOoo, std::tuple<Patterns...>>>;
constexpr bool isArray = isArrayV<ValueTuple>;
auto result = matchPatternMultiple<0, 0, idxOoo>(std::forward<ValueTuple>(valueTuple), dsPat.patterns(), depth, context);
constexpr size_t valLen = std::tuple_size_v<std::decay_t<ValueTuple>>;
constexpr size_t patLen = sizeof...(Patterns);
if constexpr (isArray) {
if constexpr (isBinder) {
const size_t rangeSize = static_cast<long>(valLen - (patLen - 1));
context.emplace_back(makeSubrange(&valueTuple[idxOoo], &valueTuple[idxOoo] + rangeSize));
using type = decltype(makeSubrange(&valueTuple[idxOoo], &valueTuple[idxOoo] + rangeSize));
result = result &&
matchPattern(std::get<type>(context.back()), std::get<idxOoo>(dsPat.patterns()), depth, context);
}
} else {
static_assert(!isBinder);
}
return result && matchPatternMultiple<valLen - patLen + idxOoo + 1, idxOoo + 1, patLen - idxOoo - 1>(std::forward<ValueTuple>(valueTuple), dsPat.patterns(), depth, context);
}
return false;
}
template <typename ValueRange, typename ContextT>
constexpr static fn matchPatternImpl(ValueRange&& valueRange, const Ds<Patterns...>& dsPat, int32_t depth, ContextT& context)
-> bool
requires(!isTupleLikeV<ValueRange> && isRangeV<ValueRange>)
{
static_assert(nbOooOrBinder == 0 || nbOooOrBinder == 1);
constexpr size_t nbPat = sizeof...(Patterns);
if constexpr (nbOooOrBinder == 0) {
// size mismatch for dynamic array is not an error;
if (std::forward<ValueRange>(valueRange).size() != nbPat)
return false;
return matchPatternRange<0, nbPat>(std::begin(std::forward<ValueRange>(valueRange)), dsPat.patterns(), depth, context);
} else if constexpr (nbOooOrBinder == 1) {
if (std::forward<ValueRange>(valueRange).size() < nbPat - 1)
return false;
constexpr auto idxOoo = findOooIdx<typename Ds<Patterns...>::Type>();
constexpr auto isBinder = isOooBinderV<std::tuple_element_t<idxOoo, std::tuple<Patterns...>>>;
auto result = matchPatternRange<0, idxOoo>(std::begin(std::forward<ValueRange>(valueRange)), dsPat.patterns(), depth, context);
const size_t valLen = std::forward<ValueRange>(valueRange).size();
constexpr size_t patLen = sizeof...(Patterns);
const auto beginOoo = std::next(std::begin(std::forward<ValueRange>(valueRange)), idxOoo);
if constexpr (isBinder) {
const size_t rangeSize = static_cast<long>(valLen - (patLen - 1));
const auto end = std::next(beginOoo, rangeSize);
context.emplace_back(makeSubrange(beginOoo, end));
using type = decltype(makeSubrange(beginOoo, end));
result = result &&
matchPattern(std::get<type>(context.back()), std::get<idxOoo>(dsPat.patterns()), depth, context);
}
const auto beginAfterOoo = std::next(beginOoo, static_cast<long>(valLen - patLen + 1));
return result &&
matchPatternRange<idxOoo + 1, patLen - idxOoo - 1>(beginAfterOoo, dsPat.patterns(), depth, context);
}
return false;
}
constexpr static fn processIdImpl(const Ds<Patterns...>& dsPat, int32_t depth, IdProcess idProcess) -> void {
return std::apply(
[depth, idProcess](auto&&... patterns) { return (processId(patterns, depth, idProcess), ...); },
dsPat.patterns()
);
}
};
static_assert(std::is_same_v<PatternTraits<Ds<OooBinder<SubrangeT<const std::array<int32_t, 2>>>>>::AppResultTuple<const std::array<int32_t, 2>>, std::tuple<matchit::impl::Subrange<const int32_t*>>>);
static_assert(std::is_same_v<PatternTraits<Ds<OooBinder<Subrange<int32_t*>>, matchit::impl::Id<int32_t>>>::AppResultTuple<const std::array<int32_t, 3>>, std::tuple<matchit::impl::Subrange<const int32_t*>>>);
static_assert(std::is_same_v<PatternTraits<Ds<OooBinder<Subrange<int32_t*>>, matchit::impl::Id<int32_t>>>::AppResultTuple<std::array<int32_t, 3>>, std::tuple<matchit::impl::Subrange<int32_t*>>>);
template <typename Pattern, typename Pred>
class PostCheck {
public:
constexpr explicit PostCheck(const Pattern& pattern, const Pred& pred)
: mPattern { pattern }, mPred { pred } {}
[[nodiscard]] constexpr fn check() const -> bool {
return mPred();
}
constexpr fn pattern() const -> const Pattern& {
return mPattern;
}
private:
Pattern mPattern;
Pred mPred;
};
template <typename Pattern, typename Pred>
class PatternTraits<PostCheck<Pattern, Pred>> {
public:
template <typename Value>
using AppResultTuple = typename PatternTraits<Pattern>::template AppResultTuple<Value>;
template <typename Value, typename ContextT>
constexpr static fn matchPatternImpl(
Value&& value,
const PostCheck<Pattern, Pred>& postCheck,
const int32_t depth,
ContextT& context
) -> bool {
return matchPattern(std::forward<Value>(value), postCheck.pattern(), depth + 1, context) && postCheck.check();
}
constexpr static fn processIdImpl(const PostCheck<Pattern, Pred>& postCheck, int32_t depth, IdProcess idProcess) -> void {
processId(postCheck.pattern(), depth, idProcess);
}
};
static_assert(std::is_same_v<PatternTraits<Wildcard>::AppResultTuple<int32_t>, std::tuple<>>);
static_assert(std::is_same_v<PatternTraits<int32_t>::AppResultTuple<int32_t>, std::tuple<>>);
constexpr fn x = [](auto&& t) { return t; };
static_assert(std::is_same_v<PatternTraits<App<decltype(x), Wildcard>>::AppResultTuple<std::array<int32_t, 3>>, std::tuple<std::array<int32_t, 3>>>);
static_assert(PatternTraits<And<App<decltype(x), Wildcard>>>::nbIdV == 0);
static_assert(PatternTraits<And<App<decltype(x), Id<int32_t>>>>::nbIdV == 1);
static_assert(PatternTraits<And<Id<int32_t>, Id<float>>>::nbIdV == 2);
static_assert(PatternTraits<Or<Id<int32_t>, Id<float>>>::nbIdV == 2);
static_assert(PatternTraits<Or<Wildcard, float>>::nbIdV == 0);
template <typename Value, typename... PatternPairs>
constexpr fn matchPatterns(Value&& value, const PatternPairs&... patterns) {
using RetType = typename PatternPairsRetType<PatternPairs...>::RetType;
using TypeTuple = decltype(std::tuple_cat(
std::declval<typename PatternTraits<typename PatternPairs::PatternT>::template AppResultTuple<Value>>()...
));
// expression, has return value.
if constexpr (!std::is_same_v<RetType, void>) {
constexpr fn func = [](const auto& pattern, auto&& val, RetType& result) constexpr -> bool {
if (auto context = typename ContextTrait<TypeTuple>::ContextT {};
pattern.matchValue(std::forward<Value>(val), context)) {
result = pattern.execute();
processId(pattern, 0, IdProcess::kCANCEL);
return true;
}
return false;
};
RetType result {};
const bool matched = (func(patterns, std::forward<Value>(value), result) || ...);
if (!matched)
throw std::logic_error { "Error: no patterns got matched!" };
static_cast<void>(matched);
return result;
} else {
const fn func = [](const auto& pattern, auto&& val) -> bool {
if (auto context = typename ContextTrait<TypeTuple>::ContextT {};
pattern.matchValue(std::forward<Value>(val), context)) {
pattern.execute();
processId(pattern, 0, IdProcess::kCANCEL);
return true;
}
return false;
};
const bool matched = (func(patterns, std::forward<Value>(value)) || ...);
static_cast<void>(matched);
}
}
template <typename T>
constexpr fn cast = [](auto&& input) { return static_cast<T>(input); };
constexpr fn deref = [](auto&& x) -> decltype(*x)& { return *x; };
constexpr fn some = [](const auto pat) { return and_(app(cast<bool>, true), app(deref, pat)); };
constexpr fn none = app(cast<bool>, false);
template <typename Value, typename Variant, typename = std::void_t<>>
struct ViaGetIf : std::false_type {};
using std::get_if;
template <typename T, typename Variant>
struct ViaGetIf<T, Variant, std::void_t<decltype(get_if<T>(std::declval<const Variant*>()))>> : std::true_type {};
template <typename T, typename Variant>
constexpr bool viaGetIfV = ViaGetIf<T, Variant>::value;
static_assert(viaGetIfV<int, std::variant<int, bool>>);
template <typename T>
class AsPointer {
static_assert(!std::is_reference_v<T>);
public:
template <typename Variant>
constexpr fn operator()(Variant&& v) const
requires(viaGetIfV<T, std::decay_t<Variant>>)
{
return get_if<T>(std::addressof(std::forward<Variant>(v)));
}
// template to disable implicit cast to std::any
template <typename A>
constexpr fn operator()(A&& a) const
requires(std::is_same_v<std::decay_t<A>, std::any>)
{
return std::any_cast<T>(std::addressof(std::forward<A>(a)));
}
// cast to base class
template <typename D>
constexpr fn operator()(const D& d) const->decltype(static_cast<const T*>(std::addressof(d)))
requires(!viaGetIfV<T, D> && std::is_base_of_v<T, D>)
{
return static_cast<const T*>(std::addressof(d));
}
// No way to handle rvalue to save copy in this class. Need to define some in another way to handle this.
// cast to base class
template <typename D>
constexpr fn operator()(D& d) const->decltype(static_cast<T*>(std::addressof(d)))
requires(!viaGetIfV<T, D> && std::is_base_of_v<T, D>)
{
return static_cast<T*>(std::addressof(d));
}
// cast to derived class
template <typename B>
constexpr fn operator()(const B& b) const->decltype(dynamic_cast<const T*>(std::addressof(b)))
requires(!viaGetIfV<T, B> && std::is_base_of_v<B, T>)
{
return dynamic_cast<const T*>(std::addressof(b));
}
// cast to derived class
template <typename B>
constexpr fn operator()(B& b) const->decltype(dynamic_cast<T*>(std::addressof(b)))
requires(!viaGetIfV<T, B> && std::is_base_of_v<B, T>)
{
return dynamic_cast<T*>(std::addressof(b));
}
constexpr fn operator()(const T& b) const {
return std::addressof(b);
}
constexpr fn operator()(T& b) const {
return std::addressof(b);
}
};
static_assert(std::is_invocable_v<AsPointer<int>, int>);
static_assert(std::is_invocable_v<AsPointer<std::tuple<int>>, std::tuple<int>>);
template <typename T>
constexpr AsPointer<T> asPointer;
template <typename T>
constexpr fn as = [](const auto pat) { return app(asPointer<T>, some(pat)); };
template <typename Value, typename Pattern>
constexpr fn matched(Value&& v, Pattern&& p) {
return match(std::forward<Value>(v))(
is | std::forward<Pattern>(p) = [] { return true; }, is | _ = [] { return false; }
);
}
constexpr fn dsVia = [](auto... members) {
return [members...](auto... pats) { return and_(app(members, pats)...); };
};
template <typename T>
constexpr fn asDsVia = [](auto... members) { return [members...](auto... pats) { return as<T>(and_(app(members, pats)...)); }; };
constexpr fn in = [](const auto& first, const auto& last) {
return meet([=](auto&& v) { return first <= v && v <= last; });
};
} // namespace impl
using impl::_;
using impl::and_;
using impl::app;
using impl::as;
using impl::asDsVia;
using impl::ds;
using impl::dsVia;
using impl::expr;
using impl::Id;
using impl::in;
using impl::is;
using impl::match;
using impl::matched;
using impl::meet;
using impl::none;
using impl::not_;
using impl::ooo;
using impl::or_;
using impl::some;
using impl::Subrange;
using impl::SubrangeT;
using impl::when;
} // namespace matchit
// NOLINTEND(readability-identifier-*, cppcoreguidelines-special-member-functions)