Skip to content

Commit

Permalink
introduce a workaround for float parsing
Browse files Browse the repository at this point in the history
this will afford some time until we get a proper fix with minimal
lexical for floats that are to large
  • Loading branch information
Geal committed Nov 3, 2021
1 parent 02433c2 commit 66a77a2
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 5 deletions.
70 changes: 67 additions & 3 deletions src/number/complete.rs
Expand Up @@ -1426,6 +1426,40 @@ where
)(input)
}

// workaround until issues with minimal-lexical are fixed
#[doc(hidden)]
pub fn recognize_float_or_exceptions<T, E: ParseError<T>>(input: T) -> IResult<T, T, E>
where
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>>,
T: Clone + Offset,
T: InputIter + InputTake + Compare<&'static str>,
<T as InputIter>::Item: AsChar,
T: InputTakeAtPosition,
<T as InputTakeAtPosition>::Item: AsChar,
{
alt((
|i: T| {
recognize_float::<_, E>(i.clone()).map_err(|e| match e {
crate::Err::Error(_) => crate::Err::Error(E::from_error_kind(i, ErrorKind::Float)),
crate::Err::Failure(_) => crate::Err::Failure(E::from_error_kind(i, ErrorKind::Float)),
crate::Err::Incomplete(needed) => crate::Err::Incomplete(needed),
})
},
|i: T| {
crate::bytes::complete::tag_no_case::<_, _, E>("nan")(i.clone())
.map_err(|_| crate::Err::Error(E::from_error_kind(i, ErrorKind::Float)))
},
|i: T| {
crate::bytes::complete::tag_no_case::<_, _, E>("inf")(i.clone())
.map_err(|_| crate::Err::Error(E::from_error_kind(i, ErrorKind::Float)))
},
|i: T| {
crate::bytes::complete::tag_no_case::<_, _, E>("infinity")(i.clone())
.map_err(|_| crate::Err::Error(E::from_error_kind(i, ErrorKind::Float)))
},
))(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,6 +1551,8 @@ where
Ok((i, (sign, integer, fraction, exp)))
}

use crate::traits::ParseTo;

/// Recognizes floating point number in text format and returns a f32.
///
/// *Complete version*: Can parse until the end of input.
Expand All @@ -1537,7 +1573,7 @@ where
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: Clone + Offset + ParseTo<f32> + Compare<&'static str>,
T: InputIter + InputLength + InputTake,
<T as InputIter>::Item: AsChar + Copy,
<T as InputIter>::IterElem: Clone,
Expand All @@ -1546,6 +1582,7 @@ where
T: AsBytes,
T: for<'a> Compare<&'a [u8]>,
{
/*
let (i, (sign, integer, fraction, exponent)) = recognize_float_parts(input)?;
let mut float: f32 = minimal_lexical::parse_float(
Expand All @@ -1558,6 +1595,15 @@ where
}
Ok((i, float))
*/
let (i, s) = recognize_float_or_exceptions(input)?;
match s.parse_to() {
Some(f) => (Ok((i, f))),
None => Err(crate::Err::Error(E::from_error_kind(
i,
crate::error::ErrorKind::Float,
))),
}
}

/// Recognizes floating point number in text format and returns a f64.
Expand All @@ -1580,7 +1626,7 @@ where
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: Clone + Offset + ParseTo<f64> + Compare<&'static str>,
T: InputIter + InputLength + InputTake,
<T as InputIter>::Item: AsChar + Copy,
<T as InputIter>::IterElem: Clone,
Expand All @@ -1589,6 +1635,7 @@ where
T: AsBytes,
T: for<'a> Compare<&'a [u8]>,
{
/*
let (i, (sign, integer, fraction, exponent)) = recognize_float_parts(input)?;
let mut float: f64 = minimal_lexical::parse_float(
Expand All @@ -1601,6 +1648,15 @@ where
}
Ok((i, float))
*/
let (i, s) = recognize_float_or_exceptions(input)?;
match s.parse_to() {
Some(f) => (Ok((i, f))),
None => Err(crate::Err::Error(E::from_error_kind(
i,
crate::error::ErrorKind::Float,
))),
}
}

#[cfg(test)]
Expand Down Expand Up @@ -1941,6 +1997,7 @@ mod tests {
"12.34",
"-1.234E-12",
"-1.234e-12",
"0.00000000000000000087",
];

for test in test_cases.drain(..) {
Expand All @@ -1964,6 +2021,14 @@ mod tests {
recognize_float(remaining_exponent),
Err(Err::Failure(("", ErrorKind::Digit)))
);

let (_i, nan) = float::<_, ()>("NaN").unwrap();
assert!(nan.is_nan());

let (_i, inf) = float::<_, ()>("inf").unwrap();
assert!(inf.is_infinite());
let (_i, inf) = float::<_, ()>("infinite").unwrap();
assert!(inf.is_infinite());
}

#[test]
Expand Down Expand Up @@ -2051,7 +2116,6 @@ mod tests {

#[cfg(feature = "std")]
fn parse_f64(i: &str) -> IResult<&str, f64, ()> {
use crate::traits::ParseTo;
match recognize_float(i) {
Err(e) => Err(e),
Ok((i, s)) => {
Expand Down
67 changes: 65 additions & 2 deletions src/number/streaming.rs
Expand Up @@ -1395,6 +1395,40 @@ where
)(input)
}

// workaround until issues with minimal-lexical are fixed
#[doc(hidden)]
pub fn recognize_float_or_exceptions<T, E: ParseError<T>>(input: T) -> IResult<T, T, E>
where
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>>,
T: Clone + Offset,
T: InputIter + InputTake + InputLength + Compare<&'static str>,
<T as InputIter>::Item: AsChar,
T: InputTakeAtPosition,
<T as InputTakeAtPosition>::Item: AsChar,
{
alt((
|i: T| {
recognize_float::<_, E>(i.clone()).map_err(|e| match e {
crate::Err::Error(_) => crate::Err::Error(E::from_error_kind(i, ErrorKind::Float)),
crate::Err::Failure(_) => crate::Err::Failure(E::from_error_kind(i, ErrorKind::Float)),
crate::Err::Incomplete(needed) => crate::Err::Incomplete(needed),
})
},
|i: T| {
crate::bytes::streaming::tag_no_case::<_, _, E>("nan")(i.clone())
.map_err(|_| crate::Err::Error(E::from_error_kind(i, ErrorKind::Float)))
},
|i: T| {
crate::bytes::streaming::tag_no_case::<_, _, E>("inf")(i.clone())
.map_err(|_| crate::Err::Error(E::from_error_kind(i, ErrorKind::Float)))
},
|i: T| {
crate::bytes::streaming::tag_no_case::<_, _, E>("infinity")(i.clone())
.map_err(|_| crate::Err::Error(E::from_error_kind(i, ErrorKind::Float)))
},
))(input)
}

/// Recognizes a floating point number in text format and returns the integer, fraction and exponent parts of the input data
///
/// *Streaming version*: Will return `Err(nom::Err::Incomplete(_))` if there is not enough data.
Expand Down Expand Up @@ -1512,14 +1546,15 @@ pub fn float<T, E: ParseError<T>>(input: T) -> IResult<T, f32, E>
where
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>>,
T: Clone + Offset,
T: InputIter + InputLength + InputTake + crate::traits::ParseTo<i32>,
T: InputIter + InputLength + InputTake + crate::traits::ParseTo<f32> + Compare<&'static str>,
<T as InputIter>::Item: AsChar,
<T as InputIter>::IterElem: Clone,
T: InputTakeAtPosition,
<T as InputTakeAtPosition>::Item: AsChar,
T: AsBytes,
T: for<'a> Compare<&'a [u8]>,
{
/*
let (i, (sign, integer, fraction, exponent)) = recognize_float_parts(input)?;
let mut float: f32 = minimal_lexical::parse_float(
Expand All @@ -1532,6 +1567,15 @@ where
}
Ok((i, float))
*/
let (i, s) = recognize_float_or_exceptions(input)?;
match s.parse_to() {
Some(f) => (Ok((i, f))),
None => Err(crate::Err::Error(E::from_error_kind(
i,
crate::error::ErrorKind::Float,
))),
}
}

/// Recognizes floating point number in text format and returns a f64.
Expand All @@ -1556,14 +1600,15 @@ pub fn double<T, E: ParseError<T>>(input: T) -> IResult<T, f64, E>
where
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>>,
T: Clone + Offset,
T: InputIter + InputLength + InputTake + crate::traits::ParseTo<i32>,
T: InputIter + InputLength + InputTake + crate::traits::ParseTo<f64> + Compare<&'static str>,
<T as InputIter>::Item: AsChar,
<T as InputIter>::IterElem: Clone,
T: InputTakeAtPosition,
<T as InputTakeAtPosition>::Item: AsChar,
T: AsBytes,
T: for<'a> Compare<&'a [u8]>,
{
/*
let (i, (sign, integer, fraction, exponent)) = recognize_float_parts(input)?;
let mut float: f64 = minimal_lexical::parse_float(
Expand All @@ -1576,6 +1621,15 @@ where
}
Ok((i, float))
*/
let (i, s) = recognize_float_or_exceptions(input)?;
match s.parse_to() {
Some(f) => (Ok((i, f))),
None => Err(crate::Err::Error(E::from_error_kind(
i,
crate::error::ErrorKind::Float,
))),
}
}

#[cfg(test)]
Expand Down Expand Up @@ -2022,6 +2076,7 @@ mod tests {
"12.34",
"-1.234E-12",
"-1.234e-12",
"0.00000000000000000087",
];

for test in test_cases.drain(..) {
Expand All @@ -2045,6 +2100,14 @@ mod tests {
recognize_float(remaining_exponent),
Err(Err::Incomplete(Needed::new(1)))
);

let (_i, nan) = float::<_, ()>("NaN").unwrap();
assert!(nan.is_nan());

let (_i, inf) = float::<_, ()>("inf").unwrap();
assert!(inf.is_infinite());
let (_i, inf) = float::<_, ()>("infinite").unwrap();
assert!(inf.is_infinite());
}

#[test]
Expand Down

0 comments on commit 66a77a2

Please sign in to comment.