parseFloats method

List<double> parseFloats(
  1. dynamic input, [
  2. dynamic flags,
  3. dynamic stride
])

Implementation

List<double> parseFloats(input, [flags, stride]) {
  if (input is! String) {
    throw ('Invalid input: ${input.runtimeType} ');
  }

  // Character groups
  Map<String, dynamic> RE = {
    "SEPARATOR": RegExp(r"[ \t\r\n\,.\-+]"),
    "WHITESPACE": RegExp(r"[ \t\r\n]"),
    "DIGIT": RegExp(r"[\d]"),
    "SIGN": RegExp(r"[-+]"),
    "POINT": RegExp(r"\."),
    "COMMA": RegExp(r","),
    "EXP": RegExp(r"e", caseSensitive: false),
    "FLAGS": RegExp(r"[01]")
  };

  // States
  const SEP = 0;
  const INT = 1;
  const FLOAT = 2;
  const EXP = 3;

  var state = SEP;
  var seenComma = true;
  var number = '', exponent = '';
  List<double> result = [];

  throwSyntaxError(current, i, partial) {
    var error =
        ('Unexpected character "' + current + '" at index ' + i + '.');
    throw (error);
  }

  newNumber() {
    if (number != '') {
      if (exponent == '') {
        result.add(double.parse(number));
      } else {
        result.add(double.parse(number) * Math.pow(10, double.parse(exponent)));
      }
    }

    number = '';
    exponent = '';
  }

  String current;
  var length = input.length;

  for (var i = 0; i < length; i++) {
    current = input[i];

    // check for flags
    if (flags is List &&
        flags.contains(result.length % stride) &&
        RE["FLAGS"].hasMatch(current)) {
      state = INT;
      number = current;
      newNumber();
      continue;
    }

    // parse until next number
    if (state == SEP) {
      // eat whitespace
      if (RE["WHITESPACE"].hasMatch(current)) {
        continue;
      }

      // start new number
      if (RE["DIGIT"].hasMatch(current) || RE["SIGN"].hasMatch(current)) {
        state = INT;
        number = current;
        continue;
      }

      if (RE["POINT"].hasMatch(current)) {
        state = FLOAT;
        number = current;
        continue;
      }

      // throw on double commas (e.g. "1, , 2")
      if (RE["COMMA"].hasMatch(current)) {
        if (seenComma) {
          throwSyntaxError(current, i, result);
        }

        seenComma = true;
      }
    }

    // parse integer part
    if (state == INT) {
      if (RE["DIGIT"].hasMatch(current)) {
        number += current;
        continue;
      }

      if (RE["POINT"].hasMatch(current)) {
        number += current;
        state = FLOAT;
        continue;
      }

      if (RE["EXP"].hasMatch(current)) {
        state = EXP;
        continue;
      }

      // throw on double signs ("-+1"), but not on sign as separator ("-1-2")
      if (RE["SIGN"].hasMatch(current) &&
          number.length == 1 &&
          RE["SIGN"].hasMatch(number[0])) {
        throwSyntaxError(current, i, result);
      }
    }

    // parse decimal part
    if (state == FLOAT) {
      if (RE["DIGIT"].hasMatch(current)) {
        number += current;
        continue;
      }

      if (RE["EXP"].hasMatch(current)) {
        state = EXP;
        continue;
      }

      // throw on double decimal points (e.g. "1..2")
      if (RE["POINT"].hasMatch(current) && number[number.length - 1] == '.') {
        throwSyntaxError(current, i, result);
      }
    }

    // parse exponent part
    if (state == EXP) {
      if (RE["DIGIT"].hasMatch(current)) {
        exponent += current;
        continue;
      }

      if (RE["SIGN"].hasMatch(current)) {
        if (exponent == '') {
          exponent += current;
          continue;
        }

        if (exponent.length == 1 && RE["SIGN"].hasMatch(exponent)) {
          throwSyntaxError(current, i, result);
        }
      }
    }

    // end of number
    if (RE["WHITESPACE"].hasMatch(current)) {
      newNumber();
      state = SEP;
      seenComma = false;
    } else if (RE["COMMA"].hasMatch(current)) {
      newNumber();
      state = SEP;
      seenComma = true;
    } else if (RE["SIGN"].hasMatch(current)) {
      newNumber();
      state = INT;
      number = current;
    } else if (RE["POINT"].hasMatch(current)) {
      newNumber();
      state = FLOAT;
      number = current;
    } else {
      throwSyntaxError(current, i, result);
    }
  }

  // add the last number found (if any)
  newNumber();

  return result;
}