winnow/_tutorial/chapter_5.rs
1//! # Chapter 5: Repetition
2//!
3//! In [`chapter_3`], we covered how to sequence different parsers into a tuple but sometimes you need to run a
4//! single parser multiple times, collecting the result into a container, like `Vec`.
5//!
6//! Let's collect the result of `parse_digits`:
7//! ```rust
8//! # use winnow::prelude::*;
9//! # use winnow::Result;
10//! # use winnow::token::take_while;
11//! # use winnow::combinator::dispatch;
12//! # use winnow::token::take;
13//! # use winnow::combinator::fail;
14//! use winnow::combinator::opt;
15//! use winnow::combinator::repeat;
16//! use winnow::combinator::terminated;
17//!
18//! fn parse_list(input: &mut &str) -> Result<Vec<usize>> {
19//! let mut list = Vec::new();
20//! while let Some(output) = opt(terminated(parse_digits, opt(','))).parse_next(input)? {
21//! list.push(output);
22//! }
23//! Ok(list)
24//! }
25//!
26//! // ...
27//! # fn parse_digits(input: &mut &str) -> Result<usize> {
28//! # dispatch!(take(2usize);
29//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
30//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
31//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
32//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
33//! # _ => fail,
34//! # ).parse_next(input)
35//! # }
36//! #
37//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
38//! # take_while(1.., (
39//! # ('0'..='1'),
40//! # )).parse_next(input)
41//! # }
42//! #
43//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
44//! # take_while(1.., (
45//! # ('0'..='7'),
46//! # )).parse_next(input)
47//! # }
48//! #
49//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
50//! # take_while(1.., (
51//! # ('0'..='9'),
52//! # )).parse_next(input)
53//! # }
54//! #
55//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
56//! # take_while(1.., (
57//! # ('0'..='9'),
58//! # ('A'..='F'),
59//! # ('a'..='f'),
60//! # )).parse_next(input)
61//! # }
62//!
63//! fn main() {
64//! let mut input = "0x1a2b,0x3c4d,0x5e6f Hello";
65//!
66//! let digits = parse_list.parse_next(&mut input).unwrap();
67//!
68//! assert_eq!(input, " Hello");
69//! assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]);
70//!
71//! assert!(parse_digits(&mut "ghiWorld").is_err());
72//! }
73//! ```
74//!
75//! We can implement this declaratively with [`repeat`]:
76//! ```rust
77//! # use winnow::prelude::*;
78//! # use winnow::Result;
79//! # use winnow::token::take_while;
80//! # use winnow::combinator::dispatch;
81//! # use winnow::token::take;
82//! # use winnow::combinator::fail;
83//! use winnow::combinator::opt;
84//! use winnow::combinator::repeat;
85//! use winnow::combinator::terminated;
86//!
87//! fn parse_list(input: &mut &str) -> Result<Vec<usize>> {
88//! repeat(0..,
89//! terminated(parse_digits, opt(','))
90//! ).parse_next(input)
91//! }
92//! #
93//! # fn parse_digits(input: &mut &str) -> Result<usize> {
94//! # dispatch!(take(2usize);
95//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
96//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
97//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
98//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
99//! # _ => fail,
100//! # ).parse_next(input)
101//! # }
102//! #
103//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
104//! # take_while(1.., (
105//! # ('0'..='1'),
106//! # )).parse_next(input)
107//! # }
108//! #
109//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
110//! # take_while(1.., (
111//! # ('0'..='7'),
112//! # )).parse_next(input)
113//! # }
114//! #
115//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
116//! # take_while(1.., (
117//! # ('0'..='9'),
118//! # )).parse_next(input)
119//! # }
120//! #
121//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
122//! # take_while(1.., (
123//! # ('0'..='9'),
124//! # ('A'..='F'),
125//! # ('a'..='f'),
126//! # )).parse_next(input)
127//! # }
128//! #
129//! # fn main() {
130//! # let mut input = "0x1a2b,0x3c4d,0x5e6f Hello";
131//! #
132//! # let digits = parse_list.parse_next(&mut input).unwrap();
133//! #
134//! # assert_eq!(input, " Hello");
135//! # assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]);
136//! #
137//! # assert!(parse_digits(&mut "ghiWorld").is_err());
138//! # }
139//! ```
140//!
141//! You'll notice that the above allows trailing `,`. However, if that's not desired, it
142//! can easily be fixed by using [`separated`] instead of [`repeat`]:
143//! ```rust
144//! # use winnow::prelude::*;
145//! # use winnow::Result;
146//! # use winnow::token::take_while;
147//! # use winnow::combinator::dispatch;
148//! # use winnow::token::take;
149//! # use winnow::combinator::fail;
150//! use winnow::combinator::separated;
151//!
152//! fn parse_list(input: &mut &str) -> Result<Vec<usize>> {
153//! separated(0.., parse_digits, ",").parse_next(input)
154//! }
155//!
156//! // ...
157//! # fn parse_digits(input: &mut &str) -> Result<usize> {
158//! # dispatch!(take(2usize);
159//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
160//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
161//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
162//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
163//! # _ => fail,
164//! # ).parse_next(input)
165//! # }
166//! #
167//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
168//! # take_while(1.., (
169//! # ('0'..='1'),
170//! # )).parse_next(input)
171//! # }
172//! #
173//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
174//! # take_while(1.., (
175//! # ('0'..='7'),
176//! # )).parse_next(input)
177//! # }
178//! #
179//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
180//! # take_while(1.., (
181//! # ('0'..='9'),
182//! # )).parse_next(input)
183//! # }
184//! #
185//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
186//! # take_while(1.., (
187//! # ('0'..='9'),
188//! # ('A'..='F'),
189//! # ('a'..='f'),
190//! # )).parse_next(input)
191//! # }
192//!
193//! fn main() {
194//! let mut input = "0x1a2b,0x3c4d,0x5e6f Hello";
195//!
196//! let digits = parse_list.parse_next(&mut input).unwrap();
197//!
198//! assert_eq!(input, " Hello");
199//! assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]);
200//!
201//! assert!(parse_digits(&mut "ghiWorld").is_err());
202//! }
203//! ```
204//!
205//! If you look closely at [`separated`] and [`repeat`], they aren't limited to collecting
206//! the result into a `Vec`, but rather anything that implements the [`Accumulate`] trait.
207//! [`Accumulate`] is for instance also implemented for [`HashSet`], [`String`] and `()`.
208//!
209//! This lets us build more complex parsers than we did in
210//! [`chapter_2`] by accumulating the results into a `()` and [`take`][Parser::take]-ing
211//! the consumed input.
212//!
213//! `take` works by
214//! 1. Creating a [`checkpoint`][Stream::checkpoint]
215//! 2. Running the inner parser, in our case the `parse_list` parser, which will advance the input
216//! 3. Returning the slice from the first checkpoint to the current position.
217//!
218//! Since the result of `parse_list` gets thrown away, we
219//! accumulates into a `()` to not waste work creating an unused `Vec`.
220//!
221//! ```rust
222//! # use winnow::prelude::*;
223//! # use winnow::Result;
224//! # use winnow::token::take_while;
225//! # use winnow::combinator::dispatch;
226//! # use winnow::token::take;
227//! # use winnow::combinator::fail;
228//! # use winnow::combinator::separated;
229//! #
230//! fn take_list<'s>(input: &mut &'s str) -> Result<&'s str> {
231//! parse_list.take().parse_next(input)
232//! }
233//!
234//! fn parse_list(input: &mut &str) -> Result<()> {
235//! separated(0.., parse_digits, ",").parse_next(input)
236//! }
237//!
238//! # fn parse_digits(input: &mut &str) -> Result<usize> {
239//! # dispatch!(take(2usize);
240//! # "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
241//! # "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
242//! # "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
243//! # "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
244//! # _ => fail,
245//! # ).parse_next(input)
246//! # }
247//! #
248//! # fn parse_bin_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
249//! # take_while(1.., (
250//! # ('0'..='1'),
251//! # )).parse_next(input)
252//! # }
253//! #
254//! # fn parse_oct_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
255//! # take_while(1.., (
256//! # ('0'..='7'),
257//! # )).parse_next(input)
258//! # }
259//! #
260//! # fn parse_dec_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
261//! # take_while(1.., (
262//! # ('0'..='9'),
263//! # )).parse_next(input)
264//! # }
265//! #
266//! # fn parse_hex_digits<'s>(input: &mut &'s str) -> Result<&'s str> {
267//! # take_while(1.., (
268//! # ('0'..='9'),
269//! # ('A'..='F'),
270//! # ('a'..='f'),
271//! # )).parse_next(input)
272//! # }
273//!
274//! fn main() {
275//! let mut input = "0x1a2b,0x3c4d,0x5e6f Hello";
276//!
277//! let digits = take_list.parse_next(&mut input).unwrap();
278//!
279//! assert_eq!(input, " Hello");
280//! assert_eq!(digits, "0x1a2b,0x3c4d,0x5e6f");
281//!
282//! assert!(parse_digits(&mut "ghiWorld").is_err());
283//! }
284//! ```
285//! See [`combinator`] for more repetition parsers.
286
287#![allow(unused_imports)]
288use super::chapter_2;
289use super::chapter_3;
290use crate::combinator;
291use crate::combinator::repeat;
292use crate::combinator::separated;
293use crate::stream::Accumulate;
294use crate::stream::Stream;
295use crate::Parser;
296use std::collections::HashSet;
297
298pub use super::chapter_4 as previous;
299pub use super::chapter_6 as next;
300pub use crate::_tutorial as table_of_contents;