@@ -397,99 +397,89 @@ impl FromStr for NumberExpression {
397
397
type Err = NumberParsingError ;
398
398
399
399
fn from_str ( value : & str ) -> Result < Self , Self :: Err > {
400
- let number = if value. starts_with ( "0x" ) || value. starts_with ( "0X" ) {
401
- let is_x_uppercase = value
402
- . chars ( )
403
- . nth ( 1 )
404
- . map ( char:: is_uppercase)
405
- . unwrap_or ( false ) ;
406
-
407
- if let Some ( index) = value. find ( 'p' ) {
408
- let exponent = value
409
- . get ( index + 1 ..)
410
- . and_then ( |string| string. parse ( ) . ok ( ) )
411
- . ok_or ( Self :: Err :: InvalidHexadecimalExponent ) ?;
412
- let number = u64:: from_str_radix ( value. get ( 2 ..index) . unwrap ( ) , 16 )
413
- . map_err ( |_| Self :: Err :: InvalidHexadecimalNumber ) ?;
414
-
415
- HexNumber :: new ( number, is_x_uppercase) . with_exponent ( exponent, false )
416
- } else if let Some ( index) = value. find ( 'P' ) {
417
- let exponent = value
418
- . get ( index + 1 ..)
419
- . map ( filter_underscore)
420
- . and_then ( |string| string. parse ( ) . ok ( ) )
421
- . ok_or ( Self :: Err :: InvalidHexadecimalExponent ) ?;
422
- let filtered = filter_underscore ( value. get ( 2 ..index) . unwrap ( ) ) ;
423
- let number = u64:: from_str_radix ( & filtered, 16 )
424
- . map_err ( |_| Self :: Err :: InvalidHexadecimalNumber ) ?;
425
-
426
- HexNumber :: new ( number, is_x_uppercase) . with_exponent ( exponent, true )
427
- } else {
428
- let filtered = filter_underscore ( value. get ( 2 ..) . unwrap ( ) ) ;
429
- let number = u64:: from_str_radix ( & filtered, 16 )
430
- . map_err ( |_| Self :: Err :: InvalidHexadecimalNumber ) ?;
431
-
432
- HexNumber :: new ( number, is_x_uppercase)
433
- }
434
- . into ( )
435
- } else if value. starts_with ( "0b" ) || value. starts_with ( "0B" ) {
436
- let is_b_uppercase = value
437
- . chars ( )
438
- . nth ( 1 )
439
- . map ( char:: is_uppercase)
440
- . unwrap_or ( false ) ;
441
-
442
- let filtered = filter_underscore ( value) ;
443
- let number = u64:: from_str_radix ( filtered. get ( 2 ..) . unwrap ( ) , 2 )
444
- . map_err ( |_| Self :: Err :: InvalidBinaryNumber ) ?;
445
-
446
- BinaryNumber :: new ( number, is_b_uppercase) . into ( )
447
- } else {
448
- // in Luau, underscores are valid everywhere in a number except
449
- // after a `.`
450
- if value. contains ( "._" ) {
451
- return Err ( Self :: Err :: InvalidDecimalNumber ) ;
400
+ let notation_prefix = value
401
+ . char_indices ( )
402
+ . filter ( |( _, c) | * c != '_' )
403
+ . take ( 2 )
404
+ . nth ( 1 ) ;
405
+ let starts_with_zero = value. starts_with ( '0' ) ;
406
+
407
+ let number = match ( starts_with_zero, notation_prefix) {
408
+ ( true , Some ( ( position, notation) ) ) if matches ! ( notation, 'x' | 'X' | 'b' | 'B' ) => {
409
+ let is_uppercase = notation. is_uppercase ( ) ;
410
+
411
+ if notation == 'x' || notation == 'X' {
412
+ if let Some ( ( exponent_is_uppercase, index) ) = value
413
+ . find ( 'p' )
414
+ . map ( |index| ( false , index) )
415
+ . or_else ( || value. find ( 'P' ) . map ( |index| ( true , index) ) )
416
+ {
417
+ let exponent = value
418
+ . get ( index + 1 ..)
419
+ . and_then ( |string| string. parse ( ) . ok ( ) )
420
+ . ok_or ( Self :: Err :: InvalidHexadecimalExponent ) ?;
421
+ let before_exponent = value. get ( position + 1 ..index) . unwrap ( ) ;
422
+ let number = u64:: from_str_radix ( before_exponent, 16 )
423
+ . map_err ( |_| Self :: Err :: InvalidHexadecimalNumber ) ?;
424
+
425
+ HexNumber :: new ( number, is_uppercase)
426
+ . with_exponent ( exponent, exponent_is_uppercase)
427
+ } else {
428
+ let filtered = filter_underscore ( value. get ( position + 1 ..) . unwrap ( ) ) ;
429
+ let number = u64:: from_str_radix ( & filtered, 16 )
430
+ . map_err ( |_| Self :: Err :: InvalidHexadecimalNumber ) ?;
431
+
432
+ HexNumber :: new ( number, is_uppercase)
433
+ }
434
+ . into ( )
435
+ } else if notation == 'b' || notation == 'B' {
436
+ let filtered = filter_underscore ( value. get ( position + 1 ..) . unwrap ( ) ) ;
437
+ let number = u64:: from_str_radix ( & filtered, 2 )
438
+ . map_err ( |_| Self :: Err :: InvalidBinaryNumber ) ?;
439
+
440
+ BinaryNumber :: new ( number, is_uppercase) . into ( )
441
+ } else {
442
+ unreachable ! ( )
443
+ }
452
444
}
453
-
454
- if let Some ( index ) = value . find ( 'e' ) {
455
- // in Luau, underscores are not valid before the exponent sign
456
- if value. contains ( "_-" ) || value . contains ( "_+ ") {
457
- return Err ( Self :: Err :: InvalidDecimalExponent ) ;
445
+ _ => {
446
+ // in Luau, underscores are valid everywhere in a number except
447
+ // after a `.`
448
+ if value. contains ( "._ " ) {
449
+ return Err ( Self :: Err :: InvalidDecimalNumber ) ;
458
450
}
459
451
460
- let exponent = value
461
- . get ( index + 1 ..)
462
- . map ( filter_underscore)
463
- . and_then ( |string| string. parse ( ) . ok ( ) )
464
- . ok_or ( Self :: Err :: InvalidDecimalExponent ) ?;
465
- let number = value
466
- . get ( 0 ..index)
467
- . map ( filter_underscore)
468
- . and_then ( |string| string. parse ( ) . ok ( ) )
469
- . ok_or ( Self :: Err :: InvalidDecimalNumber ) ?;
470
-
471
- DecimalNumber :: new ( number) . with_exponent ( exponent, false )
472
- } else if let Some ( index) = value. find ( 'E' ) {
473
- let exponent: i64 = value
474
- . get ( index + 1 ..)
475
- . map ( filter_underscore)
476
- . and_then ( |string| string. parse ( ) . ok ( ) )
477
- . ok_or ( Self :: Err :: InvalidDecimalExponent ) ?;
478
- let number = value
479
- . get ( 0 ..index)
480
- . map ( filter_underscore)
481
- . and_then ( |string| string. parse ( ) . ok ( ) )
482
- . ok_or ( Self :: Err :: InvalidDecimalNumber ) ?;
483
-
484
- DecimalNumber :: new ( number) . with_exponent ( exponent, true )
485
- } else {
486
- let number = filter_underscore ( value)
487
- . parse :: < f64 > ( )
488
- . map_err ( |_| Self :: Err :: InvalidDecimalNumber ) ?;
489
-
490
- DecimalNumber :: new ( number)
452
+ if let Some ( ( exponent_is_uppercase, index) ) = value
453
+ . find ( 'e' )
454
+ . map ( |index| ( false , index) )
455
+ . or_else ( || value. find ( 'E' ) . map ( |index| ( true , index) ) )
456
+ {
457
+ // in Luau, underscores are not valid before the exponent sign
458
+ if value. contains ( "_-" ) || value. contains ( "_+" ) {
459
+ return Err ( Self :: Err :: InvalidDecimalExponent ) ;
460
+ }
461
+
462
+ let exponent = value
463
+ . get ( index + 1 ..)
464
+ . map ( filter_underscore)
465
+ . and_then ( |string| string. parse ( ) . ok ( ) )
466
+ . ok_or ( Self :: Err :: InvalidDecimalExponent ) ?;
467
+ let number = value
468
+ . get ( 0 ..index)
469
+ . map ( filter_underscore)
470
+ . and_then ( |string| string. parse ( ) . ok ( ) )
471
+ . ok_or ( Self :: Err :: InvalidDecimalNumber ) ?;
472
+
473
+ DecimalNumber :: new ( number) . with_exponent ( exponent, exponent_is_uppercase)
474
+ } else {
475
+ let number = filter_underscore ( value)
476
+ . parse :: < f64 > ( )
477
+ . map_err ( |_| Self :: Err :: InvalidDecimalNumber ) ?;
478
+
479
+ DecimalNumber :: new ( number)
480
+ }
481
+ . into ( )
491
482
}
492
- . into ( )
493
483
} ;
494
484
495
485
Ok ( number)
@@ -606,28 +596,42 @@ mod test {
606
596
parse_float_with_exponent_and_underscores( "10_0.12_e_8" ) => DecimalNumber :: new( 100.12_f64 ) . with_exponent( 8 , false ) ,
607
597
parse_trailing_dot_with_exponent( "10.e8" ) => DecimalNumber :: new( 10_f64 ) . with_exponent( 8 , false ) ,
608
598
parse_hex_number( "0x12" ) => HexNumber :: new( 18 , false ) ,
599
+ parse_hex_number_with_underscore_before_x( "0_x12" ) => HexNumber :: new( 18 , false ) ,
600
+ parse_hex_number_with_underscores_around_x( "0_x_12" ) => HexNumber :: new( 18 , false ) ,
609
601
parse_hex_number_with_underscore( "0x12_13" ) => HexNumber :: new( 0x1213 , false ) ,
610
602
parse_uppercase_hex_number( "0X12" ) => HexNumber :: new( 18 , true ) ,
603
+ parse_uppercase_hex_number_with_underscore_before_x( "0_X13" ) => HexNumber :: new( 19 , true ) ,
611
604
parse_hex_number_with_lowercase( "0x12a" ) => HexNumber :: new( 298 , false ) ,
612
605
parse_hex_number_with_uppercase( "0x12A" ) => HexNumber :: new( 298 , false ) ,
613
606
parse_hex_number_with_mixed_case( "0x1bF2A" ) => HexNumber :: new( 114_474 , false ) ,
614
607
parse_hex_with_exponent( "0x12p4" ) => HexNumber :: new( 18 , false ) . with_exponent( 4 , false ) ,
615
608
parse_hex_with_exponent_uppercase( "0xABP3" ) => HexNumber :: new( 171 , false ) . with_exponent( 3 , true ) ,
616
609
parse_binary_zero( "0b0" ) => BinaryNumber :: new( 0 , false ) ,
610
+ parse_binary_zero_with_underscore_before_b( "0_b1" ) => BinaryNumber :: new( 1 , false ) ,
617
611
parse_binary_zero_with_underscore( "0b1010_1100" ) => BinaryNumber :: new( 0b1010_1100 , false ) ,
618
612
parse_binary_zero_uppercase( "0B0" ) => BinaryNumber :: new( 0 , true ) ,
613
+ parse_binary_zero_uppercase_with_underscore_before_b( "0_B1" ) => BinaryNumber :: new( 1 , true ) ,
619
614
) ;
620
615
621
616
test_parse_errors ! (
622
617
parse_empty_string( "" ) => NumberParsingError :: InvalidDecimalNumber ,
623
618
missing_exponent_value( "1e" ) => NumberParsingError :: InvalidDecimalExponent ,
619
+ missing_exponent_value_uppercase( "1E" ) => NumberParsingError :: InvalidDecimalExponent ,
624
620
invalid_underscore_position( "1._1" ) => NumberParsingError :: InvalidDecimalNumber ,
625
621
missing_negative_exponent_value( "1e-" ) => NumberParsingError :: InvalidDecimalExponent ,
622
+ missing_negative_exponent_value_uppercase( "1E-" ) => NumberParsingError :: InvalidDecimalExponent ,
626
623
invalid_underscore_before_negative_exponent( "1e_-1" ) => NumberParsingError :: InvalidDecimalExponent ,
627
624
invalid_underscore_before_positive_exponent( "1e_+1" ) => NumberParsingError :: InvalidDecimalExponent ,
625
+ invalid_underscore_before_negative_exponent_uppercase( "1E_-1" ) => NumberParsingError :: InvalidDecimalExponent ,
626
+ invalid_underscore_before_positive_exponent_uppercase( "1E_+1" ) => NumberParsingError :: InvalidDecimalExponent ,
628
627
missing_hex_exponent_value( "0x1p" ) => NumberParsingError :: InvalidHexadecimalExponent ,
629
628
negative_hex_exponent_value( "0x1p-3" ) => NumberParsingError :: InvalidHexadecimalExponent ,
629
+ missing_hex_exponent_value_uppercase( "0x1P" ) => NumberParsingError :: InvalidHexadecimalExponent ,
630
+ invalid_hex_exponent_value( "0x1p1Z" ) => NumberParsingError :: InvalidHexadecimalExponent ,
631
+ invalid_hex_exponent_value_uppercase( "0x1P1Z" ) => NumberParsingError :: InvalidHexadecimalExponent ,
632
+ negative_hex_exponent_value_uppercase( "0x1P-3" ) => NumberParsingError :: InvalidHexadecimalExponent ,
630
633
invalid_digit_in_binary( "0b190" ) => NumberParsingError :: InvalidBinaryNumber ,
634
+ invalid_digit_in_binary_uppercase( "0B190" ) => NumberParsingError :: InvalidBinaryNumber ,
631
635
) ;
632
636
}
633
637
0 commit comments