Mercurial > lbo > hg > rcombinators
changeset 7:1f30f2d25d5e
Implement PartialSequence
author | Lewin Bormann <lewin@lewin-bormann.info> |
---|---|
date | Sun, 02 Jun 2019 10:14:51 +0000 |
parents | 7873119002f7 |
children | 335ba6716c4e |
files | src/combinators.rs |
diffstat | 1 files changed, 135 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/src/combinators.rs Thu May 30 14:27:11 2019 +0200 +++ b/src/combinators.rs Sun Jun 02 10:14:51 2019 +0000 @@ -9,6 +9,7 @@ } impl<R, R2, P: Parser<Result = R>, F: Fn(R) -> ParseResult<R2>> Transform<R, R2, P, F> { + /// Create a new Transform parser using f. pub fn new(p: P, f: F) -> Transform<R, R2, P, F> { Transform { f: f, p: p } } @@ -174,6 +175,125 @@ P9 / 9 )); +/// PartialSequence concatenates parsers and tries to parse as far as possible. +/// +/// Individual parsers need to have result types implementing Default. +pub struct PartialSequence<T>(T); + +impl<T> PartialSequence<T> { + pub fn new(tuple: T) -> PartialSequence<T> { + PartialSequence(tuple) + } +} + +/// Macro for implementing sequence parsers for arbitrary tuples. Not for public use. +macro_rules! pseq_impl { + ( ( $($ptype:ident/$ix:tt),+ ) ) => { + impl<$($ptype : Parser<Result=impl Default>, )*> Parser for PartialSequence<($($ptype,)*)> { + type Result = ($(Option<$ptype::Result>,)*); + fn parse(&mut self, st: &mut ParseState<impl Iterator<Item = char>>) -> ParseResult<Self::Result> { + let hold = st.hold(); + let mut result = Self::Result::default(); + $( + let r = (self.0).$ix.parse(st); + if r.is_err() { + st.release(hold); + return Ok(result); + } + result.$ix = Some(r.unwrap()); + )* + st.release(hold); + return Ok(result); + } + } + } +} + +pseq_impl!((P0 / 0, P1 / 1)); +pseq_impl!((P0 / 0, P1 / 1, P2 / 2)); +pseq_impl!((P0 / 0, P1 / 1, P2 / 2, P3 / 3)); +pseq_impl!((P0 / 0, P1 / 1, P2 / 2, P3 / 3, P4 / 4)); +pseq_impl!((P0 / 0, P1 / 1, P2 / 2, P3 / 3, P4 / 4, P5 / 5)); +pseq_impl!((P0 / 0, P1 / 1, P2 / 2, P3 / 3, P4 / 4, P5 / 5, P6 / 6)); +pseq_impl!(( + P0 / 0, + P1 / 1, + P2 / 2, + P3 / 3, + P4 / 4, + P5 / 5, + P6 / 6, + P7 / 7 +)); +pseq_impl!(( + P0 / 0, + P1 / 1, + P2 / 2, + P3 / 3, + P4 / 4, + P5 / 5, + P6 / 6, + P7 / 7, + P8 / 8 +)); +pseq_impl!(( + P0 / 0, + P1 / 1, + P2 / 2, + P3 / 3, + P4 / 4, + P5 / 5, + P6 / 6, + P7 / 7, + P8 / 8, + P9 / 9 +)); + +pub enum RepeatSpec { + /// Any is equivalent to Min(0). + Any, + Min(usize), + Max(usize), + Between(usize, usize), +} + +pub struct Repeat<P: Parser> { + inner: P, + repeat: RepeatSpec, +} + +impl<R, P: Parser<Result=R>> Parser for Repeat<P> { + type Result = Vec<R>; + fn parse(&mut self, st: &mut ParseState<impl Iterator<Item=char>>) -> ParseResult<Self::Result> { + let (min, max) = match self.repeat { + RepeatSpec::Any => (0, -1), + RepeatSpec::Min(i) => (i as isize, -1), + RepeatSpec::Max(i) => (0, i as isize), + RepeatSpec::Between(i, j) => (i as isize, j as isize), + }; + let mut v: Self::Result = Vec::new(); + let hold = st.hold(); + for i in 0.. { + if i > max { + return Ok(v); + } + match self.inner.parse(st) { + Ok(r) => v.push(r), + Err(e) => { + if i >= min { + st.release(hold); + return Ok(v); + } else { + st.reset(hold); + return Err(e); + } + } + } + } + unreachable!() + } +} + #[cfg(test)] mod tests { use super::*; @@ -222,4 +342,19 @@ assert_eq!(Ok(" ".to_string()), p.parse(&mut ps)); assert_eq!(Ok("34".to_string()), p.parse(&mut ps)); } + + #[test] + fn test_partial_sequence() { + let mut p = PartialSequence::new((StringParser::new("a"), StringParser::new("c"), Int)); + let mut ps = ParseState::new("acde"); + assert_eq!(Ok((Some("a".to_string()), Some("c".to_string()), None)), p.parse(&mut ps)); + + let mut p = PartialSequence::new( + (Sequence::new((Int, StringParser::new(" "), Int)), + StringParser::new("x"))); + let mut ps = ParseState::new("12 -12 nothing else"); + assert_eq!(Ok(( + Some((12, " ".to_string(), -12)), + None)), p.parse(&mut ps)); + } }