diff --git a/src/choice.rs b/src/choice.rs index f037cfb..9577e77 100644 --- a/src/choice.rs +++ b/src/choice.rs @@ -1,21 +1,19 @@ use crate::Parser; -pub fn choice<'a, I, O, E>(parsers: &'a [&'a dyn Parser]) -> impl Parser + 'a -where - I: Clone, -{ - move |input: I| { +pub fn choice<'a, I, O, E>(parsers: &'a [&'a dyn Parser]) -> impl Parser + 'a { + move |mut input: I| { //TODO need a more principled way to return an error when no choices work let mut err = None; for parser in parsers.iter() { - match parser.parse(input.clone()) { + match parser.parse(input) { Ok(res) => return Ok(res), - Err(e) => { + Err((e, rest)) => { err = Some(e); + input = rest; } } } - Err(err.unwrap()) + Err((err.unwrap(), input)) } } diff --git a/src/combinators.rs b/src/combinators.rs index a505427..3e0aee6 100644 --- a/src/combinators.rs +++ b/src/combinators.rs @@ -3,15 +3,22 @@ use crate::Parser; pub fn repeated(parser: P) -> impl Parser, E> where P: Parser, - I: Copy, { move |input: I| { let mut acc = input; let mut results = vec![]; - while let Ok((item, rest)) = parser.parse(acc) { - results.push(item); - acc = rest; + loop { + match parser.parse(acc) { + Ok((item, rest)) => { + results.push(item); + acc = rest; + } + Err((_err, rest)) => { + acc = rest; + break; + } + } } Ok((results, acc)) } diff --git a/src/lib.rs b/src/lib.rs index 41eb741..30ca740 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ pub use map::*; pub use primitives::*; pub use sequence::*; -type ParseResult = Result<(O, I), E>; +type ParseResult = Result<(O, I), (E, I)>; pub trait Parser { fn parse(&self, input: I) -> ParseResult; @@ -51,7 +51,7 @@ mod tests { let a = literal("bongo"); let b = literal("sucy"); let c = literal("ara"); - let inputs = [&a as &dyn Parser<&str, &str, &str>, &b, &c]; + let inputs = [&a as &dyn Parser<&str, &str, ()>, &b, &c]; let parser = choice(&inputs); let output = parser.parse("ara hajimete").unwrap(); diff --git a/src/primitives.rs b/src/primitives.rs index c5e1622..8101175 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -1,16 +1,16 @@ use crate::{ParseResult, Parser}; -pub fn literal(expected: &'static str) -> impl Fn(&str) -> ParseResult<&str, &str, &str> { +pub fn literal(expected: &'static str) -> impl Fn(&str) -> ParseResult<&str, &str, ()> { move |input| match input.get(0..expected.len()) { Some(next) if next == expected => Ok((next, &input[expected.len()..])), - _ => Err(input), + _ => Err(((), input)), } } -pub fn literal_char<'a>(expected: char) -> impl Parser<&'a str, char, &'a str> { +pub fn literal_char<'a>(expected: char) -> impl Parser<&'a str, char, ()> { move |input: &'a str| match input.chars().next() { Some(ch) if ch == expected => Ok((expected, &input[ch.len_utf8()..])), - _ => Err(input), + _ => Err(((), input)), } }