"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) /* istanbul ignore next */  if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    /* istanbul ignore next */  d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var utils_1 = require("../utils/utils");
var lang_extensions_1 = require("../lang/lang_extensions");
var lexer_public_1 = require("./lexer_public");
var tokens_1 = require("./tokens");
/**
 *  This can be used to improve the quality/readability of error messages or syntax diagrams.
 *
 * @param {Function} clazz - A constructor for a Token subclass
 * @returns {string} - The Human readable label a Token if it exists.
 */
function tokenLabel(clazz) {
    if (hasTokenLabel(clazz)) {
        return clazz.LABEL;
    }
    else {
        return tokenName(clazz);
    }
}
exports.tokenLabel = tokenLabel;
function hasTokenLabel(clazz) {
    return utils_1.isString(clazz.LABEL) && clazz.LABEL !== "";
}
exports.hasTokenLabel = hasTokenLabel;
function tokenName(clazz) {
    // The tokenName property is needed under some old versions of node.js (0.10/0.12)
    // where the Function.prototype.name property is not defined as a 'configurable' property
    // enable producing readable error messages.
    /* istanbul ignore if -> will only run in old versions of node.js */
    if (utils_1.isString(clazz.tokenName)) {
        return clazz.tokenName;
    }
    else {
        return lang_extensions_1.functionName(clazz);
    }
}
exports.tokenName = tokenName;
function extendLazyToken(tokenName, patternOrParent, parentConstructor) {
    if (patternOrParent === void 0) { patternOrParent = undefined; }
    if (parentConstructor === void 0) { parentConstructor = LazyToken; }
    return extendToken(tokenName, patternOrParent, parentConstructor);
}
exports.extendLazyToken = extendLazyToken;
function extendSimpleLazyToken(tokenName, patternOrParent, parentConstructor) {
    if (patternOrParent === void 0) { patternOrParent = undefined; }
    if (parentConstructor === void 0) { parentConstructor = SimpleLazyToken; }
    return extendToken(tokenName, patternOrParent, parentConstructor);
}
exports.extendSimpleLazyToken = extendSimpleLazyToken;
/**
 * utility to help the poor souls who are still stuck writing pure javascript 5.1
 * extend and create Token subclasses in a less verbose manner
 *
 * @param {string} tokenName - The name of the new TokenClass
 * @param {RegExp|Function} patternOrParent - RegExp Pattern or Parent Token Constructor
 * @param {Function} parentConstructor - The Token class to be extended
 * @returns {Function} - A constructor for the new extended Token subclass
 */
