From 644d1b63c209e0cee18519987ffe50fa6985621c Mon Sep 17 00:00:00 2001 From: sentomk Date: Fri, 28 Nov 2025 02:22:16 +0800 Subject: [PATCH 1/7] refactor: initialize --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 96bc9319..d49edab4 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ +REFACTOR +
**A header-only, zero-overhead, compile-time pattern matching library for modern C++.** From 4ce4fa7525b8aee5b8ffe9c6568a0a29a1581e66 Mon Sep 17 00:00:00 2001 From: sentomk Date: Fri, 28 Nov 2025 02:47:09 +0800 Subject: [PATCH 2/7] refactor(core): reorganize core layer into engine architecture Restructure the core pattern matching components from DSL-based layout to a more organized engine architecture. Changes include: - Replace match_builder with engine/builder and engine/match modules - Add forward declarations header (fwd.h) - Move implementation details to engine/detail subdirectory - Remove legacy dsl/case_expr and dsl/operators headers This improves code organization by separating builder and match functionality while maintaining a clearer separation between interface and implementation details. --- CMakeLists.txt | 12 +- .../ptn/core/detail/match_builder_detail.hpp | 260 ------------------ include/ptn/core/engine/builder.hpp | 36 +++ .../ptn/core/engine/detail/builder_impl.hpp | 95 +++++++ include/ptn/core/engine/detail/case_eval.hpp | 77 ++++++ include/ptn/core/engine/detail/match_impl.hpp | 83 ++++++ include/ptn/core/engine/match.hpp | 48 ++++ include/ptn/core/fwd.h | 50 ++++ include/ptn/core/match_builder.hpp | 52 ---- 9 files changed, 396 insertions(+), 317 deletions(-) delete mode 100644 include/ptn/core/detail/match_builder_detail.hpp create mode 100644 include/ptn/core/engine/builder.hpp create mode 100644 include/ptn/core/engine/detail/builder_impl.hpp create mode 100644 include/ptn/core/engine/detail/case_eval.hpp create mode 100644 include/ptn/core/engine/detail/match_impl.hpp create mode 100644 include/ptn/core/engine/match.hpp create mode 100644 include/ptn/core/fwd.h delete mode 100644 include/ptn/core/match_builder.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index aecf1d3a..868a5d60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,11 +77,13 @@ option(PTN_SKIP_COMPILER_CHECK "Skip compiler version check" OFF) PTN_JOIN_HEADERS(PTN_HEADERS include/ptn/config.hpp include/ptn/patternia.hpp - include/ptn/core/match_builder.hpp - include/ptn/core/detail/match_builder_detail.hpp - include/ptn/core/dsl/case_expr.hpp - include/ptn/core/dsl/detail/case_expr_detail.hpp - include/ptn/core/dsl/operators.hpp + + # Core Layer + include/ptn/core/fwd.h + include/ptn/core/engine/builder.hpp + include/ptn/core/engine/match.hpp + include/ptn/core/engine/detail/builder_impl.hpp + include/ptn/core/engine/detail/match_impl.hpp # Meta Utils include/ptn/meta/algorithms.hpp diff --git a/include/ptn/core/detail/match_builder_detail.hpp b/include/ptn/core/detail/match_builder_detail.hpp deleted file mode 100644 index a3cc038d..00000000 --- a/include/ptn/core/detail/match_builder_detail.hpp +++ /dev/null @@ -1,260 +0,0 @@ -#pragma once - -#include "ptn/config.hpp" -#include -#include -#include -#include -#include - -#if PTN_USE_CONCEPTS -#include -#endif - -/** - * @file match_builder_impl.hpp (detail) - * @brief Internal implementation of the core matching engine. - * - * This is an internal header containing the implementation of: - * - `match_builder`: Core engine storing subject and case list. - * - `run_handler()`: Handler invocation logic. - * - * Users should use the public `match()` function and `.when()/.otherwise()` API - * from `ptn/core/match_builder.hpp` instead of directly instantiating - * match_builder. - * - * Part of the Core Layer (namespace ptn::core::detail) - * - * @internal This is an internal implementation detail. - */ - -namespace ptn::dsl::detail { - template - struct case_expr; -} - -namespace ptn::core::detail { - -#if PTN_USE_CONCEPTS - - template - concept Invocable1 = std::is_invocable_v; - - template - concept Invocable0 = std::is_invocable_v; - - /** - * @brief Invoke handler `h(x)` if available. - * - * @internal This is an internal implementation detail. - */ - template - requires Invocable1 - constexpr decltype(auto) run_handler(H &h, X &x) { - return std::invoke(h, x); - } - - /** - * @brief Invoke handler `h()` if `(x)` overload is not available. - * - * @internal This is an internal implementation detail. - */ - template - requires(!Invocable1 && Invocable0) - constexpr decltype(auto) run_handler(H &h, X &) { - return std::invoke(h); - } - - /** - * @brief Fallback: return handler as value. - * - * @internal This is an internal implementation detail. - */ - template - requires(!Invocable1 && !Invocable0) - constexpr decltype(auto) run_handler(H &h, X &) { - return (h); - } - -#else // C++17 fallback - - template < - typename H, - typename X, - typename = std::enable_if_t>> - constexpr decltype(auto) run_handler(H &h, X &x) { - return std::invoke(h, x); - } - - template < - typename H, - typename X, - typename = void, - typename = std::enable_if_t< - !std::is_invocable_v && std::is_invocable_v>> - constexpr decltype(auto) run_handler(H &h, X &) { - return std::invoke(h); - } - - template < - typename H, - typename X, - typename = void, - typename = void, - typename = std::enable_if_t< - !std::is_invocable_v && !std::is_invocable_v>> - constexpr decltype(auto) run_handler(H &h, X &) { - return (h); - } - -#endif - - /** - * @brief Tag used to disambiguate private constructors. - * - * @internal This is an internal implementation detail. - */ - struct ctor_tag {}; - - /** - * @brief Core engine storing subject and case list, evaluated via - * `.otherwise()`. - * - * @tparam TV Type of stored subject value. - * @tparam Cases Variadic pack of `(Pattern, Handler)` pairs. - * - * This class is immutable: each `.when()` returns a new type with the added - * case. - * - * @internal This is an internal implementation detail. Use `match()` - * function. - */ - template - class match_builder { - TV value_; ///< Subject value - std::tuple cases_; ///< Stored (Pattern, Handler) pairs - using ctor_tag_t = ptn::core::detail::ctor_tag; - - template - friend struct ptn::dsl::detail::case_expr; - - template - friend class match_builder; - - /** - * @brief Private constructor used by `create()`. - */ - template -#if PTN_USE_CONCEPTS - requires std::constructible_from && - std::constructible_from, Tuple> -#endif - explicit constexpr match_builder(TV2 &&v, Tuple &&cs, ctor_tag_t) - : value_(std::forward(v)), cases_(std::forward(cs)) { - } - - // Append a case (lvalue) - template - constexpr auto with(Pattern p, Handler h) & { - using pair_t = std::pair; - auto new_cases = std::tuple_cat( - cases_, std::make_tuple(pair_t{std::move(p), std::move(h)})); - - return match_builder( - value_, std::move(new_cases), ctor_tag_t{}); - } - - // Append a case (rvalue) - template - constexpr auto with(Pattern p, Handler h) && { - using pair_t = std::pair; - auto new_cases = std::tuple_cat( - std::move(cases_), - std::make_tuple(pair_t{std::move(p), std::move(h)})); - - return match_builder( - std::move(value_), std::move(new_cases), ctor_tag_t{}); - } - - // Recursive case matching - /** - * @brief Try each case in order. - * - * Stops when a pattern matches or all cases exhausted. - * - * @tparam I Current tuple index. - * @tparam OutT Output type inferred from handlers. - */ - template - constexpr void try_cases(OutT &out, bool &done) { - if constexpr (I < sizeof...(Cases)) { - auto &c = std::get(cases_); - auto &[p, handler] = c; - - // Unified Pattern Layer API: Pattern must implement match(value) - if (!done && p.match(value_)) { - out = static_cast(run_handler(handler, value_)); - done = true; - } - else { - try_cases(out, done); - } - } - } - - public: - // Builder creation (called from ptn::match()) - template -#if PTN_USE_CONCEPTS - requires std::constructible_from, Tuple> -#endif - static constexpr auto create(VArg &&v, Tuple &&cs) - -> match_builder, Cases...> { - using result_t = match_builder, Cases...>; - return result_t( - std::forward(v), std::forward(cs), ctor_tag{}); - } - - // otherwise() - /** - * @brief Finalize matching. - * - * Computes a common return type from all handlers, applies the first - * matching handler, or the fallback if none match. - */ - template - constexpr auto otherwise(H &&fallback) && { - using R = std::common_type_t< - decltype(run_handler( - std::declval(), - std::declval()))..., - decltype(run_handler( - std::declval &>(), std::declval()))>; - - R out{}; - bool done = false; - - try_cases(out, done); - - if (!done) - out = static_cast(run_handler(fallback, value_)); - - return out; - } - - // when() - /** - * @brief Add a pattern-handler case from a `case_expr`. - */ - template - constexpr auto when(ptn::dsl::detail::case_expr &&e) & { - return this->with(std::move(e.pattern), std::move(e.handler)); - } - - template - constexpr auto when(ptn::dsl::detail::case_expr &&e) && { - return std::move(*this).with(std::move(e.pattern), std::move(e.handler)); - } - }; - -} // namespace ptn::core::detail diff --git a/include/ptn/core/engine/builder.hpp b/include/ptn/core/engine/builder.hpp new file mode 100644 index 00000000..3b5865a6 --- /dev/null +++ b/include/ptn/core/engine/builder.hpp @@ -0,0 +1,36 @@ +#pragma once + +/** + * @file builder.hpp + * @brief Public-facing builder type for the Core matching engine. + * + * Internally, it is an alias to `ptn::core::detail::match_builder`. + */ + +#include "ptn/config.hpp" + +// Pull in the implementation +#include "ptn/core/engine/detail/builder_impl.hpp" + +namespace ptn::core { + + namespace detail { + + /** + * @brief Forward declaration of the concrete builder implementation. + * + * TV : subject type + * Cases : sequence of case_expr-like types + */ + template + class match_builder; + + } // namespace detail + + /** + * @brief Public alias forwarding to the actual implementation. + */ + template + using match_builder = detail::match_builder; + +} // namespace ptn::core diff --git a/include/ptn/core/engine/detail/builder_impl.hpp b/include/ptn/core/engine/detail/builder_impl.hpp new file mode 100644 index 00000000..cd7241df --- /dev/null +++ b/include/ptn/core/engine/detail/builder_impl.hpp @@ -0,0 +1,95 @@ +#pragma once +/** + * @file builder_impl.hpp + * @brief Implementation of the Core match_builder. + */ + +#include +#include +#include + +#include "ptn/config.hpp" +#include "ptn/core/engine/detail/match_impl.hpp" + +namespace ptn::core::detail { + + template + class match_builder { + public: + using subject_type = TV; + using cases_type = std::tuple; + + private: + subject_type subject_; + cases_type cases_; + + public: + /** @brief Static factory with an empty case list. */ + static constexpr auto create(subject_type subject) { + return match_builder{std::move(subject), cases_type{}}; + } + + /** @brief Static factory with an explicit case tuple (internal use). */ + static constexpr auto create(subject_type subject, cases_type cases) { + return match_builder{std::move(subject), std::move(cases)}; + } + + /** @brief Construct from subject and case tuple. */ + constexpr match_builder(subject_type subject, cases_type cases) + : subject_(std::move(subject)), cases_(std::move(cases)) { + } + + // ----------------- chaining API: .when(...) ----------------- + + /** + * @brief Add a new case (rvalue-qualified). + * + * Accepts any CaseExpr-like object; typically `dsl::case_expr`. + */ + template + constexpr auto when(CaseExpr &&expr) && { + using new_case_type = std::decay_t; + + auto new_cases = std::tuple_cat( + std::move(cases_), + std::tuple(std::forward(expr))); + + using builder_t = match_builder; + + return builder_t{std::move(subject_), std::move(new_cases)}; + } + + /** + * @brief Add a new case (lvalue-qualified). + * + * This overload allows code like: + * auto b = match(x); + * auto b2 = b.when(...); + */ + template + constexpr auto when(CaseExpr &&expr) const & { + using new_case_type = std::decay_t; + + auto new_cases = std::tuple_cat( + cases_, std::tuple(std::forward(expr))); + + using builder_t = match_builder; + + return builder_t{subject_, std::move(new_cases)}; + } + + // ----------------- terminal API: .otherwise(...) ------------ + + /** + * @brief Terminal step: evaluate all cases; if none matches, + * call the provided fallback handler. + */ + template + constexpr decltype(auto) otherwise(Otherwise &&otherwise_handler) && { + return match_impl::eval( + subject_, cases_, std::forward(otherwise_handler)); + } + }; + +} // namespace ptn::core::detail diff --git a/include/ptn/core/engine/detail/case_eval.hpp b/include/ptn/core/engine/detail/case_eval.hpp new file mode 100644 index 00000000..3604aed2 --- /dev/null +++ b/include/ptn/core/engine/detail/case_eval.hpp @@ -0,0 +1,77 @@ +#pragma once +/** + * @file case_eval.hpp + * @brief Low-level evaluation of a single case (pattern + handler). + */ + +#include +#include +#include + +#include "ptn/config.hpp" + +namespace ptn::core::detail { + + // Helper for static_assert that depends on a template parameter + template + struct dependent_false : std::false_type {}; + + template + struct case_eval { + using case_type = Case; + using subject_type = Subject; + + // Use ADL-friendly pattern type deduction if needed + using pattern_type = decltype(std::declval().pattern); + using handler_type = decltype(std::declval().handler); + + /** + * @brief Check whether this case matches the subject. + * + * Default policy: + * - call `pattern(subject)` via std::invoke + */ + static constexpr bool + matches(const case_type &c, const subject_type &subject) { + if constexpr (std::is_invocable_r_v) { + return std::invoke(c.pattern, subject); + } + else if constexpr (std::is_invocable_v) { + // Allow non-bool results, but treat them as "truthy" + return static_cast(std::invoke(c.pattern, subject)); + } + else { + static_assert( + dependent_false::value, + "[case_eval]: Pattern must be invocable as " + "bool(pattern(subject))."); + } + } + + /** + * @brief Invoke the handler associated with this case. + * + * Handler policy: + * - Prefer handler(subject) if available + * - Otherwise, call handler() + */ + template + static constexpr decltype(auto) invoke(const case_type &c, S &&subject) { + using handler_t = handler_type; + + if constexpr (std::is_invocable_v) { + return std::invoke(c.handler, std::forward(subject)); + } + else if constexpr (std::is_invocable_v) { + return std::invoke(c.handler); + } + else { + static_assert( + dependent_false::value, + "[case_eval]: Handler must be invocable either as handler(subject) " + "or handler()."); + } + } + }; + +} // namespace ptn::core::detail diff --git a/include/ptn/core/engine/detail/match_impl.hpp b/include/ptn/core/engine/detail/match_impl.hpp new file mode 100644 index 00000000..f9e91be6 --- /dev/null +++ b/include/ptn/core/engine/detail/match_impl.hpp @@ -0,0 +1,83 @@ +#pragma once +/** + * @file match_impl.hpp + * @brief Core evaluation logic for a sequence of cases. + */ + +#include +#include +#include +#include + +#include "ptn/config.hpp" +#include "ptn/core/engine/detail/case_eval.hpp" + +namespace ptn::core::detail { + + struct match_impl { + /** + * @brief Evaluate the tuple of cases against a subject. + * + * TV : subject type + * CasesTuple : std::tuple + * Otherwise : fallback handler + */ + template + static constexpr decltype(auto) + eval(TV &subject, CasesTuple &cases, Otherwise &&otherwise_handler) { + using tuple_type = std::remove_reference_t; + constexpr std::size_t N = std::tuple_size_v; + + return eval_chain<0>( + subject, + cases, + std::forward(otherwise_handler), + std::integral_constant{}); + } + + private: + // Recursive chain over tuple indices [0, N) + template < + std::size_t I, + typename TV, + typename CasesTuple, + typename Otherwise> + static constexpr decltype(auto) eval_chain( + TV &subject, + CasesTuple &cases, + Otherwise &&otherwise_handler, + std::integral_constant) { + using tuple_type = std::remove_reference_t; + constexpr std::size_t N = std::tuple_size_v; + + if constexpr (I >= N) { + // No case matched; fall back to "otherwise" + if constexpr (std::is_invocable_v) { + return std::forward(otherwise_handler)(subject); + } + else { + return std::forward(otherwise_handler)(); + } + } + else { + auto &c = std::get(cases); + using case_type = std::remove_reference_t; + using eval_t = case_eval; + + if (eval_t::matches(c, subject)) { + // First matching case — invoke its handler + return eval_t::invoke(c, subject); + } + else { + // Try next + return eval_chain( + subject, + cases, + std::forward(otherwise_handler), + std::integral_constant{}); + } + } + } + }; + +} // namespace ptn::core::detail diff --git a/include/ptn/core/engine/match.hpp b/include/ptn/core/engine/match.hpp new file mode 100644 index 00000000..1b77bd99 --- /dev/null +++ b/include/ptn/core/engine/match.hpp @@ -0,0 +1,48 @@ +#pragma once + +/** + * @file match.hpp + * @brief Entry function for the Core matching engine. + */ + +#include + +#include "ptn/core/fwd.h" +#include "ptn/config.hpp" + +namespace ptn { + + /** + * @brief Primary entry for pattern matching. + * + * Subject type is deduced as std::decay_t. + */ + template + constexpr auto match(T &&value) { + using V = std::decay_t; + // Forward into the builder with an empty case list + return core::match_builder::create(V(std::forward(value))); + } + + /** + * @brief Explicit-typed entry for pattern matching: match(value) + * This overload is SFINAE-disabled if U and T are the same after decay, + * to avoid ambiguous calls with the primary overload. + */ + template + constexpr auto match(T &&value) -> std::enable_if_t< + !std::is_same_v, std::decay_t>, + core::match_builder> { + constexpr bool subject_constructible = + std::is_constructible_v || std::is_constructible_v; + + static_assert( + subject_constructible, + "[ptn::match(value)]: explicit subject type U cannot be " + "constructed from the given value."); + + U subject = static_cast(std::forward(value)); + return core::match_builder::create(std::move(subject)); + } + +} // namespace ptn \ No newline at end of file diff --git a/include/ptn/core/fwd.h b/include/ptn/core/fwd.h new file mode 100644 index 00000000..a8583c5f --- /dev/null +++ b/include/ptn/core/fwd.h @@ -0,0 +1,50 @@ +#pragma once +/** + * @file fwd.hpp + * @brief Forward declarations for Patternia Core Layer. + */ + +namespace ptn { + /* Entry function: match() */ + template + constexpr auto match(T &&); + + template + constexpr auto match(T &&); + + /* Core builder type (public-facing alias) */ + namespace core { + + /** + * @brief Public-facing builder template. + * + * Actual implementation is in namespace ptn::core::detail::match_builder. + */ + template + class match_builder; // only forward declared here + + } // namespace core + + /* Core internals (detail) */ + namespace core::detail { + + /** + * @brief Internal builder implementation. + */ + template + class match_builder; + + /** + * @brief Single-case evaluation (pattern + handler). + */ + template + struct case_eval; + + /** + * @brief Full evaluation logic for the match builder. + */ + struct match_impl; + + } // namespace core::detail + +} // namespace ptn \ No newline at end of file diff --git a/include/ptn/core/match_builder.hpp b/include/ptn/core/match_builder.hpp deleted file mode 100644 index 7338b933..00000000 --- a/include/ptn/core/match_builder.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "ptn/config.hpp" -#include "ptn/core/detail/match_builder_detail.hpp" - -/** - * @file match_builder.hpp - * @brief Public API for the Core Layer matching engine. - * - * This header provides the public interface to Patternia's *Core Layer*. - * The actual implementation is in `ptn/core/detail/match_builder_impl.hpp`. - * - * `match_builder` owns: - * - a subject value - * - a compile-time tuple of (Pattern, Handler) pairs - * - * The builder supports: - * - `.when(Pattern >> Handler)` to append a new case - * - `.otherwise(handler)` to perform final evaluation - * - * @ingroup core - */ - -namespace ptn { - - /** - * @brief Forward declaration of free-function `match()`. - */ - template - constexpr auto - match(T &&) noexcept(std::is_nothrow_constructible_v, T &&>); - -} // namespace ptn - -namespace ptn::core { - - // Forward declare the detail namespace - namespace detail { - template - class match_builder; - } - - /** - * @brief Public type alias forwarding to the detail implementation. - * - * Users see `ptn::core::match_builder` which is actually - * `ptn::core::detail::match_builder` internally. - */ - template - using match_builder = detail::match_builder; - -} // namespace ptn::core From bcd1a9d896dec836a35cecee4c8790470cff2d2d Mon Sep 17 00:00:00 2001 From: sentomk Date: Fri, 28 Nov 2025 21:40:14 +0800 Subject: [PATCH 3/7] refactor(core): reorganize namespace structure and headers Reorganized the core module's namespace structure from ptn::dsl to ptn::core::dsl for better logical grouping. Added new common headers (diagnostics, eval, optimize, common_traits) to the CMakeLists installation. Updated namespaces in case_expr.hpp and operators.hpp files. Removed NAMESPACE export from CMake configuration. This refactoring improves code organization and clarifies the module hierarchy. --- CMakeLists.txt | 8 +- include/ptn/core/common/common_traits.hpp | 197 ++++++++++++++++++ include/ptn/core/common/diagnostics.hpp | 96 +++++++++ include/ptn/core/common/eval.hpp | 121 +++++++++++ include/ptn/core/common/optimize.hpp | 16 ++ include/ptn/core/dsl/case_expr.hpp | 32 +-- ...ase_expr_detail.hpp => case_expr_impl.hpp} | 12 +- include/ptn/core/dsl/operators.hpp | 8 +- include/ptn/core/engine/builder.hpp | 2 +- .../ptn/core/engine/detail/builder_impl.hpp | 43 +++- include/ptn/core/engine/detail/case_eval.hpp | 77 ------- include/ptn/core/engine/detail/match_impl.hpp | 70 +------ include/ptn/core/engine/match.hpp | 2 + include/ptn/core/fwd.h | 73 ++++--- 14 files changed, 538 insertions(+), 219 deletions(-) create mode 100644 include/ptn/core/common/common_traits.hpp create mode 100644 include/ptn/core/common/diagnostics.hpp create mode 100644 include/ptn/core/common/eval.hpp create mode 100644 include/ptn/core/common/optimize.hpp rename include/ptn/core/dsl/detail/{case_expr_detail.hpp => case_expr_impl.hpp} (64%) delete mode 100644 include/ptn/core/engine/detail/case_eval.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 868a5d60..3dceb8d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,10 +80,17 @@ PTN_JOIN_HEADERS(PTN_HEADERS # Core Layer include/ptn/core/fwd.h + include/ptn/core/common/common_traits.hpp + include/ptn/core/common/diagnostics.hpp + include/ptn/core/common/eval.hpp + include/ptn/core/common/optimize.hpp include/ptn/core/engine/builder.hpp include/ptn/core/engine/match.hpp include/ptn/core/engine/detail/builder_impl.hpp include/ptn/core/engine/detail/match_impl.hpp + include/ptn/core/dsl/case_expr.hpp + include/ptn/core/dsl/operators.hpp + include/ptn/core/dsl/detail/case_expr_impl.hpp # Meta Utils include/ptn/meta/algorithms.hpp @@ -273,6 +280,5 @@ if(PTN_INSTALL) export( EXPORT patterniaTargets FILE "${CMAKE_CURRENT_BINARY_DIR}/patterniaTargets.cmake" - NAMESPACE patternia:: ) endif() diff --git a/include/ptn/core/common/common_traits.hpp b/include/ptn/core/common/common_traits.hpp new file mode 100644 index 00000000..5a80aa4b --- /dev/null +++ b/include/ptn/core/common/common_traits.hpp @@ -0,0 +1,197 @@ +#pragma once + +/** + * @file common_traits.hpp + * @brief Core type traits used by the matching engine. + */ + +#include "ptn/config.hpp" +#include +#include +#include + +#if PTN_USE_CONCEPTS +#include +#endif + +// Forward declaration +namespace ptn::core::dsl::detail { + template + struct case_expr; +} + +namespace ptn::core::common { + + /** Basic Type Extraction */ + + /// @brief Detects if a type is a `case_expr`. + template + struct is_case_expr : std::false_type {}; + + template + struct is_case_expr> + : std::true_type {}; + + template + inline constexpr bool is_case_expr_v = is_case_expr::value; + + /// @brief Extracts the `Pattern` type from a `case_expr`. + template + struct case_pattern { + using type = void; + }; + + template + struct case_pattern> { + using type = Pattern; + }; + + template + using case_pattern_t = typename case_pattern::type; + + /// @brief Extracts the `Handler` type from a `case_expr`. + template + struct case_handler { + using type = void; + }; + + template + struct case_handler> { + using type = Handler; + }; + + template + using case_handler_t = typename case_handler::type; + + /** Pattern Interface Definition */ +#if PTN_USE_CONCEPTS + /// @brief Concept that defines a valid pattern. + /// A pattern must be invocable with a subject and return a boolean. + template + concept pattern_like = requires(const P &p, auto &&subj) { + { p.match(subj) } -> std::convertible_to; + }; +#else + /// @brief C++17 SFINAE fallback for `pattern_like`. + template + struct is_pattern : std::false_type {}; + + template + struct is_pattern< + P, + std::void_t().match( + std::declval()))>()))>> + : std::true_type {}; + + template + inline constexpr bool is_pattern_v = is_pattern

::value; +#endif + + /** Core Abstraction: `binding_args` */ + + /// @brief Primary template for pattern binding results. + /// + /// This trait defines the contract for what a pattern "binds" from a subject. + /// It must be specialized for each concrete pattern type. The default + /// implementation assumes the pattern binds the entire subject. + /// + /// @tparam Pattern The pattern type. + /// @tparam Subject The subject type being matched. + template + struct binding_args { + using tuple_type = std::tuple; // Default: bind the whole subject. + }; + + template + using binding_args_t = typename binding_args::tuple_type; + + /** Handler Invocability Check */ +#if PTN_USE_CONCEPTS + /// @brief C++20 Concept to check if a handler can be invoked with arguments + /// bound by its pattern. + template + concept handler_invocable_for = requires(Case &&c, Subject &&s) { + // Simulates the call: pattern.bind(s) -> args..., handler(args...) + std::apply( + std::forward(c).handler, + std::forward(c).pattern.bind(std::forward(s))); + }; + + template + inline constexpr bool is_handler_invocable_v = + handler_invocable_for; +#else + /// @brief C++17 SFINAE implementation for handler invocability. + namespace detail { + template + static constexpr std::true_type is_applicable_impl( + decltype(std::apply(std::declval(), std::declval())) *); + + template + static constexpr std::false_type is_applicable_impl(...); + } // namespace detail + + template + struct is_handler_invocable { + private: + using handler_type = case_handler_t; + using pattern_type = case_pattern_t; + using bound_args_tuple = binding_args_t; + + public: + static constexpr bool value = + decltype(detail::is_applicable_impl( + nullptr))::value; + }; + + template + inline constexpr bool is_handler_invocable_v = + is_handler_invocable::value; +#endif + + /** Case and Match Result Types */ + + /// @brief Computes the result type of a single case expression. + template + struct case_result { + private: + using handler_type = case_handler_t; + using pattern_type = case_pattern_t; + using bound_args_tuple = binding_args_t; + + public: + using type = decltype(std::apply( + std::declval(), std::declval())); + }; + + template + using case_result_t = typename case_result::type; + + /// @brief Helper to deduce the result type of an `otherwise` handler. + namespace detail { + template + static constexpr auto get_otherwise_result_impl(int) + -> decltype(std::declval()(std::declval())); + + template + static constexpr auto get_otherwise_result_impl(...) + -> decltype(std::declval()()); + } // namespace detail + + template + using otherwise_result_t = + decltype(detail::get_otherwise_result_impl(0)); + + /// @brief Computes the common result type of the entire match expression. + template + struct match_result { + using type = std::common_type_t< + case_result_t..., + otherwise_result_t>; + }; + + template + using match_result_t = + typename match_result::type; + +} // namespace ptn::core::common diff --git a/include/ptn/core/common/diagnostics.hpp b/include/ptn/core/common/diagnostics.hpp new file mode 100644 index 00000000..3b296562 --- /dev/null +++ b/include/ptn/core/common/diagnostics.hpp @@ -0,0 +1,96 @@ +#pragma once + +/** + * @file diagnostics.hpp + * @brief Compile-time diagnostics and static assertions for Patternia. + */ + +#include +#include "ptn/core/common/common_traits.hpp" + +namespace ptn::core::common { + + /** + * @brief Validates the entire match expression for consistency. + */ + template + constexpr void static_assert_valid_match() { + // Check: All case handlers must be invocable with their pattern's bindings. + static_assert( + (is_handler_invocable_v && ...), + "[Patternia Error] At least one case's handler cannot be invoked with " + "the arguments bound by its pattern. " + "Please check the handler's signature against the pattern's expected " + "bindings."); + + // Check: The `otherwise` handler must be invocable. + static_assert( + std::is_invocable_v || + std::is_invocable_v, + "[Patternia Error] The `otherwise` handler has an invalid signature. " + "It should be either callable with the subject value or callable with " + "no arguments."); + + // Check: All handlers must have a common return type. + using common_return_type = match_result_t; + static_assert( + !std::is_same_v, + "[Patternia Error] All handlers in a match expression (including " + "`otherwise`) must have a compatible return type."); + } + + /** + * @brief Validates that a handler matches a pattern's bindings. + */ + template + constexpr void static_assert_valid_handler() { + static_assert( + is_handler_invocable_v, +#if PTN_USE_CONCEPTS + "[Patternia Error] Handler cannot be invoked with the arguments bound " + "by the pattern." +#else + "[Patternia Error] Handler signature does not match the pattern's " + "binding result." +#endif + ); + } + + /** + * @brief Validates a single case expression. + */ + template + constexpr void static_assert_valid_case() { + static_assert( + is_case_expr_v, + "[Patternia Error] Argument to `.when()` must be a case expression " + "created with the '>>' operator."); + static_assert_valid_handler(); + } + + /** + * @brief Validates that a type is a valid pattern. + */ + template + constexpr void static_assert_valid_pattern() { +#if PTN_USE_CONCEPTS + static_assert( + pattern_like, + "[Patternia Error] The provided type is not a valid pattern. " + "A pattern must be invocable with a subject and return a boolean."); +#else + static_assert( + is_pattern_v, + "[Patternia Error] The provided type is not a valid pattern. " + "A pattern must be invocable with a subject and return a boolean."); +#endif + } + + template + struct has_unreachable_case : std::false_type {}; + + template + inline constexpr bool has_unreachable_case_v = + has_unreachable_case::value; + +} // namespace ptn::core::common diff --git a/include/ptn/core/common/eval.hpp b/include/ptn/core/common/eval.hpp new file mode 100644 index 00000000..8171a59c --- /dev/null +++ b/include/ptn/core/common/eval.hpp @@ -0,0 +1,121 @@ +#pragma once + +/** + * @file eval.hpp + * @brief Common evaluation logic for matching and handler invocation. + */ + +#include +#include +#include + +#include "ptn/config.hpp" +#include "ptn/core/common/common_traits.hpp" + +namespace ptn::core::common { + + /** Case Matching */ + + /// @brief Checks if a case's pattern matches a subject. + template + struct case_matcher { + using case_type = Case; + using subject_type = Subject; + using pattern_type = case_pattern_t; + + /// @brief Returns true if the pattern matches the subject. + static constexpr bool + matches(const case_type &c, const subject_type &subject) { + // Supports both class-based patterns with a .match() method and + // functional patterns. + if constexpr (std::is_member_function_pointer_v< + decltype(&pattern_type::match)>) { + return c.pattern.match(subject); + } + else { + return static_cast(std::invoke(c.pattern, subject)); + } + } + }; + + /** Handler Invocation Logic */ + + /// @brief Invokes a handler with values bound by its pattern. + template + constexpr decltype(auto) invoke_handler(const Case &c, Subject &&subject) { + using pattern_type = case_pattern_t; + +// The check is different for C++17 and C++20. +#if PTN_USE_CONCEPTS + static_assert( + requires(const pattern_type &p, Subject &&s) { + { p.bind(std::forward(s)) }; + }, + "Pattern must have a 'bind(subject)' method that returns a tuple of " + "bound values."); +#else + // C++17 SFINAE trick to check if `bind` is a valid expression. + static_assert( + sizeof( + decltype(std::declval().bind(std::declval()), void())) != + 0, + "Pattern must have a 'bind(subject)' method that returns a tuple of " + "bound values."); +#endif + + // Let the pattern perform the binding. + auto bound_values = c.pattern.bind(std::forward(subject)); + + // Apply the handler to the bound values. + return std::apply(c.handler, std::move(bound_values)); + } + + /** Case Sequence Evaluation */ + namespace detail { + /// @brief Recursive implementation for evaluating a tuple of cases. + template < + std::size_t I, + typename Subject, + typename CasesTuple, + typename Otherwise, + std::size_t N = std::tuple_size_v>> + constexpr decltype(auto) eval_cases_impl( + Subject &subject, CasesTuple &cases, Otherwise &&otherwise_handler) { + if constexpr (I >= N) { + // Base case: no case matched, invoke the `otherwise` handler. + if constexpr (std::is_invocable_v) { + return std::forward(otherwise_handler)(subject); + } + else { + return std::forward(otherwise_handler)(); + } + } + else { + auto ¤t_case = std::get(cases); + using case_t = std::remove_reference_t; + + if (case_matcher::matches(current_case, subject)) { + // Found a match, invoke its handler. + return invoke_handler(current_case, std::forward(subject)); + } + else { + // Try the next case. + return eval_cases_impl( + subject, cases, std::forward(otherwise_handler)); + } + } + } + } // namespace detail + + /// @brief Public entry point to evaluate a sequence of cases. + template + constexpr decltype(auto) eval_cases( + Subject &subject, CasesTuple &cases, Otherwise &&otherwise_handler) { + using tuple_t = std::remove_reference_t; + constexpr std::size_t N = std::tuple_size_v; + + return detail::eval_cases_impl<0>( + subject, cases, std::forward(otherwise_handler)); + } + +} // namespace ptn::core::common diff --git a/include/ptn/core/common/optimize.hpp b/include/ptn/core/common/optimize.hpp new file mode 100644 index 00000000..6a512d31 --- /dev/null +++ b/include/ptn/core/common/optimize.hpp @@ -0,0 +1,16 @@ +#pragma once + +namespace ptn::core::common { + + namespace detail { + template + struct optimize_case_sequence { + using type = CasesTuple; + }; + } // namespace detail + + template + using optimize_case_sequence_t = + typename detail::optimize_case_sequence::type; + +} // namespace ptn::core::common diff --git a/include/ptn/core/dsl/case_expr.hpp b/include/ptn/core/dsl/case_expr.hpp index 74247632..e7c48225 100644 --- a/include/ptn/core/dsl/case_expr.hpp +++ b/include/ptn/core/dsl/case_expr.hpp @@ -3,47 +3,21 @@ /** * @file case_expr.hpp * @brief Public type alias for case expressions. - * - * This module provides the public `case_expr` type (via alias to detail - * implementation). Case expressions are created implicitly when using the `>>` - * operator: - * - * ```cpp - * auto case = pattern >> handler; // Creates a case_expr - * ``` - * - * Users should NOT instantiate `case_expr` directly. Use the `>>` operator - * instead. - * - * Part of the Core Layer (namespace ptn::dsl) */ -#include "ptn/core/dsl/detail/case_expr_detail.hpp" - -namespace ptn::dsl::detail { +namespace ptn::core::dsl::detail { template struct case_expr; } -namespace ptn::dsl { +namespace ptn::core::dsl { /** * @brief Public type alias for case expressions. * * Represents a pattern-handler pair in the match DSL. - * Created via the `>>` operator: `pattern >> handler`. - * - * @tparam Pattern The pattern type. - * @tparam Handler The handler type. - * - * @example - * ```cpp - * using namespace ptn::pattern::value; - * auto case1 = lit(10) >> "ten"; - * auto case2 = lt(5) >> "small"; - * ``` */ template using case_expr = detail::case_expr; -} // namespace ptn::dsl \ No newline at end of file +} // namespace ptn::core::dsl \ No newline at end of file diff --git a/include/ptn/core/dsl/detail/case_expr_detail.hpp b/include/ptn/core/dsl/detail/case_expr_impl.hpp similarity index 64% rename from include/ptn/core/dsl/detail/case_expr_detail.hpp rename to include/ptn/core/dsl/detail/case_expr_impl.hpp index 04f5a835..8a060877 100644 --- a/include/ptn/core/dsl/detail/case_expr_detail.hpp +++ b/include/ptn/core/dsl/detail/case_expr_impl.hpp @@ -4,18 +4,10 @@ * @file case_expr_impl.hpp (detail) * @brief Internal implementation of case expressions. * - * This is an internal header containing the implementation of: - * - `case_expr`: Represents a single case in match() DSL. - * - * Users should create case expressions using the `>>` operator defined in - * `ptn/core/dsl/operators.hpp` instead of directly instantiating this type. - * - * Part of the Core Layer (namespace ptn::dsl::detail) - * * @internal This is an internal implementation detail. */ -namespace ptn::dsl::detail { +namespace ptn::core::dsl::detail { /** * @brief Internal representation of a pattern-handler pair (case). @@ -36,4 +28,4 @@ namespace ptn::dsl::detail { Handler handler; ///< The handler to execute if pattern matches }; -} // namespace ptn::dsl::detail +} // namespace ptn::core::dsl::detail diff --git a/include/ptn/core/dsl/operators.hpp b/include/ptn/core/dsl/operators.hpp index c7cefbe6..3d773408 100644 --- a/include/ptn/core/dsl/operators.hpp +++ b/include/ptn/core/dsl/operators.hpp @@ -3,7 +3,7 @@ #include #include -namespace ptn::dsl::detail { +namespace ptn::core::dsl::detail { template struct case_expr; } @@ -36,12 +36,12 @@ namespace ptn::pattern::detail { extern const bool is_pattern_v; } // namespace ptn::pattern::detail -namespace ptn::dsl::ops { +namespace ptn::core::dsl::ops { // ========== operator>> (pattern >> handler) ========== template constexpr auto operator>>(P &&p, H &&h) { - return ptn::dsl::detail::case_expr, std::decay_t>{ + return detail::case_expr, std::decay_t>{ std::forward

(p), std::forward(h)}; } @@ -105,4 +105,4 @@ namespace ptn::dsl::ops { #endif -} // namespace ptn::dsl::ops \ No newline at end of file +} // namespace ptn::core::dsl::ops \ No newline at end of file diff --git a/include/ptn/core/engine/builder.hpp b/include/ptn/core/engine/builder.hpp index 3b5865a6..5a0b754a 100644 --- a/include/ptn/core/engine/builder.hpp +++ b/include/ptn/core/engine/builder.hpp @@ -31,6 +31,6 @@ namespace ptn::core { * @brief Public alias forwarding to the actual implementation. */ template - using match_builder = detail::match_builder; + using match_builder = engine::detail::match_builder; } // namespace ptn::core diff --git a/include/ptn/core/engine/detail/builder_impl.hpp b/include/ptn/core/engine/detail/builder_impl.hpp index cd7241df..3147cff6 100644 --- a/include/ptn/core/engine/detail/builder_impl.hpp +++ b/include/ptn/core/engine/detail/builder_impl.hpp @@ -9,9 +9,10 @@ #include #include "ptn/config.hpp" +#include "ptn/core/common/diagnostics.hpp" #include "ptn/core/engine/detail/match_impl.hpp" -namespace ptn::core::detail { +namespace ptn::core::engine::detail { template class match_builder { @@ -51,6 +52,21 @@ namespace ptn::core::detail { constexpr auto when(CaseExpr &&expr) && { using new_case_type = std::decay_t; + // Compile-time validation of the new case. + static_assert( + ptn::core::common::is_case_expr_v, + "Argument to .when() must be a case expression created with the '>>' " + "operator."); + static_assert( + ptn::core::common:: + is_handler_invocable_v, +#if PTN_USE_CONCEPTS + "Handler cannot be invoked with the arguments bound by the pattern." +#else + "Handler signature does not match the pattern's binding result." +#endif + ); + auto new_cases = std::tuple_cat( std::move(cases_), std::tuple(std::forward(expr))); @@ -62,15 +78,26 @@ namespace ptn::core::detail { /** * @brief Add a new case (lvalue-qualified). - * - * This overload allows code like: - * auto b = match(x); - * auto b2 = b.when(...); */ + template constexpr auto when(CaseExpr &&expr) const & { using new_case_type = std::decay_t; + static_assert( + ptn::core::common::is_case_expr_v, + "Argument to .when() must be a case expression created with the '>>' " + "operator."); + static_assert( + ptn::core::common:: + is_handler_invocable_v, +#if PTN_USE_CONCEPTS + "Handler cannot be invoked with the arguments bound by the pattern." +#else + "Handler signature does not match the pattern's binding result." +#endif + ); + auto new_cases = std::tuple_cat( cases_, std::tuple(std::forward(expr))); @@ -87,9 +114,13 @@ namespace ptn::core::detail { */ template constexpr decltype(auto) otherwise(Otherwise &&otherwise_handler) && { + + ptn::core::common:: + static_assert_valid_match(); + return match_impl::eval( subject_, cases_, std::forward(otherwise_handler)); } }; -} // namespace ptn::core::detail +} // namespace ptn::core::engine::detail diff --git a/include/ptn/core/engine/detail/case_eval.hpp b/include/ptn/core/engine/detail/case_eval.hpp deleted file mode 100644 index 3604aed2..00000000 --- a/include/ptn/core/engine/detail/case_eval.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once -/** - * @file case_eval.hpp - * @brief Low-level evaluation of a single case (pattern + handler). - */ - -#include -#include -#include - -#include "ptn/config.hpp" - -namespace ptn::core::detail { - - // Helper for static_assert that depends on a template parameter - template - struct dependent_false : std::false_type {}; - - template - struct case_eval { - using case_type = Case; - using subject_type = Subject; - - // Use ADL-friendly pattern type deduction if needed - using pattern_type = decltype(std::declval().pattern); - using handler_type = decltype(std::declval().handler); - - /** - * @brief Check whether this case matches the subject. - * - * Default policy: - * - call `pattern(subject)` via std::invoke - */ - static constexpr bool - matches(const case_type &c, const subject_type &subject) { - if constexpr (std::is_invocable_r_v) { - return std::invoke(c.pattern, subject); - } - else if constexpr (std::is_invocable_v) { - // Allow non-bool results, but treat them as "truthy" - return static_cast(std::invoke(c.pattern, subject)); - } - else { - static_assert( - dependent_false::value, - "[case_eval]: Pattern must be invocable as " - "bool(pattern(subject))."); - } - } - - /** - * @brief Invoke the handler associated with this case. - * - * Handler policy: - * - Prefer handler(subject) if available - * - Otherwise, call handler() - */ - template - static constexpr decltype(auto) invoke(const case_type &c, S &&subject) { - using handler_t = handler_type; - - if constexpr (std::is_invocable_v) { - return std::invoke(c.handler, std::forward(subject)); - } - else if constexpr (std::is_invocable_v) { - return std::invoke(c.handler); - } - else { - static_assert( - dependent_false::value, - "[case_eval]: Handler must be invocable either as handler(subject) " - "or handler()."); - } - } - }; - -} // namespace ptn::core::detail diff --git a/include/ptn/core/engine/detail/match_impl.hpp b/include/ptn/core/engine/detail/match_impl.hpp index f9e91be6..7da14be3 100644 --- a/include/ptn/core/engine/detail/match_impl.hpp +++ b/include/ptn/core/engine/detail/match_impl.hpp @@ -4,80 +4,22 @@ * @brief Core evaluation logic for a sequence of cases. */ -#include -#include -#include #include -#include "ptn/config.hpp" -#include "ptn/core/engine/detail/case_eval.hpp" +#include "ptn/core/common/eval.hpp" -namespace ptn::core::detail { +namespace ptn::core::engine::detail { struct match_impl { + /** * @brief Evaluate the tuple of cases against a subject. - * - * TV : subject type - * CasesTuple : std::tuple - * Otherwise : fallback handler */ template static constexpr decltype(auto) eval(TV &subject, CasesTuple &cases, Otherwise &&otherwise_handler) { - using tuple_type = std::remove_reference_t; - constexpr std::size_t N = std::tuple_size_v; - - return eval_chain<0>( - subject, - cases, - std::forward(otherwise_handler), - std::integral_constant{}); - } - - private: - // Recursive chain over tuple indices [0, N) - template < - std::size_t I, - typename TV, - typename CasesTuple, - typename Otherwise> - static constexpr decltype(auto) eval_chain( - TV &subject, - CasesTuple &cases, - Otherwise &&otherwise_handler, - std::integral_constant) { - using tuple_type = std::remove_reference_t; - constexpr std::size_t N = std::tuple_size_v; - - if constexpr (I >= N) { - // No case matched; fall back to "otherwise" - if constexpr (std::is_invocable_v) { - return std::forward(otherwise_handler)(subject); - } - else { - return std::forward(otherwise_handler)(); - } - } - else { - auto &c = std::get(cases); - using case_type = std::remove_reference_t; - using eval_t = case_eval; - - if (eval_t::matches(c, subject)) { - // First matching case — invoke its handler - return eval_t::invoke(c, subject); - } - else { - // Try next - return eval_chain( - subject, - cases, - std::forward(otherwise_handler), - std::integral_constant{}); - } - } + return ptn::core::common::eval_cases( + subject, cases, std::forward(otherwise_handler)); } }; - -} // namespace ptn::core::detail +} // namespace ptn::core::engine::detail diff --git a/include/ptn/core/engine/match.hpp b/include/ptn/core/engine/match.hpp index 1b77bd99..617080ca 100644 --- a/include/ptn/core/engine/match.hpp +++ b/include/ptn/core/engine/match.hpp @@ -17,6 +17,7 @@ namespace ptn { * * Subject type is deduced as std::decay_t. */ + template constexpr auto match(T &&value) { using V = std::decay_t; @@ -29,6 +30,7 @@ namespace ptn { * This overload is SFINAE-disabled if U and T are the same after decay, * to avoid ambiguous calls with the primary overload. */ + template constexpr auto match(T &&value) -> std::enable_if_t< !std::is_same_v, std::decay_t>, diff --git a/include/ptn/core/fwd.h b/include/ptn/core/fwd.h index a8583c5f..bf719571 100644 --- a/include/ptn/core/fwd.h +++ b/include/ptn/core/fwd.h @@ -1,50 +1,69 @@ #pragma once + /** * @file fwd.hpp * @brief Forward declarations for Patternia Core Layer. */ +#include "ptn/config.hpp" // For PTN_USE_CONCEPTS + +#if PTN_USE_CONCEPTS +#include +#endif + namespace ptn { - /* Entry function: match() */ + + /* --- Public API --- */ + template constexpr auto match(T &&); template constexpr auto match(T &&); - /* Core builder type (public-facing alias) */ namespace core { + template + class match_builder; // Public-facing alias + } + + /* --- Core Internals --- */ - /** - * @brief Public-facing builder template. - * - * Actual implementation is in namespace ptn::core::detail::match_builder. - */ + namespace core::engine::detail { template - class match_builder; // only forward declared here + class match_builder; // Internal implementation + } - } // namespace core + namespace core::dsl::detail { + template + struct case_expr; + } - /* Core internals (detail) */ - namespace core::detail { + /* --- Common Traits (Forward Declarations) --- */ - /** - * @brief Internal builder implementation. - */ - template - class match_builder; + namespace core::common { + +#if PTN_USE_CONCEPTS + /// @brief Concept that defines a valid pattern. + template + concept pattern_like = requires(const P &p, auto &&subj) { + { p.match(subj) } -> std::convertible_to; + }; +#else + /// @brief C++17 SFINAE fallback for `pattern_like`. + template + struct is_pattern; - /** - * @brief Single-case evaluation (pattern + handler). - */ - template - struct case_eval; + template + inline constexpr bool is_pattern_v = is_pattern

::value; +#endif - /** - * @brief Full evaluation logic for the match builder. - */ - struct match_impl; + /// @brief Forward declaration of the core binding trait. + /// + /// This is the primary template. Specializations for concrete patterns + /// should be provided in the same header as the pattern definition. + template + struct binding_args; - } // namespace core::detail + } // namespace core::common -} // namespace ptn \ No newline at end of file +} // namespace ptn From c340ce94ba1008e207876f663e0a841dd75dbb78 Mon Sep 17 00:00:00 2001 From: sentomk Date: Fri, 28 Nov 2025 22:16:19 +0800 Subject: [PATCH 4/7] refactor: relocate pattern traits to pattern/base module Move pattern-related traits (pattern_like concept, is_pattern, binding_args) from common_traits.hpp to the pattern/base/fwd.h module. This reorganization improves code modularity by placing pattern-specific abstractions in their dedicated module, reducing coupling between common utilities and pattern implementations. Updated references in diagnostics.hpp and common_traits.hpp to use the new location. --- include/ptn/core/common/common_traits.hpp | 47 +------- include/ptn/core/common/diagnostics.hpp | 4 +- include/ptn/pattern/base/binding_traits.hpp | 14 +++ include/ptn/pattern/base/fwd.h | 50 +++++++++ include/ptn/pattern/base/pattern_base.hpp | 24 ++++ include/ptn/pattern/base/pattern_kind.hpp | 14 +++ include/ptn/pattern/base/pattern_traits.hpp | 64 +++++++++++ include/ptn/pattern/detail/pattern_base.hpp | 74 ------------- include/ptn/pattern/detail/pattern_traits.hpp | 104 ------------------ 9 files changed, 171 insertions(+), 224 deletions(-) create mode 100644 include/ptn/pattern/base/binding_traits.hpp create mode 100644 include/ptn/pattern/base/fwd.h create mode 100644 include/ptn/pattern/base/pattern_base.hpp create mode 100644 include/ptn/pattern/base/pattern_kind.hpp create mode 100644 include/ptn/pattern/base/pattern_traits.hpp delete mode 100644 include/ptn/pattern/detail/pattern_base.hpp delete mode 100644 include/ptn/pattern/detail/pattern_traits.hpp diff --git a/include/ptn/core/common/common_traits.hpp b/include/ptn/core/common/common_traits.hpp index 5a80aa4b..b55b2ce9 100644 --- a/include/ptn/core/common/common_traits.hpp +++ b/include/ptn/core/common/common_traits.hpp @@ -6,6 +6,7 @@ */ #include "ptn/config.hpp" +#include "ptn/pattern/base/fwd.h" #include #include #include @@ -63,48 +64,6 @@ namespace ptn::core::common { template using case_handler_t = typename case_handler::type; - /** Pattern Interface Definition */ -#if PTN_USE_CONCEPTS - /// @brief Concept that defines a valid pattern. - /// A pattern must be invocable with a subject and return a boolean. - template - concept pattern_like = requires(const P &p, auto &&subj) { - { p.match(subj) } -> std::convertible_to; - }; -#else - /// @brief C++17 SFINAE fallback for `pattern_like`. - template - struct is_pattern : std::false_type {}; - - template - struct is_pattern< - P, - std::void_t().match( - std::declval()))>()))>> - : std::true_type {}; - - template - inline constexpr bool is_pattern_v = is_pattern

::value; -#endif - - /** Core Abstraction: `binding_args` */ - - /// @brief Primary template for pattern binding results. - /// - /// This trait defines the contract for what a pattern "binds" from a subject. - /// It must be specialized for each concrete pattern type. The default - /// implementation assumes the pattern binds the entire subject. - /// - /// @tparam Pattern The pattern type. - /// @tparam Subject The subject type being matched. - template - struct binding_args { - using tuple_type = std::tuple; // Default: bind the whole subject. - }; - - template - using binding_args_t = typename binding_args::tuple_type; - /** Handler Invocability Check */ #if PTN_USE_CONCEPTS /// @brief C++20 Concept to check if a handler can be invoked with arguments @@ -136,7 +95,7 @@ namespace ptn::core::common { private: using handler_type = case_handler_t; using pattern_type = case_pattern_t; - using bound_args_tuple = binding_args_t; + using bound_args_tuple = pat::base::binding_args_t; public: static constexpr bool value = @@ -157,7 +116,7 @@ namespace ptn::core::common { private: using handler_type = case_handler_t; using pattern_type = case_pattern_t; - using bound_args_tuple = binding_args_t; + using bound_args_tuple = pat::base::binding_args_t; public: using type = decltype(std::apply( diff --git a/include/ptn/core/common/diagnostics.hpp b/include/ptn/core/common/diagnostics.hpp index 3b296562..3057168c 100644 --- a/include/ptn/core/common/diagnostics.hpp +++ b/include/ptn/core/common/diagnostics.hpp @@ -75,12 +75,12 @@ namespace ptn::core::common { constexpr void static_assert_valid_pattern() { #if PTN_USE_CONCEPTS static_assert( - pattern_like, + ptn::pattern::detail::pattern, "[Patternia Error] The provided type is not a valid pattern. " "A pattern must be invocable with a subject and return a boolean."); #else static_assert( - is_pattern_v, + ptn::pat::base::is_pattern_v, "[Patternia Error] The provided type is not a valid pattern. " "A pattern must be invocable with a subject and return a boolean."); #endif diff --git a/include/ptn/pattern/base/binding_traits.hpp b/include/ptn/pattern/base/binding_traits.hpp new file mode 100644 index 00000000..940d4a81 --- /dev/null +++ b/include/ptn/pattern/base/binding_traits.hpp @@ -0,0 +1,14 @@ +#pragma once +#include + +namespace ptn::pat::base { + + template + struct binding_args { + using type = std::tuple; // default + }; + + template + using binding_args_t = typename binding_args::type; + +} // namespace ptn::pat::base diff --git a/include/ptn/pattern/base/fwd.h b/include/ptn/pattern/base/fwd.h new file mode 100644 index 00000000..0552dc5b --- /dev/null +++ b/include/ptn/pattern/base/fwd.h @@ -0,0 +1,50 @@ +#pragma once + +/** + * @file fwd.hpp + * @brief Forward declarations for Patternia Pattern Base Layer. + */ + +namespace ptn::pat::base { + + /** Pattern identification base */ + struct pattern_tag; + + /** Pattern identification base */ + enum class pattern_kind; + + /** Pattern identification base */ + template + struct binding_args; + + template + using binding_args_t = typename binding_args::type; + + /** Pattern identification base */ + + // has_match_method + template + struct has_match_method; + + template + inline constexpr bool has_match_method_v = has_match_method

::value; + + // has_bind_method + template + struct has_bind_method; + + template + inline constexpr bool has_bind_method_v = has_bind_method

::value; + + // is_pattern + template + struct is_pattern; + + template + inline constexpr bool is_pattern_v = is_pattern

::value; + + // pattern_category + template + struct pattern_category; + +} // namespace ptn::pat::base diff --git a/include/ptn/pattern/base/pattern_base.hpp b/include/ptn/pattern/base/pattern_base.hpp new file mode 100644 index 00000000..6c6e3a69 --- /dev/null +++ b/include/ptn/pattern/base/pattern_base.hpp @@ -0,0 +1,24 @@ +#pragma once +#include +#include "pattern_kind.hpp" + +namespace ptn::pat::base { + + struct pattern_tag {}; + + template + struct pattern_base : pattern_tag { + + static constexpr pattern_kind kind = pattern_kind::unknown; + + // match(): CRTP forwarding + template + constexpr bool match(Subject &&subj) const + noexcept(noexcept(static_cast(this)->match( + std::forward(subj)))) { + return static_cast(this)->match( + std::forward(subj)); + } + }; + +} // namespace ptn::pat::base diff --git a/include/ptn/pattern/base/pattern_kind.hpp b/include/ptn/pattern/base/pattern_kind.hpp new file mode 100644 index 00000000..49e0e724 --- /dev/null +++ b/include/ptn/pattern/base/pattern_kind.hpp @@ -0,0 +1,14 @@ +#pragma once +namespace ptn::pat::base { + + enum class pattern_kind { + literal, + relational, + predicate, + type, + combinator, + wildcard, + unknown + }; + +} // namespace ptn::pat::base diff --git a/include/ptn/pattern/base/pattern_traits.hpp b/include/ptn/pattern/base/pattern_traits.hpp new file mode 100644 index 00000000..9c73be74 --- /dev/null +++ b/include/ptn/pattern/base/pattern_traits.hpp @@ -0,0 +1,64 @@ +#pragma once +#include +#include +#include "ptn/config.hpp" + +/** + * @file pattern_traits.hpp + * @brief Compile-time detection utilities for Patternia patterns. + */ + +namespace ptn::pat::detail { + /** Pattern identification base */ + + template + struct has_match_method : std::false_type {}; + + template + struct has_match_method< + P, + std::void_t(std::declval().match( + std::declval())))>> : std::true_type {}; + + /** Pattern identification base */ + + /** + * @brief Marker base type for all built-in Patternia patterns. + */ + struct pattern_tag { + static constexpr bool is_pattern = true; + }; + + /** Pattern identification base */ +#if defined(PTN_USE_CONCEPTS) && PTN_USE_CONCEPTS + + /** + * @brief C++20 Concept: determines whether a type behaves like a Pattern. + * @tparam P The type being tested. + */ + template + concept pattern = + std::derived_from || requires(const P &p, auto &&subj) { + { p.match(subj) } -> std::convertible_to; + }; + +#else // C++17 fallback + + /** + * @brief Trait: determines whether P acts as a Pattern. + */ + template + struct is_pattern : std::integral_constant< + bool, + std::is_base_of_v || + ptn::pat::detail::has_match_method

::value> {}; + + /** + * @brief Convenience variable template for is_pattern

::value. + */ + template + inline constexpr bool is_pattern_v = is_pattern

::value; + +#endif + +} // namespace ptn::pat::detail diff --git a/include/ptn/pattern/detail/pattern_base.hpp b/include/ptn/pattern/detail/pattern_base.hpp deleted file mode 100644 index b7893bc2..00000000 --- a/include/ptn/pattern/detail/pattern_base.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include "ptn/pattern/detail/pattern_traits.hpp" // marker -#include "ptn/config.hpp" -#include - -/** - * @file pattern_base.hpp - * @brief CRTP base class providing unified pattern interface for Patternia. - * - * All built-in patterns should derive from `pattern_base`. - * This base class delivers: - * - the “pattern identity” (via pattern_tag) - * - default forwarding for `match(subject)` - * - default forwarding for `bind(subject)` - * - default no-op bind for predicate-like patterns - * - * Part of Pattern Layer (ptn::pattern) - */ - -namespace ptn::pattern::detail { - - /* forward declare */ - template - struct pattern_base; - - /// Helper to obtain Derived& - template - constexpr Derived &derived_of(pattern_base &self) noexcept { - return static_cast(self); - } - /// Helper to obtain const Derived& - template - constexpr const Derived & - derived_of(const pattern_base &self) noexcept { - return static_cast(self); - } - - /** - * @brief CRTP base class for all Patternia patterns. - * - * Derived must implement: - * - `bool match(const Subject&) const` - * - optionally: `auto bind(const Subject&) const` - * - * If bind() is not implemented, pattern_base returns subject unchanged. - */ - template - struct pattern_base : detail::pattern_tag { - - /** - * @brief default match() forwarding to Derived::match() - */ - template - constexpr bool match(Subject &&subj) const noexcept( - noexcept(derived_of(*this).match(std::forward(subj)))) { - return derived_of(*this).match(std::forward(subj)); - } - - /** - * @brief Default bind(): return the subject unchanged. - * - * Predicate patterns or patterns that do not transform the value may - * simply rely on this implementation. - * - * Value-pattern, type-pattern etc should override bind(). - */ - template - constexpr bool bind(Subject &&subj) const noexcept { - return std::forward(subj); - } - }; - -} // namespace ptn::pattern::detail \ No newline at end of file diff --git a/include/ptn/pattern/detail/pattern_traits.hpp b/include/ptn/pattern/detail/pattern_traits.hpp deleted file mode 100644 index 2e4dc426..00000000 --- a/include/ptn/pattern/detail/pattern_traits.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once -#include -#include -#include "ptn/config.hpp" - -/** - * @file pattern_traits.hpp - * @brief Compile-time detection utilities for Patternia patterns. - * - * This header defines: - * - pattern_tag : marker base class - * - has_match_method

- * - concept pattern (C++20) - * - trait is_pattern

/ is_pattern_v (C++17 fallback) - * - * All type-detection logic for the Pattern Layer is centralized here. - * - * namespace ptn::pattern:detail - */ - -namespace ptn::pattern::detail { - // --------------------------------------------------------------------------- - // has_match_method (C++17 detection for user-defined patterns) - // --------------------------------------------------------------------------- - - /** - * @brief C++17 SFINAE: checks whether `P::match(subj)` is a valid expression - * convertible to bool. - * - * Used in environments where concepts are disabled (`PTN_USE_CONCEPTS=0`). - * - * @tparam P Type being inspected. - */ - template - struct has_match_method : std::false_type {}; - - template - struct has_match_method< - P, - std::void_t(std::declval().match( - std::declval())))>> : std::true_type {}; - - // --------------------------------------------------------------------------- - // pattern_tag (marker base type) - // --------------------------------------------------------------------------- - - /** - * @brief Marker base type for all built-in Patternia patterns. - * - * Deriving from this type signals that a type participates in Patternia's - * DSL. - * - */ - struct pattern_tag { - static constexpr bool is_pattern = true; - }; - - // --------------------------------------------------------------------------- - // is_pattern - // --------------------------------------------------------------------------- -// Concepts-based detection (C++20) -#if defined(PTN_USE_CONCEPTS) && PTN_USE_CONCEPTS - - /** - * @brief C++20 Concept: determines whether a type behaves like a Pattern. - * - * A type P is considered pattern-like if: - * - it derives from `pattern_tag`, OR - * - it defines a valid `match(x)` operation returning something - * convertible to `bool`. - * - * This makes Patternia extensible: user-defined types with - * `match(subject)` automatically qualify as patterns without needing - * inheritance from `pattern_tag`. - * - * @tparam P The type being tested. - */ - template - concept pattern = - std::derived_from || requires(const P &p, auto &&subj) { - { p.match(subj) } -> std::convertible_to; - }; - -#else // C++17 fallback - - /** - * @brief Trait: determines whether P acts as a Pattern. - */ - template - struct is_pattern - : std::integral_constant< - bool, - std::is_base_of_v || - ptn::pattern::detail::has_match_method

::value> {}; - - /** - * @brief Convenience variable template for is_pattern

::value. - */ - template - inline constexpr bool is_pattern_v = is_pattern

::value; - -#endif - -} // namespace ptn::pattern::detail From 6b6eb6792862d3860da1751467c0f9ed1057fd0f Mon Sep 17 00:00:00 2001 From: sentomk Date: Sat, 29 Nov 2025 15:30:01 +0800 Subject: [PATCH 5/7] refactor(core): include subject in handler invocation args Update pattern matching to pass the subject as the first argument to handlers by concatenating it with bound arguments using tuple_cat. This change requires modifications to type traits to reflect the new invocation signature and enhances the pattern matching consistency across different handler types. --- include/ptn/core/common/common_traits.hpp | 23 +- include/ptn/core/common/eval.hpp | 73 ++++- include/ptn/core/dsl/operators.hpp | 108 ------- include/ptn/core/dsl/ops.hpp | 134 ++++++++ include/ptn/core/engine/match.hpp | 9 +- include/ptn/meta/{ => base}/traits.hpp | 12 - include/ptn/meta/{ => dsa}/algorithms.hpp | 16 +- include/ptn/meta/{ => dsa}/type_list.hpp | 11 - include/ptn/meta/meta.hpp | 25 +- include/ptn/meta/{ => query}/index.hpp | 10 +- .../ptn/meta/{ => query}/template_info.hpp | 13 +- include/ptn/pattern/base/fwd.h | 7 +- include/ptn/pattern/value.hpp | 15 + .../pattern/value/detail/literal_pattern.hpp | 170 ---------- .../value/detail/predicate_pattern.hpp | 171 ---------- .../value/detail/relational_pattern.hpp | 301 ------------------ include/ptn/pattern/value/literal.hpp | 174 ++++++++-- include/ptn/pattern/value/predicate.hpp | 188 ++++++++++- include/ptn/pattern/value/relational.hpp | 249 ++++++++++++--- include/ptn/patternia.hpp | 114 ++----- tests/tests_literal_pattern.cpp | 31 +- 21 files changed, 815 insertions(+), 1039 deletions(-) delete mode 100644 include/ptn/core/dsl/operators.hpp create mode 100644 include/ptn/core/dsl/ops.hpp rename include/ptn/meta/{ => base}/traits.hpp (78%) rename include/ptn/meta/{ => dsa}/algorithms.hpp (74%) rename include/ptn/meta/{ => dsa}/type_list.hpp (76%) rename include/ptn/meta/{ => query}/index.hpp (79%) rename include/ptn/meta/{ => query}/template_info.hpp (63%) create mode 100644 include/ptn/pattern/value.hpp delete mode 100644 include/ptn/pattern/value/detail/literal_pattern.hpp delete mode 100644 include/ptn/pattern/value/detail/predicate_pattern.hpp delete mode 100644 include/ptn/pattern/value/detail/relational_pattern.hpp diff --git a/include/ptn/core/common/common_traits.hpp b/include/ptn/core/common/common_traits.hpp index b55b2ce9..32ec4831 100644 --- a/include/ptn/core/common/common_traits.hpp +++ b/include/ptn/core/common/common_traits.hpp @@ -73,7 +73,9 @@ namespace ptn::core::common { // Simulates the call: pattern.bind(s) -> args..., handler(args...) std::apply( std::forward(c).handler, - std::forward(c).pattern.bind(std::forward(s))); + std::tuple_cat( + std::forward_as_tuple(std::forward(s)), + std::forward(c).pattern.bind(std::forward(s)))); }; template @@ -97,10 +99,15 @@ namespace ptn::core::common { using pattern_type = case_pattern_t; using bound_args_tuple = pat::base::binding_args_t; + using full_invoke_args_tuple = decltype(std::tuple_cat( + std::declval>(), + std::declval())); + public: static constexpr bool value = - decltype(detail::is_applicable_impl( - nullptr))::value; + decltype(detail:: + is_applicable_impl( + nullptr))::value; }; template @@ -118,16 +125,21 @@ namespace ptn::core::common { using pattern_type = case_pattern_t; using bound_args_tuple = pat::base::binding_args_t; + using full_invoke_args_tuple = decltype(std::tuple_cat( + std::declval>(), + std::declval())); + public: using type = decltype(std::apply( - std::declval(), std::declval())); + std::declval(), std::declval())); }; template using case_result_t = typename case_result::type; - /// @brief Helper to deduce the result type of an `otherwise` handler. namespace detail { + + /// @brief Helper to deduce the result type of an `otherwise` handler. template static constexpr auto get_otherwise_result_impl(int) -> decltype(std::declval()(std::declval())); @@ -135,6 +147,7 @@ namespace ptn::core::common { template static constexpr auto get_otherwise_result_impl(...) -> decltype(std::declval()()); + } // namespace detail template diff --git a/include/ptn/core/common/eval.hpp b/include/ptn/core/common/eval.hpp index 8171a59c..c2eaf1c6 100644 --- a/include/ptn/core/common/eval.hpp +++ b/include/ptn/core/common/eval.hpp @@ -14,6 +14,44 @@ namespace ptn::core::common { + // --- SFINAE Helper for C++17 --- + namespace detail { + // A helper to provide a `void` type for SFINAE. + template + struct void_type { + using type = void; + }; + + // Detects if `p.match(s)` is a valid expression. + template + struct has_match_member : std::false_type {}; + + template + struct has_match_member< + P, + S, + typename void_type().match( + std::declval()))>::type> : std::true_type {}; + + template + constexpr bool has_match_member_v = has_match_member::value; + + // Detects if `p.bind(s)` is a valid expression. + template + struct has_bind_member : std::false_type {}; + + template + struct has_bind_member< + P, + S, + typename void_type().bind( + std::declval()))>::type> : std::true_type {}; + + template + constexpr bool has_bind_member_v = has_bind_member::value; + } // namespace detail + // --------------------------------- + /** Case Matching */ /// @brief Checks if a case's pattern matches a subject. @@ -27,20 +65,32 @@ namespace ptn::core::common { static constexpr bool matches(const case_type &c, const subject_type &subject) { // Supports both class-based patterns with a .match() method and - // functional patterns. - if constexpr (std::is_member_function_pointer_v< - decltype(&pattern_type::match)>) { + // functional patterns (via std::invoke). +#if PTN_USE_CONCEPTS + if constexpr (requires(const pattern_type &p, const subject_type &s) { + { p.match(s) } -> std::convertible_to; + }) { + return c.pattern.match(subject); + } + else { + return static_cast(std::invoke(c.pattern, subject)); + } +#else + // Use a robust SFINAE check instead of the faulty member function + // pointer check. + if constexpr (detail::has_match_member_v) { return c.pattern.match(subject); } else { return static_cast(std::invoke(c.pattern, subject)); } +#endif } }; /** Handler Invocation Logic */ - /// @brief Invokes a handler with values bound by its pattern. + /// @brief Invokes a handler with the subject and values bound by its pattern. template constexpr decltype(auto) invoke_handler(const Case &c, Subject &&subject) { using pattern_type = case_pattern_t; @@ -54,11 +104,9 @@ namespace ptn::core::common { "Pattern must have a 'bind(subject)' method that returns a tuple of " "bound values."); #else - // C++17 SFINAE trick to check if `bind` is a valid expression. + // Use the robust SFINAE check instead of the faulty sizeof trick. static_assert( - sizeof( - decltype(std::declval().bind(std::declval()), void())) != - 0, + detail::has_bind_member_v, "Pattern must have a 'bind(subject)' method that returns a tuple of " "bound values."); #endif @@ -66,8 +114,13 @@ namespace ptn::core::common { // Let the pattern perform the binding. auto bound_values = c.pattern.bind(std::forward(subject)); - // Apply the handler to the bound values. - return std::apply(c.handler, std::move(bound_values)); + // Combine the subject and bound values into a single tuple. + auto full_args = std::tuple_cat( + std::forward_as_tuple(std::forward(subject)), + std::move(bound_values)); + + // Apply the handler to the full set of arguments. + return std::apply(c.handler, std::move(full_args)); } /** Case Sequence Evaluation */ diff --git a/include/ptn/core/dsl/operators.hpp b/include/ptn/core/dsl/operators.hpp deleted file mode 100644 index 3d773408..00000000 --- a/include/ptn/core/dsl/operators.hpp +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once - -#include -#include - -namespace ptn::core::dsl::detail { - template - struct case_expr; -} - -namespace ptn::pattern::value { - - template - struct predicate_pattern; - -} // namespace ptn::pattern::value - -namespace ptn::pattern::value::detail { - - template - struct and_pattern; - - template - struct or_pattern; - - template - struct not_pattern; - -} // namespace ptn::pattern::value::detail - -namespace ptn::pattern::detail { - template - struct is_pattern; - - template - extern const bool is_pattern_v; -} // namespace ptn::pattern::detail - -namespace ptn::core::dsl::ops { - - // ========== operator>> (pattern >> handler) ========== - template - constexpr auto operator>>(P &&p, H &&h) { - return detail::case_expr, std::decay_t>{ - std::forward

(p), std::forward(h)}; - } - - // ===================================================== - // Logical DSL operators: && || ! - // ===================================================== - -#if defined(PTN_USE_CONCEPTS) && PTN_USE_CONCEPTS - - template - constexpr auto operator&&(L &&l, R &&r) { - return ptn::value::and_pattern, std::decay_t>( - std::forward(l), std::forward(r)); - } - - template - constexpr auto operator||(L &&l, R &&r) { - return ptn::value::or_pattern, std::decay_t>( - std::forward(l), std::forward(r)); - } - - template - constexpr auto operator!(P &&p) { - return ptn::value::not_pattern>(std::forward

