changeset 22:435e4e609128

Implement Then combinator and trait method for Parser.
author Lewin Bormann <lewin@lewin-bormann.info>
date Tue, 04 Jun 2019 23:55:55 +0200
parents d90066bf03d0
children 4cc1c97724f2
files src/combinators.rs src/lib.rs src/parser.rs
diffstat 3 files changed, 54 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/src/combinators.rs	Tue Jun 04 23:30:18 2019 +0200
+++ b/src/combinators.rs	Tue Jun 04 23:55:55 2019 +0200
@@ -353,6 +353,36 @@
     }
 }
 
+/// Applies one parser, discards the result, and returns the second parser's results if the first
+/// one succeeded.
+pub struct Then<A: Parser, B: Parser> {
+    a: A,
+    b: B,
+}
+
+impl<A: Parser, B: Parser> Then<A, B> {
+    pub fn new(first: A, second: B) -> Then<A, B> {
+        Then {
+            a: first,
+            b: second,
+        }
+    }
+}
+
+impl<A: Parser, B: Parser> Parser for Then<A, B> {
+    type Result = B::Result;
+    fn parse(
+        &mut self,
+        st: &mut ParseState<impl Iterator<Item = char>>,
+    ) -> ParseResult<Self::Result> {
+        match self.a.parse(st) {
+            Ok(_) => (),
+            Err(e) => return Err(e),
+        }
+        self.b.parse(st)
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -389,6 +419,16 @@
     }
 
     #[test]
+    fn test_then() {
+        let mut ps = ParseState::new("abcdef 123");
+        let mut p = StringParser::new("abc")
+            .then(StringParser::new("def"))
+            .then(whitespace())
+            .then(Int32::new());
+        assert_eq!(Ok(123), p.parse(&mut ps));
+    }
+
+    #[test]
     fn test_alternative() {
         let mut p = Alternative::new((
             StringParser::new("ab"),
--- a/src/lib.rs	Tue Jun 04 23:30:18 2019 +0200
+++ b/src/lib.rs	Tue Jun 04 23:55:55 2019 +0200
@@ -55,7 +55,7 @@
 pub mod primitives;
 mod state;
 
-pub use combinators::{Alternative, PartialSequence, Repeat, Sequence, Transform};
+pub use combinators::{Alternative, Maybe, PartialSequence, Repeat, Sequence, Then, Transform};
 pub use parser::{execerr, Parser};
 pub use primitives::{float, string_none_of, string_of, whitespace, Int, StringParser};
 pub use state::ParseState;
--- a/src/parser.rs	Tue Jun 04 23:30:18 2019 +0200
+++ b/src/parser.rs	Tue Jun 04 23:55:55 2019 +0200
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use crate::combinators::Transform;
+use crate::combinators::{Then, Transform};
 use crate::state::ParseState;
 
 #[derive(Debug, PartialEq)]
@@ -59,12 +59,14 @@
     type Result;
 
     /// parse consumes input from `st` and returns a result or an error.
+    ///
+    /// NOTE: This could be generalized to apply to any item yielded by ParseState.
     fn parse(
         &mut self,
         st: &mut ParseState<impl Iterator<Item = char>>,
     ) -> ParseResult<Self::Result>;
 
-    /// apply transforms the result of this parser using a Transform combinator.
+    /// `apply` transforms the result of this parser using a Transform combinator.
     fn apply<R2, F: Fn(Self::Result) -> ParseResult<R2>>(
         self,
         f: F,
@@ -74,4 +76,13 @@
     {
         Transform::new(self, f)
     }
+
+    /// `then` attempts to parse input, and if it succeeds, executes parser `p`, only returning
+    /// `p`'s result. This is useful for chaining parsers of which the results are not need.
+    fn then<R2, P: Parser<Result = R2>>(self, p: P) -> Then<Self, P>
+    where
+        Self: std::marker::Sized,
+    {
+        Then::new(self, p)
+    }
 }