From 2b6532e04bdbc0c6a5c513dcf3118b1af0454327 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Mon, 10 Feb 2020 18:06:39 -0800 Subject: [PATCH] Add 'try_collect'. --- lib/src/combinators.rs | 36 +++++++++++++++++++++++++++++++++--- lib/tests/parsers.rs | 2 +- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/lib/src/combinators.rs b/lib/src/combinators.rs index 9733a79..a722a23 100644 --- a/lib/src/combinators.rs +++ b/lib/src/combinators.rs @@ -1,7 +1,7 @@ use std::hash::Hash; use std::collections::{HashMap, BTreeMap}; -use crate::input::{Input, Token, Result}; +use crate::input::{Input, Rewind, Token, Result}; use crate::macros::parser; use crate::parsers::*; @@ -76,7 +76,7 @@ pub fn surrounded(input: &mut I, mut p: P, mut f: F) -> Result } /// Parses as many `p` as possible until EOF is reached, collecting them into a -/// `C`. `C` may be empty. +/// `C`. Fails if `p` every fails. `C` may be empty. #[parser(raw)] pub fn collect(input: &mut I, mut p: P) -> Result where C: Collection, I: Input, P: FnMut(&mut I) -> Result @@ -92,7 +92,7 @@ pub fn collect(input: &mut I, mut p: P) -> Result } /// Parses as many `p` as possible until EOF is reached, collecting them into a -/// `C`. `C` is not allowed to be empty. +/// `C`. Fails if `p` ever fails. `C` is not allowed to be empty. #[parser(raw)] pub fn collect_some(input: &mut I, mut p: P) -> Result where C: Collection, I: Input, P: FnMut(&mut I) -> Result @@ -106,6 +106,36 @@ pub fn collect_some(input: &mut I, mut p: P) -> Result } } +/// Parses as many `p` as possible until EOF is reached or `p` fails, collecting +/// them into a `C`. `C` may be empty. +#[parser(raw)] +pub fn try_collect(input: &mut I, mut p: P) -> Result + where C: Collection, I: Input + Rewind, P: FnMut(&mut I) -> Result +{ + let mut collection = C::new(); + loop { + if eof(input).is_ok() { + return Ok(collection); + } + + // FIXME: We should be able to call `parse_marker!` here. + let start = input.mark(&crate::input::ParserInfo { + name: "try_collect", + raw: true + }); + + match p(input) { + Ok(val) => collection.add(val), + Err(_) => { + input.rewind_to(&start); + break; + } + } + } + + Ok(collection) +} + /// Parses many `separator` delimited `p`s, the entire collection of which must /// start with `start` and end with `end`. `item` Gramatically, this is: /// diff --git a/lib/tests/parsers.rs b/lib/tests/parsers.rs index 62a7536..833a13c 100644 --- a/lib/tests/parsers.rs +++ b/lib/tests/parsers.rs @@ -99,7 +99,7 @@ fn test_window_termination() { let result = take_while_window(&mut Text::from("aa"), 2, |_| false); assert_eq!(result.unwrap(), ""); - let result = take_some_while_window(&mut Text::from("a"), 2, |_| false); + let result = take_some_while_some_window(&mut Text::from("a"), 2, |_| false); assert!(result.is_err()); let result = take_some_while_window(&mut Text::from("aa"), 2, |_| false);