2
0
Эх сурвалжийг харах

Add combinators to comment scopes, discard results, and feed parsers

Isaac Woods 5 жил өмнө
parent
commit
7ae3dc7044
1 өөрчлөгдсөн 73 нэмэгдсэн , 0 устгасан
  1. 73 0
      aml_parser/src/parser.rs

+ 73 - 0
aml_parser/src/parser.rs

@@ -1,4 +1,5 @@
 use crate::AmlError;
+use alloc::vec::Vec;
 use core::marker::PhantomData;
 use log::trace;
 
@@ -14,6 +15,10 @@ pub trait Parser<'a, R>: Sized {
         Map { parser: self, map_fn, _phantom: PhantomData }
     }
 
+    fn discard_result(self) -> DiscardResult<'a, Self, R> {
+        DiscardResult { parser: self, _phantom: PhantomData }
+    }
+
     /// Try parsing with `self`. If it fails, try parsing with `other`, returning the result of the
     /// first of the two parsers to succeed. To `or` multiple parsers ergonomically, see the
     /// `choice!` macro.
@@ -30,6 +35,19 @@ pub trait Parser<'a, R>: Sized {
     {
         Then { p1: self, p2: next, _phantom: PhantomData }
     }
+
+    /// `feed` takes a function that takes the result of this parser (`self`) and creates another
+    /// parser, which is then used to parse the next part of the stream. This sounds convoluted,
+    /// but is useful for when the next parser's behaviour depends on a property of the result of
+    /// the first (e.g. the first parser might parse a length `n`, and the second parser then
+    /// consumes `n` bytes).
+    fn feed<F, P2, R2>(self, producer_fn: F) -> Feed<'a, Self, P2, F, R, R2>
+    where
+        P2: Parser<'a, R2>,
+        F: Fn(R) -> P2,
+    {
+        Feed { parser: self, producer_fn, _phantom: PhantomData }
+    }
 }
 
 impl<'a, F, R> Parser<'a, R> for F
@@ -103,6 +121,18 @@ where
     }
 }
 
+pub fn comment_scope<'a, P, R>(scope_name: &'a str, parser: P) -> impl Parser<'a, R>
+where
+    P: Parser<'a, R>,
+{
+    move |input| {
+        trace!("--> {}", scope_name);
+        let result = parser.parse(input);
+        trace!("<-- {}", scope_name);
+        result
+    }
+}
+
 pub struct Or<'a, P1, P2, R>
 where
     P1: Parser<'a, R>,
@@ -148,6 +178,23 @@ where
     }
 }
 
+pub struct DiscardResult<'a, P, R>
+where
+    P: Parser<'a, R>,
+{
+    parser: P,
+    _phantom: PhantomData<&'a R>,
+}
+
+impl<'a, P, R> Parser<'a, ()> for DiscardResult<'a, P, R>
+where
+    P: Parser<'a, R>,
+{
+    fn parse(&self, input: &'a [u8]) -> ParseResult<'a, ()> {
+        self.parser.parse(input).map(|(new_input, _)| (new_input, ()))
+    }
+}
+
 pub struct Then<'a, P1, P2, R1, R2>
 where
     P1: Parser<'a, R1>,
@@ -172,6 +219,32 @@ where
     }
 }
 
+pub struct Feed<'a, P1, P2, F, R1, R2>
+where
+    P1: Parser<'a, R1>,
+    P2: Parser<'a, R2>,
+    F: Fn(R1) -> P2,
+{
+    parser: P1,
+    producer_fn: F,
+    _phantom: PhantomData<&'a (R1, R2)>,
+}
+
+impl<'a, P1, P2, F, R1, R2> Parser<'a, R2> for Feed<'a, P1, P2, F, R1, R2>
+where
+    P1: Parser<'a, R1>,
+    P2: Parser<'a, R2>,
+    F: Fn(R1) -> P2,
+{
+    fn parse(&self, input: &'a [u8]) -> ParseResult<'a, R2> {
+        let (input, first_result) = self.parser.parse(input)?;
+
+        // We can now produce the second parser, and parse using that.
+        let second_parser = (self.producer_fn)(first_result);
+        second_parser.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 succeeds, or fails if all of them fail.
 pub macro choice {