mirror of
https://github.com/openharmony/third_party_rust_nom.git
synced 2026-07-01 21:04:01 -04:00
replace lexical-core with minimal-lexical
minimal-lexical is a smaller library (compile faster), has no dependencies, and is faster than lexical-core 0.7 (0.8 will have the same algorithm). It requires a separate tokenization phase, done manually, but this will give more flexibility in supporting different syntaxes This commit removes the "lexical" feature, as there is no need now tosupport a separate version without the crate
This commit is contained in:
@@ -25,7 +25,7 @@ jobs:
|
||||
- rust: stable
|
||||
features: ''
|
||||
- rust: stable
|
||||
features: '--features "std lexical"'
|
||||
features: '--features "std"'
|
||||
- rust: stable
|
||||
features: '--no-default-features'
|
||||
- rust: stable
|
||||
@@ -127,7 +127,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: doc
|
||||
args: --verbose --features "std lexical docsrs"
|
||||
args: --verbose --features "std docsrs"
|
||||
|
||||
fmt:
|
||||
name: Check formatting
|
||||
|
||||
+4
-6
@@ -30,18 +30,16 @@ include = [
|
||||
[features]
|
||||
alloc = []
|
||||
std = ["alloc", "memchr/use_std"]
|
||||
default = ["std", "lexical"]
|
||||
lexical = ["lexical-core"]
|
||||
default = ["std"]
|
||||
docsrs = []
|
||||
|
||||
[dependencies]
|
||||
minimal-lexical = "0.1.2"
|
||||
|
||||
[dependencies.memchr]
|
||||
version = "2.0"
|
||||
default-features = false
|
||||
|
||||
[dependencies.lexical-core]
|
||||
version = "^0.7.5"
|
||||
optional = true
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
jemallocator = "^0.3"
|
||||
|
||||
@@ -191,12 +191,35 @@ fn float_str(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
use nom::ParseTo;
|
||||
use nom::Err;
|
||||
fn std_float(input: &[u8]) -> IResult<&[u8], f64, (&[u8], ErrorKind)> {
|
||||
match recognize_float(input) {
|
||||
Err(e) => Err(e),
|
||||
Ok((i, s)) => match s.parse_to() {
|
||||
Some(n) => Ok((i, n)),
|
||||
None => Err(Err::Error((i, ErrorKind::Float))),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn std_float_bytes(c: &mut Criterion) {
|
||||
println!(
|
||||
"std_float_bytes result: {:?}",
|
||||
std_float(&b"-1.234E-12"[..])
|
||||
);
|
||||
c.bench_function("std_float bytes", |b| {
|
||||
b.iter(|| std_float(&b"-1.234E-12"[..]));
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
json_bench,
|
||||
recognize_float_bytes,
|
||||
recognize_float_str,
|
||||
float_bytes,
|
||||
std_float_bytes,
|
||||
float_str
|
||||
);
|
||||
criterion_main!(benches);
|
||||
|
||||
@@ -7,3 +7,4 @@
|
||||
cc cc9654fa1abddf4d6045e4c4977fea390903ee6e6469630b0bb17fdf69219b6d # shrinks to s = "𑵧"
|
||||
cc 7dcadb118055527708beb3c5eadd3e14202a8f70e019004c33e9696853691827 # shrinks to s = ""
|
||||
cc e8af68daccf860a49177b5aab0dfeecea24c7530fec6c88469ca0f820188c6b1 # shrinks to s = "-"
|
||||
cc c98c899dcd0a9359ddbf246e3a1edddb349e6dd7e1d166637e551e4dcf570db6 # shrinks to s = "+0"
|
||||
|
||||
@@ -6,3 +6,5 @@
|
||||
# everyone who runs the test benefits from these saved cases.
|
||||
cc 82a575ed1f031825e7474bff3702d0c42017471b5ac845bdbdc00c1534dbc4cb # shrinks to s = ""
|
||||
cc 155f8f4b052941ba58b8b90a5f8fa7da78c04c1a451083a4a89a348c86226904 # shrinks to s = "0"
|
||||
cc c35a5a751223822dd0a94416d5ca3cc53b4a61cdc4f9422251bc2c72712ed844 # shrinks to s = "-0"
|
||||
cc 478373182b684c42ce3746ea62d57a35a9c764ef75943e0bb1dc08f88b295581 # shrinks to s = "- "
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Seeds for failure cases proptest has generated in the past. It is
|
||||
# automatically read and these particular cases re-run before any
|
||||
# novel cases are generated.
|
||||
#
|
||||
# It is recommended to check this file in to source control so that
|
||||
# everyone who runs the test benefits from these saved cases.
|
||||
cc b4267e69b3f62d9bfbc8b9a11d9250a4cc78a2f329841190fd2740b1b3e236d6 # shrinks to s = ""
|
||||
cc cf51966684042789e64f4b99449551cb227309f9db61f0fdd4266b0b7b572ce4 # shrinks to s = "0"
|
||||
cc fc0d8df9f3a0ea46ec05ff021be24244fe2533c4b5d75e74a78191148e2d07bb # shrinks to s = "0"
|
||||
cc 15e795e3c045df60a2fa871336f9bf43ca9036aeda19e8e440b956866b031a65 # shrinks to s = "0e"
|
||||
cc 20b201c32f3f8314cf32133c3e6a58dd1e4409ae883f7910efa1147ba7c73b6c # shrinks to s = "e"
|
||||
cc 47f9c093d94bc952a3593a79adc2cafa75c9cca51bee8a77150cfaeb89acbaf7 # shrinks to s = "01"
|
||||
cc 9d65816e63ee5da410b64aeb5f7d44dbfa7d0773c053380e05bff0beb1bc8d92 # shrinks to s = ".0"
|
||||
+148
-89
@@ -1,15 +1,15 @@
|
||||
//! Parsers recognizing numbers, complete input version
|
||||
|
||||
use crate::branch::alt;
|
||||
use crate::character::complete::{char, digit1};
|
||||
use crate::character::complete::{char, digit0, digit1};
|
||||
use crate::bytes::complete::take_while;
|
||||
use crate::combinator::{cut, map, opt, recognize};
|
||||
use crate::error::ParseError;
|
||||
use crate::error::{make_error, ErrorKind};
|
||||
use crate::internal::*;
|
||||
use crate::lib::std::ops::{RangeFrom, RangeTo};
|
||||
use crate::sequence::{pair, tuple};
|
||||
use crate::traits::{AsChar, InputIter, InputLength, InputTakeAtPosition};
|
||||
use crate::traits::{Offset, Slice};
|
||||
use crate::traits::{AsChar, InputIter, InputLength, InputTakeAtPosition, AsBytes, Offset, Slice};
|
||||
|
||||
#[doc(hidden)]
|
||||
macro_rules! map(
|
||||
@@ -1398,7 +1398,6 @@ pub fn hex_u32<'a, E: ParseError<&'a [u8]>>(input: &'a [u8]) -> IResult<&'a [u8]
|
||||
/// assert_eq!(parser("123K-01"), Ok(("K-01", "123")));
|
||||
/// assert_eq!(parser("abc"), Err(Err::Error(("abc", ErrorKind::Char))));
|
||||
/// ```
|
||||
#[allow(unused_imports)]
|
||||
#[rustfmt::skip]
|
||||
pub fn recognize_float<T, E:ParseError<T>>(input: T) -> IResult<T, T, E>
|
||||
where
|
||||
@@ -1425,7 +1424,90 @@ where
|
||||
)(input)
|
||||
}
|
||||
|
||||
/// Recognizes floating point number in a byte string and returns a f32.
|
||||
/// 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.
|
||||
///
|
||||
pub fn recognize_float_parts<T, E:ParseError<T>>(input: T) -> IResult<T, (bool, T, T, i32), E>
|
||||
where
|
||||
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>>,
|
||||
T: Clone + Offset,
|
||||
T: InputIter + crate::traits::ParseTo<i32>,
|
||||
<T as InputIter>::Item: AsChar,
|
||||
T: InputTakeAtPosition + InputLength,
|
||||
<T as InputTakeAtPosition>::Item: AsChar
|
||||
{
|
||||
let (i, opt_sign) = opt(alt((char('+'), char('-'))))(input.clone())?;
|
||||
let sign = match opt_sign {
|
||||
Some('+') => true,
|
||||
Some('-') => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
let (i, mut integer) = digit0(i)?;
|
||||
|
||||
if integer.input_len() >= 2 {
|
||||
// left trim zeroes for faster parsing in minimal-lexical
|
||||
let (_, int2) = take_while(|c: <T as InputTakeAtPosition>::Item| c.as_char() == '0')(integer.clone())?;
|
||||
// if it's all zeroes, keep one of them
|
||||
integer = if int2.input_len() == integer.input_len() {
|
||||
integer.slice(integer.input_len()-1..)
|
||||
} else {
|
||||
integer.slice(int2.input_len()..)
|
||||
};
|
||||
}
|
||||
|
||||
let (i, opt_dot) = opt(char('.'))(i)?;
|
||||
let (i, fraction) = if opt_dot.is_none() {
|
||||
let i2 = i.clone();
|
||||
(i2, i.slice(..0))
|
||||
} else {
|
||||
// match number, trim right zeroes
|
||||
let mut zero_count = 0usize;
|
||||
let mut position = None;
|
||||
for (pos, c) in i.iter_indices() {
|
||||
let c = c.as_char();
|
||||
|
||||
if c.is_ascii_digit() {
|
||||
if c == '0' {
|
||||
zero_count += 1;
|
||||
} else {
|
||||
zero_count = 0;
|
||||
}
|
||||
} else {
|
||||
position = Some(pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let position = position.unwrap_or(i.input_len());
|
||||
|
||||
let index = if zero_count == 0 {
|
||||
position
|
||||
} else if zero_count == position {
|
||||
position - zero_count + 1
|
||||
} else {
|
||||
position - zero_count
|
||||
};
|
||||
|
||||
(i.slice(position..), i.slice(..index))
|
||||
};
|
||||
|
||||
if integer.input_len() == 0 && fraction.input_len() == 0 {
|
||||
return Err(Err::Error(E::from_error_kind(input, ErrorKind::Float)))
|
||||
}
|
||||
|
||||
let (i, e) = opt(alt((char('e'), char('E'))))(i)?;
|
||||
let (i, exp) = if e.is_some() {
|
||||
cut(crate::character::complete::i32)(i)?
|
||||
} else {
|
||||
(i, 0)
|
||||
};
|
||||
|
||||
Ok((i, (sign, integer, fraction, exp)))
|
||||
}
|
||||
|
||||
/// Recognizes floating point number in text format and returns a f32.
|
||||
///
|
||||
/// *Complete version*: Can parse until the end of input.
|
||||
/// ```rust
|
||||
@@ -1440,33 +1522,36 @@ where
|
||||
/// 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::Char))));
|
||||
/// assert_eq!(parser("abc"), Err(Err::Error(("abc", ErrorKind::Float))));
|
||||
/// ```
|
||||
#[cfg(not(feature = "lexical"))]
|
||||
pub fn float<T, E: ParseError<T>>(input: T) -> IResult<T, f32, E>
|
||||
pub fn float<'a, T: 'a, E: ParseError<T>>(input: T) -> IResult<T, f32, E>
|
||||
where
|
||||
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>>,
|
||||
T: Clone + Offset,
|
||||
T: InputIter + InputLength + crate::traits::ParseTo<f32>,
|
||||
T: InputIter + InputLength + crate::traits::ParseTo<i32>,
|
||||
<T as InputIter>::Item: AsChar,
|
||||
<T as InputIter>::IterElem: Clone,
|
||||
T: InputTakeAtPosition,
|
||||
<T as InputTakeAtPosition>::Item: AsChar,
|
||||
T: AsBytes,
|
||||
{
|
||||
match recognize_float(input) {
|
||||
Err(e) => Err(e),
|
||||
Ok((i, s)) => match s.parse_to() {
|
||||
Some(n) => Ok((i, n)),
|
||||
None => Err(Err::Error(E::from_error_kind(i, ErrorKind::Float))),
|
||||
},
|
||||
}
|
||||
let (i, (sign, integer, fraction, exponent)) = recognize_float_parts(input)?;
|
||||
|
||||
let mut float: f32 =
|
||||
minimal_lexical::parse_float(
|
||||
integer.as_bytes().iter(),
|
||||
fraction.as_bytes().iter(),
|
||||
exponent);
|
||||
if !sign {
|
||||
float = -float;
|
||||
}
|
||||
|
||||
Ok((i, float))
|
||||
}
|
||||
|
||||
/// Recognizes floating point number in a byte string and returns a f32.
|
||||
/// Recognizes floating point number in text format and returns a f32.
|
||||
///
|
||||
/// *Complete version*: Can parse until the end of input.
|
||||
///
|
||||
/// This function uses the `lexical-core` crate for float parsing by default, you
|
||||
/// can deactivate it by removing the "lexical" feature.
|
||||
/// ```rust
|
||||
/// # use nom::{Err, error::ErrorKind, Needed};
|
||||
/// # use nom::Needed::Size;
|
||||
@@ -1476,87 +1561,34 @@ where
|
||||
/// float(s)
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(parser("1.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))));
|
||||
/// ```
|
||||
#[cfg(feature = "lexical")]
|
||||
pub fn float<T, E: ParseError<T>>(input: T) -> IResult<T, f32, E>
|
||||
where
|
||||
T: crate::traits::AsBytes + InputLength + Slice<RangeFrom<usize>>,
|
||||
{
|
||||
match ::lexical_core::parse_partial(input.as_bytes()) {
|
||||
Ok((value, processed)) => Ok((input.slice(processed..), value)),
|
||||
Err(_) => Err(Err::Error(E::from_error_kind(input, ErrorKind::Float))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes floating point number in a byte string 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::Char))));
|
||||
/// assert_eq!(parser("abc"), Err(Err::Error(("abc", ErrorKind::Float))));
|
||||
/// ```
|
||||
#[cfg(not(feature = "lexical"))]
|
||||
pub fn double<T, E: ParseError<T>>(input: T) -> IResult<T, f64, E>
|
||||
pub fn double<'a, T: 'a, E: ParseError<T>>(input: T) -> IResult<T, f64, E>
|
||||
where
|
||||
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>>,
|
||||
T: Clone + Offset,
|
||||
T: InputIter + InputLength + crate::traits::ParseTo<f64>,
|
||||
T: InputIter + InputLength + crate::traits::ParseTo<i32>,
|
||||
<T as InputIter>::Item: AsChar,
|
||||
<T as InputIter>::IterElem: Clone,
|
||||
T: InputTakeAtPosition,
|
||||
<T as InputTakeAtPosition>::Item: AsChar,
|
||||
T: AsBytes,
|
||||
{
|
||||
match recognize_float(input) {
|
||||
Err(e) => Err(e),
|
||||
Ok((i, s)) => match s.parse_to() {
|
||||
Some(n) => Ok((i, n)),
|
||||
None => Err(Err::Error(E::from_error_kind(i, ErrorKind::Float))),
|
||||
},
|
||||
}
|
||||
}
|
||||
let (i, (sign, integer, fraction, exponent)) = recognize_float_parts(input)?;
|
||||
|
||||
/// Recognizes floating point number in a byte string and returns a f64.
|
||||
///
|
||||
/// *Complete version*: Can parse until the end of input.
|
||||
///
|
||||
/// This function uses the `lexical-core` crate for float parsing by default, you
|
||||
/// can deactivate it by removing the "lexical" feature.
|
||||
/// ```rust
|
||||
/// # use nom::{Err, error::ErrorKind, Needed};
|
||||
/// # use nom::Needed::Size;
|
||||
/// use nom::number::complete::double;
|
||||
///
|
||||
/// let parser = |s| {
|
||||
/// double(s)
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(parser("1.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))));
|
||||
/// ```
|
||||
#[cfg(feature = "lexical")]
|
||||
pub fn double<T, E: ParseError<T>>(input: T) -> IResult<T, f64, E>
|
||||
where
|
||||
T: crate::traits::AsBytes + InputLength + Slice<RangeFrom<usize>>,
|
||||
{
|
||||
match ::lexical_core::parse_partial(input.as_bytes()) {
|
||||
Ok((value, processed)) => Ok((input.slice(processed..), value)),
|
||||
Err(_) => Err(Err::Error(E::from_error_kind(input, ErrorKind::Float))),
|
||||
}
|
||||
let mut float: f64 =
|
||||
minimal_lexical::parse_float(
|
||||
integer.as_bytes().iter(),
|
||||
fraction.as_bytes().iter(),
|
||||
exponent);
|
||||
if !sign {
|
||||
float = -float;
|
||||
}
|
||||
|
||||
Ok((i, float))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -1564,6 +1596,8 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::error::ErrorKind;
|
||||
use crate::internal::Err;
|
||||
use crate::traits::ParseTo;
|
||||
use proptest::prelude::*;
|
||||
|
||||
macro_rules! assert_parse(
|
||||
($left: expr, $right: expr) => {
|
||||
@@ -2003,4 +2037,29 @@ mod tests {
|
||||
Ok((&b""[..], 36_028_874_334_732_032_i64))
|
||||
);
|
||||
}
|
||||
|
||||
fn parse_f64(i:&str) -> IResult<&str, f64, ()> {
|
||||
match recognize_float(i) {
|
||||
Err(e) => Err(e),
|
||||
Ok((i, s)) => {
|
||||
if s.is_empty() {
|
||||
return Err(Err::Error(()));
|
||||
}
|
||||
match s.parse_to() {
|
||||
Some(n) => Ok((i, n)),
|
||||
None => Err(Err::Error(())),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn floats(s in "\\PC*") {
|
||||
println!("testing {}", s);
|
||||
let res1 = parse_f64(&s);
|
||||
let res2 = double::<_, ()>(s.as_str());
|
||||
assert_eq!(res1, res2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+162
-105
@@ -1,14 +1,14 @@
|
||||
//! Parsers recognizing numbers, streaming version
|
||||
|
||||
use crate::branch::alt;
|
||||
use crate::character::streaming::{char, digit1};
|
||||
use crate::character::streaming::{char, digit0, digit1};
|
||||
use crate::bytes::streaming::take_while;
|
||||
use crate::combinator::{cut, map, opt, recognize};
|
||||
use crate::error::{ErrorKind, ParseError};
|
||||
use crate::internal::*;
|
||||
use crate::lib::std::ops::{RangeFrom, RangeTo};
|
||||
use crate::sequence::{pair, tuple};
|
||||
use crate::traits::{AsChar, InputIter, InputLength, InputTakeAtPosition};
|
||||
use crate::traits::{Offset, Slice};
|
||||
use crate::traits::{AsChar, InputIter, InputLength, InputTakeAtPosition, Offset, Slice, AsBytes};
|
||||
|
||||
#[doc(hidden)]
|
||||
macro_rules! map(
|
||||
@@ -1367,7 +1367,6 @@ pub fn hex_u32<'a, E: ParseError<&'a [u8]>>(input: &'a [u8]) -> IResult<&'a [u8]
|
||||
/// assert_eq!(parser("123K-01"), Ok(("K-01", "123")));
|
||||
/// assert_eq!(parser("abc"), Err(Err::Error(("abc", ErrorKind::Char))));
|
||||
/// ```
|
||||
#[allow(unused_imports)]
|
||||
#[rustfmt::skip]
|
||||
pub fn recognize_float<T, E:ParseError<T>>(input: T) -> IResult<T, T, E>
|
||||
where
|
||||
@@ -1394,146 +1393,177 @@ where
|
||||
)(input)
|
||||
}
|
||||
|
||||
/// Recognizes floating point number in a byte string and returns a `f32`.
|
||||
/// 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.
|
||||
///
|
||||
pub fn recognize_float_parts<T, E:ParseError<T>>(input: T) -> IResult<T, (bool, T, T, i32), E>
|
||||
where
|
||||
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>>,
|
||||
T: Clone + Offset,
|
||||
T: InputIter + crate::traits::ParseTo<i32>,
|
||||
<T as InputIter>::Item: AsChar,
|
||||
T: InputTakeAtPosition + InputLength,
|
||||
<T as InputTakeAtPosition>::Item: AsChar
|
||||
{
|
||||
let (i, opt_sign) = opt(alt((char('+'), char('-'))))(input.clone())?;
|
||||
let sign = match opt_sign {
|
||||
Some('+') => true,
|
||||
Some('-') => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
let (i, mut integer) = digit0(i)?;
|
||||
|
||||
if integer.input_len() >= 2 {
|
||||
// left trim zeroes for faster parsing in minimal-lexical
|
||||
let (_, int2) = take_while(|c: <T as InputTakeAtPosition>::Item| c.as_char() == '0')(integer.clone())?;
|
||||
// if it's all zeroes, keep one of them
|
||||
integer = if int2.input_len() == integer.input_len() {
|
||||
integer.slice(integer.input_len()-1..)
|
||||
} else {
|
||||
integer.slice(int2.input_len()..)
|
||||
};
|
||||
}
|
||||
|
||||
let (i, opt_dot) = opt(char('.'))(i)?;
|
||||
let (i, fraction) = if opt_dot.is_none() {
|
||||
let i2 = i.clone();
|
||||
(i2, i.slice(..0))
|
||||
} else {
|
||||
// match number, trim right zeroes
|
||||
let mut zero_count = 0usize;
|
||||
let mut position = None;
|
||||
for (pos, c) in i.iter_indices() {
|
||||
let c = c.as_char();
|
||||
|
||||
if c.is_ascii_digit() {
|
||||
if c == '0' {
|
||||
zero_count += 1;
|
||||
} else {
|
||||
zero_count = 0;
|
||||
}
|
||||
} else {
|
||||
position = Some(pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let position = if let Some(pos) = position {
|
||||
pos
|
||||
} else {
|
||||
return Err(Err::Incomplete(Needed::new(1)));
|
||||
};
|
||||
|
||||
let index = if zero_count == 0 {
|
||||
position
|
||||
} else if zero_count == position {
|
||||
position - zero_count + 1
|
||||
} else {
|
||||
position - zero_count
|
||||
};
|
||||
|
||||
(i.slice(position..), i.slice(..index))
|
||||
};
|
||||
|
||||
if integer.input_len() == 0 && fraction.input_len() == 0 {
|
||||
return Err(Err::Error(E::from_error_kind(input, ErrorKind::Float)))
|
||||
}
|
||||
|
||||
let (i, e) = opt(alt((char('e'), char('E'))))(i)?;
|
||||
let (i, exp) = if e.is_some() {
|
||||
cut(crate::character::streaming::i32)(i)?
|
||||
} else {
|
||||
(i, 0)
|
||||
};
|
||||
|
||||
Ok((i, (sign, integer, fraction, exp)))
|
||||
}
|
||||
|
||||
/// Recognizes floating point number in text format and returns a f32.
|
||||
///
|
||||
/// *Streaming version*: Will return `Err(nom::Err::Incomplete(_))` if there is not enough data.
|
||||
///
|
||||
/// *Streaming version*: Will return `Err(nom::Err::Incomplete(_))` if it reaches the end of input.
|
||||
/// ```rust
|
||||
/// # use nom::{Err, error::ErrorKind, Needed};
|
||||
/// use nom::number::streaming::float;
|
||||
/// # use nom::Needed::Size;
|
||||
/// use nom::number::complete::float;
|
||||
///
|
||||
/// let parser = |s| {
|
||||
/// float(s)
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(parser("11e-1;"), Ok((";", 1.1)));
|
||||
/// assert_eq!(parser("123E-02;"), Ok((";", 1.23)));
|
||||
/// 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::Char))));
|
||||
/// assert_eq!(parser("abc"), Err(Err::Error(("abc", ErrorKind::Float))));
|
||||
/// ```
|
||||
#[cfg(not(feature = "lexical"))]
|
||||
pub fn float<T, E: ParseError<T>>(input: T) -> IResult<T, f32, E>
|
||||
pub fn float<'a, T: 'a, E: ParseError<T>>(input: T) -> IResult<T, f32, E>
|
||||
where
|
||||
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>>,
|
||||
T: Clone + Offset,
|
||||
T: InputIter + InputLength + crate::traits::ParseTo<f32>,
|
||||
T: InputIter + InputLength + crate::traits::ParseTo<i32>,
|
||||
<T as InputIter>::Item: AsChar,
|
||||
<T as InputIter>::IterElem: Clone,
|
||||
T: InputTakeAtPosition,
|
||||
<T as InputTakeAtPosition>::Item: AsChar,
|
||||
T: AsBytes,
|
||||
{
|
||||
match recognize_float(input) {
|
||||
Err(e) => Err(e),
|
||||
Ok((i, s)) => match s.parse_to() {
|
||||
Some(n) => Ok((i, n)),
|
||||
None => Err(Err::Error(E::from_error_kind(i, ErrorKind::Float))),
|
||||
},
|
||||
}
|
||||
let (i, (sign, integer, fraction, exponent)) = recognize_float_parts(input)?;
|
||||
|
||||
let mut float: f32 =
|
||||
minimal_lexical::parse_float(
|
||||
integer.as_bytes().iter(),
|
||||
fraction.as_bytes().iter(),
|
||||
exponent);
|
||||
if !sign {
|
||||
float = -float;
|
||||
}
|
||||
|
||||
Ok((i, float))
|
||||
}
|
||||
|
||||
/// Recognizes floating point number in a byte string and returns a `f32`.
|
||||
/// Recognizes floating point number in text format and returns a f32.
|
||||
///
|
||||
/// *Streaming version*: Will return `Err(nom::Err::Incomplete(_))` if there is not enough data.
|
||||
///
|
||||
/// *Streaming version*: Will return `Err(nom::Err::Incomplete(_))` if it reaches the end of input.
|
||||
/// ```rust
|
||||
/// # use nom::{Err, error::ErrorKind, Needed};
|
||||
/// use nom::number::streaming::float;
|
||||
/// # use nom::Needed::Size;
|
||||
/// use nom::number::complete::float;
|
||||
///
|
||||
/// let parser = |s| {
|
||||
/// float(s)
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(parser("11e-1;"), Ok((";", 1.1)));
|
||||
/// assert_eq!(parser("123E-02;"), Ok((";", 1.23)));
|
||||
/// 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))));
|
||||
/// ```
|
||||
///
|
||||
/// this function uses the lexical-core crate for float parsing by default, you
|
||||
/// can deactivate it by removing the "lexical" feature
|
||||
#[cfg(feature = "lexical")]
|
||||
pub fn float<T, E: ParseError<T>>(input: T) -> IResult<T, f32, E>
|
||||
where
|
||||
T: crate::traits::AsBytes + InputLength + Slice<RangeFrom<usize>>,
|
||||
{
|
||||
match ::lexical_core::parse_partial(input.as_bytes()) {
|
||||
Ok((value, processed)) => {
|
||||
if processed == input.input_len() {
|
||||
Err(Err::Incomplete(Needed::Unknown))
|
||||
} else {
|
||||
Ok((input.slice(processed..), value))
|
||||
}
|
||||
}
|
||||
Err(_) => Err(Err::Error(E::from_error_kind(input, ErrorKind::Float))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes floating point number in a byte string and returns a `f64`.
|
||||
///
|
||||
/// *Streaming version*: Will return `Err(nom::Err::Incomplete(_))` if it reaches the end of input.
|
||||
/// ```rust
|
||||
/// # use nom::{Err, error::ErrorKind, Needed};
|
||||
/// use nom::number::streaming::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::Char))));
|
||||
/// ```
|
||||
#[cfg(not(feature = "lexical"))]
|
||||
pub fn double<T, E: ParseError<T>>(input: T) -> IResult<T, f64, E>
|
||||
pub fn double<'a, T: 'a, E: ParseError<T>>(input: T) -> IResult<T, f64, E>
|
||||
where
|
||||
T: Slice<RangeFrom<usize>> + Slice<RangeTo<usize>>,
|
||||
T: Clone + Offset,
|
||||
T: InputIter + InputLength + crate::traits::ParseTo<f64>,
|
||||
T: InputIter + InputLength + crate::traits::ParseTo<i32>,
|
||||
<T as InputIter>::Item: AsChar,
|
||||
<T as InputIter>::IterElem: Clone,
|
||||
T: InputTakeAtPosition,
|
||||
<T as InputTakeAtPosition>::Item: AsChar,
|
||||
T: AsBytes,
|
||||
{
|
||||
match recognize_float(input) {
|
||||
Err(e) => Err(e),
|
||||
Ok((i, s)) => match s.parse_to() {
|
||||
Some(n) => Ok((i, n)),
|
||||
None => Err(Err::Error(E::from_error_kind(i, ErrorKind::Float))),
|
||||
},
|
||||
}
|
||||
}
|
||||
let (i, (sign, integer, fraction, exponent)) = recognize_float_parts(input)?;
|
||||
|
||||
/// Recognizes floating point number in a byte string and returns a `f64`.
|
||||
///
|
||||
/// *Streaming version*: Will return `Err(nom::Err::Incomplete(_))` if it reaches the end of input.
|
||||
/// ```rust
|
||||
/// # use nom::{Err, error::ErrorKind, Needed};
|
||||
/// use nom::number::streaming::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))));
|
||||
/// ```
|
||||
///
|
||||
/// this function uses the lexical-core crate for float parsing by default, you
|
||||
/// can deactivate it by removing the "lexical" feature
|
||||
#[cfg(feature = "lexical")]
|
||||
pub fn double<T, E: ParseError<T>>(input: T) -> IResult<T, f64, E>
|
||||
where
|
||||
T: crate::traits::AsBytes + InputLength + Slice<RangeFrom<usize>>,
|
||||
{
|
||||
match ::lexical_core::parse_partial(input.as_bytes()) {
|
||||
Ok((value, processed)) => {
|
||||
if processed == input.input_len() {
|
||||
Err(Err::Incomplete(Needed::Unknown))
|
||||
} else {
|
||||
Ok((input.slice(processed..), value))
|
||||
}
|
||||
let mut float: f64 =
|
||||
minimal_lexical::parse_float(
|
||||
integer.as_bytes().iter(),
|
||||
fraction.as_bytes().iter(),
|
||||
exponent);
|
||||
if !sign {
|
||||
float = -float;
|
||||
}
|
||||
Err(_) => Err(Err::Error(E::from_error_kind(input, ErrorKind::Float))),
|
||||
}
|
||||
|
||||
Ok((i, float))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -1541,6 +1571,8 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::error::ErrorKind;
|
||||
use crate::internal::{Err, Needed};
|
||||
use crate::traits::ParseTo;
|
||||
use proptest::prelude::*;
|
||||
|
||||
macro_rules! assert_parse(
|
||||
($left: expr, $right: expr) => {
|
||||
@@ -2086,4 +2118,29 @@ mod tests {
|
||||
Ok((&b""[..], 36_028_874_334_732_032_i64))
|
||||
);
|
||||
}
|
||||
|
||||
fn parse_f64(i:&str) -> IResult<&str, f64, ()> {
|
||||
match recognize_float(i) {
|
||||
Err(e) => Err(e),
|
||||
Ok((i, s)) => {
|
||||
if s.is_empty() {
|
||||
return Err(Err::Error(()));
|
||||
}
|
||||
match s.parse_to() {
|
||||
Some(n) => Ok((i, n)),
|
||||
None => Err(Err::Error(())),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn floats(s in "\\PC*") {
|
||||
println!("testing {}", s);
|
||||
let res1 = parse_f64(&s);
|
||||
let res2 = double::<_, ()>(s.as_str());
|
||||
assert_eq!(res1, res2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user