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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 '+').
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
Reference in New Issue
Block a user