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