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