| /* |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| function FormattedContentBuilder(content, mapping, originalOffset, formattedOffset, indentString) |
| { |
| this._originalContent = content; |
| this._originalOffset = originalOffset; |
| this._lastOriginalPosition = 0; |
| |
| this._formattedContent = []; |
| this._formattedContentLength = 0; |
| this._formattedOffset = formattedOffset; |
| this._lastFormattedPosition = 0; |
| |
| this._mapping = mapping; |
| |
| this._lineNumber = 0; |
| this._nestingLevel = 0; |
| this._indentString = indentString; |
| this._cachedIndents = {}; |
| } |
| |
| FormattedContentBuilder.prototype = { |
| addToken: function(token) |
| { |
| for (var i = 0; i < token.comments_before.length; ++i) |
| this._addComment(token.comments_before[i]); |
| |
| while (this._lineNumber < token.line) { |
| this._addText("\n"); |
| this._addIndent(); |
| this._needNewLine = false; |
| this._lineNumber += 1; |
| } |
| |
| if (this._needNewLine) { |
| this._addText("\n"); |
| this._addIndent(); |
| this._needNewLine = false; |
| } |
| |
| this._addMappingIfNeeded(token.pos); |
| this._addText(this._originalContent.substring(token.pos, token.endPos)); |
| this._lineNumber = token.endLine; |
| }, |
| |
| addSpace: function() |
| { |
| this._addText(" "); |
| }, |
| |
| addNewLine: function() |
| { |
| this._needNewLine = true; |
| }, |
| |
| increaseNestingLevel: function() |
| { |
| this._nestingLevel += 1; |
| }, |
| |
| decreaseNestingLevel: function() |
| { |
| this._nestingLevel -= 1; |
| }, |
| |
| content: function() |
| { |
| return this._formattedContent.join(""); |
| }, |
| |
| mapping: function() |
| { |
| return { original: this._originalPositions, formatted: this._formattedPositions }; |
| }, |
| |
| _addIndent: function() |
| { |
| if (this._cachedIndents[this._nestingLevel]) { |
| this._addText(this._cachedIndents[this._nestingLevel]); |
| return; |
| } |
| |
| var fullIndent = ""; |
| for (var i = 0; i < this._nestingLevel; ++i) |
| fullIndent += this._indentString; |
| this._addText(fullIndent); |
| |
| // Cache a maximum of 20 nesting level indents. |
| if (this._nestingLevel <= 20) |
| this._cachedIndents[this._nestingLevel] = fullIndent; |
| }, |
| |
| _addComment: function(comment) |
| { |
| if (this._lineNumber < comment.line) { |
| for (var j = this._lineNumber; j < comment.line; ++j) |
| this._addText("\n"); |
| this._lineNumber = comment.line; |
| this._needNewLine = false; |
| this._addIndent(); |
| } else |
| this.addSpace(); |
| |
| this._addMappingIfNeeded(comment.pos); |
| if (comment.type === "comment1") |
| this._addText("//"); |
| else |
| this._addText("/*"); |
| |
| this._addText(comment.value); |
| |
| if (comment.type !== "comment1") { |
| this._addText("*/"); |
| var position; |
| while ((position = comment.value.indexOf("\n", position + 1)) !== -1) |
| this._lineNumber += 1; |
| } |
| }, |
| |
| _addText: function(text) |
| { |
| this._formattedContent.push(text); |
| this._formattedContentLength += text.length; |
| }, |
| |
| _addMappingIfNeeded: function(originalPosition) |
| { |
| if (originalPosition - this._lastOriginalPosition === this._formattedContentLength - this._lastFormattedPosition) |
| return; |
| this._mapping.original.push(this._originalOffset + originalPosition); |
| this._lastOriginalPosition = originalPosition; |
| this._mapping.formatted.push(this._formattedOffset + this._formattedContentLength); |
| this._lastFormattedPosition = this._formattedContentLength; |
| } |
| } |
| |
| var tokens = [ |
| ["EOS"], |
| ["LPAREN", "("], ["RPAREN", ")"], ["LBRACK", "["], ["RBRACK", "]"], ["LBRACE", "{"], ["RBRACE", "}"], ["COLON", ":"], ["SEMICOLON", ";"], ["PERIOD", "."], ["CONDITIONAL", "?"], |
| ["INC", "++"], ["DEC", "--"], |
| ["ASSIGN", "="], ["ASSIGN_BIT_OR", "|="], ["ASSIGN_BIT_XOR", "^="], ["ASSIGN_BIT_AND", "&="], ["ASSIGN_SHL", "<<="], ["ASSIGN_SAR", ">>="], ["ASSIGN_SHR", ">>>="], |
| ["ASSIGN_ADD", "+="], ["ASSIGN_SUB", "-="], ["ASSIGN_MUL", "*="], ["ASSIGN_DIV", "/="], ["ASSIGN_MOD", "%="], |
| ["COMMA", ","], ["OR", "||"], ["AND", "&&"], ["BIT_OR", "|"], ["BIT_XOR", "^"], ["BIT_AND", "&"], ["SHL", "<<"], ["SAR", ">>"], ["SHR", ">>>"], |
| ["ADD", "+"], ["SUB", "-"], ["MUL", "*"], ["DIV", "/"], ["MOD", "%"], |
| ["EQ", "=="], ["NE", "!="], ["EQ_STRICT", "==="], ["NE_STRICT", "!=="], ["LT", "<"], ["GT", ">"], ["LTE", "<="], ["GTE", ">="], |
| ["INSTANCEOF", "instanceof"], ["IN", "in"], ["NOT", "!"], ["BIT_NOT", "~"], ["DELETE", "delete"], ["TYPEOF", "typeof"], ["VOID", "void"], |
| ["BREAK", "break"], ["CASE", "case"], ["CATCH", "catch"], ["CONTINUE", "continue"], ["DEBUGGER", "debugger"], ["DEFAULT", "default"], ["DO", "do"], ["ELSE", "else"], ["FINALLY", "finally"], |
| ["FOR", "for"], ["FUNCTION", "function"], ["IF", "if"], ["NEW", "new"], ["RETURN", "return"], ["SWITCH", "switch"], ["THIS", "this"], ["THROW", "throw"], ["TRY", "try"], ["VAR", "var"], |
| ["WHILE", "while"], ["WITH", "with"], ["NULL_LITERAL", "null"], ["TRUE_LITERAL", "true"], ["FALSE_LITERAL", "false"], ["NUMBER"], ["STRING"], ["IDENTIFIER"], ["CONST", "const"] |
| ]; |
| |
| var Tokens = {}; |
| for (var i = 0; i < tokens.length; ++i) |
| Tokens[tokens[i][0]] = i; |
| |
| var TokensByValue = {}; |
| for (var i = 0; i < tokens.length; ++i) { |
| if (tokens[i][1]) |
| TokensByValue[tokens[i][1]] = i; |
| } |
| |
| var TokensByType = { |
| "eof": Tokens.EOS, |
| "name": Tokens.IDENTIFIER, |
| "num": Tokens.NUMBER, |
| "regexp": Tokens.DIV, |
| "string": Tokens.STRING |
| }; |
| |
| function Tokenizer(content) |
| { |
| this._readNextToken = parse.tokenizer(content); |
| this._state = this._readNextToken.context(); |
| } |
| |
| Tokenizer.prototype = { |
| content: function() |
| { |
| return this._state.text; |
| }, |
| |
| next: function(forceRegexp) |
| { |
| var uglifyToken = this._readNextToken(forceRegexp); |
| uglifyToken.endPos = this._state.pos; |
| uglifyToken.endLine = this._state.line; |
| uglifyToken.token = this._convertUglifyToken(uglifyToken); |
| return uglifyToken; |
| }, |
| |
| _convertUglifyToken: function(uglifyToken) |
| { |
| var token = TokensByType[uglifyToken.type]; |
| if (typeof token === "number") |
| return token; |
| token = TokensByValue[uglifyToken.value]; |
| if (typeof token === "number") |
| return token; |
| throw "Unknown token type " + uglifyToken.type; |
| } |
| } |
| |
| function JavaScriptFormatter(tokenizer, builder) |
| { |
| this._tokenizer = tokenizer; |
| this._builder = builder; |
| this._token = null; |
| this._nextToken = this._tokenizer.next(); |
| } |
| |
| JavaScriptFormatter.prototype = { |
| format: function() |
| { |
| this._parseSourceElements(Tokens.EOS); |
| this._consume(Tokens.EOS); |
| }, |
| |
| _peek: function() |
| { |
| return this._nextToken.token; |
| }, |
| |
| _next: function() |
| { |
| if (this._token && this._token.token === Tokens.EOS) |
| throw "Unexpected EOS token"; |
| |
| this._builder.addToken(this._nextToken); |
| this._token = this._nextToken; |
| this._nextToken = this._tokenizer.next(this._forceRegexp); |
| this._forceRegexp = false; |
| return this._token.token; |
| }, |
| |
| _consume: function(token) |
| { |
| var next = this._next(); |
| if (next !== token) |
| throw "Unexpected token in consume: expected " + token + ", actual " + next; |
| }, |
| |
| _expect: function(token) |
| { |
| var next = this._next(); |
| if (next !== token) |
| throw "Unexpected token: expected " + token + ", actual " + next; |
| }, |
| |
| _expectSemicolon: function() |
| { |
| if (this._peek() === Tokens.SEMICOLON) |
| this._consume(Tokens.SEMICOLON); |
| }, |
| |
| _hasLineTerminatorBeforeNext: function() |
| { |
| return this._nextToken.nlb; |
| }, |
| |
| _parseSourceElements: function(endToken) |
| { |
| while (this._peek() !== endToken) { |
| this._parseStatement(); |
| this._builder.addNewLine(); |
| } |
| }, |
| |
| _parseStatementOrBlock: function() |
| { |
| if (this._peek() === Tokens.LBRACE) { |
| this._builder.addSpace(); |
| this._parseBlock(); |
| return true; |
| } |
| |
| this._builder.addNewLine(); |
| this._builder.increaseNestingLevel(); |
| this._parseStatement(); |
| this._builder.decreaseNestingLevel(); |
| }, |
| |
| _parseStatement: function() |
| { |
| switch (this._peek()) { |
| case Tokens.LBRACE: |
| return this._parseBlock(); |
| case Tokens.CONST: |
| case Tokens.VAR: |
| return this._parseVariableStatement(); |
| case Tokens.SEMICOLON: |
| return this._next(); |
| case Tokens.IF: |
| return this._parseIfStatement(); |
| case Tokens.DO: |
| return this._parseDoWhileStatement(); |
| case Tokens.WHILE: |
| return this._parseWhileStatement(); |
| case Tokens.FOR: |
| return this._parseForStatement(); |
| case Tokens.CONTINUE: |
| return this._parseContinueStatement(); |
| case Tokens.BREAK: |
| return this._parseBreakStatement(); |
| case Tokens.RETURN: |
| return this._parseReturnStatement(); |
| case Tokens.WITH: |
| return this._parseWithStatement(); |
| case Tokens.SWITCH: |
| return this._parseSwitchStatement(); |
| case Tokens.THROW: |
| return this._parseThrowStatement(); |
| case Tokens.TRY: |
| return this._parseTryStatement(); |
| case Tokens.FUNCTION: |
| return this._parseFunctionDeclaration(); |
| case Tokens.DEBUGGER: |
| return this._parseDebuggerStatement(); |
| default: |
| return this._parseExpressionOrLabelledStatement(); |
| } |
| }, |
| |
| _parseFunctionDeclaration: function() |
| { |
| this._expect(Tokens.FUNCTION); |
| this._builder.addSpace(); |
| this._expect(Tokens.IDENTIFIER); |
| this._parseFunctionLiteral() |
| }, |
| |
| _parseBlock: function() |
| { |
| this._expect(Tokens.LBRACE); |
| this._builder.addNewLine(); |
| this._builder.increaseNestingLevel(); |
| while (this._peek() !== Tokens.RBRACE) { |
| this._parseStatement(); |
| this._builder.addNewLine(); |
| } |
| this._builder.decreaseNestingLevel(); |
| this._expect(Tokens.RBRACE); |
| }, |
| |
| _parseVariableStatement: function() |
| { |
| this._parseVariableDeclarations(); |
| this._expectSemicolon(); |
| }, |
| |
| _parseVariableDeclarations: function() |
| { |
| if (this._peek() === Tokens.VAR) |
| this._consume(Tokens.VAR); |
| else |
| this._consume(Tokens.CONST) |
| this._builder.addSpace(); |
| |
| var isFirstVariable = true; |
| do { |
| if (!isFirstVariable) { |
| this._consume(Tokens.COMMA); |
| this._builder.addSpace(); |
| } |
| isFirstVariable = false; |
| this._expect(Tokens.IDENTIFIER); |
| if (this._peek() === Tokens.ASSIGN) { |
| this._builder.addSpace(); |
| this._consume(Tokens.ASSIGN); |
| this._builder.addSpace(); |
| this._parseAssignmentExpression(); |
| } |
| } while (this._peek() === Tokens.COMMA); |
| }, |
| |
| _parseExpressionOrLabelledStatement: function() |
| { |
| this._parseExpression(); |
| if (this._peek() === Tokens.COLON) { |
| this._expect(Tokens.COLON); |
| this._builder.addSpace(); |
| this._parseStatement(); |
| } |
| this._expectSemicolon(); |
| }, |
| |
| _parseIfStatement: function() |
| { |
| this._expect(Tokens.IF); |
| this._builder.addSpace(); |
| this._expect(Tokens.LPAREN); |
| this._parseExpression(); |
| this._expect(Tokens.RPAREN); |
| |
| var isBlock = this._parseStatementOrBlock(); |
| if (this._peek() === Tokens.ELSE) { |
| if (isBlock) |
| this._builder.addSpace(); |
| else |
| this._builder.addNewLine(); |
| this._next(); |
| |
| if (this._peek() === Tokens.IF) { |
| this._builder.addSpace(); |
| this._parseStatement(); |
| } else |
| this._parseStatementOrBlock(); |
| } |
| }, |
| |
| _parseContinueStatement: function() |
| { |
| this._expect(Tokens.CONTINUE); |
| var token = this._peek(); |
| if (!this._hasLineTerminatorBeforeNext() && token !== Tokens.SEMICOLON && token !== Tokens.RBRACE && token !== Tokens.EOS) { |
| this._builder.addSpace(); |
| this._expect(Tokens.IDENTIFIER); |
| } |
| this._expectSemicolon(); |
| }, |
| |
| _parseBreakStatement: function() |
| { |
| this._expect(Tokens.BREAK); |
| var token = this._peek(); |
| if (!this._hasLineTerminatorBeforeNext() && token !== Tokens.SEMICOLON && token !== Tokens.RBRACE && token !== Tokens.EOS) { |
| this._builder.addSpace(); |
| this._expect(Tokens.IDENTIFIER); |
| } |
| this._expectSemicolon(); |
| }, |
| |
| _parseReturnStatement: function() |
| { |
| this._expect(Tokens.RETURN); |
| var token = this._peek(); |
| if (!this._hasLineTerminatorBeforeNext() && token !== Tokens.SEMICOLON && token !== Tokens.RBRACE && token !== Tokens.EOS) { |
| this._builder.addSpace(); |
| this._parseExpression(); |
| } |
| this._expectSemicolon(); |
| }, |
| |
| _parseWithStatement: function() |
| { |
| this._expect(Tokens.WITH); |
| this._builder.addSpace(); |
| this._expect(Tokens.LPAREN); |
| this._parseExpression(); |
| this._expect(Tokens.RPAREN); |
| this._parseStatementOrBlock(); |
| }, |
| |
| _parseCaseClause: function() |
| { |
| if (this._peek() === Tokens.CASE) { |
| this._expect(Tokens.CASE); |
| this._builder.addSpace(); |
| this._parseExpression(); |
| } else |
| this._expect(Tokens.DEFAULT); |
| this._expect(Tokens.COLON); |
| this._builder.addNewLine(); |
| |
| this._builder.increaseNestingLevel(); |
| while (this._peek() !== Tokens.CASE && this._peek() !== Tokens.DEFAULT && this._peek() !== Tokens.RBRACE) { |
| this._parseStatement(); |
| this._builder.addNewLine(); |
| } |
| this._builder.decreaseNestingLevel(); |
| }, |
| |
| _parseSwitchStatement: function() |
| { |
| this._expect(Tokens.SWITCH); |
| this._builder.addSpace(); |
| this._expect(Tokens.LPAREN); |
| this._parseExpression(); |
| this._expect(Tokens.RPAREN); |
| this._builder.addSpace(); |
| |
| this._expect(Tokens.LBRACE); |
| this._builder.addNewLine(); |
| this._builder.increaseNestingLevel(); |
| while (this._peek() !== Tokens.RBRACE) |
| this._parseCaseClause(); |
| this._builder.decreaseNestingLevel(); |
| this._expect(Tokens.RBRACE); |
| }, |
| |
| _parseThrowStatement: function() |
| { |
| this._expect(Tokens.THROW); |
| this._builder.addSpace(); |
| this._parseExpression(); |
| this._expectSemicolon(); |
| }, |
| |
| _parseTryStatement: function() |
| { |
| this._expect(Tokens.TRY); |
| this._builder.addSpace(); |
| this._parseBlock(); |
| |
| var token = this._peek(); |
| if (token === Tokens.CATCH) { |
| this._builder.addSpace(); |
| this._consume(Tokens.CATCH); |
| this._builder.addSpace(); |
| this._expect(Tokens.LPAREN); |
| this._expect(Tokens.IDENTIFIER); |
| this._expect(Tokens.RPAREN); |
| this._builder.addSpace(); |
| this._parseBlock(); |
| token = this._peek(); |
| } |
| |
| if (token === Tokens.FINALLY) { |
| this._consume(Tokens.FINALLY); |
| this._builder.addSpace(); |
| this._parseBlock(); |
| } |
| }, |
| |
| _parseDoWhileStatement: function() |
| { |
| this._expect(Tokens.DO); |
| var isBlock = this._parseStatementOrBlock(); |
| if (isBlock) |
| this._builder.addSpace(); |
| else |
| this._builder.addNewLine(); |
| this._expect(Tokens.WHILE); |
| this._builder.addSpace(); |
| this._expect(Tokens.LPAREN); |
| this._parseExpression(); |
| this._expect(Tokens.RPAREN); |
| this._expectSemicolon(); |
| }, |
| |
| _parseWhileStatement: function() |
| { |
| this._expect(Tokens.WHILE); |
| this._builder.addSpace(); |
| this._expect(Tokens.LPAREN); |
| this._parseExpression(); |
| this._expect(Tokens.RPAREN); |
| this._parseStatementOrBlock(); |
| }, |
| |
| _parseForStatement: function() |
| { |
| this._expect(Tokens.FOR); |
| this._builder.addSpace(); |
| this._expect(Tokens.LPAREN); |
| if (this._peek() !== Tokens.SEMICOLON) { |
| if (this._peek() === Tokens.VAR || this._peek() === Tokens.CONST) { |
| this._parseVariableDeclarations(); |
| if (this._peek() === Tokens.IN) { |
| this._builder.addSpace(); |
| this._consume(Tokens.IN); |
| this._builder.addSpace(); |
| this._parseExpression(); |
| } |
| } else |
| this._parseExpression(); |
| } |
| |
| if (this._peek() !== Tokens.RPAREN) { |
| this._expect(Tokens.SEMICOLON); |
| this._builder.addSpace(); |
| if (this._peek() !== Tokens.SEMICOLON) |
| this._parseExpression(); |
| this._expect(Tokens.SEMICOLON); |
| this._builder.addSpace(); |
| if (this._peek() !== Tokens.RPAREN) |
| this._parseExpression(); |
| } |
| this._expect(Tokens.RPAREN); |
| |
| this._parseStatementOrBlock(); |
| }, |
| |
| _parseExpression: function() |
| { |
| this._parseAssignmentExpression(); |
| while (this._peek() === Tokens.COMMA) { |
| this._expect(Tokens.COMMA); |
| this._builder.addSpace(); |
| this._parseAssignmentExpression(); |
| } |
| }, |
| |
| _parseAssignmentExpression: function() |
| { |
| this._parseConditionalExpression(); |
| var token = this._peek(); |
| if (Tokens.ASSIGN <= token && token <= Tokens.ASSIGN_MOD) { |
| this._builder.addSpace(); |
| this._next(); |
| this._builder.addSpace(); |
| this._parseAssignmentExpression(); |
| } |
| }, |
| |
| _parseConditionalExpression: function() |
| { |
| this._parseBinaryExpression(); |
| if (this._peek() === Tokens.CONDITIONAL) { |
| this._builder.addSpace(); |
| this._consume(Tokens.CONDITIONAL); |
| this._builder.addSpace(); |
| this._parseAssignmentExpression(); |
| this._builder.addSpace(); |
| this._expect(Tokens.COLON); |
| this._builder.addSpace(); |
| this._parseAssignmentExpression(); |
| } |
| }, |
| |
| _parseBinaryExpression: function() |
| { |
| this._parseUnaryExpression(); |
| var token = this._peek(); |
| while (Tokens.OR <= token && token <= Tokens.IN) { |
| this._builder.addSpace(); |
| this._next(); |
| this._builder.addSpace(); |
| this._parseBinaryExpression(); |
| token = this._peek(); |
| } |
| }, |
| |
| _parseUnaryExpression: function() |
| { |
| var token = this._peek(); |
| if ((Tokens.NOT <= token && token <= Tokens.VOID) || token === Tokens.ADD || token === Tokens.SUB || token === Tokens.INC || token === Tokens.DEC) { |
| this._next(); |
| if (token === Tokens.DELETE || token === Tokens.TYPEOF || token === Tokens.VOID) |
| this._builder.addSpace(); |
| this._parseUnaryExpression(); |
| } else |
| return this._parsePostfixExpression(); |
| }, |
| |
| _parsePostfixExpression: function() |
| { |
| this._parseLeftHandSideExpression(); |
| var token = this._peek(); |
| if (!this._hasLineTerminatorBeforeNext() && (token === Tokens.INC || token === Tokens.DEC)) |
| this._next(); |
| }, |
| |
| _parseLeftHandSideExpression: function() |
| { |
| if (this._peek() === Tokens.NEW) |
| this._parseNewExpression(); |
| else |
| this._parseMemberExpression(); |
| |
| while (true) { |
| switch (this._peek()) { |
| case Tokens.LBRACK: |
| this._consume(Tokens.LBRACK); |
| this._parseExpression(); |
| this._expect(Tokens.RBRACK); |
| break; |
| |
| case Tokens.LPAREN: |
| this._parseArguments(); |
| break; |
| |
| case Tokens.PERIOD: |
| this._consume(Tokens.PERIOD); |
| this._expect(Tokens.IDENTIFIER); |
| break; |
| |
| default: |
| return; |
| } |
| } |
| }, |
| |
| _parseNewExpression: function() |
| { |
| this._expect(Tokens.NEW); |
| this._builder.addSpace(); |
| if (this._peek() === Tokens.NEW) |
| this._parseNewExpression(); |
| else |
| this._parseMemberExpression(); |
| }, |
| |
| _parseMemberExpression: function() |
| { |
| if (this._peek() === Tokens.FUNCTION) { |
| this._expect(Tokens.FUNCTION); |
| if (this._peek() === Tokens.IDENTIFIER) { |
| this._builder.addSpace(); |
| this._expect(Tokens.IDENTIFIER); |
| } |
| this._parseFunctionLiteral(); |
| } else |
| this._parsePrimaryExpression(); |
| |
| while (true) { |
| switch (this._peek()) { |
| case Tokens.LBRACK: |
| this._consume(Tokens.LBRACK); |
| this._parseExpression(); |
| this._expect(Tokens.RBRACK); |
| break; |
| |
| case Tokens.PERIOD: |
| this._consume(Tokens.PERIOD); |
| this._expect(Tokens.IDENTIFIER); |
| break; |
| |
| case Tokens.LPAREN: |
| this._parseArguments(); |
| break; |
| |
| default: |
| return; |
| } |
| } |
| }, |
| |
| _parseDebuggerStatement: function() |
| { |
| this._expect(Tokens.DEBUGGER); |
| this._expectSemicolon(); |
| }, |
| |
| _parsePrimaryExpression: function() |
| { |
| switch (this._peek()) { |
| case Tokens.THIS: |
| return this._consume(Tokens.THIS); |
| case Tokens.NULL_LITERAL: |
| return this._consume(Tokens.NULL_LITERAL); |
| case Tokens.TRUE_LITERAL: |
| return this._consume(Tokens.TRUE_LITERAL); |
| case Tokens.FALSE_LITERAL: |
| return this._consume(Tokens.FALSE_LITERAL); |
| case Tokens.IDENTIFIER: |
| return this._consume(Tokens.IDENTIFIER); |
| case Tokens.NUMBER: |
| return this._consume(Tokens.NUMBER); |
| case Tokens.STRING: |
| return this._consume(Tokens.STRING); |
| case Tokens.ASSIGN_DIV: |
| return this._parseRegExpLiteral(); |
| case Tokens.DIV: |
| return this._parseRegExpLiteral(); |
| case Tokens.LBRACK: |
| return this._parseArrayLiteral(); |
| case Tokens.LBRACE: |
| return this._parseObjectLiteral(); |
| case Tokens.LPAREN: |
| this._consume(Tokens.LPAREN); |
| this._parseExpression(); |
| this._expect(Tokens.RPAREN); |
| return; |
| default: |
| return this._next(); |
| } |
| }, |
| |
| _parseArrayLiteral: function() |
| { |
| this._expect(Tokens.LBRACK); |
| this._builder.increaseNestingLevel(); |
| while (this._peek() !== Tokens.RBRACK) { |
| if (this._peek() !== Tokens.COMMA) |
| this._parseAssignmentExpression(); |
| if (this._peek() !== Tokens.RBRACK) { |
| this._expect(Tokens.COMMA); |
| this._builder.addSpace(); |
| } |
| } |
| this._builder.decreaseNestingLevel(); |
| this._expect(Tokens.RBRACK); |
| }, |
| |
| _parseObjectLiteralGetSet: function() |
| { |
| var token = this._peek(); |
| if (token === Tokens.IDENTIFIER || token === Tokens.NUMBER || token === Tokens.STRING || |
| Tokens.DELETE <= token && token <= Tokens.FALSE_LITERAL || |
| token === Tokens.INSTANCEOF || token === Tokens.IN || token === Tokens.CONST) { |
| this._next(); |
| this._parseFunctionLiteral(); |
| } |
| }, |
| |
| _parseObjectLiteral: function() |
| { |
| this._expect(Tokens.LBRACE); |
| this._builder.increaseNestingLevel(); |
| while (this._peek() !== Tokens.RBRACE) { |
| var token = this._peek(); |
| switch (token) { |
| case Tokens.IDENTIFIER: |
| this._consume(Tokens.IDENTIFIER); |
| var name = this._token.value; |
| if ((name === "get" || name === "set") && this._peek() !== Tokens.COLON) { |
| this._builder.addSpace(); |
| this._parseObjectLiteralGetSet(); |
| if (this._peek() !== Tokens.RBRACE) { |
| this._expect(Tokens.COMMA); |
| } |
| continue; |
| } |
| break; |
| |
| case Tokens.STRING: |
| this._consume(Tokens.STRING); |
| break; |
| |
| case Tokens.NUMBER: |
| this._consume(Tokens.NUMBER); |
| break; |
| |
| default: |
| this._next(); |
| } |
| |
| this._expect(Tokens.COLON); |
| this._builder.addSpace(); |
| this._parseAssignmentExpression(); |
| if (this._peek() !== Tokens.RBRACE) { |
| this._expect(Tokens.COMMA); |
| } |
| } |
| this._builder.decreaseNestingLevel(); |
| |
| this._expect(Tokens.RBRACE); |
| }, |
| |
| _parseRegExpLiteral: function() |
| { |
| if (this._nextToken.type === "regexp") |
| this._next(); |
| else { |
| this._forceRegexp = true; |
| this._next(); |
| } |
| }, |
| |
| _parseArguments: function() |
| { |
| this._expect(Tokens.LPAREN); |
| var done = (this._peek() === Tokens.RPAREN); |
| while (!done) { |
| this._parseAssignmentExpression(); |
| done = (this._peek() === Tokens.RPAREN); |
| if (!done) { |
| this._expect(Tokens.COMMA); |
| this._builder.addSpace(); |
| } |
| } |
| this._expect(Tokens.RPAREN); |
| }, |
| |
| _parseFunctionLiteral: function() |
| { |
| this._expect(Tokens.LPAREN); |
| var done = (this._peek() === Tokens.RPAREN); |
| while (!done) { |
| this._expect(Tokens.IDENTIFIER); |
| done = (this._peek() === Tokens.RPAREN); |
| if (!done) { |
| this._expect(Tokens.COMMA); |
| this._builder.addSpace(); |
| } |
| } |
| this._expect(Tokens.RPAREN); |
| this._builder.addSpace(); |
| |
| this._expect(Tokens.LBRACE); |
| this._builder.addNewLine(); |
| this._builder.increaseNestingLevel(); |
| this._parseSourceElements(Tokens.RBRACE); |
| this._builder.decreaseNestingLevel(); |
| this._expect(Tokens.RBRACE); |
| } |
| } |