Skip to content

Commit ffde67f

Browse files
committed
Add while_parser
1 parent 9f0b9bd commit ffde67f

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed

src/parser/sequence.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,3 +891,123 @@ where
891891
{
892892
ThenRef(p, f)
893893
}
894+
895+
#[derive(Copy, Clone)]
896+
pub struct While<P, F, S>(Option<P>, F, S);
897+
898+
impl<Input, P, F, S> Parser<Input> for While<P, F, S>
899+
where
900+
Input: Stream,
901+
P: Parser<Input>,
902+
F: FnMut(&mut S, P::Output) -> Option<P>,
903+
S: Clone
904+
{
905+
type Output = S;
906+
type PartialState = (Option<S>, Option<P>, bool, P::PartialState);
907+
908+
parse_mode!(Input);
909+
#[inline]
910+
fn parse_mode_impl<M>(
911+
&mut self,
912+
mut mode: M,
913+
input: &mut Input,
914+
state: &mut Self::PartialState,
915+
) -> ParseResult<Self::Output, <Input as StreamOnce>::Error>
916+
where
917+
M: ParseMode,
918+
{
919+
let While(ref mut init_parser, ref mut func, ref init_state) = *self;
920+
let (ref mut state, ref mut parser, ref mut committed, ref mut partial_state) = *state;
921+
// state == None means we haven't run func yet so we need to depend on init_parser
922+
let mut current_parser_opt: &mut Option<P> = if mode.is_first() || state.is_none() {
923+
debug_assert!(state.is_none());
924+
debug_assert!(parser.is_none());
925+
init_parser
926+
} else {
927+
debug_assert!(state.is_some());
928+
parser
929+
};
930+
while let Some(ref mut current_parser) = *current_parser_opt {
931+
let result = current_parser.parse_mode_impl(mode, input, partial_state);
932+
let value = match result {
933+
CommitOk(next_value) => {
934+
*committed = true;
935+
next_value
936+
},
937+
PeekOk(next_value) => next_value,
938+
CommitErr(e) => return CommitErr(e),
939+
PeekErr(e) => if *committed {
940+
return CommitErr(e.error)
941+
} else {
942+
return PeekErr(e)
943+
}
944+
};
945+
if state.is_none() {
946+
*state = Some(init_state.clone());
947+
}
948+
*parser = func(state.as_mut().unwrap(), value);
949+
current_parser_opt = parser;
950+
mode.set_first();
951+
}
952+
let result = state.as_ref().unwrap_or(init_state).clone();
953+
if *committed {
954+
CommitOk(result)
955+
} else {
956+
PeekOk(result)
957+
}
958+
}
959+
}
960+
961+
962+
// If `init` is not `None` it parses using it first. Than subsequently
963+
// applies `func` to result until `None` is returned. The result is
964+
// the last state.
965+
//
966+
// Otherwise, if `init` is `None`, it returns the `state` without
967+
// consuming any input.
968+
//
969+
/// ```
970+
/// # extern crate combine;
971+
/// # use std::collections::HashMap;
972+
/// # use combine::{Parser, Stream, many1, token, value, unexpected_any, optional, choice};
973+
/// # use combine::parser::char::digit;
974+
/// # use combine::parser::sequence::while_parser;
975+
/// # fn main() {
976+
/// // Parses 'a', 'b' and 'c' such that there is no consecutive letters returning their count
977+
/// #[derive(PartialEq, Eq, Copy, Clone, Hash)]
978+
/// enum Token { A, B, C }
979+
/// fn token_parser<Input>(last_token: Option<Token>) -> impl Parser<Input, Output = Option<Token>>
980+
/// where
981+
/// Input: Stream<Token = char>
982+
/// {
983+
/// let mut choices = vec![];
984+
/// if last_token != Some(Token::A) {
985+
/// choices.push(token('a').map(|_| Token::A).left());
986+
/// }
987+
/// if last_token != Some(Token::B) {
988+
/// choices.push(token('b').map(|_| Token::B).left().right());
989+
/// }
990+
/// if last_token != Some(Token::C) {
991+
/// choices.push(token('c').map(|_| Token::C).right().right());
992+
/// }
993+
/// optional(choice(choices))
994+
/// }
995+
/// let result = while_parser(Some(token_parser(None)), |acc, token_opt| token_opt.map(|token| {
996+
/// *acc.entry(token).or_insert(0) += 1;
997+
/// token_parser(token_opt)
998+
/// }), HashMap::<Token, usize>::new()).parse("ababacbcbcaa");
999+
/// assert_eq!(result.as_ref().map(|x| x.0.get(&Token::A)), Ok(Some(&4)));
1000+
/// assert_eq!(result.as_ref().map(|x| x.0.get(&Token::B)), Ok(Some(&4)));
1001+
/// assert_eq!(result.as_ref().map(|x| x.0.get(&Token::C)), Ok(Some(&3)));
1002+
/// assert_eq!(result.as_ref().map(|x| x.1), Ok("a"));
1003+
/// # }
1004+
/// ```
1005+
pub fn while_parser<Input, P, F, S>(init: Option<P>, func: F, state: S) -> While<P, F, S>
1006+
where
1007+
Input: Stream,
1008+
P: Parser<Input>,
1009+
F: FnMut(&mut S, P::Output) -> Option<P>,
1010+
S: Clone
1011+
{
1012+
While(init, func, state)
1013+
}

0 commit comments

Comments
 (0)