GGD/node_modules/chevrotain/lib/src/parse/grammar/interpreter.js

548 lines
24 KiB
JavaScript

"use strict";
/* istanbul ignore next */ var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var rest_1 = require("./rest");
var utils_1 = require("../../utils/utils");
var first_1 = require("./first");
var gast_public_1 = require("./gast/gast_public");
var AbstractNextPossibleTokensWalker = /** @class */ (function (_super) {
__extends(AbstractNextPossibleTokensWalker, _super);
function AbstractNextPossibleTokensWalker(topProd, path) {
var _this = _super.call(this) /* istanbul ignore next */ || this;
_this.topProd = topProd;
_this.path = path;
_this.possibleTokTypes = [];
_this.nextProductionName = "";
_this.nextProductionOccurrence = 0;
_this.found = false;
_this.isAtEndOfPath = false;
return _this;
}
AbstractNextPossibleTokensWalker.prototype.startWalking = function () {
this.found = false;
if (this.path.ruleStack[0] !== this.topProd.name) {
throw Error("The path does not start with the walker's top Rule!");
}
// immutable for the win
this.ruleStack = utils_1.cloneArr(this.path.ruleStack).reverse(); // intelij bug requires assertion
this.occurrenceStack = utils_1.cloneArr(this.path.occurrenceStack).reverse(); // intelij bug requires assertion
// already verified that the first production is valid, we now seek the 2nd production
this.ruleStack.pop();
this.occurrenceStack.pop();
this.updateExpectedNext();
this.walk(this.topProd);
return this.possibleTokTypes;
};
AbstractNextPossibleTokensWalker.prototype.walk = function (prod, prevRest) {
if (prevRest === void 0) { prevRest = []; }
// stop scanning once we found the path
if (!this.found) {
_super.prototype.walk.call(this, prod, prevRest);
}
};
AbstractNextPossibleTokensWalker.prototype.walkProdRef = function (refProd, currRest, prevRest) {
// found the next production, need to keep walking in it
if (refProd.referencedRule.name === this.nextProductionName &&
refProd.idx === this.nextProductionOccurrence) {
var fullRest = currRest.concat(prevRest);
this.updateExpectedNext();
this.walk(refProd.referencedRule, fullRest);
}
};
AbstractNextPossibleTokensWalker.prototype.updateExpectedNext = function () {
// need to consume the Terminal
if (utils_1.isEmpty(this.ruleStack)) {
// must reset nextProductionXXX to avoid walking down another Top Level production while what we are
// really seeking is the last Terminal...
this.nextProductionName = "";
this.nextProductionOccurrence = 0;
this.isAtEndOfPath = true;
}
else {
this.nextProductionName = this.ruleStack.pop();
this.nextProductionOccurrence = this.occurrenceStack.pop();
}
};
return AbstractNextPossibleTokensWalker;
}(rest_1.RestWalker));
exports.AbstractNextPossibleTokensWalker = AbstractNextPossibleTokensWalker;
var NextAfterTokenWalker = /** @class */ (function (_super) {
__extends(NextAfterTokenWalker, _super);
function NextAfterTokenWalker(topProd, path) {
var _this = _super.call(this, topProd, path) /* istanbul ignore next */ || this;
_this.path = path;
_this.nextTerminalName = "";
_this.nextTerminalOccurrence = 0;
_this.nextTerminalName = _this.path.lastTok.name;
_this.nextTerminalOccurrence = _this.path.lastTokOccurrence;
return _this;
}
NextAfterTokenWalker.prototype.walkTerminal = function (terminal, currRest, prevRest) {
if (this.isAtEndOfPath &&
terminal.terminalType.name === this.nextTerminalName &&
terminal.idx === this.nextTerminalOccurrence &&
!this.found) {
var fullRest = currRest.concat(prevRest);
var restProd = new gast_public_1.Flat({ definition: fullRest });
this.possibleTokTypes = first_1.first(restProd);
this.found = true;
}
};
return NextAfterTokenWalker;
}(AbstractNextPossibleTokensWalker));
exports.NextAfterTokenWalker = NextAfterTokenWalker;
/**
* This walker only "walks" a single "TOP" level in the Grammar Ast, this means
* it never "follows" production refs
*/
var AbstractNextTerminalAfterProductionWalker = /** @class */ (function (_super) {
__extends(AbstractNextTerminalAfterProductionWalker, _super);
function AbstractNextTerminalAfterProductionWalker(topRule, occurrence) {
var _this = _super.call(this) /* istanbul ignore next */ || this;
_this.topRule = topRule;
_this.occurrence = occurrence;
_this.result = {
token: undefined,
occurrence: undefined,
isEndOfRule: undefined
};
return _this;
}
AbstractNextTerminalAfterProductionWalker.prototype.startWalking = function () {
this.walk(this.topRule);
return this.result;
};
return AbstractNextTerminalAfterProductionWalker;
}(rest_1.RestWalker));
exports.AbstractNextTerminalAfterProductionWalker = AbstractNextTerminalAfterProductionWalker;
var NextTerminalAfterManyWalker = /** @class */ (function (_super) {
__extends(NextTerminalAfterManyWalker, _super);
function NextTerminalAfterManyWalker() {
return _super !== null && _super.apply(this, arguments) /* istanbul ignore next */ || this;
}
NextTerminalAfterManyWalker.prototype.walkMany = function (manyProd, currRest, prevRest) {
if (manyProd.idx === this.occurrence) {
var firstAfterMany = utils_1.first(currRest.concat(prevRest));
this.result.isEndOfRule = firstAfterMany === undefined;
if (firstAfterMany instanceof gast_public_1.Terminal) {
this.result.token = firstAfterMany.terminalType;
this.result.occurrence = firstAfterMany.idx;
}
}
else {
_super.prototype.walkMany.call(this, manyProd, currRest, prevRest);
}
};
return NextTerminalAfterManyWalker;
}(AbstractNextTerminalAfterProductionWalker));
exports.NextTerminalAfterManyWalker = NextTerminalAfterManyWalker;
var NextTerminalAfterManySepWalker = /** @class */ (function (_super) {
__extends(NextTerminalAfterManySepWalker, _super);
function NextTerminalAfterManySepWalker() {
return _super !== null && _super.apply(this, arguments) /* istanbul ignore next */ || this;
}
NextTerminalAfterManySepWalker.prototype.walkManySep = function (manySepProd, currRest, prevRest) {
if (manySepProd.idx === this.occurrence) {
var firstAfterManySep = utils_1.first(currRest.concat(prevRest));
this.result.isEndOfRule = firstAfterManySep === undefined;
if (firstAfterManySep instanceof gast_public_1.Terminal) {
this.result.token = firstAfterManySep.terminalType;
this.result.occurrence = firstAfterManySep.idx;
}
}
else {
_super.prototype.walkManySep.call(this, manySepProd, currRest, prevRest);
}
};
return NextTerminalAfterManySepWalker;
}(AbstractNextTerminalAfterProductionWalker));
exports.NextTerminalAfterManySepWalker = NextTerminalAfterManySepWalker;
var NextTerminalAfterAtLeastOneWalker = /** @class */ (function (_super) {
__extends(NextTerminalAfterAtLeastOneWalker, _super);
function NextTerminalAfterAtLeastOneWalker() {
return _super !== null && _super.apply(this, arguments) /* istanbul ignore next */ || this;
}
NextTerminalAfterAtLeastOneWalker.prototype.walkAtLeastOne = function (atLeastOneProd, currRest, prevRest) {
if (atLeastOneProd.idx === this.occurrence) {
var firstAfterAtLeastOne = utils_1.first(currRest.concat(prevRest));
this.result.isEndOfRule = firstAfterAtLeastOne === undefined;
if (firstAfterAtLeastOne instanceof gast_public_1.Terminal) {
this.result.token = firstAfterAtLeastOne.terminalType;
this.result.occurrence = firstAfterAtLeastOne.idx;
}
}
else {
_super.prototype.walkAtLeastOne.call(this, atLeastOneProd, currRest, prevRest);
}
};
return NextTerminalAfterAtLeastOneWalker;
}(AbstractNextTerminalAfterProductionWalker));
exports.NextTerminalAfterAtLeastOneWalker = NextTerminalAfterAtLeastOneWalker;
// TODO: reduce code duplication in the AfterWalkers
var NextTerminalAfterAtLeastOneSepWalker = /** @class */ (function (_super) {
__extends(NextTerminalAfterAtLeastOneSepWalker, _super);
function NextTerminalAfterAtLeastOneSepWalker() {
return _super !== null && _super.apply(this, arguments) /* istanbul ignore next */ || this;
}
NextTerminalAfterAtLeastOneSepWalker.prototype.walkAtLeastOneSep = function (atleastOneSepProd, currRest, prevRest) {
if (atleastOneSepProd.idx === this.occurrence) {
var firstAfterfirstAfterAtLeastOneSep = utils_1.first(currRest.concat(prevRest));
this.result.isEndOfRule =
firstAfterfirstAfterAtLeastOneSep === undefined;
if (firstAfterfirstAfterAtLeastOneSep instanceof gast_public_1.Terminal) {
this.result.token =
firstAfterfirstAfterAtLeastOneSep.terminalType;
this.result.occurrence = firstAfterfirstAfterAtLeastOneSep.idx;
}
}
else {
_super.prototype.walkAtLeastOneSep.call(this, atleastOneSepProd, currRest, prevRest);
}
};
return NextTerminalAfterAtLeastOneSepWalker;
}(AbstractNextTerminalAfterProductionWalker));
exports.NextTerminalAfterAtLeastOneSepWalker = NextTerminalAfterAtLeastOneSepWalker;
function possiblePathsFrom(targetDef, maxLength, currPath) {
if (currPath === void 0) { currPath = []; }
// avoid side effects
currPath = utils_1.cloneArr(currPath);
var result = [];
var i = 0;
// TODO: avoid inner funcs
function remainingPathWith(nextDef) {
return nextDef.concat(utils_1.drop(targetDef, i + 1));
}
// TODO: avoid inner funcs
function getAlternativesForProd(definition) {
var alternatives = possiblePathsFrom(remainingPathWith(definition), maxLength, currPath);
return result.concat(alternatives);
}
/**
* Mandatory productions will halt the loop as the paths computed from their recursive calls will already contain the
* following (rest) of the targetDef.
*
* For optional productions (Option/Repetition/...) the loop will continue to represent the paths that do not include the
* the optional production.
*/
while (currPath.length < maxLength && i < targetDef.length) {
var prod = targetDef[i];
/* istanbul ignore else */
if (prod instanceof gast_public_1.Flat) {
return getAlternativesForProd(prod.definition);
}
else if (prod instanceof gast_public_1.NonTerminal) {
return getAlternativesForProd(prod.definition);
}
else if (prod instanceof gast_public_1.Option) {
result = getAlternativesForProd(prod.definition);
}
else if (prod instanceof gast_public_1.RepetitionMandatory) {
var newDef = prod.definition.concat([
new gast_public_1.Repetition({
definition: prod.definition
})
]);
return getAlternativesForProd(newDef);
}
else if (prod instanceof gast_public_1.RepetitionMandatoryWithSeparator) {
var newDef = [
new gast_public_1.Flat({ definition: prod.definition }),
new gast_public_1.Repetition({
definition: [
new gast_public_1.Terminal({ terminalType: prod.separator })
].concat(prod.definition)
})
];
return getAlternativesForProd(newDef);
}
else if (prod instanceof gast_public_1.RepetitionWithSeparator) {
var newDef = prod.definition.concat([
new gast_public_1.Repetition({
definition: [
new gast_public_1.Terminal({ terminalType: prod.separator })
].concat(prod.definition)
})
]);
result = getAlternativesForProd(newDef);
}
else if (prod instanceof gast_public_1.Repetition) {
var newDef = prod.definition.concat([
new gast_public_1.Repetition({
definition: prod.definition
})
]);
result = getAlternativesForProd(newDef);
}
else if (prod instanceof gast_public_1.Alternation) {
utils_1.forEach(prod.definition, function (currAlt) {
result = getAlternativesForProd(currAlt.definition);
});
return result;
}
else if (prod instanceof gast_public_1.Terminal) {
currPath.push(prod.terminalType);
}
else {
throw Error("non exhaustive match");
}
i++;
}
result.push({
partialPath: currPath,
suffixDef: utils_1.drop(targetDef, i)
});
return result;
}
exports.possiblePathsFrom = possiblePathsFrom;
function nextPossibleTokensAfter(initialDef, tokenVector, tokMatcher, maxLookAhead) {
var EXIT_NON_TERMINAL = "EXIT_NONE_TERMINAL";
// to avoid creating a new Array each time.
var EXIT_NON_TERMINAL_ARR = [EXIT_NON_TERMINAL];
var EXIT_ALTERNATIVE = "EXIT_ALTERNATIVE";
var foundCompletePath = false;
var tokenVectorLength = tokenVector.length;
var minimalAlternativesIndex = tokenVectorLength - maxLookAhead - 1;
var result = [];
var possiblePaths = [];
possiblePaths.push({
idx: -1,
def: initialDef,
ruleStack: [],
occurrenceStack: []
});
while (!utils_1.isEmpty(possiblePaths)) {
var currPath = possiblePaths.pop();
// skip alternatives if no more results can be found (assuming deterministic grammar with fixed lookahead)
if (currPath === EXIT_ALTERNATIVE) {
if (foundCompletePath &&
utils_1.last(possiblePaths).idx <= minimalAlternativesIndex) {
// remove irrelevant alternative
possiblePaths.pop();
}
continue;
}
var currDef = currPath.def;
var currIdx = currPath.idx;
var currRuleStack = currPath.ruleStack;
var currOccurrenceStack = currPath.occurrenceStack;
// For Example: an empty path could exist in a valid grammar in the case of an EMPTY_ALT
if (utils_1.isEmpty(currDef)) {
continue;
}
var prod = currDef[0];
/* istanbul ignore else */
if (prod === EXIT_NON_TERMINAL) {
var nextPath = {
idx: currIdx,
def: utils_1.drop(currDef),
ruleStack: utils_1.dropRight(currRuleStack),
occurrenceStack: utils_1.dropRight(currOccurrenceStack)
};
possiblePaths.push(nextPath);
}
else if (prod instanceof gast_public_1.Terminal) {
/* istanbul ignore else */
if (currIdx < tokenVectorLength - 1) {
var nextIdx = currIdx + 1;
var actualToken = tokenVector[nextIdx];
if (tokMatcher(actualToken, prod.terminalType)) {
var nextPath = {
idx: nextIdx,
def: utils_1.drop(currDef),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPath);
}
// end of the line
}
else if (currIdx === tokenVectorLength - 1) {
// IGNORE ABOVE ELSE
result.push({
nextTokenType: prod.terminalType,
nextTokenOccurrence: prod.idx,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
});
foundCompletePath = true;
}
else {
throw Error("non exhaustive match");
}
}
else if (prod instanceof gast_public_1.NonTerminal) {
var newRuleStack = utils_1.cloneArr(currRuleStack);
newRuleStack.push(prod.nonTerminalName);
var newOccurrenceStack = utils_1.cloneArr(currOccurrenceStack);
newOccurrenceStack.push(prod.idx);
var nextPath = {
idx: currIdx,
def: prod.definition.concat(EXIT_NON_TERMINAL_ARR, utils_1.drop(currDef)),
ruleStack: newRuleStack,
occurrenceStack: newOccurrenceStack
};
possiblePaths.push(nextPath);
}
else if (prod instanceof gast_public_1.Option) {
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
var nextPathWithout = {
idx: currIdx,
def: utils_1.drop(currDef),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPathWithout);
// required marker to avoid backtracking paths whose higher priority alternatives already matched
possiblePaths.push(EXIT_ALTERNATIVE);
var nextPathWith = {
idx: currIdx,
def: prod.definition.concat(utils_1.drop(currDef)),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPathWith);
}
else if (prod instanceof gast_public_1.RepetitionMandatory) {
// TODO:(THE NEW operators here take a while...) (convert once?)
var secondIteration = new gast_public_1.Repetition({
definition: prod.definition,
idx: prod.idx
});
var nextDef = prod.definition.concat([secondIteration], utils_1.drop(currDef));
var nextPath = {
idx: currIdx,
def: nextDef,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPath);
}
else if (prod instanceof gast_public_1.RepetitionMandatoryWithSeparator) {
// TODO:(THE NEW operators here take a while...) (convert once?)
var separatorGast = new gast_public_1.Terminal({
terminalType: prod.separator
});
var secondIteration = new gast_public_1.Repetition({
definition: [separatorGast].concat(prod.definition),
idx: prod.idx
});
var nextDef = prod.definition.concat([secondIteration], utils_1.drop(currDef));
var nextPath = {
idx: currIdx,
def: nextDef,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPath);
}
else if (prod instanceof gast_public_1.RepetitionWithSeparator) {
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
var nextPathWithout = {
idx: currIdx,
def: utils_1.drop(currDef),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPathWithout);
// required marker to avoid backtracking paths whose higher priority alternatives already matched
possiblePaths.push(EXIT_ALTERNATIVE);
var separatorGast = new gast_public_1.Terminal({
terminalType: prod.separator
});
var nthRepetition = new gast_public_1.Repetition({
definition: [separatorGast].concat(prod.definition),
idx: prod.idx
});
var nextDef = prod.definition.concat([nthRepetition], utils_1.drop(currDef));
var nextPathWith = {
idx: currIdx,
def: nextDef,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPathWith);
}
else if (prod instanceof gast_public_1.Repetition) {
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
var nextPathWithout = {
idx: currIdx,
def: utils_1.drop(currDef),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPathWithout);
// required marker to avoid backtracking paths whose higher priority alternatives already matched
possiblePaths.push(EXIT_ALTERNATIVE);
// TODO: an empty repetition will cause infinite loops here, will the parser detect this in selfAnalysis?
var nthRepetition = new gast_public_1.Repetition({
definition: prod.definition,
idx: prod.idx
});
var nextDef = prod.definition.concat([nthRepetition], utils_1.drop(currDef));
var nextPathWith = {
idx: currIdx,
def: nextDef,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPathWith);
}
else if (prod instanceof gast_public_1.Alternation) {
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
for (var i = prod.definition.length - 1; i >= 0; i--) {
var currAlt = prod.definition[i];
var currAltPath = {
idx: currIdx,
def: currAlt.definition.concat(utils_1.drop(currDef)),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(currAltPath);
possiblePaths.push(EXIT_ALTERNATIVE);
}
}
else if (prod instanceof gast_public_1.Flat) {
possiblePaths.push({
idx: currIdx,
def: prod.definition.concat(utils_1.drop(currDef)),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
});
}
else if (prod instanceof gast_public_1.Rule) {
// last because we should only encounter at most a single one of these per invocation.
possiblePaths.push(expandTopLevelRule(prod, currIdx, currRuleStack, currOccurrenceStack));
}
else {
throw Error("non exhaustive match");
}
}
return result;
}
exports.nextPossibleTokensAfter = nextPossibleTokensAfter;
function expandTopLevelRule(topRule, currIdx, currRuleStack, currOccurrenceStack) {
var newRuleStack = utils_1.cloneArr(currRuleStack);
newRuleStack.push(topRule.name);
var newCurrOccurrenceStack = utils_1.cloneArr(currOccurrenceStack);
// top rule is always assumed to have been called with occurrence index 1
newCurrOccurrenceStack.push(1);
return {
idx: currIdx,
def: topRule.definition,
ruleStack: newRuleStack,
occurrenceStack: newCurrOccurrenceStack
};
}
//# sourceMappingURL=interpreter.js.map