parseNumber function
Parses a decimal number: an optional sign, digits with an optional
fraction (12, -1.5, .5, 5.) and an optional exponent (1e3).
Leading and trailing ASCII whitespace is allowed. Returns an int for
integers that fit, a double otherwise, and null if the string is not
a number. Hexadecimal numbers, Infinity and NaN are not numbers.
Implementation
num? parseNumber(String str) {
final length = str.length;
var start = 0;
while (start < length && _isSpace(str.codeUnitAt(start))) {
start++;
}
var end = length;
while (end > start && _isSpace(str.codeUnitAt(end - 1))) {
end--;
}
if (start == end) return null;
var i = start;
var isNegative = false;
final char = str.codeUnitAt(i);
if (char == 0x2D || char == 0x2B) {
// "-" or "+"
isNegative = char == 0x2D;
i++;
}
// Digits are accumulated while the value is exact: the fast path for the
// common case of a short integer, without substring() and tryParse()
var intValue = 0;
var digits = 0;
while (i < end) {
final digit = str.codeUnitAt(i) - 0x30;
if (digit < 0 || digit > 9) break;
if (digits < 15) intValue = intValue * 10 + digit;
digits++;
i++;
}
var isInteger = true;
var fractionDigits = 0;
if (i < end && str.codeUnitAt(i) == 0x2E) {
// "."
isInteger = false;
i++;
while (i < end && _isDigit(str.codeUnitAt(i))) {
fractionDigits++;
i++;
}
}
if (digits == 0 && fractionDigits == 0) return null;
if (i < end && (str.codeUnitAt(i) | 0x20) == 0x65) {
// "e" or "E"
isInteger = false;
i++;
if (i < end && (str.codeUnitAt(i) == 0x2D || str.codeUnitAt(i) == 0x2B)) {
i++;
}
var exponentDigits = 0;
while (i < end && _isDigit(str.codeUnitAt(i))) {
exponentDigits++;
i++;
}
if (exponentDigits == 0) return null;
}
if (i != end) return null;
if (isInteger && digits <= 15) return isNegative ? -intValue : intValue;
final numberStr = start == 0 && end == length
? str
: str.substring(start, end);
if (isInteger) {
final parsed = int.tryParse(numberStr);
if (parsed != null) return parsed;
}
final parsed = double.tryParse(numberStr);
return parsed != null && parsed.isFinite ? parsed : null;
}