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

Commit f1a8304

Browse files
committed
Simplier recursive impl
1 parent 3ce847f commit f1a8304

5 files changed

Lines changed: 11 additions & 48 deletions

File tree

Cargo.toml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ memoization = []
3535
# Allows extending chumsky by writing your own parser implementations.
3636
extension = []
3737

38-
# Make builtin parsers such as `Boxed` use atomic instead of non-atomic internals.
39-
# TODO: Remove or rework this
40-
sync = ["spin"]
41-
4238
# Enable Pratt parsing combinator
4339
pratt = []
4440

@@ -66,7 +62,7 @@ docsrs = []
6662
# An alias of all features that work with the stable compiler.
6763
# Do not use this feature, its removal is not considered a breaking change and its behaviour may change.
6864
# If you're working on chumsky and you're adding a feature that does not require nightly support, please add it to this list.
69-
_test_stable = ["std", "stacker", "memoization", "extension", "sync", "pratt"]
65+
_test_stable = ["std", "stacker", "memoization", "extension", "pratt"]
7066

7167
[package.metadata.docs.rs]
7268
all-features = true
@@ -76,7 +72,7 @@ rustdoc-args = ["--cfg", "docsrs"]
7672
hashbrown = "0.15"
7773
stacker = { version = "0.1", optional = true }
7874
regex-automata = { version = "0.3", default-features = false, optional = true, features = ["alloc", "meta", "perf", "unicode", "nfa", "dfa", "hybrid"] }
79-
spin = { version = "0.9", features = ["once"], default-features = false, optional = true }
75+
spin = { version = "0.9", features = ["once"], default-features = false }
8076
lexical = { version = "6.1.1", default-features = false, features = ["parse-integers", "parse-floats", "format"], optional = true }
8177
either = { version = "1.8.1", optional = true }
8278
serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] }

