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

Commit 5ea6acb

Browse files
committed
Slightly cursed visitor stuff
1 parent cf7b266 commit 5ea6acb

5 files changed

Lines changed: 396 additions & 1 deletion

File tree

examples/zero-copy.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use chumsky::zero_copy::prelude::*;
2+
use chumsky::zero_copy::problems::Problems;
23

34
#[derive(PartialEq, Debug)]
45
enum Token<'a> {
@@ -27,6 +28,17 @@ fn parser<'a>() -> impl Parser<'a, str, [(SimpleSpan<usize>, Token<'a>); 6]> {
2728
.collect()
2829
}
2930

31+
fn bad_parser() -> impl for<'a> Parser<'a, str, Vec<()>, extra::Err<Rich<str>>> + Problems {
32+
just("").or(just("b"))
33+
.or_not()
34+
.separated_by(just(""))
35+
.ignored()
36+
.then(just("").ignored())
37+
.ignored()
38+
.repeated()
39+
.collect::<_>()
40+
}
41+
3042
fn main() {
3143
assert_eq!(
3244
parser()
@@ -41,4 +53,9 @@ fn main() {
4153
((31..37).into(), Token::Ident("tokens")),
4254
]),
4355
);
56+
57+
let p = bad_parser();
58+
for p in p.find_problems() {
59+
println!("{p}");
60+
}
4461
}

src/zero_copy/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,14 @@ pub mod error;
4444
pub mod extra;
4545
pub mod input;
4646
pub mod primitive;
47+
pub mod problems;
4748
pub mod recovery;
4849
pub mod recursive;
4950
#[cfg(feature = "regex")]
5051
pub mod regex;
5152
pub mod span;
5253
pub mod text;
54+
pub(crate) mod visit;
5355

5456
/// Commonly used functions, traits and types.
5557
///

src/zero_copy/primitive.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ impl<T> Default for JustCfg<T> {
147147

148148
/// See [`just`].
149149
pub struct Just<T, I: ?Sized, E = EmptyErr> {
150-
seq: T,
150+
pub(crate) seq: T,
151151
phantom: PhantomData<(E, I)>,
152152
}
153153

src/zero_copy/problems.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//! TODO
2+
3+
use crate::zero_copy::visit::{ParserInfo, ParserVisitor, Visitable};
4+
5+
#[derive(Clone, PartialEq)]
6+
enum CheckState {
7+
None,
8+
Check(String),
9+
Defer(String),
10+
}
11+
12+
struct ProblemVisitor {
13+
problems: Vec<String>,
14+
15+
first: bool,
16+
check_nonempty: CheckState,
17+
}
18+
19+
impl ProblemVisitor {
20+
fn new() -> Self {
21+
ProblemVisitor {
22+
problems: Vec::new(),
23+
24+
first: true,
25+
check_nonempty: CheckState::None,
26+
}
27+
}
28+
29+
fn into_problems(self) -> Vec<String> {
30+
self.problems
31+
}
32+
}
33+
34+
impl ParserVisitor for ProblemVisitor {
35+
fn visit<P>(&mut self, info: &ParserInfo<'_, P>)
36+
where
37+
P: ?Sized + Visitable,
38+
{
39+
if self.first {
40+
self.first = false;
41+
if info.size_hint.lower() == 0 {
42+
self.problems.push(format!(
43+
"The top-level `{}` parser can potentially consume no input, meaning that it cannot fail.\nThis is probably a bug.\nConsider adding `.then_ignore(end())` to the parser.", info.name
44+
))
45+
}
46+
}
47+
48+
let state = core::mem::replace(&mut self.check_nonempty, CheckState::None);
49+
if let CheckState::Check(name) = state {
50+
if info.size_hint.lower() == 0 {
51+
self.problems.push(format!(
52+
"`{}` parser has an inner parser that can potentially consume no input, meaning that it cannot fail.\nThis is probably a bug, because it could lead to an infinite loop.\nConsider using `.at_least(1)` to force the inner parser to consume some input.",
53+
name,
54+
))
55+
}
56+
self.check_nonempty = CheckState::Defer(name);
57+
}
58+
59+
match info.name {
60+
"repeated" | "separated_by" => {
61+
let old = core::mem::replace(
62+
&mut self.check_nonempty,
63+
CheckState::Check(info.name.to_string())
64+
);
65+
info.visit(self);
66+
self.check_nonempty = old;
67+
}
68+
_ => info.visit(self),
69+
}
70+
71+
let state = core::mem::replace(&mut self.check_nonempty, CheckState::None);
72+
if let CheckState::Defer(name) = state {
73+
self.check_nonempty = CheckState::Check(name);
74+
}
75+
}
76+
}
77+
78+
/// TODO
79+
pub trait Problems: Visitable {
80+
/// TODO
81+
fn find_problems(&self) -> Vec<String>;
82+
}
83+
84+
impl<P: Visitable> Problems for P {
85+
fn find_problems(&self) -> Vec<String> {
86+
let mut visitor = ProblemVisitor::new();
87+
self.visit(&mut visitor);
88+
visitor.into_problems()
89+
}
90+
}

0 commit comments

Comments
 (0)