Skip to content
This repository was archived by the owner on Apr 2, 2026. It is now read-only.

Commit a1bbe4e

Browse files
authored
Merge pull request #287 from CraftSpider/zc-choice-experiment
[zero-copy] Update choice impl a little, add array impl from master
2 parents 7aa32b7 + a343723 commit a1bbe4e

3 files changed

Lines changed: 125 additions & 11 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,7 @@ harness = false
4545
[[bench]]
4646
name = "lex"
4747
harness = false
48+
49+
[[bench]]
50+
name = "parser"
51+
harness = false

benches/parser.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
2+
use chumsky::zero_copy::prelude::*;
3+
use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId};
4+
5+
fn bench_choice(c: &mut Criterion) {
6+
let alphabet_choice = choice((
7+
just::<_, str, extra::Default>('A'),
8+
just('B'),
9+
just('C'),
10+
just('D'),
11+
just('E'),
12+
just('F'),
13+
just('G'),
14+
just('H'),
15+
just('I'),
16+
just('J'),
17+
just('K'),
18+
just('L'),
19+
just('M'),
20+
just('N'),
21+
just('O'),
22+
just('P'),
23+
just('Q'),
24+
just('R'),
25+
just('S'),
26+
just('T'),
27+
just('U'),
28+
just('V'),
29+
just('W'),
30+
just('X'),
31+
just('Y'),
32+
just('Z'),
33+
));
34+
35+
let mut group = c.benchmark_group("choice");
36+
37+
group.bench_function(BenchmarkId::new("choice::<(A..Z)>", "A"), |b| {
38+
b.iter(|| {
39+
black_box(Parser::parse(&alphabet_choice, black_box("A"))).into_result().unwrap();
40+
})
41+
});
42+
43+
group.bench_function(BenchmarkId::new("choice::<(A..Z)>", "Z"), |b| {
44+
b.iter(|| {
45+
black_box(alphabet_choice.parse(black_box("Z"))).into_result().unwrap();
46+
})
47+
});
48+
49+
group.bench_function(BenchmarkId::new("choice::<(A..Z)>", "0"), |b| {
50+
b.iter(|| {
51+
black_box(alphabet_choice.parse(black_box("0"))).into_result().unwrap_err();
52+
})
53+
});
54+
}
55+
56+
criterion_group!(benches, bench_choice);
57+
criterion_main!(benches);

src/zero_copy/primitive.rs

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -767,44 +767,97 @@ macro_rules! impl_choice_for_tuple {
767767
impl_choice_for_tuple!($($X)*);
768768
impl_choice_for_tuple!(~ $head $($X)*);
769769
};
770-
(~ $($X:ident)*) => {
770+
(~ $Head:ident $($X:ident)+) => {
771771
#[allow(unused_variables, non_snake_case)]
772-
impl<'a, I, E, $($X),*, O> Parser<'a, I, O, E> for Choice<($($X,)*), O>
772+
impl<'a, I, E, $Head, $($X),*, O> Parser<'a, I, O, E> for Choice<($Head, $($X,)*), O>
773773
where
774774
I: Input + ?Sized,
775775
E: ParserExtra<'a, I>,
776+
$Head: Parser<'a, I, O, E>,
776777
$($X: Parser<'a, I, O, E>),*
777778
{
778779
#[inline]
779780
fn go<M: Mode>(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult<M, O, E::Error> {
780781
let before = inp.save();
781782

782-
let Choice { parsers: ($($X,)*), .. } = self;
783+
let Choice { parsers: ($Head, $($X,)*), .. } = self;
784+
785+
let mut err = match $Head.go::<M>(inp) {
786+
Ok(out) => return Ok(out),
787+
Err(e) => {
788+
inp.rewind(before);
789+
e
790+
}
791+
};
783792

784-
let mut err: Option<Located<E::Error>> = None;
785793
$(
786794
match $X.go::<M>(inp) {
787795
Ok(out) => return Ok(out),
788796
Err(e) => {
789797
// TODO: prioritise errors
790-
err = Some(match err {
791-
Some(err) => err.prioritize(e, |a, b| a.merge(b)),
792-
None => e,
793-
});
798+
err = err.prioritize(e, |a, b| a.merge(b));
794799
inp.rewind(before);
795-
},
800+
}
796801
}
797802
)*
798803

799-
Err(err.unwrap_or_else(|| Located::at(inp.offset().into(), E::Error::expected_found(None, None, inp.span_since(before.offset)))))
804+
Err(err)
805+
}
806+
807+
go_extra!(O);
808+
}
809+
};
810+
(~ $Head:ident) => {
811+
impl<'a, I, E, $Head, O> Parser<'a, I, O, E> for Choice<($Head,), O>
812+
where
813+
I: Input + ?Sized,
814+
E: ParserExtra<'a, I>,
815+
$Head: Parser<'a, I, O, E>,
816+
{
817+
#[inline]
818+
fn go<M: Mode>(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult<M, O, E::Error> {
819+
self.parsers.0.go::<M>(inp)
800820
}
801821

802822
go_extra!(O);
803823
}
804824
};
805825
}
806826

807-
impl_choice_for_tuple!(A_ B_ C_ D_ E_ F_ G_ H_ I_ J_ K_ L_ M_ N_ O_ P_ Q_ S_ T_ U_ V_ W_ X_ Y_ Z_);
827+
impl_choice_for_tuple!(A_ B_ C_ D_ E_ F_ G_ H_ I_ J_ K_ L_ M_ N_ O_ P_ Q_ R_ S_ T_ U_ V_ W_ X_ Y_ Z_);
828+
829+
impl<'a, A, I, O, E, const N: usize> Parser<'a, I, O, E> for Choice<[A; N], O>
830+
where
831+
A: Parser<'a, I, O, E>,
832+
I: Input + ?Sized,
833+
E: ParserExtra<'a, I>,
834+
{
835+
fn go<M: Mode>(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult<M, O, E::Error> {
836+
let before = inp.save();
837+
let res = self.parsers
838+
.iter()
839+
.try_fold(None::<Located<E::Error>>, |err, parser, | {
840+
match parser.go::<M>(inp) {
841+
Ok(out) => Err(out),
842+
Err(e) => {
843+
Ok(Some(match err {
844+
Some(err) => err.prioritize(e, |a, b| a.merge(b)),
845+
None => e,
846+
}))
847+
}
848+
}
849+
});
850+
851+
match res {
852+
Ok(err) => Err(err.unwrap_or_else(
853+
|| Located::at(inp.offset().into(), E::Error::expected_found(None, None, inp.span_since(before.offset)))
854+
)),
855+
Err(out) => Ok(out),
856+
}
857+
}
858+
859+
go_extra!(O);
860+
}
808861

809862
/// See [`group`].
810863
#[derive(Copy, Clone)]

0 commit comments

Comments
 (0)