lexerus/helpers/
groups.rs

1//! Various helper groups meant to capture
2//! groups of things.
3
4// App imports
5use crate::*;
6use ::std::marker::PhantomData;
7
8#[derive(Clone)]
9/// Captures a group of _contiguous_ `Capture`. Unlike [GroupUntil], this structure does not check
10/// for an end token.
11///
12/// **Fails*:
13/// - Requisite `NUM` of `Capture` not met.
14///
15/// The `Capture` is _not allocated_ which means that the [Buffer] is not split out into individual [Buffer]s. Use only if `Capture` does not need to be individually parsed, for example in a string of characters.
16///
17/// **Warning**: Caller is responsible for checking for escaped `Capture`
18pub struct Group<'code, Capture, const NUM: usize> {
19    buffer: Option<Buffer<'code>>,
20    _captured: PhantomData<Capture>,
21}
22
23impl<'code, Capture, const NUM: usize> std::fmt::Debug
24    for Group<'code, Capture, NUM>
25{
26    fn fmt(
27        &self,
28        f: &mut std::fmt::Formatter<'_>,
29    ) -> std::fmt::Result {
30        f.debug_tuple("Group").field(&self.buffer).finish()
31    }
32}
33
34impl<'code, Capture, const NUM: usize> Token<'code>
35    for Group<'code, Capture, NUM>
36where
37    Capture: Token<'code>,
38{
39    const NAME: &'static str = <Capture as Token>::NAME;
40
41    fn buffer(&self) -> Option<Buffer<'code>> {
42        self.buffer.clone()
43    }
44}
45
46impl<'code, Capture, const NUM: usize> Lexer<'code>
47    for Group<'code, Capture, NUM>
48where
49    Capture: Lexer<'code>,
50{
51    fn lex(
52        buffer: &mut Buffer<'code>,
53    ) -> Result<Self, Error<'code>> {
54        // Local
55        let mut local_buffer = buffer.clone();
56
57        let mut return_buffer = None::<Buffer<'code>>;
58        let mut matched = 0usize;
59
60        // Attempt to eat from buffer
61        loop {
62            match <Capture as Lexer>::lex(&mut local_buffer)
63            {
64                Ok(captured) => {
65                    return_buffer = match return_buffer {
66                        Some(return_buffer) => Some(
67                            match <Capture as Token>::buffer(
68                                &captured,
69                            ) {
70                                Some(captured_buffer) => {
71                                    return_buffer
72                                        + captured_buffer
73                                }
74                                None => return_buffer,
75                            },
76                        ),
77                        None => <Capture as Token>::buffer(
78                            &captured,
79                        )
80                        .clone(),
81                    };
82                    matched += 1;
83                }
84                Err(mut err) => {
85                    // Check if one
86                    if NUM > 0 && matched < NUM {
87                        // Update error count and call error
88                        err.matched = matched;
89                        Err(err)?
90                    }
91                    // return
92                    break;
93                }
94            }
95        }
96
97        *buffer = local_buffer;
98
99        Ok(Self {
100            buffer: return_buffer,
101            _captured: PhantomData,
102        })
103    }
104}
105
106#[derive(Clone)]
107/// Captures a group of _contiguous_ `Capture` until  `End` is found.
108///
109/// **Fails*:
110/// - Buffer is unable to match any more `Capture` but is also unable to match `End`; **or**
111/// - Requisite `NUM` of `Capture` not met.
112///
113/// The `Capture` is _not allocated_ which means that the [Buffer] is not split out into individual [Buffer]s. Use only if `Capture` does not need to be individually parsed, for example in a string of characters.
114pub struct GroupUntil<'code, Capture, End, const NUM: usize>
115{
116    buffer: Option<Buffer<'code>>,
117    _captured: PhantomData<Capture>,
118    _end: PhantomData<End>,
119}
120
121impl<'code, Capture, End, const NUM: usize> std::fmt::Debug
122    for GroupUntil<'code, Capture, End, NUM>
123{
124    fn fmt(
125        &self,
126        f: &mut std::fmt::Formatter<'_>,
127    ) -> std::fmt::Result {
128        f.debug_tuple("Group").field(&self.buffer).finish()
129    }
130}
131
132impl<'code, Capture, End, const NUM: usize> Token<'code>
133    for GroupUntil<'code, Capture, End, NUM>
134where
135    Capture: Token<'code>,
136    End: Token<'code>,
137{
138    const NAME: &'static str = <Capture as Token>::NAME;
139
140    fn buffer(&self) -> Option<Buffer<'code>> {
141        self.buffer.clone()
142    }
143}
144
145impl<'code, Capture, End, const NUM: usize> Lexer<'code>
146    for GroupUntil<'code, Capture, End, NUM>
147where
148    Capture: Lexer<'code>,
149    End: Lexer<'code>,
150{
151    fn lex(
152        buffer: &mut Buffer<'code>,
153    ) -> Result<Self, Error<'code>> {
154        // Local
155        let mut local_buffer = buffer.clone();
156
157        let mut return_buffer = None::<Buffer<'code>>;
158        let mut matched = 0usize;
159
160        // Local buffer is cloned agained to prevent
161        // eating the end token since the end token is not
162        // treated as part of the capture group
163        while <End as Lexer>::lex(&mut local_buffer.clone())
164            .is_err()
165        {
166            // Capture
167            let captured =
168                <Capture as Lexer>::lex(&mut local_buffer)
169                    .map_err(|mut err| {
170                        err.matched = matched;
171                        if let Kind::NotFound(syntax) =
172                            &mut err.kind
173                        {
174                            *syntax = <End as Token>::NAME
175                        }
176                        err
177                    })?;
178
179            // Return
180            return_buffer = match return_buffer {
181                Some(return_buffer) => Some(
182                    match <Capture as Token>::buffer(
183                        &captured,
184                    ) {
185                        Some(captured_buffer) => {
186                            return_buffer + captured_buffer
187                        }
188                        None => return_buffer,
189                    },
190                ),
191                None => {
192                    <Capture as Token>::buffer(&captured)
193                        .clone()
194                }
195            };
196            matched += 1;
197        }
198
199        // Check if one and throw if not
200        if NUM > 0 && matched < NUM {
201            Err(Error {
202                buffer: local_buffer.clone(),
203                matched,
204                kind: Kind::NotFound(<Self as Token>::NAME),
205            })?
206        }
207
208        *buffer = local_buffer;
209
210        Ok(Self {
211            buffer: return_buffer,
212            _captured: PhantomData,
213            _end: PhantomData,
214        })
215    }
216}
217
218#[derive(Debug, Clone)]
219/// Captures a group of _contiguous_ `Capture` bookended by the `Start` and `End` types.
220///
221/// **Fails*:
222/// - No `Start` is found; **or**
223/// - Buffer is unable to match any more `Capture` but is also unable to match `End`; **or**
224/// - Requisite `NUM` of `Capture` not met.
225///
226/// The `Capture` is _not allocated_ which means that the [Buffer] is not split out into individual [Buffer]s. Use only if `Capture` does not need to be individually parsed, for example in a string of characters.
227pub struct GroupBookEnd<
228    'code,
229    Start,
230    Capture,
231    End,
232    const NUM: usize,
233> {
234    start: Start,
235    group: GroupUntil<'code, Capture, End, NUM>,
236    end: End,
237}
238
239impl<'code, Start, Capture, End, const NUM: usize>
240    Token<'code>
241    for GroupBookEnd<'code, Start, Capture, End, NUM>
242where
243    Start: Token<'code>,
244    Capture: Token<'code>,
245    End: Token<'code>,
246{
247    const NAME: &'static str = <Capture as Token>::NAME;
248
249    fn buffer(&self) -> Option<Buffer<'code>> {
250        let buffers = [
251            <Start as Token>::buffer(&self.start),
252            <GroupUntil<'code, Capture, End, NUM> as Token>::buffer(&self.group),
253            <End as Token>::buffer(&self.end),
254        ]
255        .into_iter();
256
257        buffers
258            .flatten()
259            .reduce(|acc, current| acc + current)
260    }
261}
262
263impl<'code, Start, Capture, End, const NUM: usize>
264    Lexer<'code>
265    for GroupBookEnd<'code, Start, Capture, End, NUM>
266where
267    Start: Lexer<'code>,
268    Capture: Lexer<'code>,
269    End: Lexer<'code>,
270{
271    fn lex(
272        buffer: &mut Buffer<'code>,
273    ) -> Result<Self, Error<'code>> {
274        let mut local_buffer = buffer.clone();
275        let mut matched = 0usize;
276
277        // Consume start
278        let start =
279            <Start as Lexer>::lex(&mut local_buffer)?;
280        matched += 1;
281
282        // Get group buffer
283        let group =
284            <GroupUntil<Capture, End, NUM> as Lexer>::lex(
285                &mut local_buffer,
286            )
287            .map_err(|mut err| {
288                matched += err.matched;
289                err.matched = matched;
290                err
291            })?;
292
293        // iterate
294        let end = <End as Lexer>::lex(&mut local_buffer)
295            .map_err(|mut err| {
296                matched += err.matched;
297                err.matched = matched;
298                err
299            })?;
300
301        // Reset buffer
302        *buffer = local_buffer;
303
304        Ok(Self { start, group, end })
305    }
306}