Skip to content

Commit 913ca08

Browse files
committed
auto merge of #5776 : dbaupp/rust/syntax-parse-large-number, r=thestinger
Addresses #5544 and #5770, as well as a comment left in the documentation of `from_str_bytes_common`, so that there is now an option to ignore underscores.
2 parents c929363 + 0c2ceb1 commit 913ca08

File tree

9 files changed

+104
-30
lines changed

9 files changed

+104
-30
lines changed

src/libcore/num/f32.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ impl num::ToStrRadix for f32 {
507507
#[inline(always)]
508508
pub fn from_str(num: &str) -> Option<f32> {
509509
strconv::from_str_common(num, 10u, true, true, true,
510-
strconv::ExpDec, false)
510+
strconv::ExpDec, false, false)
511511
}
512512

513513
/**
@@ -540,7 +540,7 @@ pub fn from_str(num: &str) -> Option<f32> {
540540
#[inline(always)]
541541
pub fn from_str_hex(num: &str) -> Option<f32> {
542542
strconv::from_str_common(num, 16u, true, true, true,
543-
strconv::ExpBin, false)
543+
strconv::ExpBin, false, false)
544544
}
545545

546546
/**
@@ -565,7 +565,7 @@ pub fn from_str_hex(num: &str) -> Option<f32> {
565565
#[inline(always)]
566566
pub fn from_str_radix(num: &str, rdx: uint) -> Option<f32> {
567567
strconv::from_str_common(num, rdx, true, true, false,
568-
strconv::ExpNone, false)
568+
strconv::ExpNone, false, false)
569569
}
570570

571571
impl from_str::FromStr for f32 {

src/libcore/num/f64.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ impl num::ToStrRadix for f64 {
529529
#[inline(always)]
530530
pub fn from_str(num: &str) -> Option<f64> {
531531
strconv::from_str_common(num, 10u, true, true, true,
532-
strconv::ExpDec, false)
532+
strconv::ExpDec, false, false)
533533
}
534534

535535
/**
@@ -562,7 +562,7 @@ pub fn from_str(num: &str) -> Option<f64> {
562562
#[inline(always)]
563563
pub fn from_str_hex(num: &str) -> Option<f64> {
564564
strconv::from_str_common(num, 16u, true, true, true,
565-
strconv::ExpBin, false)
565+
strconv::ExpBin, false, false)
566566
}
567567

568568
/**
@@ -587,7 +587,7 @@ pub fn from_str_hex(num: &str) -> Option<f64> {
587587
#[inline(always)]
588588
pub fn from_str_radix(num: &str, rdx: uint) -> Option<f64> {
589589
strconv::from_str_common(num, rdx, true, true, false,
590-
strconv::ExpNone, false)
590+
strconv::ExpNone, false, false)
591591
}
592592

593593
impl from_str::FromStr for f64 {

src/libcore/num/float.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ impl num::ToStrRadix for float {
242242
#[inline(always)]
243243
pub fn from_str(num: &str) -> Option<float> {
244244
strconv::from_str_common(num, 10u, true, true, true,
245-
strconv::ExpDec, false)
245+
strconv::ExpDec, false, false)
246246
}
247247
248248
/**
@@ -275,7 +275,7 @@ pub fn from_str(num: &str) -> Option<float> {
275275
#[inline(always)]
276276
pub fn from_str_hex(num: &str) -> Option<float> {
277277
strconv::from_str_common(num, 16u, true, true, true,
278-
strconv::ExpBin, false)
278+
strconv::ExpBin, false, false)
279279
}
280280
281281
/**
@@ -300,7 +300,7 @@ pub fn from_str_hex(num: &str) -> Option<float> {
300300
#[inline(always)]
301301
pub fn from_str_radix(num: &str, radix: uint) -> Option<float> {
302302
strconv::from_str_common(num, radix, true, true, false,
303-
strconv::ExpNone, false)
303+
strconv::ExpNone, false, false)
304304
}
305305
306306
impl from_str::FromStr for float {

src/libcore/num/int-template.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -202,21 +202,21 @@ impl ops::Neg<T> for T {
202202
#[inline(always)]
203203
pub fn from_str(s: &str) -> Option<T> {
204204
strconv::from_str_common(s, 10u, true, false, false,
205-
strconv::ExpNone, false)
205+
strconv::ExpNone, false, false)
206206
}
207207
208208
/// Parse a string as a number in the given base.
209209
#[inline(always)]
210210
pub fn from_str_radix(s: &str, radix: uint) -> Option<T> {
211211
strconv::from_str_common(s, radix, true, false, false,
212-
strconv::ExpNone, false)
212+
strconv::ExpNone, false, false)
213213
}
214214
215215
/// Parse a byte slice as a number in the given base.
216216
#[inline(always)]
217217
pub fn parse_bytes(buf: &[u8], radix: uint) -> Option<T> {
218218
strconv::from_str_bytes_common(buf, radix, true, false, false,
219-
strconv::ExpNone, false)
219+
strconv::ExpNone, false, false)
220220
}
221221
222222
impl FromStr for T {

src/libcore/num/strconv.rs

+52-13
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,8 @@ priv static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
429429
* `FFp128`. The exponent string itself is always base 10.
430430
* Can conflict with `radix`, see Failure.
431431
* - `empty_zero` - Whether to accept a empty `buf` as a 0 or not.
432+
* - `ignore_underscores` - Whether all underscores within the string should
433+
* be ignored.
432434
*
433435
* # Return value
434436
* Returns `Some(n)` if `buf` parses to a number n without overflowing, and
@@ -443,16 +445,13 @@ priv static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
443445
* between digit and exponent sign `'p'`.
444446
* - Fails if `radix` > 18 and `special == true` due to conflict
445447
* between digit and lowest first character in `inf` and `NaN`, the `'i'`.
446-
*
447-
* # Possible improvements
448-
* - Could accept option to allow ignoring underscores, allowing for numbers
449-
* formated like `FF_AE_FF_FF`.
450448
*/
451-
pub fn from_str_bytes_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+
449+
pub fn from_str_bytes_common<T:NumCast+Zero+One+Eq+Ord+Copy+Div<T,T>+
452450
Mul<T,T>+Sub<T,T>+Neg<T>+Add<T,T>+
453451
NumStrConv>(
454452
buf: &[u8], radix: uint, negative: bool, fractional: bool,
455-
special: bool, exponent: ExponentFormat, empty_zero: bool
453+
special: bool, exponent: ExponentFormat, empty_zero: bool,
454+
ignore_underscores: bool
456455
) -> Option<T> {
457456
match exponent {
458457
ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e'
@@ -531,12 +530,16 @@ pub fn from_str_bytes_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+
531530
accum -= cast(digit as int);
532531
}
533532

534-
// Detect overflow by comparing to last value
535-
if accum_positive && accum < last_accum { return None; }
536-
if !accum_positive && accum > last_accum { return None; }
533+
// Detect overflow by comparing to last value, except
534+
// if we've not seen any non-zero digits.
535+
if last_accum != _0 {
536+
if accum_positive && accum <= last_accum { return None; }
537+
if !accum_positive && accum >= last_accum { return None; }
538+
}
537539
last_accum = accum;
538540
}
539541
None => match c {
542+
'_' if ignore_underscores => {}
540543
'e' | 'E' | 'p' | 'P' => {
541544
exp_found = true;
542545
break; // start of exponent
@@ -580,6 +583,7 @@ pub fn from_str_bytes_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+
580583
last_accum = accum;
581584
}
582585
None => match c {
586+
'_' if ignore_underscores => {}
583587
'e' | 'E' | 'p' | 'P' => {
584588
exp_found = true;
585589
break; // start of exponent
@@ -607,6 +611,7 @@ pub fn from_str_bytes_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+
607611
if exp_found {
608612
let c = buf[i] as char;
609613
let base = match (c, exponent) {
614+
// c is never _ so don't need to handle specially
610615
('e', ExpDec) | ('E', ExpDec) => 10u,
611616
('p', ExpBin) | ('P', ExpBin) => 2u,
612617
_ => return None // char doesn't fit given exponent format
@@ -615,7 +620,8 @@ pub fn from_str_bytes_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+
615620
// parse remaining bytes as decimal integer,
616621
// skipping the exponent char
617622
let exp: Option<int> = from_str_bytes_common(
618-
buf.slice(i+1, len), 10, true, false, false, ExpNone, false);
623+
buf.slice(i+1, len), 10, true, false, false, ExpNone, false,
624+
ignore_underscores);
619625

620626
match exp {
621627
Some(exp_pow) => {
@@ -637,11 +643,44 @@ pub fn from_str_bytes_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+
637643
* `from_str_bytes_common()`, for details see there.
638644
*/
639645
#[inline(always)]
640-
pub fn from_str_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+Mul<T,T>+
646+
pub fn from_str_common<T:NumCast+Zero+One+Eq+Ord+Copy+Div<T,T>+Mul<T,T>+
641647
Sub<T,T>+Neg<T>+Add<T,T>+NumStrConv>(
642648
buf: &str, radix: uint, negative: bool, fractional: bool,
643-
special: bool, exponent: ExponentFormat, empty_zero: bool
649+
special: bool, exponent: ExponentFormat, empty_zero: bool,
650+
ignore_underscores: bool
644651
) -> Option<T> {
645652
from_str_bytes_common(str::to_bytes(buf), radix, negative,
646-
fractional, special, exponent, empty_zero)
653+
fractional, special, exponent, empty_zero,
654+
ignore_underscores)
655+
}
656+
657+
#[cfg(test)]
658+
mod test {
659+
use super::*;
660+
use option::*;
661+
662+
#[test]
663+
fn from_str_ignore_underscores() {
664+
let s : Option<u8> = from_str_common("__1__", 2, false, false, false,
665+
ExpNone, false, true);
666+
assert_eq!(s, Some(1u8));
667+
668+
let n : Option<u8> = from_str_common("__1__", 2, false, false, false,
669+
ExpNone, false, false);
670+
assert_eq!(n, None);
671+
672+
let f : Option<f32> = from_str_common("_1_._1_e_1_", 10, false, true, false,
673+
ExpDec, false, true);
674+
assert_eq!(f, Some(1.1e1f32));
675+
}
676+
677+
#[test]
678+
fn from_str_issue5770() {
679+
// try to parse 0b1_1111_1111 = 511 as a u8. Caused problems
680+
// since 255*2+1 == 255 (mod 256) so the overflow wasn't
681+
// detected.
682+
let n : Option<u8> = from_str_common("111111111", 2, false, false, false,
683+
ExpNone, false, false);
684+
assert_eq!(n, None);
685+
}
647686
}

src/libcore/num/uint-template.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -168,21 +168,21 @@ impl ops::Neg<T> for T {
168168
#[inline(always)]
169169
pub fn from_str(s: &str) -> Option<T> {
170170
strconv::from_str_common(s, 10u, false, false, false,
171-
strconv::ExpNone, false)
171+
strconv::ExpNone, false, false)
172172
}
173173
174174
/// Parse a string as a number in the given base.
175175
#[inline(always)]
176176
pub fn from_str_radix(s: &str, radix: uint) -> Option<T> {
177177
strconv::from_str_common(s, radix, false, false, false,
178-
strconv::ExpNone, false)
178+
strconv::ExpNone, false, false)
179179
}
180180
181181
/// Parse a byte slice as a number in the given base.
182182
#[inline(always)]
183183
pub fn parse_bytes(buf: &[u8], radix: uint) -> Option<T> {
184184
strconv::from_str_bytes_common(buf, radix, false, false, false,
185-
strconv::ExpNone, false)
185+
strconv::ExpNone, false, false)
186186
}
187187
188188
impl FromStr for T {

src/libsyntax/parse/lexer.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,11 @@ fn scan_number(c: char, rdr: @mut StringReader) -> token::Token {
442442
if str::len(num_str) == 0u {
443443
rdr.fatal(~"no valid digits found for number");
444444
}
445-
let parsed = u64::from_str_radix(num_str, base as uint).get();
445+
let parsed = match u64::from_str_radix(num_str, base as uint) {
446+
Some(p) => p,
447+
None => rdr.fatal(~"int literal is too large")
448+
};
449+
446450
match tp {
447451
either::Left(t) => return token::LIT_INT(parsed as i64, t),
448452
either::Right(t) => return token::LIT_UINT(parsed, t)
@@ -503,7 +507,10 @@ fn scan_number(c: char, rdr: @mut StringReader) -> token::Token {
503507
if str::len(num_str) == 0u {
504508
rdr.fatal(~"no valid digits found for number");
505509
}
506-
let parsed = u64::from_str_radix(num_str, base as uint).get();
510+
let parsed = match u64::from_str_radix(num_str, base as uint) {
511+
Some(p) => p,
512+
None => rdr.fatal(~"int literal is too large")
513+
};
507514

508515
debug!("lexing %s as an unsuffixed integer literal",
509516
num_str);

src/test/compile-fail/issue-5544-a.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
let _i = 18446744073709551616; // 2^64
13+
//~^ ERROR int literal is too large
14+
}

src/test/compile-fail/issue-5544-b.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
let _i = 0xff_ffff_ffff_ffff_ffff;
13+
//~^ ERROR int literal is too large
14+
}

0 commit comments

Comments
 (0)