binator_utils/
try_fold_bounds.rs

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/// Implementation of [crate::Utils::try_fold_bounds]
25#[derive(Clone)]
26pub struct TryFoldBounds<Parser, Bounds, Init, F> {
27  parser: Parser,
28  bounds: Bounds,
29  init: Init,
30  f: F,
31}
32
33/// Function style version of [crate::Utils::try_fold_bounds]
34pub 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
56/// This trait must be implemented by Bounds you are using.
57/// As user you should not need to care about this except for few cases.
58pub trait TryFoldBoundsParse {
59  /// This method allow to implement parse for every type that implement
60  /// Try FoldBoundsParse.
61  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  // #[cfg_attr(
91  //   feature = "tracing",
92  //   tracing::instrument(level = "trace", skip_all, ret(Display))
93  // )]
94  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
324// the error when not using a usize when calling try_fold is very hard to
325// understand u8 and u16 could be implemented too BUT this could be error prone
326// because of overflow
327impl_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  // impl<Stream> PartialEq for FromAtom<Stream> {
367  //   fn eq(&self, other: &Self) -> bool {
368  //     discriminant(self) == discriminant(other)
369  //   }
370  // }
371
372  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}