@@ -443,8 +443,7 @@ fn scan_exponent(rdr: &StringReader, start_bpos: BytePos) -> Option<~str> {
443
443
// \x00 hits the `return None` case immediately, so this is fine.
444
444
let mut c = rdr. curr . get ( ) . unwrap_or ( '\x00' ) ;
445
445
let mut rslt = ~"";
446
- if c == 'e' || c == 'E' {
447
- rslt. push_char ( c) ;
446
+ if c == 'e' || c == 'E' || c == 'p' || c == 'P' {
448
447
bump ( rdr) ;
449
448
c = rdr. curr . get ( ) . unwrap_or ( '\x00' ) ;
450
449
if c == '-' || c == '+' {
@@ -476,40 +475,32 @@ fn scan_digits(rdr: &StringReader, radix: uint) -> ~str {
476
475
} ;
477
476
}
478
477
479
- fn check_float_base ( rdr : & StringReader , start_bpos : BytePos , last_bpos : BytePos ,
480
- base : uint ) {
481
- match base {
482
- 16 u => fatal_span ( rdr, start_bpos, last_bpos,
483
- ~"hexadecimal float literal is not supported") ,
484
- 8 u => fatal_span ( rdr, start_bpos, last_bpos,
485
- ~"octal float literal is not supported") ,
486
- 2 u => fatal_span ( rdr, start_bpos, last_bpos,
487
- ~"binary float literal is not supported") ,
488
- _ => ( )
489
- }
490
- }
491
-
492
- fn scan_number( c : char , rdr : & StringReader ) -> token:: Token {
493
- let mut num_str;
494
- let mut base = 10 u;
495
- let mut c = c;
496
- let mut n = nextch ( rdr) . unwrap_or ( '\x00' ) ;
497
- let start_bpos = rdr. last_pos . get ( ) ;
478
+ fn scan_radix ( rdr : & StringReader ) -> uint {
479
+ let c = rdr. curr . get ( ) . unwrap_or ( '\x00' ) ;
480
+ let n = nextch ( rdr) . unwrap_or ( '\x00' ) ;
498
481
if c == '0' && n == 'x' {
499
482
bump ( rdr) ;
500
483
bump ( rdr) ;
501
- base = 16 u;
484
+ return 16 u;
502
485
} else if c == '0' && n == 'o' {
503
486
bump ( rdr) ;
504
487
bump ( rdr) ;
505
- base = 8 u;
488
+ return 8 u;
506
489
} else if c == '0' && n == 'b' {
507
490
bump ( rdr) ;
508
491
bump ( rdr) ;
509
- base = 2 u;
492
+ return 2 u;
510
493
}
494
+ return 10 u;
495
+ }
496
+
497
+ fn scan_number( rdr : & StringReader ) -> token:: Token {
498
+ let mut num_str;
499
+ let start_bpos = rdr. last_pos . get ( ) ;
500
+ let mut base = scan_radix ( rdr) ;
511
501
num_str = scan_digits ( rdr, base) ;
512
- c = rdr. curr . get ( ) . unwrap_or ( '\x00' ) ;
502
+ let mut c = rdr. curr . get ( ) . unwrap_or ( '\x00' ) ;
503
+ let mut n: char ;
513
504
nextch ( rdr) ;
514
505
if c == 'u' || c == 'i' {
515
506
enum Result { Signed ( ast:: IntTy ) , Unsigned ( ast:: UintTy ) }
@@ -558,19 +549,71 @@ fn scan_number(c: char, rdr: &StringReader) -> token::Token {
558
549
}
559
550
}
560
551
let mut is_float = false ;
552
+ let mut dec_part = ~"";
561
553
if rdr. curr_is ( '.' ) && !( ident_start ( nextch ( rdr) ) || nextch_is ( rdr, '.' ) ) {
562
554
is_float = true ;
563
555
bump ( rdr) ;
564
- let dec_part = scan_digits ( rdr, 10 u) ;
565
- num_str. push_char ( '.' ) ;
566
- num_str. push_str ( dec_part) ;
556
+ let mantissa_base = scan_radix ( rdr) ;
557
+ if mantissa_base != base {
558
+ //The ability to switch base, while conceivably useful, is much more
559
+ //likely to be triggered by accident.
560
+ fatal_span ( rdr, start_bpos, rdr. last_pos . get ( ) ,
561
+ ~"float literals must have consistent base before and after decimal point") ;
562
+ }
563
+ base = mantissa_base;
564
+ dec_part = scan_digits ( rdr, mantissa_base) ;
567
565
}
566
+ let mut exp_part = ~"";
568
567
match scan_exponent ( rdr, start_bpos) {
569
- Some ( ref s) => {
568
+ Some ( s) => {
570
569
is_float = true ;
571
- num_str . push_str ( * s ) ;
570
+ exp_part = s ;
572
571
}
573
- None => ( )
572
+ None => {
573
+ if is_float && base > 10 {
574
+ //otherwise we have ambiguity: 0x1.0xffff_f32 gets parsed as
575
+ //0x1.fffff32, which will create confusing results.
576
+ fatal_span ( rdr, start_bpos, rdr. last_pos . get ( ) ,
577
+ ~"hexadecimal float literals must contain exponent") ;
578
+ }
579
+ }
580
+ }
581
+ if is_float {
582
+ if base == 10 || base == 16 {
583
+ num_str. push_char ( '.' ) ;
584
+ num_str. push_str ( if dec_part. len ( ) > 0 { dec_part} else { ~"0 "} ) ;
585
+ if exp_part. len ( ) != 0 {
586
+ num_str. push_char ( if base == 10 { 'e' } else { 'p' } ) ;
587
+ num_str. push_str ( exp_part) ;
588
+ }
589
+ } else {
590
+ num_str = from_str_radix :: < u64 > ( num_str, base) . unwrap ( ) . to_str_radix ( 16 ) ;
591
+ let mut i = 0 ;
592
+ let len = dec_part. len ( ) ;
593
+ let step = match base { 8 => 2 , 2 => 4 , _ => fail ! ( "Impossible base for float" ) } ;
594
+ let mut dec_str = ~"";
595
+ while i < len {
596
+ let chunk = if i + step > len {
597
+ let mut chunk = dec_part. slice_from ( i) . to_str ( ) ;
598
+ for _ in range ( 0 , i + step - len) {
599
+ chunk. push_char ( '0' ) ;
600
+ }
601
+ chunk
602
+ } else {
603
+ dec_part. slice ( i, i + step) . to_str ( )
604
+ } ;
605
+ dec_str. push_str ( from_str_radix :: < u8 > ( chunk, base) . unwrap_or ( 0 ) . to_str ( ) ) ;
606
+ i += step;
607
+ }
608
+ num_str. push_char ( '.' ) ;
609
+ num_str. push_str ( dec_str) ;
610
+ num_str. push_char ( 'p' ) ;
611
+ num_str. push_str ( if exp_part. len ( ) > 0 { exp_part} else { ~"0 "} ) ;
612
+ }
613
+ if base != 10 {
614
+ num_str. unshift_char ( 'x' ) ;
615
+ num_str. unshift_char ( '0' ) ;
616
+ }
574
617
}
575
618
576
619
if rdr. curr_is ( 'f' ) {
@@ -580,12 +623,10 @@ fn scan_number(c: char, rdr: &StringReader) -> token::Token {
580
623
if c == '3' && n == '2' {
581
624
bump ( rdr) ;
582
625
bump ( rdr) ;
583
- check_float_base ( rdr, start_bpos, rdr. last_pos . get ( ) , base) ;
584
626
return token:: LIT_FLOAT ( str_to_ident ( num_str) , ast:: TyF32 ) ;
585
627
} else if c == '6' && n == '4' {
586
628
bump ( rdr) ;
587
629
bump ( rdr) ;
588
- check_float_base ( rdr, start_bpos, rdr. last_pos . get ( ) , base) ;
589
630
return token:: LIT_FLOAT ( str_to_ident ( num_str) , ast:: TyF64 ) ;
590
631
/* FIXME (#2252): if this is out of range for either a
591
632
32-bit or 64-bit float, it won't be noticed till the
@@ -596,7 +637,6 @@ fn scan_number(c: char, rdr: &StringReader) -> token::Token {
596
637
}
597
638
}
598
639
if is_float {
599
- check_float_base ( rdr, start_bpos, rdr. last_pos . get ( ) , base) ;
600
640
return token:: LIT_FLOAT_UNSUFFIXED ( str_to_ident ( num_str) ) ;
601
641
} else {
602
642
if num_str. len ( ) == 0 u {
@@ -687,7 +727,7 @@ fn next_token_inner(rdr: &StringReader) -> token::Token {
687
727
} )
688
728
}
689
729
if is_dec_digit ( c) {
690
- return scan_number ( c . unwrap ( ) , rdr) ;
730
+ return scan_number ( rdr) ;
691
731
}
692
732
fn binop ( rdr : & StringReader , op : token:: BinOp ) -> token:: Token {
693
733
bump ( rdr) ;
@@ -1005,6 +1045,7 @@ mod test {
1005
1045
use diagnostic;
1006
1046
use parse:: token;
1007
1047
use parse:: token:: { str_to_ident} ;
1048
+ use ast;
1008
1049
1009
1050
// represents a testing reader (incl. both reader and interner)
1010
1051
struct Env {
@@ -1139,4 +1180,20 @@ mod test {
1139
1180
assert_eq!( tok, token:: LIT_CHAR ( 'a' as u32 ) ) ;
1140
1181
}
1141
1182
1183
+ #[ test] fn hex_floats( ) {
1184
+ let env = setup( ~"0x1 . 0xffffff p100_f32") ;
1185
+ let TokenAndSpan { tok, sp: _} =
1186
+ env. string_reader. next_token( ) ;
1187
+ let id = token:: str_to_ident( "0x1 . ffffffp100") ;
1188
+ assert_eq!( tok, token:: LIT_FLOAT ( id, ast:: TyF32 ) ) ;
1189
+ }
1190
+
1191
+ #[ test] fn bin_floats( ) {
1192
+ let env = setup( ~"0b1 . 0b0000_0001_0010_0011_1 p100_f32") ;
1193
+ let TokenAndSpan { tok, sp: _} =
1194
+ env. string_reader. next_token( ) ;
1195
+ let id = token:: str_to_ident( "0x1 . 01238 p100") ;
1196
+ assert_eq!( tok, token:: LIT_FLOAT ( id, ast:: TyF32 ) ) ;
1197
+ }
1198
+
1142
1199
}
0 commit comments