diff --git a/src/input.rs b/src/input.rs index a41eb31c..896a3146 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1033,7 +1033,6 @@ where } } - #[cfg(test)] pub(crate) fn as_ref_at<'parse>( &'parse mut self, offset: I::Offset, diff --git a/src/lib.rs b/src/lib.rs index 666ee1b0..d2abe0b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2306,7 +2306,6 @@ where } /// An iterator that wraps an iterable parser. See [`IterParser::parse_iter`]. -#[cfg(test)] pub struct ParserIter<'a, 'iter, P: IterParser<'a, I, O, E>, I: Input<'a>, O, E: ParserExtra<'a, I>> { parser: P, @@ -2317,7 +2316,6 @@ pub struct ParserIter<'a, 'iter, P: IterParser<'a, I, O, E>, I: Input<'a>, O, E: phantom: EmptyPhantom<(&'a (), O)>, } -#[cfg(test)] impl<'a, 'iter, P, I: Input<'a>, O, E: ParserExtra<'a, I>> Iterator for ParserIter<'a, 'iter, P, I, O, E> where @@ -2409,6 +2407,72 @@ where } } + /// Convert the output of this iterable parser into a stream which can be used by another parser. + /// + /// # Examples + /// + /// ``` + /// use chumsky::{prelude::*, text::ident, input::{Stream, ValueInput}}; + /// + /// #[derive(Copy, Clone, Eq, PartialEq)] + /// enum Token<'a> { + /// Comma, + /// Symbol(&'a str), + /// } + /// + /// fn tokenize<'a>() -> impl Parser<'a, &'a str, Token<'a>> { + /// choice(( + /// just(',').to(Token::Comma), + /// ident().map(Token::Symbol), + /// )) + /// } + /// + /// #[derive(PartialEq, Eq, Debug)] + /// struct SymbolList<'a> { + /// pub symbols: Vec<&'a str>, + /// } + /// + /// fn list<'a, I>() -> impl Parser<'a, I, SymbolList<'a>> + /// where I: ValueInput<'a, Token=Token<'a>, Span=SimpleSpan> { + /// fn symbol<'a, I>() -> impl Parser<'a, I, &'a str> + /// where I: ValueInput<'a, Token=Token<'a>, Span=SimpleSpan> { + /// select! { + /// Token::Symbol(name) => name, + /// } + /// } + /// + /// let comma = select! { Token::Comma => () }; + /// + /// symbol() + /// .then(comma.ignore_then(symbol()).repeated().collect::>()) + /// .map(|(head, tail)| SymbolList { + /// symbols: vec![head].into_iter().chain(tail.into_iter()).collect(), + /// }) + /// } + /// + /// let input = "a,bb,asdfe"; + /// let token_stream = tokenize().repeated().stream(input).into_result().unwrap(); + /// let result = list().parse(token_stream).into_result().unwrap(); + /// assert_eq!(result, SymbolList { symbols: vec!["a", "bb", "asdfe"] }); + /// ``` + fn stream( + self, + input: I, + ) -> ParseResult>, E::Error> + where + Self: IterParser<'a, I, O, E> + Sized, + E::State: Default, + E::Context: Default, + { + let (iter, errs) = self.parse_iter(input).into_output_errors(); + if iter.is_none() { + return ParseResult::new(None, errs); + } + + let stream = input::Stream::from_iter(iter.unwrap()); + ParseResult::new(Some(stream), errs) + } + /// Collect this iterable parser into a [`usize`], outputting the number of elements that were parsed. /// /// This is sugar for [`.collect::()`](Self::collect). @@ -2553,7 +2617,6 @@ where /// /// Warning: Trailing errors will be ignored // TODO: Stabilize once error handling is properly decided on - #[cfg(test)] fn parse_iter(self, input: I) -> ParseResult, E::Error> where Self: IterParser<'a, I, O, E> + Sized, diff --git a/src/stream.rs b/src/stream.rs index 6cf9510d..9d2ac1e7 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -105,6 +105,7 @@ where } } +// TODO: impl BorrowInput! impl<'a, I: Iterator + 'a> ValueInput<'a> for Stream where I::Item: Clone,