(p)); - } - -#else // C++17 fallback (SFINAE) - - template < - typename L, - typename R, - typename = std::enable_if_t< - pattern::detail::is_pattern_v> && - pattern::detail::is_pattern_v>>> - constexpr auto operator&&(L &&l, R &&r) { - return ptn::pattern::value::detail:: - and_pattern, std::decay_t>( - std::forward(l), std::forward(r)); - } - - template < - typename L, - typename R, - typename = std::enable_if_t< - pattern::detail::is_pattern_v> && - pattern::detail::is_pattern_v>>> - constexpr auto operator||(L &&l, R &&r) { - return ptn::pattern::value::detail:: - or_pattern, std::decay_t>( - std::forward(l), std::forward(r)); - } - - template < - typename P, - typename = - std::enable_if_t>>> - constexpr auto operator!(P &&p) { - return ptn::pattern::value::detail::not_pattern>( - std::forward

(p)); - } - -#endif - -} // namespace ptn::core::dsl::ops \ No newline at end of file diff --git a/include/ptn/core/dsl/ops.hpp b/include/ptn/core/dsl/ops.hpp new file mode 100644 index 00000000..2f9030be --- /dev/null +++ b/include/ptn/core/dsl/ops.hpp @@ -0,0 +1,134 @@ +#pragma once + +/** + * @file ops.hpp + * @brief DSL operators for building case expressions and composing patterns. + */ + +#include +#include +#include +#include + +#include "ptn/pattern/value/predicate.hpp" +#include "ptn/core/dsl/detail/case_expr_impl.hpp" +#include "ptn/pattern/base/pattern_traits.hpp" + +namespace ptn::core::dsl::ops { + + // --- Internal Implementation Details --- + + namespace detail { + + /** + * @brief Detects "value-like" types to enable `pattern >> value` syntax + * sugar. + * + * Only types that are clearly values (arithmetic, enum, string) are + * considered value-like. Other types (lambdas, function objects, function + * pointers) are treated as handlers. + */ + template + struct is_value_like_impl { + using D = std::decay_t; + + static constexpr bool value = std::is_arithmetic_v || + std::is_enum_v || + std::is_same_v || + std::is_same_v || + std::is_convertible_v; + }; + + template + inline constexpr bool is_value_like_v = + is_value_like_impl>::value; + + } // namespace detail + + // --- Operator Overloads --- + + /** + * @brief `operator>>`: Pattern >> Handler / Pattern >> Value + * + * This operator supports two modes: + * 1. If Handler is a "value type" (int, enum, string, etc.), it constructs + * a value-handler that ignores all arguments and always returns that + * value. + * 2. Otherwise, it is treated as a proper handler that can be invoked + * according to the rules in case_result_t. + * + * This prevents lambdas with parameters from being misidentified as values, + * avoiding closure types being passed into std::common_type_t and resolving + * common_type errors. + */ + template + constexpr auto operator>>(Pattern &&pattern, Handler &&handler) { + using P = std::decay_t; + using H = std::decay_t; + + // The Pattern should ideally be a valid pattern, but we do not enforce + // this constraint here. Let case_expr_impl and pattern_traits handle more + // detailed validation. For stricter enforcement, you can add: + // + // static_assert(pat::detail::is_pattern_v

