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 sepValue = 0;
  const intValue = 1;
  const floatValue = 2;
  const expValue = 3;

  var state = sepValue;
  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 = intValue;
      number = current;
      newNumber();
      continue;
    }

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

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

      if (re["POINT"].hasMatch(current)) {
        state = floatValue;
        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 == intValue) {
      if (re["DIGIT"].hasMatch(current)) {
        number += current;
        continue;
      }

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

      if (re["EXP"].hasMatch(current)) {
        state = expValue;
        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 == floatValue) {
      if (re["DIGIT"].hasMatch(current)) {
        number += current;
        continue;
      }

      if (re["EXP"].hasMatch(current)) {
        state = expValue;
        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 == expValue) {
      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 = sepValue;
      seenComma = false;
    } else if (re["COMMA"].hasMatch(current)) {
      newNumber();
      state = sepValue;
      seenComma = true;
    } else if (re["SIGN"].hasMatch(current)) {
      newNumber();
      state = intValue;
      number = current;
    } else if (re["POINT"].hasMatch(current)) {
      newNumber();
      state = floatValue;
      number = current;
    } else {
      throwSyntaxError(current, i, result);
    }
  }

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

  return result;
}