add the satisfy combinator, that checks a predicate on the next char

This commit is contained in:
Geoffroy Couprie 2020-08-30 14:14:22 +02:00
parent b2ed755e27
commit 5108ed18d5
3 changed files with 70 additions and 0 deletions

View File

@ -37,6 +37,39 @@ where
}
}
/// Recognizes one character and checks that it satisfies a predicate
///
/// *Complete version*: Will return an error if there's not enough input data.
/// # Example
///
/// ```
/// # use nom::{Err, error::{ErrorKind, Error}, Needed, IResult};
/// # use nom::character::complete::satisfy;
/// # fn main() {
/// fn parser(i: &str) -> IResult<&str, char> {
/// satisfy(|c| c == 'a' || c == 'b')(i)
/// }
/// assert_eq!(parser("abc"), Ok(("bc", 'a')));
/// assert_eq!(parser("cd"), Err(Err::Error(Error::new("cd", ErrorKind::Satisfy))));
/// assert_eq!(parser(""), Err(Err::Error(Error::new("", ErrorKind::Satisfy))));
/// # }
/// ```
pub fn satisfy<F, I, Error: ParseError<I>>(cond: F) -> impl Fn(I) -> IResult<I, char, Error>
where
I: Slice<RangeFrom<usize>> + InputIter,
<I as InputIter>::Item: AsChar,
F: Fn(char) -> bool,
{
move |i: I| match (i).iter_elements().next().map(|t| {
let c = t.as_char();
let b = cond(c);
(c, b)
}) {
Some((c, true)) => Ok((i.slice(c.len()..), c)),
_ => Err(Err::Error(Error::from_error_kind(i, ErrorKind::Satisfy))),
}
}
/// Recognizes one of the provided characters.
///
/// *Complete version*: Will return an error if there's not enough input data.

View File

@ -39,6 +39,40 @@ where
}
}
/// Recognizes one character and checks that it satisfies a predicate
///
/// *Streaming version*: Will return `Err(nom::Err::Incomplete(_))` if there's not enough input data.
/// # Example
///
/// ```
/// # use nom::{Err, error::{ErrorKind, Error}, Needed, IResult};
/// # use nom::character::streaming::satisfy;
/// # fn main() {
/// fn parser(i: &str) -> IResult<&str, char> {
/// satisfy(|c| c == 'a' || c == 'b')(i)
/// }
/// assert_eq!(parser("abc"), Ok(("bc", 'a')));
/// assert_eq!(parser("cd"), Err(Err::Error(Error::new("cd", ErrorKind::Satisfy))));
/// assert_eq!(parser(""), Err(Err::Incomplete(Needed::Unknown)));
/// # }
/// ```
pub fn satisfy<F, I, Error: ParseError<I>>(cond: F) -> impl Fn(I) -> IResult<I, char, Error>
where
I: Slice<RangeFrom<usize>> + InputIter,
<I as InputIter>::Item: AsChar,
F: Fn(char) -> bool,
{
move |i: I| match (i).iter_elements().next().map(|t| {
let c = t.as_char();
let b = cond(c);
(c, b)
}) {
None => Err(Err::Incomplete(Needed::Unknown)),
Some((_, false)) => Err(Err::Error(Error::from_error_kind(i, ErrorKind::Satisfy))),
Some((c, true)) => Ok((i.slice(c.len()..), c)),
}
}
/// Recognizes one of the provided characters.
///
/// *Streaming version*: Will return `Err(nom::Err::Incomplete(_))` if there's not enough input data.

View File

@ -400,6 +400,7 @@ pub enum ErrorKind {
Many0Count,
Many1Count,
Float,
Satisfy,
}
#[cfg_attr(rustfmt, rustfmt_skip)]
@ -459,6 +460,7 @@ pub fn error_to_u32(e: &ErrorKind) -> u32 {
ErrorKind::Many0Count => 72,
ErrorKind::Many1Count => 73,
ErrorKind::Float => 74,
ErrorKind::Satisfy => 75,
}
}
@ -520,6 +522,7 @@ impl ErrorKind {
ErrorKind::Many0Count => "Count occurrence of >=0 patterns",
ErrorKind::Many1Count => "Count occurrence of >=1 patterns",
ErrorKind::Float => "Float",
ErrorKind::Satisfy => "Satisfy",
}
}
}