docs: function is(_ref) {
    var productions = _ref.productions;
    var phi = (0, _first2.default)(productions);
  
    var FIRST = function FIRST(rule) {
      return (0, _first3.default)(phi, rule);
    }; // TODO this is stupid
  
  
    var containsduplicates = function containsduplicates(array) {
      return new Set(array).size !== array.length;
    }; // Dragon Book (2006) page 224: For every A, a, b such that A => a | b
    // ``... FIRST(a) and FIRST(b) are disjoint sets.''
  
  
    if ((0, _jsItertools.any)((0, _jsItertools.map)(function (rules) {
      return containsduplicates((0, _jsItertools.list)((0, _jsItertools.chain)((0, _jsItertools.map)(FIRST, rules.values()))));
    }, productions.values()))) return false; //   and ``... if the empty
    //   word is in FIRST(b), then FIRST(a) and FOLLOW(A) are disjoint sets,
    //   and likewise if the empty word is in FIRST(a).''
  
    var pho = (0, _follow2.default)(phi, productions);
  
    var FOLLOW = function FOLLOW(A) {
      return pho.get(A);
    };
  
    var dflt = {}; // dummy object
  
    var _iteratorNormalCompletion = true;
    var _didIteratorError = false;
    var _iteratorError = undefined;
  
    try {
      var _loop = function _loop() {
        var _step$value = _slicedToArray(_step.value, 2),
            A = _step$value[0],
            rules = _step$value[1];
  
        var yieldsEW = (0, _jsItertools.next)((0, _jsItertools.iter)((0, _jsItertools.filter)(function (rule) {
          return FIRST(rule).has(_grammar.EW);
        }, rules.values())), dflt);
        if (yieldsEW === dflt) return "continue";
        if ((0, _jsItertools.any)((0, _jsItertools.map)(function (rule) {
          return containsduplicates((0, _jsItertools.list)((0, _jsItertools.chain)([FIRST(rule), FOLLOW(A)])));
        }, (0, _jsItertools.filter)(function (rule) {
          return rule !== yieldsEW;
        }, rules.values())))) return {
          v: false
        };
      };
  
      for (var _iterator = productions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
        var _ret = _loop();
  
        switch (_ret) {
          case "continue":
            continue;
  
          default:
            if (_typeof(_ret) === "object") return _ret.v;
        }
      }
    } catch (err) {
      _didIteratorError = true;
      _iteratorError = err;
    } finally {
      try {
        if (!_iteratorNormalCompletion && _iterator.return != null) {
          _iterator.return();
        }
      } finally {
        if (_didIteratorError) {
          throw _iteratorError;
        }
      }
    }
  
    return true;
  }