Skip to content

Commit

Permalink
fixup! Add loop_parser
Browse files Browse the repository at this point in the history
  • Loading branch information
uzytkownik committed Jun 29, 2021
1 parent 0af270c commit 637379a
Showing 1 changed file with 96 additions and 17 deletions.
113 changes: 96 additions & 17 deletions src/parser/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -895,13 +895,96 @@ where
#[derive(Copy, Clone)]
pub struct Loop<F, S>(F, S);

impl<Input, F, P, G, S> Parser<Input> for Loop<F, S>
impl<Input, F, S, P, G> Parser<Input> for Loop<F, S>
where
Input: Stream,
F: FnMut(&mut S) -> P,
S: Clone,
P: Parser<Input, Output = G>,
G: FnOnce(&mut S)
{
type Output = S;
type PartialState = (Option<S>, Option<P>, bool, P::PartialState);

parse_mode!(Input);
#[inline]
fn parse_mode_impl<M>(
&mut self,
mode: M,
input: &mut Input,
state: &mut Self::PartialState,
) -> ParseResult<Self::Output, <Input as StreamOnce>::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<Input>(last_token: &Option<Token>) -> impl Parser<Input, Output = Token>
/// where
/// Input: Stream<Token = char>
/// {
/// 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::<Token, usize>::new(), None), |(_, last_token)| {
/// token_parser(last_token).map(|current_token| move |(ref mut acc, ref mut last_token): &mut (HashMap::<Token, usize>, Option<Token>)| {
/// *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<Input, F, P, G, S>(state: S, func: F) -> Loop<F, S>
where
Input: Stream,
F: FnMut(&mut S) -> P,
P: Parser<Input, Output = G>,
G: FnOnce(&mut S),
S: Clone
{
Loop(func, state)
}

#[derive(Copy, Clone)]
pub struct LoopGen<F, G>(F, G);

impl<Input, F, G, S, P, H> Parser<Input> for LoopGen<F, G>
where
Input: Stream,
F: FnMut(&mut S) -> P,
G: FnMut() -> S,
P: Parser<Input, Output = H>,
H: FnOnce(&mut S)
{
type Output = S;
type PartialState = (Option<S>, Option<P>, bool, P::PartialState);
Expand All @@ -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();
Expand Down Expand Up @@ -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)]
Expand All @@ -991,7 +1070,7 @@ where
/// }
/// choice(choices)
/// }
/// let result = loop_parser((HashMap::<Token, usize>::new(), None), |(_, last_token)| {
/// let result = loop_gen(|| (HashMap::<Token, usize>::new(), None), |(_, last_token)| {
/// token_parser(last_token).map(|current_token| move |(ref mut acc, ref mut last_token): &mut (HashMap::<Token, usize>, Option<Token>)| {
/// *acc.entry(current_token.clone()).or_insert(0) += 1;
/// *last_token = Some(current_token);
Expand All @@ -1003,13 +1082,13 @@ where
/// assert_eq!(result.as_ref().map(|x| x.1), Ok("a"));
/// # }
/// ```
pub fn loop_parser<Input, F, P, G, S>(state: S, func: F) -> Loop<F, S>
pub fn loop_gen<Input, F, G, S, P, H>(state_gen: G, func: F) -> LoopGen<F, G>
where
Input: Stream,
F: FnMut(&mut S) -> P,
P: Parser<Input, Output = G>,
G: FnOnce(&mut S),
S: Clone
G: FnMut() -> S,
P: Parser<Input, Output = H>,
H: FnOnce(&mut S)
{
Loop(func, state)
LoopGen(func, state_gen)
}

0 comments on commit 637379a

Please sign in to comment.