From 2e3a103d86511f695d95f626ac802138bfe3fe81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Peccatte?= Date: Thu, 24 Jul 2025 13:53:37 +0200 Subject: [PATCH 1/5] Add the Set combinator that applies a set of parser without order --- src/lib.rs | 2 +- src/primitive.rs | 123 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 0b85fa9a..50390d44 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,7 +75,7 @@ pub mod prelude { extra, input::Input, primitive::{ - any, any_ref, choice, custom, empty, end, group, just, map_ctx, none_of, one_of, todo, + any, any_ref, choice, custom, empty, end, group, set, just, map_ctx, none_of, one_of, todo, }, recovery::{nested_delimiters, skip_then_retry_until, skip_until, via_parser}, recursive::{recursive, Recursive}, diff --git a/src/primitive.rs b/src/primitive.rs index 321fa0f5..efc213ed 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -1131,3 +1131,126 @@ impl_group_for_tuple! { Y_ OY Z_ OZ } + +/// See [`set`]. +#[derive(Copy, Clone)] +pub struct Set { + parsers: T, +} + +/// Parse using a tuple of parsers in any order, producing the output of the parser set in the provided order. +/// +/// The output type of this parser is a tuple of the output types of the inner parsers. +/// +/// All parsers must successfully parse. +/// +/// Parsers are always in the provided order, which means that most specific parsers must be +/// provided first or they might never match. +/// +/// Parsers that match without making progress (like `empty()` or `something.or_not()`) are applied +/// last to make sure they have no better alternative. +/// +/// This means that you can make a parser optional by adding a call to `.or_not()` to it. +/// +/// # Examples +/// +/// ``` +/// # use chumsky::prelude::*; +/// let options = set(( +/// just("option_a\n"), +/// just("option_b\n"), +/// just("option_c\n").or_not(), +/// )); +/// +/// assert_eq!( +/// options.parse("option_b\noption_a\n"), +/// Ok(("option_a\n", "option_b\n", None)), +/// ); +/// ``` +pub const fn set(parsers: T) -> Set { + Set { parsers } +} + +fn go_or_finish<'src, O, I, E, P>(item: &mut Option, parser: &P, inp: &mut InputRef<'src, '_, I, E>) -> PResult +where + I: Input<'src>, + E: ParserExtra<'src, I>, + P: Parser<'src, I, O, E>, +{ + if item.is_none() { + match parser.go::(inp) { + Ok(out) => { + *item = Some(out); + } + Err(()) => { return Err(()); } + } + } + Ok(()) + +} + +fn go_or_rewind<'src, O, I, E, P>(item: &mut Option, parser: &P, inp: &mut InputRef<'src, '_, I, E>) +where + I: Input<'src>, + E: ParserExtra<'src, I>, + P: Parser<'src, I, O, E>, +{ + if item.is_none() { + let save_before = inp.save(); + let pos_before = inp.cursor(); + match parser.go::(inp) { + Ok(out) => { + if pos_before == inp.cursor() { + inp.rewind(save_before.clone()); + } else { + *item = Some(out); + } + } + Err(()) => { inp.rewind(save_before.clone()) } + } + } +} + +macro_rules! impl_set_for_tuple { + () => {}; + ($head_1:ident $head_2:ident $head_3:ident $($X:ident)*) => { + impl_set_for_tuple!($($X)*); + impl_set_for_tuple!(~ $head_1 $head_2 $head_3 $($X)*); + }; + (~ $($O:ident $P:ident $I:ident)+) => { + #[allow(unused_variables, non_snake_case)] + impl<'src, I, E, $($P),*, $($O,)*> Parser<'src, I, ($($O,)*), E> for Set<($($P,)*)> + where + I: Input<'src>, + E: ParserExtra<'src, I>, + $($P: Parser<'src, I, $O, E>),* + { + #[inline] + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { + let Set { parsers: ($($P,)*), .. } = self; + $( let mut $I = None; )* + + // first iterate until there are no progress + loop { + let start = inp.cursor(); + $( go_or_rewind(&mut $I, $P, inp); )* + // none matched during this loop + if start == inp.cursor() { + break; + } + } + + // Then a final iteration that matches remaining empty parsers + $( go_or_finish(&mut $I, $P, inp)?; )* + + // unwrap is ok since we matched all items in the set exactly once + Ok(M::bind(|| ( $($I.unwrap(),)* ))) + } + + go_extra!(($($O,)*)); + } + }; +} + +impl_set_for_tuple!(A1 A2 A3 B1 B2 B3 C1 C2 C3 D1 D2 D3 E1 E2 E3 F1 F2 F3 G1 G2 G3 H1 H2 H3 I1 I2 I3 J1 J2 J3 K1 K2 K3 L1 L2 L3 M1 M2 M3 N1 N2 N3 O1 O2 O3 P1 P2 P3 Q1 Q2 Q3 R1 R2 R3 S1 S2 S3 T1 T2 T3 U1 U2 U3 V1 V2 V3 W1 W2 W3 X1 X2 X3 Y1 Y2 Y3 Z1 Z2 Z3); + From bdf8eb6bf67a1bdb175d1823707acd349f03e6a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Peccatte?= Date: Thu, 24 Jul 2025 17:39:35 +0200 Subject: [PATCH 2/5] Fix doctests and comments --- src/lib.rs | 3 ++- src/primitive.rs | 41 +++++++++++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 50390d44..e740f257 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,7 +75,8 @@ pub mod prelude { extra, input::Input, primitive::{ - any, any_ref, choice, custom, empty, end, group, set, just, map_ctx, none_of, one_of, todo, + any, any_ref, choice, custom, empty, end, group, just, map_ctx, none_of, one_of, set, + todo, }, recovery::{nested_delimiters, skip_then_retry_until, skip_until, via_parser}, recursive::{recursive, Recursive}, diff --git a/src/primitive.rs b/src/primitive.rs index efc213ed..60d29bf8 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -1144,8 +1144,14 @@ pub struct Set { /// /// All parsers must successfully parse. /// -/// Parsers are always in the provided order, which means that most specific parsers must be -/// provided first or they might never match. +/// Parsers are always tried in the provided order, which means that most specific parsers must be +/// provided first or they might never match. In other words, if two parsers match the same item, +/// the `set`'s input must containt this item twice for the `set` to be successful. +/// +/// Be careful! This combinator is O(n^2), which means that in the worst case you could slow down parsing with +/// a lot of parser calls. The worst case happens when all items are present but in the reverse +/// order compared to the list of parsers. To mitigate this you should order your parsers in the +/// same order input usually happens. And you should avoid having too many parsers in the set. /// /// Parsers that match without making progress (like `empty()` or `something.or_not()`) are applied /// last to make sure they have no better alternative. @@ -1157,21 +1163,25 @@ pub struct Set { /// ``` /// # use chumsky::prelude::*; /// let options = set(( -/// just("option_a\n"), -/// just("option_b\n"), -/// just("option_c\n").or_not(), +/// just::<_, _, extra::Err>>("apple\n"), +/// just("banana\n"), +/// just("carrot\n").or_not(), /// )); /// /// assert_eq!( -/// options.parse("option_b\noption_a\n"), -/// Ok(("option_a\n", "option_b\n", None)), +/// options.parse("banana\napple\n").into_result(), +/// Ok(("apple\n", "banana\n", None)), /// ); /// ``` pub const fn set(parsers: T) -> Set { Set { parsers } } -fn go_or_finish<'src, O, I, E, P>(item: &mut Option, parser: &P, inp: &mut InputRef<'src, '_, I, E>) -> PResult +fn go_or_finish<'src, O, I, E, P>( + item: &mut Option, + parser: &P, + inp: &mut InputRef<'src, '_, I, E>, +) -> PResult where I: Input<'src>, E: ParserExtra<'src, I>, @@ -1182,15 +1192,19 @@ where Ok(out) => { *item = Some(out); } - Err(()) => { return Err(()); } + Err(()) => { + return Err(()); + } } } Ok(()) - } -fn go_or_rewind<'src, O, I, E, P>(item: &mut Option, parser: &P, inp: &mut InputRef<'src, '_, I, E>) -where +fn go_or_rewind<'src, O, I, E, P>( + item: &mut Option, + parser: &P, + inp: &mut InputRef<'src, '_, I, E>, +) where I: Input<'src>, E: ParserExtra<'src, I>, P: Parser<'src, I, O, E>, @@ -1206,7 +1220,7 @@ where *item = Some(out); } } - Err(()) => { inp.rewind(save_before.clone()) } + Err(()) => inp.rewind(save_before.clone()), } } } @@ -1253,4 +1267,3 @@ macro_rules! impl_set_for_tuple { } impl_set_for_tuple!(A1 A2 A3 B1 B2 B3 C1 C2 C3 D1 D2 D3 E1 E2 E3 F1 F2 F3 G1 G2 G3 H1 H2 H3 I1 I2 I3 J1 J2 J3 K1 K2 K3 L1 L2 L3 M1 M2 M3 N1 N2 N3 O1 O2 O3 P1 P2 P3 Q1 Q2 Q3 R1 R2 R3 S1 S2 S3 T1 T2 T3 U1 U2 U3 V1 V2 V3 W1 W2 W3 X1 X2 X3 Y1 Y2 Y3 Z1 Z2 Z3); - From 3c7ed42c86eccacb796068efe8cf339b28c89d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Peccatte?= Date: Fri, 25 Jul 2025 09:18:02 +0200 Subject: [PATCH 3/5] Add tests and support for vecs and arrays --- src/primitive.rs | 129 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/src/primitive.rs b/src/primitive.rs index 60d29bf8..2989248a 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -1267,3 +1267,132 @@ macro_rules! impl_set_for_tuple { } impl_set_for_tuple!(A1 A2 A3 B1 B2 B3 C1 C2 C3 D1 D2 D3 E1 E2 E3 F1 F2 F3 G1 G2 G3 H1 H2 H3 I1 I2 I3 J1 J2 J3 K1 K2 K3 L1 L2 L3 M1 M2 M3 N1 N2 N3 O1 O2 O3 P1 P2 P3 Q1 Q2 Q3 R1 R2 R3 S1 S2 S3 T1 T2 T3 U1 U2 U3 V1 V2 V3 W1 W2 W3 X1 X2 X3 Y1 Y2 Y3 Z1 Z2 Z3); + +impl<'src, P, I, O, E> Parser<'src, I, Vec, E> for Set> +where + P: Parser<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, +{ + #[inline] + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult> { + let mut tmp = self.parsers.iter().map(|_| None).collect::>>(); + + // first iterate until there are no progress + loop { + let start = inp.cursor(); + for i in 0..self.parsers.len() { + go_or_rewind(&mut tmp[i], &self.parsers[i], inp); + } + // none matched during this loop + if start == inp.cursor() { + break; + } + } + + // Then a final iteration that matches remaining empty parsers + for i in 0..self.parsers.len() { + go_or_finish(&mut tmp[i], &self.parsers[i], inp)?; + } + + // unwrap is ok since we matched all items in the se + Ok(M::bind(|| tmp.into_iter().map(|x| x.unwrap()).collect() )) + } + + go_extra!(Vec); +} + +impl<'src, P, I, O, E, const N: usize> Parser<'src, I, [O; N], E> for Set<[P; N]> +where + P: Parser<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, +{ + #[inline] + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { + let mut tmp: [Option; N] = [const { None }; N]; + + // first iterate until there are no progress + loop { + let start = inp.cursor(); + for i in 0..N { + go_or_rewind(&mut tmp[i], &self.parsers[i], inp); + } + // none matched during this loop + if start == inp.cursor() { + break; + } + } + + // Then a final iteration that matches remaining empty parsers + for i in 0..N { + go_or_finish(&mut tmp[i], &self.parsers[i], inp)?; + } + + // unwrap is ok since we matched all items in the se + Ok(M::bind(|| tmp.map(|x| x.unwrap()) )) + } + + go_extra!([O; N]); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn set_parser() { + fn just(s: &str) -> Just<&str, &str, extra::Err> { + super::just(s) + } + + // test ordering + let parser = set((just("abc"), just("def"), just("ijk") )); + assert_eq!( + parser.parse("ijkdefabc").into_result(), + Ok(("abc", "def", "ijk")), + ); + assert_eq!( + parser.parse("abcdefijk").into_result(), + Ok(("abc", "def", "ijk")), + ); + + // test inclusion + let parser = set(( choice((just("abc"), just("def"))), just("abc") )); + assert_eq!( + parser.parse("defabc").into_result(), + Ok(("def", "abc")), + ); + let parser = set(( choice((just("abc"), just("def"))), just("abc") )); + assert!(parser.parse("abcdef").into_result().is_err()); + + // test optionals + let parser = set((just("abc").or_not(), just("def").or_not(), just("ijk").or_not() )); + assert_eq!( + parser.parse("ijkdefabc").into_result(), + Ok((Some("abc"), Some("def"), Some("ijk"))), + ); + assert_eq!( + parser.parse("ijkabc").into_result(), + Ok((Some("abc"), None, Some("ijk"))), + ); + assert_eq!( + parser.parse("").into_result(), + Ok((None, None, None)), + ); + + // test types + let parser = set(vec![just("abc"), just("def"), just("ijk") ]); + assert_eq!( + parser.parse("ijkdefabc").into_result(), + Ok(vec!["abc", "def", "ijk"]), + ); + let parser = set([just("abc"), just("def"), just("ijk") ]); + assert_eq!( + parser.parse("ijkdefabc").into_result(), + Ok(["abc", "def", "ijk"]), + ); + + } +} + From 4a9aedde2d57f300b7b11ba66a8759042a91cd9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Peccatte?= Date: Fri, 25 Jul 2025 18:57:02 +0200 Subject: [PATCH 4/5] Make internal `go()` call inside `set`generic over `Mode`, and fix some clippy arrors --- src/primitive.rs | 61 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/src/primitive.rs b/src/primitive.rs index 2989248a..b5bbec96 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -1177,18 +1177,19 @@ pub const fn set(parsers: T) -> Set { Set { parsers } } -fn go_or_finish<'src, O, I, E, P>( - item: &mut Option, +fn go_or_finish<'src, O, I, E, P, M>( + item: &mut Option>, parser: &P, inp: &mut InputRef<'src, '_, I, E>, -) -> PResult +) -> PResult where I: Input<'src>, E: ParserExtra<'src, I>, P: Parser<'src, I, O, E>, + M: Mode, { if item.is_none() { - match parser.go::(inp) { + match parser.go::(inp) { Ok(out) => { *item = Some(out); } @@ -1197,22 +1198,23 @@ where } } } - Ok(()) + Ok(M::bind(|| ())) } -fn go_or_rewind<'src, O, I, E, P>( - item: &mut Option, +fn go_or_rewind<'src, O, I, E, P, M>( + item: &mut Option>, parser: &P, inp: &mut InputRef<'src, '_, I, E>, ) where I: Input<'src>, E: ParserExtra<'src, I>, P: Parser<'src, I, O, E>, + M: Mode, { if item.is_none() { let save_before = inp.save(); let pos_before = inp.cursor(); - match parser.go::(inp) { + match parser.go::(inp) { Ok(out) => { if pos_before == inp.cursor() { inp.rewind(save_before.clone()); @@ -1242,12 +1244,12 @@ macro_rules! impl_set_for_tuple { #[inline] fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { let Set { parsers: ($($P,)*), .. } = self; - $( let mut $I = None; )* + $( let mut $I: Option> = None; )* // first iterate until there are no progress loop { let start = inp.cursor(); - $( go_or_rewind(&mut $I, $P, inp); )* + $( go_or_rewind::<_, _, _, _, M>(&mut $I, $P, inp); )* // none matched during this loop if start == inp.cursor() { break; @@ -1255,10 +1257,11 @@ macro_rules! impl_set_for_tuple { } // Then a final iteration that matches remaining empty parsers - $( go_or_finish(&mut $I, $P, inp)?; )* + $( go_or_finish::<_, _, _, _, M>(&mut $I, $P, inp)?; )* // unwrap is ok since we matched all items in the set exactly once - Ok(M::bind(|| ( $($I.unwrap(),)* ))) + $( let $I = $I.unwrap(); )* + Ok(flatten_map!( $($I)*)) } go_extra!(($($O,)*)); @@ -1268,6 +1271,8 @@ macro_rules! impl_set_for_tuple { impl_set_for_tuple!(A1 A2 A3 B1 B2 B3 C1 C2 C3 D1 D2 D3 E1 E2 E3 F1 F2 F3 G1 G2 G3 H1 H2 H3 I1 I2 I3 J1 J2 J3 K1 K2 K3 L1 L2 L3 M1 M2 M3 N1 N2 N3 O1 O2 O3 P1 P2 P3 Q1 Q2 Q3 R1 R2 R3 S1 S2 S3 T1 T2 T3 U1 U2 U3 V1 V2 V3 W1 W2 W3 X1 X2 X3 Y1 Y2 Y3 Z1 Z2 Z3); + +#[allow(clippy::needless_range_loop)] impl<'src, P, I, O, E> Parser<'src, I, Vec, E> for Set> where P: Parser<'src, I, O, E>, @@ -1276,13 +1281,13 @@ where { #[inline] fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult> { - let mut tmp = self.parsers.iter().map(|_| None).collect::>>(); + let mut tmp = self.parsers.iter().map(|_| None).collect::>>>(); // first iterate until there are no progress loop { let start = inp.cursor(); for i in 0..self.parsers.len() { - go_or_rewind(&mut tmp[i], &self.parsers[i], inp); + go_or_rewind::<_, _, _, _, M>(&mut tmp[i], &self.parsers[i], inp); } // none matched during this loop if start == inp.cursor() { @@ -1292,31 +1297,43 @@ where // Then a final iteration that matches remaining empty parsers for i in 0..self.parsers.len() { - go_or_finish(&mut tmp[i], &self.parsers[i], inp)?; + go_or_finish::<_, _, _, _, M>(&mut tmp[i], &self.parsers[i], inp)?; } // unwrap is ok since we matched all items in the se - Ok(M::bind(|| tmp.into_iter().map(|x| x.unwrap()).collect() )) + let mut result = M::bind(|| vec![]); + tmp.into_iter().for_each( + |x| M::combine_mut(&mut result, x.unwrap(), + |result,x| result.push(x) )); + Ok(result) } go_extra!(Vec); } +#[allow(clippy::needless_range_loop)] impl<'src, P, I, O, E, const N: usize> Parser<'src, I, [O; N], E> for Set<[P; N]> where P: Parser<'src, I, O, E>, I: Input<'src>, E: ParserExtra<'src, I>, + // remove this requirement when MSRV > 1.80 + O: Copy, { #[inline] - fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { - let mut tmp: [Option; N] = [const { None }; N]; + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult + { + // Replace this when MRSV > 1.80 + //let mut tmp: [Option>; N] = [ const { None }; N]; + let mut tmp: [Option; N] = [ None; N]; // first iterate until there are no progress loop { let start = inp.cursor(); for i in 0..N { - go_or_rewind(&mut tmp[i], &self.parsers[i], inp); + // Replace this when MRSV > 1.80 + //go_or_rewind::<_, _, _, _, M>(&mut tmp[i], &self.parsers[i], inp); + go_or_rewind::<_, _, _, _, Emit>(&mut tmp[i], &self.parsers[i], inp); } // none matched during this loop if start == inp.cursor() { @@ -1326,10 +1343,14 @@ where // Then a final iteration that matches remaining empty parsers for i in 0..N { - go_or_finish(&mut tmp[i], &self.parsers[i], inp)?; + // Replace this when MRSV > 1.80 + //go_or_finish::<_, _, _, _, M>(&mut tmp[i], &self.parsers[i], inp)?; + go_or_finish::<_, _, _, _, Emit>(&mut tmp[i], &self.parsers[i], inp)?; } // unwrap is ok since we matched all items in the se + // Replace this when MRSV > 1.80 + //Ok(M::array( tmp.map(|x| x.unwrap()) )) Ok(M::bind(|| tmp.map(|x| x.unwrap()) )) } From ccf8cd2b7f3c681d69b18d51e343b0a0f1f4c9ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Peccatte?= Date: Fri, 25 Jul 2025 19:19:41 +0200 Subject: [PATCH 5/5] Even more clippy cleanup --- src/primitive.rs | 71 ++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/src/primitive.rs b/src/primitive.rs index b5bbec96..67cdb04d 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -1271,8 +1271,6 @@ macro_rules! impl_set_for_tuple { impl_set_for_tuple!(A1 A2 A3 B1 B2 B3 C1 C2 C3 D1 D2 D3 E1 E2 E3 F1 F2 F3 G1 G2 G3 H1 H2 H3 I1 I2 I3 J1 J2 J3 K1 K2 K3 L1 L2 L3 M1 M2 M3 N1 N2 N3 O1 O2 O3 P1 P2 P3 Q1 Q2 Q3 R1 R2 R3 S1 S2 S3 T1 T2 T3 U1 U2 U3 V1 V2 V3 W1 W2 W3 X1 X2 X3 Y1 Y2 Y3 Z1 Z2 Z3); - -#[allow(clippy::needless_range_loop)] impl<'src, P, I, O, E> Parser<'src, I, Vec, E> for Set> where P: Parser<'src, I, O, E>, @@ -1281,13 +1279,17 @@ where { #[inline] fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult> { - let mut tmp = self.parsers.iter().map(|_| None).collect::>>>(); + let mut tmp = self + .parsers + .iter() + .map(|_| None) + .collect::>>>(); // first iterate until there are no progress loop { let start = inp.cursor(); - for i in 0..self.parsers.len() { - go_or_rewind::<_, _, _, _, M>(&mut tmp[i], &self.parsers[i], inp); + for (i, parser) in self.parsers.iter().enumerate() { + go_or_rewind::<_, _, _, _, M>(&mut tmp[i], parser, inp); } // none matched during this loop if start == inp.cursor() { @@ -1296,22 +1298,20 @@ where } // Then a final iteration that matches remaining empty parsers - for i in 0..self.parsers.len() { - go_or_finish::<_, _, _, _, M>(&mut tmp[i], &self.parsers[i], inp)?; + for (i, parser) in self.parsers.iter().enumerate() { + go_or_finish::<_, _, _, _, M>(&mut tmp[i], parser, inp)?; } // unwrap is ok since we matched all items in the se - let mut result = M::bind(|| vec![]); - tmp.into_iter().for_each( - |x| M::combine_mut(&mut result, x.unwrap(), - |result,x| result.push(x) )); + let mut result = M::bind(|| Vec::new()); + tmp.into_iter() + .for_each(|x| M::combine_mut(&mut result, x.unwrap(), |result, x| result.push(x))); Ok(result) } go_extra!(Vec); } -#[allow(clippy::needless_range_loop)] impl<'src, P, I, O, E, const N: usize> Parser<'src, I, [O; N], E> for Set<[P; N]> where P: Parser<'src, I, O, E>, @@ -1321,19 +1321,18 @@ where O: Copy, { #[inline] - fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult - { + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { // Replace this when MRSV > 1.80 //let mut tmp: [Option>; N] = [ const { None }; N]; - let mut tmp: [Option; N] = [ None; N]; + let mut tmp: [Option; N] = [None; N]; // first iterate until there are no progress loop { let start = inp.cursor(); - for i in 0..N { + for (i, parser) in self.parsers.iter().enumerate() { // Replace this when MRSV > 1.80 - //go_or_rewind::<_, _, _, _, M>(&mut tmp[i], &self.parsers[i], inp); - go_or_rewind::<_, _, _, _, Emit>(&mut tmp[i], &self.parsers[i], inp); + //go_or_rewind::<_, _, _, _, M>(&mut tmp[i], parser, inp); + go_or_rewind::<_, _, _, _, Emit>(&mut tmp[i], parser, inp); } // none matched during this loop if start == inp.cursor() { @@ -1342,16 +1341,16 @@ where } // Then a final iteration that matches remaining empty parsers - for i in 0..N { + for (i, parser) in self.parsers.iter().enumerate() { // Replace this when MRSV > 1.80 - //go_or_finish::<_, _, _, _, M>(&mut tmp[i], &self.parsers[i], inp)?; - go_or_finish::<_, _, _, _, Emit>(&mut tmp[i], &self.parsers[i], inp)?; + //go_or_finish::<_, _, _, _, M>(&mut tmp[i], parser, inp)?; + go_or_finish::<_, _, _, _, Emit>(&mut tmp[i], parser, inp)?; } // unwrap is ok since we matched all items in the se // Replace this when MRSV > 1.80 //Ok(M::array( tmp.map(|x| x.unwrap()) )) - Ok(M::bind(|| tmp.map(|x| x.unwrap()) )) + Ok(M::bind(|| tmp.map(|x| x.unwrap()))) } go_extra!([O; N]); @@ -1368,7 +1367,7 @@ mod tests { } // test ordering - let parser = set((just("abc"), just("def"), just("ijk") )); + let parser = set((just("abc"), just("def"), just("ijk"))); assert_eq!( parser.parse("ijkdefabc").into_result(), Ok(("abc", "def", "ijk")), @@ -1379,16 +1378,17 @@ mod tests { ); // test inclusion - let parser = set(( choice((just("abc"), just("def"))), just("abc") )); - assert_eq!( - parser.parse("defabc").into_result(), - Ok(("def", "abc")), - ); - let parser = set(( choice((just("abc"), just("def"))), just("abc") )); + let parser = set((choice((just("abc"), just("def"))), just("abc"))); + assert_eq!(parser.parse("defabc").into_result(), Ok(("def", "abc")),); + let parser = set((choice((just("abc"), just("def"))), just("abc"))); assert!(parser.parse("abcdef").into_result().is_err()); // test optionals - let parser = set((just("abc").or_not(), just("def").or_not(), just("ijk").or_not() )); + let parser = set(( + just("abc").or_not(), + just("def").or_not(), + just("ijk").or_not(), + )); assert_eq!( parser.parse("ijkdefabc").into_result(), Ok((Some("abc"), Some("def"), Some("ijk"))), @@ -1397,23 +1397,18 @@ mod tests { parser.parse("ijkabc").into_result(), Ok((Some("abc"), None, Some("ijk"))), ); - assert_eq!( - parser.parse("").into_result(), - Ok((None, None, None)), - ); + assert_eq!(parser.parse("").into_result(), Ok((None, None, None)),); // test types - let parser = set(vec![just("abc"), just("def"), just("ijk") ]); + let parser = set(vec![just("abc"), just("def"), just("ijk")]); assert_eq!( parser.parse("ijkdefabc").into_result(), Ok(vec!["abc", "def", "ijk"]), ); - let parser = set([just("abc"), just("def"), just("ijk") ]); + let parser = set([just("abc"), just("def"), just("ijk")]); assert_eq!( parser.parse("ijkdefabc").into_result(), Ok(["abc", "def", "ijk"]), ); - } } -