"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