From 93a327b9f4eee0da0c91129af8495259447e0cc9 Mon Sep 17 00:00:00 2001 From: Kevin Mehall Date: Mon, 15 Feb 2021 10:32:54 -0700 Subject: [PATCH] Restore support for `[^ ]` inverted pattern syntax. --- peg-macros/translate.rs | 28 +++++++++------------------- src/lib.rs | 7 +++++-- tests/run-pass/pattern.rs | 12 ++++++++++++ 3 files changed, 26 insertions(+), 21 deletions(-) create mode 100644 tests/run-pass/pattern.rs diff --git a/peg-macros/translate.rs b/peg-macros/translate.rs index cbb5db4..87b07ea 100644 --- a/peg-macros/translate.rs +++ b/peg-macros/translate.rs @@ -343,15 +343,6 @@ fn labeled_seq(context: &Context, exprs: &[TaggedExpr], inner: TokenStream) -> T }) } -fn cond_swap(swap: bool, tup: (T, T)) -> (T, T) { - let (a, b) = tup; - if swap { - (b, a) - } else { - (a, b) - } -} - fn compile_expr(context: &Context, e: &Expr, result_used: bool) -> TokenStream { match e { LiteralExpr(ref s) => { @@ -363,17 +354,16 @@ fn compile_expr(context: &Context, e: &Expr, result_used: bool) -> TokenStream { } PatternExpr(ref pattern_group) => { - let invert = false; - let pattern = pattern_group.stream(); - let pat_str = pattern.to_string(); + let pat_str = pattern_group.to_string(); - let (in_set, not_in_set) = cond_swap( - invert, - ( - quote! { ::peg::RuleResult::Matched(__next, ()) }, - quote! { __err_state.mark_failure(__pos, #pat_str) }, - ), - ); + let success_res = quote! { ::peg::RuleResult::Matched(__next, ()) }; + let failure_res = quote! { __err_state.mark_failure(__pos, #pat_str) }; + + let (pattern, in_set, not_in_set) = if let Some(pattern) = group_check_prefix(pattern_group, '^') { + (pattern, failure_res, success_res) + } else { + (pattern_group.stream(), success_res, failure_res) + }; let in_set_arm = quote!( #pattern => #in_set, ); diff --git a/src/lib.rs b/src/lib.rs index be1be86..5e8a846 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,9 @@ //! //! * `"keyword"` - _Literal:_ match a literal string. //! * `['0'..='9']` - _Pattern:_ match a single element that matches a Rust `match`-style -//! pattern. [(details)](#match-expressions) +//! pattern. [(details)](#pattern-expressions) +//! * `[^ '0'..='9']` - _Inverted pattern:_ match a single element that does not match a Rust `match`-style +//! pattern. [(details)](#pattern-expressions) //! * `some_rule()` - _Rule:_ match a rule defined elsewhere in the grammar and return its //! result. Arguments in the parentheses are Rust expressions. //! * `_` or `__` or `___` - _Rule (underscore):_ As a special case, rule names @@ -130,7 +132,8 @@ //! //! The `[pat]` syntax expands into a [Rust `match` //! pattern](https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html) against the next character -//! (or element) of the input. +//! (or element) of the input. When the pattern begins with `^`, the matching behavior is inverted: +//! the expression succeeds only if the pattern does *not* match. //! //! To match sets of characters, use Rust's `..=` inclusive range pattern //! syntax and `|` to match multiple patterns. For example `['a'..='z' | 'A'..='Z']` matches an diff --git a/tests/run-pass/pattern.rs b/tests/run-pass/pattern.rs new file mode 100644 index 0000000..c3fadc7 --- /dev/null +++ b/tests/run-pass/pattern.rs @@ -0,0 +1,12 @@ +peg::parser!( grammar test() for str { + pub rule alphanumeric() = ['a'..='z' | 'A'..='Z' | '0'..='9']* + pub rule inverted_pat() -> &'input str = "(" s:$([^')']*) ")" {s} +}); + +fn main() { + assert!(test::alphanumeric("azAZ09").is_ok()); + assert!(test::alphanumeric("@").is_err()); + + assert_eq!(test::inverted_pat("(asdf)"), Ok("asdf")); +} +