From 1a1123f5b6970c88d99643d87d75c8fb63117607 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 4 Aug 2018 17:15:02 +0200 Subject: [PATCH] feat: Add the `opaque` parser --- src/parser/combinator.rs | 124 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/src/parser/combinator.rs b/src/parser/combinator.rs index 1d373364..e877b645 100644 --- a/src/parser/combinator.rs +++ b/src/parser/combinator.rs @@ -986,3 +986,127 @@ where [ }) } } + +#[derive(Copy, Clone)] +pub struct Opaque(F, PhantomData O>); +impl Parser for Opaque +where + I: Stream, + S: Default, + F: FnMut(&mut FnMut(&mut Parser)), +{ + type Input = I; + type Output = O; + type PartialState = S; + + fn parse_stream_consumed(&mut self, input: &mut Self::Input) -> ConsumedResult { + let mut x = None; + (self.0)(&mut |parser| x = Some(parser.parse_stream_consumed(input))); + x.expect("Parser") + } + + fn parse_lazy(&mut self, input: &mut Self::Input) -> ConsumedResult { + let mut x = None; + (self.0)(&mut |parser| x = Some(parser.parse_lazy(input))); + x.expect("Parser") + } + + parse_mode!(); + + fn parse_mode_impl( + &mut self, + mode: M, + input: &mut Self::Input, + state: &mut Self::PartialState, + ) -> ConsumedResult + where + M: ParseMode, + { + let mut x = None; + (self.0)(&mut |parser| { + x = Some(if mode.is_first() { + parser.parse_first(input, state) + } else { + parser.parse_partial(input, state) + }) + }); + x.expect("Parser") + } + + fn add_error(&mut self, errors: &mut Tracked<::Error>) { + (self.0)(&mut |parser| parser.add_error(errors)); + } + + fn add_consumed_expected_error( + &mut self, + errors: &mut Tracked<::Error>, + ) { + (self.0)(&mut |parser| parser.add_consumed_expected_error(errors)); + } +} + +/// Alias over `Opaque` where the function can be a plain function pointer (does not need to +/// capture any values) +pub type FnOpaque = + Opaque)), I, O, S>; + +/// Creates a parser from a function which takes a function that are given the actual parser. +/// Though convoluted this makes it possible to hide the concrete parser type without `Box` or +/// losing the full information about the parser as is the case of [`parser`][]. +/// +/// Since this hides the type this can also be useful for writing mutually recursive `impl Parser` +/// parsers to break the otherwise arbitrarily large type that rustc creates internally. +/// +/// If you need a more general version (that does not need trait objects) try the [`parser!`][] +/// macro. +/// +/// ``` +/// # #[macro_use] +/// # extern crate combine; +/// # use combine::combinator::{FnOpaque, opaque, no_partial}; +/// # use combine::parser::char::{char, digit}; +/// # use combine::*; +/// +/// # fn main() { +/// +/// #[derive(PartialEq, Debug)] +/// enum Expr { +/// Number(i64), +/// Pair(Box, Box), +/// } +/// +/// fn expr() -> FnOpaque +/// where +/// I: Stream, +/// I::Error: ParseError, +/// { +/// opaque(|f| { +/// // `no_partial` disables partial parsing and replaces the partial state with `()`, +/// // letting us avoid naming that type +/// f(&mut no_partial(choice(( +/// from_str(many1::(digit())) +/// .map(Expr::Number), +/// (char('('), expr(), char(','), expr(), char(')')) +/// .map(|(_, l, _, r, _)| Expr::Pair(Box::new(l), Box::new(r))) +/// )))) +/// }) +/// } +/// +/// assert_eq!( +/// expr().easy_parse("123"), +/// Ok((Expr::Number(123), "")) +/// ); +/// +/// # } +/// ``` +/// +/// [`parser`]: ../function/fn.parser.html +/// [`parser!`]: ../../macro.parser.html +pub fn opaque(f: F) -> Opaque +where + I: Stream, + S: Default, + F: FnMut(&mut FnMut(&mut Parser)), +{ + Opaque(f, PhantomData) +}