diff --git a/src/parser/sequence.rs b/src/parser/sequence.rs index 441fc064..5a6b6d4e 100644 --- a/src/parser/sequence.rs +++ b/src/parser/sequence.rs @@ -895,13 +895,96 @@ where #[derive(Copy, Clone)] pub struct Loop(F, S); -impl Parser for Loop +impl Parser for Loop +where + Input: Stream, + F: FnMut(&mut S) -> P, + S: Clone, + P: Parser, + G: FnOnce(&mut S) +{ + type Output = S; + type PartialState = (Option, Option

, bool, P::PartialState); + + parse_mode!(Input); + #[inline] + fn parse_mode_impl( + &mut self, + mode: M, + input: &mut Input, + state: &mut Self::PartialState, + ) -> ParseResult::Error> + where + M: ParseMode, + { + let Self(ref mut next_func, ref init_state) = *self; + LoopGen(next_func, || init_state.clone()).parse_mode_impl(mode, input, state) + } +} + +// Takes a function `func` and initial `state`. Function is applied to current +// state and generates a parser outputting function to update the state. This +// is repeated until the generated parser fails. +// +/// ``` +/// # extern crate combine; +/// # use std::collections::HashMap; +/// # use combine::{Parser, Stream, many1, token, value, unexpected_any, optional, choice}; +/// # use combine::parser::char::digit; +/// # use combine::parser::sequence::loop_parser; +/// # fn main() { +/// // Parses 'a', 'b' and 'c' such that there is no consecutive letters returning their count +/// #[derive(PartialEq, Eq, Clone, Hash)] +/// enum Token { A, B, C } +/// fn token_parser(last_token: &Option) -> impl Parser +/// where +/// Input: Stream +/// { +/// let mut choices = vec![]; +/// if *last_token != Some(Token::A) { +/// choices.push(token('a').map(|_| Token::A).left()); +/// } +/// if *last_token != Some(Token::B) { +/// choices.push(token('b').map(|_| Token::B).left().right()); +/// } +/// if *last_token != Some(Token::C) { +/// choices.push(token('c').map(|_| Token::C).right().right()); +/// } +/// choice(choices) +/// } +/// let result = loop_parser((HashMap::::new(), None), |(_, last_token)| { +/// token_parser(last_token).map(|current_token| move |(ref mut acc, ref mut last_token): &mut (HashMap::, Option)| { +/// *acc.entry(current_token.clone()).or_insert(0) += 1; +/// *last_token = Some(current_token); +/// }) +/// }).map(|x| x.0).parse("ababacbcbcaa"); +/// assert_eq!(result.as_ref().map(|x| x.0.get(&Token::A)), Ok(Some(&4))); +/// assert_eq!(result.as_ref().map(|x| x.0.get(&Token::B)), Ok(Some(&4))); +/// assert_eq!(result.as_ref().map(|x| x.0.get(&Token::C)), Ok(Some(&3))); +/// assert_eq!(result.as_ref().map(|x| x.1), Ok("a")); +/// # } +/// ``` +pub fn loop_parser(state: S, func: F) -> Loop where Input: Stream, F: FnMut(&mut S) -> P, P: Parser, G: FnOnce(&mut S), S: Clone +{ + Loop(func, state) +} + +#[derive(Copy, Clone)] +pub struct LoopGen(F, G); + +impl Parser for LoopGen +where + Input: Stream, + F: FnMut(&mut S) -> P, + G: FnMut() -> S, + P: Parser, + H: FnOnce(&mut S) { type Output = S; type PartialState = (Option, Option

, bool, P::PartialState); @@ -917,13 +1000,13 @@ where where M: ParseMode, { - let Self(ref mut next_func, ref init_state) = *self; + let Self(ref mut next_func, ref mut state_gen) = *self; let (ref mut state, ref mut parser, ref mut committed, ref mut partial_state) = *state; if mode.is_first() { debug_assert!(state.is_none()); debug_assert!(parser.is_none()); debug_assert!(!*committed); - *state = Some(init_state.clone()); + *state = Some(state_gen()); *parser = Some(next_func(state.as_mut().unwrap())); } let parser = parser.as_mut().unwrap(); @@ -957,20 +1040,16 @@ where } } - -// If `init` is not `None` it parses using it first. Than subsequently -// applies `func` to result until `None` is returned. The result is -// the last state. -// -// Otherwise, if `init` is `None`, it returns the `state` without -// consuming any input. +// Takes a function `func` and initial `state`. Function is applied to current +// state and generates a parser outputting function to update the state. This +// is repeated until the generated parser fails. // /// ``` /// # extern crate combine; /// # use std::collections::HashMap; /// # use combine::{Parser, Stream, many1, token, value, unexpected_any, optional, choice}; /// # use combine::parser::char::digit; -/// # use combine::parser::sequence::loop_parser; +/// # use combine::parser::sequence::loop_gen; /// # fn main() { /// // Parses 'a', 'b' and 'c' such that there is no consecutive letters returning their count /// #[derive(PartialEq, Eq, Clone, Hash)] @@ -991,7 +1070,7 @@ where /// } /// choice(choices) /// } -/// let result = loop_parser((HashMap::::new(), None), |(_, last_token)| { +/// let result = loop_gen(|| (HashMap::::new(), None), |(_, last_token)| { /// token_parser(last_token).map(|current_token| move |(ref mut acc, ref mut last_token): &mut (HashMap::, Option)| { /// *acc.entry(current_token.clone()).or_insert(0) += 1; /// *last_token = Some(current_token); @@ -1003,13 +1082,13 @@ where /// assert_eq!(result.as_ref().map(|x| x.1), Ok("a")); /// # } /// ``` -pub fn loop_parser(state: S, func: F) -> Loop +pub fn loop_gen(state_gen: G, func: F) -> LoopGen where Input: Stream, F: FnMut(&mut S) -> P, - P: Parser, - G: FnOnce(&mut S), - S: Clone + G: FnMut() -> S, + P: Parser, + H: FnOnce(&mut S) { - Loop(func, state) + LoopGen(func, state_gen) }