"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var utils_1 = require("../../utils/utils"); var gast_public_1 = require("./gast/gast_public"); var gast_1 = require("./gast/gast"); function first(prod) { /* istanbul ignore else */ if (prod instanceof gast_public_1.NonTerminal) { // this could in theory cause infinite loops if // (1) prod A refs prod B. // (2) prod B refs prod A // (3) AB can match the empty set // in other words a cycle where everything is optional so the first will keep // looking ahead for the next optional part and will never exit // currently there is no safeguard for this unique edge case because // (1) not sure a grammar in which this can happen is useful for anything (productive) return first(prod.referencedRule); } else if (prod instanceof gast_public_1.Terminal) { return firstForTerminal(prod); } else if (gast_1.isSequenceProd(prod)) { return firstForSequence(prod); } else if (gast_1.isBranchingProd(prod)) { return firstForBranching(prod); } else { throw Error("non exhaustive match"); } } exports.first = first; function firstForSequence(prod) { var firstSet = []; var seq = prod.definition; var nextSubProdIdx = 0; var hasInnerProdsRemaining = seq.length > nextSubProdIdx; var currSubProd; // so we enter the loop at least once (if the definition is not empty var isLastInnerProdOptional = true; // scan a sequence until it's end or until we have found a NONE optional production in it while (hasInnerProdsRemaining && isLastInnerProdOptional) { currSubProd = seq[nextSubProdIdx]; isLastInnerProdOptional = gast_1.isOptionalProd(currSubProd); firstSet = firstSet.concat(first(currSubProd)); nextSubProdIdx = nextSubProdIdx + 1; hasInnerProdsRemaining = seq.length > nextSubProdIdx; } return utils_1.uniq(firstSet); } exports.firstForSequence = firstForSequence; function firstForBranching(prod) { var allAlternativesFirsts = utils_1.map(prod.definition, function (innerProd) { return first(innerProd); }); return utils_1.uniq(utils_1.flatten(allAlternativesFirsts)); } exports.firstForBranching = firstForBranching; function firstForTerminal(terminal) { return [terminal.terminalType]; } exports.firstForTerminal = firstForTerminal; //# sourceMappingURL=first.js.map