63 lines
2.4 KiB
JavaScript
63 lines
2.4 KiB
JavaScript
"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
|