Skip to content

Commit

Permalink
Fixes rust-bakery#1384, by ensuring specials are properly parsed.
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexhuszagh committed Sep 10, 2021
1 parent 3b872da commit 4c76b47
Show file tree
Hide file tree
Showing 2 changed files with 388 additions and 22 deletions.
203 changes: 191 additions & 12 deletions src/number/complete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1426,6 +1426,8 @@ where
)(input)
}

///

/// Recognizes a floating point number in text format and returns the integer, fraction and exponent parts of the input data
///
/// *Complete version*: Can parse until the end of input.
Expand Down Expand Up @@ -1517,24 +1519,35 @@ where
Ok((i, (sign, integer, fraction, exp)))
}

/// Case-insensitive comparison of digits. Only works if `y` is only ASCII letters.
#[inline]
fn case_insensitive_cmp(x: &[u8], y: &[u8]) -> bool {
let d = (x.iter().zip(y.iter())).fold(0, |d, (xi, yi)| d | xi ^ yi);
// This uses the trick that 'a' - 'A' == 0x20, and this is true
// for all characters, so as long as `yi` is a valid ASCII letter,
// `xi ^ yi` can only be 0 or 0x20.
d == 0 || d == 32
}

/// Recognizes floating point number in text format and returns a f32.
///
/// *Complete version*: Can parse until the end of input.
/// *Complete version*: Can parse until the end of input. This only handles
/// finite (non-special floats).
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::number::complete::float;
/// use nom::number::complete::float_finite;
///
/// let parser = |s| {
/// float(s)
/// float_finite(s)
/// };
///
/// assert_eq!(parser("11e-1"), Ok(("", 1.1)));
/// assert_eq!(parser("123E-02"), Ok(("", 1.23)));
/// assert_eq!(parser("123K-01"), Ok(("K-01", 123.0)));
/// assert_eq!(parser("abc"), Err(Err::Error(("abc", ErrorKind::Float))));
/// ```
pub fn float<T, E: ParseError<T>>(input: T) -> IResult<T, f32, E>
pub fn float_finite<T, E: ParseError<T>>(input: T) -> IResult<T, f32, E>
where
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>> + Slice<Range<usize>>,
T: Clone + Offset,
Expand All @@ -1560,6 +1573,42 @@ where
Ok((i, float))
}

/// Recognizes floating point number in text format and returns a f32.
/// This only handles non-finite (special) values.
pub fn float_nonfinite<T, E: ParseError<T>>(input: T) -> IResult<T, f32, E>
where
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>> + Slice<Range<usize>>,
T: Clone + Offset,
T: InputIter + InputLength + InputTake,
<T as InputIter>::Item: AsChar + Copy,
<T as InputIter>::IterElem: Clone,
T: InputTakeAtPosition,
<T as InputTakeAtPosition>::Item: AsChar,
T: AsBytes,
T: for<'a> Compare<&'a [u8]>,
{
let (i, sign) = sign(input.clone())?;
let (mut float, count) = if i.input_len() >= 3 {
if case_insensitive_cmp(i.as_bytes(), b"nan") {
(f32::NAN, 3)
} else if i.input_len() >= 8 && case_insensitive_cmp(i.as_bytes(), b"infinity") {
(f32::INFINITY, 8)
} else if case_insensitive_cmp(i.as_bytes(), b"inf") {
(f32::INFINITY, 3)
} else {
return Err(Err::Error(E::from_error_kind(input, ErrorKind::Float)));
}
} else {
return Err(Err::Error(E::from_error_kind(input, ErrorKind::Float)));
};

if !sign {
float = -float;
}

Ok((i.slice(count..), float))
}

/// Recognizes floating point number in text format and returns a f32.
///
/// *Complete version*: Can parse until the end of input.
Expand All @@ -1577,7 +1626,44 @@ where
/// assert_eq!(parser("123K-01"), Ok(("K-01", 123.0)));
/// assert_eq!(parser("abc"), Err(Err::Error(("abc", ErrorKind::Float))));
/// ```
pub fn double<T, E: ParseError<T>>(input: T) -> IResult<T, f64, E>
pub fn float<T, E: ParseError<T>>(input: T) -> IResult<T, f32, E>
where
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>> + Slice<Range<usize>>,
T: Clone + Offset,
T: InputIter + InputLength + InputTake,
<T as InputIter>::Item: AsChar + Copy,
<T as InputIter>::IterElem: Clone,
T: InputTakeAtPosition,
<T as InputTakeAtPosition>::Item: AsChar,
T: AsBytes,
T: for<'a> Compare<&'a [u8]>,
{
if let Ok((i, float)) = float_finite::<T, E>(input.clone()) {
Ok((i, float))
} else {
float_nonfinite::<T, E>(input)
}
}

/// Recognizes floating point number in text format and returns a f64.
///
/// *Complete version*: Can parse until the end of input. This only handles
/// finite (non-special floats).
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::number::complete::double_finite;
///
/// let parser = |s| {
/// double_finite(s)
/// };
///
/// assert_eq!(parser("11e-1"), Ok(("", 1.1)));
/// assert_eq!(parser("123E-02"), Ok(("", 1.23)));
/// assert_eq!(parser("123K-01"), Ok(("K-01", 123.0)));
/// assert_eq!(parser("abc"), Err(Err::Error(("abc", ErrorKind::Float))));
/// ```
pub fn double_finite<T, E: ParseError<T>>(input: T) -> IResult<T, f64, E>
where
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>> + Slice<Range<usize>>,
T: Clone + Offset,
Expand All @@ -1603,6 +1689,78 @@ where
Ok((i, float))
}

