Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/ptn/core/common/optimize.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ namespace ptn::core::common {
std::remove_cv_t<std::remove_reference_t<T>>>::value;

constexpr std::size_t
k_variant_inline_dispatch_alt_threshold = 16;
k_variant_inline_dispatch_alt_threshold = 8;
constexpr std::size_t
k_variant_segmented_dispatch_alt_threshold = 64;
constexpr std::size_t k_variant_dispatch_segment_size = 16;
Expand Down
46 changes: 46 additions & 0 deletions include/ptn/pattern/negation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once

#include <type_traits>
#include <tuple>
#include <utility>

#include "ptn/pattern/base/fwd.h"

namespace ptn::pat {

namespace detail {
template <typename P>
struct negation_pattern : base::pattern_base<negation_pattern<P>> {
P sub;

constexpr explicit negation_pattern(P p) : sub(std::move(p)) {}

template <typename X>
constexpr bool match(X const& x) const
noexcept(noexcept(!sub.match(x))) {
return !sub.match(x);
}

template <typename X>
constexpr auto bind(const X& subj) const {
return std::tuple<>{};
}
};
} // namespace detail

template <typename P>
constexpr auto neg(P&& p) {
return detail::negation_pattern<std::decay_t<P>>(
std::forward<P>(p));
}

} // namespace ptn::pat

namespace ptn::pat::base {

template <typename P, typename Subject>
struct binding_args<ptn::pat::detail::negation_pattern<P>, Subject> {
using type = std::tuple<>;
};

} // namespace ptn::pat::base
4 changes: 4 additions & 0 deletions include/ptn/patternia.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "ptn/pattern/combinator.hpp" // any/all
#include "ptn/pattern/type.hpp" // is<T>, alt<I>
#include "ptn/pattern/pred.hpp" // pred
#include "ptn/pattern/negation.hpp" // neg

namespace ptn {
// Imports DSL operators.
Expand Down Expand Up @@ -70,6 +71,9 @@ namespace ptn {
// Predicate pattern utility.
using ptn::pat::pred;

// Negation pattern utility.
using ptn::pat::neg;

} // namespace ptn

// Optional sugar for the statically cached `on(...)` factory form.
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ set(PTN_COMPILE_FAIL_CASES
compile_fail/all_requires_pattern_args.cpp
compile_fail/static_on_with_capture.cpp
compile_fail/on_pipeline_without_wildcard.cpp
compile_fail/val_requires_compile_time_constant.cpp
)

if(CMAKE_CXX_STANDARD LESS 20)
Expand Down
10 changes: 10 additions & 0 deletions tests/compile_fail/val_requires_compile_time_constant.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include "ptn/patternia.hpp"

// `argc` is a truly runtime value — non-type template parameter must be a
// converted constant expression (C++17 [temp.arg.nontype]/1). The compiler
// will reject this at template argument deduction.
int main(int argc, char**) {
auto pattern = ptn::val<argc>; // expected-error: non-type template argument is not a constant expression
(void)pattern;
return 0;
}
35 changes: 35 additions & 0 deletions tests/tests_combinator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,3 +270,38 @@ TEST(CombinatorPattern, AllInsidePtnOnMacro) {
EXPECT_EQ(run(a), 1);
EXPECT_EQ(run(b), 0);
}

// ===== neg(p) negation pattern =====

TEST(NegationPattern, BasicNegation) {
int a = 1, b = 2;
EXPECT_EQ(match(a) | on(neg(val<1>) >> []{ return 0; },
_ >> []{ return 1; }), 1);
EXPECT_EQ(match(b) | on(neg(val<1>) >> []{ return 0; },
_ >> []{ return 1; }), 0);
}

TEST(NegationPattern, NegWildcardNeverMatches) {
int x = 42;
// neg(_) matches nothing — no subject fails the wildcard
EXPECT_EQ(match(x) | on(neg(_) >> []{ return 0; },
_ >> []{ return 1; }), 1);
}

TEST(NegationPattern, NegWithPred) {
int a = 3, b = 4;
auto is_even = [](int x) { return x % 2 == 0; };
EXPECT_EQ(match(a) | on(neg(pred(is_even)) >> []{ return 0; },
_ >> []{ return 1; }), 0);
EXPECT_EQ(match(b) | on(neg(pred(is_even)) >> []{ return 0; },
_ >> []{ return 1; }), 1);
}

TEST(NegationPattern, NegNegIsIdentity) {
int a = 1, b = 2;
// double negation: neg(neg(p)) matches iff p matches
EXPECT_EQ(match(a) | on(neg(neg(val<1>)) >> []{ return 42; },
_ >> []{ return 0; }), 42);
EXPECT_EQ(match(b) | on(neg(neg(val<2>)) >> []{ return 42; },
_ >> []{ return 0; }), 42);
}