"use strict";
var cache = require("./cache");
var exceptions_public_1 = require("./exceptions_public");
var lang_extensions_1 = require("../lang/lang_extensions");
var resolver_1 = require("./grammar/resolver");
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");
(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";
})(exports.ParserDefinitionErrorType || (exports.ParserDefinitionErrorType = {}));
var ParserDefinitionErrorType = exports.ParserDefinitionErrorType;
var DEFAULT_PARSER_CONFIG = Object.freeze({
    recoveryEnabled: false
});
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 = (function () {
    function Parser(input, tokensMapOrArr, config) {
        if (config === void 0) { config = DEFAULT_PARSER_CONFIG; }
        this.errors = [];
        this._input = [];
        this.inputIdx = -1;
        this.isBackTrackingStack = [];
        this.RULE_STACK = [];
        this.RULE_OCCURRENCE_STACK = [];
        this.tokensMap = undefined;
        this.definedRulesNames = [];
        /**
         * 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;
        this.recoveryEnabled = utils_1.has(config, "recoveryEnabled") ? config.recoveryEnabled : DEFAULT_PARSER_CONFIG.recoveryEnabled;
        this.className = lang_extensions_1.classNameFromInstance(this);
        this.firstAfterRepMap = cache.getFirstAfterRepForClass(this.className);
        this.classLAFuncs = cache.getLookaheadFuncsForClass(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(tokensMapOrArr)) {
            this.tokensMap = utils_1.reduce(tokensMapOrArr, function (acc, tokenClazz) {
                acc[tokens_public_1.tokenName(tokenClazz)] = tokenClazz;
                return acc;
            }, {});
        }
        else if (utils_1.isObject(tokensMapOrArr)) {
            this.tokensMap = utils_1.cloneObj(tokensMapOrArr);
        }
        else {
            throw new Error("'tokensMapOrArr' argument must be An Array of Token constructors or a Dictionary of Tokens.");
        }
        // 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 ...")
        this.tokensMap[tokens_public_1.tokenName(tokens_public_1.EOF)] = tokens_public_1.EOF;
        if (cache.CLASS_TO_OR_LA_CACHE[this.className] === undefined) {
            cache.initLookAheadKeyCache(this.className);
        }
        this.orLookaheadKeys = cache.CLASS_TO_OR_LA_CACHE[this.className];
        this.manyLookaheadKeys = cache.CLASS_TO_MANY_LA_CACHE[this.className];
        this.manySepLookaheadKeys = cache.CLASS_TO_MANY_SEP_LA_CACHE[this.className];
        this.atLeastOneLookaheadKeys = cache.CLASS_TO_AT_LEAST_ONE_LA_CACHE[this.className];
        this.atLeastOneSepLookaheadKeys = cache.CLASS_TO_AT_LEAST_ONE_SEP_LA_CACHE[this.className];
        this.optionLookaheadKeys = cache.CLASS_TO_OPTION_LA_CACHE[this.className];
    }
    Parser.performSelfAnalysis = function (classInstance) {
        var definitionErrors = [];
        var defErrorsMsgs;
        var className = lang_extensions_1.classNameFromInstance(classInstance);
        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...
            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)) {
            // clone here
            var orgProductions_1 = classInstance._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 = resolver_1.resolveGrammar(clonedProductions_1);
            definitionErrors.push.apply(definitionErrors, resolverErrors); // mutability for the win?
            cache.CLASS_TO_SELF_ANALYSIS_DONE.put(className, true);
            var validationErrors = checks_1.validateGrammar(clonedProductions_1.values());
            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)) {
                var allFollows = follow_1.computeAllProdsFollows(clonedProductions_1.values());
                cache.setResyncFollowsForClass(className, allFollows);
            }
        }
        // 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, "input", {
        get: function () {
            return utils_1.cloneArr(this._input);
        },
        set: function (newInput) {
            this.reset();
            this._input = newInput;
        },
        enumerable: true,
        configurable: true
    });
    Parser.prototype.reset = function () {
        this.isBackTrackingStack = [];
        this.errors = [];
        this._input = [];
        this.inputIdx = -1;
        this.RULE_STACK = [];
        this.RULE_OCCURRENCE_STACK = [];
    };
    Parser.prototype.isAtEndOfInput = function () {
        return this.LA(1) instanceof tokens_public_1.EOF;
    };
    Parser.prototype.getGAstProductions = function () {
        return cache.getProductionsForClass(this.className);
    };
    Parser.prototype.isBackTracking = function () {
        return !(utils_1.isEmpty(this.isBackTrackingStack));
    };
    Parser.prototype.SAVE_ERROR = function (error) {
        if (exceptions_public_1.exceptions.isRecognitionException(error)) {
            error.context = {
                ruleStack: utils_1.cloneArr(this.RULE_STACK),
                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");
        }
    };
    Parser.prototype.NEXT_TOKEN = function () {
        return this.LA(1);
    };
    Parser.prototype.LA = function (howMuch) {
        if (this._input.length <= this.inputIdx + howMuch) {
            return new tokens_public_1.EOF();
        }
        else {
            return this._input[this.inputIdx + howMuch];
        }
    };
    Parser.prototype.isNextRule = function (ruleName) {
        var classLAFuncs = cache.getLookaheadFuncsForClass(this.className);
        var condition = classLAFuncs.get(ruleName);
        if (condition === undefined) {
            var ruleGrammar = this.getGAstProductions().get(ruleName);
            condition = lookahead_1.buildLookaheadForTopLevel(ruleGrammar);
            classLAFuncs.put(ruleName, condition);
        }
        return condition.call(this);
    };
    /**
     *
     * @param grammarRule the rule to try and parse in backtracking mode
     * @param isValid a predicate that given the result of the parse attempt will "decide" if the parse was successfully or not
     * @return a lookahead function that will try to parse the given grammarRule and will return true if succeed
     */
    Parser.prototype.BACKTRACK = function (grammarRule, isValid) {
        var _this = this;
        return function () {
            // save org state
            _this.isBackTrackingStack.push(1);
            var orgState = _this.saveRecogState();
            try {
                var ruleResult = grammarRule.call(_this);
                return isValid(ruleResult);
            }
            catch (e) {
                if (exceptions_public_1.exceptions.isRecognitionException(e)) {
                    return false;
                }
                else {
                    throw e;
                }
            }
            finally {
                _this.reloadRecogState(orgState);
                _this.isBackTrackingStack.pop();
            }
        };
    };
    // skips a token and returns the next token
    Parser.prototype.SKIP_TOKEN = function () {
        // example: assume 45 tokens in the input, if input index is 44 it means that NEXT_TOKEN will return
        // input[45] which is the 46th item and no longer exists,
        // so in this case the largest valid input index is 43 (input.length - 2 )
        if (this.inputIdx <= this._input.length - 2) {
            this.inputIdx++;
            return this.NEXT_TOKEN();
        }
        else {
            return new tokens_public_1.EOF();
        }
    };
    // Parsing DSL
    /**
     * Convenience method equivalent to CONSUME1
     * @see CONSUME1
     */
    Parser.prototype.CONSUME = function (tokClass) {
        return this.CONSUME1(tokClass);
    };
    /**
     *
     * A Parsing DSL method use to consume a single terminal Token.
     * a Token will be consumed, IFF the next token in the token vector is an instanceof tokClass.
     * 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 {Function} tokClass A constructor function specifying the type of token
     *        to be consumed.
     *
     * @returns {Token} The consumed token.
     */
    Parser.prototype.CONSUME1 = function (tokClass) {
        return this.consumeInternal(tokClass, 1);
    };
    /**
     * @see CONSUME1
     */
    Parser.prototype.CONSUME2 = function (tokClass) {
        return this.consumeInternal(tokClass, 2);
    };
    /**
     * @see CONSUME1
     */
    Parser.prototype.CONSUME3 = function (tokClass) {
        return this.consumeInternal(tokClass, 3);
    };
    /**
     * @see CONSUME1
     */
    Parser.prototype.CONSUME4 = function (tokClass) {
        return this.consumeInternal(tokClass, 4);
    };
    /**
     * @see CONSUME1
     */
    Parser.prototype.CONSUME5 = function (tokClass) {
        return this.consumeInternal(tokClass, 5);
    };
    /**
     * Convenience method equivalent to SUBRULE1
     * @see SUBRULE1
     */
    Parser.prototype.SUBRULE = function (ruleToCall, args) {
        if (args === void 0) { args = []; }
        return this.SUBRULE1(ruleToCall, 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 {Function} 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 = []; }
        return ruleToCall.call(this, 1, args);
    };
    /**
     * @see SUBRULE1
     */
    Parser.prototype.SUBRULE2 = function (ruleToCall, args) {
        if (args === void 0) { args = []; }
        return ruleToCall.call(this, 2, args);
    };
    /**
     * @see SUBRULE1
     */
    Parser.prototype.SUBRULE3 = function (ruleToCall, args) {
        if (args === void 0) { args = []; }
        return ruleToCall.call(this, 3, args);
    };
    /**
     * @see SUBRULE1
     */
    Parser.prototype.SUBRULE4 = function (ruleToCall, args) {
        if (args === void 0) { args = []; }
        return ruleToCall.call(this, 4, args);
    };
    /**
     * @see SUBRULE1
     */
    Parser.prototype.SUBRULE5 = function (ruleToCall, args) {
        if (args === void 0) { args = []; }
        return ruleToCall.call(this, 5, args);
    };
    /**
     * Convenience method equivalent to OPTION1
     * @see OPTION1
     */
    Parser.prototype.OPTION = function (laFuncOrAction, action) {
        return this.OPTION1.call(this, laFuncOrAction, action);
    };
    /**
     * Parsing DSL Method that Indicates an Optional production
     * in EBNF notation: [...]
     *
     * note that the 'action' param is optional. so both of the following forms are valid:
     *
     * short: this.OPTION(()=>{ this.CONSUME(Digit});
     * long: this.OPTION(isDigit, ()=>{ this.CONSUME(Digit});
     *
     * using the short form is recommended as it will compute the lookahead function
     * automatically. however this currently has one limitation:
     * It only works if the lookahead for the grammar is one.
     *
     * As in CONSUME the index in the method name indicates the occurrence
     * of the optional production in it's top rule.
     *
     * @param {Function} laFuncOrAction The lookahead function that 'decides'
     *                                  whether or not the OPTION's action will be
     *                                  invoked or the action to optionally invoke
     * @param {Function} [action] The action to optionally invoke.
     *
     * @returns {boolean} true iff the OPTION's action has been invoked
     */
    Parser.prototype.OPTION1 = function (laFuncOrAction, action) {
        if (action === undefined) {
            action = laFuncOrAction;
            laFuncOrAction = this.getLookaheadFuncForOption(1);
        }
        return this.optionInternal(laFuncOrAction, action);
    };
    /**
     * @see OPTION1
     */
    Parser.prototype.OPTION2 = function (laFuncOrAction, action) {
        if (action === undefined) {
            action = laFuncOrAction;
            laFuncOrAction = this.getLookaheadFuncForOption(2);
        }
        return this.optionInternal(laFuncOrAction, action);
    };
    /**
     * @see OPTION1
     */
    Parser.prototype.OPTION3 = function (laFuncOrAction, action) {
        if (action === undefined) {
            action = laFuncOrAction;
            laFuncOrAction = this.getLookaheadFuncForOption(3);
        }
        return this.optionInternal(laFuncOrAction, action);
    };
    /**
     * @see OPTION1
     */
    Parser.prototype.OPTION4 = function (laFuncOrAction, action) {
        if (action === undefined) {
            action = laFuncOrAction;
            laFuncOrAction = this.getLookaheadFuncForOption(4);
        }
        return this.optionInternal(laFuncOrAction, action);
    };
    /**
     * @see OPTION1
     */
    Parser.prototype.OPTION5 = function (laFuncOrAction, action) {
        if (action === undefined) {
            action = laFuncOrAction;
            laFuncOrAction = this.getLookaheadFuncForOption(5);
        }
        return this.optionInternal(laFuncOrAction, action);
    };
    /**
     * Convenience method equivalent to OR1
     * @see OR1
     */
    Parser.prototype.OR = function (alts, errMsgTypes, ignoreAmbiguities) {
        if (ignoreAmbiguities === void 0) { ignoreAmbiguities = false; }
        return this.OR1(alts, errMsgTypes, ignoreAmbiguities);
    };
    /**
     * 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 two forms:
     *
     * short: this.OR([
     *           {ALT:()=>{this.CONSUME(One)}},
     *           {ALT:()=>{this.CONSUME(Two)}},
     *           {ALT:()=>{this.CONSUME(Three)}},
     *        ], "a number")
     *
     * long: this.OR([
     *           {WHEN: isOne, THEN_DO:()=>{this.CONSUME(One)}},
     *           {WHEN: isTwo, THEN_DO:()=>{this.CONSUME(Two)}},
     *           {WHEN: isThree, THEN_DO:()=>{this.CONSUME(Three)}},
     *        ], "a number")
     *
     * using the short form is recommended as it will compute the lookahead function
     * automatically. however this currently has one limitation:
     * It only works if the lookahead for the grammar is one LL(1).
     *
     * As in CONSUME the index in the method name indicates the occurrence
     * of the alternation production in it's top rule.
     *
     * @param {{ALT:Function}[] | {WHEN:Function, THEN_DO:Function}[]} alts - An array of alternatives
     *
     * @param {string} [errMsgTypes] - A description for the alternatives used in error messages
     *                                 If none is provided, the error message will include the names of the expected
     *                                 Tokens which may start each alternative.
     *
     * @param {boolean} [ignoreAmbiguities] - if true this will ignore ambiguities caused when two alternatives can not
     *        be distinguished by a lookahead of one. enabling this means the first alternative
     *        that matches will be taken. This is sometimes the grammar's intent.
     *        * only enable this if you know what you are doing!
     *
     * @returns {*} The result of invoking the chosen alternative

     */
    Parser.prototype.OR1 = function (alts, errMsgTypes, ignoreAmbiguities) {
        if (ignoreAmbiguities === void 0) { ignoreAmbiguities = false; }
        return this.orInternal(alts, errMsgTypes, 1, ignoreAmbiguities);
    };
    /**
     * @see OR1
     */
    Parser.prototype.OR2 = function (alts, errMsgTypes, ignoreAmbiguities) {
        if (ignoreAmbiguities === void 0) { ignoreAmbiguities = false; }
        return this.orInternal(alts, errMsgTypes, 2, ignoreAmbiguities);
    };
    /**
     * @see OR1
     */
    Parser.prototype.OR3 = function (alts, errMsgTypes, ignoreAmbiguities) {
        if (ignoreAmbiguities === void 0) { ignoreAmbiguities = false; }
        return this.orInternal(alts, errMsgTypes, 3, ignoreAmbiguities);
    };
    /**
     * @see OR1
     */
    Parser.prototype.OR4 = function (alts, errMsgTypes, ignoreAmbiguities) {
        if (ignoreAmbiguities === void 0) { ignoreAmbiguities = false; }
        return this.orInternal(alts, errMsgTypes, 4, ignoreAmbiguities);
    };
    /**
     * @see OR1
     */
    Parser.prototype.OR5 = function (alts, errMsgTypes, ignoreAmbiguities) {
        if (ignoreAmbiguities === void 0) { ignoreAmbiguities = false; }
        return this.orInternal(alts, errMsgTypes, 5, ignoreAmbiguities);
    };
    /**
     * Convenience method equivalent to MANY1
     * @see MANY1
     */
    Parser.prototype.MANY = function (laFuncOrAction, action) {
        return this.MANY1.call(this, laFuncOrAction, action);
    };
    /**
     * Parsing DSL method, that indicates a repetition of zero or more.
     * This is equivalent to EBNF repetition {...}
     *
     * note that the 'action' param is optional. so both of the following forms are valid:
     *
     * short: this.MANY(()=>{
     *                       this.CONSUME(Comma};
     *                       this.CONSUME(Digit});
     * long: this.MANY(isComma, ()=>{
     *                       this.CONSUME(Comma};
     *                       this.CONSUME(Digit});
     *
     * using the short form is recommended as it will compute the lookahead function
     * automatically. however this currently has one limitation:
     * It only works if the lookahead for the grammar is one.
     *
     * As in CONSUME the index in the method name indicates the occurrence
     * of the repetition production in it's top rule.
     *
     * @param {Function} laFuncOrAction The lookahead function that 'decides'
     *                                  whether or not the MANY's action will be
     *                                  invoked or the action to optionally invoke
     * @param {Function} [action] The action to optionally invoke.
     */
    Parser.prototype.MANY1 = function (laFuncOrAction, action) {
        this.manyInternal(this.MANY1, "MANY1", 1, laFuncOrAction, action);
    };
    /**
     * @see MANY1
     */
    Parser.prototype.MANY2 = function (laFuncOrAction, action) {
        this.manyInternal(this.MANY2, "MANY2", 2, laFuncOrAction, action);
    };
    /**
     * @see MANY1
     */
    Parser.prototype.MANY3 = function (laFuncOrAction, action) {
        this.manyInternal(this.MANY3, "MANY3", 3, laFuncOrAction, action);
    };
    /**
     * @see MANY1
     */
    Parser.prototype.MANY4 = function (laFuncOrAction, action) {
        this.manyInternal(this.MANY4, "MANY4", 4, laFuncOrAction, action);
    };
    /**
     * @see MANY1
     */
    Parser.prototype.MANY5 = function (laFuncOrAction, action) {
        this.manyInternal(this.MANY5, "MANY5", 5, laFuncOrAction, action);
    };
    /**
     * Convenience method equivalent to MANY_SEP1
     * @see MANY_SEP1
     */
    Parser.prototype.MANY_SEP = function (separator, laFuncOrAction, action) {
        return this.MANY_SEP1.call(this, separator, laFuncOrAction, action);
    };
    /**
     * Parsing DSL method, that indicates a repetition of zero or more with a separator
     * Token between the repetitions.
     *
     * note that the 'action' param is optional. so both of the following forms are valid:
     *
     * short: this.MANY_SEP(Comma, ()=>{
     *                          this.CONSUME(Number};
     *                       ...
     *                       );
     *
     * long: this.MANY(Comma, isNumber, ()=>{
     *                           this.CONSUME(Number}
     *                       ...
     *                       );
     *
     * using the short form is recommended as it will compute the lookahead function
     * (for the first iteration) automatically. however this currently has one limitation:
     * It only works if the lookahead for the grammar is one.
     *
     * As in CONSUME the index in the method name indicates the occurrence
     * of the repetition production in it's top rule.
     *
     * @param separator - The Token to use as a separator between repetitions.
     * @param {Function} laFuncOrAction - The lookahead function that 'decides'
     *                                  whether or not the MANY_SEP's action will be
     *                                  invoked or the action to optionally invoke
     * @param {Function} [action] - The action to optionally invoke.
     *
     * @return {Token[]} - The consumed separator Tokens.
     */
    Parser.prototype.MANY_SEP1 = function (separator, laFuncOrAction, action) {
        return this.manySepFirstInternal(this.MANY_SEP1, "MANY_SEP1", 1, separator, laFuncOrAction, action);
    };
    /**
     * @see MANY_SEP1
     */
    Parser.prototype.MANY_SEP2 = function (separator, laFuncOrAction, action) {
        return this.manySepFirstInternal(this.MANY_SEP2, "MANY_SEP2", 2, separator, laFuncOrAction, action);
    };
    /**
     * @see MANY_SEP1
     */
    Parser.prototype.MANY_SEP3 = function (separator, laFuncOrAction, action) {
        return this.manySepFirstInternal(this.MANY_SEP3, "MANY_SEP3", 3, separator, laFuncOrAction, action);
    };
    /**
     * @see MANY_SEP1
     */
    Parser.prototype.MANY_SEP4 = function (separator, laFuncOrAction, action) {
        return this.manySepFirstInternal(this.MANY_SEP4, "MANY_SEP4", 4, separator, laFuncOrAction, action);
    };
    /**
     * @see MANY_SEP1
     */
    Parser.prototype.MANY_SEP5 = function (separator, laFuncOrAction, action) {
        return this.manySepFirstInternal(this.MANY_SEP5, "MANY_SEP5", 5, separator, laFuncOrAction, action);
    };
    /**
     * Convenience method equivalent to AT_LEAST_ONE1
     * @see AT_LEAST_ONE1
     */
    Parser.prototype.AT_LEAST_ONE = function (laFuncOrAction, action, errMsg) {
        return this.AT_LEAST_ONE1.call(this, laFuncOrAction, action, errMsg);
    };
    /**
     *
     * 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 the parser to attempt error recovery.
     *
     * @see MANY1
     *
     * @param {Function} laFuncOrAction The lookahead function that 'decides'
     *                                  whether or not the AT_LEAST_ONE's action will be
     *                                  invoked or the action to optionally invoke
     * @param {Function} [action] The action to optionally invoke.
     * @param {string} [errMsg] short title/classification to what is being matched
     */
    Parser.prototype.AT_LEAST_ONE1 = function (laFuncOrAction, action, errMsg) {
        this.atLeastOneInternal(this.AT_LEAST_ONE1, "AT_LEAST_ONE1", 1, laFuncOrAction, action, errMsg);
    };
    /**
     * @see AT_LEAST_ONE1
     */
    Parser.prototype.AT_LEAST_ONE2 = function (laFuncOrAction, action, errMsg) {
        this.atLeastOneInternal(this.AT_LEAST_ONE2, "AT_LEAST_ONE2", 2, laFuncOrAction, action, errMsg);
    };
    /**
     * @see AT_LEAST_ONE1
     */
    Parser.prototype.AT_LEAST_ONE3 = function (laFuncOrAction, action, errMsg) {
        this.atLeastOneInternal(this.AT_LEAST_ONE3, "AT_LEAST_ONE3", 3, laFuncOrAction, action, errMsg);
    };
    /**
     * @see AT_LEAST_ONE1
     */
    Parser.prototype.AT_LEAST_ONE4 = function (laFuncOrAction, action, errMsg) {
        this.atLeastOneInternal(this.AT_LEAST_ONE4, "AT_LEAST_ONE4", 4, laFuncOrAction, action, errMsg);
    };
    /**
     * @see AT_LEAST_ONE1
     */
    Parser.prototype.AT_LEAST_ONE5 = function (laFuncOrAction, action, errMsg) {
        this.atLeastOneInternal(this.AT_LEAST_ONE5, "AT_LEAST_ONE5", 5, laFuncOrAction, action, errMsg);
    };
    /**
     * Convenience method equivalent to AT_LEAST_ONE_SEP1
     * @see AT_LEAST_ONE1
     */
    Parser.prototype.AT_LEAST_ONE_SEP = function (separator, laFuncOrAction, action, errMsg) {
        return this.AT_LEAST_ONE_SEP1.call(this, separator, laFuncOrAction, action, errMsg);
    };
    /**
     *
     * 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.
     *
     * @see MANY_SEP1
     *
     * @param separator {Token}
     * @param {Function} laFuncOrAction The lookahead function that 'decides'
     *                                  whether or not the AT_LEAST_ONE's action will be
     *                                  invoked or the action to optionally invoke
     * @param {Function} [action] The action to optionally invoke.
     * @param {string} [errMsg] short title/classification to what is being matched
     */
    Parser.prototype.AT_LEAST_ONE_SEP1 = function (separator, laFuncOrAction, action, errMsg) {
        return this.atLeastOneSepFirstInternal(this.atLeastOneSepFirstInternal, "AT_LEAST_ONE_SEP1", 1, separator, laFuncOrAction, action, errMsg);
    };
    /**
     * @see AT_LEAST_ONE_SEP1
     */
    Parser.prototype.AT_LEAST_ONE_SEP2 = function (separator, laFuncOrAction, action, errMsg) {
        return this.atLeastOneSepFirstInternal(this.atLeastOneSepFirstInternal, "AT_LEAST_ONE_SEP2", 2, separator, laFuncOrAction, action, errMsg);
    };
    /**
     * @see AT_LEAST_ONE_SEP1
     */
    Parser.prototype.AT_LEAST_ONE_SEP3 = function (separator, laFuncOrAction, action, errMsg) {
        return this.atLeastOneSepFirstInternal(this.atLeastOneSepFirstInternal, "AT_LEAST_ONE_SEP3", 3, separator, laFuncOrAction, action, errMsg);
    };
    /**
     * @see AT_LEAST_ONE_SEP1
     */
    Parser.prototype.AT_LEAST_ONE_SEP4 = function (separator, laFuncOrAction, action, errMsg) {
        return this.atLeastOneSepFirstInternal(this.atLeastOneSepFirstInternal, "AT_LEAST_ONE_SEP4", 4, separator, laFuncOrAction, action, errMsg);
    };
    /**
     * @see AT_LEAST_ONE_SEP1
     */
    Parser.prototype.AT_LEAST_ONE_SEP5 = function (separator, laFuncOrAction, action, errMsg) {
        return this.atLeastOneSepFirstInternal(this.atLeastOneSepFirstInternal, "AT_LEAST_ONE_SEP5", 5, separator, laFuncOrAction, action, errMsg);
    };
    /**
     *
     * @param {string} name - The name of the rule.
     * @param {Function} implementation - The implementation of the rule.
     * @param {IRuleConfig} [config] - The rule's optional configurationn
     *
     * @returns {Function} 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, config) {
        if (config === void 0) { config = DEFAULT_RULE_CONFIG; }
        var ruleErrors = checks_1.validateRuleName(name, this.className);
        ruleErrors = ruleErrors.concat(checks_1.validateRuleDoesNotAlreadyExist(name, this.definedRulesNames, this.className));
        this.definedRulesNames.push(name);
        this.definitionErrors.push.apply(this.definitionErrors, ruleErrors); // mutability for the win
        // 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);
            }
        }
        return this.defineRule(name, implementation, config);
    };
    /**
     *
     * @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 = checks_1.validateRuleName(name, this.className);
        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 (ruleName, idxInCallingRule) {
        this.RULE_OCCURRENCE_STACK.push(idxInCallingRule);
        this.RULE_STACK.push(ruleName);
    };
    Parser.prototype.ruleFinallyStateUpdate = function () {
        this.RULE_STACK.pop();
        this.RULE_OCCURRENCE_STACK.pop();
        var maxInputIdx = this._input.length - 1;
        if ((this.RULE_STACK.length === 0) && this.inputIdx < maxInputIdx) {
            var firstRedundantTok = this.NEXT_TOKEN();
            this.SAVE_ERROR(new exceptions_public_1.exceptions.NotAllInputParsedException("Redundant input, expecting EOF but found: " + firstRedundantTok.image, firstRedundantTok));
        }
    };
    /**
     * Returns an "imaginary" Token to insert when Single Token Insertion is done
     * Override this if you require special behavior in your grammar
     * for example if an IntegerToken is required provide one with the image '0' so it would be valid syntactically
     */
    Parser.prototype.getTokenToInsert = function (tokClass) {
        return new tokClass(-1, -1);
    };
    /**
     * By default all tokens type may be inserted. This behavior may be overridden in inheriting Recognizers
     * for example: One may decide that only punctuation tokens may be inserted automatically as they have no additional
     * semantic value. (A mandatory semicolon has no additional semantic meaning, but an Integer may have additional meaning
     * depending on its int value and context (Inserting an integer 0 in cardinality: "[1..]" will cause semantic issues
     * as the max of the cardinality will be greater than the min value. (and this is a false error!)
     */
    Parser.prototype.canTokenTypeBeInsertedInRecovery = function (tokClass) {
        return true;
    };
    /**
     * @param {Token} actualToken - The actual unexpected (mismatched) Token instance encountered.
     * @param {Function} expectedTokType - The Class of the expected Token.
     * @returns {string} The error message saved as part of a MismatchedTokenException.
     */
    Parser.prototype.getMisMatchTokenErrorMessage = function (expectedTokType, actualToken) {
        var hasLabel = tokens_public_1.hasTokenLabel(expectedTokType);
        var expectedMsg = hasLabel ?
            "--> " + tokens_public_1.tokenLabel(expectedTokType) + " <--" :
            "token of type --> " + tokens_public_1.tokenName(expectedTokType) + " <--";
        var msg = "Expecting " + expectedMsg + " but found --> '" + actualToken.image + "' <--";
        return msg;
    };
    Parser.prototype.defineRule = function (ruleName, impl, config) {
        var resyncEnabled = utils_1.has(config, "resyncEnabled") ? config.resyncEnabled : DEFAULT_RULE_CONFIG.resyncEnabled;
        var recoveryValueFunc = utils_1.has(config, "recoveryValueFunc") ? config.recoveryValueFunc : DEFAULT_RULE_CONFIG.recoveryValueFunc;
        var wrappedGrammarRule = function (idxInCallingRule, args) {
            if (idxInCallingRule === void 0) { idxInCallingRule = 1; }
            if (args === void 0) { args = []; }
            this.ruleInvocationStateUpdate(ruleName, idxInCallingRule);
            try {
                // actual parsing happens here
                return impl.apply(this, args);
            }
            catch (e) {
                var isFirstInvokedRule = (this.RULE_STACK.length === 1);
                // note the reSync is always enabled for the first rule invocation, because we must always be able to
                // reSync with EOF and just output some INVALID ParseTree
                // during backtracking reSync recovery is disabled, otherwise we can't be certain the backtracking
                // path is really the most valid one
                var reSyncEnabled = isFirstInvokedRule || (resyncEnabled
                    && !this.isBackTracking()
                    && this.recoveryEnabled);
                if (reSyncEnabled && exceptions_public_1.exceptions.isRecognitionException(e)) {
                    var reSyncTokType = this.findReSyncTokenType();
                    if (this.isInCurrentRuleReSyncSet(reSyncTokType)) {
                        e.resyncedTokens = this.reSyncTo(reSyncTokType);
                        return recoveryValueFunc();
                    }
                    else {
                        // to be handled farther up the call stack
                        throw e;
                    }
                }
                else {
                    // some other Error type which we don't know how to handle (for example a built in JavaScript Error)
                    throw e;
                }
            }
            finally {
                this.ruleFinallyStateUpdate();
            }
        };
        var ruleNamePropName = "ruleName";
        wrappedGrammarRule[ruleNamePropName] = ruleName;
        return wrappedGrammarRule;
    };
    Parser.prototype.tryInRepetitionRecovery = function (grammarRule, grammarRuleArgs, lookAheadFunc, expectedTokType) {
        var _this = this;
        // TODO: can the resyncTokenType be cached?
        var reSyncTokType = this.findReSyncTokenType();
        var orgInputIdx = this.inputIdx;
        var resyncedTokens = [];
        var passedResyncPoint = false;
        var nextTokenWithoutResync = this.NEXT_TOKEN();
        var currToken = this.NEXT_TOKEN();
        var generateErrorMessage = function () {
            // we are preemptively re-syncing before an error has been detected, therefor we must reproduce
            // the error that would have been thrown
            var msg = _this.getMisMatchTokenErrorMessage(expectedTokType, nextTokenWithoutResync);
            var error = new exceptions_public_1.exceptions.MismatchedTokenException(msg, nextTokenWithoutResync);
            // the first token here will be the original cause of the error, this is not part of the resyncedTokens property.
            error.resyncedTokens = utils_1.dropRight(resyncedTokens);
            _this.SAVE_ERROR(error);
        };
        while (!passedResyncPoint) {
            // re-synced to a point where we can safely exit the repetition/
            if (currToken instanceof expectedTokType) {
                generateErrorMessage();
                return; // must return here to avoid reverting the inputIdx
            }
            else if (lookAheadFunc.call(this)) {
                generateErrorMessage();
                // recursive invocation in other to support multiple re-syncs in the same top level repetition grammar rule
                grammarRule.apply(this, grammarRuleArgs);
                return; // must return here to avoid reverting the inputIdx
            }
            else if (currToken instanceof reSyncTokType) {
                passedResyncPoint = true;
            }
            else {
                currToken = this.SKIP_TOKEN();
                this.addToResyncTokens(currToken, resyncedTokens);
            }
        }
        // we were unable to find a CLOSER point to resync inside the Repetition, reset the state.
        // The parsing exception we were trying to prevent will happen in the NEXT parsing step. it may be handled by
        // "between rules" resync recovery later in the flow.
        this.inputIdx = orgInputIdx;
    };
    Parser.prototype.shouldInRepetitionRecoveryBeTried = function (expectTokAfterLastMatch, nextTokIdx) {
        // arguments to try and perform resync into the next iteration of the many are missing
        if (expectTokAfterLastMatch === undefined || nextTokIdx === undefined) {
            return false;
        }
        // no need to recover, next token is what we expect...
        if (this.NEXT_TOKEN() instanceof expectTokAfterLastMatch) {
            return false;
        }
        // error recovery is disabled during backtracking as it can make the parser ignore a valid grammar path
        // and prefer some backtracking path that includes recovered errors.
        if (this.isBackTracking()) {
            return false;
        }
        // if we can perform inRule recovery (single token insertion or deletion) we always prefer that recovery algorithm
        // because if it works, it makes the least amount of changes to the input stream (greedy algorithm)
        //noinspection RedundantIfStatementJS
        if (this.canPerformInRuleRecovery(expectTokAfterLastMatch, this.getFollowsForInRuleRecovery(expectTokAfterLastMatch, nextTokIdx))) {
            return false;
        }
        return true;
    };
    // Error Recovery functionality
    Parser.prototype.getFollowsForInRuleRecovery = function (tokClass, tokIdxInRule) {
        var pathRuleStack = utils_1.cloneArr(this.RULE_STACK);
        var pathOccurrenceStack = utils_1.cloneArr(this.RULE_OCCURRENCE_STACK);
        var grammarPath = {
            ruleStack: pathRuleStack,
            occurrenceStack: pathOccurrenceStack,
            lastTok: tokClass,
            lastTokOccurrence: tokIdxInRule
        };
        var topRuleName = utils_1.first(pathRuleStack);
        var gastProductions = this.getGAstProductions();
        var topProduction = gastProductions.get(topRuleName);
        var follows = new interpreter_1.NextAfterTokenWalker(topProduction, grammarPath).startWalking();
        return follows;
    };
    Parser.prototype.tryInRuleRecovery = function (expectedTokType, follows) {
        if (this.canRecoverWithSingleTokenInsertion(expectedTokType, follows)) {
            var tokToInsert = this.getTokenToInsert(expectedTokType);
            tokToInsert.isInsertedInRecovery = true;
            return tokToInsert;
        }
        if (this.canRecoverWithSingleTokenDeletion(expectedTokType)) {
            var nextTok = this.SKIP_TOKEN();
            this.inputIdx++;
            return nextTok;
        }
        throw new InRuleRecoveryException("sad sad panda");
    };
    Parser.prototype.canPerformInRuleRecovery = function (expectedToken, follows) {
        return this.canRecoverWithSingleTokenInsertion(expectedToken, follows) ||
            this.canRecoverWithSingleTokenDeletion(expectedToken);
    };
    Parser.prototype.canRecoverWithSingleTokenInsertion = function (expectedTokType, follows) {
        if (!this.canTokenTypeBeInsertedInRecovery(expectedTokType)) {
            return false;
        }
        // must know the possible following tokens to perform single token insertion
        if (utils_1.isEmpty(follows)) {
            return false;
        }
        var mismatchedTok = this.NEXT_TOKEN();
        var isMisMatchedTokInFollows = utils_1.find(follows, function (possibleFollowsTokType) {
            return mismatchedTok instanceof possibleFollowsTokType;
        }) !== undefined;
        return isMisMatchedTokInFollows;
    };
    Parser.prototype.canRecoverWithSingleTokenDeletion = function (expectedTokType) {
        var isNextTokenWhatIsExpected = this.LA(2) instanceof expectedTokType;
        return isNextTokenWhatIsExpected;
    };
    Parser.prototype.isInCurrentRuleReSyncSet = function (token) {
        var followKey = this.getCurrFollowKey();
        var currentRuleReSyncSet = this.getFollowSetFromFollowKey(followKey);
        return utils_1.contains(currentRuleReSyncSet, token);
    };
    Parser.prototype.findReSyncTokenType = function () {
        var allPossibleReSyncTokTypes = this.flattenFollowSet();
        // this loop will always terminate as EOF is always in the follow stack and also always (virtually) in the input
        var nextToken = this.NEXT_TOKEN();
        var k = 2;
        while (true) {
            var nextTokenType = nextToken.constructor;
            if (utils_1.contains(allPossibleReSyncTokTypes, nextTokenType)) {
                return nextTokenType;
            }
            nextToken = this.LA(k);
            k++;
        }
    };
    Parser.prototype.getCurrFollowKey = function () {
        // the length is at least one as we always add the ruleName to the stack before invoking the rule.
        if (this.RULE_STACK.length === 1) {
            return EOF_FOLLOW_KEY;
        }
        var currRuleIdx = this.RULE_STACK.length - 1;
        var currRuleOccIdx = currRuleIdx;
        var prevRuleIdx = currRuleIdx - 1;
        return {
            ruleName: this.RULE_STACK[currRuleIdx],
            idxInCallingRule: this.RULE_OCCURRENCE_STACK[currRuleOccIdx],
            inRule: this.RULE_STACK[prevRuleIdx]
        };
    };
    Parser.prototype.buildFullFollowKeyStack = function () {
        var _this = this;
        return utils_1.map(this.RULE_STACK, function (ruleName, idx) {
            if (idx === 0) {
                return EOF_FOLLOW_KEY;
            }
            return {
                ruleName: ruleName,
                idxInCallingRule: _this.RULE_OCCURRENCE_STACK[idx],
                inRule: _this.RULE_STACK[idx - 1]
            };
        });
    };
    Parser.prototype.flattenFollowSet = function () {
        var _this = this;
        var followStack = utils_1.map(this.buildFullFollowKeyStack(), function (currKey) {
            return _this.getFollowSetFromFollowKey(currKey);
        });
        return utils_1.flatten(followStack);
    };
    Parser.prototype.getFollowSetFromFollowKey = function (followKey) {
        if (followKey === EOF_FOLLOW_KEY) {
            return [tokens_public_1.EOF];
        }
        var followName = followKey.ruleName + followKey.idxInCallingRule + constants_1.IN + followKey.inRule;
        return cache.getResyncFollowsForClass(this.className).get(followName);
    };
    // It does not make any sense to include a virtual EOF token in the list of resynced tokens
    // as EOF does not really exist and thus does not contain any useful information (line/column numbers)
    Parser.prototype.addToResyncTokens = function (token, resyncTokens) {
        if (!(token instanceof tokens_public_1.EOF)) {
            resyncTokens.push(token);
        }
        return resyncTokens;
    };
    Parser.prototype.reSyncTo = function (tokClass) {
        var resyncedTokens = [];
        var nextTok = this.NEXT_TOKEN();
        while ((nextTok instanceof tokClass) === false) {
            nextTok = this.SKIP_TOKEN();
            this.addToResyncTokens(nextTok, resyncedTokens);
        }
        // the last token is not part of the error.
        return utils_1.dropRight(resyncedTokens);
    };
    Parser.prototype.attemptInRepetitionRecovery = function (prodFunc, args, lookaheadFunc, prodName, prodOccurrence, nextToksWalker, prodKeys) {
        var key = this.getKeyForAutomaticLookahead(prodName, prodKeys, prodOccurrence);
        var firstAfterRepInfo = this.firstAfterRepMap.get(key);
        if (firstAfterRepInfo === undefined) {
            var currRuleName = utils_1.last(this.RULE_STACK);
            var ruleGrammar = this.getGAstProductions().get(currRuleName);
            var walker = new nextToksWalker(ruleGrammar, prodOccurrence);
            firstAfterRepInfo = walker.startWalking();
            this.firstAfterRepMap.put(key, firstAfterRepInfo);
        }
        var expectTokAfterLastMatch = firstAfterRepInfo.token;
        var nextTokIdx = firstAfterRepInfo.occurrence;
        var isEndOfRule = firstAfterRepInfo.isEndOfRule;
        // special edge case of a TOP most repetition after which the input should END.
        // this will force an attempt for inRule recovery in that scenario.
        if (this.RULE_STACK.length === 1 &&
            isEndOfRule &&
            expectTokAfterLastMatch === undefined) {
            expectTokAfterLastMatch = tokens_public_1.EOF;
            nextTokIdx = 1;
        }
        if (this.shouldInRepetitionRecoveryBeTried(expectTokAfterLastMatch, nextTokIdx)) {
            // TODO: performance optimization: instead of passing the original args here, we modify
            // the args param (or create a new one) and make sure the lookahead func is explicitly provided
            // to avoid searching the cache for it once more.
            this.tryInRepetitionRecovery(prodFunc, args, lookaheadFunc, expectTokAfterLastMatch);
        }
    };
    // Implementation of parsing DSL
    Parser.prototype.optionInternal = function (condition, action) {
        if (condition.call(this)) {
            action.call(this);
            return true;
        }
        return false;
    };
    Parser.prototype.atLeastOneInternal = function (prodFunc, prodName, prodOccurrence, lookAheadFunc, action, userDefinedErrMsg) {
        if (!utils_1.isFunction(action)) {
            userDefinedErrMsg = action;
            action = lookAheadFunc;
            lookAheadFunc = this.getLookaheadFuncForAtLeastOne(prodOccurrence);
        }
        if (lookAheadFunc.call(this)) {
            action.call(this);
            while (lookAheadFunc.call(this)) {
                action.call(this);
            }
        }
        else {
            throw this.raiseEarlyExitException(prodOccurrence, interpreter_1.NextInsideAtLeastOneWalker, userDefinedErrMsg);
        }
        // note that while it may seem that this can cause an error because by using a recursive call to
        // AT_LEAST_ONE we change the grammar to AT_LEAST_TWO, AT_LEAST_THREE ... , the possible recursive call
        // from the tryInRepetitionRecovery(...) will only happen IFF there really are TWO/THREE/.... items.
        if (this.recoveryEnabled) {
            this.attemptInRepetitionRecovery(prodFunc, [lookAheadFunc, action, userDefinedErrMsg], lookAheadFunc, prodName, prodOccurrence, interpreter_1.NextTerminalAfterAtLeastOneWalker, this.atLeastOneLookaheadKeys);
        }
    };
    Parser.prototype.atLeastOneSepFirstInternal = function (prodFunc, prodName, prodOccurrence, separator, firstIterationLookAheadFunc, action, userDefinedErrMsg) {
        var _this = this;
        var separatorsResult = [];
        if (!utils_1.isFunction(action)) {
            userDefinedErrMsg = action;
            action = firstIterationLookAheadFunc;
            firstIterationLookAheadFunc = this.getLookaheadFuncForAtLeastOneSep(prodOccurrence);
        }
        // 1st iteration
        if (firstIterationLookAheadFunc.call(this)) {
            action.call(this);
            var separatorLookAheadFunc = function () { return _this.NEXT_TOKEN() instanceof separator; };
            // 2nd..nth iterations
            while (separatorLookAheadFunc()) {
                // note that this CONSUME will never enter recovery because
                // the separatorLookAheadFunc checks that the separator really does exist.
                separatorsResult.push(this.CONSUME(separator));
                action.call(this);
            }
            if (this.recoveryEnabled) {
                this.attemptInRepetitionRecovery(this.repetitionSepSecondInternal, [prodName, prodOccurrence, separator, separatorLookAheadFunc, action, separatorsResult,
                    this.atLeastOneSepLookaheadKeys, interpreter_1.NextTerminalAfterAtLeastOneSepWalker], separatorLookAheadFunc, prodName, prodOccurrence, interpreter_1.NextTerminalAfterAtLeastOneSepWalker, this.atLeastOneSepLookaheadKeys);
            }
        }
        else {
            throw this.raiseEarlyExitException(prodOccurrence, interpreter_1.NextInsideAtLeastOneSepWalker, userDefinedErrMsg);
        }
        return separatorsResult;
    };
    Parser.prototype.manyInternal = function (prodFunc, prodName, prodOccurrence, lookAheadFunc, action) {
        if (action === undefined) {
            action = lookAheadFunc;
            lookAheadFunc = this.getLookaheadFuncForMany(prodOccurrence);
        }
        while (lookAheadFunc.call(this)) {
            action.call(this);
        }
        if (this.recoveryEnabled) {
            this.attemptInRepetitionRecovery(prodFunc, [lookAheadFunc, action], lookAheadFunc, prodName, prodOccurrence, interpreter_1.NextTerminalAfterManyWalker, this.manyLookaheadKeys);
        }
    };
    Parser.prototype.manySepFirstInternal = function (prodFunc, prodName, prodOccurrence, separator, firstIterationLookAheadFunc, action) {
        var _this = this;
        var separatorsResult = [];
        if (action === undefined) {
            action = firstIterationLookAheadFunc;
            firstIterationLookAheadFunc = this.getLookaheadFuncForManySep(prodOccurrence);
        }
        // 1st iteration
        if (firstIterationLookAheadFunc.call(this)) {
            action.call(this);
            var separatorLookAheadFunc = function () { return _this.NEXT_TOKEN() instanceof separator; };
            // 2nd..nth iterations
            while (separatorLookAheadFunc()) {
                // note that this CONSUME will never enter recovery because
                // the separatorLookAheadFunc checks that the separator really does exist.
                separatorsResult.push(this.CONSUME(separator));
                action.call(this);
            }
            if (this.recoveryEnabled) {
                this.attemptInRepetitionRecovery(this.repetitionSepSecondInternal, [prodName, prodOccurrence, separator, separatorLookAheadFunc, action, separatorsResult,
                    this.manySepLookaheadKeys, interpreter_1.NextTerminalAfterManySepWalker], separatorLookAheadFunc, prodName, prodOccurrence, interpreter_1.NextTerminalAfterManySepWalker, this.manySepLookaheadKeys);
            }
        }
        return separatorsResult;
    };
    Parser.prototype.repetitionSepSecondInternal = function (prodName, prodOccurrence, separator, separatorLookAheadFunc, action, separatorsResult, laKeys, nextTerminalAfterWalker) {
        while (separatorLookAheadFunc()) {
            // note that this CONSUME will never enter recovery because
            // the separatorLookAheadFunc checks that the separator really does exist.
            separatorsResult.push(this.CONSUME(separator));
            action.call(this);
        }
        // we can only arrive to this function after an error
        // has occurred (hence the name 'second') so the following
        // IF will always be entered, its possible to remove it...
        // however it is kept to avoid confusion and be consistent.
        /* istanbul ignore else */
        if (this.recoveryEnabled) {
            this.attemptInRepetitionRecovery(this.repetitionSepSecondInternal, [prodName, prodOccurrence, separator, separatorLookAheadFunc,
                action, separatorsResult, laKeys, nextTerminalAfterWalker], separatorLookAheadFunc, prodName, prodOccurrence, nextTerminalAfterWalker, laKeys);
        }
    };
    Parser.prototype.orInternal = function (alts, errMsgTypes, occurrence, ignoreAmbiguities) {
        // explicit alternatives look ahead
        if (alts[0].WHEN !== undefined) {
            for (var i = 0; i < alts.length; i++) {
                if (alts[i].WHEN.call(this)) {
                    var res = alts[i].THEN_DO();
                    return res;
                }
            }
            this.raiseNoAltException(occurrence, errMsgTypes);
        }
        // else implicit lookahead
        var laFunc = this.getLookaheadFuncForOr(occurrence, ignoreAmbiguities);
        var altToTake = laFunc.call(this);
        if (altToTake !== -1) {
            return alts[altToTake].ALT.call(this);
        }
        this.raiseNoAltException(occurrence, errMsgTypes);
    };
    /**
     * @param tokClass The Type of Token we wish to consume (Reference to its constructor function)
     * @param idx occurrence index of consumed token in the invoking parser rule text
     *         for example:
     *         IDENT (DOT IDENT)*
     *         the first ident will have idx 1 and the second one idx 2
     *         * note that for the second ident the idx is always 2 even if its invoked 30 times in the same rule
     *           the idx is about the position in grammar (source code) and has nothing to do with a specific invocation
     *           details
     *
     * @returns the consumed Token
     */
    Parser.prototype.consumeInternal = function (tokClass, idx) {
        try {
            return this.consumeInternalOptimized(tokClass);
        }
        catch (eFromConsumption) {
            // no recovery allowed during backtracking, otherwise backtracking may recover invalid syntax and accept it
            // but the original syntax could have been parsed successfully without any backtracking + recovery
            if (this.recoveryEnabled &&
                eFromConsumption instanceof exceptions_public_1.exceptions.MismatchedTokenException && !this.isBackTracking()) {
                var follows = this.getFollowsForInRuleRecovery(tokClass, idx);
                try {
                    return this.tryInRuleRecovery(tokClass, follows);
                }
                catch (eFromInRuleRecovery) {
                    if (eFromInRuleRecovery.name === lang_extensions_1.functionName(InRuleRecoveryException)) {
                        // failed in RuleRecovery.
                        // throw the original error in order to trigger reSync error recovery
                        throw eFromConsumption;
                    }
                    else {
                        throw eFromInRuleRecovery;
                    }
                }
            }
            else {
                throw eFromConsumption;
            }
        }
    };
    // to enable optimizations this logic has been extract to a method as its invoker contains try/catch
    Parser.prototype.consumeInternalOptimized = function (expectedTokClass) {
        var nextToken = this.NEXT_TOKEN();
        if (this.NEXT_TOKEN() instanceof expectedTokClass) {
            this.inputIdx++;
            return nextToken;
        }
        else {
            var msg = this.getMisMatchTokenErrorMessage(expectedTokClass, nextToken);
            throw this.SAVE_ERROR(new exceptions_public_1.exceptions.MismatchedTokenException(msg, nextToken));
        }
    };
    Parser.prototype.getKeyForAutomaticLookahead = function (prodName, prodKeys, occurrence) {
        var occuMap = prodKeys[occurrence - 1];
        var currRule = utils_1.last(this.RULE_STACK);
        var key = occuMap[currRule];
        if (key === undefined) {
            key = prodName + occurrence + constants_1.IN + currRule;
            occuMap[currRule] = key;
        }
        return key;
    };
    // Automatic lookahead calculation
    Parser.prototype.getLookaheadFuncForOption = function (occurence) {
        var key = this.getKeyForAutomaticLookahead("OPTION", this.optionLookaheadKeys, occurence);
        return this.getLookaheadFuncFor(key, occurence, lookahead_1.buildLookaheadForOption);
    };
    Parser.prototype.getLookaheadFuncForOr = function (occurence, ignoreErrors) {
        var key = this.getKeyForAutomaticLookahead("OR", this.orLookaheadKeys, occurence);
        return this.getLookaheadFuncFor(key, occurence, lookahead_1.buildLookaheadForOr, [ignoreErrors]);
    };
    Parser.prototype.getLookaheadFuncForMany = function (occurence) {
        var key = this.getKeyForAutomaticLookahead("MANY", this.manyLookaheadKeys, occurence);
        return this.getLookaheadFuncFor(key, occurence, lookahead_1.buildLookaheadForMany);
    };
    Parser.prototype.getLookaheadFuncForManySep = function (occurence) {
        var key = this.getKeyForAutomaticLookahead("MANY_SEP", this.manySepLookaheadKeys, occurence);
        return this.getLookaheadFuncFor(key, occurence, lookahead_1.buildLookaheadForManySep);
    };
    Parser.prototype.getLookaheadFuncForAtLeastOne = function (occurence) {
        var key = this.getKeyForAutomaticLookahead("AT_LEAST_ONE", this.atLeastOneLookaheadKeys, occurence);
        return this.getLookaheadFuncFor(key, occurence, lookahead_1.buildLookaheadForAtLeastOne);
    };
    Parser.prototype.getLookaheadFuncForAtLeastOneSep = function (occurence) {
        var key = this.getKeyForAutomaticLookahead("AT_LEAST_ONE_SEP", this.atLeastOneSepLookaheadKeys, occurence);
        return this.getLookaheadFuncFor(key, occurence, lookahead_1.buildLookaheadForAtLeastOneSep);
    };
    Parser.prototype.getLookaheadFuncFor = function (key, occurrence, laFuncBuilder, extraArgs) {
        if (extraArgs === void 0) { extraArgs = []; }
        var ruleName = utils_1.last(this.RULE_STACK);
        var condition = this.classLAFuncs.get(key);
        if (condition === undefined) {
            var ruleGrammar = this.getGAstProductions().get(ruleName);
            condition = laFuncBuilder.apply(null, [occurrence, ruleGrammar].concat(extraArgs));
            this.classLAFuncs.put(key, condition);
        }
        return condition;
    };
    // other functionality
    Parser.prototype.saveRecogState = function () {
        var savedErrors = utils_1.cloneArr(this.errors);
        var savedRuleStack = utils_1.cloneArr(this.RULE_STACK);
        return {
            errors: savedErrors,
            inputIdx: this.inputIdx,
            RULE_STACK: savedRuleStack
        };
    };
    Parser.prototype.reloadRecogState = function (newState) {
        this.errors = newState.errors;
        this.inputIdx = newState.inputIdx;
        this.RULE_STACK = newState.RULE_STACK;
    };
    Parser.prototype.raiseNoAltException = function (occurrence, errMsgTypes) {
        var errSuffix = " but found: '" + this.NEXT_TOKEN().image + "'";
        if (errMsgTypes === undefined) {
            var ruleName = utils_1.last(this.RULE_STACK);
            var ruleGrammar = this.getGAstProductions().get(ruleName);
            var nextTokens = new interpreter_1.NextInsideOrWalker(ruleGrammar, occurrence).startWalking();
            var nextTokensFlat = utils_1.flatten(nextTokens);
            var nextTokensNames = utils_1.map(nextTokensFlat, function (currTokenClass) { return tokens_public_1.tokenLabel(currTokenClass); });
            errMsgTypes = "one of: <" + nextTokensNames.join(" ,") + ">";
        }
        throw this.SAVE_ERROR(new exceptions_public_1.exceptions.NoViableAltException("Expecting: " + errMsgTypes + " " + errSuffix, this.NEXT_TOKEN()));
    };
    Parser.prototype.raiseEarlyExitException = function (occurrence, nextWalkerConstructor, userDefinedErrMsg) {
        var errSuffix = " but found: '" + this.NEXT_TOKEN().image + "'";
        if (userDefinedErrMsg === undefined) {
            var ruleName = utils_1.last(this.RULE_STACK);
            var ruleGrammar = this.getGAstProductions().get(ruleName);
            var grammarPath = {
                ruleStack: this.RULE_STACK,
                occurrenceStack: this.RULE_OCCURRENCE_STACK,
                occurrence: occurrence
            };
            var nextTokens = new nextWalkerConstructor(ruleGrammar, grammarPath).startWalking();
            var nextTokensFlat = utils_1.flatten(nextTokens);
            var nextTokensNames = utils_1.map(nextTokensFlat, function (currTokenClass) { return tokens_public_1.tokenLabel(currTokenClass); });
            userDefinedErrMsg = "expecting at least one iteration which starts with one of: <" + nextTokensNames.join(" ,") + ">";
        }
        else {
            userDefinedErrMsg = "Expecting at least one " + userDefinedErrMsg;
        }
        throw this.SAVE_ERROR(new exceptions_public_1.exceptions.EarlyExitException(userDefinedErrMsg + errSuffix, this.NEXT_TOKEN()));
    };
    Parser.IGNORE_AMBIGUITIES = true;
    Parser.NO_RESYNC = false;
    // Set this flag to true if you don't want the Parser to throw error when problems in it's definition are detected.
    // (normally during the parser's constructor).
    // This is a design time flag, it will not affect the runtime error handling of the parser, just design time errors,
    // for example: duplicate rule names, referencing an unresolved subrule, ect...
    // This flag should not be enabled during normal usage, it is used in special situations, for example when
    // needing to display the parser definition errors in some GUI(online playground).
    Parser.DEFER_DEFINITION_ERRORS_HANDLING = false;
    return Parser;
}());
exports.Parser = Parser;
function InRuleRecoveryException(message) {
    this.name = lang_extensions_1.functionName(InRuleRecoveryException);
    this.message = message;
}
InRuleRecoveryException.prototype = Error.prototype;
