From 597fb735710a6cc1bf02919334c3d82718314cfe Mon Sep 17 00:00:00 2001 From: Simon Guest Date: Fri, 17 Nov 2023 12:06:36 +1300 Subject: [PATCH] Add not_ref() --- src/combinator.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 32 +++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/src/combinator.rs b/src/combinator.rs index a8956cf4..d531c68c 100644 --- a/src/combinator.rs +++ b/src/combinator.rs @@ -2085,6 +2085,54 @@ where go_extra!(()); } +/// See [`Parser::not_ref`]. +pub struct NotRef { + pub(crate) parser: A, + #[allow(dead_code)] + pub(crate) phantom: EmptyPhantom, +} + +impl Copy for NotRef {} +impl Clone for NotRef { + fn clone(&self) -> Self { + Self { + parser: self.parser.clone(), + phantom: EmptyPhantom::new(), + } + } +} + +impl<'a, I, E, A, OA> ParserSealed<'a, I, (), E> for NotRef +where + I: BorrowInput<'a>, + E: ParserExtra<'a, I>, + A: Parser<'a, I, OA, E>, +{ + #[inline(always)] + fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { + let before = inp.save(); + + let alt = inp.errors.alt.take(); + + let result = self.parser.go::(inp); + let result_span = inp.span_since(before.offset()); + inp.rewind(before); + + inp.errors.alt = alt; + + match result { + Ok(()) => { + let (at, found) = inp.next_ref_inner(); + inp.add_alt(at, None, found.map(|f| f.into()), result_span); + Err(()) + } + Err(()) => Ok(M::bind(|| ())), + } + } + + go_extra!(()); +} + /// See [`Parser::and_is`]. pub struct AndIs { pub(crate) parser_a: A, diff --git a/src/lib.rs b/src/lib.rs index 67190ad8..f77f09f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1421,6 +1421,38 @@ pub trait Parser<'a, I: Input<'a>, O, E: ParserExtra<'a, I> = extra::Default>: } } + /// Invert the result of the contained parser, failing if it succeeds and succeeding if it fails. + /// The output of this parser is always `()`, the unit type. + /// + /// This function is the borrowing equivalent of [`Parser::not`]. Where possible, it's recommended to use [`Parser::not`] instead. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let not_dashes = any_ref::<_, extra::Err>>() + /// .and_is(just('-').not_ref()) + /// .map(|c| *c) + /// .repeated() + /// .collect::(); + /// + /// let v1 = "abcdef".chars().collect::>(); + /// assert_eq!(not_dashes.parse(v1.as_slice()).into_result(), Ok("abcdef".to_string())); + /// let v2 = "09Qr-X-*&".chars().collect::>(); + /// assert!(not_dashes.parse(v2.as_slice()).has_errors()); + /// let v3 = "-91024".chars().collect::>(); + /// assert!(not_dashes.parse(v3.as_slice()).has_errors()); + /// ``` + fn not_ref(self) -> NotRef + where + Self: Sized, + { + NotRef { + parser: self, + phantom: EmptyPhantom::new(), + } + } + /// Parse a pattern zero or more times (analog to Regex's `*`). /// /// Input is eagerly parsed. Be aware that the parser will accept no occurrences of the pattern too. Consider using