Skip to content

Commit

Permalink
Add fold_count parser equivalent for count
Browse files Browse the repository at this point in the history
  • Loading branch information
nickelc committed Sep 16, 2021
1 parent 615e6a4 commit 1efbe3a
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 2 deletions.
68 changes: 68 additions & 0 deletions src/multi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,74 @@ where
}
}

/// Runs the embedded parser a specified number of times and accumulates
/// the results using a given function and initial value.
/// # Arguments
/// * `count` How often to apply the parser.
/// * `f` The parser to apply.
/// * `init` A function returning the initial value.
/// * `fold` The function that combines a result of `f` with
/// the current accumulator.
/// ```rust
/// # use nom::{Err, error::{Error, ErrorKind}, Needed, IResult};
/// use nom::multi::fold_count;
/// use nom::bytes::complete::tag;
///
/// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
/// fold_count(
/// 2,
/// tag("abc"),
/// Vec::new,
/// |mut acc, item| {
/// acc.push(item);
/// acc
/// }
/// )(s)
/// }
///
/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"])));
/// assert_eq!(parser("abc123"), Err(Err::Error(Error::new("123", ErrorKind::Tag))));
/// assert_eq!(parser("123123"), Err(Err::Error(Error::new("123123", ErrorKind::Tag))));
/// assert_eq!(parser(""), Err(Err::Error(Error::new("", ErrorKind::Tag))));
/// assert_eq!(parser("abcabcabc"), Ok(("abc", vec!["abc", "abc"])));
/// ```
pub fn fold_count<I, O, E, F, G, H, R>(
count: usize,
mut f: F,
mut init: H,
mut fold: G,
) -> impl FnMut(I) -> IResult<I, R, E>
where
I: Clone + InputLength,
F: Parser<I, O, E>,
G: FnMut(R, O) -> R,
H: FnMut() -> R,
E: ParseError<I>,
{
move |i: I| {
let mut input = i.clone();
let mut acc = init();

for _ in 0..count {
let input_ = input.clone();
match f.parse(input_) {
Ok((i, o)) => {
acc = fold(acc, o);
input = i;
}
Err(Err::Error(e)) => {
return Err(Err::Error(E::append(i, ErrorKind::Count, e)));
}
Err(e) => {
return Err(e);
}
}
}

Ok((input, acc))
}
}

/// Applies a parser until it fails and accumulates
/// the results using a given function and initial value.
/// # Arguments
Expand Down
40 changes: 38 additions & 2 deletions src/multi/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use crate::{
use crate::{
lib::std::vec::Vec,
multi::{
count, fold_many0, fold_many1, fold_many_m_n, length_count, many0, many1, many_m_n, many_till,
separated_list0, separated_list1,
count, fold_count, fold_many0, fold_many1, fold_many_m_n, length_count, many0, many1, many_m_n,
many_till, separated_list0, separated_list1,
},
};

Expand Down Expand Up @@ -414,6 +414,42 @@ fn length_value_test() {
assert_eq!(length_value_2(&i4), Ok((&i4[4..], (5, 6))));
}

#[test]
#[cfg(feature = "alloc")]
fn fold_count_test() {
fn fold_into_vec<T>(mut acc: Vec<T>, item: T) -> Vec<T> {
acc.push(item);
acc
}
fn cnt_2(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
fold_count(2, tag("abc"), Vec::new, fold_into_vec)(i)
}
assert_eq!(
cnt_2(&b"abcabcabcdef"[..]),
Ok((&b"abcdef"[..], vec![&b"abc"[..], &b"abc"[..]]))
);
assert_eq!(cnt_2(&b"ab"[..]), Err(Err::Incomplete(Needed::new(1))));
assert_eq!(cnt_2(&b"abcab"[..]), Err(Err::Incomplete(Needed::new(1))));
assert_eq!(
cnt_2(&b"xxx"[..]),
Err(Err::Error(error_position!(&b"xxx"[..], ErrorKind::Tag)))
);
assert_eq!(
cnt_2(&b"xxxabcabcdef"[..]),
Err(Err::Error(error_position!(
&b"xxxabcabcdef"[..],
ErrorKind::Tag
)))
);
assert_eq!(
cnt_2(&b"abcxxxabcdef"[..]),
Err(Err::Error(error_position!(
&b"xxxabcdef"[..],
ErrorKind::Tag
)))
);
}

#[test]
#[cfg(feature = "alloc")]
fn fold_many0_test() {
Expand Down

0 comments on commit 1efbe3a

Please sign in to comment.