diff --git a/src/choice.rs b/src/choice.rs index 18c953d..ec01c0d 100644 --- a/src/choice.rs +++ b/src/choice.rs @@ -147,12 +147,15 @@ where #[cfg(test)] mod tests { use super::*; - use crate::combinators::one_or_more; + use crate::combinators::repeated; use crate::primitives::literal; #[test] fn test_choice() { - let p = choice2(literal("gnostika").to(1), one_or_more(literal(" ")).to(2)); + let p = choice2( + literal("gnostika").to(1), + repeated(literal(" ")).at_least(1).to(2), + ); assert_eq!(p.parse("gnostika twentynine"), Ok((1, " twentynine"))); } } diff --git a/src/combinators.rs b/src/combinators.rs index 4b3ef25..38d0efb 100644 --- a/src/combinators.rs +++ b/src/combinators.rs @@ -1,4 +1,4 @@ -use crate::parser::Parser; +use crate::parser::{BoxedParser, ParseResult, Parser}; pub fn optional(parser: P) -> impl Parser, E> where @@ -23,6 +23,78 @@ where } } +pub struct Repeated<'a, I, O> +where + I: Clone, +{ + inner_parser: BoxedParser<'a, I, O, I>, + at_least: Option, + at_most: Option, +} + +impl<'a, I, O> Repeated<'a, I, O> +where + I: Clone, +{ + pub fn at_least(self, n: u16) -> Self { + Self { + at_least: Some(n), + ..self + } + } + pub fn at_most(self, n: u16) -> Self { + Self { + at_most: Some(n), + ..self + } + } +} + +impl<'a, I, O> Parser, I> for Repeated<'a, I, O> +where + I: Clone + 'a, +{ + fn parse(&self, input: I) -> ParseResult, I> { + let mut results = Vec::new(); + let mut count: u16 = 0; + + let at_least = self.at_least.unwrap_or(0); + let at_most = self.at_most.unwrap_or(u16::MAX); + + if at_most == 0 { + return Ok((results, input)); + } + + let mut further_input = input.clone(); + + while let Ok((item, rest)) = self.inner_parser.parse(further_input.clone()) { + results.push(item); + further_input = rest; + count += 1; + if count >= at_most { + break; + } + } + if count < at_least { + return Err(input); + } + + Ok((results, further_input)) + } +} + +pub fn repeated<'a, P, I, O>(parser: P) -> Repeated<'a, I, O> +where + P: Parser + 'static, + I: Clone + 'static, +{ + Repeated { + inner_parser: BoxedParser::new(parser), + at_least: None, + at_most: None, + } +} + pub fn zero_or_more(parser: P) -> impl Parser, I> where P: Parser, @@ -60,7 +132,6 @@ where #[cfg(test)] mod tests { use super::*; - use crate::combinators::one_or_more; use crate::primitives::literal; #[test] @@ -72,7 +143,7 @@ mod tests { #[test] fn test_one_or_more() { - let p = one_or_more(literal("bongo ")); + let p = repeated(literal("bongo ")).at_least(1); let input = "bongo bongo bongo bongo bongo "; let (output, rest) = p.parse(input).unwrap(); diff --git a/src/parser.rs b/src/parser.rs index ff64f07..86c9908 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -60,7 +60,7 @@ pub struct BoxedParser<'a, I, O, E> { } impl<'a, I, O, E> BoxedParser<'a, I, O, E> { - fn new

(inner: P) -> Self + pub(crate) fn new

(inner: P) -> Self where P: Parser + 'a, {