function extendToken(tokenName, patternOrParent, parentConstructor) {
    if (patternOrParent === void 0) { patternOrParent = undefined; }
    if (parentConstructor === void 0) { parentConstructor = Token; }
    var pattern;
    if (utils_1.isRegExp(patternOrParent) ||
        patternOrParent === lexer_public_1.Lexer.SKIPPED ||
        patternOrParent === lexer_public_1.Lexer.NA) {
        pattern = patternOrParent;
    }
    else if (utils_1.isFunction(patternOrParent)) {
        parentConstructor = patternOrParent;
        pattern = undefined;
    }
    var derivedConstructor = function () {
        parentConstructor.apply(this, arguments);
    };
    // can be overwritten according to:
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/
    // name?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fname
    /* istanbul ignore if -> will only run in old versions of node.js */
    if (!lang_extensions_1.defineNameProp(derivedConstructor, tokenName)) {
        // hack to save the tokenName in situations where the constructor's name property cannot be reconfigured
        derivedConstructor.tokenName = tokenName;
    }
    derivedConstructor.prototype = Object.create(parentConstructor.prototype);
    derivedConstructor.prototype.constructor = derivedConstructor;
    if (!utils_1.isUndefined(pattern)) {
        derivedConstructor.PATTERN = pattern;
    }
    tokens_1.augmentTokenClasses([derivedConstructor]);
    // static properties mixing
    derivedConstructor = utils_1.assignNoOverwrite(derivedConstructor, parentConstructor);
    return derivedConstructor;
}
exports.extendToken = extendToken;
var Token = (function () {
    /**
     * @param {string} image - The textual representation of the Token as it appeared in the text.
     * @param {number} startOffset - Offset of the first character of the Token.
     * @param {number} startLine - Line of the first character of the Token.
     * @param {number} startColumn - Column of the first character of the Token.
     * @param {number} endLine - Line of the last character of the Token.
     * @param {number} endColumn - Column of the last character of the Token.
     */
    function Token(image, startOffset, startLine, startColumn, endLine, endColumn) {
        if (endLine === void 0) { endLine = startLine; }
        if (endColumn === void 0) { endColumn = startColumn + image.length - 1; }
        this.image = image;
        this.startOffset = startOffset;
        this.startLine = startLine;
        this.startColumn = startColumn;
        this.endLine = endLine;
        this.endColumn = endColumn;
        // this marks if a Token does not really exist and has been inserted "artificially" during parsing in rule error recovery
        this.isInsertedInRecovery = false;
    }
    Object.defineProperty(Token.prototype, "endOffset", {
        get: function () {
            return this.startOffset + this.image.length - 1;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Token.prototype, "offset", {
        /**
         * @deprecated
         * An Alias for getting the startOffset. this is deprecated and remains only to be backwards compatiable.
         * This API will be removed in future version of Chevrotain.
         */
        get: function () {
            return this.startOffset;
        },
        /**
         * @deprecated
         * An Alias for setting the startOffset. this is deprecated and remains only to be backwards compatiable.
         * This API will be removed in future version of Chevrotain.
         */
        set: function (newOffset) {
            this.startOffset = newOffset;
        },
        enumerable: true,
        configurable: true
    });
    /**
     * A "human readable" Label for a Token.
     * Subclasses of Token may define their own static LABEL property.
     * This label will be used in error messages and drawing syntax diagrams.
     *
     * For example a Token constructor may be called LCurly, which is short for LeftCurlyBrackets, These names are either too short
     * or too unwieldy to be used in error messages.
     *
     * Imagine : "expecting LCurly but found ')'" or "expecting LeftCurlyBrackets but found ')'"
     *
     * However if a static property LABEL with the value '{' exists on LCurly class, that error message will be:
     * "expecting '{' but found ')'"
     */
    Token.LABEL = undefined;
    return Token;
}());
exports.Token = Token;
/**
 * @see IToken
 * @see Token
 *
 * Same API as a IToken, using a Lazy implementation, with most properties being immutable.
 * See related doc in: https://github.com/SAP/chevrotain/blob/startO/docs/faq.md#-how-do-i-maximize-my-parsers-performance
 * ("Use Lazy Tokens" section)
 */
var LazyToken = (function () {
    function LazyToken(startOffset, endOffset, cacheData) {
        this.startOffset = startOffset;
        this.endOffset = endOffset;
        this.cacheData = cacheData;
    }
    Object.defineProperty(LazyToken.prototype, "image", {
        get: function () {
            return tokens_1.getImageFromLazyToken(this);
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(LazyToken.prototype, "startLine", {
        get: function () {
            return tokens_1.getStartLineFromLazyToken(this);
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(LazyToken.prototype, "startColumn", {
        get: function () {
            return tokens_1.getStartColumnFromLazyToken(this);
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(LazyToken.prototype, "endLine", {
        get: function () {
            return tokens_1.getEndLineFromLazyToken(this);
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(LazyToken.prototype, "endColumn", {
        get: function () {
            return tokens_1.getEndColumnFromLazyToken(this);
        },
        enumerable: true,
        configurable: true
    });
    /**
     * A "human readable" Label for a Token.
     * Subclasses of Token may define their own static LABEL property.
     * This label will be used in error messages and drawing syntax diagrams.
     *
     * For example a Token constructor may be called LCurly, which is short for LeftCurlyBrackets, These names are either too short
     * or too unwieldy to be used in error messages.
     *
     * Imagine : "expecting LCurly but found ')'" or "expecting LeftCurlyBrackets but found ')'"
     *
     * However if a static property LABEL with the value '{' exists on LCurly class, that error message will be:
     * "expecting '{' but found ')'"
     */
    LazyToken.LABEL = undefined;
    return LazyToken;
}());
exports.LazyToken = LazyToken;
/**
 * @see IToken
 * @see LazyToken
 *
 * A Less complex LazyToken used to increase performance.
 * Instances of SimpleLazyToken will not actually inherit from it using prototype inheritance.
 * Instead they will be simple JS Objects (Simple Structures)
 * {
 *    startOffset : 10,
 *    endOffset   : 16,
 *    tokenType   : 66,
 *    cacheData   : cacheData
 * }
 *
 * The additional computed properties (startLine/StartColumn/...) of the IToken interface can be computed using
 * The provided Utility methods (getImage, getStartColumn, getEndLine, ...)
 *
 * This makes SimpleLazyTokens slightly less convenient, however this can produce a substantial increase in performance
 * which may be relevant in certain use cases where performance is of paramount concern.
 */
var SimpleLazyToken = (function () {
    // This constructor is never actually called as simpleLazyToken are just a Structure.
    // However this class must still exist as the definition and hierarchy of the SimpleLazyTokens
    // still uses the standard prototype chain.
    /* istanbul ignore next */
    function SimpleLazyToken(startOffset, endOffset, tokenType, cacheData) {
        this.startOffset = startOffset;
        this.endOffset = endOffset;
        this.tokenType = tokenType;
        this.cacheData = cacheData;
    }
    /**
     * A "human readable" Label for a Token.
     * Subclasses of Token may define their own static LABEL property.
     * This label will be used in error messages and drawing syntax diagrams.
     *
     * For example a Token constructor may be called LCurly, which is short for LeftCurlyBrackets, These names are either too short
     * or too unwieldy to be used in error messages.
     *
     * Imagine : "expecting LCurly but found ')'" or "expecting LeftCurlyBrackets but found ')'"
     *
     * However if a static property LABEL with the value '{' exists on LCurly class, that error message will be:
     * "expecting '{' but found ')'"
     */
    SimpleLazyToken.LABEL = undefined;
    return SimpleLazyToken;
}());
exports.SimpleLazyToken = SimpleLazyToken;
/**
 * A special kind of Token which does not really exist in the input
 * (hence the 'Virtual' prefix). These type of Tokens can be used as special markers:
 * for example, EOF (end-of-file).
 */
var VirtualToken = (function (_super) {
    __extends(VirtualToken, _super);
    function VirtualToken() {
        _super.call(this, "", NaN, NaN, NaN, NaN, NaN);
    }
    return VirtualToken;
}(Token));
exports.VirtualToken = VirtualToken;
var EOF = (function (_super) {
    __extends(EOF, _super);
    function EOF() {
        _super.apply(this, arguments);
    }
    return EOF;
}(VirtualToken));
exports.EOF = EOF;
tokens_1.augmentTokenClasses([EOF]);
// Token Getter Utilities
function getImage(token) {
    return tokens_1.isInheritanceBasedToken(token) ?
        token.image :
        tokens_1.getImageFromLazyToken(token);
}
exports.getImage = getImage;
function getStartOffset(token) {
    return token.startOffset;
}
exports.getStartOffset = getStartOffset;
function getStartLine(token) {
    return tokens_1.isInheritanceBasedToken(token) ?
        token.startLine :
        tokens_1.getStartLineFromLazyToken(token);
}
exports.getStartLine = getStartLine;
function getStartColumn(token) {
    return tokens_1.isInheritanceBasedToken(token) ?
        token.startColumn :
        tokens_1.getStartColumnFromLazyToken(token);
}
exports.getStartColumn = getStartColumn;
function getEndOffset(token) {
    return token.endOffset;
}
exports.getEndOffset = getEndOffset;
function getEndLine(token) {
    return tokens_1.isInheritanceBasedToken(token) ?
        token.endLine :
        tokens_1.getEndLineFromLazyToken(token);
}
exports.getEndLine = getEndLine;
function getEndColumn(token) {
    return tokens_1.isInheritanceBasedToken(token) ?
        token.endColumn :
        tokens_1.getEndColumnFromLazyToken(token);
}
exports.getEndColumn = getEndColumn;
/**
 * Given a Token instance, will return the Token Constructor.
 * Note that this function is not just for convenience, Because a SimpleLazyToken "instance'
 * Does not use standard prototype inheritance and thus it's constructor cannot be accessed
 * by traversing the prototype chain.
 *
 * @param tokenInstance {ISimpleTokenOrIToken}
 * @returns {TokenConstructor}
 */
function getTokenConstructor(tokenInstance) {
    var tokenIdx;
    if (tokens_1.isInheritanceBasedToken(tokenInstance)) {
        tokenIdx = tokenInstance.constructor.tokenType;
    }
    else {
        tokenIdx = tokenInstance.tokenType;
    }
    return tokens_1.tokenIdxToClass.get(tokenIdx);
}
exports.getTokenConstructor = getTokenConstructor;
/**
 * A Utility method to check if a token instance of of the type of a specific Token class.
 * Simply using instanceof is not enough because SimpleLazyToken Implementation does not use
 * ECMAScript's built-in prototype inheritance.
 *
 * @param tokInstance {ISimpleTokenOrIToken}
 * @param tokClass {TokenConstructor}
 * @returns {boolean}
 */
function tokenMatcher(tokInstance, tokClass) {
    if (LazyToken.prototype.isPrototypeOf(tokClass.prototype) ||
        Token.prototype.isPrototypeOf(tokClass.prototype)) {
        return tokens_1.tokenInstanceofMatcher(tokInstance, tokClass);
    }/* istanbul ignore else */ 
    else if (SimpleLazyToken.prototype.isPrototypeOf(tokClass.prototype)) {
        return tokens_1.tokenStructuredMatcher(tokInstance, tokClass);
    }
    else {
        /* istanbul ignore next */ throw Error("non exhaustive match");
    }
}
exports.tokenMatcher = tokenMatcher;
