diff --git a/.gitignore b/.gitignore index d44cf7e..26e109b 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,6 @@ config.toml draconis++ Makefile result -/include/ subprojects/* !subprojects/*.wrap subprojects/sqlite3.wrap diff --git a/include/matchit.h b/include/matchit.h new file mode 100644 index 0000000..76dcb58 --- /dev/null +++ b/include/matchit.h @@ -0,0 +1,1786 @@ +/* + * 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 { + // NOLINTBEGIN(*-identifier-naming) + namespace impl { + template + class ValueType { + public: + using ValueT = Value; + }; + + template + class ValueType { + public: + using ValueT = Value&&; + }; + + template + constexpr auto 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 auto operator()(const PatternPair&... 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(const T& t) { + return Nullary { t }; + } + + template + class Id; + template + constexpr auto expr(Id& id) { + return nullary([&] { return *id; }); + } + + template + constexpr auto expr(const T& 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(const T& v, const Args&...) { + return v; + } + }; + + template + class EvalTraits> { + public: + constexpr static decltype(auto) evalImpl(const Nullary& e) { return e(); } + }; + + // Only allowed in nullary + template + class EvalTraits> { + public: + constexpr static decltype(auto) evalImpl(const Id& 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(const Unary& e, const Arg& arg) { + return e(arg); + } + }; + + class Wildcard; + template <> + class EvalTraits { + public: + template + constexpr static decltype(auto) evalImpl(const Wildcard&, const Arg& arg) { + return arg; + } + }; + + template + constexpr decltype(auto) evaluate_(const T& t, const Args&... 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 + + #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(const I begin, const S end) : mBegin { begin }, mEnd { end } {} + + constexpr Subrange(const Subrange& other) : mBegin { other.begin() }, mEnd { other.end() } {} + + Subrange& operator=(const Subrange& 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> { + public: + using beginT = decltype(&*std::begin(std::declval&>())); + using endT = beginT; + }; + + template + using SubrangeT = + Subrange::beginT, typename IterUnderlyingType::endT>; + + template + bool operator==(const Subrange& lhs, const Subrange& rhs) { + using std::operator==; + return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); + } + + template + auto operator==(const std::pair& t, const std::pair& 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 std::remove_reference_t U; + + 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 : int32_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 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, 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 bool matchValue(Value&& value, ContextT& context) const { + return matchPattern(std::forward(value), mPattern, /*depth*/ 0, context); + } + constexpr auto execute() const { return mHandler(); } + + private: + const Pattern mPattern; + std::conditional_t, const Func&, const Func> 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(const Pattern& pattern) : mPattern { pattern } {} + template + constexpr auto operator=(Func&& func) { + auto f = toNullary(func); + return PatternPair { mPattern, f }; + } + template + constexpr auto operator|(const When& w) { + return PatternHelper>(PostCheck(mPattern, w.mPred)); + } + + private: + const Pattern mPattern; + }; + + template + class Ds; + + template + constexpr auto ds(const Patterns&... patterns) -> Ds; + + template + class OooBinder; + + class PatternPipable { + public: + template + // ReSharper disable once CppDFAUnreachableFunctionCall + constexpr auto operator|(const Pattern& p) const { + return PatternHelper { p }; + } + + template + constexpr auto operator|(const T* p) const { + return PatternHelper { p }; + } + + template + constexpr auto operator|(const OooBinder& p) const { + return operator|(ds(p)); + } + }; + + constexpr PatternPipable is {}; + + template + class PatternTraits { + public: + template + using AppResultTuple = std::tuple<>; + + constexpr static auto nbIdV = 0; + + template + constexpr static auto + matchPatternImpl(Value&& value, const Pattern& pattern, int32_t /* depth */, ContextT& /*context*/) { + return pattern == std::forward(value); + } + constexpr static void processIdImpl(const Pattern&, 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&&, const Pattern&, int32_t, ContextT&) { + return true; + } + constexpr static void processIdImpl(const Pattern&, int32_t /*depth*/, IdProcess) {} + }; + + template + class Or { + public: + constexpr explicit Or(const Patterns&... patterns) : mPatterns { patterns... } {} + constexpr const auto& patterns() const { return mPatterns; } + + private: + std::tuple...> mPatterns; + }; + + template + constexpr auto or_(const Patterns&... 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, const Or& orPat, int32_t depth, ContextT& context) { + constexpr auto 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 auto meet(const Pred& 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, const Meet& meetPat, int32_t /* depth */, ContextT&) { + return meetPat(std::forward(value)); + } + constexpr static void processIdImpl(const Meet&, int32_t /*depth*/, IdProcess) {} + }; + + template + class App { + public: + constexpr App(Unary&& unary, const Pattern& pattern) + : mUnary { std::forward(unary) }, mPattern { pattern } {} + constexpr const auto& unary() const { return mUnary; } + constexpr const auto& pattern() const { return mPattern; } + + private: + const Unary mUnary; + const InternalPatternT mPattern; + }; + + template + constexpr auto app(Unary&& unary, const Pattern& 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< + std::is_lvalue_reference_v> + #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>>() + )); + + constexpr static auto nbIdV = PatternTraits::nbIdV; + + template + constexpr static auto + matchPatternImpl(Value&& value, const App& appPat, const 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(const App& appPat, int32_t depth, IdProcess idProcess) { + return processId(appPat.pattern(), depth, idProcess); + } + }; + + template + class And { + public: + constexpr explicit And(const Patterns&... patterns) : mPatterns { patterns... } {} + constexpr const auto& patterns() const { return mPatterns; } + + private: + std::tuple...> mPatterns; + }; + + template + constexpr auto and_(const Patterns&... 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>()...)); + + constexpr static auto nbIdV = (PatternTraits::nbIdV + ... + 0); + + template + constexpr static auto + matchPatternImpl(Value&& value, const And& andPat, int32_t depth, ContextT& context) { + constexpr auto 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 } {} + const auto& pattern() const { return mPattern; } + + private: + InternalPatternT mPattern; + }; + + template + constexpr auto not_(const Pattern& 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, const Not& notPat, const int32_t depth, ContextT& context) { + 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< + Type, + Value, + std::void_t&>() = &std::declval())>> + : std::is_reference // need to double check this condition. to loosen it. + {}; + + 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 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(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: + constexpr auto hasValue() const { + return std::visit( + overload( + [](const Type&) { return true; }, + [](const Type*) { return true; }, + // [](Type *) + // { return true; }, + [](const std::monostate&) { return false; } + ), + IdBlockBase::mVariant + ); + } + constexpr decltype(auto) get() const { + return std::visit( + overload( + [](const Type& v) -> const 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: + constexpr auto hasValue() const { + return std::visit( + overload([](Type*) { return true; }, [](const std::monostate&) { 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( + [](const Type&) { return true; }, [](Type*) { return true; }, [](const std::monostate&) { 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(const Id& 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(const Ooo&) { 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, const Id& idPat, int32_t /* depth */, ContextT&) { + return idPat.matchValue(std::forward(value)); + } + constexpr static void processIdImpl(const Id& idPat, int32_t depth, const 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(const Patterns&... patterns) : mPatterns { patterns... } {} + constexpr const auto& patterns() const { return mPatterns; } + + using Type = std::tuple...>; + + private: + Type mPatterns; + }; + + template + constexpr auto ds(const Patterns&... patterns) -> Ds { + return Ds { patterns... }; + } + + template + class OooBinder { + Id mId; + + public: + explicit OooBinder(const Id& 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, const OooBinder& oooBinderPat, const int32_t depth, ContextT& context) { + return matchPattern(std::forward(value), oooBinderPat.binder(), depth + 1, context); + } + constexpr static void processIdImpl(const OooBinder& 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>> {}); + } + + static_assert(isOooOrBinderV); + static_assert(isOooOrBinderV>); + static_assert(findOooIdx, const char*>>() == 1); + static_assert(findOooIdx>() == 1); + + using std::get; + template < + std::size_t valueStartIdx, + std::size_t patternStartIdx, + std::size_t... I, + typename ValueTuple, + typename PatternTuple, + typename ContextT> + constexpr decltype(auto) + matchPatternMultipleImpl(ValueTuple&& valueTuple, PatternTuple&& patternTuple, const int32_t depth, ContextT& context, std::index_sequence) { + const auto 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 < + std::size_t valueStartIdx, + std::size_t patternStartIdx, + std::size_t size, + typename ValueTuple, + typename PatternTuple, + typename ContextT> + 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 < + std::size_t patternStartIdx, + std::size_t... I, + typename RangeBegin, + typename PatternTuple, + typename ContextT> + constexpr decltype(auto) + matchPatternRangeImpl(RangeBegin&& rangeBegin, PatternTuple&& patternTuple, const int32_t depth, ContextT& context, std::index_sequence) { + const auto func = [&](T&& 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 < + std::size_t patternStartIdx, + std::size_t size, + typename ValueRangeBegin, + typename PatternTuple, + typename ContextT> + 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::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, SubTypesT<0, 1, std::tuple>>); + static_assert(std::is_same_v, 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 + // ReSharper disable once CppUseTypeTraitAlias + 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< + Value, + std::void_t())), 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::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 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 = + std::conditional_t>>, std::tuple<>>; + 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(std::declval()))>::type; + + template + using RangeTuple = std::conditional_t>, std::tuple<>>; + + template + using AppResultForRangeType = decltype(std::tuple_cat( + std::declval>(), + std::declval< + typename PatternTraits::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 auto nbIdV = (PatternTraits::nbIdV + ... + 0); + + template + constexpr static auto + matchPatternImpl(ValueTuple&& valueTuple, const Ds& dsPat, int32_t depth, ContextT& context) + -> std::enable_if_t, bool> { + 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 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) { + const auto 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; + } + + template + constexpr static auto + matchPatternImpl(ValueRange&& valueRange, const Ds& 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); + const auto valLen = valueRange.size(); + constexpr auto patLen = sizeof...(Patterns); + const auto beginOoo = std::next(std::begin(valueRange), idxOoo); + if constexpr (isBinder) { + const auto 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; + } + + constexpr static void processIdImpl(const Ds& 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< + PatternTraits>>>>::AppResultTuple< + const std::array>, + std::tuple>>); + + static_assert(std::is_same_v< + PatternTraits>, matchit::impl::Id>>::AppResultTuple< + const std::array>, + std::tuple>>); + + static_assert(std::is_same_v< + PatternTraits>, matchit::impl::Id>>::AppResultTuple< + std::array>, + std::tuple>>); + + template + class PostCheck { + public: + constexpr explicit PostCheck(const Pattern& pattern, const Pred& pred) : mPattern { pattern }, mPred { pred } {} + constexpr bool check() const { return mPred(); } + constexpr const auto& pattern() const { return mPattern; } + + private: + const Pattern mPattern; + const Pred mPred; + }; + + template + class PatternTraits> { + public: + template + using AppResultTuple = typename PatternTraits::template AppResultTuple; + + template + constexpr static auto matchPatternImpl( + Value&& value, + const PostCheck& postCheck, + const int32_t depth, + ContextT& context + ) { + return matchPattern(std::forward(value), postCheck.pattern(), depth + 1, context) && postCheck.check(); + } + constexpr static void + processIdImpl(const PostCheck& postCheck, int32_t depth, IdProcess idProcess) { + processId(postCheck.pattern(), depth, idProcess); + } + }; + + static_assert(std::is_same_v::AppResultTuple, std::tuple<>>); + static_assert(std::is_same_v::AppResultTuple, std::tuple<>>); + constexpr auto x = [](auto&& t) { return t; }; + static_assert(std::is_same_v< + PatternTraits>::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, 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 auto 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, 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. + { + const auto 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, value) || ...); + static_cast(matched); + } + } + + } // namespace impl + + // export symbols + using impl::_; + using impl::and_; + using impl::app; + using impl::ds; + using impl::Id; + using impl::is; + using impl::meet; + using impl::not_; + using impl::ooo; + using impl::or_; + using impl::Subrange; + using impl::SubrangeT; + using impl::when; +} // namespace matchit + +#endif // MATCHIT_PATTERNS_H +#ifndef MATCHIT_UTILITY_H + #define MATCHIT_UTILITY_H + + #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 = [](const auto 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(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 >>* = nullptr> + constexpr auto operator()(Variant&& v) const { + return get_if(std::addressof(v)); + } + + // template to disable implicit cast to std::any + template , std::any>>* = nullptr> + constexpr auto operator()(A&& a) const { + return std::any_cast(std::addressof(a)); + } + + // cast to base class + template && std::is_base_of_v>* = nullptr> + constexpr auto operator()(const D& 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>* = 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>* = nullptr> + constexpr auto operator()(const B& b) const -> decltype(dynamic_cast(std::addressof(b))) { + return dynamic_cast(std::addressof(b)); + } + + // cast to derived class + template && std::is_base_of_v>* = nullptr> + constexpr auto operator()(B& b) const -> decltype(dynamic_cast(std::addressof(b))) { + return dynamic_cast(std::addressof(b)); + } + + constexpr auto operator()(const T& 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 = [](const auto pat) { return app(asPointer, some(pat)); }; + + template + constexpr auto matched(Value&& v, Pattern&& p) { + return match(std::forward(v))( + is | std::forward(p) = [] { return true; }, is | _ = [] { 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) { 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; + // NOLINTEND(*-identifier-naming) +} // namespace matchit + +#endif // MATCHIT_UTILITY_H + +#endif // MATCHIT_H