    /** * This class parses simple expressions. The parser can be enhanced by providing any of * the following configs: * *  * `constants` *  * `infix` *  * `infixRight` *  * `postfix` *  * `symbols` * * The parser requires a `{@link Ext.parse.Tokenizer tokenizer}` which can be configured * using the `tokenizer` config. The parser keeps the tokenizer instance and recycles it * as it is itself reused. * * See http://javascript.crockford.com/tdop/tdop.html for background on the techniques * used in this parser. * @private */Ext.define('Ext.parse.Parser', function () {    var ITSELF = function () { return this; }; return {    extend: 'Ext.util.Fly',     requires: [        'Ext.parse.Tokenizer',        'Ext.parse.symbol.Constant',        'Ext.parse.symbol.InfixRight',        'Ext.parse.symbol.Paren',        'Ext.parse.symbol.Prefix'    ],     isParser: true,     config: {        /**         * @cfg {Object} constants          * A map of identifiers that should be converted to literal value tokens. The         * key in this object is the name of the constant and the value is the constant         * value.         *         * If the value of a key is an object, it is a config object for the         * `{@link Ext.parse.symbol.Constant constant}`.         */        constants: {            'null': null,            'false': false,            'true': true        },         /**         * @cfg {Object} infix          * A map of binary operators and their associated precedence (or binding priority).         * These binary operators are left-associative.         *         * If the value of a key is an object, it is a config object for the         * `{@link Ext.parse.symbol.Infix operator}`.         */        infix: {            //'===': 40,             //'!==': 40,             //'<': 40,             //'<=': 40,             //'>': 40,             //'>=': 40,             '+': 50,            '-': 50,            '*': 60,            '/': 60        },         /**         * @cfg {Object} infixRight          * A map of binary operators and their associated precedence (or binding priority).         * These binary operators are right-associative.         *         * If the value of a key is an object, it is a config object for the         * `{@link Ext.parse.symbol.InfixRight operator}`.         */        infixRight: {            '&&': 30,            '||': 30        },         /**         * @cfg {Object} prefix          * A map of unary operators. Typically no value is needed, so `0` is used.         *         * If the value of a key is an object, it is a config object for the         * `{@link Ext.parse.symbol.Prefix operator}`.         */        prefix: {            '!': 0,            '-': 0,            '+': 0        },         /**         * @cfg {Object} symbols          * General language symbols. The values in this object are used as config objects         * to configure the associated `{@link Ext.parse.Symbol symbol}`. If there is no         * configuration, use `0` for the value.         */        symbols: {            ':': 0,            ',': 0,            ')': 0,            '[': 0,            ']': 0,            '{': 0,            '}': 0,             '(end)': 0,             '(ident)': {                arity: 'ident',                isIdent: true,                nud: ITSELF            },             '(literal)': {                arity: 'literal',                isLiteral: true,                nud: ITSELF            },             '(': {                xclass: 'Ext.parse.symbol.Paren'            }        },         /**         * @cfg {Object/Ext.parse.Tokenizer} tokenizer         * The tokenizer or a config object used to create one.         */        tokenizer: {            keywords: null  // we'll handle keywords here         }    },     /**     * @cfg {Ext.parse.Symbol} token     * The current token. These tokens extend this base class and contain additional     * properties such as:     *     *   * `at` - The index of the token in the text.     *   * `value` - The value of the token (e.g., the name of an identifier).     *     * @readonly     */    token: null,     constructor: function (config) {        this.symbols = {};         this.initConfig(config);    },     /**     * Advances the token stream and returns the next `token`.     * @param {String} [expected] The type of symbol that is expected to follow.     * @return {Ext.parse.Symbol}     */    advance: function (expected) {        var me = this,            tokenizer = me.tokenizer,            token = tokenizer.peek(),            symbols = me.symbols,            index = tokenizer.index,            is, symbol, value;         if (me.error) {            throw me.error;        }        if (expected) {            me.expect(expected);        }         if (!token) {            return me.token = symbols['(end)'];        }         tokenizer.next();         is = token.is;        value = token.value;         if (is.ident) {            symbol = symbols[value] || symbols['(ident)'];        }        else if (is.operator) {            if (!(symbol = symbols[value])) {                me.syntaxError(token.at, 'Unknown operator "' + value + '"');            }        }        else if (is.literal) {            symbol = symbols['(literal)'];        }        else {            me.syntaxError(token.at, 'Unexpected token');        }         me.token = symbol = Ext.Object.chain(symbol);        symbol.at = index;        symbol.value = value;         if (!symbol.arity) {            symbol.arity = token.type;        }         return symbol;    },     expect: function (expected) {        var token = this.token;         if (expected !== token.id) {            this.syntaxError(token.at, 'Expected "' + expected + '"');        }        return this;    },     /**     *     * @param {Number} [rightPriority=0] The precedence of the current operator.     * @return {Ext.parse.Symbol} The parsed expression tree.     */    parseExpression: function (rightPriority) {        var me = this,            token = me.token,            left;         rightPriority = rightPriority || 0;         me.advance();         left = token.nud();         while (rightPriority < (token = me.token).priority) {            me.advance();            left = token.led(left);        }         return left;    },     /**     * Resets this parser given the text to parse or a `Tokenizer`.     * @param {String} text      * @param {Number} [pos=0] The character position at which to start.     * @param {Number} [end] The index of the first character beyond the token range.     * @return {Ext.parse.Parser}     */    reset: function (text, pos, end) {        var me = this;         me.error = me.token = null;        me.tokenizer.reset(text, pos, end);         me.advance(); // kick start this.token          return me;    },     /**     * This method is called when a syntax error is encountered. It updates `error`     * and returns the error token.     * @param {Number} at The index of the syntax error (optional).     * @param {String} message The error message.     * @return {Object} The error token.     */    syntaxError: function (at, message) {        if (typeof at === 'string') {            message = at;            at = this.pos;        }         var suffix = (at == null) ? '' : (' (at index ' + at + ')'),            error = new Error(message + suffix);         error.type = 'error';         if (suffix) {            error.at = at;        }         throw this.error = error;    },     privates: {        /**         * This property is set to an `Error` instance if the parser encounters a syntax         * error.         * @property {Object} error          * @readonly         */        error: null,         addSymbol: function (id, config, type, update) {            var symbols = this.symbols,                symbol = symbols[id],                cfg, length, i;             if (symbol) {                // If the symbol was already defined then we need to update it                 // we either use the config provided in the symbol definition                 // or we use the `update` param to build a config object.                 // We usually need to update either `led` or `nud` function                 if(typeof config === 'object'){                    cfg = config;                }else if(update && type){                    update = Ext.Array.from(update);                    length = update.length;                    cfg = {};                    for(i = 0; i < length; i++) {                        cfg[update[i]] = type.prototype[update[i]];                    }                }else{                    return symbol;                }                symbol.update(cfg);            } else {                if (config && config.xclass) {                    type = Ext.ClassManager.get(config.xclass);                } else {                    type = type || Ext.parse.Symbol;                }                 symbols[id] = symbol = new type(id, config);                symbol.parser = this;            }             return symbol;        },         addSymbols: function (symbols, type, update) {            for (var id in symbols) {                this.addSymbol(id, symbols[id], type, update);            }        },         applyConstants: function (constants) {            this.addSymbols(constants, Ext.parse.symbol.Constant, 'nud');        },         applyInfix: function (operators) {            this.addSymbols(operators, Ext.parse.symbol.Infix, 'led');        },         applyInfixRight: function (operators) {            this.addSymbols(operators, Ext.parse.symbol.InfixRight, 'led');        },         applyPrefix: function (operators) {            this.addSymbols(operators, Ext.parse.symbol.Prefix, 'nud');        },         applySymbols: function (symbols) {            this.addSymbols(symbols);        },         applyTokenizer: function (config) {            var ret = config;             if (config && !config.isTokenizer) {                ret = new Ext.parse.Tokenizer(config);            }             this.tokenizer = ret;        }    }}});