changeset 5:fe1259c95911

Implement Alternative combinator
author Lewin Bormann <lewin@lewin-bormann.info>
date Thu, 30 May 2019 14:10:26 +0200
parents 88dc74ffd497
children 7873119002f7
files src/combinators.rs
diffstat 1 files changed, 92 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/src/combinators.rs	Thu May 30 11:35:11 2019 +0200
+++ b/src/combinators.rs	Thu May 30 14:10:26 2019 +0200
@@ -1,16 +1,91 @@
-use crate::parser::{ParseResult, Parser};
+use crate::parser::{ParseError, ParseResult, Parser};
 use crate::state::ParseState;
 
-pub struct Sequence<T> {
-    t: T,
+pub struct Alternative<T>(T);
+
+impl<T> Alternative<T> {
+    pub fn new(tuple: T) -> Alternative<T> {
+        Alternative(tuple)
+    }
+}
+
+macro_rules! alt_impl {
+    ( ( $($ptype:ident/$ix:tt),* ) ) => {
+        impl<R, $($ptype : Parser<Result=R>, )*> Parser for Alternative<($($ptype,)*)> {
+            type Result = R;
+            fn parse(&mut self, st: &mut ParseState<impl Iterator<Item = char>>) -> ParseResult<Self::Result> {
+                $(
+                    let hold = st.hold();
+                    match (self.0).$ix.parse(st) {
+                        Err(_) => (),
+                        Ok(o) => { st.release(hold); return Ok(o) }
+                    }
+                    st.reset(hold);
+                )*
+                return Err(ParseError::Fail("no alternative matched", st.index()))
+            }
+        }
+    }
+}
+
+alt_impl!((P0 / 0, P1 / 1));
+alt_impl!((P0 / 0, P1 / 1, P2 / 2));
+alt_impl!((P0 / 0, P1 / 1, P2 / 2, P3 / 3));
+alt_impl!((P0 / 0, P1 / 1, P2 / 2, P3 / 3, P4 / 4));
+alt_impl!((P0 / 0, P1 / 1, P2 / 2, P3 / 3, P4 / 4, P5 / 5));
+alt_impl!((P0 / 0, P1 / 1, P2 / 2, P3 / 3, P4 / 4, P5 / 5, P6 / 6));
+alt_impl!((
+    P0 / 0,
+    P1 / 1,
+    P2 / 2,
+    P3 / 3,
+    P4 / 4,
+    P5 / 5,
+    P6 / 6,
+    P7 / 7
+));
+alt_impl!((
+    P0 / 0,
+    P1 / 1,
+    P2 / 2,
+    P3 / 3,
+    P4 / 4,
+    P5 / 5,
+    P6 / 6,
+    P7 / 7,
+    P8 / 8
+));
+alt_impl!((
+    P0 / 0,
+    P1 / 1,
+    P2 / 2,
+    P3 / 3,
+    P4 / 4,
+    P5 / 5,
+    P6 / 6,
+    P7 / 7,
+    P8 / 8,
+    P9 / 9
+));
+
+/// Sequence concatenates parsers and only succeeds if all of them do. T is always a tuple in order
+/// for Sequence to implement the Parser trait. The result is a tuple of all the parser results.
+///
+/// Individual parsers need to have result types implementing Default.
+pub struct Sequence<T>(T);
+
+macro_rules! sequence {
+    ($($p:expr),+) => { Sequence::new(($($p),*)) };
+    ($p:expr) => { $p };
 }
 
 impl<T> Sequence<T> {
     pub fn new(tuple: T) -> Sequence<T> {
-        Sequence { t: tuple }
+        Sequence(tuple)
     }
 }
 
+/// Macro for implementing sequence parsers for arbitrary tuples. Not for public use.
 macro_rules! seq_impl {
     ( ( $($ptype:ident/$ix:tt),+ ) ) => {
         impl<$($ptype : Parser<Result=impl Default>, )*> Parser for Sequence<($($ptype,)*)> {
@@ -19,7 +94,7 @@
                 let hold = st.hold();
                 let mut result = Self::Result::default();
                 $(
-                    let r = (self.t.$ix).parse(st);
+                    let r = (self.0).$ix.parse(st);
                     if r.is_err() {
                         st.reset(hold);
                         return Err(r.err().unwrap());
@@ -107,4 +182,16 @@
             p.parse(&mut ps)
         );
     }
+
+    #[test]
+    fn test_alternative() {
+        let mut p = Alternative::new((
+            StringParser::new("ab"),
+            StringParser::new("de"),
+            StringParser::new(" "),
+        ));
+        let mut ps = ParseState::new("de 34");
+        assert_eq!(Ok("de".to_string()), p.parse(&mut ps));
+        assert_eq!(Ok(" ".to_string()), p.parse(&mut ps));
+    }
 }