parseFloats method

List<double> parseFloats(
  1. String input, [
  2. List? flags,
  3. int stride = 1
])

Implementation

List<double> parseFloats(String input, [List? flags, int stride = 1]) {
  flags ??= [];
  // 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;

  int state = sep;
  bool seenComma = true;
  String number = '', exponent = '';
  List<double> result = [];

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

  void 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;
  final length = input.length;

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

    // check for flags
    if (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;
}