Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewind parser #33

Merged
merged 4 commits into from Nov 25, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/combinator.rs
Expand Up @@ -1022,6 +1022,55 @@ impl<I: Clone, O, A: Parser<I, O, Error = E>, U: Clone, E: Error<I>> Parser<I, U
}
}

/// See [`Parser::rewind`].
pub struct Rewind<A>(pub(crate) A);

impl<I: Clone, O, E: Error<I>, A> Parser<I, O> for Rewind<A>
where
A: Parser<I, O, Error = E>,
{
type Error = E;

fn parse_inner<D: Debugger>(
&self,
debugger: &mut D,
stream: &mut StreamOf<I, Self::Error>,
) -> PResult<I, O, Self::Error>
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<I, Self::Error>,
) -> PResult<I, O, Self::Error> {
#[allow(deprecated)]
self.parse_inner(d, s)
}

fn parse_inner_silent(
&self,
d: &mut Silent,
s: &mut StreamOf<I, Self::Error>,
) -> PResult<I, O, Self::Error> {
#[allow(deprecated)]
self.parse_inner(d, s)
}
}

#[cfg(test)]
mod tests {
use error::Simple;
Expand Down
23 changes: 23 additions & 0 deletions src/lib.rs
Expand Up @@ -953,6 +953,29 @@ pub trait Parser<I: Clone, O> {
}
}

/// 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<char>>(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<Self>
Comment on lines +956 to +972
Copy link
Contributor Author

@ryo33 ryo33 Nov 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed it to rewind, and made the documentation slightly better.

where
Self: Sized,
{
Rewind(self)
}

/// Box the parser, yielding a parser that performs parsing through dynamic dispatch.
///
/// Boxing a parser might be useful for:
Expand Down