/// Recognizes floating point number in text format and returns a f64.
/// This only handles non-finite (special) values.
pub fn double_nonfinite<T, E: ParseError<T>>(input: T) -> IResult<T, f64, E>
where
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>> + Slice<Range<usize>>,
T: Clone + Offset,
T: InputIter + InputLength + InputTake,
<T as InputIter>::Item: AsChar + Copy,
<T as InputIter>::IterElem: Clone,
T: InputTakeAtPosition,
<T as InputTakeAtPosition>::Item: AsChar,
T: AsBytes,
T: for<'a> Compare<&'a [u8]>,
{
let (i, sign) = sign(input.clone())?;
let (mut double, count) = if i.input_len() >= 3 {
if case_insensitive_cmp(i.as_bytes(), b"nan") {
(f64::NAN, 3)
} else if i.input_len() >= 8 && case_insensitive_cmp(i.as_bytes(), b"infinity") {
(f64::INFINITY, 8)
} else if case_insensitive_cmp(i.as_bytes(), b"inf") {
(f64::INFINITY, 3)
} else {
return Err(Err::Error(E::from_error_kind(input, ErrorKind::Float)));
}
} else {
return Err(Err::Error(E::from_error_kind(input, ErrorKind::Float)));
};

if !sign {
double = -double;
}

Ok((i.slice(count..), double))
}

/// Recognizes floating point number in text format and returns a f64.
///
/// *Complete version*: Can parse until the end of input.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::number::complete::double;
///
/// let parser = |s| {
/// double(s)
/// };
///
/// assert_eq!(parser("11e-1"), Ok(("", 1.1)));
/// assert_eq!(parser("123E-02"), Ok(("", 1.23)));
/// assert_eq!(parser("123K-01"), Ok(("K-01", 123.0)));
/// assert_eq!(parser("abc"), Err(Err::Error(("abc", ErrorKind::Float))));
/// ```
pub fn double<T, E: ParseError<T>>(input: T) -> IResult<T, f64, E>
where
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>> + Slice<Range<usize>>,
T: Clone + Offset,
T: InputIter + InputLength + InputTake,
<T as InputIter>::Item: AsChar + Copy,
<T as InputIter>::IterElem: Clone,
T: InputTakeAtPosition,
<T as InputTakeAtPosition>::Item: AsChar,
T: AsBytes,
T: for<'a> Compare<&'a [u8]>,
{
match double_finite::<T, E>(input.clone()) {
Ok((i, double)) => Ok((i, double)),
Err(Err::Incomplete(e)) => Err(Err::Incomplete(e)),
_ => double_nonfinite::<T, E>(input),
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -1618,6 +1776,23 @@ mod tests {
};
);

// Need more complex logic, since NaN != NaN.
macro_rules! assert_float_eq {
($left: expr, $right: expr) => {
let left: $crate::IResult<_, _, (_, ErrorKind)> = $left;
let right: $crate::IResult<_, _, (_, ErrorKind)> = $right;
if let Ok((_, float)) = right {
if float.is_nan() {
assert!(left.unwrap().1.is_nan());
} else {
assert_eq!(left, right);
}
}else {
assert_eq!(left, right);
}
};
}

#[test]
fn i8_tests() {
assert_parse!(i8(&[0x00][..]), Ok((&b""[..], 0)));
Expand Down Expand Up @@ -1942,6 +2117,8 @@ mod tests {
"12.34",
"-1.234E-12",
"-1.234e-12",
"NaN",
"inf",
];

for test in test_cases.drain(..) {
Expand All @@ -1951,13 +2128,15 @@ mod tests {
println!("now parsing: {} -> {}", test, expected32);

let larger = format!("{}", test);
assert_parse!(recognize_float(&larger[..]), Ok(("", test)));
if expected32.is_finite() {
assert_parse!(recognize_float(&larger[..]), Ok(("", test)));
}

assert_parse!(float(larger.as_bytes()), Ok((&b""[..], expected32)));
assert_parse!(float(&larger[..]), Ok(("", expected32)));
assert_float_eq!(float(larger.as_bytes()), Ok((&b""[..], expected32)));
assert_float_eq!(float(&larger[..]), Ok(("", expected32)));

assert_parse!(double(larger.as_bytes()), Ok((&b""[..], expected64)));
assert_parse!(double(&larger[..]), Ok(("", expected64)));
assert_float_eq!(double(larger.as_bytes()), Ok((&b""[..], expected64)));
assert_float_eq!(double(&larger[..]), Ok(("", expected64)));
}

let remaining_exponent = "-1.234E-";
Expand Down Expand Up @@ -2051,8 +2230,8 @@ mod tests {
}

fn parse_f64(i: &str) -> IResult<&str, f64, ()> {
match recognize_float(i) {
Err(e) => Err(e),
match recognize_float::<_, ()>(i) {
Err(_) => Err(Err::Error(())),
Ok((i, s)) => {
if s.is_empty() {
return Err(Err::Error(()));
Expand Down

0 comments on commit 4c76b47

Please sign in to comment.