diff --git a/src/sequence.rs b/src/sequence.rs index 9983ee4..b1eb37a 100644 --- a/src/sequence.rs +++ b/src/sequence.rs @@ -1,11 +1,37 @@ -use crate::Parser; +use crate::{ParseResult, Parser}; pub fn tuple2(parser1: P1, parser2: P2) -> impl Parser where P1: Parser, P2: Parser, { - move |input| { + seq((parser1, parser2)) +} + +pub fn seq(sequence: T) -> impl Parser +where + T: Sequence, +{ + move |input| sequence.parse(input) +} + +/* TODO - eventually rewrite this parser combinator in Schala. Seeing what this + * code that makes heavy use of type variables and abstraction over types looks like + * in Schala's type system should be educational + */ + +pub trait Sequence { + fn parse(&self, input: I) -> ParseResult; +} + +impl Sequence for (P1, P2) +where + P1: Parser, + P2: Parser, +{ + fn parse(&self, input: I) -> ParseResult { + let parser1 = &self.0; + let parser2 = &self.1; parser1.parse(input).and_then(|(result1, rest1)| { parser2 .parse(rest1) @@ -14,10 +40,77 @@ where } } +impl Sequence for (P1, P2, P3) +where + P1: Parser, + P2: Parser, + P3: Parser, +{ + fn parse(&self, input: I) -> ParseResult { + let parser1 = &self.0; + let parser2 = &self.1; + let parser3 = &self.2; + + let (result1, rest1) = parser1.parse(input)?; + let (result2, rest2) = parser2.parse(rest1)?; + let (result3, rest3) = parser3.parse(rest2)?; + + Ok(((result1, result2, result3), rest3)) + } +} + +impl Sequence for (P1, P2, P3, P4) +where + P1: Parser, + P2: Parser, + P3: Parser, + P4: Parser, +{ + fn parse(&self, input: I) -> ParseResult { + let parser1 = &self.0; + let parser2 = &self.1; + let parser3 = &self.2; + let parser4 = &self.3; + + let (result1, rest1) = parser1.parse(input)?; + let (result2, rest2) = parser2.parse(rest1)?; + let (result3, rest3) = parser3.parse(rest2)?; + let (result4, rest4) = parser4.parse(rest3)?; + + Ok(((result1, result2, result3, result4), rest4)) + } +} + +impl Sequence + for (P1, P2, P3, P4, P5) +where + P1: Parser, + P2: Parser, + P3: Parser, + P4: Parser, + P5: Parser, +{ + fn parse(&self, input: I) -> ParseResult { + let parser1 = &self.0; + let parser2 = &self.1; + let parser3 = &self.2; + let parser4 = &self.3; + let parser5 = &self.4; + + let (result1, rest1) = parser1.parse(input)?; + let (result2, rest2) = parser2.parse(rest1)?; + let (result3, rest3) = parser3.parse(rest2)?; + let (result4, rest4) = parser4.parse(rest3)?; + let (result5, rest5) = parser5.parse(rest4)?; + + Ok(((result1, result2, result3, result4, result5), rest5)) + } +} + #[cfg(test)] mod test { use super::*; - use crate::{identifier, literal}; + use crate::{identifier, literal, zero_or_more}; use std::assert_matches::assert_matches; #[test] @@ -28,4 +121,26 @@ mod test { let p = identifier.then(literal(" ")).then(literal("ruts")); assert_matches!(p.parse("fort1 ruts"), Ok((r, "")) if r.0.0 == "fort1" && r.0.1== " " && r.1 == "ruts"); } + + #[test] + fn test_seq() { + let p = seq(( + literal("bong").to(10), + zero_or_more(literal(" ")).to(()), + literal("hits").to(20), + )); + assert_eq!(p.parse("bong hits").unwrap(), ((10, (), 20), "")); + + let p = seq(( + literal("alpha").to(10), + zero_or_more(literal(" ")).to(()), + zero_or_more(literal("-")).to(()), + zero_or_more(literal(" ")), + literal("beta"), + )); + assert_eq!( + p.parse("alpha ------ beta gamma").unwrap(), + ((10, (), (), vec![" ", " ", " "], "beta"), " gamma") + ); + } }