examples/mini_ml.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ where
116116
I: BorrowInput<'tokens, Token = Token<'src>, Span = SimpleSpan>,
117117
// Because this function is generic over the input type, we need the caller to tell us how to create a new input,
118118
// `I`, from a nested token tree. This function serves that purpose.
119-
M: Fn(SimpleSpan, &'tokens [Spanned<Token<'src>>]) -> I + Clone + 'src,
119+
M: Fn(SimpleSpan, &'tokens [Spanned<Token<'src>>]) -> I + Clone + Send + Sync + 'src,
120120
{
121121
recursive(|expr| {
122122
let ident = select_ref! { Token::Ident(x) => *x };

examples/nested_spans.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ enum Token {
1313
fn parser<'src, I, M>(make_input: M) -> impl Parser<'src, I, i64>
1414
where
1515
I: BorrowInput<'src, Token = Token, Span = SimpleSpan>,
16-
M: Fn(SimpleSpan, &'src [(Token, SimpleSpan)]) -> I + Clone + 'src,
16+
M: Fn(SimpleSpan, &'src [(Token, SimpleSpan)]) -> I + Send + Sync + Clone + 'src,
1717
{
1818
recursive(|expr| {
1919
let num = select_ref! { Token::Num(x) => *x };

src/recursive.rs

Lines changed: 5 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,6 @@
1010
1111
use super::*;
1212

13-
struct OnceCell<T>(core::cell::Cell<Option<T>>);
14-
impl<T> OnceCell<T> {
15-
pub fn new() -> Self {
16-
Self(core::cell::Cell::new(None))
17-
}
18-
pub fn set(&self, x: T) -> Result<(), ()> {
19-
// SAFETY: Function is not reentrant so we have exclusive access to the inner data
20-
unsafe {
21-
let vacant = (*self.0.as_ptr()).is_none();
22-
if vacant {
23-
self.0.as_ptr().write(Some(x));
24-
Ok(())
25-
} else {
26-
Err(())
27-
}
28-
}
29-
}
30-
#[inline]
31-
pub fn get(&self) -> Option<&T> {
32-
// SAFETY: We ensure that we never insert twice (so the inner `T` always lives as long as us, if it exists) and
33-
// neither function is possibly reentrant so there's no way we can invalidate mut xor shared aliasing
34-
unsafe { (*self.0.as_ptr()).as_ref() }
35-
}
36-
}
37-
3813
// TODO: Ensure that this doesn't produce leaks
3914
enum RecursiveInner<T: ?Sized> {
4015
Owned(Arc<T>),
@@ -48,7 +23,7 @@ pub type Direct<'src, 'b, I, O, Extra> = DynParser<'src, 'b, I, O, Extra>;
4823
/// Type for recursive parsers that are defined through a call to [`Recursive::declare`], and as
4924
/// such require an additional layer of allocation.
5025
pub struct Indirect<'src, 'b, I: Input<'src>, O, Extra: ParserExtra<'src, I>> {
51-
inner: OnceCell<Box<DynParser<'src, 'b, I, O, Extra>>>,
26+
inner: spin::Once<Box<DynParser<'src, 'b, I, O, Extra>>>,
5227
}
5328

5429
/// A parser that can be defined in terms of itself by separating its [declaration](Recursive::declare) from its
@@ -101,22 +76,14 @@ impl<'src, 'b, I: Input<'src>, O, E: ParserExtra<'src, I>> Recursive<Indirect<'s
10176
pub fn declare() -> Self {
10277
Recursive {
10378
inner: RecursiveInner::Owned(Arc::new(Indirect {
104-
inner: OnceCell::new(),
79+
inner: spin::Once::new(),
10580
})),
10681
}
10782
}
10883

10984
/// Defines the parser after declaring it, allowing it to be used for parsing.
110-
// INFO: Clone bound not actually needed, but good to be safe for future compat
111-
#[track_caller]
112-
pub fn define<P: Parser<'src, I, O, E> + Clone + Send + Sync + 'b>(&mut self, parser: P) {
113-
let location = *Location::caller();
114-
self.parser()
115-
.inner
116-
.set(Box::new(parser))
117-
.unwrap_or_else(|_| {
118-
panic!("recursive parsers can only be defined once, trying to redefine it at {location}")
119-
});
85+
pub fn define<P: Parser<'src, I, O, E> + Send + Sync + 'b>(&mut self, parser: P) {
86+
self.parser().inner.call_once(|| Box::new(parser));
12087
}
12188
}
12289

@@ -243,7 +210,7 @@ pub fn recursive<'src, 'b, I, O, E, A, F>(f: F) -> Recursive<Direct<'src, 'b, I,
243210
where
244211
I: Input<'src>,
245212
E: ParserExtra<'src, I>,
246-
A: Parser<'src, I, O, E> + Clone + Send + Sync + 'b,
213+
A: Parser<'src, I, O, E> + Send + Sync + 'b,
247214
F: FnOnce(Recursive<Direct<'src, 'b, I, O, E>>) -> A,
248215
{
249216
let rc = Arc::new_cyclic(|rc| {

src/text.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,7 +1101,7 @@ mod tests {
11011101
fn make_ascii_kw_parser<'src, I>(s: I::Slice) -> impl Parser<'src, I, ()>
11021102
where
11031103
I: crate::StrInput<'src>,
1104-
I::Slice: PartialEq + Clone,
1104+
I::Slice: PartialEq + Send + Sync + Clone,
11051105
I::Token: crate::Char + fmt::Debug + 'src,
11061106
{
11071107
text::ascii::keyword(s).ignored()
@@ -1110,7 +1110,7 @@ mod tests {
11101110
fn make_unicode_kw_parser<'src, I>(s: I::Slice) -> impl Parser<'src, I, ()>
11111111
where
11121112
I: crate::StrInput<'src>,
1113-
I::Slice: PartialEq + Clone,
1113+
I::Slice: PartialEq + Clone + Send + Sync,
11141114
I::Token: crate::Char + fmt::Debug + 'src,
11151115
{
11161116
text::unicode::keyword(s).ignored()

0 commit comments

Comments
 (0)