diff --git a/Cargo.lock b/Cargo.lock index 36223d73..d10c7e22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,7 +178,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chumsky" -version = "0.11.1" +version = "0.12.0" dependencies = [ "ariadne", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 8b80d249..fe79b453 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chumsky" -version = "0.11.1" +version = "0.12.0" description = "A parser library for humans with powerful error recovery" authors = ["Joshua Barretto ", "Elijah Hartvigsen "] repository = "https://github.com/zesterer/chumsky" diff --git a/src/combinator.rs b/src/combinator.rs index e895a6f9..7018d205 100644 --- a/src/combinator.rs +++ b/src/combinator.rs @@ -1157,15 +1157,15 @@ where } /// See [`Parser::nested_in`]. -pub struct NestedIn { +pub struct NestedIn { pub(crate) parser_a: A, pub(crate) parser_b: B, #[allow(dead_code)] - pub(crate) phantom: EmptyPhantom<(J, F, O, E)>, + pub(crate) phantom: EmptyPhantom<(J, O, E)>, } -impl Copy for NestedIn {} -impl Clone for NestedIn { +impl Copy for NestedIn {} +impl Clone for NestedIn { fn clone(&self) -> Self { Self { parser_a: self.parser_a.clone(), @@ -1175,14 +1175,32 @@ impl Clone for NestedIn { } } -impl<'src, I, J, E, F, A, B, O> Parser<'src, I, O, E> for NestedIn +impl<'src, I, J, E, A, B, O> Parser<'src, I, O, E> for NestedIn where I: Input<'src>, E: ParserExtra<'src, I>, + // These bounds looks silly, but they basically just ensure that the extra type of the inner parser is compatible with the extra of the outer parser + E: ParserExtra< + 'src, + J, + Error = >::Error, + State = >::State, + Context = >::Context, + >, + >::Error: Error<'src, J>, + >::State: Inspector<'src, J>, B: Parser<'src, I, J, E>, J: Input<'src>, - F: ParserExtra<'src, J, State = E::State, Context = E::Context, Error = E::Error>, - A: Parser<'src, J, O, F>, + A: Parser< + 'src, + J, + O, + extra::Full< + >::Error, + >::State, + >::Context, + >, + >, { #[inline(always)] fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { diff --git a/src/lib.rs b/src/lib.rs index 3751dc27..2eb6f77d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1087,7 +1087,7 @@ pub trait Parser<'src, I: Input<'src>, O, E: ParserExtra<'src, I> = extra::Defau /// /// assert_eq!(tl.parse(&tokens).into_result(), Ok(vec![("foo", vec!["a", "b"])])); /// ``` - fn nested_in, J, F>(self, other: B) -> NestedIn + fn nested_in, J, F>(self, other: B) -> NestedIn where Self: Sized, I: 'src, diff --git a/src/recursive.rs b/src/recursive.rs index b1969ebc..2f46e3a3 100644 --- a/src/recursive.rs +++ b/src/recursive.rs @@ -130,6 +130,15 @@ impl Recursive

{ .expect("Recursive parser used before being defined"), } } + + fn weak(&self) -> Self { + Self { + inner: match &self.inner { + RecursiveInner::Owned(x) => RecursiveInner::Unowned(Rc::downgrade(x)), + RecursiveInner::Unowned(x) => RecursiveInner::Unowned(x.clone()), + }, + } + } } impl Clone for Recursive

