blob: b703bcf5c1e51996859aa796b778f33add4a5e76 [file] [log] [blame]
//! # Chapter 5: Repetition
//!
//! In [`chapter_3`], we covered how to sequence different parsers into a tuple but sometimes you need to run a
//! single parser many times into a [`Vec`].
//!
//! Let's take our `parse_digits` and collect a list of them with [`many0`]:
//! ```rust
//! # use winnow::IResult;
//! # use winnow::Parser;
//! # use winnow::bytes::take_while1;
//! # use winnow::branch::dispatch;
//! # use winnow::bytes::take;
//! # use winnow::combinator::fail;
//! use winnow::combinator::opt;
//! use winnow::multi::many0;
//! use winnow::sequence::terminated;
//!
//! fn parse_list(input: &str) -> IResult<&str, Vec<usize>> {
//! many0(terminated(parse_digits, opt(','))).parse_next(input)
//! }
//!
//! // ...
//! # fn parse_digits(input: &str) -> IResult<&str, usize> {
//! # dispatch!(take(2usize);
//! # "0b" => parse_bin_digits.map_res(|s| usize::from_str_radix(s, 2)),
//! # "0o" => parse_oct_digits.map_res(|s| usize::from_str_radix(s, 8)),
//! # "0d" => parse_dec_digits.map_res(|s| usize::from_str_radix(s, 10)),
//! # "0x" => parse_hex_digits.map_res(|s| usize::from_str_radix(s, 16)),
//! # _ => fail,
//! # ).parse_next(input)
//! # }
//! #
//! # fn parse_bin_digits(input: &str) -> IResult<&str, &str> {
//! # take_while1((
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits(input: &str) -> IResult<&str, &str> {
//! # take_while1((
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits(input: &str) -> IResult<&str, &str> {
//! # take_while1((
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits(input: &str) -> IResult<&str, &str> {
//! # take_while1((
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//!
//! fn main() {
//! let input = "0x1a2b,0x3c4d,0x5e6f Hello";
//!
//! let (remainder, digits) = parse_list.parse_next(input).unwrap();
//!
//! assert_eq!(remainder, " Hello");
//! assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]);
//!
//! assert!(parse_digits("ghiWorld").is_err());
//! }
//! ```
//!
//! You'll notice that the above allows trailing `,` when we intended to not support that. We can
//! easily fix this by using [`separated0`]:
//! ```rust
//! # use winnow::IResult;
//! # use winnow::Parser;
//! # use winnow::bytes::take_while1;
//! # use winnow::branch::dispatch;
//! # use winnow::bytes::take;
//! # use winnow::combinator::fail;
//! use winnow::multi::separated0;
//!
//! fn parse_list(input: &str) -> IResult<&str, Vec<usize>> {
//! separated0(parse_digits, ",").parse_next(input)
//! }
//!
//! // ...
//! # fn parse_digits(input: &str) -> IResult<&str, usize> {
//! # dispatch!(take(2usize);
//! # "0b" => parse_bin_digits.map_res(|s| usize::from_str_radix(s, 2)),
//! # "0o" => parse_oct_digits.map_res(|s| usize::from_str_radix(s, 8)),
//! # "0d" => parse_dec_digits.map_res(|s| usize::from_str_radix(s, 10)),
//! # "0x" => parse_hex_digits.map_res(|s| usize::from_str_radix(s, 16)),
//! # _ => fail,
//! # ).parse_next(input)
//! # }
//! #
//! # fn parse_bin_digits(input: &str) -> IResult<&str, &str> {
//! # take_while1((
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits(input: &str) -> IResult<&str, &str> {
//! # take_while1((
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits(input: &str) -> IResult<&str, &str> {
//! # take_while1((
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits(input: &str) -> IResult<&str, &str> {
//! # take_while1((
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//!
//! fn main() {
//! let input = "0x1a2b,0x3c4d,0x5e6f Hello";
//!
//! let (remainder, digits) = parse_list.parse_next(input).unwrap();
//!
//! assert_eq!(remainder, " Hello");
//! assert_eq!(digits, vec![0x1a2b, 0x3c4d, 0x5e6f]);
//!
//! assert!(parse_digits("ghiWorld").is_err());
//! }
//! ```
//!
//! If you look closely at [`many0`], it isn't collecting directly into a [`Vec`] but
//! [`Accumulate`] to gather the results. This let's us make more complex parsers than we did in
//! [`chapter_2`] by accumulating the results into a `()` and [`recognize`][Parser::recognize]-ing the captured input:
//! ```rust
//! # use winnow::IResult;
//! # use winnow::Parser;
//! # use winnow::bytes::take_while1;
//! # use winnow::branch::dispatch;
//! # use winnow::bytes::take;
//! # use winnow::combinator::fail;
//! # use winnow::multi::separated0;
//! #
//! fn recognize_list(input: &str) -> IResult<&str, &str> {
//! parse_list.recognize().parse_next(input)
//! }
//!
//! fn parse_list(input: &str) -> IResult<&str, ()> {
//! separated0(parse_digits, ",").parse_next(input)
//! }
//!
//! // ...
//! # fn parse_digits(input: &str) -> IResult<&str, usize> {
//! # dispatch!(take(2usize);
//! # "0b" => parse_bin_digits.map_res(|s| usize::from_str_radix(s, 2)),
//! # "0o" => parse_oct_digits.map_res(|s| usize::from_str_radix(s, 8)),
//! # "0d" => parse_dec_digits.map_res(|s| usize::from_str_radix(s, 10)),
//! # "0x" => parse_hex_digits.map_res(|s| usize::from_str_radix(s, 16)),
//! # _ => fail,
//! # ).parse_next(input)
//! # }
//! #
//! # fn parse_bin_digits(input: &str) -> IResult<&str, &str> {
//! # take_while1((
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_oct_digits(input: &str) -> IResult<&str, &str> {
//! # take_while1((
//! # ('0'..='7'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_dec_digits(input: &str) -> IResult<&str, &str> {
//! # take_while1((
//! # ('0'..='9'),
//! # )).parse_next(input)
//! # }
//! #
//! # fn parse_hex_digits(input: &str) -> IResult<&str, &str> {
//! # take_while1((
//! # ('0'..='9'),
//! # ('A'..='F'),
//! # ('a'..='f'),
//! # )).parse_next(input)
//! # }
//!
//! fn main() {
//! let input = "0x1a2b,0x3c4d,0x5e6f Hello";
//!
//! let (remainder, digits) = recognize_list.parse_next(input).unwrap();
//!
//! assert_eq!(remainder, " Hello");
//! assert_eq!(digits, "0x1a2b,0x3c4d,0x5e6f");
//!
//! assert!(parse_digits("ghiWorld").is_err());
//! }
//! ```
#![allow(unused_imports)]
use super::chapter_2;
use super::chapter_3;
use crate::multi::many0;
use crate::multi::separated0;
use crate::stream::Accumulate;
use crate::Parser;
use std::vec::Vec;
pub use super::chapter_4 as previous;
pub use super::chapter_6 as next;