/* * Copyright (c) 2021-2022 Bowen Fu * Distributed Under The Apache-2.0 License */ #ifndef MATCHIT_H #define MATCHIT_H #ifndef MATCHIT_CORE_H #define MATCHIT_CORE_H #include #include #include namespace matchit { namespace impl { template class ValueType { public: using ValueT = Value; }; template class ValueType { public: using ValueT = Value &&; }; template constexpr auto matchPatterns(Value &&value, Patterns const &...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 auto operator()(PatternPair const &...patterns) { return matchPatterns(std::forward(mValue), patterns...); } }; template constexpr auto match(Value &&value) { return MatchHelper{std::forward(value)}; } template constexpr auto match(First &&first, Values &&...values) { auto result = std::forward_as_tuple(std::forward(first), std::forward(values)...); return MatchHelper{ std::forward(result)}; } } // namespace impl // export symbols using impl::match; } // namespace matchit #endif // MATCHIT_CORE_H #ifndef MATCHIT_EXPRESSION_H #define MATCHIT_EXPRESSION_H #include namespace matchit { namespace impl { template class Nullary : public T { public: using T::operator(); }; template constexpr auto nullary(T const &t) { return Nullary{t}; } template class Id; template constexpr auto expr(Id &id) { return nullary([&] { return *id; }); } template constexpr auto expr(T const &v) { return nullary([&] { return v; }); } template constexpr auto toNullary(T &&v) { if constexpr (std::is_invocable_v>) { return v; } else { return expr(v); } } // for constant template class EvalTraits { public: template constexpr static decltype(auto) evalImpl(T const &v, Args const &...) { return v; } }; template class EvalTraits> { public: constexpr static decltype(auto) evalImpl(Nullary const &e) { return e(); } }; // Only allowed in nullary template class EvalTraits> { public: constexpr static decltype(auto) evalImpl(Id const &id) { return *const_cast &>(id); } }; template class Meet; // Unary is an alias of Meet. template using Unary = Meet; template class EvalTraits> { public: template constexpr static decltype(auto) evalImpl(Unary const &e, Arg const &arg) { return e(arg); } }; class Wildcard; template <> class EvalTraits { public: template constexpr static decltype(auto) evalImpl(Wildcard const &, Arg const &arg) { return arg; } }; template constexpr decltype(auto) evaluate_(T const &t, Args const &...args) { 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 auto isNullaryOrIdV = IsNullaryOrId>::value; #define UN_OP_FOR_NULLARY(op) \ template , bool> = true> \ constexpr auto operator op(T const &t) \ { \ return nullary([&] { return op evaluate_(t); }); \ } #define BIN_OP_FOR_NULLARY(op) \ template || isNullaryOrIdV, bool> = \ true> \ constexpr auto operator op(T const &t, U const &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 class IsUnaryOrWildcard : public std::false_type { }; template <> class IsUnaryOrWildcard : public std::true_type { }; template class IsUnaryOrWildcard> : public std::true_type { }; template constexpr auto isUnaryOrWildcardV = IsUnaryOrWildcard>::value; // unary is an alias of meet. template constexpr auto unary(T &&t) { return meet(std::forward(t)); } #define UN_OP_FOR_UNARY(op) \ template , bool> = true> \ constexpr auto operator op(T const &t) \ { \ return unary([&](auto &&arg) constexpr { return op evaluate_(t, arg); }); \ } #define BIN_OP_FOR_UNARY(op) \ template || isUnaryOrWildcardV, \ bool> = true> \ constexpr auto operator op(T const &t, U const &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 } // namespace impl using impl::expr; } // namespace matchit #endif // MATCHIT_EXPRESSION_H #ifndef MATCHIT_PATTERNS_H #define MATCHIT_PATTERNS_H #include #include #include #include #include #include #include #if !defined(NO_SCALAR_REFERENCES_USED_IN_PATTERNS) #define NO_SCALAR_REFERENCES_USED_IN_PATTERNS 0 #endif // !defined(NO_SCALAR_REFERENCES_USED_IN_PATTERNS) namespace matchit { namespace impl { template class Subrange { I mBegin; S mEnd; public: constexpr Subrange(I const begin, S const end) : mBegin{begin}, mEnd{end} {} constexpr Subrange(Subrange const &other) : mBegin{other.begin()}, mEnd{other.end()} {} Subrange &operator=(Subrange const &other) { mBegin = other.begin(); mEnd = other.end(); return *this; } size_t size() const { return static_cast(std::distance(mBegin, mEnd)); } auto begin() const { return mBegin; } auto end() const { return mEnd; } }; template constexpr auto 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 const> { public: using beginT = decltype(&*std::begin(std::declval const &>())); using endT = beginT; }; template using SubrangeT = Subrange::beginT, typename IterUnderlyingType::endT>; template bool operator==(Subrange const &lhs, Subrange const &rhs) { using std::operator==; return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } template auto operator==(std::pair const &t, std::pair const &u) { return t.first == u.first && t.second == u.second; } template class WithinTypes { public: constexpr static auto value = (std::is_same_v || ...); }; template class PrependUnique; template class PrependUnique> { constexpr static auto 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 decltype(auto) subtupleImpl(Tuple &&t, std::index_sequence) { return std::forward_as_tuple(get(std::forward(t))...); } } // namespace detail // [start, end) template constexpr decltype(auto) subtuple(Tuple &&t) { constexpr auto 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 decltype(auto) drop(Tuple &&t) { constexpr auto tupleSize = std::tuple_size_v>; static_assert(start <= tupleSize); return subtuple(std::forward(t)); } template constexpr decltype(auto) take(Tuple &&t) { constexpr auto tupleSize = std::tuple_size_v>; static_assert(len <= tupleSize); return subtuple<0, len>(std::forward(t)); } template constexpr decltype(auto) apply_(F &&f, Tuple &&t) { return std::apply(std::forward(f), drop<0>(std::forward(t))); } // as constexpr template constexpr std::invoke_result_t invoke_(F &&f, Args &&...args) noexcept(std::is_nothrow_invocable_v) { return std::apply(std::forward(f), std::forward_as_tuple(std::forward(args)...)); } template struct decayArray { private: typedef typename std::remove_reference::type U; public: using type = typename std::conditional_t::value, typename std::remove_extent::type *, T>; }; template using decayArrayT = typename decayArray::type; static_assert(std::is_same_v, int32_t *>); static_assert(std::is_same_v, int32_t const *>); static_assert(std::is_same_v, int32_t const &>); template struct AddConstToPointer { using type = std::conditional_t< !std::is_pointer_v, T, std::add_pointer_t>>>; }; template using AddConstToPointerT = typename AddConstToPointer::type; static_assert(std::is_same_v, void const *>); 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 : int32_t { kCANCEL, kCONFIRM }; template constexpr void processId(Pattern const &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 void emplace_back(T &&t) { mMemHolder[mSize] = std::forward(t); ++mSize; } constexpr auto back() -> ElementT & { return mMemHolder[mSize - 1]; } }; template <> class Context<> { }; template class ContextTrait; template class ContextTrait> { public: using ContextT = Context; }; template constexpr auto matchPattern(Value &&value, Pattern const &pattern, int32_t depth, ConctextT &context) { auto const result = PatternTraits::matchPatternImpl( std::forward(value), pattern, depth, context); auto const 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(Pattern const &pattern, Func const &func) : mPattern{pattern}, mHandler{func} {} template constexpr bool matchValue(Value &&value, ContextT &context) const { return matchPattern(std::forward(value), mPattern, /*depth*/ 0, context); } constexpr auto execute() const { return mHandler(); } private: Pattern const mPattern; std::conditional_t, Func const &, Func const> mHandler; }; template class PostCheck; template class When { public: Pred mPred; }; template constexpr auto when(Pred &&pred) { auto p = toNullary(pred); return When{p}; } template class PatternHelper { public: constexpr explicit PatternHelper(Pattern const &pattern) : mPattern{pattern} {} template constexpr auto operator=(Func &&func) { auto f = toNullary(func); return PatternPair{mPattern, f}; } template constexpr auto operator|(When const &w) { return PatternHelper>( PostCheck(mPattern, w.mPred)); } private: Pattern const mPattern; }; template class Ds; template constexpr auto ds(Patterns const &...patterns) -> Ds; template class OooBinder; class PatternPipable { public: template constexpr auto operator|(Pattern const &p) const { return PatternHelper{p}; } template constexpr auto operator|(T const *p) const { return PatternHelper{p}; } template constexpr auto operator|(OooBinder const &p) const { return operator|(ds(p)); } }; constexpr PatternPipable pattern{}; template class PatternTraits { public: template using AppResultTuple = std::tuple<>; constexpr static auto nbIdV = 0; template constexpr static auto matchPatternImpl(Value &&value, Pattern const &pattern, int32_t /* depth */, ContextT & /*context*/) { return pattern == std::forward(value); } constexpr static void processIdImpl(Pattern const &, int32_t /*depth*/, IdProcess) {} }; class Wildcard { }; constexpr Wildcard _; template <> class PatternTraits { using Pattern = Wildcard; public: template using AppResultTuple = std::tuple<>; constexpr static auto nbIdV = 0; template constexpr static bool matchPatternImpl(Value &&, Pattern const &, int32_t, ContextT &) { return true; } constexpr static void processIdImpl(Pattern const &, int32_t /*depth*/, IdProcess) {} }; template class Or { public: constexpr explicit Or(Patterns const &...patterns) : mPatterns{patterns...} {} constexpr auto const &patterns() const { return mPatterns; } private: std::tuple...> mPatterns; }; template constexpr auto or_(Patterns const &...patterns) { return Or{patterns...}; } template class PatternTraits> { public: template using AppResultTuple = decltype(std::tuple_cat( typename PatternTraits::template AppResultTuple{}...)); constexpr static auto nbIdV = (PatternTraits::nbIdV + ... + 0); template constexpr static auto matchPatternImpl(Value &&value, Or const &orPat, int32_t depth, ContextT &context) { constexpr auto patSize = sizeof...(Patterns); return std::apply( [&value, depth, &context](auto const &...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(Or const &orPat, int32_t depth, IdProcess idProcess) { return std::apply( [depth, idProcess](Patterns const &...patterns) { return (processId(patterns, depth, idProcess), ...); }, orPat.patterns()); } }; template class Meet : public Pred { public: using Pred::operator(); }; template constexpr auto meet(Pred const &pred) { return Meet{pred}; } template class PatternTraits> { public: template using AppResultTuple = std::tuple<>; constexpr static auto nbIdV = 0; template constexpr static auto matchPatternImpl(Value &&value, Meet const &meetPat, int32_t /* depth */, ContextT &) { return meetPat(std::forward(value)); } constexpr static void processIdImpl(Meet const &, int32_t /*depth*/, IdProcess) {} }; template class App { public: constexpr App(Unary &&unary, Pattern const &pattern) : mUnary{std::forward(unary)}, mPattern{pattern} {} constexpr auto const &unary() const { return mUnary; } constexpr auto const &pattern() const { return mPattern; } private: Unary const mUnary; InternalPatternT const mPattern; }; template constexpr auto app(Unary &&unary, Pattern const &pattern) { return App{std::forward(unary), pattern}; } constexpr auto 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> #if NO_SCALAR_REFERENCES_USED_IN_PATTERNS || std::is_scalar_v> #endif // NO_SCALAR_REFERENCES_USED_IN_PATTERNS , std::tuple<>, std::tuple>>>; template using AppResultTuple = decltype(std::tuple_cat( std::declval>(), std::declval::template AppResultTuple< AppResult>>())); constexpr static auto nbIdV = PatternTraits::nbIdV; template constexpr static auto matchPatternImpl(Value &&value, App const &appPat, int32_t depth, ContextT &context) { if constexpr (std::is_same_v, std::tuple<>>) { return matchPattern( std::forward>(invoke_(appPat.unary(), value)), appPat.pattern(), depth + 1, context); } else { context.emplace_back(invoke_(appPat.unary(), value)); decltype(auto) result = get>>(context.back()); return matchPattern(std::forward>(result), appPat.pattern(), depth + 1, context); } } constexpr static void processIdImpl(App const &appPat, int32_t depth, IdProcess idProcess) { return processId(appPat.pattern(), depth, idProcess); } }; template class And { public: constexpr explicit And(Patterns const &...patterns) : mPatterns{patterns...} {} constexpr auto const &patterns() const { return mPatterns; } private: std::tuple...> mPatterns; }; template constexpr auto and_(Patterns const &...patterns) { return And{patterns...}; } template class NbIdInTuple; template class NbIdInTuple> { public: constexpr static auto nbIdV = (PatternTraits>::nbIdV + ... + 0); }; template class PatternTraits> { public: template using AppResultTuple = decltype(std::tuple_cat( std::declval::template AppResultTuple< Value>>()...)); constexpr static auto nbIdV = (PatternTraits::nbIdV + ... + 0); template constexpr static auto matchPatternImpl(Value &&value, And const &andPat, int32_t depth, ContextT &context) { constexpr auto patSize = sizeof...(Patterns); auto const exceptLast = std::apply( [&value, depth, &context](auto const &...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(And const &andPat, int32_t depth, IdProcess idProcess) { return std::apply( [depth, idProcess](Patterns const &...patterns) { return (processId(patterns, depth, idProcess), ...); }, andPat.patterns()); } }; template class Not { public: explicit Not(Pattern const &pattern) : mPattern{pattern} {} auto const &pattern() const { return mPattern; } private: InternalPatternT mPattern; }; template constexpr auto not_(Pattern const &pattern) { return Not{pattern}; } template class PatternTraits> { public: template using AppResultTuple = typename PatternTraits::template AppResultTuple; constexpr static auto nbIdV = PatternTraits::nbIdV; template constexpr static auto matchPatternImpl(Value &&value, Not const ¬Pat, int32_t depth, ContextT &context) { return !matchPattern(std::forward(value), notPat.pattern(), depth + 1, context); } constexpr static void processIdImpl(Not const ¬Pat, int32_t depth, IdProcess idProcess) { processId(notPat.pattern(), depth, idProcess); } }; template > struct StorePointer : std::false_type { }; template using ValueVariant = std::conditional_t, UniqVariant*>, std::conditional_t, UniqVariant, std::remove_reference_t *>, std::conditional_t>, UniqVariant*, std::remove_reference_t const *>, UniqVariant, std::remove_reference_t*, std::remove_reference_t const *> > >>; template struct StorePointer &>() = &std::declval())>> : std::is_reference // need to double check this condition. to loosen it. { }; static_assert(!StorePointer::value); // static_assert(StorePointer, std::tuple>::value); static_assert(StorePointer::value); static_assert(StorePointer::value); static_assert(StorePointer::value); static_assert(StorePointer const, std::tuple const &>::value); template class Overload : public Ts... { public: using Ts::operator()...; }; template constexpr auto overload(Ts &&...ts) { return Overload{ts...}; } template class OooBinder; class Ooo; template class IdTraits { public: constexpr static auto #if defined(__has_feature) #if __has_feature(address_sanitizer) __attribute__((no_sanitize_address)) #endif #endif equal(Type const &lhs, Type const &rhs) { return lhs == rhs; } }; template class IdBlockBase { int32_t mDepth; protected: ValueVariant mVariant; public: constexpr IdBlockBase() : mDepth{} , mVariant{} {} constexpr auto &variant() { return mVariant; } constexpr void reset(int32_t depth) { if (mDepth - depth >= 0) { mVariant = {}; mDepth = depth; } } constexpr void confirm(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: constexpr auto hasValue() const { return std::visit(overload([](Type const &) { return true; }, [](Type const *) { return true; }, // [](Type *) // { return true; }, [](std::monostate const &) { return false; }), IdBlockBase::mVariant); } constexpr decltype(auto) get() const { return std::visit( overload([](Type const &v) -> Type const & { return v; }, [](Type const *p) -> Type const & { return *p; }, [](Type *p) -> Type const & { return *p; }, [](std::monostate const &) -> Type const & { throw std::logic_error("invalid state!"); }), IdBlockBase::mVariant); } }; template class IdBlock : public IdBlock {}; template class IdBlock : public IdBlockBase { public: constexpr auto hasValue() const { return std::visit(overload([](Type *) { return true; }, [](std::monostate const &) { return false; }), IdBlockBase::mVariant); } constexpr decltype(auto) get() { 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: constexpr auto hasValue() const { return std::visit(overload([](Type const &) { return true; }, [](Type *) { return true; }, [](std::monostate const &) { return false; }), IdBlockBase::mVariant); } constexpr decltype(auto) get() { 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 auto bindValue(ValueVariant &v, Value &&value, std::false_type /* StorePointer */) { // for constexpr v = ValueVariant{std::forward(value)}; } template constexpr static auto bindValue(ValueVariant &v, Value &&value, std::true_type /* StorePointer */) { v = ValueVariant{&value}; } }; template class Id { private: using BlockT = IdBlock; using BlockVT = std::variant; BlockVT mBlock = BlockT{}; constexpr decltype(auto) internalValue() const { return block().get(); } public: constexpr Id() = default; constexpr Id(Id const &id) { mBlock = BlockVT{&id.block()}; } // non-const to inform users not to mark Id as const. template constexpr auto at(Pattern &&pattern) { return and_(pattern, *this); } // non-const to inform users not to mark Id as const. constexpr auto at(Ooo const &) { return OooBinder{*this}; } constexpr BlockT &block() const { return std::visit(overload([](BlockT &v) -> BlockT & { return v; }, [](BlockT *p) -> BlockT & { return *p; }), // constexpr does not allow mutable, we use const_cast // instead. Never declare Id as const. const_cast(mBlock)); } template constexpr auto matchValue(Value &&v) const { if (hasValue()) { return IdTraits>::equal(internalValue(), v); } IdUtil::bindValue(block().variant(), std::forward(v), StorePointer{}); return true; } constexpr void reset(int32_t depth) const { return block().reset(depth); } constexpr void confirm(int32_t depth) const { return block().confirm(depth); } constexpr bool hasValue() const { return block().hasValue(); } // non-const to inform users not to mark Id as const. constexpr decltype(auto) get() { return block().get(); } // non-const to inform users not to mark Id as const. constexpr decltype(auto) operator*() { return get(); } }; template class PatternTraits> { public: template using AppResultTuple = std::tuple<>; constexpr static auto nbIdV = true; template constexpr static auto matchPatternImpl(Value &&value, Id const &idPat, int32_t /* depth */, ContextT &) { return idPat.matchValue(std::forward(value)); } constexpr static void processIdImpl(Id const &idPat, int32_t depth, IdProcess idProcess) { switch (idProcess) { case IdProcess::kCANCEL: idPat.reset(depth); break; case IdProcess::kCONFIRM: idPat.confirm(depth); break; } } }; template class Ds { public: constexpr explicit Ds(Patterns const &...patterns) : mPatterns{patterns...} {} constexpr auto const &patterns() const { return mPatterns; } using Type = std::tuple...>; private: Type mPatterns; }; template constexpr auto ds(Patterns const &...patterns) -> Ds { return Ds{patterns...}; } template class OooBinder { Id mId; public: OooBinder(Id const &id) : mId{id} {} decltype(auto) binder() const { return mId; } }; class Ooo { public: template constexpr auto operator()(Id id) const { return OooBinder{id}; } }; constexpr Ooo ooo; template <> class PatternTraits { public: template using AppResultTuple = std::tuple<>; constexpr static auto nbIdV = false; template constexpr static auto matchPatternImpl(Value &&, Ooo, int32_t /*depth*/, ContextT &) { return true; } constexpr static void processIdImpl(Ooo, int32_t /*depth*/, IdProcess) {} }; template class PatternTraits> { public: template using AppResultTuple = typename PatternTraits::template AppResultTuple; constexpr static auto nbIdV = PatternTraits::nbIdV; template constexpr static auto matchPatternImpl(Value &&value, OooBinder const &oooBinderPat, int32_t depth, ContextT &context) { return matchPattern(std::forward(value), oooBinderPat.binder(), depth + 1, context); } constexpr static void processIdImpl(OooBinder const &oooBinderPat, int32_t depth, IdProcess idProcess) { 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 auto isOooBinderV = IsOooBinder>::value; template constexpr auto isOooOrBinderV = IsOoo>::value || isOooBinderV; template constexpr auto nbOooOrBinderV = ((isOooOrBinderV ? 1 : 0) + ... + 0); static_assert( nbOooOrBinderV == 2); template constexpr size_t findOooIdxImpl(std::index_sequence) { return ((isOooOrBinderV(std::declval()))> ? I : 0) + ...); } template constexpr size_t findOooIdx() { return findOooIdxImpl( std::make_index_sequence< std::tuple_size_v>>{}); } static_assert(isOooOrBinderV); static_assert(isOooOrBinderV>); static_assert( findOooIdx, const char *>>() == 1); static_assert(findOooIdx>() == 1); using std::get; template constexpr decltype(auto) matchPatternMultipleImpl(ValueTuple &&valueTuple, PatternTuple &&patternTuple, int32_t depth, ContextT &context, std::index_sequence) { auto const func = [&](auto &&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 decltype(auto) matchPatternMultiple(ValueTuple &&valueTuple, PatternTuple &&patternTuple, int32_t depth, ContextT &context) { return matchPatternMultipleImpl( std::forward(valueTuple), patternTuple, depth, context, std::make_index_sequence{}); } template constexpr decltype(auto) matchPatternRangeImpl(RangeBegin &&rangeBegin, PatternTuple &&patternTuple, int32_t depth, ContextT &context, std::index_sequence) { auto const func = [&](auto &&value, auto &&pattern) { return matchPattern(std::forward(value), pattern, depth + 1, context); }; static_cast(func); // Fix Me, avoid call next from begin every time. return (func(*std::next(rangeBegin, static_cast(I)), std::get(patternTuple)) && ...); } template constexpr decltype(auto) matchPatternRange(ValueRangeBegin &&valueRangeBegin, PatternTuple &&patternTuple, int32_t depth, ContextT &context) { return matchPatternRangeImpl( valueRangeBegin, patternTuple, depth, context, std::make_index_sequence{}); } template class IndexedTypes; template class IndexedTypes, Tuple> { public: using type = std::tuple< std::decay_t(std::declval()))>...>; }; template class SubTypes { constexpr static auto 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< std::tuple, SubTypesT<3, 4, std::tuple>>); static_assert( std::is_same_v< std::tuple, SubTypesT<0, 1, std::tuple>>); static_assert( std::is_same_v< std::tuple<>, SubTypesT<1, 1, std::tuple>>); static_assert( std::is_same_v< std::tuple, SubTypesT<2, 4, std::tuple>>); template class IsArray : public std::false_type { }; template class IsArray> : public std::true_type { }; template constexpr auto isArrayV = IsArray>::value; template > struct IsTupleLike : std::false_type { }; template struct IsTupleLike::value)>> : std::true_type { }; template constexpr auto 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 auto isRangeV = IsRange>::value; static_assert(!isRangeV>); static_assert(isRangeV>); template class PatternTraits> { constexpr static auto 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< typename PatternTraits::template AppResultTuple>()...)); }; template class AppResultForTupleHelper; template class AppResultForTupleHelper<0, std::tuple> { public: using type = decltype(std::tuple_cat( std::declval::template AppResultTuple< Values>>()...)); }; template class AppResultForTupleHelper<1, std::tuple> { constexpr static auto idxOoo = findOooIdx::Type>(); using Ps0 = SubTypesT<0, idxOoo, std::tuple>; using Vs0 = SubTypesT<0, idxOoo, std::tuple>; constexpr static auto 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 = typename std::conditional< isBinder, std::tuple>>, std::tuple<>>::type; using FirstHalfTuple = typename PairPV::type; using Ps1 = SubTypesT>; constexpr static auto 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< nbOooOrBinder, decltype(drop<0>(std::declval()))>::type; template using RangeTuple = std::conditional_t>, std::tuple<>>; template using AppResultForRangeType = decltype(std::tuple_cat( std::declval>(), std::declval::template AppResultTuple< decltype(*std::begin(std::declval()))>>()...)); 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 auto nbIdV = (PatternTraits::nbIdV + ... + 0); template constexpr static auto matchPatternImpl(ValueTuple &&valueTuple, Ds const &dsPat, int32_t depth, ContextT &context) -> std::enable_if_t, bool> { if constexpr (nbOooOrBinder == 0) { return std::apply( [&valueTuple, depth, &context](auto const &...patterns) { return apply_( [ depth, &context, &patterns... ](auto &&...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 auto idxOoo = findOooIdx::Type>(); constexpr auto isBinder = isOooBinderV>>; constexpr auto isArray = isArrayV; auto result = matchPatternMultiple<0, 0, idxOoo>( std::forward(valueTuple), dsPat.patterns(), depth, context); constexpr auto valLen = std::tuple_size_v>; constexpr auto patLen = sizeof...(Patterns); if constexpr (isArray) { if constexpr (isBinder) { auto const 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); } } template constexpr static auto matchPatternImpl(ValueRange &&valueRange, Ds const &dsPat, int32_t depth, ContextT &context) -> std::enable_if_t && isRangeV, bool> { static_assert(nbOooOrBinder == 0 || nbOooOrBinder == 1); constexpr auto nbPat = sizeof...(Patterns); if constexpr (nbOooOrBinder == 0) { // size mismatch for dynamic array is not an error; if (valueRange.size() != nbPat) { return false; } return matchPatternRange<0, nbPat>(std::begin(valueRange), dsPat.patterns(), depth, context); } else if constexpr (nbOooOrBinder == 1) { if (valueRange.size() < nbPat - 1) { return false; } constexpr auto idxOoo = findOooIdx::Type>(); constexpr auto isBinder = isOooBinderV>>; auto result = matchPatternRange<0, idxOoo>( std::begin(valueRange), dsPat.patterns(), depth, context); auto const valLen = valueRange.size(); constexpr auto patLen = sizeof...(Patterns); auto const beginOoo = std::next(std::begin(valueRange), idxOoo); if constexpr (isBinder) { auto const rangeSize = static_cast(valLen - (patLen - 1)); auto const 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); } auto const beginAfterOoo = std::next(beginOoo, static_cast(valLen - patLen + 1)); return result && matchPatternRange( beginAfterOoo, dsPat.patterns(), depth, context); } } constexpr static void processIdImpl(Ds const &dsPat, int32_t depth, IdProcess idProcess) { return std::apply( [depth, idProcess](auto &&...patterns) { return (processId(patterns, depth, idProcess), ...); }, dsPat.patterns()); } }; static_assert( std::is_same_v< typename PatternTraits< Ds>>>>:: AppResultTuple>, std::tuple>>); static_assert( std::is_same_v< typename PatternTraits>, matchit::impl::Id>>:: AppResultTuple>, std::tuple>>); static_assert( std::is_same_v< typename PatternTraits>, matchit::impl::Id>>:: AppResultTuple>, std::tuple>>); template class PostCheck { public: constexpr explicit PostCheck(Pattern const &pattern, Pred const &pred) : mPattern{pattern}, mPred{pred} {} constexpr bool check() const { return mPred(); } constexpr auto const &pattern() const { return mPattern; } private: Pattern const mPattern; Pred const mPred; }; template class PatternTraits> { public: template using AppResultTuple = typename PatternTraits::template AppResultTuple; template constexpr static auto matchPatternImpl(Value &&value, PostCheck const &postCheck, int32_t depth, ContextT &context) { return matchPattern(std::forward(value), postCheck.pattern(), depth + 1, context) && postCheck.check(); } constexpr static void processIdImpl(PostCheck const &postCheck, int32_t depth, IdProcess idProcess) { processId(postCheck.pattern(), depth, idProcess); } }; static_assert( std::is_same_v::template AppResultTuple, std::tuple<>>); static_assert( std::is_same_v::template AppResultTuple, std::tuple<>>); constexpr auto x = [](auto &&t) { return t; }; // static_assert(std::is_same_v>:: // template AppResultTuple, // std::tuple<>>); static_assert( std::is_same_v>:: template AppResultTuple>, std::tuple>>); // static_assert(std::is_same_v>>:: // template 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 auto matchPatterns(Value &&value, PatternPairs const &...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 auto const func = [](auto const &pattern, auto &&value, RetType &result) constexpr->bool { auto context = typename ContextTrait::ContextT{}; if (pattern.matchValue(std::forward(value), context)) { result = pattern.execute(); processId(pattern, 0, IdProcess::kCANCEL); return true; } return false; }; RetType result{}; bool const matched = (func(patterns, value, result) || ...); if (!matched) { throw std::logic_error{"Error: no patterns got matched!"}; } static_cast(matched); return result; } else // statement, no return value, mismatching all patterns is not an error. { auto const func = [](auto const &pattern, auto &&value) -> bool { auto context = typename ContextTrait::ContextT{}; if (pattern.matchValue(std::forward(value), context)) { pattern.execute(); processId(pattern, 0, IdProcess::kCANCEL); return true; } return false; }; bool const matched = (func(patterns, value) || ...); static_cast(matched); } } } // namespace impl // export symbols using impl::_; using impl::and_; using impl::app; using impl::ds; using impl::Id; using impl::meet; using impl::not_; using impl::ooo; using impl::or_; using impl::pattern; using impl::Subrange; using impl::SubrangeT; using impl::when; } // namespace matchit #endif // MATCHIT_PATTERNS_H #ifndef MATCHIT_UTILITY_H #define MATCHIT_UTILITY_H #include #include namespace matchit { namespace impl { template constexpr auto cast = [](auto &&input) { return static_cast(input); }; constexpr auto deref = [](auto &&x) -> decltype(*x) & { return *x; }; constexpr auto some = [](auto const pat) { return and_(app(cast, true), app(deref, pat)); }; constexpr auto none = app(cast, false); template > struct ViaGetIf : std::false_type { }; using std::get_if; template struct ViaGetIf< T, Variant, std::void_t(std::declval()))>> : std::true_type { }; template constexpr auto viaGetIfV = ViaGetIf::value; static_assert(viaGetIfV>); template class AsPointer { static_assert(!std::is_reference_v); public: template >>::type * = nullptr> constexpr auto operator()(Variant&& v) const { return get_if(std::addressof(v)); } // template to disable implicit cast to std::any template , std::any>::value>::type * = nullptr> constexpr auto operator()(A&& a) const { return std::any_cast(std::addressof(a)); } // cast to base class template && std::is_base_of_v>::type * = nullptr> constexpr auto operator()(D const& d) const -> decltype(static_cast(std::addressof(d))) { 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 && std::is_base_of_v>::type * = nullptr> constexpr auto operator()(D& d) const -> decltype(static_cast(std::addressof(d))) { return static_cast(std::addressof(d)); } // cast to derived class template && std::is_base_of_v>::type * = nullptr> constexpr auto operator()(B const& b) const -> decltype(dynamic_cast(std::addressof(b))) { return dynamic_cast(std::addressof(b)); } // cast to derived class template && std::is_base_of_v>::type * = nullptr> constexpr auto operator()(B& b) const -> decltype(dynamic_cast(std::addressof(b))) { return dynamic_cast(std::addressof(b)); } constexpr auto operator()(T const& b) const { return std::addressof(b); } constexpr auto 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 auto as = [](auto const pat) { return app(asPointer, some(pat)); }; template constexpr auto matched(Value &&v, Pattern &&p) { return match(std::forward(v))( pattern | std::forward(p) = [] { return true; }, pattern | _ = [] { return false; }); } constexpr auto dsVia = [](auto ...members) { return [members...](auto ...pats) { return and_(app(members, pats)...); }; }; template constexpr auto asDsVia = [](auto ...members) { return [members...](auto ...pats) { // FIXME, why the following line will cause segfault in at-Bindings.cpp // return as(dsVia(members...)(pats...)); return as(and_(app(members, pats)...)); }; }; } // namespace impl using impl::as; using impl::asDsVia; using impl::dsVia; using impl::matched; using impl::none; using impl::some; } // namespace matchit #endif // MATCHIT_UTILITY_H #endif // MATCHIT_H