Make the various from_str functions return options

So that they can be used with user input without causing task
failures.

Closes #1335
This commit is contained in:
Marijn Haverbeke
2012-02-22 13:18:15 +01:00
parent 72373438d2
commit ffd50b9cdf
26 changed files with 196 additions and 308 deletions

View File

@@ -59,11 +59,11 @@ pure fn is_false(v: t) -> bool { !v }
#[doc(
brief = "Parse logic value from `s`"
)]
pure fn from_str(s: str) -> t {
pure fn from_str(s: str) -> option<t> {
alt check s {
"true" { true }
"false" { false }
_ { fail "'" + s + "' is not a valid boolean string"; }
"true" { some(true) }
"false" { some(false) }
_ { none }
}
}
@@ -89,7 +89,7 @@ pure fn to_bit(v: t) -> u8 { if v { 1u8 } else { 0u8 } }
#[test]
fn test_bool_from_str() {
all_values { |v|
assert v == from_str(bool::to_str(v))
assert some(v) == from_str(bool::to_str(v))
}
}

View File

@@ -38,7 +38,7 @@ export is_alphabetic,
is_lowercase, is_uppercase,
is_whitespace, is_alphanumeric,
is_ascii, is_digit,
to_digit, to_lower, to_upper, maybe_digit, cmp;
to_digit, to_lower, to_upper, cmp;
import is_alphabetic = unicode::derived_property::Alphabetic;
import is_XID_start = unicode::derived_property::XID_Start;
@@ -102,26 +102,18 @@ pure fn is_digit(c: char) -> bool {
Safety note: This function fails if `c` is not a valid char",
return = "If `c` is between '0' and '9', the corresponding value \
between 0 and 9. If `c` is 'a' or 'A', 10. If `c` is \
'b' or 'B', 11, etc."
'b' or 'B', 11, etc. Returns none if the char does not \
refer to a digit in the given radix."
)]
pure fn to_digit(c: char) -> u8 unsafe {
alt maybe_digit(c) {
option::some(x) { x }
option::none { fail; }
}
}
#[doc(
brief = "Convert a char to the corresponding digit. Returns none when \
character is not a valid hexadecimal digit."
)]
pure fn maybe_digit(c: char) -> option<u8> {
alt c {
'0' to '9' { option::some(c as u8 - ('0' as u8)) }
'a' to 'z' { option::some(c as u8 + 10u8 - ('a' as u8)) }
'A' to 'Z' { option::some(c as u8 + 10u8 - ('A' as u8)) }
_ { option::none }
}
pure fn to_digit(c: char, radix: uint) -> option<uint> {
let val = alt c {
'0' to '9' { c as uint - ('0' as uint) }
'a' to 'z' { c as uint + 10u - ('a' as uint) }
'A' to 'Z' { c as uint + 10u - ('A' as uint) }
_ { ret none; }
};
if val < radix { some(val) }
else { none }
}
/*
@@ -192,30 +184,19 @@ fn test_is_whitespace() {
#[test]
fn test_to_digit() {
assert (to_digit('0') == 0u8);
assert (to_digit('1') == 1u8);
assert (to_digit('2') == 2u8);
assert (to_digit('9') == 9u8);
assert (to_digit('a') == 10u8);
assert (to_digit('A') == 10u8);
assert (to_digit('b') == 11u8);
assert (to_digit('B') == 11u8);
assert (to_digit('z') == 35u8);
assert (to_digit('Z') == 35u8);
}
assert to_digit('0', 10u) == some(0u);
assert to_digit('1', 2u) == some(1u);
assert to_digit('2', 3u) == some(2u);
assert to_digit('9', 10u) == some(9u);
assert to_digit('a', 16u) == some(10u);
assert to_digit('A', 16u) == some(10u);
assert to_digit('b', 16u) == some(11u);
assert to_digit('B', 16u) == some(11u);
assert to_digit('z', 36u) == some(35u);
assert to_digit('Z', 36u) == some(35u);
#[test]
#[should_fail]
#[ignore(cfg(target_os = "win32"))]
fn test_to_digit_fail_1() {
to_digit(' ');
}
#[test]
#[should_fail]
#[ignore(cfg(target_os = "win32"))]
fn test_to_digit_fail_2() {
to_digit('$');
assert to_digit(' ', 10u) == none;
assert to_digit('$', 36u) == none;
}
#[test]

View File

@@ -122,28 +122,27 @@ Leading and trailing whitespace are ignored.
Parameters:
num - A string, possibly empty.
num - A string
Returns:
<NaN> If the string did not represent a valid number.
Otherwise, the floating-point number represented [num].
none if the string did not represent a valid number.
Otherwise, some(n) where n is the floating-point
number represented by [num].
*/
fn from_str(num: str) -> float {
let num = str::trim(num);
fn from_str(num: str) -> option<float> {
let pos = 0u; //Current byte position in the string.
//Used to walk the string in O(n).
let len = str::len_bytes(num); //Length of the string, in bytes.
if len == 0u { ret 0.; }
if len == 0u { ret none; }
let total = 0f; //Accumulated result
let c = 'z'; //Latest char.
//The string must start with one of the following characters.
alt str::char_at(num, 0u) {
'-' | '+' | '0' to '9' | '.' {}
_ { ret NaN; }
_ { ret none; }
}
//Determine if first char is '-'/'+'. Set [pos] and [neg] accordingly.
@@ -173,7 +172,7 @@ fn from_str(num: str) -> float {
break;
}
_ {
ret NaN;
ret none;
}
}
}
@@ -193,7 +192,7 @@ fn from_str(num: str) -> float {
break;
}
_ {
ret NaN;
ret none;
}
}
}
@@ -238,17 +237,17 @@ fn from_str(num: str) -> float {
total = total * multiplier;
}
} else {
ret NaN;
ret none;
}
}
if(pos < len) {
ret NaN;
ret none;
} else {
if(neg) {
total *= -1f;
}
ret total;
ret some(total);
}
}
@@ -291,39 +290,36 @@ fn pow_uint_to_uint_as_float(x: uint, pow: uint) -> float {
#[test]
fn test_from_str() {
assert ( from_str("3") == 3. );
assert ( from_str(" 3 ") == 3. );
assert ( from_str("3.14") == 3.14 );
assert ( from_str("+3.14") == 3.14 );
assert ( from_str("-3.14") == -3.14 );
assert ( from_str("2.5E10") == 25000000000. );
assert ( from_str("2.5e10") == 25000000000. );
assert ( from_str("25000000000.E-10") == 2.5 );
assert ( from_str("") == 0. );
assert ( from_str(".") == 0. );
assert ( from_str(".e1") == 0. );
assert ( from_str(".e-1") == 0. );
assert ( from_str("5.") == 5. );
assert ( from_str(".5") == 0.5 );
assert ( from_str("0.5") == 0.5 );
assert ( from_str("0.5 ") == 0.5 );
assert ( from_str(" 0.5 ") == 0.5 );
assert ( from_str(" -.5 ") == -0.5 );
assert ( from_str(" -.5 ") == -0.5 );
assert ( from_str(" -5 ") == -5. );
assert from_str("3") == some(3.);
assert from_str("3") == some(3.);
assert from_str("3.14") == some(3.14);
assert from_str("+3.14") == some(3.14);
assert from_str("-3.14") == some(-3.14);
assert from_str("2.5E10") == some(25000000000.);
assert from_str("2.5e10") == some(25000000000.);
assert from_str("25000000000.E-10") == some(2.5);
assert from_str(".") == some(0.);
assert from_str(".e1") == some(0.);
assert from_str(".e-1") == some(0.);
assert from_str("5.") == some(5.);
assert from_str(".5") == some(0.5);
assert from_str("0.5") == some(0.5);
assert from_str("0.5") == some(0.5);
assert from_str("0.5") == some(0.5);
assert from_str("-.5") == some(-0.5);
assert from_str("-.5") == some(-0.5);
assert from_str("-5") == some(-5.);
assert ( is_NaN(from_str("x")) );
assert ( from_str(" ") == 0. );
assert ( from_str(" ") == 0. );
assert ( from_str(" 0.5") == 0.5 );
assert ( from_str(" 0.5 ") == 0.5 );
assert ( from_str(" .1 ") == 0.1 );
assert ( is_NaN(from_str("e")) );
assert ( is_NaN(from_str("E")) );
assert ( is_NaN(from_str("E1")) );
assert ( is_NaN(from_str("1e1e1")) );
assert ( is_NaN(from_str("1e1.1")) );
assert ( is_NaN(from_str("1e1-1")) );
assert from_str("") == none;
assert from_str("x") == none;
assert from_str(" ") == none;
assert from_str(" ") == none;
assert from_str("e") == none;
assert from_str("E") == none;
assert from_str("E1") == none;
assert from_str("1e1e1") == none;
assert from_str("1e1.1") == none;
assert from_str("1e1-1") == none;
}
#[test]

View File

@@ -103,17 +103,10 @@ Parameters:
buf - A byte buffer
radix - The base of the number
Failure:
buf must not be empty
*/
fn parse_buf(buf: [u8], radix: uint) -> int {
if vec::len::<u8>(buf) == 0u {
#error("parse_buf(): buf is empty");
fail;
}
let i = vec::len::<u8>(buf) - 1u;
fn parse_buf(buf: [u8], radix: uint) -> option<int> {
if vec::len(buf) == 0u { ret none; }
let i = vec::len(buf) - 1u;
let start = 0u;
let power = 1;
@@ -123,13 +116,12 @@ fn parse_buf(buf: [u8], radix: uint) -> int {
}
let n = 0;
while true {
let digit = char::to_digit(buf[i] as char);
if (digit as uint) >= radix {
fail;
alt char::to_digit(buf[i] as char, radix) {
some(d) { n += (d as int) * power; }
none { ret none; }
}
n += (digit as int) * power;
power *= radix as int;
if i <= start { ret n; }
if i <= start { ret some(n); }
i -= 1u;
}
fail;
@@ -139,12 +131,8 @@ fn parse_buf(buf: [u8], radix: uint) -> int {
Function: from_str
Parse a string to an int
Failure:
s must not be empty
*/
fn from_str(s: str) -> int { parse_buf(str::bytes(s), 10u) }
fn from_str(s: str) -> option<int> { parse_buf(str::bytes(s), 10u) }
/*
Function: to_str
@@ -198,67 +186,45 @@ fn abs(i: int) -> int {
#[test]
fn test_from_str() {
assert(from_str("0") == 0);
assert(from_str("3") == 3);
assert(from_str("10") == 10);
assert(from_str("123456789") == 123456789);
assert(from_str("00100") == 100);
assert from_str("0") == some(0);
assert from_str("3") == some(3);
assert from_str("10") == some(10);
assert from_str("123456789") == some(123456789);
assert from_str("00100") == some(100);
assert(from_str("-1") == -1);
assert(from_str("-3") == -3);
assert(from_str("-10") == -10);
assert(from_str("-123456789") == -123456789);
assert(from_str("-00100") == -100);
}
assert from_str("-1") == some(-1);
assert from_str("-3") == some(-3);
assert from_str("-10") == some(-10);
assert from_str("-123456789") == some(-123456789);
assert from_str("-00100") == some(-100);
#[test]
#[should_fail]
#[ignore(cfg(target_os = "win32"))]
fn test_from_str_fail_1() {
from_str(" ");
}
#[test]
#[should_fail]
#[ignore(cfg(target_os = "win32"))]
fn test_from_str_fail_2() {
from_str("x");
assert from_str(" ") == none;
assert from_str("x") == none;
}
#[test]
fn test_parse_buf() {
import str::bytes;
assert (parse_buf(bytes("123"), 10u) == 123);
assert (parse_buf(bytes("1001"), 2u) == 9);
assert (parse_buf(bytes("123"), 8u) == 83);
assert (parse_buf(bytes("123"), 16u) == 291);
assert (parse_buf(bytes("ffff"), 16u) == 65535);
assert (parse_buf(bytes("FFFF"), 16u) == 65535);
assert (parse_buf(bytes("z"), 36u) == 35);
assert (parse_buf(bytes("Z"), 36u) == 35);
assert parse_buf(bytes("123"), 10u) == some(123);
assert parse_buf(bytes("1001"), 2u) == some(9);
assert parse_buf(bytes("123"), 8u) == some(83);
assert parse_buf(bytes("123"), 16u) == some(291);
assert parse_buf(bytes("ffff"), 16u) == some(65535);
assert parse_buf(bytes("FFFF"), 16u) == some(65535);
assert parse_buf(bytes("z"), 36u) == some(35);
assert parse_buf(bytes("Z"), 36u) == some(35);
assert (parse_buf(bytes("-123"), 10u) == -123);
assert (parse_buf(bytes("-1001"), 2u) == -9);
assert (parse_buf(bytes("-123"), 8u) == -83);
assert (parse_buf(bytes("-123"), 16u) == -291);
assert (parse_buf(bytes("-ffff"), 16u) == -65535);
assert (parse_buf(bytes("-FFFF"), 16u) == -65535);
assert (parse_buf(bytes("-z"), 36u) == -35);
assert (parse_buf(bytes("-Z"), 36u) == -35);
}
assert parse_buf(bytes("-123"), 10u) == some(-123);
assert parse_buf(bytes("-1001"), 2u) == some(-9);
assert parse_buf(bytes("-123"), 8u) == some(-83);
assert parse_buf(bytes("-123"), 16u) == some(-291);
assert parse_buf(bytes("-ffff"), 16u) == some(-65535);
assert parse_buf(bytes("-FFFF"), 16u) == some(-65535);
assert parse_buf(bytes("-z"), 36u) == some(-35);
assert parse_buf(bytes("-Z"), 36u) == some(-35);
#[test]
#[should_fail]
#[ignore(cfg(target_os = "win32"))]
fn test_parse_buf_fail_1() {
parse_buf(str::bytes("Z"), 35u);
}
#[test]
#[should_fail]
#[ignore(cfg(target_os = "win32"))]
fn test_parse_buf_fail_2() {
parse_buf(str::bytes("-9"), 2u);
assert parse_buf(str::bytes("Z"), 35u) == none;
assert parse_buf(str::bytes("-9"), 2u) == none;
}
#[test]

View File

@@ -40,6 +40,16 @@ fn map<T, U: copy>(opt: t<T>, f: fn(T) -> U) -> t<U> {
alt opt { some(x) { some(f(x)) } none { none } }
}
/*
Function: chain
Update an optional value by optionally running its content through a function
that returns an option.
*/
fn chain<T, U>(opt: t<T>, f: fn(T) -> t<U>) -> t<U> {
alt opt { some(x) { f(x) } none { none } }
}
/*
Function: is_none

View File

@@ -117,19 +117,17 @@ Function: from_str
Parse a string as an unsigned integer.
*/
fn from_str(buf: str, radix: u64) -> u64 {
if str::len_bytes(buf) == 0u {
#error("parse_buf(): buf is empty");
fail;
}
fn from_str(buf: str, radix: u64) -> option<u64> {
if str::len_bytes(buf) == 0u { ret none; }
let i = str::len_bytes(buf) - 1u;
let power = 1u64, n = 0u64;
while true {
let digit = char::to_digit(buf[i] as char) as u64;
if digit >= radix { fail; }
n += digit * power;
alt char::to_digit(buf[i] as char, radix) {
some(d) { n += d as u64 * power; }
none { ret none; }
}
power *= radix;
if i == 0u { ret n; }
if i == 0u { ret some(n); }
i -= 1u;
}
fail;

View File

@@ -188,22 +188,18 @@ Failure:
buf must not be empty
*/
fn parse_buf(buf: [u8], radix: uint) -> uint {
if vec::len::<u8>(buf) == 0u {
#error("parse_buf(): buf is empty");
fail;
}
let i = vec::len::<u8>(buf) - 1u;
fn parse_buf(buf: [u8], radix: uint) -> option<uint> {
if vec::len(buf) == 0u { ret none; }
let i = vec::len(buf) - 1u;
let power = 1u;
let n = 0u;
while true {
let digit = char::to_digit(buf[i] as char);
if (digit as uint) >= radix {
fail;
alt char::to_digit(buf[i] as char, radix) {
some(d) { n += d * power; }
none { ret none; }
}
n += (digit as uint) * power;
power *= radix;
if i == 0u { ret n; }
if i == 0u { ret some(n); }
i -= 1u;
}
fail;
@@ -213,12 +209,8 @@ fn parse_buf(buf: [u8], radix: uint) -> uint {
Function: from_str
Parse a string to an int
Failure:
s must not be empty
*/
fn from_str(s: str) -> uint { parse_buf(str::bytes(s), 10u) }
fn from_str(s: str) -> option<uint> { parse_buf(str::bytes(s), 10u) }
/*
Function: to_str
@@ -282,50 +274,29 @@ mod tests {
#[test]
fn test_from_str() {
assert (uint::from_str("0") == 0u);
assert (uint::from_str("3") == 3u);
assert (uint::from_str("10") == 10u);
assert (uint::from_str("123456789") == 123456789u);
assert (uint::from_str("00100") == 100u);
assert uint::from_str("0") == some(0u);
assert uint::from_str("3") == some(3u);
assert uint::from_str("10") == some(10u);
assert uint::from_str("123456789") == some(123456789u);
assert uint::from_str("00100") == some(100u);
assert uint::from_str("") == none;
assert uint::from_str(" ") == none;
assert uint::from_str("x") == none;
}
#[test]
#[should_fail]
#[ignore(cfg(target_os = "win32"))]
fn test_from_str_fail_1() {
uint::from_str(" ");
}
#[test]
#[should_fail]
#[ignore(cfg(target_os = "win32"))]
fn test_from_str_fail_2() {
uint::from_str("x");
}
#[test]
#[Test]
fn test_parse_buf() {
import str::bytes;
assert (uint::parse_buf(bytes("123"), 10u) == 123u);
assert (uint::parse_buf(bytes("1001"), 2u) == 9u);
assert (uint::parse_buf(bytes("123"), 8u) == 83u);
assert (uint::parse_buf(bytes("123"), 16u) == 291u);
assert (uint::parse_buf(bytes("ffff"), 16u) == 65535u);
assert (uint::parse_buf(bytes("z"), 36u) == 35u);
}
assert uint::parse_buf(bytes("123"), 10u) == some(123u);
assert uint::parse_buf(bytes("1001"), 2u) == some(9u);
assert uint::parse_buf(bytes("123"), 8u) == some(83u);
assert uint::parse_buf(bytes("123"), 16u) == some(291u);
assert uint::parse_buf(bytes("ffff"), 16u) == some(65535u);
assert uint::parse_buf(bytes("z"), 36u) == some(35u);
#[test]
#[should_fail]
#[ignore(cfg(target_os = "win32"))]
fn test_parse_buf_fail_1() {
uint::parse_buf(str::bytes("Z"), 10u);
}
#[test]
#[should_fail]
#[ignore(cfg(target_os = "win32"))]
fn test_parse_buf_fail_2() {
uint::parse_buf(str::bytes("_"), 2u);
assert uint::parse_buf(str::bytes("Z"), 10u) == none;
assert uint::parse_buf(str::bytes("_"), 2u) == none;
}
#[test]