, + // "[patternia]: left operand of >> must be a pattern."); + + if constexpr (detail::is_value_like_v) { + // Pattern >> Value syntax sugar + auto value_handler = [val = std::forward(handler)]( + auto &&...) -> H { return val; }; + + return core::dsl::detail::case_expr{ + std::forward(pattern), std::move(value_handler)}; + } + else { + // Pattern >> Handler + return core::dsl::detail::case_expr{ + std::forward(pattern), std::forward(handler)}; + } + } + + /** + * @brief Pattern logical composition: &&, ||, ! + * + * These operators enable logical composition of patterns using + * `pat::detail::is_pattern_v` from pattern/base/pattern_traits.hpp. + * They are only enabled for actual pattern types. + */ + + /// @brief AND composition: p1 && p2 + template < + typename L, + typename R, + typename = std::enable_if_t< + pat::detail::is_pattern_v> && + pat::detail::is_pattern_v>>> + constexpr auto operator&&(L &&l, R &&r) { + return ptn::pat::value::detail:: + and_pattern, std::decay_t>( + std::forward(l), std::forward(r)); + } + + /// @brief OR composition: p1 || p2 + template < + typename L, + typename R, + typename = std::enable_if_t< + pat::detail::is_pattern_v> && + pat::detail::is_pattern_v>>> + constexpr auto operator||(L &&l, R &&r) { + return ptn::pat::value::detail:: + or_pattern, std::decay_t>( + std::forward(l), std::forward(r)); + } + + /// @brief NOT composition: !p + template < + typename P, + typename = std::enable_if_t>>> + constexpr auto operator!(P &&p) { + return ptn::pat::value::detail::not_pattern>( + std::forward

