From e584668efc915f6ded73fb46baba22005721748d Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Sun, 16 Oct 2022 19:16:21 -0700 Subject: [PATCH] combinators module --- src/combinators.rs | 75 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 87 ++-------------------------------------------- src/parser.rs | 2 +- src/sequence.rs | 2 +- 4 files changed, 79 insertions(+), 87 deletions(-) create mode 100644 src/combinators.rs 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(parser1: P1, parser2: P2) -> impl Parser where P1: Parser, @@ -91,6 +23,7 @@ where #[cfg(test)] mod tests { use super::*; + use crate::combinators::one_or_more; use crate::primitives::literal; use std::assert_matches::assert_matches; use std::collections::HashMap; @@ -101,22 +34,6 @@ mod tests { assert_matches!(output.unwrap(), ("a", " yolo")); } - #[test] - fn test_map() { - let lit_a = literal("a"); - let output = lit_a.map(|s| s.to_uppercase()).parse("a yolo"); - assert_matches!(output.unwrap(), (s, " yolo") if s == "A"); - } - - #[test] - fn test_one_or_more() { - let p = one_or_more(literal("bongo ")); - let input = "bongo bongo bongo bongo bongo "; - assert_matches!(p.parse(input), Ok((v, "")) if v.len() == 5); - let input = "bongo ecks"; - assert_matches!(p.parse(input), Ok((v, "ecks")) if v.len() == 1); - } - #[test] fn test_choice() { let p = choice(literal("gnostika").to(1), one_or_more(literal(" ")).to(2)); diff --git a/src/parser.rs b/src/parser.rs index 87d0bad..475f577 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -18,7 +18,7 @@ pub trait Parser { O2: 'a, F: Fn(O) -> O2 + 'a, { - BoxedParser::new(crate::map(self, map_fn)) + BoxedParser::new(crate::combinators::map(self, map_fn)) } fn to<'a, O2>(self, item: O2) -> BoxedParser<'a, I, O2, E> diff --git a/src/sequence.rs b/src/sequence.rs index 8b75a6f..809726c 100644 --- a/src/sequence.rs +++ b/src/sequence.rs @@ -110,8 +110,8 @@ where #[cfg(test)] mod test { use super::*; + use crate::combinators::zero_or_more; use crate::primitives::{identifier, literal}; - use crate::zero_or_more; use std::assert_matches::assert_matches; #[test]