diff --git a/lexical-parse-float/tests/issue_98_tests.rs b/lexical-parse-float/tests/issue_98_tests.rs new file mode 100644 index 00000000..ee137b83 --- /dev/null +++ b/lexical-parse-float/tests/issue_98_tests.rs @@ -0,0 +1,67 @@ +#![cfg(all(feature = "power-of-two", feature = "format"))] + +use std::assert_eq; + +use lexical_parse_float::FromLexicalWithOptions; +use lexical_parse_float::NumberFormatBuilder; +use lexical_parse_float::Options; +use lexical_util::error::Error; + +#[test] +fn issue_98_test() { + const DECIMAL_FORMAT: u128 = NumberFormatBuilder::new() + .required_digits(true) + .no_positive_mantissa_sign(false) + .no_special(true) + .no_integer_leading_zeros(true) + .no_float_leading_zeros(true) + .build(); + let result = f64::from_lexical_with_options::(b"1.1.0", &Options::new()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::InvalidDigit(3)); + assert_eq!( + f64::from_lexical_partial_with_options::(b"1.1.0", &Options::new()), + Ok((1.1f64, 3)) + ); + assert_eq!( + f64::from_lexical_with_options::(b"1.1", &Options::new()), + Ok(1.1f64) + ); + assert_eq!( + f64::from_lexical_partial_with_options::(b"1.1", &Options::new()), + Ok((1.1f64, 3)) + ); + + let result = f64::from_lexical_with_options::(b"0.1.0", &Options::new()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::InvalidDigit(3)); + assert_eq!( + f64::from_lexical_partial_with_options::(b"0.1.0", &Options::new()), + Ok((0.1f64, 3)) + ); + assert_eq!( + f64::from_lexical_with_options::(b"0.1", &Options::new()), + Ok(0.1f64) + ); + assert_eq!( + f64::from_lexical_partial_with_options::(b"0.1", &Options::new()), + Ok((0.1f64, 3)) + ); + + let result = f64::from_lexical_with_options::(b"01.1.0", &Options::new()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::InvalidLeadingZeros(0)); + + let result = f64::from_lexical_with_options::(b"00.1", &Options::new()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::InvalidLeadingZeros(0)); + + assert_eq!( + f64::from_lexical_partial_with_options::(b"10.1", &Options::new()), + Ok((10.1, 4)) + ); + assert_eq!( + f64::from_lexical_partial_with_options::(b"11.1", &Options::new()), + Ok((11.1, 4)) + ); +} diff --git a/lexical-parse-integer/src/algorithm.rs b/lexical-parse-integer/src/algorithm.rs index 014e62a6..255484fd 100644 --- a/lexical-parse-integer/src/algorithm.rs +++ b/lexical-parse-integer/src/algorithm.rs @@ -65,7 +65,7 @@ macro_rules! into_ok_partial { /// Return an error for a complete parser upon an invalid digit. macro_rules! invalid_digit_complete { - ($value:ident, $index:expr) => { + ($value:expr, $index:expr) => { // Don't do any overflow checking here: we don't need it. into_error!(InvalidDigit, $index - 1) }; @@ -74,7 +74,7 @@ macro_rules! invalid_digit_complete { /// Return a value for a partial parser upon an invalid digit. /// This checks for numeric overflow, and returns the appropriate error. macro_rules! invalid_digit_partial { - ($value:ident, $index:expr) => { + ($value:expr, $index:expr) => { // NOTE: The value is already positive/negative into_ok_partial!($value, $index - 1) }; @@ -548,6 +548,7 @@ macro_rules! parse_digits_checked { /// * `into_ok` - Behavior when returning a valid value. /// * `invalid_digit` - Behavior when an invalid digit is found. /// * `no_multi_digit` - If to disable multi-digit optimizations. +/// * `is_partial` - If the parser is a partial parser. #[rustfmt::skip] macro_rules! algorithm { ($bytes:ident, $into_ok:ident, $invalid_digit:ident, $no_multi_digit:expr) => {{ @@ -618,11 +619,14 @@ macro_rules! algorithm { if zeros > 1 { return into_error!(InvalidLeadingZeros, index); } + // NOTE: Zeros has to be 0 here, so our index == 1 or 2 (depending on sign) match iter.peek().map(|&c| char_to_digit_const(c, format.radix())) { // Valid digit, we have an invalid value. Some(Some(_)) => return into_error!(InvalidLeadingZeros, index), - // Either not a digit that follows, or nothing follows. - _ => return $into_ok!(::ZERO, index), + // Have a non-digit character that follows. + Some(None) => return $invalid_digit!(::ZERO, iter.cursor() + 1), + // No digits following, has to be ok + None => return $into_ok!(::ZERO, index), }; } } diff --git a/lexical-parse-integer/tests/issue_98_tests.rs b/lexical-parse-integer/tests/issue_98_tests.rs new file mode 100644 index 00000000..b41c1a93 --- /dev/null +++ b/lexical-parse-integer/tests/issue_98_tests.rs @@ -0,0 +1,67 @@ +#![cfg(all(feature = "power-of-two", feature = "format"))] + +use lexical_parse_integer::FromLexicalWithOptions; +use lexical_parse_integer::NumberFormatBuilder; +use lexical_parse_integer::Options; +use lexical_util::error::Error; + +#[test] +fn issue_98_test() { + const DECIMAL_FORMAT: u128 = NumberFormatBuilder::new() + .required_digits(true) + .no_positive_mantissa_sign(false) + .no_special(true) + .no_integer_leading_zeros(true) + .no_float_leading_zeros(false) + .build(); + let result = i64::from_lexical_with_options::(b"1.1.0", &Options::new()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::InvalidDigit(1)); + assert_eq!( + i64::from_lexical_partial_with_options::(b"1.1.0", &Options::new()), + Ok((1, 1)) + ); + + let result = i64::from_lexical_with_options::(b"1.1", &Options::new()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::InvalidDigit(1)); + assert!(i64::from_lexical_with_options::(b"1.1", &Options::new()).is_err()); + assert_eq!( + i64::from_lexical_partial_with_options::(b"1.1", &Options::new()), + Ok((1, 1)) + ); + + let result = i64::from_lexical_with_options::(b"0.1.0", &Options::new()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::InvalidDigit(1)); + assert_eq!( + i64::from_lexical_partial_with_options::(b"0.1.0", &Options::new()), + Ok((0, 1)) + ); + + let result = i64::from_lexical_with_options::(b"0.1", &Options::new()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::InvalidDigit(1)); + assert!(i64::from_lexical_with_options::(b"0.1", &Options::new()).is_err()); + assert_eq!( + i64::from_lexical_partial_with_options::(b"0.1", &Options::new()), + Ok((0, 1)) + ); + + let result = i64::from_lexical_with_options::(b"01.1", &Options::new()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::InvalidLeadingZeros(0)); + + let result = i64::from_lexical_with_options::(b"00.1", &Options::new()); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::InvalidLeadingZeros(0)); + + assert_eq!( + i64::from_lexical_partial_with_options::(b"10.1", &Options::new()), + Ok((10, 2)) + ); + assert_eq!( + i64::from_lexical_partial_with_options::(b"11.1", &Options::new()), + Ok((11, 2)) + ); +}