Add num.parse.

The implementation is still primitive: It calls int.parse, then double.parse, and then fails if it hasn't found a result yet.

BUG= http://dartbug.com/8237
R=floitsch@google.com

Committed: https://code.google.com/p/dart/source/detail?r=30624

Review URL: https://codereview.chromium.org//85633003

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@30663 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
lrn@google.com
2013-11-26 08:44:38 +00:00
parent b97593a8df
commit e1b4afa85d
5 changed files with 235 additions and 2 deletions
+2
View File
@@ -45,6 +45,8 @@ patch class double {
if (onError == null) throw new FormatException(str);
return onError(str);
}
// Parse can create a NaN that is not identical to double.NAN.
if (result.isNaN) return NAN;
return result;
}
}
+3 -2
View File
@@ -246,8 +246,9 @@ abstract class int extends num {
* first the decimal digits 0..9, and then the letters 'a'..'z'.
* Accepts capital letters as well.
*
* If no [radix] is given then it defaults to 16 if the string starts
* with "0x", "-0x" or "+0x" and 10 otherwise.
* If no [radix] is given then it defaults to 10, unless the string starts
* with "0x", "-0x" or "+0x", in which case the radix is set to 16 and the
* "0x" is ignored.
*
* The [source] must be a non-empty sequence of base-[radix] digits,
* optionally prefixed with a minus or plus sign ('-' or '+').
+29
View File
@@ -359,4 +359,33 @@ abstract class num implements Comparable<num> {
*
*/
String toString();
/**
* Parses a string containing a number literal into a number.
*
* The method first tries to read the [input] as integer (similar to
* [int.parse] without a radix).
* If that fails, it tries to parse the [input] as a double (similar to
* [double.parse]).
* If that fails, too, it invokes [onError] with [input].
*
* If no [onError] is supplied, it defaults to a function that throws a
* [FormatException].
*
* For any number `n`, this function satisfies
* `identical(n, num.parse(n.toString()))`.
*/
static num parse(String input, [num onError(String input)]) {
String source = input.trim();
// TODO(lrn): Optimize to detect format and result type in one check.
num result = int.parse(source, onError: _returnNull);
if (result != null) return result;
result = double.parse(source, _returnNull);
if (result != null) return result;
if (onError == null) throw new FormatException(input);
return onError(input);
}
/** Helper function for [parse]. */
static _returnNull(_) => null;
}
+6
View File
@@ -86,6 +86,8 @@ compare_to2_test: RuntimeError, OK # Requires bigint support.
string_base_vm_test: RuntimeError, OK # VM specific test.
nan_infinity_test/01: Fail # Issue 11551
num_parse_test/01: RuntimeError # Issue 11551
[ $compiler == dart2js && $runtime == none ]
*: Fail, Pass # TODO(ahe): Triage these tests.
@@ -145,3 +147,7 @@ string_test: StaticWarning, OK # Test generates error on purpose.
[ $compiler == dart2js && $runtime == safari ]
list_test/01: Fail # Safari bug: Array(-2) seen as dead code.
[ $runtime == ie9 || $runtime == ie10 ]
num_parse_test: RuntimeError # Issue 15316
num_parse_test/01: RuntimeError # Issue 15316
+195
View File
@@ -0,0 +1,195 @@
// Copyright (c) 2013 the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import "package:expect/expect.dart";
const whiteSpace = const [
"",
"\x09",
"\x0a",
"\x0b",
"\x0c",
"\x0d",
"\x85",
"\xa0",
"\u1680",
"\u180e",
"\u2000",
"\u2001",
"\u2002",
"\u2003",
"\u2004",
"\u2005",
"\u2006",
"\u2007",
"\u2008",
"\u2009",
"\u200a",
"\u2028",
"\u2029",
"\u202f",
"\u205f",
"\u3000",
"\uFEFF"
];
void testParse(String source, num result) {
for (String ws1 in whiteSpace) {
for (String ws2 in whiteSpace) {
String padded = "$ws1$source$ws2";
// Use Expect.identical because it also handles NaN and 0.0/-0.0.
// Except on dart2js: http://dartbug.com/11551
Expect.identical(result, num.parse(padded), "parse '$padded'");
padded = "$ws1$ws2$source";
Expect.identical(result, num.parse(padded), "parse '$padded'");
padded = "$source$ws1$ws2";
Expect.identical(result, num.parse(padded), "parse '$padded'");
}
}
}
void testInt(int value) {
testParse("$value", value);
testParse("+$value", value);
testParse("-$value", -value);
var hex = "0x${value.toRadixString(16)}";
var lchex = hex.toLowerCase();
testParse(lchex, value);
testParse("+$lchex", value);
testParse("-$lchex", -value);
var uchex = hex.toUpperCase();
testParse(uchex, value);
testParse("+$uchex", value);
testParse("-$uchex", -value);
}
void testIntAround(int value) {
testInt(value - 1);
testInt(value);
testInt(value + 1);
}
void testDouble(double value) {
testParse("$value", value);
testParse("+$value", value);
testParse("-$value", -value);
if (value.isFinite) {
String exp = value.toStringAsExponential();
String lcexp = exp.toLowerCase();
testParse(lcexp, value);
testParse("+$lcexp", value);
testParse("-$lcexp", -value);
String ucexp = exp.toUpperCase();
testParse(ucexp, value);
testParse("+$ucexp", value);
testParse("-$ucexp", -value);
}
}
void testFail(String source) {
var object = new Object();
Expect.throws(() {
num.parse(source, (s) {
Expect.equals(source, s);
throw object;
});
}, (e) => identical(object, e), "Fail: '$source'");
}
void main() {
testInt(0);
testInt(1);
testInt(9);
testInt(10);
testInt(99);
testInt(100);
testIntAround(256);
testIntAround(0x80000000); // 2^31
testIntAround(0x100000000); // 2^32
testIntAround(0x10000000000000); // 2^52
testIntAround(0x20000000000000); // 2^53
testIntAround(0x40000000000000); // 2^54
testIntAround(0x8000000000000000); // 2^63
testIntAround(0x10000000000000000); // 2^64
testIntAround(0x100000000000000000000); // 2^80
testDouble(0.0);
testDouble(5e-324);
testDouble(2.225073858507201e-308);
testDouble(2.2250738585072014e-308);
testDouble(0.49999999999999994);
testDouble(0.5);
testDouble(0.50000000000000006);
testDouble(0.9999999999999999);
testDouble(1.0);
testDouble(1.0000000000000002);
testDouble(4294967295.0);
testDouble(4294967296.0);
testDouble(4503599627370495.5);
testDouble(4503599627370497.0);
testDouble(9007199254740991.0);
testDouble(9007199254740992.0);
testDouble(1.7976931348623157e+308);
testDouble(double.INFINITY);
testDouble(double.NAN); /// 01: ok
// Strings that cannot occur from toString of a number.
testParse("000000000000", 0);
testParse("000000000001", 1);
testParse("000000000000.0000000000000", 0.0);
testParse("000000000001.0000000000000", 1.0);
testParse("0x0000000000", 0);
testParse("0e0", 0.0);
testParse("0e+0", 0.0);
testParse("0e-0", 0.0);
testParse("-0e0", -0.0);
testParse("-0e+0", -0.0);
testParse("-0e-0", -0.0);
testParse("1e0", 1.0);
testParse("1e+0", 1.0);
testParse("1e-0", 1.0);
testParse("-1e0", -1.0);
testParse("-1e+0", -1.0);
testParse("-1e-0", -1.0);
testParse("1.", 1.0);
testParse(".1", 0.1);
testParse("1.e1", 10.0);
testParse(".1e1", 1.0);
// Negative tests - things not to allow.
// Spaces inside the numeral.
testFail("- 1");
testFail("+ 1");
testFail("2 2");
testFail("0x 42");
testFail("1 .");
testFail(". 1");
testFail("1e 2");
testFail("1 e2");
// Invalid characters.
testFail("0x1H");
testFail("12H");
testFail("1x2");
testFail("00x2");
// Empty hex number.
testFail("0x");
testFail("-0x");
testFail("+0x");
// Double exponent without value.
testFail("e1");
testFail("e+1");
testFail("e-1");
testFail("-e1");
testFail("-e+1");
testFail("-e-1");
// Incorrect ways to write NaN/Infinity.
testFail("infinity");
testFail("INFINITY");
testFail("inf");
testFail("nan");
testFail("NAN");
testFail("qnan");
testFail("snan");
}