@@ -458,7 +458,7 @@ pub(crate) fn parse<'a>(
458458 let mut chars = rest. chars ( ) . enumerate ( ) . fuse ( ) . peekable ( ) ;
459459 let mut digits: Option < BigUint > = None ;
460460 let mut scale = 0u64 ;
461- let mut exponent = BigInt :: zero ( ) ;
461+ let mut exponent: Option < BigInt > = None ;
462462 while let Some ( d) = chars. peek ( ) . and_then ( |& ( _, c) | base. digit ( c) ) {
463463 chars. next ( ) ;
464464 digits = Some ( digits. unwrap_or_default ( ) * base as u8 + d) ;
@@ -487,6 +487,8 @@ pub(crate) fn parse<'a>(
487487 . peek ( )
488488 . is_some_and ( |& ( _, c) | c. to_ascii_lowercase ( ) == exp_char)
489489 {
490+ // Save the iterator position in case we do not parse any exponent.
491+ let save_chars = chars. clone ( ) ;
490492 chars. next ( ) ;
491493 let exp_negative = match chars. peek ( ) {
492494 Some ( ( _, '-' ) ) => {
@@ -501,10 +503,15 @@ pub(crate) fn parse<'a>(
501503 } ;
502504 while let Some ( d) = chars. peek ( ) . and_then ( |& ( _, c) | Base :: Decimal . digit ( c) ) {
503505 chars. next ( ) ;
504- exponent = exponent * 10 + d as i64 ;
506+ exponent = Some ( exponent. unwrap_or_default ( ) * 10 + d as i64 ) ;
505507 }
506- if exp_negative {
507- exponent = -exponent;
508+ if let Some ( exp) = & exponent {
509+ if exp_negative {
510+ exponent = Some ( -exp) ;
511+ }
512+ } else {
513+ // No exponent actually parsed, reset iterator to return partial match.
514+ chars = save_chars;
508515 }
509516 }
510517 }
@@ -532,7 +539,7 @@ pub(crate) fn parse<'a>(
532539 }
533540
534541 let ebd_result =
535- construct_extended_big_decimal ( digits, negative, base, scale, exponent) ;
542+ construct_extended_big_decimal ( digits, negative, base, scale, exponent. unwrap_or_default ( ) ) ;
536543
537544 // Return what has been parsed so far. If there are extra characters, mark the
538545 // parsing as a partial match.
@@ -671,6 +678,16 @@ mod tests {
671678 Ok ( 0.15 ) ,
672679 f64 :: extended_parse( ".150000000000000000000000000231313" )
673680 ) ;
681+ assert ! ( matches!( f64 :: extended_parse( "123.15e" ) ,
682+ Err ( ExtendedParserError :: PartialMatch ( f, "e" ) ) if f == 123.15 ) ) ;
683+ assert ! ( matches!( f64 :: extended_parse( "123.15E" ) ,
684+ Err ( ExtendedParserError :: PartialMatch ( f, "E" ) ) if f == 123.15 ) ) ;
685+ assert ! ( matches!( f64 :: extended_parse( "123.15e-" ) ,
686+ Err ( ExtendedParserError :: PartialMatch ( f, "e-" ) ) if f == 123.15 ) ) ;
687+ assert ! ( matches!( f64 :: extended_parse( "123.15e+" ) ,
688+ Err ( ExtendedParserError :: PartialMatch ( f, "e+" ) ) if f == 123.15 ) ) ;
689+ assert ! ( matches!( f64 :: extended_parse( "123.15e." ) ,
690+ Err ( ExtendedParserError :: PartialMatch ( f, "e." ) ) if f == 123.15 ) ) ;
674691 assert ! ( matches!( f64 :: extended_parse( "1.2.3" ) ,
675692 Err ( ExtendedParserError :: PartialMatch ( f, ".3" ) ) if f == 1.2 ) ) ;
676693 assert ! ( matches!( f64 :: extended_parse( "123.15p5" ) ,
@@ -833,6 +850,38 @@ mod tests {
833850 Err ( ExtendedParserError :: NotNumeric ) ,
834851 ExtendedBigDecimal :: extended_parse( "." )
835852 ) ;
853+ assert_eq ! (
854+ Err ( ExtendedParserError :: NotNumeric ) ,
855+ ExtendedBigDecimal :: extended_parse( "e" )
856+ ) ;
857+ assert_eq ! (
858+ Err ( ExtendedParserError :: NotNumeric ) ,
859+ ExtendedBigDecimal :: extended_parse( ".e" )
860+ ) ;
861+ assert_eq ! (
862+ Err ( ExtendedParserError :: NotNumeric ) ,
863+ ExtendedBigDecimal :: extended_parse( "-e" )
864+ ) ;
865+ assert_eq ! (
866+ Err ( ExtendedParserError :: NotNumeric ) ,
867+ ExtendedBigDecimal :: extended_parse( "+.e" )
868+ ) ;
869+ assert_eq ! (
870+ Err ( ExtendedParserError :: NotNumeric ) ,
871+ ExtendedBigDecimal :: extended_parse( "e10" )
872+ ) ;
873+ assert_eq ! (
874+ Err ( ExtendedParserError :: NotNumeric ) ,
875+ ExtendedBigDecimal :: extended_parse( "e-10" )
876+ ) ;
877+ assert_eq ! (
878+ Err ( ExtendedParserError :: NotNumeric ) ,
879+ ExtendedBigDecimal :: extended_parse( "-e10" )
880+ ) ;
881+ assert_eq ! (
882+ Err ( ExtendedParserError :: NotNumeric ) ,
883+ ExtendedBigDecimal :: extended_parse( "+e10" )
884+ ) ;
836885 }
837886
838887 #[ test]
@@ -853,6 +902,15 @@ mod tests {
853902 // but we can check that the number still gets parsed properly: 0x0.8e5 is 0x8e5 / 16**3
854903 assert_eq ! ( Ok ( 0.555908203125 ) , f64 :: extended_parse( "0x0.8e5" ) ) ;
855904
905+ assert ! ( matches!( f64 :: extended_parse( "0x0.1p" ) ,
906+ Err ( ExtendedParserError :: PartialMatch ( f, "p" ) ) if f == 0.0625 ) ) ;
907+ assert ! ( matches!( f64 :: extended_parse( "0x0.1p-" ) ,
908+ Err ( ExtendedParserError :: PartialMatch ( f, "p-" ) ) if f == 0.0625 ) ) ;
909+ assert ! ( matches!( f64 :: extended_parse( "0x.1p+" ) ,
910+ Err ( ExtendedParserError :: PartialMatch ( f, "p+" ) ) if f == 0.0625 ) ) ;
911+ assert ! ( matches!( f64 :: extended_parse( "0x.1p." ) ,
912+ Err ( ExtendedParserError :: PartialMatch ( f, "p." ) ) if f == 0.0625 ) ) ;
913+
856914 assert_eq ! (
857915 Ok ( ExtendedBigDecimal :: BigDecimal (
858916 BigDecimal :: from_str( "0.0625" ) . unwrap( )
@@ -910,7 +968,7 @@ mod tests {
910968 ) )
911969 ) ) ;
912970
913- // TODO: GNU coreutils treats these 2 as partial match .
971+ // TODO: GNU coreutils treats these as partial matches .
914972 assert_eq ! (
915973 Err ( ExtendedParserError :: NotNumeric ) ,
916974 ExtendedBigDecimal :: extended_parse( "0x" )
@@ -919,6 +977,14 @@ mod tests {
919977 Err ( ExtendedParserError :: NotNumeric ) ,
920978 ExtendedBigDecimal :: extended_parse( "0x." )
921979 ) ;
980+ assert_eq ! (
981+ Err ( ExtendedParserError :: NotNumeric ) ,
982+ ExtendedBigDecimal :: extended_parse( "0xp" )
983+ ) ;
984+ assert_eq ! (
985+ Err ( ExtendedParserError :: NotNumeric ) ,
986+ ExtendedBigDecimal :: extended_parse( "0xp-2" )
987+ ) ;
922988 }
923989
924990 #[ test]
0 commit comments