diff --git a/src/combinator.rs b/src/combinator.rs index fd007f0d..07c3f941 100644 --- a/src/combinator.rs +++ b/src/combinator.rs @@ -1022,6 +1022,55 @@ impl, U: Clone, E: Error> Parser(pub(crate) A); + +impl, A> Parser for Rewind +where + A: Parser, +{ + type Error = E; + + fn parse_inner( + &self, + debugger: &mut D, + stream: &mut StreamOf, + ) -> PResult + where + Self: Sized, + { + let rewind_from = stream.save(); + match { + #[allow(deprecated)] + debugger.invoke(&self.0, stream) + } { + (errors, Ok((out, alt))) => { + stream.revert(rewind_from); + (errors, Ok((out, alt))) + } + (errors, Err(err)) => (errors, Err(err)), + } + } + + fn parse_inner_verbose( + &self, + d: &mut Verbose, + s: &mut StreamOf, + ) -> PResult { + #[allow(deprecated)] + self.parse_inner(d, s) + } + + fn parse_inner_silent( + &self, + d: &mut Silent, + s: &mut StreamOf, + ) -> PResult { + #[allow(deprecated)] + self.parse_inner(d, s) + } +} + #[cfg(test)] mod tests { use error::Simple; diff --git a/src/lib.rs b/src/lib.rs index 198e5830..b953ade1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -953,6 +953,29 @@ pub trait Parser { } } + /// Parse something and then rewind the state so that it only looks ahead and does not consume input. + /// Chumsky's parsers are always rewind the state when they fail. + /// But this combinator makes a parsers to rewind the state whether it succeeds or fails. + /// A typical use-case of this is that you want to parse something which is not followed by something else. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// let just_numbers = text::digits::<_, Simple>(10) + /// .padded() + /// .then_ignore(none_of("+-*/".chars()).rewind()) + /// .separated_by(just(',')); + /// // 3 is not parsed because it's followed by '+'. + /// assert_eq!(just_numbers.parse("1, 2, 3 + 4"), Ok(vec!["1".to_string(), "2".to_string()])); + /// ``` + fn rewind(self) -> Rewind + where + Self: Sized, + { + Rewind(self) + } + /// Box the parser, yielding a parser that performs parsing through dynamic dispatch. /// /// Boxing a parser might be useful for: