559 lines
25 KiB
JavaScript
559 lines
25 KiB
JavaScript
"use strict";
|
|
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 (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
return function (d, b) {
|
|
if (typeof b !== "function" && b !== null)
|
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
})();
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.nextPossibleTokensAfter = exports.possiblePathsFrom = exports.NextTerminalAfterAtLeastOneSepWalker = exports.NextTerminalAfterAtLeastOneWalker = exports.NextTerminalAfterManySepWalker = exports.NextTerminalAfterManyWalker = exports.AbstractNextTerminalAfterProductionWalker = exports.NextAfterTokenWalker = exports.AbstractNextPossibleTokensWalker = void 0;
|
|
var rest_1 = require("./rest");
|
|
var first_1 = __importDefault(require("lodash/first"));
|
|
var isEmpty_1 = __importDefault(require("lodash/isEmpty"));
|
|
var dropRight_1 = __importDefault(require("lodash/dropRight"));
|
|
var drop_1 = __importDefault(require("lodash/drop"));
|
|
var last_1 = __importDefault(require("lodash/last"));
|
|
var forEach_1 = __importDefault(require("lodash/forEach"));
|
|
var clone_1 = __importDefault(require("lodash/clone"));
|
|
var first_2 = require("./first");
|
|
var gast_1 = require("@chevrotain/gast");
|
|
var AbstractNextPossibleTokensWalker = /** @class */ (function (_super) {
|
|
__extends(AbstractNextPossibleTokensWalker, _super);
|
|
function AbstractNextPossibleTokensWalker(topProd, path) {
|
|
var _this = _super.call(this) || 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 = (0, clone_1.default)(this.path.ruleStack).reverse(); // intelij bug requires assertion
|
|
this.occurrenceStack = (0, clone_1.default)(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 ((0, isEmpty_1.default)(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) || 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_1.Alternative({ definition: fullRest });
|
|
this.possibleTokTypes = (0, first_2.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) || 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) || this;
|
|
}
|
|
NextTerminalAfterManyWalker.prototype.walkMany = function (manyProd, currRest, prevRest) {
|
|
if (manyProd.idx === this.occurrence) {
|
|
var firstAfterMany = (0, first_1.default)(currRest.concat(prevRest));
|
|
this.result.isEndOfRule = firstAfterMany === undefined;
|
|
if (firstAfterMany instanceof gast_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) || this;
|
|
}
|
|
NextTerminalAfterManySepWalker.prototype.walkManySep = function (manySepProd, currRest, prevRest) {
|
|
if (manySepProd.idx === this.occurrence) {
|
|
var firstAfterManySep = (0, first_1.default)(currRest.concat(prevRest));
|
|
this.result.isEndOfRule = firstAfterManySep === undefined;
|
|
if (firstAfterManySep instanceof gast_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) || this;
|
|
}
|
|
NextTerminalAfterAtLeastOneWalker.prototype.walkAtLeastOne = function (atLeastOneProd, currRest, prevRest) {
|
|
if (atLeastOneProd.idx === this.occurrence) {
|
|
var firstAfterAtLeastOne = (0, first_1.default)(currRest.concat(prevRest));
|
|
this.result.isEndOfRule = firstAfterAtLeastOne === undefined;
|
|
if (firstAfterAtLeastOne instanceof gast_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) || this;
|
|
}
|
|
NextTerminalAfterAtLeastOneSepWalker.prototype.walkAtLeastOneSep = function (atleastOneSepProd, currRest, prevRest) {
|
|
if (atleastOneSepProd.idx === this.occurrence) {
|
|
var firstAfterfirstAfterAtLeastOneSep = (0, first_1.default)(currRest.concat(prevRest));
|
|
this.result.isEndOfRule = firstAfterfirstAfterAtLeastOneSep === undefined;
|
|
if (firstAfterfirstAfterAtLeastOneSep instanceof gast_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 = (0, clone_1.default)(currPath);
|
|
var result = [];
|
|
var i = 0;
|
|
// TODO: avoid inner funcs
|
|
function remainingPathWith(nextDef) {
|
|
return nextDef.concat((0, drop_1.default)(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_1.Alternative) {
|
|
return getAlternativesForProd(prod.definition);
|
|
}
|
|
else if (prod instanceof gast_1.NonTerminal) {
|
|
return getAlternativesForProd(prod.definition);
|
|
}
|
|
else if (prod instanceof gast_1.Option) {
|
|
result = getAlternativesForProd(prod.definition);
|
|
}
|
|
else if (prod instanceof gast_1.RepetitionMandatory) {
|
|
var newDef = prod.definition.concat([
|
|
new gast_1.Repetition({
|
|
definition: prod.definition
|
|
})
|
|
]);
|
|
return getAlternativesForProd(newDef);
|
|
}
|
|
else if (prod instanceof gast_1.RepetitionMandatoryWithSeparator) {
|
|
var newDef = [
|
|
new gast_1.Alternative({ definition: prod.definition }),
|
|
new gast_1.Repetition({
|
|
definition: [new gast_1.Terminal({ terminalType: prod.separator })].concat(prod.definition)
|
|
})
|
|
];
|
|
return getAlternativesForProd(newDef);
|
|
}
|
|
else if (prod instanceof gast_1.RepetitionWithSeparator) {
|
|
var newDef = prod.definition.concat([
|
|
new gast_1.Repetition({
|
|
definition: [new gast_1.Terminal({ terminalType: prod.separator })].concat(prod.definition)
|
|
})
|
|
]);
|
|
result = getAlternativesForProd(newDef);
|
|
}
|
|
else if (prod instanceof gast_1.Repetition) {
|
|
var newDef = prod.definition.concat([
|
|
new gast_1.Repetition({
|
|
definition: prod.definition
|
|
})
|
|
]);
|
|
result = getAlternativesForProd(newDef);
|
|
}
|
|
else if (prod instanceof gast_1.Alternation) {
|
|
(0, forEach_1.default)(prod.definition, function (currAlt) {
|
|
// TODO: this is a limited check for empty alternatives
|
|
// It would prevent a common case of infinite loops during parser initialization.
|
|
// However **in-directly** empty alternatives may still cause issues.
|
|
if ((0, isEmpty_1.default)(currAlt.definition) === false) {
|
|
result = getAlternativesForProd(currAlt.definition);
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
else if (prod instanceof gast_1.Terminal) {
|
|
currPath.push(prod.terminalType);
|
|
}
|
|
else {
|
|
throw Error("non exhaustive match");
|
|
}
|
|
i++;
|
|
}
|
|
result.push({
|
|
partialPath: currPath,
|
|
suffixDef: (0, drop_1.default)(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 (!(0, isEmpty_1.default)(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 &&
|
|
(0, last_1.default)(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 ((0, isEmpty_1.default)(currDef)) {
|
|
continue;
|
|
}
|
|
var prod = currDef[0];
|
|
/* istanbul ignore else */
|
|
if (prod === EXIT_NON_TERMINAL) {
|
|
var nextPath = {
|
|
idx: currIdx,
|
|
def: (0, drop_1.default)(currDef),
|
|
ruleStack: (0, dropRight_1.default)(currRuleStack),
|
|
occurrenceStack: (0, dropRight_1.default)(currOccurrenceStack)
|
|
};
|
|
possiblePaths.push(nextPath);
|
|
}
|
|
else if (prod instanceof gast_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: (0, drop_1.default)(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_1.NonTerminal) {
|
|
var newRuleStack = (0, clone_1.default)(currRuleStack);
|
|
newRuleStack.push(prod.nonTerminalName);
|
|
var newOccurrenceStack = (0, clone_1.default)(currOccurrenceStack);
|
|
newOccurrenceStack.push(prod.idx);
|
|
var nextPath = {
|
|
idx: currIdx,
|
|
def: prod.definition.concat(EXIT_NON_TERMINAL_ARR, (0, drop_1.default)(currDef)),
|
|
ruleStack: newRuleStack,
|
|
occurrenceStack: newOccurrenceStack
|
|
};
|
|
possiblePaths.push(nextPath);
|
|
}
|
|
else if (prod instanceof gast_1.Option) {
|
|
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
|
|
var nextPathWithout = {
|
|
idx: currIdx,
|
|
def: (0, drop_1.default)(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((0, drop_1.default)(currDef)),
|
|
ruleStack: currRuleStack,
|
|
occurrenceStack: currOccurrenceStack
|
|
};
|
|
possiblePaths.push(nextPathWith);
|
|
}
|
|
else if (prod instanceof gast_1.RepetitionMandatory) {
|
|
// TODO:(THE NEW operators here take a while...) (convert once?)
|
|
var secondIteration = new gast_1.Repetition({
|
|
definition: prod.definition,
|
|
idx: prod.idx
|
|
});
|
|
var nextDef = prod.definition.concat([secondIteration], (0, drop_1.default)(currDef));
|
|
var nextPath = {
|
|
idx: currIdx,
|
|
def: nextDef,
|
|
ruleStack: currRuleStack,
|
|
occurrenceStack: currOccurrenceStack
|
|
};
|
|
possiblePaths.push(nextPath);
|
|
}
|
|
else if (prod instanceof gast_1.RepetitionMandatoryWithSeparator) {
|
|
// TODO:(THE NEW operators here take a while...) (convert once?)
|
|
var separatorGast = new gast_1.Terminal({
|
|
terminalType: prod.separator
|
|
});
|
|
var secondIteration = new gast_1.Repetition({
|
|
definition: [separatorGast].concat(prod.definition),
|
|
idx: prod.idx
|
|
});
|
|
var nextDef = prod.definition.concat([secondIteration], (0, drop_1.default)(currDef));
|
|
var nextPath = {
|
|
idx: currIdx,
|
|
def: nextDef,
|
|
ruleStack: currRuleStack,
|
|
occurrenceStack: currOccurrenceStack
|
|
};
|
|
possiblePaths.push(nextPath);
|
|
}
|
|
else if (prod instanceof gast_1.RepetitionWithSeparator) {
|
|
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
|
|
var nextPathWithout = {
|
|
idx: currIdx,
|
|
def: (0, drop_1.default)(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_1.Terminal({
|
|
terminalType: prod.separator
|
|
});
|
|
var nthRepetition = new gast_1.Repetition({
|
|
definition: [separatorGast].concat(prod.definition),
|
|
idx: prod.idx
|
|
});
|
|
var nextDef = prod.definition.concat([nthRepetition], (0, drop_1.default)(currDef));
|
|
var nextPathWith = {
|
|
idx: currIdx,
|
|
def: nextDef,
|
|
ruleStack: currRuleStack,
|
|
occurrenceStack: currOccurrenceStack
|
|
};
|
|
possiblePaths.push(nextPathWith);
|
|
}
|
|
else if (prod instanceof gast_1.Repetition) {
|
|
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
|
|
var nextPathWithout = {
|
|
idx: currIdx,
|
|
def: (0, drop_1.default)(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_1.Repetition({
|
|
definition: prod.definition,
|
|
idx: prod.idx
|
|
});
|
|
var nextDef = prod.definition.concat([nthRepetition], (0, drop_1.default)(currDef));
|
|
var nextPathWith = {
|
|
idx: currIdx,
|
|
def: nextDef,
|
|
ruleStack: currRuleStack,
|
|
occurrenceStack: currOccurrenceStack
|
|
};
|
|
possiblePaths.push(nextPathWith);
|
|
}
|
|
else if (prod instanceof gast_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((0, drop_1.default)(currDef)),
|
|
ruleStack: currRuleStack,
|
|
occurrenceStack: currOccurrenceStack
|
|
};
|
|
possiblePaths.push(currAltPath);
|
|
possiblePaths.push(EXIT_ALTERNATIVE);
|
|
}
|
|
}
|
|
else if (prod instanceof gast_1.Alternative) {
|
|
possiblePaths.push({
|
|
idx: currIdx,
|
|
def: prod.definition.concat((0, drop_1.default)(currDef)),
|
|
ruleStack: currRuleStack,
|
|
occurrenceStack: currOccurrenceStack
|
|
});
|
|
}
|
|
else if (prod instanceof gast_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 = (0, clone_1.default)(currRuleStack);
|
|
newRuleStack.push(topRule.name);
|
|
var newCurrOccurrenceStack = (0, clone_1.default)(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
|