"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