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}