diff --git a/src/combinators.rs b/src/combinators.rs new file mode 100644 index 0000000..ec0cc5d --- /dev/null +++ b/src/combinators.rs @@ -0,0 +1,75 @@ +use crate::parser::Parser; + +pub fn map
(parser: P, map_fn: F) -> impl Parser +where + P: Parser, + F: Fn(O1) -> O2, +{ + move |input| { + parser + .parse(input) + .map(|(result, rest)| (map_fn(result), rest)) + } +} + +pub fn zero_or_more
(parser: P) -> impl Parser, I> +where + P: Parser, + I: Copy, +{ + move |mut input| { + let mut results = Vec::new(); + + while let Ok((item, rest)) = parser.parse(input) { + results.push(item); + input = rest; + } + + Ok((results, input)) + } +} + +pub fn one_or_more
(parser: P) -> impl Parser, I> +where + P: Parser + 'static, + I: Copy + 'static, + O: 'static, +{ + let parser = std::rc::Rc::new(parser); + parser + .clone() + .then(zero_or_more(parser)) + .map(|(first, rest)| { + let mut output = vec![first]; + output.extend(rest.into_iter()); + output + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::combinators::one_or_more; + use crate::primitives::literal; + + #[test] + fn test_map() { + let lit_a = literal("a"); + let output = lit_a.map(|s| s.to_uppercase()).parse("a yolo"); + assert_eq!(output.unwrap(), ("A".to_string(), " yolo")); + } + + #[test] + fn test_one_or_more() { + let p = one_or_more(literal("bongo ")); + let input = "bongo bongo bongo bongo bongo "; + + let (output, rest) = p.parse(input).unwrap(); + assert_eq!(rest, ""); + assert_eq!(output.len(), 5); + + let (output, rest) = p.parse("bongo ecks").unwrap(); + assert_eq!(output.len(), 1); + assert_eq!(rest, "ecks"); + } +} diff --git a/src/lib.rs b/src/lib.rs index cf5a928..138ffdf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,81 +1,13 @@ #![feature(assert_matches)] #![allow(dead_code)] //TODO eventually turn this off mod bnf; +mod combinators; mod parser; mod primitives; mod sequence; use parser::{ParseResult, Parser}; -fn map
(parser: P, map_fn: F) -> impl Parser -where - P: Parser, - F: Fn(O1) -> O2, -{ - move |input| { - parser - .parse(input) - .map(|(result, rest)| (map_fn(result), rest)) - } -} - -fn pred
(parser: P, pred_fn: F) -> impl Parser -where - P: Parser, - F: Fn(&O) -> bool, -{ - move |input| { - parser.parse(input).and_then(|(result, rest)| { - if pred_fn(&result) { - Ok((result, rest)) - } else { - Err(rest) - } - }) - } -} - -fn zero_or_more
(parser: P) -> impl Parser, I> -where - P: Parser, - I: Copy, -{ - move |mut input| { - let mut results = Vec::new(); - - while let Ok((item, rest)) = parser.parse(input) { - results.push(item); - input = rest; - } - - Ok((results, input)) - } -} - -fn one_or_more
(parser: P) -> impl Parser, I>
-where
- P: Parser + 'static,
- I: Copy + 'static,
- O: 'static,
-{
- let parser = std::rc::Rc::new(parser);
- parser
- .clone()
- .then(zero_or_more(parser))
- .map(|(first, rest)| {
- let mut output = vec![first];
- output.extend(rest.into_iter());
- output
- })
-}
-
-fn any_char(input: &str) -> ParseResult<&str, char, &str> {
- match input.chars().next() {
- Some(ch) => Ok((ch, &input[ch.len_utf8()..])),
- None => Err(input),
- }
-}
-
fn choice