1use core::{
2 fmt::Debug,
3 ops::{
4 FromResidual,
5 Range,
6 RangeFrom,
7 RangeFull,
8 RangeInclusive,
9 RangeTo,
10 RangeToInclusive,
11 Try,
12 },
13};
14
15use binator_core::{
16 Contexting,
17 Parse,
18 Parsed,
19 Streaming,
20};
21
22use crate::UtilsAtom;
23
24#[derive(Clone)]
26pub struct TryFoldBounds<Parser, Bounds, Init, F> {
27 parser: Parser,
28 bounds: Bounds,
29 init: Init,
30 f: F,
31}
32
33pub fn try_fold_bounds<Bounds, Stream, Context, Parser, Acc, Init, Ret, F>(
35 parser: Parser, bounds: Bounds, init: Init, try_fold: F,
36) -> TryFoldBounds<Parser, Bounds, Init, F>
37where
38 Stream: Streaming,
39 Context: Contexting<UtilsAtom<Stream>>,
40 Parser: Parse<Stream, Context>,
41 Init: Fn() -> Ret,
42 F: Fn(Acc, Parser::Token) -> Ret,
43 Ret: Try<Output = Acc>,
44 Parsed<Acc, Stream, Context>: FromResidual<Ret::Residual>,
45 Bounds: TryFoldBoundsParse,
46 Acc: Debug,
47{
48 TryFoldBounds {
49 bounds,
50 parser,
51 init,
52 f: try_fold,
53 }
54}
55
56pub trait TryFoldBoundsParse {
59 fn try_fold_bounds<Stream, Context, Parser, Acc, Init, Ret, F>(
62 &self, parser: &mut Parser, init: &mut Init, f: &mut F, stream: Stream,
63 ) -> Parsed<Acc, Stream, Context>
64 where
65 Stream: Streaming,
66 Context: Contexting<UtilsAtom<Stream>>,
67 Parser: Parse<Stream, Context>,
68 Init: Fn() -> Ret,
69 F: Fn(Acc, Parser::Token) -> Ret,
70 Ret: Try<Output = Acc>,
71 Parsed<Acc, Stream, Context>: FromResidual<Ret::Residual>,
72 Acc: Debug;
73}
74
75impl<Bounds, Stream, Context, Parser, Acc, Init, Ret, F> Parse<Stream, Context>
76 for TryFoldBounds<Parser, Bounds, Init, F>
77where
78 Stream: Streaming,
79 Context: Contexting<UtilsAtom<Stream>>,
80 Parser: Parse<Stream, Context>,
81 Init: Fn() -> Ret,
82 F: Fn(Acc, Parser::Token) -> Ret,
83 Ret: Try<Output = Acc>,
84 Parsed<Acc, Stream, Context>: FromResidual<Ret::Residual>,
85 Bounds: TryFoldBoundsParse,
86 Acc: Debug,
87{
88 type Token = Acc;
89
90 fn parse(&mut self, stream: Stream) -> Parsed<Acc, Stream, Context> {
95 self
96 .bounds
97 .try_fold_bounds(&mut self.parser, &mut self.init, &mut self.f, stream)
98 }
99}
100
101macro_rules! allow_failure {
102 ($stream:expr, $parser:expr, $acc:expr, $try_fold:expr) => {{
103 match $parser.parse($stream.clone()) {
104 Parsed::Success { token, stream } => {
105 $acc = $try_fold($acc, token)?;
106 $stream = stream;
107 }
108 Parsed::Failure(_context) => {
109 return Parsed::Success {
110 token: $acc,
111 stream: $stream,
112 };
113 }
114 Parsed::Error(context) => {
115 return Parsed::Error(context);
116 }
117 };
118 }};
119}
120
121macro_rules! deny_failure {
122 ($stream:expr, $parser:expr, $acc:expr, $try_fold:expr, $min:expr, $i:expr) => {{
123 match $parser.parse($stream) {
124 Parsed::Success { token, stream } => {
125 $acc = $try_fold($acc, token)?;
126 $stream = stream;
127 }
128 Parsed::Failure(context) => {
129 return Parsed::Failure(context.add(UtilsAtom::MinNotReach { min: $min, i: $i }));
130 }
131 Parsed::Error(context) => {
132 return Parsed::Error(context);
133 }
134 };
135 }};
136}
137
138impl TryFoldBoundsParse for RangeFull {
139 fn try_fold_bounds<Stream, Context, Parser, Acc, Init, Ret, F>(
140 &self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
141 ) -> Parsed<Acc, Stream, Context>
142 where
143 Stream: Streaming,
144 Context: Contexting<UtilsAtom<Stream>>,
145 Parser: Parse<Stream, Context>,
146 Init: Fn() -> Ret,
147 F: Fn(Acc, Parser::Token) -> Ret,
148 Ret: Try<Output = Acc>,
149 Parsed<Acc, Stream, Context>: FromResidual<Ret::Residual>,
150 Acc: Debug,
151 {
152 let mut acc = init()?;
153 loop {
154 allow_failure!(stream, parser, acc, f)
155 }
156 }
157}
158
159impl TryFoldBoundsParse for RangeFrom<usize> {
160 fn try_fold_bounds<Stream, Context, Parser, Acc, Init, Ret, F>(
161 &self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
162 ) -> Parsed<Acc, Stream, Context>
163 where
164 Stream: Streaming,
165 Context: Contexting<UtilsAtom<Stream>>,
166 Parser: Parse<Stream, Context>,
167 Init: Fn() -> Ret,
168 F: Fn(Acc, Parser::Token) -> Ret,
169 Ret: Try<Output = Acc>,
170 Parsed<Acc, Stream, Context>: FromResidual<Ret::Residual>,
171 Acc: Debug,
172 {
173 let mut acc = init()?;
174
175 for i in 0..self.start {
176 deny_failure!(stream, parser, acc, f, self.start, i)
177 }
178
179 loop {
180 allow_failure!(stream, parser, acc, f)
181 }
182 }
183}
184
185impl TryFoldBoundsParse for Range<usize> {
186 fn try_fold_bounds<Stream, Context, Parser, Acc, Init, Ret, F>(
187 &self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
188 ) -> Parsed<Acc, Stream, Context>
189 where
190 Stream: Streaming,
191 Context: Contexting<UtilsAtom<Stream>>,
192 Parser: Parse<Stream, Context>,
193 Init: Fn() -> Ret,
194 F: Fn(Acc, Parser::Token) -> Ret,
195 Ret: Try<Output = Acc>,
196 Parsed<Acc, Stream, Context>: FromResidual<Ret::Residual>,
197 Acc: Debug,
198 {
199 let mut acc = init()?;
200
201 for i in 0..self.start {
202 deny_failure!(stream, parser, acc, f, self.start, i)
203 }
204
205 for _ in self.start..self.end {
206 allow_failure!(stream, parser, acc, f)
207 }
208
209 Parsed::Success { token: acc, stream }
210 }
211}
212
213impl TryFoldBoundsParse for RangeInclusive<usize> {
214 fn try_fold_bounds<Stream, Context, Parser, Acc, Init, Ret, F>(
215 &self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
216 ) -> Parsed<Acc, Stream, Context>
217 where
218 Stream: Streaming,
219 Context: Contexting<UtilsAtom<Stream>>,
220 Parser: Parse<Stream, Context>,
221 Init: Fn() -> Ret,
222 F: Fn(Acc, Parser::Token) -> Ret,
223 Ret: Try<Output = Acc>,
224 Parsed<Acc, Stream, Context>: FromResidual<Ret::Residual>,
225 Acc: Debug,
226 {
227 let mut acc = init()?;
228
229 for i in 0..*self.start() {
230 deny_failure!(stream, parser, acc, f, *self.start(), i)
231 }
232
233 for _ in *self.start()..=*self.end() {
234 allow_failure!(stream, parser, acc, f)
235 }
236
237 Parsed::Success { token: acc, stream }
238 }
239}
240
241impl TryFoldBoundsParse for RangeTo<usize> {
242 fn try_fold_bounds<Stream, Context, Parser, Acc, Init, Ret, F>(
243 &self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
244 ) -> Parsed<Acc, Stream, Context>
245 where
246 Stream: Streaming,
247 Context: Contexting<UtilsAtom<Stream>>,
248 Parser: Parse<Stream, Context>,
249 Init: Fn() -> Ret,
250 F: Fn(Acc, Parser::Token) -> Ret,
251 Ret: Try<Output = Acc>,
252 Parsed<Acc, Stream, Context>: FromResidual<Ret::Residual>,
253 Acc: Debug,
254 {
255 let mut acc = init()?;
256
257 for _ in 0..self.end {
258 allow_failure!(stream, parser, acc, f)
259 }
260
261 Parsed::Success { token: acc, stream }
262 }
263}
264
265impl TryFoldBoundsParse for RangeToInclusive<usize> {
266 fn try_fold_bounds<Stream, Context, Parser, Acc, Init, Ret, F>(
267 &self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
268 ) -> Parsed<Acc, Stream, Context>
269 where
270 Stream: Streaming,
271 Context: Contexting<UtilsAtom<Stream>>,
272 Parser: Parse<Stream, Context>,
273 Init: Fn() -> Ret,
274 F: Fn(Acc, Parser::Token) -> Ret,
275 Ret: Try<Output = Acc>,
276 Parsed<Acc, Stream, Context>: FromResidual<Ret::Residual>,
277 Acc: Debug,
278 {
279 let mut acc = init()?;
280
281 for _ in 0..=self.end {
282 allow_failure!(stream, parser, acc, f)
283 }
284
285 Parsed::Success { token: acc, stream }
286 }
287}
288
289macro_rules! impl_primitive {
290 ($primitive:ident) => {
291 impl TryFoldBoundsParse for $primitive {
292 fn try_fold_bounds<Stream, Context, Parser, Acc, Init, Ret, F>(
293 &self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
294 ) -> Parsed<Acc, Stream, Context>
295 where
296 Stream: Streaming,
297 Context: Contexting<UtilsAtom<Stream>>,
298 Parser: Parse<Stream, Context>,
299 Init: Fn() -> Ret,
300 F: Fn(Acc, Parser::Token) -> Ret,
301 Ret: Try<Output = Acc>,
302 Parsed<Acc, Stream, Context>: FromResidual<Ret::Residual>,
303 Acc: Debug,
304 {
305 let mut acc = init()?;
306
307 let min = usize::from(*self);
308 for i in 0..min {
309 deny_failure!(stream, parser, acc, f, min, i)
310 }
311
312 Parsed::Success { token: acc, stream }
313 }
314 }
315 };
316}
317
318macro_rules! impl_primitives {
319 ($($primitives:ident,)*) => {
320 $(impl_primitive!{$primitives})*
321 };
322}
323
324impl_primitives!(usize,);
328
329#[cfg(test)]
330mod tests {
331 use core::convert::Infallible;
332
333 use binator_base::{
334 is,
335 BaseAtom,
336 };
337 use binator_context::{
338 Keep,
339 Last,
340 };
341 use binator_core::{
342 Contexting,
343 CoreAtom,
344 Parse,
345 Parsed,
346 Streaming,
347 };
348 use derive_more::{
349 Display,
350 From,
351 };
352
353 use crate::{
354 Utils,
355 UtilsAtom,
356 };
357
358 #[derive(Display, Debug, Clone, From, PartialEq)]
359 enum FromAtom<Stream: Streaming> {
360 TryFold(UtilsAtom<Stream>),
361 Is(BaseAtom<u8>),
362 Any(CoreAtom<Stream, Infallible>),
363 Stream,
364 }
365
366 type HandleAtom<Stream> = Keep<Last, FromAtom<Stream>>;
373
374 #[test]
375 fn try_fold_bounds_full() {
376 let stream = b"[".as_ref();
377
378 let result: Parsed<_, _, HandleAtom<_>> = is(b'[')
379 .or(is(b']'))
380 .try_fold_bounds(.., || Ok(()), |_, _| Ok(()))
381 .parse(stream);
382 let expected = Parsed::Success {
383 token: (),
384 stream: &stream[1..],
385 };
386 assert_eq!(result, expected);
387 }
388
389 #[test]
390 fn try_fold_bounds() {
391 let stream = b"[".as_ref();
392 let result: Parsed<_, _, HandleAtom<_>> = is(b'[')
393 .or(is(b']'))
394 .try_fold_bounds(1..5, || Ok(()), |_, _| Ok(()))
395 .parse(stream);
396 let expected = Parsed::Success {
397 token: (),
398 stream: &stream[1..],
399 };
400 assert_eq!(result, expected);
401 }
402
403 #[test]
404 fn try_fold_bounds_failure() {
405 let stream = b"abcd".as_ref();
406 let result: Parsed<_, _, HandleAtom<_>> = is(b'[')
407 .or(is(b']'))
408 .try_fold_bounds(1..5, || Ok(()), |_, _| Ok(()))
409 .parse(stream);
410 let context = Keep::new(UtilsAtom::MinNotReach { i: 0, min: 1 });
411 assert_eq!(result, Parsed::Failure(context));
412 }
413}