From 2b6402ad76b0091d436a27523f3470b791469d75 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 31 Jan 2024 12:41:48 -0600 Subject: [PATCH] fix(comb): Deprecate fold_repeat in favor of repeat().fold() --- assets/trace.svg | 4 +- examples/arithmetic/parser.rs | 52 +++++++------- examples/arithmetic/parser_ast.rs | 52 +++++++------- examples/arithmetic/parser_lexer.rs | 9 ++- examples/json/parser.rs | 4 +- examples/json/parser_dispatch.rs | 4 +- examples/json/parser_partial.rs | 4 +- examples/ndjson/parser.rs | 4 +- examples/string/parser.rs | 10 +-- src/combinator/multi.rs | 103 ++-------------------------- src/combinator/tests.rs | 14 ++-- tests/testsuite/reborrow_fold.rs | 15 ++-- 12 files changed, 93 insertions(+), 182 deletions(-) diff --git a/assets/trace.svg b/assets/trace.svg index 395cbc58..b9c6d477 100644 --- a/assets/trace.svg +++ b/assets/trace.svg @@ -75,7 +75,7 @@ < any | +1 | verify | < one_of | +1 - > fold_repeat | "abc\"" + > repeat_fold | "abc\"" > alt | "abc\"" > take_till | "abc\"" < take_till | +3 @@ -99,7 +99,7 @@ < one_of | backtrack < preceded | backtrack < alt | backtrack - < fold_repeat | +3 + < repeat_fold | +3 > one_of | "\"" > any | "\"" < any | +1 diff --git a/examples/arithmetic/parser.rs b/examples/arithmetic/parser.rs index 16572751..d51007aa 100644 --- a/examples/arithmetic/parser.rs +++ b/examples/arithmetic/parser.rs @@ -5,7 +5,7 @@ use winnow::{ ascii::{digit1 as digits, multispace0 as multispaces}, combinator::alt, combinator::delimited, - combinator::fold_repeat, + combinator::repeat, token::one_of, }; @@ -14,19 +14,18 @@ use winnow::{ pub fn expr(i: &mut &str) -> PResult { let init = term.parse_next(i)?; - fold_repeat( - 0.., - (one_of(['+', '-']), term), - move || init, - |acc, (op, val): (char, i64)| { - if op == '+' { - acc + val - } else { - acc - val - } - }, - ) - .parse_next(i) + repeat(0.., (one_of(['+', '-']), term)) + .fold( + move || init, + |acc, (op, val): (char, i64)| { + if op == '+' { + acc + val + } else { + acc - val + } + }, + ) + .parse_next(i) } // We read an initial factor and for each time we find @@ -35,19 +34,18 @@ pub fn expr(i: &mut &str) -> PResult { fn term(i: &mut &str) -> PResult { let init = factor.parse_next(i)?; - fold_repeat( - 0.., - (one_of(['*', '/']), factor), - move || init, - |acc, (op, val): (char, i64)| { - if op == '*' { - acc * val - } else { - acc / val - } - }, - ) - .parse_next(i) + repeat(0.., (one_of(['*', '/']), factor)) + .fold( + move || init, + |acc, (op, val): (char, i64)| { + if op == '*' { + acc * val + } else { + acc / val + } + }, + ) + .parse_next(i) } // We transform an integer string into a i64, ignoring surrounding whitespace diff --git a/examples/arithmetic/parser_ast.rs b/examples/arithmetic/parser_ast.rs index 0ca15348..20feb267 100644 --- a/examples/arithmetic/parser_ast.rs +++ b/examples/arithmetic/parser_ast.rs @@ -8,7 +8,7 @@ use winnow::{ ascii::{digit1 as digits, multispace0 as multispaces}, combinator::alt, combinator::delimited, - combinator::fold_repeat, + combinator::repeat, token::one_of, }; @@ -52,37 +52,35 @@ impl Display for Expr { pub fn expr(i: &mut &str) -> PResult { let init = term.parse_next(i)?; - fold_repeat( - 0.., - (one_of(['+', '-']), term), - move || init.clone(), - |acc, (op, val): (char, Expr)| { - if op == '+' { - Expr::Add(Box::new(acc), Box::new(val)) - } else { - Expr::Sub(Box::new(acc), Box::new(val)) - } - }, - ) - .parse_next(i) + repeat(0.., (one_of(['+', '-']), term)) + .fold( + move || init.clone(), + |acc, (op, val): (char, Expr)| { + if op == '+' { + Expr::Add(Box::new(acc), Box::new(val)) + } else { + Expr::Sub(Box::new(acc), Box::new(val)) + } + }, + ) + .parse_next(i) } fn term(i: &mut &str) -> PResult { let init = factor.parse_next(i)?; - fold_repeat( - 0.., - (one_of(['*', '/']), factor), - move || init.clone(), - |acc, (op, val): (char, Expr)| { - if op == '*' { - Expr::Mul(Box::new(acc), Box::new(val)) - } else { - Expr::Div(Box::new(acc), Box::new(val)) - } - }, - ) - .parse_next(i) + repeat(0.., (one_of(['*', '/']), factor)) + .fold( + move || init.clone(), + |acc, (op, val): (char, Expr)| { + if op == '*' { + Expr::Mul(Box::new(acc), Box::new(val)) + } else { + Expr::Div(Box::new(acc), Box::new(val)) + } + }, + ) + .parse_next(i) } fn factor(i: &mut &str) -> PResult { diff --git a/examples/arithmetic/parser_lexer.rs b/examples/arithmetic/parser_lexer.rs index f49566d3..d6b7422d 100644 --- a/examples/arithmetic/parser_lexer.rs +++ b/examples/arithmetic/parser_lexer.rs @@ -9,7 +9,6 @@ use winnow::{ combinator::alt, combinator::dispatch, combinator::fail, - combinator::fold_repeat, combinator::peek, combinator::repeat, combinator::{delimited, preceded, terminated}, @@ -125,12 +124,14 @@ fn token(i: &mut &str) -> PResult { pub fn expr(i: &mut &[Token]) -> PResult { let init = term.parse_next(i)?; - fold_repeat( + repeat( 0.., ( one_of([Token::Oper(Oper::Add), Token::Oper(Oper::Sub)]), term, ), + ) + .fold( move || init.clone(), |acc, (op, val): (Token, Expr)| { if op == Token::Oper(Oper::Add) { @@ -146,12 +147,14 @@ pub fn expr(i: &mut &[Token]) -> PResult { fn term(i: &mut &[Token]) -> PResult { let init = factor.parse_next(i)?; - fold_repeat( + repeat( 0.., ( one_of([Token::Oper(Oper::Mul), Token::Oper(Oper::Div)]), factor, ), + ) + .fold( move || init.clone(), |acc, (op, val): (Token, Expr)| { if op == Token::Oper(Oper::Mul) { diff --git a/examples/json/parser.rs b/examples/json/parser.rs index 250621e9..e8d9c8ac 100644 --- a/examples/json/parser.rs +++ b/examples/json/parser.rs @@ -7,7 +7,7 @@ use winnow::{ combinator::alt, combinator::cut_err, combinator::{delimited, preceded, separated_pair, terminated}, - combinator::{fold_repeat, separated}, + combinator::{repeat, separated}, error::{AddContext, ParserError}, token::{any, none_of, take, take_while}, }; @@ -87,7 +87,7 @@ fn string<'i, E: ParserError> + AddContext, &'static str>> // right branch (since we found the `"` character) but encountered an error when // parsing the string cut_err(terminated( - fold_repeat(0.., character, String::new, |mut string, c| { + repeat(0.., character).fold(String::new, |mut string, c| { string.push(c); string }), diff --git a/examples/json/parser_dispatch.rs b/examples/json/parser_dispatch.rs index bc54d431..11bda4f8 100644 --- a/examples/json/parser_dispatch.rs +++ b/examples/json/parser_dispatch.rs @@ -10,7 +10,7 @@ use winnow::{ combinator::peek, combinator::{alt, dispatch}, combinator::{delimited, preceded, separated_pair, terminated}, - combinator::{fold_repeat, separated}, + combinator::{repeat, separated}, error::{AddContext, ParserError}, token::{any, none_of, take, take_while}, }; @@ -96,7 +96,7 @@ fn string<'i, E: ParserError> + AddContext, &'static str>> // right branch (since we found the `"` character) but encountered an error when // parsing the string cut_err(terminated( - fold_repeat(0.., character, String::new, |mut string, c| { + repeat(0.., character).fold(String::new, |mut string, c| { string.push(c); string }), diff --git a/examples/json/parser_partial.rs b/examples/json/parser_partial.rs index 779e05f8..31aba008 100644 --- a/examples/json/parser_partial.rs +++ b/examples/json/parser_partial.rs @@ -7,7 +7,7 @@ use winnow::{ combinator::alt, combinator::{cut_err, rest}, combinator::{delimited, preceded, separated_pair, terminated}, - combinator::{fold_repeat, separated}, + combinator::{repeat, separated}, error::{AddContext, ParserError}, stream::Partial, token::{any, none_of, take, take_while}, @@ -88,7 +88,7 @@ fn string<'i, E: ParserError> + AddContext, &'static str>> // right branch (since we found the `"` character) but encountered an error when // parsing the string cut_err(terminated( - fold_repeat(0.., character, String::new, |mut string, c| { + repeat(0.., character).fold(String::new, |mut string, c| { string.push(c); string }), diff --git a/examples/ndjson/parser.rs b/examples/ndjson/parser.rs index 81b47459..101391e1 100644 --- a/examples/ndjson/parser.rs +++ b/examples/ndjson/parser.rs @@ -8,7 +8,7 @@ use winnow::{ combinator::alt, combinator::cut_err, combinator::{delimited, preceded, separated_pair, terminated}, - combinator::{fold_repeat, separated}, + combinator::{repeat, separated}, error::{AddContext, ParserError}, stream::Partial, token::{any, none_of, take, take_while}, @@ -92,7 +92,7 @@ fn string<'i, E: ParserError> + AddContext, &'static str>> // right branch (since we found the `"` character) but encountered an error when // parsing the string cut_err(terminated( - fold_repeat(0.., character, String::new, |mut string, c| { + repeat(0.., character).fold(String::new, |mut string, c| { string.push(c); string }), diff --git a/examples/string/parser.rs b/examples/string/parser.rs index 01de737b..77013351 100644 --- a/examples/string/parser.rs +++ b/examples/string/parser.rs @@ -11,7 +11,7 @@ use winnow::ascii::multispace1; use winnow::combinator::alt; -use winnow::combinator::fold_repeat; +use winnow::combinator::repeat; use winnow::combinator::{delimited, preceded}; use winnow::error::{FromExternalError, ParserError}; use winnow::prelude::*; @@ -23,12 +23,14 @@ pub fn parse_string<'a, E>(input: &mut &'a str) -> PResult where E: ParserError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>, { - // fold_repeat is the equivalent of iterator::fold. It runs a parser in a loop, + // Repeat::fold is the equivalent of iterator::fold. It runs a parser in a loop, // and for each output value, calls a folding function on each output value. - let build_string = fold_repeat( + let build_string = repeat( 0.., // Our parser function – parses a single string fragment parse_fragment, + ) + .fold( // Our init value, an empty string String::new, // Our folding function. For each fragment, append the fragment to the @@ -45,7 +47,7 @@ where // Finally, parse the string. Note that, if `build_string` could accept a raw // " character, the closing delimiter " would never match. When using - // `delimited` with a looping parser (like fold_repeat), be sure that the + // `delimited` with a looping parser (like Repeat::fold), be sure that the // loop won't accidentally match your closing delimiter! delimited('"', build_string, '"').parse_next(input) } diff --git a/src/combinator/multi.rs b/src/combinator/multi.rs index c9bcf874..f76d6351 100644 --- a/src/combinator/multi.rs +++ b/src/combinator/multi.rs @@ -249,6 +249,10 @@ where /// assert_eq!(parser(""), Ok(("", vec![]))); /// assert_eq!(parser("abcabcabc"), Ok(("abc", vec!["abc", "abc"]))); /// ``` + #[doc(alias = "fold_many0")] + #[doc(alias = "fold_many1")] + #[doc(alias = "fold_many_m_n")] + #[doc(alias = "fold_repeat")] #[inline(always)] pub fn fold(mut self, mut init: H, mut g: G) -> impl Parser where @@ -1244,103 +1248,8 @@ where }) } -/// Repeats the embedded parser `m..=n` times, calling `g` to gather the results -/// -/// This stops before `n` when the parser returns [`ErrMode::Backtrack`]. To instead chain an error up, see -/// [`cut_err`][crate::combinator::cut_err]. -/// -/// # Arguments -/// * `m` The minimum number of iterations. -/// * `n` The maximum number of iterations. -/// * `f` The parser to apply. -/// * `init` A function returning the initial value. -/// * `g` The function that combines a result of `f` with -/// the current accumulator. -/// -/// **Warning:** If the parser passed to `fold_repeat` accepts empty inputs -/// (like `alpha0` or `digit0`), `fold_repeat` will return an error, -/// to prevent going into an infinite loop. -/// -/// # Example -/// -/// Zero or more repetitions: -/// ```rust -/// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed}; -/// # use winnow::prelude::*; -/// use winnow::combinator::fold_repeat; -/// use winnow::token::tag; -/// -/// fn parser(s: &str) -> IResult<&str, Vec<&str>> { -/// fold_repeat( -/// 0.., -/// "abc", -/// Vec::new, -/// |mut acc: Vec<_>, item| { -/// acc.push(item); -/// acc -/// } -/// ).parse_peek(s) -/// } -/// -/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); -/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); -/// assert_eq!(parser("123123"), Ok(("123123", vec![]))); -/// assert_eq!(parser(""), Ok(("", vec![]))); -/// ``` -/// -/// One or more repetitions: -/// ```rust -/// # use winnow::{error::ErrMode, error::{InputError, ErrorKind}, error::Needed}; -/// # use winnow::prelude::*; -/// use winnow::combinator::fold_repeat; -/// use winnow::token::tag; -/// -/// fn parser(s: &str) -> IResult<&str, Vec<&str>> { -/// fold_repeat( -/// 1.., -/// "abc", -/// Vec::new, -/// |mut acc: Vec<_>, item| { -/// acc.push(item); -/// acc -/// } -/// ).parse_peek(s) -/// } -/// -/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); -/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); -/// assert_eq!(parser("123123"), Err(ErrMode::Backtrack(InputError::new("123123", ErrorKind::Many)))); -/// assert_eq!(parser(""), Err(ErrMode::Backtrack(InputError::new("", ErrorKind::Many)))); -/// ``` -/// -/// Arbitrary number of repetitions: -/// ```rust -/// # use winnow::{error::ErrMode, error::ErrorKind, error::Needed}; -/// # use winnow::prelude::*; -/// use winnow::combinator::fold_repeat; -/// use winnow::token::tag; -/// -/// fn parser(s: &str) -> IResult<&str, Vec<&str>> { -/// fold_repeat( -/// 0..=2, -/// "abc", -/// Vec::new, -/// |mut acc: Vec<_>, item| { -/// acc.push(item); -/// acc -/// } -/// ).parse_peek(s) -/// } -/// -/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"]))); -/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"]))); -/// assert_eq!(parser("123123"), Ok(("123123", vec![]))); -/// assert_eq!(parser(""), Ok(("", vec![]))); -/// assert_eq!(parser("abcabcabc"), Ok(("abc", vec!["abc", "abc"]))); -/// ``` -#[doc(alias = "fold_many0")] -#[doc(alias = "fold_many1")] -#[doc(alias = "fold_many_m_n")] +/// Deprecated, replaced with [`Repeat::fold`] +#[deprecated(since = "0.5.36", note = "Replaced with `repeat(...).fold(...)`")] #[inline(always)] pub fn fold_repeat( range: impl Into, diff --git a/src/combinator/tests.rs b/src/combinator/tests.rs index 213d17d9..726b410b 100644 --- a/src/combinator/tests.rs +++ b/src/combinator/tests.rs @@ -1201,7 +1201,9 @@ fn fold_repeat0_test() { acc } fn multi(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { - fold_repeat(0.., "abcd", Vec::new, fold_into_vec).parse_peek(i) + repeat(0.., "abcd") + .fold(Vec::new, fold_into_vec) + .parse_peek(i) } assert_eq!( @@ -1239,7 +1241,7 @@ fn fold_repeat0_empty_test() { acc } fn multi_empty(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { - fold_repeat(0.., "", Vec::new, fold_into_vec).parse_peek(i) + repeat(0.., "").fold(Vec::new, fold_into_vec).parse_peek(i) } assert_eq!( @@ -1259,7 +1261,9 @@ fn fold_repeat1_test() { acc } fn multi(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { - fold_repeat(1.., "abcd", Vec::new, fold_into_vec).parse_peek(i) + repeat(1.., "abcd") + .fold(Vec::new, fold_into_vec) + .parse_peek(i) } let a = &b"abcdef"[..]; @@ -1295,7 +1299,9 @@ fn fold_repeat_test() { acc } fn multi(i: Partial<&[u8]>) -> IResult, Vec<&[u8]>> { - fold_repeat(2..=4, "Abcd", Vec::new, fold_into_vec).parse_peek(i) + repeat(2..=4, "Abcd") + .fold(Vec::new, fold_into_vec) + .parse_peek(i) } let a = &b"Abcdef"[..]; diff --git a/tests/testsuite/reborrow_fold.rs b/tests/testsuite/reborrow_fold.rs index f4597216..f3248e38 100644 --- a/tests/testsuite/reborrow_fold.rs +++ b/tests/testsuite/reborrow_fold.rs @@ -4,7 +4,7 @@ use std::str; use winnow::combinator::delimited; -use winnow::combinator::fold_repeat; +use winnow::combinator::repeat; use winnow::error::InputError; use winnow::prelude::*; use winnow::token::take_till; @@ -20,15 +20,10 @@ fn atom<'a>(_tomb: &mut ()) -> impl Parser<&'a [u8], String, InputError<&'a [u8] fn list<'a>(i: &'a [u8], tomb: &mut ()) -> IResult<&'a [u8], String> { delimited( '(', - fold_repeat( - 0.., - atom(tomb), - String::new, - |mut acc: String, next: String| { - acc.push_str(next.as_str()); - acc - }, - ), + repeat(0.., atom(tomb)).fold(String::new, |mut acc: String, next: String| { + acc.push_str(next.as_str()); + acc + }), ')', ) .parse_peek(i)