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