import '../../core/parser.dart';
import 'utilities.dart';

Map<Parser, Set<Parser>> computeFirstSets({
  required Iterable<Parser> parsers,
  required Parser sentinel,
}) {
  final firstSets = {
    for (final parser in parsers)
      parser: {
        if (isTerminal(parser)) parser,
        if (isNullable(parser)) sentinel,
      },
  };
  var changed = false;
  do {
    changed = false;
    for (final parser in parsers) {
      changed |= expandFirstSet(
        parser: parser,
        firstSets: firstSets,
        sentinel: sentinel,
      );
    }
  } while (changed);
  return firstSets;
}

bool expandFirstSet({
  required Parser parser,
  required Map<Parser, Set<Parser>> firstSets,
  required Parser sentinel,
}) {
  var changed = false;
  final firstSet = firstSets[parser]!;
  if (isSequence(parser)) {
    for (final child in parser.children) {
      var nullable = false;
      for (final first in firstSets[child]!) {
        if (isNullable(first)) {
          nullable = true;
        } else {
          changed |= firstSet.add(first);
        }
      }
      if (!nullable) {
        return changed;
      }
    }
    changed |= firstSet.add(sentinel);
  } else {
    for (final child in parser.children) {
      changed |= addAll(firstSet, firstSets[child]!);
    }
  }
  return changed;
}
