aoc_parse/
traits.rs

1//! Core traits.
2
3use crate::types::ParserOutput;
4use crate::{ParseContext, Reported, Result};
5
6/// Trait implemented by all parsers.
7///
8/// This is implemented by the built-in parsers, like `i32`, as well as
9/// user-defined parsers created with `parser!`.
10///
11/// To run a parser, pass some text to [the `parse` method][Parser::parse].
12pub trait Parser {
13    /// The type of value this parser produces from text.
14    type Output;
15
16    /// The type this parser produces internally before converting to the output type.
17    ///
18    /// Some combinators use the `RawOutput` to determine how types should combine.
19    /// For example, if `A::RawOutput = ()`, then `A` produces no output;
20    /// and if `B::RawOutput = (i32,)` then `B` produces an integer;
21    /// `SequenceParser<A, B>::RawOutput` will then be `(i32,)`, the
22    /// result of concatenating the two raw tuples, rather than `((), i32)`.
23    ///
24    /// However, `RawOutput` is very often a singleton tuple, and these are
25    /// awkward for users, so we convert to the `Output` type before presenting a
26    /// result to the user.
27    type RawOutput: ParserOutput<UserType = Self::Output>;
28
29    /// The type that implements matching, backtracking, and type conversion
30    /// for this parser, an implementation detail.
31    type Iter<'parse>: ParseIter<'parse, RawOutput = Self::RawOutput>
32    where
33        Self: 'parse;
34
35    /// Fully parse the given source string `s` and return the resulting value.
36    ///
37    /// This is the main way of using a `Parser`.
38    ///
39    /// This succeeds only if this parser matches the entire input string. It's
40    /// an error if any unmatched characters are left over at the end of `s`.
41    fn parse(&self, s: &str) -> Result<Self::Output> {
42        self.parse_raw(s).map(|v| v.into_user_type())
43    }
44
45    /// Produce a [parse iterator][ParseIter]. This is an internal implementation detail of
46    /// the parser and shouldn't normally be called directly from application code.
47    fn parse_iter<'parse>(
48        &'parse self,
49        context: &mut ParseContext<'parse>,
50        start: usize,
51    ) -> Result<Self::Iter<'parse>, Reported>;
52
53    /// Like `parse` but produce the output in its [raw form][Self::RawOutput].
54    fn parse_raw(&self, s: &str) -> Result<Self::RawOutput> {
55        let mut ctx = ParseContext::new(s);
56        let mut it = match self.parse_iter(&mut ctx, 0) {
57            Ok(iter) => iter,
58            Err(Reported) => return Err(ctx.into_reported_error()),
59        };
60        while it.match_end() != s.len() {
61            ctx.error_extra(it.match_end());
62            if it.backtrack(&mut ctx).is_err() {
63                return Err(ctx.into_reported_error());
64            }
65        }
66        Ok(it.convert())
67    }
68}
69
70/// A parser in action. Some parsers can match in several different ways (for
71/// example, in `foo* bar` backtracking is accomplished by `foo*` first
72/// matching as much as possible, then backing off one match at a time), so
73/// this is an iterator.
74///
75/// This doesn't return a `RawOutput` value from `next_parse` but instead waits
76/// until you're sure you have a complete, successful parse, and are thus ready
77/// to destroy the iterator. This helps us avoid building values only to drop
78/// them later when some downstream parser fails to match, so it makes
79/// backtracking faster. It also means we don't call `.map` closures until
80/// there is a successful overall match and the values are actually needed.
81pub trait ParseIter<'parse> {
82    /// The type this iterator can produce on a successful match.
83    type RawOutput;
84
85    /// Position at the end of the current match.
86    fn match_end(&self) -> usize;
87
88    /// Reject the current match and find the next-most-preferable match.
89    /// Returns true if another match was found, false if not.
90    ///
91    /// Once this returns `false`, no more method calls should be made.
92    fn backtrack(&mut self, context: &mut ParseContext<'parse>) -> Result<(), Reported>;
93
94    /// Convert the matched text to a Rust value.
95    fn convert(&self) -> Self::RawOutput;
96}
97
98impl<'a, P> Parser for &'a P
99where
100    P: Parser + ?Sized,
101{
102    type Output = P::Output;
103    type RawOutput = P::RawOutput;
104
105    type Iter<'parse> = P::Iter<'parse>
106    where
107        P: 'parse,
108        'a: 'parse;
109
110    fn parse_iter<'parse>(
111        &'parse self,
112        context: &mut ParseContext<'parse>,
113        start: usize,
114    ) -> Result<Self::Iter<'parse>, Reported> {
115        <P as Parser>::parse_iter(self, context, start)
116    }
117}