{ @@ -239,23 +248,195 @@ where /// ]))); /// ``` // INFO: Clone bound not actually needed, but good to be safe for future compat -pub fn recursive<'src, 'b, I, O, E, A, F>(f: F) -> Recursive> +pub fn recursive<'src, 'b, I, O, E, A, F>(f: F) -> Recursive> where I: Input<'src>, E: ParserExtra<'src, I>, A: Parser<'src, I, O, E> + Clone + 'b, - F: FnOnce(Recursive>) -> A, + F: FnOnce(Recursive>) -> A, +{ + recursive_fold(|recursive| (f(recursive), ())).0 +} +/// Construct two mutually recursive parsers (i.e: two parser that may reference each other). +/// +/// The given function must create the parser. The parser must not be used to parse input before this function returns. +/// +/// The output type of this parser is `O1`, the same as the inner parser. +/// +/// # Examples +/// +/// ``` +/// # use chumsky::prelude::*; +/// use chumsky::recursive::recursive_2; +/// #[derive(Debug, PartialEq)] +/// enum Tree<'a> { +/// Leaf(&'a str), +/// Branch(Vec>), +/// } +/// +/// // Parser that recursively parses nested lists with ordinary or square brackets +/// let tree = recursive_2::<_, _, _, extra::Err>, _, _, _, _>(|bracket, square| { +/// (choice((bracket.clone() +/// .separated_by(just(',')) +/// .collect::>() +/// .delimited_by(just('('), just(')')) +/// .map(Tree::Branch) +/// .or(text::ascii::ident().map(Tree::Leaf)) +/// .padded(), square.clone())), +/// choice((square +/// .separated_by(just(',')) +/// .collect::>() +/// .delimited_by(just('['), just(']')) +/// .map(Tree::Branch) +/// .or(text::ascii::ident().map(Tree::Leaf)) +/// .padded(), bracket.clone()))) +/// }); +/// +/// assert_eq!(tree.parse("hello").into_result(), Ok(Tree::Leaf("hello"))); +/// assert_eq!(tree.parse("(a, b, c)").into_result(), Ok(Tree::Branch(vec![ +/// Tree::Leaf("a"), +/// Tree::Leaf("b"), +/// Tree::Leaf("c"), +/// ]))); +/// // The parser can deal with arbitrarily complex nested lists +/// assert_eq!(tree.parse("[(a, b), c, [d, (e, f)]]").into_result(), Ok(Tree::Branch(vec![ +/// Tree::Branch(vec![ +/// Tree::Leaf("a"), +/// Tree::Leaf("b"), +/// ]), +/// Tree::Leaf("c"), +/// Tree::Branch(vec![ +/// Tree::Leaf("d"), +/// Tree::Branch(vec![ +/// Tree::Leaf("e"), +/// Tree::Leaf("f"), +/// ]), +/// ]), +/// ]))); +/// // Both sides of the parser can be used +/// assert_eq!(tree.flip().parse("(a, b, c)").into_result(), Ok(Tree::Branch(vec![ +/// Tree::Leaf("a"), +/// Tree::Leaf("b"), +/// Tree::Leaf("c"), +/// ]))); +/// ``` +/// +/// +pub fn recursive_2<'src, 'b, I, O1, O2, E, A1, A2, R, F>( + f: F, +) -> EitherParser>, Recursive>> +where + I: Input<'src>, + E: ParserExtra<'src, I>, + A1: Parser<'src, I, O1, E> + Clone + 'b, + A2: Parser<'src, I, O2, E> + Clone + 'b, + R: Into>, + F: FnOnce( + Recursive>, + Recursive>, + ) -> R, +{ + recursive_fold(|left| recursive_fold(|right| f(left, right).into().flip()).flip()) +} + +/// Construct an EitherParser with A1 recursive and a guarantee that A1 and A2 are dropped together. +/// This function is useful for constructing recursive parser combinators. +/// +/// The given function must create the parser. The parser must not be used to parse input before this function returns. +/// +/// The output type of this parser is `O1`, the same as the left parser. +/// +/// # Examples +/// +/// Please see [recursive] and [recursive_2] for how recursive_fold can be used to define new recursive combinators. +pub fn recursive_fold<'a, 'b, I, O1, A1, A2, R, F, E>( + f: F, +) -> EitherParser>, A2> +where + I: Input<'a>, + E: ParserExtra<'a, I>, + A1: Parser<'a, I, O1, E> + Clone + 'b, + R: Into>, + F: FnOnce(Recursive>) -> R, +{ + let mut left_parser = Recursive::declare(); + let either = f(left_parser.weak()).into(); + left_parser.define(either.0); + EitherParser(left_parser, either.1) +} + +/// A parser that parses using A1 while guaranteeing that A1 and A2 are dropped together. +#[derive(Clone)] +pub struct EitherParser(A1, A2); + +impl From<(A1, A2)> for EitherParser { + fn from(value: (A1, A2)) -> Self { + EitherParser(value.0, value.1) + } +} + +impl<'src, I, O, E, A1, A2> Parser<'src, I, O, E> for EitherParser +where + I: Input<'src>, + E: ParserExtra<'src, I>, + A1: Parser<'src, I, O, E>, { - let rc = Rc::new_cyclic(|rc| { - let rc: rc::Weak> = rc.clone() as _; - let parser = Recursive { - inner: RecursiveInner::Unowned(rc.clone()), - }; + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { + self.0.go::(inp) + } + + go_extra!(O); +} + +impl EitherParser { + /// Flip the position of A1 and A2 + pub fn flip(self) -> EitherParser { + EitherParser(self.1, self.0) + } + + #[allow(missing_docs)] + pub fn left(&self) -> &A1 { + &self.0 + } + + #[allow(missing_docs)] + pub fn right(&self) -> &A2 { + &self.1 + } +} - f(parser) - }); +#[cfg(test)] +mod tests { + use crate::prelude::*; + use crate::recursive::{recursive_2, RecursiveInner}; + + #[test] + fn no_strong_references_after_drop() { + fn check_ref_count(recursive_inner: &RecursiveInner, expected: usize) { + match recursive_inner { + RecursiveInner::Owned(_) => { + panic!("a user must not have access to strong references") + } + RecursiveInner::Unowned(weak) => { + assert_eq!(weak.strong_count(), expected); + } + }; + } + + let mut smuggle_r1 = None; + let mut smuggle_r2 = None; + + { + let _ = recursive_2::<&str, (), (), extra::Err>, _, _, _, _>(|r1, r2| { + check_ref_count(&r1.inner, 1); + check_ref_count(&r2.inner, 1); + smuggle_r1.replace(r1.clone()); + smuggle_r2.replace(r2.clone()); + (r1, r2) + }); + } - Recursive { - inner: RecursiveInner::Owned(rc), + check_ref_count(&smuggle_r1.unwrap().inner, 0); + check_ref_count(&smuggle_r2.unwrap().inner, 0); } }