binator_utils/
fold_bounds.rs

1use core::{
2  fmt::Debug,
3  ops::{
4    Range,
5    RangeFrom,
6    RangeFull,
7    RangeInclusive,
8    RangeTo,
9    RangeToInclusive,
10  },
11};
12
13use binator_core::{
14  Contexting,
15  Parse,
16  Parsed,
17  Streaming,
18};
19
20use crate::UtilsAtom;
21
22/// Implementation of [crate::Utils::fold_bounds]
23#[derive(Clone)]
24pub struct FoldBounds<Parser, Bounds, Init, F> {
25  parser: Parser,
26  bounds: Bounds,
27  init: Init,
28  f: F,
29}
30
31/// Function style version of [crate::Utils::fold_bounds]
32pub fn fold_bounds<Bounds, Stream, Context, Parser, Acc, Init, F>(
33  parser: Parser, bounds: Bounds, init: Init, fold: F,
34) -> FoldBounds<Parser, Bounds, Init, F>
35where
36  Stream: Streaming,
37  Context: Contexting<UtilsAtom<Stream>>,
38  Parser: Parse<Stream, Context>,
39  Init: FnMut() -> Acc,
40  F: FnMut(Acc, Parser::Token) -> Acc,
41  Bounds: FoldBoundsParse,
42  Acc: Debug,
43{
44  FoldBounds {
45    bounds,
46    parser,
47    init,
48    f: fold,
49  }
50}
51
52/// This trait must be implemented by Bounds you are using.
53/// As user you should not need to care about this except for few cases.
54pub trait FoldBoundsParse {
55  /// This method allow to implement parse for every type that implement
56  /// FoldBoundsParse.
57  fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
58    &self, parser: &mut Parser, init: &mut Init, f: &mut F, stream: Stream,
59  ) -> Parsed<Acc, Stream, Context>
60  where
61    Stream: Streaming,
62    Context: Contexting<UtilsAtom<Stream>>,
63    Parser: Parse<Stream, Context>,
64    Init: FnMut() -> Acc,
65    F: FnMut(Acc, Parser::Token) -> Acc;
66}
67
68impl<Bounds, Stream, Context, Parser, Acc, Init, F> Parse<Stream, Context>
69  for FoldBounds<Parser, Bounds, Init, F>
70where
71  Stream: Streaming,
72  Context: Contexting<UtilsAtom<Stream>>,
73  Parser: Parse<Stream, Context>,
74  Init: FnMut() -> Acc,
75  F: FnMut(Acc, Parser::Token) -> Acc,
76  Bounds: FoldBoundsParse,
77  Acc: Debug,
78{
79  type Token = Acc;
80
81  // #[cfg_attr(
82  //   feature = "tracing",
83  //   tracing::instrument(level = "trace", skip_all, ret(Display))
84  // )]
85  fn parse(&mut self, stream: Stream) -> Parsed<Acc, Stream, Context> {
86    self
87      .bounds
88      .fold_bounds(&mut self.parser, &mut self.init, &mut self.f, stream)
89  }
90}
91
92macro_rules! allow_failure {
93  ($stream:expr, $parser:expr, $acc:expr, $fold:expr) => {{
94    match $parser.parse($stream.clone()) {
95      Parsed::Success { token, stream } => {
96        $acc = $fold($acc, token);
97        $stream = stream;
98      }
99      Parsed::Failure(_context) => {
100        return Parsed::Success {
101          token: $acc,
102          stream: $stream,
103        };
104      }
105      Parsed::Error(context) => {
106        return Parsed::Error(context);
107      }
108    };
109  }};
110}
111
112macro_rules! deny_failure {
113  ($stream:expr, $parser:expr, $acc:expr, $fold:expr, $min:expr, $i:expr) => {{
114    match $parser.parse($stream) {
115      Parsed::Success { token, stream } => {
116        $acc = $fold($acc, token);
117        $stream = stream;
118      }
119      Parsed::Failure(context) => {
120        return Parsed::Failure(context.add(UtilsAtom::MinNotReach { min: $min, i: $i }));
121      }
122      Parsed::Error(context) => {
123        return Parsed::Error(context);
124      }
125    };
126  }};
127}
128
129impl FoldBoundsParse for RangeFull {
130  fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
131    &self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
132  ) -> Parsed<Acc, Stream, Context>
133  where
134    Stream: Streaming,
135    Context: Contexting<UtilsAtom<Stream>>,
136    Parser: Parse<Stream, Context>,
137    Init: FnMut() -> Acc,
138    F: FnMut(Acc, Parser::Token) -> Acc,
139  {
140    let mut acc = init();
141    loop {
142      allow_failure!(stream, parser, acc, f)
143    }
144  }
145}
146
147impl FoldBoundsParse for RangeFrom<usize> {
148  fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
149    &self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
150  ) -> Parsed<Acc, Stream, Context>
151  where
152    Stream: Streaming,
153    Context: Contexting<UtilsAtom<Stream>>,
154    Parser: Parse<Stream, Context>,
155    Init: FnMut() -> Acc,
156    F: FnMut(Acc, Parser::Token) -> Acc,
157  {
158    let mut acc = init();
159
160    for i in 0..self.start {
161      deny_failure!(stream, parser, acc, f, self.start, i)
162    }
163
164    loop {
165      allow_failure!(stream, parser, acc, f)
166    }
167  }
168}
169
170impl FoldBoundsParse for Range<usize> {
171  fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
172    &self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
173  ) -> Parsed<Acc, Stream, Context>
174  where
175    Stream: Streaming,
176    Context: Contexting<UtilsAtom<Stream>>,
177    Parser: Parse<Stream, Context>,
178    Init: FnMut() -> Acc,
179    F: FnMut(Acc, Parser::Token) -> Acc,
180  {
181    let mut acc = init();
182
183    for i in 0..self.start {
184      deny_failure!(stream, parser, acc, f, self.start, i)
185    }
186
187    for _ in self.start..self.end {
188      allow_failure!(stream, parser, acc, f)
189    }
190
191    Parsed::Success { token: acc, stream }
192  }
193}
194
195impl FoldBoundsParse for RangeInclusive<usize> {
196  fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
197    &self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
198  ) -> Parsed<Acc, Stream, Context>
199  where
200    Stream: Streaming,
201    Context: Contexting<UtilsAtom<Stream>>,
202    Parser: Parse<Stream, Context>,
203    Init: FnMut() -> Acc,
204    F: FnMut(Acc, Parser::Token) -> Acc,
205  {
206    let mut acc = init();
207
208    for i in 0..*self.start() {
209      deny_failure!(stream, parser, acc, f, *self.start(), i)
210    }
211
212    for _ in *self.start()..=*self.end() {
213      allow_failure!(stream, parser, acc, f)
214    }
215
216    Parsed::Success { token: acc, stream }
217  }
218}
219
220impl FoldBoundsParse for RangeTo<usize> {
221  fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
222    &self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
223  ) -> Parsed<Acc, Stream, Context>
224  where
225    Stream: Streaming,
226    Context: Contexting<UtilsAtom<Stream>>,
227    Parser: Parse<Stream, Context>,
228    Init: FnMut() -> Acc,
229    F: FnMut(Acc, Parser::Token) -> Acc,
230  {
231    let mut acc = init();
232
233    for _ in 0..self.end {
234      allow_failure!(stream, parser, acc, f)
235    }
236
237    Parsed::Success { token: acc, stream }
238  }
239}
240
241impl FoldBoundsParse for RangeToInclusive<usize> {
242  fn fold_bounds<Stream, Context, Parser, Acc, Init, 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: FnMut() -> Acc,
250    F: FnMut(Acc, Parser::Token) -> Acc,
251  {
252    let mut acc = init();
253
254    for _ in 0..=self.end {
255      allow_failure!(stream, parser, acc, f)
256    }
257
258    Parsed::Success { token: acc, stream }
259  }
260}
261
262macro_rules! impl_primitive {
263  ($primitive:ident) => {
264    impl FoldBoundsParse for $primitive {
265      fn fold_bounds<Stream, Context, Parser, Acc, Init, F>(
266        &self, parser: &mut Parser, init: &mut Init, f: &mut F, mut stream: Stream,
267      ) -> Parsed<Acc, Stream, Context>
268      where
269        Stream: Clone,
270        Stream: Eq,
271        Context: Contexting<UtilsAtom<Stream>>,
272        Parser: Parse<Stream, Context>,
273        Init: FnMut() -> Acc,
274        F: FnMut(Acc, Parser::Token) -> Acc,
275      {
276        let mut acc = init();
277
278        let min = usize::from(*self);
279        for i in 0..min {
280          deny_failure!(stream, parser, acc, f, min, i)
281        }
282
283        Parsed::Success { token: acc, stream }
284      }
285    }
286  };
287}
288
289macro_rules! impl_primitives {
290  ($($primitives:ident,)*) => {
291    $(impl_primitive!{$primitives})*
292  };
293}
294
295// the error when not using a usize when calling fold is very hard to understand
296// u8 and u16 could be implemented too BUT this could be error prone because of
297// overflow
298impl_primitives!(usize,);
299
300#[cfg(test)]
301mod tests {
302  use core::convert::Infallible;
303
304  use binator_base::{
305    is,
306    BaseAtom,
307  };
308  use binator_context::{
309    Keep,
310    Last,
311  };
312  use binator_core::{
313    Contexting,
314    CoreAtom,
315    Parse,
316    Parsed,
317    Streaming,
318  };
319  use derive_more::{
320    Display,
321    From,
322  };
323
324  use crate::{
325    Utils,
326    UtilsAtom,
327  };
328
329  #[derive(Display, Debug, Clone, From, PartialEq)]
330  enum FromAtom<Stream: Streaming> {
331    Fold(UtilsAtom<Stream>),
332    Is(BaseAtom<u8>),
333    Any(CoreAtom<Stream, Infallible>),
334    Stream,
335  }
336
337  // impl<Stream> PartialEq for FromAtom<Stream> {
338  //   fn eq(&self, other: &Self) -> bool {
339  //     discriminant(self) == discriminant(other)
340  //   }
341  // }
342
343  type HandleAtom<Stream> = Keep<Last, FromAtom<Stream>>;
344
345  #[test]
346  fn fold_bounds_full() {
347    let stream = b"[".as_ref();
348
349    let result: Parsed<_, _, HandleAtom<_>> = is(b'[')
350      .or(is(b']'))
351      .fold_bounds(.., || (), |_, _| ())
352      .parse(stream);
353    let expected = Parsed::Success {
354      token: (),
355      stream: &stream[1..],
356    };
357    assert_eq!(result, expected);
358  }
359
360  #[test]
361  fn fold_bounds() {
362    let stream = b"[".as_ref();
363    let result: Parsed<_, _, HandleAtom<_>> = is(b'[')
364      .or(is(b']'))
365      .fold_bounds(1..5, || (), |_, _| ())
366      .parse(stream);
367    let expected = Parsed::Success {
368      token: (),
369      stream: &stream[1..],
370    };
371    assert_eq!(result, expected);
372  }
373
374  #[test]
375  fn fold_bounds_failure() {
376    let stream = b"abcd".as_ref();
377    let result: Parsed<_, _, HandleAtom<_>> = is(b'[')
378      .or(is(b']'))
379      .fold_bounds(1..5, || (), |_, _| ())
380      .parse(stream);
381    let context = Keep::new(UtilsAtom::MinNotReach { i: 0, min: 1 });
382    assert_eq!(result, Parsed::Failure(context));
383  }
384}