(p)); + } + +} // namespace ptn::core::dsl::ops diff --git a/include/ptn/core/engine/match.hpp b/include/ptn/core/engine/match.hpp index 617080ca..93a861bd 100644 --- a/include/ptn/core/engine/match.hpp +++ b/include/ptn/core/engine/match.hpp @@ -7,7 +7,7 @@ #include -#include "ptn/core/fwd.h" +#include "ptn/core/engine/detail/builder_impl.hpp" #include "ptn/config.hpp" namespace ptn { @@ -22,7 +22,8 @@ namespace ptn { constexpr auto match(T &&value) { using V = std::decay_t; // Forward into the builder with an empty case list - return core::match_builder::create(V(std::forward(value))); + return core::engine::detail::match_builder::create( + V(std::forward(value))); } /** @@ -34,7 +35,7 @@ namespace ptn { template constexpr auto match(T &&value) -> std::enable_if_t< !std::is_same_v, std::decay_t>, - core::match_builder> { + core::engine::detail::match_builder> { constexpr bool subject_constructible = std::is_constructible_v || std::is_constructible_v; @@ -44,7 +45,7 @@ namespace ptn { "constructed from the given value."); U subject = static_cast(std::forward(value)); - return core::match_builder::create(std::move(subject)); + return core::engine::detail::match_builder::create(std::move(subject)); } } // namespace ptn \ No newline at end of file diff --git a/include/ptn/meta/traits.hpp b/include/ptn/meta/base/traits.hpp similarity index 78% rename from include/ptn/meta/traits.hpp rename to include/ptn/meta/base/traits.hpp index ff21992d..35ef3033 100644 --- a/include/ptn/meta/traits.hpp +++ b/include/ptn/meta/base/traits.hpp @@ -9,20 +9,14 @@ namespace ptn::meta { - //==================================================================== // remove_cvref_t - //==================================================================== - /** * @brief Remove const/volatile qualifiers and references from T. */ template using remove_cvref_t = std::remove_cv_t>; - //==================================================================== // is_spec_of & is_spec_of_v - //==================================================================== - /** * @brief Check whether T is a specialization of a given template. * @@ -41,10 +35,7 @@ namespace ptn::meta { template