aoc_parse/parsers/
sequence.rs

1//! Matching patterns in sequence.
2
3use crate::{
4    types::{ParserOutput, RawOutputConcat},
5    ParseContext, ParseIter, Parser, Reported, Result,
6};
7
8#[derive(Clone, Copy)]
9pub struct SequenceParser<Head, Tail, Op> {
10    head: Head,
11    tail: Tail,
12    _op: Op,
13}
14
15pub struct SequenceParseIter<'parse, Head, Tail, Op>
16where
17    Head: Parser + 'parse,
18    Tail: Parser + 'parse,
19{
20    parsers: &'parse SequenceParser<Head, Tail, Op>,
21    head_iter: Head::Iter<'parse>,
22    tail_iter: Tail::Iter<'parse>,
23}
24
25/// Operation that joins two values.
26pub trait BinaryOp<Head, Tail>: Copy + Clone {
27    type Output: ParserOutput;
28
29    fn apply(head: Head, tail: Tail) -> Self::Output;
30}
31
32/// Combine sequenced parsers by concatenating output tuples.
33#[derive(Debug, Copy, Clone)]
34pub struct Concat;
35
36impl<Head, Tail> BinaryOp<Head, Tail> for Concat
37where
38    Head: RawOutputConcat<Tail>,
39{
40    type Output = <Head as RawOutputConcat<Tail>>::Output;
41
42    fn apply(head: Head, tail: Tail) -> Self::Output {
43        head.concat(tail)
44    }
45}
46
47/// Combine sequenced parsers by creating nested output tuples.
48#[derive(Debug, Copy, Clone)]
49pub struct Pair;
50
51impl<Head, Tail> BinaryOp<Head, Tail> for Pair
52where
53    Head: ParserOutput,
54    Tail: ParserOutput,
55{
56    type Output = (Head::UserType, Tail::UserType);
57
58    fn apply(head: Head, tail: Tail) -> Self::Output {
59        (head.into_user_type(), tail.into_user_type())
60    }
61}
62
63impl<Head, Tail, Op> Parser for SequenceParser<Head, Tail, Op>
64where
65    Head: Parser,
66    Tail: Parser,
67    Op: BinaryOp<Head::RawOutput, Tail::RawOutput>,
68{
69    type Output = <Op::Output as ParserOutput>::UserType;
70    type RawOutput = Op::Output;
71    type Iter<'parse> = SequenceParseIter<'parse, Head, Tail, Op>
72    where
73        Head: 'parse,
74        Tail: 'parse,
75        Op: 'parse;
76
77    fn parse_iter<'parse>(
78        &'parse self,
79        context: &mut ParseContext<'parse>,
80        start: usize,
81    ) -> Result<Self::Iter<'parse>, Reported> {
82        let mut head_iter = self.head.parse_iter(context, start)?;
83        let tail_iter = first_tail_match::<Head, Tail>(context, &mut head_iter, &self.tail)?;
84        Ok(SequenceParseIter {
85            parsers: self,
86            head_iter,
87            tail_iter,
88        })
89    }
90}
91
92fn first_tail_match<'parse, Head, Tail>(
93    context: &mut ParseContext<'parse>,
94    head: &mut Head::Iter<'parse>,
95    tail: &'parse Tail,
96) -> Result<Tail::Iter<'parse>, Reported>
97where
98    Head: Parser,
99    Tail: Parser,
100{
101    loop {
102        let mid = head.match_end();
103        if let Ok(tail_iter) = tail.parse_iter(context, mid) {
104            return Ok(tail_iter);
105        }
106        head.backtrack(context)?;
107    }
108}
109
110impl<'parse, Head, Tail, Op> ParseIter<'parse> for SequenceParseIter<'parse, Head, Tail, Op>
111where
112    Head: Parser,
113    Tail: Parser,
114    Op: BinaryOp<Head::RawOutput, Tail::RawOutput>,
115    Op::Output: ParserOutput,
116{
117    type RawOutput = Op::Output;
118
119    fn match_end(&self) -> usize {
120        self.tail_iter.match_end()
121    }
122
123    fn backtrack(&mut self, context: &mut ParseContext<'parse>) -> Result<(), Reported> {
124        self.tail_iter.backtrack(context).or_else(|Reported| {
125            self.head_iter.backtrack(context)?;
126            let tail_iter =
127                first_tail_match::<Head, Tail>(context, &mut self.head_iter, &self.parsers.tail)?;
128            self.tail_iter = tail_iter;
129            Ok(())
130        })
131    }
132
133    fn convert(&self) -> Self::RawOutput {
134        let head = self.head_iter.convert();
135        let tail = self.tail_iter.convert();
136        Op::apply(head, tail)
137    }
138}
139
140// Used by the `parser!()` macro to implement concatenation.
141#[doc(hidden)]
142pub fn sequence<Head, Tail>(head: Head, tail: Tail) -> SequenceParser<Head, Tail, Concat> {
143    SequenceParser {
144        head,
145        tail,
146        _op: Concat,
147    }
148}
149
150// Used by the `parser!()` macro to implement `=>`-mapping.
151#[doc(hidden)]
152pub fn pair<Head, Tail>(head: Head, tail: Tail) -> SequenceParser<Head, Tail, Pair> {
153    SequenceParser {
154        head,
155        tail,
156        _op: Pair,
157    }
158}