parseNumber function

num? parseNumber(
  1. String str
)

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;
}