• 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'..='7'),
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'..='7'),
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 `,` when we intended to not support that. We can
140 //! easily fix this by using [`separated`]:
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'..='7'),
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 [`repeat`], it isn't collecting directly into a [`Vec`] but
203 //! [`Accumulate`] to gather the results. This lets us make more complex parsers than we did in
204 //! [`chapter_2`] by accumulating the results into a `()` and [`recognize`][Parser::recognize]-ing the captured input:
205 //! ```rust
206 //! # use winnow::prelude::*;
207 //! # use winnow::token::take_while;
208 //! # use winnow::combinator::dispatch;
209 //! # use winnow::token::take;
210 //! # use winnow::combinator::fail;
211 //! # use winnow::combinator::separated;
212 //! #
213 //! fn recognize_list<'s>(input: &mut &'s str) -> PResult<&'s str> {
214 //!     parse_list.recognize().parse_next(input)
215 //! }
216 //!
217 //! fn parse_list(input: &mut &str) -> PResult<()> {
218 //!     separated(0.., parse_digits, ",").parse_next(input)
219 //! }
220 //!
221 //! # fn parse_digits(input: &mut &str) -> PResult<usize> {
222 //! #     dispatch!(take(2usize);
223 //! #         "0b" => parse_bin_digits.try_map(|s| usize::from_str_radix(s, 2)),
224 //! #         "0o" => parse_oct_digits.try_map(|s| usize::from_str_radix(s, 8)),
225 //! #         "0d" => parse_dec_digits.try_map(|s| usize::from_str_radix(s, 10)),
226 //! #         "0x" => parse_hex_digits.try_map(|s| usize::from_str_radix(s, 16)),
227 //! #         _ => fail,
228 //! #     ).parse_next(input)
229 //! # }
230 //! #
231 //! # fn parse_bin_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
232 //! #     take_while(1.., (
233 //! #         ('0'..='7'),
234 //! #     )).parse_next(input)
235 //! # }
236 //! #
237 //! # fn parse_oct_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
238 //! #     take_while(1.., (
239 //! #         ('0'..='7'),
240 //! #     )).parse_next(input)
241 //! # }
242 //! #
243 //! # fn parse_dec_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
244 //! #     take_while(1.., (
245 //! #         ('0'..='9'),
246 //! #     )).parse_next(input)
247 //! # }
248 //! #
249 //! # fn parse_hex_digits<'s>(input: &mut &'s str) -> PResult<&'s str> {
250 //! #     take_while(1.., (
251 //! #         ('0'..='9'),
252 //! #         ('A'..='F'),
253 //! #         ('a'..='f'),
254 //! #     )).parse_next(input)
255 //! # }
256 //!
257 //! fn main() {
258 //!     let mut input = "0x1a2b,0x3c4d,0x5e6f Hello";
259 //!
260 //!     let digits = recognize_list.parse_next(&mut input).unwrap();
261 //!
262 //!     assert_eq!(input, " Hello");
263 //!     assert_eq!(digits, "0x1a2b,0x3c4d,0x5e6f");
264 //!
265 //!     assert!(parse_digits(&mut "ghiWorld").is_err());
266 //! }
267 //! ```
268 //! See [`combinator`] for more repetition parsers.
269 
270 #![allow(unused_imports)]
271 use super::chapter_2;
272 use super::chapter_3;
273 use crate::combinator;
274 use crate::combinator::repeat;
275 use crate::combinator::separated;
276 use crate::stream::Accumulate;
277 use crate::Parser;
278 use std::vec::Vec;
279 
280 pub use super::chapter_4 as previous;
281 pub use super::chapter_6 as next;
282 pub use crate::_tutorial as table_of_contents;
283