diff --git a/src/lib.rs b/src/lib.rs index f411a62..b955260 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,35 +1,77 @@ -#![feature(assert_matches)] #![allow(dead_code)] //TODO eventually turn this off - type ParseResult = Result<(O, I), E>; trait Parser { fn parse(&self, input: I) -> ParseResult; } -impl Parser for F where F: Fn(I) -> ParseResult { +impl Parser for F +where + F: Fn(I) -> ParseResult, +{ fn parse(&self, input: I) -> ParseResult { self(input) } } -fn literal(expected: &'static str) -> impl Fn(&str) -> ParseResult<&str, (), &str> { +fn literal(expected: &'static str) -> impl Fn(&str) -> ParseResult<&str, &str, &str> { move |input| match input.get(0..expected.len()) { - Some(next) if next == expected => - Ok(((), &input[expected.len()..])), - _ => Err(input) + Some(next) if next == expected => Ok((next, &input[expected.len()..])), + _ => Err(input), + } +} + +fn sequence( + first: impl Parser, + second: impl Parser, +) -> impl Parser { + move |input| -> ParseResult { + first.parse(input).and_then(|(result1, rest)| { + second + .parse(rest) + .map(|(result2, rest2)| ((result1, result2), rest2)) + }) + } +} + +fn choice<'a, I, O, E>(parsers: &'a [&'a dyn Parser]) -> impl Parser + 'a +where + I: Clone, +{ + move |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()) { + Ok(res) => return Ok(res), + Err(e) => { + err = Some(e); + } + } + } + Err(err.unwrap()) } } #[cfg(test)] mod tests { use super::*; - use std::assert_matches::assert_matches; #[test] fn parsing() { - let output = literal("a")("a yolo"); - assert_matches!(output.unwrap(), ((), " yolo")); + let (parsed, rest) = literal("a")("a yolo").unwrap(); + assert_eq!(parsed, "a"); + assert_eq!(rest, " yolo"); + } + + #[test] + fn test_sequence() { + let parser = sequence(literal("bongo"), sequence(literal(" "), literal("jonzzz"))); + let output = parser.parse("bongo jonzzz").unwrap(); + assert_eq!(output.0 .0, "bongo"); + assert_eq!(output.0 .1, (" ", "jonzzz")); + assert_eq!(output.1, ""); } }