Forráskód Böngészése

Add `or` combinator and `choice` utility macro

Isaac Woods 6 éve
szülő
commit
d4ea898826
1 módosított fájl, 49 hozzáadás és 0 törlés
  1. 49 0
      aml_parser/src/parser.rs

+ 49 - 0
aml_parser/src/parser.rs

@@ -1,9 +1,18 @@
 use crate::AmlError;
+use core::marker::PhantomData;
+use log::trace;
 
 pub type ParseResult<'a, R> = Result<(&'a [u8], R), (&'a [u8], AmlError)>;
 
 pub trait Parser<'a, R>: Sized {
     fn parse(&self, input: &'a [u8]) -> ParseResult<'a, R>;
+
+    fn or<OtherParser>(self, other: OtherParser) -> Or<'a, Self, OtherParser, R>
+    where
+        OtherParser: Parser<'a, R>,
+    {
+        Or { p1: self, p2: other, _phantom: PhantomData }
+    }
 }
 
 impl<'a, F, R> Parser<'a, R> for F
@@ -34,3 +43,43 @@ where
 {
     move |input| parser.parse(input).map(|(next_input, result)| (next_input, map_fn(result)))
 }
+
+pub struct Or<'a, P1, P2, R>
+where
+    P1: Parser<'a, R>,
+    P2: Parser<'a, R>,
+{
+    p1: P1,
+    p2: P2,
+    _phantom: PhantomData<&'a R>,
+}
+
+impl<'a, P1, P2, R> Parser<'a, R> for Or<'a, P1, P2, R>
+where
+    P1: Parser<'a, R>,
+    P2: Parser<'a, R>,
+{
+    fn parse(&self, input: &'a [u8]) -> ParseResult<'a, R> {
+        match self.p1.parse(input) {
+            Ok(result) => return Ok(result),
+            Err(_) => (),
+        }
+
+        self.p2.parse(input)
+    }
+}
+
+/// Takes a number of parsers, and tries to apply each one to the input in order. Returns the
+/// result of the first one that succeedes, or fails if all of them fail.
+pub macro choice {
+    ($first_parser: expr) => {
+        $first_parser
+    },
+
+    ($first_parser: expr, $($other_parser: expr),*) => {
+        $first_parser
+        $(
+            .or($other_parser)
+         )*
+    }
+}