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#[derive(Clone)]
24pub struct FoldBounds<Parser, Bounds, Init, F> {
25 parser: Parser,
26 bounds: Bounds,
27 init: Init,
28 f: F,
29}
30
31pub 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
52pub trait FoldBoundsParse {
55 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 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
295impl_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 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}