UNPKG

chevrotain

Version:

Chevrotain is a high performance fault tolerant javascript parsing DSL for building recursive decent parsers

1,252 lines 101 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var cache = require("./cache"); var cache_1 = require("./cache"); var exceptions_public_1 = require("./exceptions_public"); var lang_extensions_1 = require("../lang/lang_extensions"); var checks_1 = require("./grammar/checks"); var utils_1 = require("../utils/utils"); var follow_1 = require("./grammar/follow"); var tokens_public_1 = require("../scan/tokens_public"); var lookahead_1 = require("./grammar/lookahead"); var gast_builder_1 = require("./gast_builder"); var interpreter_1 = require("./grammar/interpreter"); var constants_1 = require("./constants"); var gast_1 = require("./grammar/gast/gast"); var tokens_1 = require("../scan/tokens"); var cst_1 = require("./cst/cst"); var keys_1 = require("./grammar/keys"); var cst_visitor_1 = require("./cst/cst_visitor"); var errors_public_1 = require("./errors_public"); var gast_public_1 = require("./grammar/gast/gast_public"); var gast_resolver_public_1 = require("./grammar/gast/gast_resolver_public"); var ParserDefinitionErrorType; (function (ParserDefinitionErrorType) { ParserDefinitionErrorType[ParserDefinitionErrorType["INVALID_RULE_NAME"] = 0] = "INVALID_RULE_NAME"; ParserDefinitionErrorType[ParserDefinitionErrorType["DUPLICATE_RULE_NAME"] = 1] = "DUPLICATE_RULE_NAME"; ParserDefinitionErrorType[ParserDefinitionErrorType["INVALID_RULE_OVERRIDE"] = 2] = "INVALID_RULE_OVERRIDE"; ParserDefinitionErrorType[ParserDefinitionErrorType["DUPLICATE_PRODUCTIONS"] = 3] = "DUPLICATE_PRODUCTIONS"; ParserDefinitionErrorType[ParserDefinitionErrorType["UNRESOLVED_SUBRULE_REF"] = 4] = "UNRESOLVED_SUBRULE_REF"; ParserDefinitionErrorType[ParserDefinitionErrorType["LEFT_RECURSION"] = 5] = "LEFT_RECURSION"; ParserDefinitionErrorType[ParserDefinitionErrorType["NONE_LAST_EMPTY_ALT"] = 6] = "NONE_LAST_EMPTY_ALT"; ParserDefinitionErrorType[ParserDefinitionErrorType["AMBIGUOUS_ALTS"] = 7] = "AMBIGUOUS_ALTS"; ParserDefinitionErrorType[ParserDefinitionErrorType["CONFLICT_TOKENS_RULES_NAMESPACE"] = 8] = "CONFLICT_TOKENS_RULES_NAMESPACE"; ParserDefinitionErrorType[ParserDefinitionErrorType["INVALID_TOKEN_NAME"] = 9] = "INVALID_TOKEN_NAME"; ParserDefinitionErrorType[ParserDefinitionErrorType["INVALID_NESTED_RULE_NAME"] = 10] = "INVALID_NESTED_RULE_NAME"; ParserDefinitionErrorType[ParserDefinitionErrorType["DUPLICATE_NESTED_NAME"] = 11] = "DUPLICATE_NESTED_NAME"; ParserDefinitionErrorType[ParserDefinitionErrorType["NO_NON_EMPTY_LOOKAHEAD"] = 12] = "NO_NON_EMPTY_LOOKAHEAD"; ParserDefinitionErrorType[ParserDefinitionErrorType["AMBIGUOUS_PREFIX_ALTS"] = 13] = "AMBIGUOUS_PREFIX_ALTS"; ParserDefinitionErrorType[ParserDefinitionErrorType["TOO_MANY_ALTS"] = 14] = "TOO_MANY_ALTS"; })(ParserDefinitionErrorType = exports.ParserDefinitionErrorType || (exports.ParserDefinitionErrorType = {})); var IN_RULE_RECOVERY_EXCEPTION = "InRuleRecoveryException"; exports.END_OF_FILE = tokens_public_1.createTokenInstance(tokens_public_1.EOF, "", NaN, NaN, NaN, NaN, NaN, NaN); Object.freeze(exports.END_OF_FILE); var DEFAULT_PARSER_CONFIG = Object.freeze({ recoveryEnabled: false, maxLookahead: 4, ignoredIssues: {}, dynamicTokensEnabled: false, // TODO: Document this breaking change, can it be mitigated? // TODO: change to true outputCst: false, errorMessageProvider: errors_public_1.defaultParserErrorProvider }); var DEFAULT_RULE_CONFIG = Object.freeze({ recoveryValueFunc: function () { return undefined; }, resyncEnabled: true }); /** * Convenience used to express an empty alternative in an OR (alternation). * can be used to more clearly describe the intent in a case of empty alternation. * * For example: * * 1. without using EMPTY_ALT: * * this.OR([ * {ALT: () => { * this.CONSUME1(OneTok) * return "1" * }}, * {ALT: () => { * this.CONSUME1(TwoTok) * return "2" * }}, * {ALT: () => { // implicitly empty because there are no invoked grammar rules (OR/MANY/CONSUME...) inside this alternative. * return "666" * }}, * ]) * * * 2. using EMPTY_ALT: * * this.OR([ * {ALT: () => { * this.CONSUME1(OneTok) * return "1" * }}, * {ALT: () => { * this.CONSUME1(TwoTok) * return "2" * }}, * {ALT: EMPTY_ALT("666")}, // explicitly empty, clearer intent * ]) * */ function EMPTY_ALT(value) { if (value === void 0) { value = undefined; } return function () { return value; }; } exports.EMPTY_ALT = EMPTY_ALT; var EOF_FOLLOW_KEY = {}; /** * A Recognizer capable of self analysis to determine it's grammar structure * This is used for more advanced features requiring such information. * For example: Error Recovery, Automatic lookahead calculation. */ var Parser = /** @class */ (function () { function Parser(input, tokenVocabulary, config) { if (config === void 0) { config = DEFAULT_PARSER_CONFIG; } this._errors = []; this.isBackTrackingStack = []; this.RULE_STACK = []; this.RULE_OCCURRENCE_STACK = []; this.CST_STACK = []; this.tokensMap = undefined; this.definedRulesNames = []; this.shortRuleNameToFull = new lang_extensions_1.HashTable(); this.fullRuleNameToShort = new lang_extensions_1.HashTable(); // The shortName Index must be coded "after" the first 8bits to enable building unique lookahead keys this.ruleShortNameIdx = 256; this.LAST_EXPLICIT_RULE_STACK = []; this.selfAnalysisDone = false; this.currIdx = -1; /** * Only used internally for storing productions as they are built for the first time. * The final productions should be accessed from the static cache. */ this._productions = new lang_extensions_1.HashTable(); this.input = input; // configuration this.recoveryEnabled = utils_1.has(config, "recoveryEnabled") ? config.recoveryEnabled : DEFAULT_PARSER_CONFIG.recoveryEnabled; // performance optimization, NOOP will be inlined which // effectively means that this optional feature does not exist // when not used. if (!this.recoveryEnabled) { this.attemptInRepetitionRecovery = utils_1.NOOP; } this.dynamicTokensEnabled = utils_1.has(config, "dynamicTokensEnabled") ? config.dynamicTokensEnabled : DEFAULT_PARSER_CONFIG.dynamicTokensEnabled; this.maxLookahead = utils_1.has(config, "maxLookahead") ? config.maxLookahead : DEFAULT_PARSER_CONFIG.maxLookahead; this.ignoredIssues = utils_1.has(config, "ignoredIssues") ? config.ignoredIssues : DEFAULT_PARSER_CONFIG.ignoredIssues; this.outputCst = utils_1.has(config, "outputCst") ? config.outputCst : DEFAULT_PARSER_CONFIG.outputCst; this.errorMessageProvider = utils_1.defaults(config.errorMessageProvider, DEFAULT_PARSER_CONFIG.errorMessageProvider); if (!this.outputCst) { this.cstInvocationStateUpdate = utils_1.NOOP; this.cstFinallyStateUpdate = utils_1.NOOP; this.cstPostTerminal = utils_1.NOOP; this.cstPostNonTerminal = utils_1.NOOP; this.getLastExplicitRuleShortName = this.getLastExplicitRuleShortNameNoCst; this.getPreviousExplicitRuleShortName = this.getPreviousExplicitRuleShortNameNoCst; this.getPreviousExplicitRuleOccurenceIndex = this.getPreviousExplicitRuleOccurenceIndexNoCst; this.manyInternal = this.manyInternalNoCst; this.orInternal = this.orInternalNoCst; this.optionInternal = this.optionInternalNoCst; this.atLeastOneInternal = this.atLeastOneInternalNoCst; this.manySepFirstInternal = this.manySepFirstInternalNoCst; this.atLeastOneSepFirstInternal = this.atLeastOneSepFirstInternalNoCst; } this.className = lang_extensions_1.classNameFromInstance(this); this.firstAfterRepMap = cache.getFirstAfterRepForClass(this.className); this.classLAFuncs = cache.getLookaheadFuncsForClass(this.className); this.cstDictDefForRule = cache.getCstDictDefPerRuleForClass(this.className); if (!cache.CLASS_TO_DEFINITION_ERRORS.containsKey(this.className)) { this.definitionErrors = []; cache.CLASS_TO_DEFINITION_ERRORS.put(this.className, this.definitionErrors); } else { this.definitionErrors = cache.CLASS_TO_DEFINITION_ERRORS.get(this.className); } if (utils_1.isArray(tokenVocabulary)) { this.tokensMap = utils_1.reduce(tokenVocabulary, function (acc, tokenClazz) { acc[tokens_public_1.tokenName(tokenClazz)] = tokenClazz; return acc; }, {}); } else if (utils_1.has(tokenVocabulary, "modes") && utils_1.every(utils_1.flatten(utils_1.values(tokenVocabulary.modes)), tokens_1.isTokenType)) { var allTokenTypes = utils_1.flatten(utils_1.values(tokenVocabulary.modes)); var uniqueTokens = utils_1.uniq(allTokenTypes); this.tokensMap = utils_1.reduce(uniqueTokens, function (acc, tokenClazz) { acc[tokens_public_1.tokenName(tokenClazz)] = tokenClazz; return acc; }, {}); } else if (utils_1.isObject(tokenVocabulary)) { this.tokensMap = utils_1.cloneObj(tokenVocabulary); } else { throw new Error("<tokensDictionary> argument must be An Array of Token constructors" + " A dictionary of Token constructors or an IMultiModeLexerDefinition"); } var noTokenCategoriesUsed = utils_1.every(utils_1.values(tokenVocabulary), function (tokenConstructor) { return utils_1.isEmpty(tokenConstructor.categoryMatches); }); this.tokenMatcher = noTokenCategoriesUsed ? tokens_1.tokenStructuredMatcherNoCategories : tokens_1.tokenStructuredMatcher; // always add EOF to the tokenNames -> constructors map. it is useful to assure all the input has been // parsed with a clear error message ("expecting EOF but found ...") /* tslint:disable */ this.tokensMap["EOF"] = tokens_public_1.EOF; /* tslint:enable */ // Because ES2015+ syntax should be supported for creating Token classes // We cannot assume that the Token classes were created using the "extendToken" utilities // Therefore we must augment the Token classes both on Lexer initialization and on Parser initialization tokens_1.augmentTokenTypes(utils_1.values(this.tokensMap)); } Parser.performSelfAnalysis = function (parserInstance) { var definitionErrors = []; var defErrorsMsgs; parserInstance.selfAnalysisDone = true; var className = lang_extensions_1.classNameFromInstance(parserInstance); // can't test this with nyc tool, instrumentation causes the class name to be not empty. /* istanbul ignore if */ if (className === "") { // just a simple "throw Error" without any fancy "definition error" because the logic below relies on a unique parser name to // save/access those definition errors... /* istanbul ignore next */ throw Error("A Parser's constructor may not be an anonymous Function, it must be a named function\n" + "The constructor's name is used at runtime for performance (caching) purposes."); } // this information should only be computed once if (!cache.CLASS_TO_SELF_ANALYSIS_DONE.containsKey(className)) { cache.CLASS_TO_SELF_ANALYSIS_DONE.put(className, true); var orgProductions_1 = parserInstance._productions; var clonedProductions_1 = new lang_extensions_1.HashTable(); // clone the grammar productions to support grammar inheritance. requirements: // 1. We want to avoid rebuilding the grammar every time so a cache for the productions is used. // 2. We need to collect the production from multiple grammars in an inheritance scenario during constructor invocation // so the myGast variable is used. // 3. If a Production has been overridden references to it in the GAST must also be updated. utils_1.forEach(orgProductions_1.keys(), function (key) { var value = orgProductions_1.get(key); clonedProductions_1.put(key, gast_1.cloneProduction(value)); }); cache.getProductionsForClass(className).putAll(clonedProductions_1); // assumes this cache has been initialized (in the relevant parser's constructor) // TODO: consider making the self analysis a member method to resolve this. // that way it won't be callable before the constructor has been invoked... definitionErrors = cache.CLASS_TO_DEFINITION_ERRORS.get(className); var resolverErrors = gast_resolver_public_1.resolveGrammar({ rules: clonedProductions_1.values() }); definitionErrors.push.apply(definitionErrors, resolverErrors); // mutability for the win? // only perform additional grammar validations IFF no resolving errors have occurred. // as unresolved grammar may lead to unhandled runtime exceptions in the follow up validations. if (utils_1.isEmpty(resolverErrors)) { var validationErrors = gast_resolver_public_1.validateGrammar({ rules: clonedProductions_1.values(), maxLookahead: parserInstance.maxLookahead, tokenTypes: utils_1.values(parserInstance.tokensMap), ignoredIssues: parserInstance.ignoredIssues, errMsgProvider: errors_public_1.defaultGrammarValidatorErrorProvider, grammarName: className }); definitionErrors.push.apply(definitionErrors, validationErrors); // mutability for the win? } if (!utils_1.isEmpty(definitionErrors) && !Parser.DEFER_DEFINITION_ERRORS_HANDLING) { defErrorsMsgs = utils_1.map(definitionErrors, function (defError) { return defError.message; }); throw new Error("Parser Definition Errors detected:\n " + defErrorsMsgs.join("\n-------------------------------\n")); } if (utils_1.isEmpty(definitionErrors)) { // this analysis may fail if the grammar is not perfectly valid var allFollows = follow_1.computeAllProdsFollows(clonedProductions_1.values()); cache.setResyncFollowsForClass(className, allFollows); } var cstAnalysisResult = cst_1.analyzeCst(clonedProductions_1.values(), parserInstance.fullRuleNameToShort); cache .getCstDictDefPerRuleForClass(className) .putAll(cstAnalysisResult.dictDef); cache.CLASS_TO_ALL_RULE_NAMES.put(className, cstAnalysisResult.allRuleNames); } // reThrow the validation errors each time an erroneous parser is instantiated if (!utils_1.isEmpty(cache.CLASS_TO_DEFINITION_ERRORS.get(className)) && !Parser.DEFER_DEFINITION_ERRORS_HANDLING) { defErrorsMsgs = utils_1.map(cache.CLASS_TO_DEFINITION_ERRORS.get(className), function (defError) { return defError.message; }); throw new Error("Parser Definition Errors detected:\n " + defErrorsMsgs.join("\n-------------------------------\n")); } }; Object.defineProperty(Parser.prototype, "errors", { get: function () { return utils_1.cloneArr(this._errors); }, set: function (newErrors) { this._errors = newErrors; }, enumerable: true, configurable: true }); /** * Resets the parser state, should be overridden for custom parsers which "carry" additional state. * When overriding, remember to also invoke the super implementation! */ Parser.prototype.reset = function () { this.resetLexerState(); this.isBackTrackingStack = []; this.errors = []; this.RULE_STACK = []; this.LAST_EXPLICIT_RULE_STACK = []; this.CST_STACK = []; this.RULE_OCCURRENCE_STACK = []; }; Parser.prototype.isAtEndOfInput = function () { return this.tokenMatcher(this.LA(1), tokens_public_1.EOF); }; Parser.prototype.getBaseCstVisitorConstructor = function () { var cachedConstructor = cache_1.CLASS_TO_BASE_CST_VISITOR.get(this.className); if (utils_1.isUndefined(cachedConstructor)) { var allRuleNames = cache_1.CLASS_TO_ALL_RULE_NAMES.get(this.className); cachedConstructor = cst_visitor_1.createBaseSemanticVisitorConstructor(this.className, allRuleNames); cache_1.CLASS_TO_BASE_CST_VISITOR.put(this.className, cachedConstructor); } return cachedConstructor; }; Parser.prototype.getBaseCstVisitorConstructorWithDefaults = function () { var cachedConstructor = cache_1.CLASS_TO_BASE_CST_VISITOR_WITH_DEFAULTS.get(this.className); if (utils_1.isUndefined(cachedConstructor)) { var allRuleNames = cache_1.CLASS_TO_ALL_RULE_NAMES.get(this.className); var baseConstructor = this.getBaseCstVisitorConstructor(); cachedConstructor = cst_visitor_1.createBaseVisitorConstructorWithDefaults(this.className, allRuleNames, baseConstructor); cache_1.CLASS_TO_BASE_CST_VISITOR_WITH_DEFAULTS.put(this.className, cachedConstructor); } return cachedConstructor; }; Parser.prototype.getGAstProductions = function () { return cache.getProductionsForClass(this.className); }; // This is more than a convenience method. // It is mostly used to draw the diagrams and having this method present on the parser instance // can avoid certain situations in which the serialization logic would fail due to multiple versions of chevrotain // bundled (due to multiple prototype chains and "instanceof" usage). Parser.prototype.getSerializedGastProductions = function () { return gast_public_1.serializeGrammar(cache.getProductionsForClass(this.className).values()); }; /** * @param startRuleName {string} * @param precedingInput {IToken[]} - The token vector up to (not including) the content assist point * @returns {ISyntacticContentAssistPath[]} */ Parser.prototype.computeContentAssist = function (startRuleName, precedingInput) { var startRuleGast = cache .getProductionsForClass(this.className) .get(startRuleName); if (utils_1.isUndefined(startRuleGast)) { throw Error("Rule ->" + startRuleName + "<- does not exist in this grammar."); } return interpreter_1.nextPossibleTokensAfter([startRuleGast], precedingInput, this.tokenMatcher, this.maxLookahead); }; Parser.prototype.isBackTracking = function () { return !utils_1.isEmpty(this.isBackTrackingStack); }; Parser.prototype.getCurrRuleFullName = function () { var shortName = this.getLastExplicitRuleShortName(); return this.shortRuleNameToFull.get(shortName); }; Parser.prototype.shortRuleNameToFullName = function (shortName) { return this.shortRuleNameToFull.get(shortName); }; Parser.prototype.getHumanReadableRuleStack = function () { var _this = this; if (!utils_1.isEmpty(this.LAST_EXPLICIT_RULE_STACK)) { return utils_1.map(this.LAST_EXPLICIT_RULE_STACK, function (currIdx) { return _this.shortRuleNameToFullName(_this.RULE_STACK[currIdx]); }); } else { return utils_1.map(this.RULE_STACK, function (currShortName) { return _this.shortRuleNameToFullName(currShortName); }); } }; Parser.prototype.SAVE_ERROR = function (error) { if (exceptions_public_1.isRecognitionException(error)) { error.context = { ruleStack: this.getHumanReadableRuleStack(), ruleOccurrenceStack: utils_1.cloneArr(this.RULE_OCCURRENCE_STACK) }; this._errors.push(error); return error; } else { throw Error("Trying to save an Error which is not a RecognitionException"); } }; /** * @param grammarRule - The rule to try and parse in backtracking mode. * @param args - argumens to be passed to the grammar rule execution * * @return {TokenType():boolean} a lookahead function that will try to parse the given grammarRule and will return true if succeed. */ Parser.prototype.BACKTRACK = function (grammarRule, args) { return function () { // save org state this.isBackTrackingStack.push(1); var orgState = this.saveRecogState(); try { grammarRule.apply(this, args); // if no exception was thrown we have succeed parsing the rule. return true; } catch (e) { if (exceptions_public_1.isRecognitionException(e)) { return false; } else { throw e; } } finally { this.reloadRecogState(orgState); this.isBackTrackingStack.pop(); } }; }; // Parsing DSL /** * Convenience method equivalent to CONSUME1. * @see CONSUME1 */ Parser.prototype.CONSUME = function (tokType, options) { return this.consumeInternal(tokType, 0, options); }; /** * * A Parsing DSL method use to consume a single terminal Token. * a Token will be consumed, IFF the next token in the token vector matches <tokType>. * otherwise the parser will attempt to perform error recovery. * * The index in the method name indicates the unique occurrence of a terminal consumption * inside a the top level rule. What this means is that if a terminal appears * more than once in a single rule, each appearance must have a difference index. * * for example: * * function parseQualifiedName() { * this.CONSUME1(Identifier); * this.MANY(()=> { * this.CONSUME1(Dot); * this.CONSUME2(Identifier); // <-- here we use CONSUME2 because the terminal * }); // 'Identifier' has already appeared previously in the * // the rule 'parseQualifiedName' * } * * @param {TokenType} tokType - The Type of the token to be consumed. * @param options - optional properties to modify the behavior of CONSUME. */ Parser.prototype.CONSUME1 = function (tokType, options) { return this.consumeInternal(tokType, 1, options); }; /** * @see CONSUME1 */ Parser.prototype.CONSUME2 = function (tokType, options) { return this.consumeInternal(tokType, 2, options); }; /** * @see CONSUME1 */ Parser.prototype.CONSUME3 = function (tokType, options) { return this.consumeInternal(tokType, 3, options); }; /** * @see CONSUME1 */ Parser.prototype.CONSUME4 = function (tokType, options) { return this.consumeInternal(tokType, 4, options); }; /** * @see CONSUME1 */ Parser.prototype.CONSUME5 = function (tokType, options) { return this.consumeInternal(tokType, 5, options); }; /** * @see CONSUME1 */ Parser.prototype.CONSUME6 = function (tokType, options) { return this.consumeInternal(tokType, 6, options); }; /** * @see CONSUME1 */ Parser.prototype.CONSUME7 = function (tokType, options) { return this.consumeInternal(tokType, 7, options); }; /** * @see CONSUME1 */ Parser.prototype.CONSUME8 = function (tokType, options) { return this.consumeInternal(tokType, 8, options); }; /** * @see CONSUME1 */ Parser.prototype.CONSUME9 = function (tokType, options) { return this.consumeInternal(tokType, 9, options); }; /** * Convenience method equivalent to SUBRULE1 * @see SUBRULE1 */ Parser.prototype.SUBRULE = function (ruleToCall, args) { if (args === void 0) { args = undefined; } return this.subruleInternal(ruleToCall, 0, args); }; /** * The Parsing DSL Method is used by one rule to call another. * * This may seem redundant as it does not actually do much. * However using it is mandatory for all sub rule invocations. * calling another rule without wrapping in SUBRULE(...) * will cause errors/mistakes in the Recognizer's self analysis, * which will lead to errors in error recovery/automatic lookahead calculation * and any other functionality relying on the Recognizer's self analysis * output. * * As in CONSUME the index in the method name indicates the occurrence * of the sub rule invocation in its rule. * * @param {TokenType} ruleToCall - The rule to invoke. * @param {*[]} args - The arguments to pass to the invoked subrule. * @returns {*} - The result of invoking ruleToCall. */ Parser.prototype.SUBRULE1 = function (ruleToCall, args) { if (args === void 0) { args = undefined; } return this.subruleInternal(ruleToCall, 1, args); }; /** * @see SUBRULE1 */ Parser.prototype.SUBRULE2 = function (ruleToCall, args) { if (args === void 0) { args = undefined; } return this.subruleInternal(ruleToCall, 2, args); }; /** * @see SUBRULE1 */ Parser.prototype.SUBRULE3 = function (ruleToCall, args) { if (args === void 0) { args = undefined; } return this.subruleInternal(ruleToCall, 3, args); }; /** * @see SUBRULE1 */ Parser.prototype.SUBRULE4 = function (ruleToCall, args) { if (args === void 0) { args = undefined; } return this.subruleInternal(ruleToCall, 4, args); }; /** * @see SUBRULE1 */ Parser.prototype.SUBRULE5 = function (ruleToCall, args) { if (args === void 0) { args = undefined; } return this.subruleInternal(ruleToCall, 5, args); }; /** * @see SUBRULE1 */ Parser.prototype.SUBRULE6 = function (ruleToCall, args) { if (args === void 0) { args = undefined; } return this.subruleInternal(ruleToCall, 6, args); }; /** * @see SUBRULE1 */ Parser.prototype.SUBRULE7 = function (ruleToCall, args) { if (args === void 0) { args = undefined; } return this.subruleInternal(ruleToCall, 7, args); }; /** * @see SUBRULE1 */ Parser.prototype.SUBRULE8 = function (ruleToCall, args) { if (args === void 0) { args = undefined; } return this.subruleInternal(ruleToCall, 8, args); }; /** * @see SUBRULE1 */ Parser.prototype.SUBRULE9 = function (ruleToCall, args) { if (args === void 0) { args = undefined; } return this.subruleInternal(ruleToCall, 9, args); }; /** * Convenience method equivalent to OPTION1. * @see OPTION1 */ Parser.prototype.OPTION = function (actionORMethodDef) { return this.optionInternal(actionORMethodDef, 0); }; /** * Parsing DSL Method that Indicates an Optional production * in EBNF notation: [...]. * * Note that there are two syntax forms: * - Passing the grammar action directly: * this.OPTION(()=> { * this.CONSUME(Digit)} * ); * * - using an "options" object: * this.OPTION({ * GATE:predicateFunc, * DEF: ()=>{ * this.CONSUME(Digit) * }}); * * The optional 'GATE' property in "options" object form can be used to add constraints * to invoking the grammar action. * * As in CONSUME the index in the method name indicates the occurrence * of the optional production in it's top rule. * * @param actionORMethodDef - The grammar action to optionally invoke once * or an "OPTIONS" object describing the grammar action and optional properties. * * @returns {OUT} */ Parser.prototype.OPTION1 = function (actionORMethodDef) { return this.optionInternal(actionORMethodDef, 1); }; /** * @see OPTION1 */ Parser.prototype.OPTION2 = function (actionORMethodDef) { return this.optionInternal(actionORMethodDef, 2); }; /** * @see OPTION1 */ Parser.prototype.OPTION3 = function (actionORMethodDef) { return this.optionInternal(actionORMethodDef, 3); }; /** * @see OPTION1 */ Parser.prototype.OPTION4 = function (actionORMethodDef) { return this.optionInternal(actionORMethodDef, 4); }; /** * @see OPTION1 */ Parser.prototype.OPTION5 = function (actionORMethodDef) { return this.optionInternal(actionORMethodDef, 5); }; /** * @see OPTION1 */ Parser.prototype.OPTION6 = function (actionORMethodDef) { return this.optionInternal(actionORMethodDef, 6); }; /** * @see OPTION1 */ Parser.prototype.OPTION7 = function (actionORMethodDef) { return this.optionInternal(actionORMethodDef, 7); }; /** * @see OPTION1 */ Parser.prototype.OPTION8 = function (actionORMethodDef) { return this.optionInternal(actionORMethodDef, 8); }; /** * @see OPTION1 */ Parser.prototype.OPTION9 = function (actionORMethodDef) { return this.optionInternal(actionORMethodDef, 9); }; /** * Convenience method equivalent to OR1. * @see OR1 */ Parser.prototype.OR = function (altsOrOpts) { return this.orInternal(altsOrOpts, 0); }; /** * Parsing DSL method that indicates a choice between a set of alternatives must be made. * This is equivalent to EBNF alternation (A | B | C | D ...) * * There are a couple of syntax forms for the inner alternatives array. * * Passing alternatives array directly: * this.OR([ * {ALT:()=>{this.CONSUME(One)}}, * {ALT:()=>{this.CONSUME(Two)}}, * {ALT:()=>{this.CONSUME(Three)}} * ]) * * Passing alternative array directly with predicates (GATE). * this.OR([ * {GATE: predicateFunc1, ALT:()=>{this.CONSUME(One)}}, * {GATE: predicateFuncX, ALT:()=>{this.CONSUME(Two)}}, * {GATE: predicateFuncX, ALT:()=>{this.CONSUME(Three)}} * ]) * * These syntax forms can also be mixed: * this.OR([ * {GATE: predicateFunc1, ALT:()=>{this.CONSUME(One)}}, * {ALT:()=>{this.CONSUME(Two)}}, * {ALT:()=>{this.CONSUME(Three)}} * ]) * * Additionally an "options" object may be used: * this.OR({ * DEF:[ * {ALT:()=>{this.CONSUME(One)}}, * {ALT:()=>{this.CONSUME(Two)}}, * {ALT:()=>{this.CONSUME(Three)}} * ], * // OPTIONAL property * ERR_MSG: "A Number" * }) * * The 'predicateFuncX' in the long form can be used to add constraints to choosing the alternative. * * As in CONSUME the index in the method name indicates the occurrence * of the alternation production in it's top rule. * * @param altsOrOpts - A set of alternatives or an "OPTIONS" object describing the alternatives and optional properties. * * @returns {*} - The result of invoking the chosen alternative. */ Parser.prototype.OR1 = function (altsOrOpts) { return this.orInternal(altsOrOpts, 1); }; /** * @see OR1 */ Parser.prototype.OR2 = function (altsOrOpts) { return this.orInternal(altsOrOpts, 2); }; /** * @see OR1 */ Parser.prototype.OR3 = function (altsOrOpts) { return this.orInternal(altsOrOpts, 3); }; /** * @see OR1 */ Parser.prototype.OR4 = function (altsOrOpts) { return this.orInternal(altsOrOpts, 4); }; /** * @see OR1 */ Parser.prototype.OR5 = function (altsOrOpts) { return this.orInternal(altsOrOpts, 5); }; /** * @see OR1 */ Parser.prototype.OR6 = function (altsOrOpts) { return this.orInternal(altsOrOpts, 6); }; /** * @see OR1 */ Parser.prototype.OR7 = function (altsOrOpts) { return this.orInternal(altsOrOpts, 7); }; /** * @see OR1 */ Parser.prototype.OR8 = function (altsOrOpts) { return this.orInternal(altsOrOpts, 8); }; /** * @see OR1 */ Parser.prototype.OR9 = function (altsOrOpts) { return this.orInternal(altsOrOpts, 9); }; /** * Convenience method equivalent to MANY1. * @see MANY1 */ Parser.prototype.MANY = function (actionORMethodDef) { return this.manyInternal(0, actionORMethodDef, []); }; /** * Parsing DSL method, that indicates a repetition of zero or more. * This is equivalent to EBNF repetition {...}. * * Note that there are two syntax forms: * - Passing the grammar action directly: * this.MANY(()=>{ * this.CONSUME(Comma) * this.CONSUME(Digit) * }) * * - using an "options" object: * this.MANY({ * GATE: predicateFunc, * DEF: () => { * this.CONSUME(Comma) * this.CONSUME(Digit) * } * }); * * The optional 'GATE' property in "options" object form can be used to add constraints * to invoking the grammar action. * * As in CONSUME the index in the method name indicates the occurrence * of the repetition production in it's top rule. * * @param {TokenType} actionORMethodDef - The grammar action to optionally invoke multiple times * or an "OPTIONS" object describing the grammar action and optional properties. * * @returns {OUT[]} */ Parser.prototype.MANY1 = function (actionORMethodDef) { return this.manyInternal(1, actionORMethodDef, []); }; /** * @see MANY1 */ Parser.prototype.MANY2 = function (actionORMethodDef) { return this.manyInternal(2, actionORMethodDef, []); }; /** * @see MANY1 */ Parser.prototype.MANY3 = function (actionORMethodDef) { return this.manyInternal(3, actionORMethodDef, []); }; /** * @see MANY1 */ Parser.prototype.MANY4 = function (actionORMethodDef) { return this.manyInternal(4, actionORMethodDef, []); }; /** * @see MANY1 */ Parser.prototype.MANY5 = function (actionORMethodDef) { return this.manyInternal(5, actionORMethodDef, []); }; /** * @see MANY1 */ Parser.prototype.MANY6 = function (actionORMethodDef) { return this.manyInternal(6, actionORMethodDef, []); }; /** * @see MANY1 */ Parser.prototype.MANY7 = function (actionORMethodDef) { return this.manyInternal(7, actionORMethodDef, []); }; /** * @see MANY1 */ Parser.prototype.MANY8 = function (actionORMethodDef) { return this.manyInternal(8, actionORMethodDef, []); }; /** * @see MANY1 */ Parser.prototype.MANY9 = function (actionORMethodDef) { return this.manyInternal(9, actionORMethodDef, []); }; /** * Convenience method equivalent to MANY_SEP1. * @see MANY_SEP1 */ Parser.prototype.MANY_SEP = function (options) { return this.manySepFirstInternal(0, options, { values: [], separators: [] }); }; /** * Parsing DSL method, that indicates a repetition of zero or more with a separator * Token between the repetitions. * * Example: * * this.MANY_SEP({ * SEP:Comma, * DEF: () => { * this.CONSUME(Number}; * ... * ); * }) * * Note that because this DSL method always requires more than one argument the options object is always required * and it is not possible to use a shorter form like in the MANY DSL method. * * Note that for the purposes of deciding on whether or not another iteration exists * Only a single Token is examined (The separator). Therefore if the grammar being implemented is * so "crazy" to require multiple tokens to identify an item separator please use the more basic DSL methods * to implement it. * * As in CONSUME the index in the method name indicates the occurrence * of the repetition production in it's top rule. * * Note that due to current limitations in the implementation the "SEP" property must appear BEFORE the "DEF" property. * * @param options - An object defining the grammar of each iteration and the separator between iterations * * @return {ISeparatedIterationResult<OUT>} */ Parser.prototype.MANY_SEP1 = function (options) { return this.manySepFirstInternal(1, options, { values: [], separators: [] }); }; /** * @see MANY_SEP1 */ Parser.prototype.MANY_SEP2 = function (options) { return this.manySepFirstInternal(2, options, { values: [], separators: [] }); }; /** * @see MANY_SEP1 */ Parser.prototype.MANY_SEP3 = function (options) { return this.manySepFirstInternal(3, options, { values: [], separators: [] }); }; /** * @see MANY_SEP1 */ Parser.prototype.MANY_SEP4 = function (options) { return this.manySepFirstInternal(4, options, { values: [], separators: [] }); }; /** * @see MANY_SEP1 */ Parser.prototype.MANY_SEP5 = function (options) { return this.manySepFirstInternal(5, options, { values: [], separators: [] }); }; /** * @see MANY_SEP1 */ Parser.prototype.MANY_SEP6 = function (options) { return this.manySepFirstInternal(6, options, { values: [], separators: [] }); }; /** * @see MANY_SEP1 */ Parser.prototype.MANY_SEP7 = function (options) { return this.manySepFirstInternal(7, options, { values: [], separators: [] }); }; /** * @see MANY_SEP1 */ Parser.prototype.MANY_SEP8 = function (options) { return this.manySepFirstInternal(8, options, { values: [], separators: [] }); }; /** * @see MANY_SEP1 */ Parser.prototype.MANY_SEP9 = function (options) { return this.manySepFirstInternal(9, options, { values: [], separators: [] }); }; /** * Convenience method equivalent to AT_LEAST_ONE1. * @see AT_LEAST_ONE1 */ Parser.prototype.AT_LEAST_ONE = function (actionORMethodDef) { return this.atLeastOneInternal(0, actionORMethodDef, []); }; /** * Convenience method, same as MANY but the repetition is of one or more. * failing to match at least one repetition will result in a parsing error and * cause a parsing error. * * @see MANY1 * * @param actionORMethodDef - The grammar action to optionally invoke multiple times * or an "OPTIONS" object describing the grammar action and optional properties. * * @return {OUT[]} */ Parser.prototype.AT_LEAST_ONE1 = function (actionORMethodDef) { return this.atLeastOneInternal(1, actionORMethodDef, []); }; /** * @see AT_LEAST_ONE1 */ Parser.prototype.AT_LEAST_ONE2 = function (actionORMethodDef) { return this.atLeastOneInternal(2, actionORMethodDef, []); }; /** * @see AT_LEAST_ONE1 */ Parser.prototype.AT_LEAST_ONE3 = function (actionORMethodDef) { return this.atLeastOneInternal(3, actionORMethodDef, []); }; /** * @see AT_LEAST_ONE1 */ Parser.prototype.AT_LEAST_ONE4 = function (actionORMethodDef) { return this.atLeastOneInternal(4, actionORMethodDef, []); }; /** * @see AT_LEAST_ONE1 */ Parser.prototype.AT_LEAST_ONE5 = function (actionORMethodDef) { return this.atLeastOneInternal(5, actionORMethodDef, []); }; /** * @see AT_LEAST_ONE1 */ Parser.prototype.AT_LEAST_ONE6 = function (actionORMethodDef) { return this.atLeastOneInternal(6, actionORMethodDef, []); }; /** * @see AT_LEAST_ONE1 */ Parser.prototype.AT_LEAST_ONE7 = function (actionORMethodDef) { return this.atLeastOneInternal(7, actionORMethodDef, []); }; /** * @see AT_LEAST_ONE1 */ Parser.prototype.AT_LEAST_ONE8 = function (actionORMethodDef) { return this.atLeastOneInternal(8, actionORMethodDef, []); }; /** * @see AT_LEAST_ONE1 */ Parser.prototype.AT_LEAST_ONE9 = function (actionORMethodDef) { return this.atLeastOneInternal(9, actionORMethodDef, []); }; /** * Convenience method equivalent to AT_LEAST_ONE_SEP1. * @see AT_LEAST_ONE1 */ Parser.prototype.AT_LEAST_ONE_SEP = function (options) { return this.atLeastOneSepFirstInternal(0, options, { values: [], separators: [] }); }; /** * Convenience method, same as MANY_SEP but the repetition is of one or more. * failing to match at least one repetition will result in a parsing error and * cause the parser to attempt error recovery. * * Note that an additional optional property ERR_MSG can be used to provide custom error messages. * * @see MANY_SEP1 * * @param options - An object defining the grammar of each iteration and the separator between iterations * * @return {ISeparatedIterationResult<OUT>} */ Parser.prototype.AT_LEAST_ONE_SEP1 = function (options) { return this.atLeastOneSepFirstInternal(1, options, { values: [], separators: [] }); }; /** * @see AT_LEAST_ONE_SEP1 */ Parser.prototype.AT_LEAST_ONE_SEP2 = function (options) { return this.atLeastOneSepFirstInternal(2, options, { values: [], separators: [] }); }; /** * @see AT_LEAST_ONE_SEP1 */ Parser.prototype.AT_LEAST_ONE_SEP3 = function (options) { return this.atLeastOneSepFirstInternal(3, options, { values: [], separators: [] }); }; /** * @see AT_LEAST_ONE_SEP1 */ Parser.prototype.AT_LEAST_ONE_SEP4 = function (options) { return this.atLeastOneSepFirstInternal(4, options, { values: [], separators: [] }); }; /** * @see AT_LEAST_ONE_SEP1 */ Parser.prototype.AT_LEAST_ONE_SEP5 = function (options) { return this.atLeastOneSepFirstInternal(5, options, { values: [], separators: [] }); }; /** * @see AT_LEAST_ONE_SEP1 */ Parser.prototype.AT_LEAST_ONE_SEP6 = function (options) { return this.atLeastOneSepFirstInternal(6, options, { values: [], separators: [] }); }; /** * @see AT_LEAST_ONE_SEP1 */ Parser.prototype.AT_LEAST_ONE_SEP7 = function (options) { return this.atLeastOneSepFirstInternal(7, options, { values: [], separators: [] }); }; /** * @see AT_LEAST_ONE_SEP1 */ Parser.prototype.AT_LEAST_ONE_SEP8 = function (options) { return this.atLeastOneSepFirstInternal(8, options, { values: [], separators: [] }); }; /** * @see AT_LEAST_ONE_SEP1 */ Parser.prototype.AT_LEAST_ONE_SEP9 = function (options) { return this.atLeastOneSepFirstInternal(9, options, { values: [], separators: [] }); }; /** * * @param {string} name - The name of the rule. * @param {TokenType} implementation - The implementation of the rule. * @param {IRuleConfig} [config] - The rule's optional configuration. * * @returns {TokenType} - The parsing rule which is the production implementation wrapped with the parsing logic that handles * Parser state / error recovery&reporting/ ... */ Parser.prototype.RULE = function (name, implementation, // TODO: how to describe the optional return type of CSTNode? T|CstNode is not good because it is not backward // compatible, T|any is very general... config) { // TODO: how to describe the optional return type of CSTNode? T|CstNode is not good because it is not backward // compatible, T|any is very general... if (config === void 0) { config = DEFAULT_RULE_CONFIG; } if (utils_1.contains(this.definedRulesNames, name)) { var errMsg = errors_public_1.defaultGrammarValidatorErrorProvider.buildDuplicateRuleNameError({ topLevelRule: name, grammarName: this.className }); var error = { message: errMsg, type: ParserDefinitionErrorType.DUPLICATE_RULE_NAME, ruleName: name }; this.definitionErrors.push(error); } this.definedRulesNames.push(name); // only build the gast representation once. if (!this._productions.containsKey(name)) { var gastProduction = gast_builder_1.buildTopProduction(implementation.toString(), name, this.tokensMap); this._productions.put(name, gastProduction); } else { var parserClassProductions = cache.getProductionsForClass(this.className); var cachedProduction = parserClassProductions.get(name); // in case of duplicate rules the cache will not be filled at this point. if (!utils_1.isUndefined(cachedProduction)) { // filling up the _productions is always needed to inheriting grammars can access it (as an instance member) // otherwise they will be unaware of productions defined in super grammars. this._productions.put(name, cachedProduction); } } var ruleImplementation = this.defineRule(name, implementation, config); this[name] = ruleImplementation; return ruleImplementation; }; /** * @See RULE * Same as RULE, but should only be used in "extending" grammars to override rules/productions * from the super grammar. */ Parser.prototype.OVERRIDE_RULE = function (name, impl, config) { if (config === void 0) { config = DEFAULT_RULE_CONFIG; } var ruleErrors = []; ruleErrors = ruleErrors.concat(checks_1.validateRuleIsOverridden(name, this.definedRulesNames, this.className)); this.definitionErrors.push.apply(this.definitionErrors, ruleErrors); // mutability for the win var alreadyOverridden = cache.getProductionOverriddenForClass(this.className); // only build the GAST of an overridden rule once. if (!alreadyOverridden.containsKey(name)) { alreadyOverridden.put(name, true); var gastProduction = gast_builder_1.buildTopProduction(impl.toString(), name, this.tokensMap); this._productions.put(name, gastProduction); } else { var parserClassProductions = cache.getProductionsForClass(this.className); // filling up the _productions is always needed to inheriting grammars can access it (as an instance member) // otherwise they will be unaware of productions defined in super grammars. this._productions.put(name, parserClassProductions.get(name)); } return this.defineRule(name, impl, config); }; Parser.prototype.ruleInvocationStateUpdate = function (shortName, fullName, idxInCallingRule) { this.RULE_OCCURRENCE_STACK.push(idxInCallingRule); this.RULE_STACK.push(shortName); // NOOP when cst is disabled this.cstInvocationStateUpdate(fullName, shortName); }; Parser.prototype.ruleFinallyStateUpdate = function () { this.RULE_STACK.pop(); this.RULE_OCCURRENCE_STACK.pop(); // NOOP when cst is disabled this.cstFinallyStateUpdate(); if (this.RULE_STACK.length === 0 && !this.isAtEndOfInput()) { var firstRedundantTok = this.LA(1); var errMsg = this.errorMessageProvider.buildNotAllInputParsedMessage({ firstRedundant: firstRedundantTok, ruleName: this.ge