Home | History | Annotate | Download | only in script_formatter_worker
      1 /*
      2  * Copyright (C) 2011 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 /**
     32  * @constructor
     33  * @param {!FormatterWorker.JavaScriptTokenizer} tokenizer
     34  * @param {!FormatterWorker.JavaScriptFormattedContentBuilder} builder
     35  */
     36 FormatterWorker.JavaScriptFormatter = function(tokenizer, builder)
     37 {
     38     this._tokenizer = tokenizer;
     39     this._builder = builder;
     40     this._token = null;
     41     this._nextToken = this._tokenizer.next();
     42 }
     43 
     44 FormatterWorker.JavaScriptFormatter.prototype = {
     45     format: function()
     46     {
     47         this._parseSourceElements(FormatterWorker.JavaScriptTokens.EOS);
     48         this._consume(FormatterWorker.JavaScriptTokens.EOS);
     49     },
     50 
     51     /**
     52      * @return {number}
     53      */
     54     _peek: function()
     55     {
     56         return this._nextToken.token;
     57     },
     58 
     59     /**
     60      * @return {number}
     61      */
     62     _next: function()
     63     {
     64         if (this._token && this._token.token === FormatterWorker.JavaScriptTokens.EOS)
     65             throw "Unexpected EOS token";
     66 
     67         this._builder.addToken(this._nextToken);
     68         this._token = this._nextToken;
     69         this._nextToken = this._tokenizer.next(this._forceRegexp);
     70         this._forceRegexp = false;
     71         return this._token.token;
     72     },
     73 
     74     /**
     75      * @param {number} token
     76      */
     77     _consume: function(token)
     78     {
     79         var next = this._next();
     80         if (next !== token)
     81             throw "Unexpected token in consume: expected " + token + ", actual " + next;
     82     },
     83 
     84     /**
     85      * @param {number} token
     86      */
     87     _expect: function(token)
     88     {
     89         var next = this._next();
     90         if (next !== token)
     91             throw "Unexpected token: expected " + token + ", actual " + next;
     92     },
     93 
     94     _expectSemicolon: function()
     95     {
     96         if (this._peek() === FormatterWorker.JavaScriptTokens.SEMICOLON)
     97             this._consume(FormatterWorker.JavaScriptTokens.SEMICOLON);
     98     },
     99 
    100     /**
    101      * @return {boolean}
    102      */
    103     _hasLineTerminatorBeforeNext: function()
    104     {
    105         return this._nextToken.nlb;
    106     },
    107 
    108     /**
    109      * @param {number} endToken
    110      */
    111     _parseSourceElements: function(endToken)
    112     {
    113         while (this._peek() !== endToken) {
    114             this._parseStatement();
    115             this._builder.addNewLine();
    116         }
    117     },
    118 
    119     _parseStatementOrBlock: function()
    120     {
    121         if (this._peek() === FormatterWorker.JavaScriptTokens.LBRACE) {
    122             this._builder.addSpace();
    123             this._parseBlock();
    124             return true;
    125         }
    126 
    127         this._builder.addNewLine();
    128         this._builder.increaseNestingLevel();
    129         this._parseStatement();
    130         this._builder.decreaseNestingLevel();
    131     },
    132 
    133     _parseStatement: function()
    134     {
    135         switch (this._peek()) {
    136         case FormatterWorker.JavaScriptTokens.LBRACE:
    137             return this._parseBlock();
    138         case FormatterWorker.JavaScriptTokens.CONST:
    139         case FormatterWorker.JavaScriptTokens.VAR:
    140             return this._parseVariableStatement();
    141         case FormatterWorker.JavaScriptTokens.SEMICOLON:
    142             return this._next();
    143         case FormatterWorker.JavaScriptTokens.IF:
    144             return this._parseIfStatement();
    145         case FormatterWorker.JavaScriptTokens.DO:
    146             return this._parseDoWhileStatement();
    147         case FormatterWorker.JavaScriptTokens.WHILE:
    148             return this._parseWhileStatement();
    149         case FormatterWorker.JavaScriptTokens.FOR:
    150             return this._parseForStatement();
    151         case FormatterWorker.JavaScriptTokens.CONTINUE:
    152             return this._parseContinueStatement();
    153         case FormatterWorker.JavaScriptTokens.BREAK:
    154             return this._parseBreakStatement();
    155         case FormatterWorker.JavaScriptTokens.RETURN:
    156             return this._parseReturnStatement();
    157         case FormatterWorker.JavaScriptTokens.WITH:
    158             return this._parseWithStatement();
    159         case FormatterWorker.JavaScriptTokens.SWITCH:
    160             return this._parseSwitchStatement();
    161         case FormatterWorker.JavaScriptTokens.THROW:
    162             return this._parseThrowStatement();
    163         case FormatterWorker.JavaScriptTokens.TRY:
    164             return this._parseTryStatement();
    165         case FormatterWorker.JavaScriptTokens.FUNCTION:
    166             return this._parseFunctionDeclaration();
    167         case FormatterWorker.JavaScriptTokens.DEBUGGER:
    168             return this._parseDebuggerStatement();
    169         default:
    170             return this._parseExpressionOrLabelledStatement();
    171         }
    172     },
    173 
    174     _parseFunctionDeclaration: function()
    175     {
    176         this._expect(FormatterWorker.JavaScriptTokens.FUNCTION);
    177         this._builder.addSpace();
    178         this._expect(FormatterWorker.JavaScriptTokens.IDENTIFIER);
    179         this._parseFunctionLiteral()
    180     },
    181 
    182     _parseBlock: function()
    183     {
    184         this._expect(FormatterWorker.JavaScriptTokens.LBRACE);
    185         this._builder.addNewLine();
    186         this._builder.increaseNestingLevel();
    187         while (this._peek() !== FormatterWorker.JavaScriptTokens.RBRACE) {
    188             this._parseStatement();
    189             this._builder.addNewLine();
    190         }
    191         this._builder.decreaseNestingLevel();
    192         this._expect(FormatterWorker.JavaScriptTokens.RBRACE);
    193     },
    194 
    195     _parseVariableStatement: function()
    196     {
    197         this._parseVariableDeclarations();
    198         this._expectSemicolon();
    199     },
    200 
    201     _parseVariableDeclarations: function()
    202     {
    203         if (this._peek() === FormatterWorker.JavaScriptTokens.VAR)
    204             this._consume(FormatterWorker.JavaScriptTokens.VAR);
    205         else
    206             this._consume(FormatterWorker.JavaScriptTokens.CONST)
    207         this._builder.addSpace();
    208 
    209         var isFirstVariable = true;
    210         do {
    211             if (!isFirstVariable) {
    212                 this._consume(FormatterWorker.JavaScriptTokens.COMMA);
    213                 this._builder.addSpace();
    214             }
    215             isFirstVariable = false;
    216             this._expect(FormatterWorker.JavaScriptTokens.IDENTIFIER);
    217             if (this._peek() === FormatterWorker.JavaScriptTokens.ASSIGN) {
    218                 this._builder.addSpace();
    219                 this._consume(FormatterWorker.JavaScriptTokens.ASSIGN);
    220                 this._builder.addSpace();
    221                 this._parseAssignmentExpression();
    222             }
    223         } while (this._peek() === FormatterWorker.JavaScriptTokens.COMMA);
    224     },
    225 
    226     _parseExpressionOrLabelledStatement: function()
    227     {
    228         this._parseExpression();
    229         if (this._peek() === FormatterWorker.JavaScriptTokens.COLON) {
    230             this._expect(FormatterWorker.JavaScriptTokens.COLON);
    231             this._builder.addSpace();
    232             this._parseStatement();
    233         }
    234         this._expectSemicolon();
    235     },
    236 
    237     _parseIfStatement: function()
    238     {
    239         this._expect(FormatterWorker.JavaScriptTokens.IF);
    240         this._builder.addSpace();
    241         this._expect(FormatterWorker.JavaScriptTokens.LPAREN);
    242         this._parseExpression();
    243         this._expect(FormatterWorker.JavaScriptTokens.RPAREN);
    244 
    245         var isBlock = this._parseStatementOrBlock();
    246         if (this._peek() === FormatterWorker.JavaScriptTokens.ELSE) {
    247             if (isBlock)
    248                 this._builder.addSpace();
    249             else
    250                 this._builder.addNewLine();
    251             this._next();
    252 
    253             if (this._peek() === FormatterWorker.JavaScriptTokens.IF) {
    254                 this._builder.addSpace();
    255                 this._parseStatement();
    256             } else
    257                 this._parseStatementOrBlock();
    258         }
    259     },
    260 
    261     _parseContinueStatement: function()
    262     {
    263         this._expect(FormatterWorker.JavaScriptTokens.CONTINUE);
    264         var token = this._peek();
    265         if (!this._hasLineTerminatorBeforeNext() && token !== FormatterWorker.JavaScriptTokens.SEMICOLON && token !== FormatterWorker.JavaScriptTokens.RBRACE && token !== FormatterWorker.JavaScriptTokens.EOS) {
    266             this._builder.addSpace();
    267             this._expect(FormatterWorker.JavaScriptTokens.IDENTIFIER);
    268         }
    269         this._expectSemicolon();
    270     },
    271 
    272     _parseBreakStatement: function()
    273     {
    274         this._expect(FormatterWorker.JavaScriptTokens.BREAK);
    275         var token = this._peek();
    276         if (!this._hasLineTerminatorBeforeNext() && token !== FormatterWorker.JavaScriptTokens.SEMICOLON && token !== FormatterWorker.JavaScriptTokens.RBRACE && token !== FormatterWorker.JavaScriptTokens.EOS) {
    277             this._builder.addSpace();
    278             this._expect(FormatterWorker.JavaScriptTokens.IDENTIFIER);
    279         }
    280         this._expectSemicolon();
    281     },
    282 
    283     _parseReturnStatement: function()
    284     {
    285         this._expect(FormatterWorker.JavaScriptTokens.RETURN);
    286         var token = this._peek();
    287         if (!this._hasLineTerminatorBeforeNext() && token !== FormatterWorker.JavaScriptTokens.SEMICOLON && token !== FormatterWorker.JavaScriptTokens.RBRACE && token !== FormatterWorker.JavaScriptTokens.EOS) {
    288             this._builder.addSpace();
    289             this._parseExpression();
    290         }
    291         this._expectSemicolon();
    292     },
    293 
    294     _parseWithStatement: function()
    295     {
    296         this._expect(FormatterWorker.JavaScriptTokens.WITH);
    297         this._builder.addSpace();
    298         this._expect(FormatterWorker.JavaScriptTokens.LPAREN);
    299         this._parseExpression();
    300         this._expect(FormatterWorker.JavaScriptTokens.RPAREN);
    301         this._parseStatementOrBlock();
    302     },
    303 
    304     _parseCaseClause: function()
    305     {
    306         if (this._peek() === FormatterWorker.JavaScriptTokens.CASE) {
    307             this._expect(FormatterWorker.JavaScriptTokens.CASE);
    308             this._builder.addSpace();
    309             this._parseExpression();
    310         } else
    311             this._expect(FormatterWorker.JavaScriptTokens.DEFAULT);
    312         this._expect(FormatterWorker.JavaScriptTokens.COLON);
    313         this._builder.addNewLine();
    314 
    315         this._builder.increaseNestingLevel();
    316         while (this._peek() !== FormatterWorker.JavaScriptTokens.CASE && this._peek() !== FormatterWorker.JavaScriptTokens.DEFAULT && this._peek() !== FormatterWorker.JavaScriptTokens.RBRACE) {
    317             this._parseStatement();
    318             this._builder.addNewLine();
    319         }
    320         this._builder.decreaseNestingLevel();
    321     },
    322 
    323     _parseSwitchStatement: function()
    324     {
    325         this._expect(FormatterWorker.JavaScriptTokens.SWITCH);
    326         this._builder.addSpace();
    327         this._expect(FormatterWorker.JavaScriptTokens.LPAREN);
    328         this._parseExpression();
    329         this._expect(FormatterWorker.JavaScriptTokens.RPAREN);
    330         this._builder.addSpace();
    331 
    332         this._expect(FormatterWorker.JavaScriptTokens.LBRACE);
    333         this._builder.addNewLine();
    334         this._builder.increaseNestingLevel();
    335         while (this._peek() !== FormatterWorker.JavaScriptTokens.RBRACE)
    336             this._parseCaseClause();
    337         this._builder.decreaseNestingLevel();
    338         this._expect(FormatterWorker.JavaScriptTokens.RBRACE);
    339     },
    340 
    341     _parseThrowStatement: function()
    342     {
    343         this._expect(FormatterWorker.JavaScriptTokens.THROW);
    344         this._builder.addSpace();
    345         this._parseExpression();
    346         this._expectSemicolon();
    347     },
    348 
    349     _parseTryStatement: function()
    350     {
    351         this._expect(FormatterWorker.JavaScriptTokens.TRY);
    352         this._builder.addSpace();
    353         this._parseBlock();
    354 
    355         var token = this._peek();
    356         if (token === FormatterWorker.JavaScriptTokens.CATCH) {
    357             this._builder.addSpace();
    358             this._consume(FormatterWorker.JavaScriptTokens.CATCH);
    359             this._builder.addSpace();
    360             this._expect(FormatterWorker.JavaScriptTokens.LPAREN);
    361             this._expect(FormatterWorker.JavaScriptTokens.IDENTIFIER);
    362             this._expect(FormatterWorker.JavaScriptTokens.RPAREN);
    363             this._builder.addSpace();
    364             this._parseBlock();
    365             token = this._peek();
    366         }
    367 
    368         if (token === FormatterWorker.JavaScriptTokens.FINALLY) {
    369             this._consume(FormatterWorker.JavaScriptTokens.FINALLY);
    370             this._builder.addSpace();
    371             this._parseBlock();
    372         }
    373     },
    374 
    375     _parseDoWhileStatement: function()
    376     {
    377         this._expect(FormatterWorker.JavaScriptTokens.DO);
    378         var isBlock = this._parseStatementOrBlock();
    379         if (isBlock)
    380             this._builder.addSpace();
    381         else
    382             this._builder.addNewLine();
    383         this._expect(FormatterWorker.JavaScriptTokens.WHILE);
    384         this._builder.addSpace();
    385         this._expect(FormatterWorker.JavaScriptTokens.LPAREN);
    386         this._parseExpression();
    387         this._expect(FormatterWorker.JavaScriptTokens.RPAREN);
    388         this._expectSemicolon();
    389     },
    390 
    391     _parseWhileStatement: function()
    392     {
    393         this._expect(FormatterWorker.JavaScriptTokens.WHILE);
    394         this._builder.addSpace();
    395         this._expect(FormatterWorker.JavaScriptTokens.LPAREN);
    396         this._parseExpression();
    397         this._expect(FormatterWorker.JavaScriptTokens.RPAREN);
    398         this._parseStatementOrBlock();
    399     },
    400 
    401     _parseForStatement: function()
    402     {
    403         this._expect(FormatterWorker.JavaScriptTokens.FOR);
    404         this._builder.addSpace();
    405         this._expect(FormatterWorker.JavaScriptTokens.LPAREN);
    406         if (this._peek() !== FormatterWorker.JavaScriptTokens.SEMICOLON) {
    407             if (this._peek() === FormatterWorker.JavaScriptTokens.VAR || this._peek() === FormatterWorker.JavaScriptTokens.CONST) {
    408                 this._parseVariableDeclarations();
    409                 if (this._peek() === FormatterWorker.JavaScriptTokens.IN) {
    410                     this._builder.addSpace();
    411                     this._consume(FormatterWorker.JavaScriptTokens.IN);
    412                     this._builder.addSpace();
    413                     this._parseExpression();
    414                 }
    415             } else
    416                 this._parseExpression();
    417         }
    418 
    419         if (this._peek() !== FormatterWorker.JavaScriptTokens.RPAREN) {
    420             this._expect(FormatterWorker.JavaScriptTokens.SEMICOLON);
    421             this._builder.addSpace();
    422             if (this._peek() !== FormatterWorker.JavaScriptTokens.SEMICOLON)
    423                 this._parseExpression();
    424             this._expect(FormatterWorker.JavaScriptTokens.SEMICOLON);
    425             this._builder.addSpace();
    426             if (this._peek() !== FormatterWorker.JavaScriptTokens.RPAREN)
    427                 this._parseExpression();
    428         }
    429         this._expect(FormatterWorker.JavaScriptTokens.RPAREN);
    430 
    431         this._parseStatementOrBlock();
    432     },
    433 
    434     _parseExpression: function()
    435     {
    436         this._parseAssignmentExpression();
    437         while (this._peek() === FormatterWorker.JavaScriptTokens.COMMA) {
    438             this._expect(FormatterWorker.JavaScriptTokens.COMMA);
    439             this._builder.addSpace();
    440             this._parseAssignmentExpression();
    441         }
    442     },
    443 
    444     _parseAssignmentExpression: function()
    445     {
    446         this._parseConditionalExpression();
    447         var token = this._peek();
    448         if (FormatterWorker.JavaScriptTokens.ASSIGN <= token && token <= FormatterWorker.JavaScriptTokens.ASSIGN_MOD) {
    449             this._builder.addSpace();
    450             this._next();
    451             this._builder.addSpace();
    452             this._parseAssignmentExpression();
    453         }
    454     },
    455 
    456     _parseConditionalExpression: function()
    457     {
    458         this._parseBinaryExpression();
    459         if (this._peek() === FormatterWorker.JavaScriptTokens.CONDITIONAL) {
    460             this._builder.addSpace();
    461             this._consume(FormatterWorker.JavaScriptTokens.CONDITIONAL);
    462             this._builder.addSpace();
    463             this._parseAssignmentExpression();
    464             this._builder.addSpace();
    465             this._expect(FormatterWorker.JavaScriptTokens.COLON);
    466             this._builder.addSpace();
    467             this._parseAssignmentExpression();
    468         }
    469     },
    470 
    471     _parseBinaryExpression: function()
    472     {
    473         this._parseUnaryExpression();
    474         var token = this._peek();
    475         while (FormatterWorker.JavaScriptTokens.OR <= token && token <= FormatterWorker.JavaScriptTokens.IN) {
    476             this._builder.addSpace();
    477             this._next();
    478             this._builder.addSpace();
    479             this._parseBinaryExpression();
    480             token = this._peek();
    481         }
    482     },
    483 
    484     _parseUnaryExpression: function()
    485     {
    486         var token = this._peek();
    487         if ((FormatterWorker.JavaScriptTokens.NOT <= token && token <= FormatterWorker.JavaScriptTokens.VOID) || token === FormatterWorker.JavaScriptTokens.ADD || token === FormatterWorker.JavaScriptTokens.SUB || token ===  FormatterWorker.JavaScriptTokens.INC || token === FormatterWorker.JavaScriptTokens.DEC) {
    488             this._next();
    489             if (token === FormatterWorker.JavaScriptTokens.DELETE || token === FormatterWorker.JavaScriptTokens.TYPEOF || token === FormatterWorker.JavaScriptTokens.VOID)
    490                 this._builder.addSpace();
    491             this._parseUnaryExpression();
    492         } else
    493             return this._parsePostfixExpression();
    494     },
    495 
    496     _parsePostfixExpression: function()
    497     {
    498         this._parseLeftHandSideExpression();
    499         var token = this._peek();
    500         if (!this._hasLineTerminatorBeforeNext() && (token === FormatterWorker.JavaScriptTokens.INC || token === FormatterWorker.JavaScriptTokens.DEC))
    501             this._next();
    502     },
    503 
    504     _parseLeftHandSideExpression: function()
    505     {
    506         if (this._peek() === FormatterWorker.JavaScriptTokens.NEW)
    507             this._parseNewExpression();
    508         else
    509             this._parseMemberExpression();
    510 
    511         while (true) {
    512             switch (this._peek()) {
    513             case FormatterWorker.JavaScriptTokens.LBRACK:
    514                 this._consume(FormatterWorker.JavaScriptTokens.LBRACK);
    515                 this._parseExpression();
    516                 this._expect(FormatterWorker.JavaScriptTokens.RBRACK);
    517                 break;
    518 
    519             case FormatterWorker.JavaScriptTokens.LPAREN:
    520                 this._parseArguments();
    521                 break;
    522 
    523             case FormatterWorker.JavaScriptTokens.PERIOD:
    524                 this._consume(FormatterWorker.JavaScriptTokens.PERIOD);
    525                 this._expect(FormatterWorker.JavaScriptTokens.IDENTIFIER);
    526                 break;
    527 
    528             default:
    529                 return;
    530             }
    531         }
    532     },
    533 
    534     _parseNewExpression: function()
    535     {
    536         this._expect(FormatterWorker.JavaScriptTokens.NEW);
    537         this._builder.addSpace();
    538         if (this._peek() === FormatterWorker.JavaScriptTokens.NEW)
    539             this._parseNewExpression();
    540         else
    541             this._parseMemberExpression();
    542     },
    543 
    544     _parseMemberExpression: function()
    545     {
    546         if (this._peek() === FormatterWorker.JavaScriptTokens.FUNCTION) {
    547             this._expect(FormatterWorker.JavaScriptTokens.FUNCTION);
    548             if (this._peek() === FormatterWorker.JavaScriptTokens.IDENTIFIER) {
    549                 this._builder.addSpace();
    550                 this._expect(FormatterWorker.JavaScriptTokens.IDENTIFIER);
    551             }
    552             this._parseFunctionLiteral();
    553         } else
    554             this._parsePrimaryExpression();
    555 
    556         while (true) {
    557             switch (this._peek()) {
    558             case FormatterWorker.JavaScriptTokens.LBRACK:
    559                 this._consume(FormatterWorker.JavaScriptTokens.LBRACK);
    560                 this._parseExpression();
    561                 this._expect(FormatterWorker.JavaScriptTokens.RBRACK);
    562                 break;
    563 
    564             case FormatterWorker.JavaScriptTokens.PERIOD:
    565                 this._consume(FormatterWorker.JavaScriptTokens.PERIOD);
    566                 this._expect(FormatterWorker.JavaScriptTokens.IDENTIFIER);
    567                 break;
    568 
    569             case FormatterWorker.JavaScriptTokens.LPAREN:
    570                 this._parseArguments();
    571                 break;
    572 
    573             default:
    574                 return;
    575             }
    576         }
    577     },
    578 
    579     _parseDebuggerStatement: function()
    580     {
    581         this._expect(FormatterWorker.JavaScriptTokens.DEBUGGER);
    582         this._expectSemicolon();
    583     },
    584 
    585     _parsePrimaryExpression: function()
    586     {
    587         switch (this._peek()) {
    588         case FormatterWorker.JavaScriptTokens.THIS:
    589             return this._consume(FormatterWorker.JavaScriptTokens.THIS);
    590         case FormatterWorker.JavaScriptTokens.NULL_LITERAL:
    591             return this._consume(FormatterWorker.JavaScriptTokens.NULL_LITERAL);
    592         case FormatterWorker.JavaScriptTokens.TRUE_LITERAL:
    593             return this._consume(FormatterWorker.JavaScriptTokens.TRUE_LITERAL);
    594         case FormatterWorker.JavaScriptTokens.FALSE_LITERAL:
    595             return this._consume(FormatterWorker.JavaScriptTokens.FALSE_LITERAL);
    596         case FormatterWorker.JavaScriptTokens.IDENTIFIER:
    597             return this._consume(FormatterWorker.JavaScriptTokens.IDENTIFIER);
    598         case FormatterWorker.JavaScriptTokens.NUMBER:
    599             return this._consume(FormatterWorker.JavaScriptTokens.NUMBER);
    600         case FormatterWorker.JavaScriptTokens.STRING:
    601             return this._consume(FormatterWorker.JavaScriptTokens.STRING);
    602         case FormatterWorker.JavaScriptTokens.ASSIGN_DIV:
    603             return this._parseRegExpLiteral();
    604         case FormatterWorker.JavaScriptTokens.DIV:
    605             return this._parseRegExpLiteral();
    606         case FormatterWorker.JavaScriptTokens.LBRACK:
    607             return this._parseArrayLiteral();
    608         case FormatterWorker.JavaScriptTokens.LBRACE:
    609             return this._parseObjectLiteral();
    610         case FormatterWorker.JavaScriptTokens.LPAREN:
    611             this._consume(FormatterWorker.JavaScriptTokens.LPAREN);
    612             this._parseExpression();
    613             this._expect(FormatterWorker.JavaScriptTokens.RPAREN);
    614             return;
    615         default:
    616             return this._next();
    617         }
    618     },
    619 
    620     _parseArrayLiteral: function()
    621     {
    622         this._expect(FormatterWorker.JavaScriptTokens.LBRACK);
    623         this._builder.increaseNestingLevel();
    624         while (this._peek() !== FormatterWorker.JavaScriptTokens.RBRACK) {
    625             if (this._peek() !== FormatterWorker.JavaScriptTokens.COMMA)
    626                 this._parseAssignmentExpression();
    627             if (this._peek() !== FormatterWorker.JavaScriptTokens.RBRACK) {
    628                 this._expect(FormatterWorker.JavaScriptTokens.COMMA);
    629                 this._builder.addSpace();
    630             }
    631         }
    632         this._builder.decreaseNestingLevel();
    633         this._expect(FormatterWorker.JavaScriptTokens.RBRACK);
    634     },
    635 
    636     _parseObjectLiteralGetSet: function()
    637     {
    638         var token = this._peek();
    639         if (token === FormatterWorker.JavaScriptTokens.IDENTIFIER || token === FormatterWorker.JavaScriptTokens.NUMBER || token === FormatterWorker.JavaScriptTokens.STRING ||
    640             FormatterWorker.JavaScriptTokens.DELETE <= token && token <= FormatterWorker.JavaScriptTokens.FALSE_LITERAL ||
    641             token === FormatterWorker.JavaScriptTokens.INSTANCEOF || token === FormatterWorker.JavaScriptTokens.IN || token === FormatterWorker.JavaScriptTokens.CONST) {
    642             this._next();
    643             this._parseFunctionLiteral();
    644         }
    645     },
    646 
    647     _parseObjectLiteral: function()
    648     {
    649         this._expect(FormatterWorker.JavaScriptTokens.LBRACE);
    650         this._builder.increaseNestingLevel();
    651         while (this._peek() !== FormatterWorker.JavaScriptTokens.RBRACE) {
    652             var token = this._peek();
    653             switch (token) {
    654             case FormatterWorker.JavaScriptTokens.IDENTIFIER:
    655                 this._consume(FormatterWorker.JavaScriptTokens.IDENTIFIER);
    656                 var name = this._token.value;
    657                 if ((name === "get" || name === "set") && this._peek() !== FormatterWorker.JavaScriptTokens.COLON) {
    658                     this._builder.addSpace();
    659                     this._parseObjectLiteralGetSet();
    660                     if (this._peek() !== FormatterWorker.JavaScriptTokens.RBRACE) {
    661                         this._expect(FormatterWorker.JavaScriptTokens.COMMA);
    662                     }
    663                     continue;
    664                 }
    665                 break;
    666 
    667             case FormatterWorker.JavaScriptTokens.STRING:
    668                 this._consume(FormatterWorker.JavaScriptTokens.STRING);
    669                 break;
    670 
    671             case FormatterWorker.JavaScriptTokens.NUMBER:
    672                 this._consume(FormatterWorker.JavaScriptTokens.NUMBER);
    673                 break;
    674 
    675             default:
    676                 this._next();
    677             }
    678 
    679             this._expect(FormatterWorker.JavaScriptTokens.COLON);
    680             this._builder.addSpace();
    681             this._parseAssignmentExpression();
    682             if (this._peek() !== FormatterWorker.JavaScriptTokens.RBRACE) {
    683                 this._expect(FormatterWorker.JavaScriptTokens.COMMA);
    684             }
    685         }
    686         this._builder.decreaseNestingLevel();
    687 
    688         this._expect(FormatterWorker.JavaScriptTokens.RBRACE);
    689     },
    690 
    691     _parseRegExpLiteral: function()
    692     {
    693         if (this._nextToken.type === "regexp")
    694             this._next();
    695         else {
    696             this._forceRegexp = true;
    697             this._next();
    698         }
    699     },
    700 
    701     _parseArguments: function()
    702     {
    703         this._expect(FormatterWorker.JavaScriptTokens.LPAREN);
    704         var done = (this._peek() === FormatterWorker.JavaScriptTokens.RPAREN);
    705         while (!done) {
    706             this._parseAssignmentExpression();
    707             done = (this._peek() === FormatterWorker.JavaScriptTokens.RPAREN);
    708             if (!done) {
    709                 this._expect(FormatterWorker.JavaScriptTokens.COMMA);
    710                 this._builder.addSpace();
    711             }
    712         }
    713         this._expect(FormatterWorker.JavaScriptTokens.RPAREN);
    714     },
    715 
    716     _parseFunctionLiteral: function()
    717     {
    718         this._expect(FormatterWorker.JavaScriptTokens.LPAREN);
    719         var done = (this._peek() === FormatterWorker.JavaScriptTokens.RPAREN);
    720         while (!done) {
    721             this._expect(FormatterWorker.JavaScriptTokens.IDENTIFIER);
    722             done = (this._peek() === FormatterWorker.JavaScriptTokens.RPAREN);
    723             if (!done) {
    724                 this._expect(FormatterWorker.JavaScriptTokens.COMMA);
    725                 this._builder.addSpace();
    726             }
    727         }
    728         this._expect(FormatterWorker.JavaScriptTokens.RPAREN);
    729         this._builder.addSpace();
    730 
    731         this._expect(FormatterWorker.JavaScriptTokens.LBRACE);
    732         this._builder.addNewLine();
    733         this._builder.increaseNestingLevel();
    734         this._parseSourceElements(FormatterWorker.JavaScriptTokens.RBRACE);
    735         this._builder.decreaseNestingLevel();
    736         this._expect(FormatterWorker.JavaScriptTokens.RBRACE);
    737     }
    738 }
    739 
    740 /**
    741  * @constructor
    742  * @param {string} content
    743  * @param {!{original: !Array.<number>, formatted: !Array.<number>}} mapping
    744  * @param {number} originalOffset
    745  * @param {number} formattedOffset
    746  * @param {string} indentString
    747  */
    748 FormatterWorker.JavaScriptFormattedContentBuilder = function(content, mapping, originalOffset, formattedOffset, indentString)
    749 {
    750     this._originalContent = content;
    751     this._originalOffset = originalOffset;
    752     this._lastOriginalPosition = 0;
    753 
    754     this._formattedContent = [];
    755     this._formattedContentLength = 0;
    756     this._formattedOffset = formattedOffset;
    757     this._lastFormattedPosition = 0;
    758 
    759     this._mapping = mapping;
    760 
    761     this._lineNumber = 0;
    762     this._nestingLevel = 0;
    763     this._indentString = indentString;
    764     this._cachedIndents = {};
    765 }
    766 
    767 FormatterWorker.JavaScriptFormattedContentBuilder.prototype = {
    768     /**
    769      * @param {!{comments_before: !Array.<string>, line: number, pos: number, endLine: number, nlb: boolean}} token
    770      */
    771     addToken: function(token)
    772     {
    773         for (var i = 0; i < token.comments_before.length; ++i)
    774             this._addComment(token.comments_before[i]);
    775 
    776         while (this._lineNumber < token.line) {
    777             this._addText("\n");
    778             this._addIndent();
    779             this._needNewLine = false;
    780             this._lineNumber += 1;
    781         }
    782 
    783         if (this._needNewLine) {
    784             this._addText("\n");
    785             this._addIndent();
    786             this._needNewLine = false;
    787         }
    788 
    789         this._addMappingIfNeeded(token.pos);
    790         this._addText(this._originalContent.substring(token.pos, token.endPos));
    791         this._lineNumber = token.endLine;
    792     },
    793 
    794     addSpace: function()
    795     {
    796         this._addText(" ");
    797     },
    798 
    799     addNewLine: function()
    800     {
    801         this._needNewLine = true;
    802     },
    803 
    804     increaseNestingLevel: function()
    805     {
    806         this._nestingLevel += 1;
    807     },
    808 
    809     decreaseNestingLevel: function()
    810     {
    811         this._nestingLevel -= 1;
    812     },
    813 
    814     /**
    815      * @return {string}
    816      */
    817     content: function()
    818     {
    819         return this._formattedContent.join("");
    820     },
    821 
    822     _addIndent: function()
    823     {
    824         if (this._cachedIndents[this._nestingLevel]) {
    825             this._addText(this._cachedIndents[this._nestingLevel]);
    826             return;
    827         }
    828 
    829         var fullIndent = "";
    830         for (var i = 0; i < this._nestingLevel; ++i)
    831             fullIndent += this._indentString;
    832         this._addText(fullIndent);
    833 
    834         // Cache a maximum of 20 nesting level indents.
    835         if (this._nestingLevel <= 20)
    836             this._cachedIndents[this._nestingLevel] = fullIndent;
    837     },
    838 
    839     _addComment: function(comment)
    840     {
    841         if (this._lineNumber < comment.line) {
    842             for (var j = this._lineNumber; j < comment.line; ++j)
    843                 this._addText("\n");
    844             this._lineNumber = comment.line;
    845             this._needNewLine = false;
    846             this._addIndent();
    847         } else
    848             this.addSpace();
    849 
    850         this._addMappingIfNeeded(comment.pos);
    851         if (comment.type === "comment1")
    852             this._addText("//");
    853         else
    854             this._addText("/*");
    855 
    856         this._addText(comment.value);
    857 
    858         if (comment.type !== "comment1") {
    859             this._addText("*/");
    860             var position;
    861             while ((position = comment.value.indexOf("\n", position + 1)) !== -1)
    862                 this._lineNumber += 1;
    863         }
    864     },
    865 
    866     /**
    867      * @param {string} text
    868      */
    869     _addText: function(text)
    870     {
    871         this._formattedContent.push(text);
    872         this._formattedContentLength += text.length;
    873     },
    874 
    875     /**
    876      * @param {number} originalPosition
    877      */
    878     _addMappingIfNeeded: function(originalPosition)
    879     {
    880         if (originalPosition - this._lastOriginalPosition === this._formattedContentLength - this._lastFormattedPosition)
    881             return;
    882         this._mapping.original.push(this._originalOffset + originalPosition);
    883         this._lastOriginalPosition = originalPosition;
    884         this._mapping.formatted.push(this._formattedOffset + this._formattedContentLength);
    885         this._lastFormattedPosition = this._formattedContentLength;
    886     }
    887 }
    888 
    889 FormatterWorker.JavaScriptTokens = {};
    890 FormatterWorker.JavaScriptTokensByValue = {};
    891 
    892 FormatterWorker.JavaScriptTokens.EOS = 0;
    893 FormatterWorker.JavaScriptTokens.LPAREN = FormatterWorker.JavaScriptTokensByValue["("] = 1;
    894 FormatterWorker.JavaScriptTokens.RPAREN = FormatterWorker.JavaScriptTokensByValue[")"] = 2;
    895 FormatterWorker.JavaScriptTokens.LBRACK = FormatterWorker.JavaScriptTokensByValue["["] = 3;
    896 FormatterWorker.JavaScriptTokens.RBRACK = FormatterWorker.JavaScriptTokensByValue["]"] = 4;
    897 FormatterWorker.JavaScriptTokens.LBRACE = FormatterWorker.JavaScriptTokensByValue["{"] = 5;
    898 FormatterWorker.JavaScriptTokens.RBRACE = FormatterWorker.JavaScriptTokensByValue["}"] = 6;
    899 FormatterWorker.JavaScriptTokens.COLON = FormatterWorker.JavaScriptTokensByValue[":"] = 7;
    900 FormatterWorker.JavaScriptTokens.SEMICOLON = FormatterWorker.JavaScriptTokensByValue[";"] = 8;
    901 FormatterWorker.JavaScriptTokens.PERIOD = FormatterWorker.JavaScriptTokensByValue["."] = 9;
    902 FormatterWorker.JavaScriptTokens.CONDITIONAL = FormatterWorker.JavaScriptTokensByValue["?"] = 10;
    903 FormatterWorker.JavaScriptTokens.INC = FormatterWorker.JavaScriptTokensByValue["++"] = 11;
    904 FormatterWorker.JavaScriptTokens.DEC = FormatterWorker.JavaScriptTokensByValue["--"] = 12;
    905 FormatterWorker.JavaScriptTokens.ASSIGN = FormatterWorker.JavaScriptTokensByValue["="] = 13;
    906 FormatterWorker.JavaScriptTokens.ASSIGN_BIT_OR = FormatterWorker.JavaScriptTokensByValue["|="] = 14;
    907 FormatterWorker.JavaScriptTokens.ASSIGN_BIT_XOR = FormatterWorker.JavaScriptTokensByValue["^="] = 15;
    908 FormatterWorker.JavaScriptTokens.ASSIGN_BIT_AND = FormatterWorker.JavaScriptTokensByValue["&="] = 16;
    909 FormatterWorker.JavaScriptTokens.ASSIGN_SHL = FormatterWorker.JavaScriptTokensByValue["<<="] = 17;
    910 FormatterWorker.JavaScriptTokens.ASSIGN_SAR = FormatterWorker.JavaScriptTokensByValue[">>="] = 18;
    911 FormatterWorker.JavaScriptTokens.ASSIGN_SHR = FormatterWorker.JavaScriptTokensByValue[">>>="] = 19;
    912 FormatterWorker.JavaScriptTokens.ASSIGN_ADD = FormatterWorker.JavaScriptTokensByValue["+="] = 20;
    913 FormatterWorker.JavaScriptTokens.ASSIGN_SUB = FormatterWorker.JavaScriptTokensByValue["-="] = 21;
    914 FormatterWorker.JavaScriptTokens.ASSIGN_MUL = FormatterWorker.JavaScriptTokensByValue["*="] = 22;
    915 FormatterWorker.JavaScriptTokens.ASSIGN_DIV = FormatterWorker.JavaScriptTokensByValue["/="] = 23;
    916 FormatterWorker.JavaScriptTokens.ASSIGN_MOD = FormatterWorker.JavaScriptTokensByValue["%="] = 24;
    917 FormatterWorker.JavaScriptTokens.COMMA = FormatterWorker.JavaScriptTokensByValue[","] = 25;
    918 FormatterWorker.JavaScriptTokens.OR = FormatterWorker.JavaScriptTokensByValue["||"] = 26;
    919 FormatterWorker.JavaScriptTokens.AND = FormatterWorker.JavaScriptTokensByValue["&&"] = 27;
    920 FormatterWorker.JavaScriptTokens.BIT_OR = FormatterWorker.JavaScriptTokensByValue["|"] = 28;
    921 FormatterWorker.JavaScriptTokens.BIT_XOR = FormatterWorker.JavaScriptTokensByValue["^"] = 29;
    922 FormatterWorker.JavaScriptTokens.BIT_AND = FormatterWorker.JavaScriptTokensByValue["&"] = 30;
    923 FormatterWorker.JavaScriptTokens.SHL = FormatterWorker.JavaScriptTokensByValue["<<"] = 31;
    924 FormatterWorker.JavaScriptTokens.SAR = FormatterWorker.JavaScriptTokensByValue[">>"] = 32;
    925 FormatterWorker.JavaScriptTokens.SHR = FormatterWorker.JavaScriptTokensByValue[">>>"] = 33;
    926 FormatterWorker.JavaScriptTokens.ADD = FormatterWorker.JavaScriptTokensByValue["+"] = 34;
    927 FormatterWorker.JavaScriptTokens.SUB = FormatterWorker.JavaScriptTokensByValue["-"] = 35;
    928 FormatterWorker.JavaScriptTokens.MUL = FormatterWorker.JavaScriptTokensByValue["*"] = 36;
    929 FormatterWorker.JavaScriptTokens.DIV = FormatterWorker.JavaScriptTokensByValue["/"] = 37;
    930 FormatterWorker.JavaScriptTokens.MOD = FormatterWorker.JavaScriptTokensByValue["%"] = 38;
    931 FormatterWorker.JavaScriptTokens.EQ = FormatterWorker.JavaScriptTokensByValue["=="] = 39;
    932 FormatterWorker.JavaScriptTokens.NE = FormatterWorker.JavaScriptTokensByValue["!="] = 40;
    933 FormatterWorker.JavaScriptTokens.EQ_STRICT = FormatterWorker.JavaScriptTokensByValue["==="] = 41;
    934 FormatterWorker.JavaScriptTokens.NE_STRICT = FormatterWorker.JavaScriptTokensByValue["!=="] = 42;
    935 FormatterWorker.JavaScriptTokens.LT = FormatterWorker.JavaScriptTokensByValue["<"] = 43;
    936 FormatterWorker.JavaScriptTokens.GT = FormatterWorker.JavaScriptTokensByValue[">"] = 44;
    937 FormatterWorker.JavaScriptTokens.LTE = FormatterWorker.JavaScriptTokensByValue["<="] = 45;
    938 FormatterWorker.JavaScriptTokens.GTE = FormatterWorker.JavaScriptTokensByValue[">="] = 46;
    939 FormatterWorker.JavaScriptTokens.INSTANCEOF = FormatterWorker.JavaScriptTokensByValue["instanceof"] = 47;
    940 FormatterWorker.JavaScriptTokens.IN = FormatterWorker.JavaScriptTokensByValue["in"] = 48;
    941 FormatterWorker.JavaScriptTokens.NOT = FormatterWorker.JavaScriptTokensByValue["!"] = 49;
    942 FormatterWorker.JavaScriptTokens.BIT_NOT = FormatterWorker.JavaScriptTokensByValue["~"] = 50;
    943 FormatterWorker.JavaScriptTokens.DELETE = FormatterWorker.JavaScriptTokensByValue["delete"] = 51;
    944 FormatterWorker.JavaScriptTokens.TYPEOF = FormatterWorker.JavaScriptTokensByValue["typeof"] = 52;
    945 FormatterWorker.JavaScriptTokens.VOID = FormatterWorker.JavaScriptTokensByValue["void"] = 53;
    946 FormatterWorker.JavaScriptTokens.BREAK = FormatterWorker.JavaScriptTokensByValue["break"] = 54;
    947 FormatterWorker.JavaScriptTokens.CASE = FormatterWorker.JavaScriptTokensByValue["case"] = 55;
    948 FormatterWorker.JavaScriptTokens.CATCH = FormatterWorker.JavaScriptTokensByValue["catch"] = 56;
    949 FormatterWorker.JavaScriptTokens.CONTINUE = FormatterWorker.JavaScriptTokensByValue["continue"] = 57;
    950 FormatterWorker.JavaScriptTokens.DEBUGGER = FormatterWorker.JavaScriptTokensByValue["debugger"] = 58;
    951 FormatterWorker.JavaScriptTokens.DEFAULT = FormatterWorker.JavaScriptTokensByValue["default"] = 59;
    952 FormatterWorker.JavaScriptTokens.DO = FormatterWorker.JavaScriptTokensByValue["do"] = 60;
    953 FormatterWorker.JavaScriptTokens.ELSE = FormatterWorker.JavaScriptTokensByValue["else"] = 61;
    954 FormatterWorker.JavaScriptTokens.FINALLY = FormatterWorker.JavaScriptTokensByValue["finally"] = 62;
    955 FormatterWorker.JavaScriptTokens.FOR = FormatterWorker.JavaScriptTokensByValue["for"] = 63;
    956 FormatterWorker.JavaScriptTokens.FUNCTION = FormatterWorker.JavaScriptTokensByValue["function"] = 64;
    957 FormatterWorker.JavaScriptTokens.IF = FormatterWorker.JavaScriptTokensByValue["if"] = 65;
    958 FormatterWorker.JavaScriptTokens.NEW = FormatterWorker.JavaScriptTokensByValue["new"] = 66;
    959 FormatterWorker.JavaScriptTokens.RETURN = FormatterWorker.JavaScriptTokensByValue["return"] = 67;
    960 FormatterWorker.JavaScriptTokens.SWITCH = FormatterWorker.JavaScriptTokensByValue["switch"] = 68;
    961 FormatterWorker.JavaScriptTokens.THIS = FormatterWorker.JavaScriptTokensByValue["this"] = 69;
    962 FormatterWorker.JavaScriptTokens.THROW = FormatterWorker.JavaScriptTokensByValue["throw"] = 70;
    963 FormatterWorker.JavaScriptTokens.TRY = FormatterWorker.JavaScriptTokensByValue["try"] = 71;
    964 FormatterWorker.JavaScriptTokens.VAR = FormatterWorker.JavaScriptTokensByValue["var"] = 72;
    965 FormatterWorker.JavaScriptTokens.WHILE = FormatterWorker.JavaScriptTokensByValue["while"] = 73;
    966 FormatterWorker.JavaScriptTokens.WITH = FormatterWorker.JavaScriptTokensByValue["with"] = 74;
    967 FormatterWorker.JavaScriptTokens.NULL_LITERAL = FormatterWorker.JavaScriptTokensByValue["null"] = 75;
    968 FormatterWorker.JavaScriptTokens.TRUE_LITERAL = FormatterWorker.JavaScriptTokensByValue["true"] = 76;
    969 FormatterWorker.JavaScriptTokens.FALSE_LITERAL = FormatterWorker.JavaScriptTokensByValue["false"] = 77;
    970 FormatterWorker.JavaScriptTokens.NUMBER = 78;
    971 FormatterWorker.JavaScriptTokens.STRING = 79;
    972 FormatterWorker.JavaScriptTokens.IDENTIFIER = 80;
    973 FormatterWorker.JavaScriptTokens.CONST = FormatterWorker.JavaScriptTokensByValue["const"] = 81;
    974 
    975 FormatterWorker.JavaScriptTokensByType = {
    976     "eof": FormatterWorker.JavaScriptTokens.EOS,
    977     "name": FormatterWorker.JavaScriptTokens.IDENTIFIER,
    978     "num": FormatterWorker.JavaScriptTokens.NUMBER,
    979     "regexp": FormatterWorker.JavaScriptTokens.DIV,
    980     "string": FormatterWorker.JavaScriptTokens.STRING
    981 };
    982 
    983 /**
    984  * @constructor
    985  * @param {string} content
    986  */
    987 FormatterWorker.JavaScriptTokenizer = function(content)
    988 {
    989     this._readNextToken = parse.tokenizer(content);
    990     this._state = this._readNextToken.context();
    991 }
    992 
    993 FormatterWorker.JavaScriptTokenizer.prototype = {
    994     /**
    995      * @return {string}
    996      */
    997     content: function()
    998     {
    999         return this._state.text;
   1000     },
   1001 
   1002     /**
   1003      * @param {boolean=} forceRegexp
   1004      * @return {!{comments_before: !Array.<string>, line: number, pos: number, endLine: number, nlb: boolean, token: number, type: string, value: *}}
   1005      */
   1006     next: function(forceRegexp)
   1007     {
   1008         var uglifyToken = this._readNextToken(forceRegexp);
   1009         uglifyToken.endPos = this._state.pos;
   1010         uglifyToken.endLine = this._state.line;
   1011         uglifyToken.token = this._convertUglifyToken(uglifyToken);
   1012         return uglifyToken;
   1013     },
   1014 
   1015     /**
   1016      * @return {number}
   1017      */
   1018     _convertUglifyToken: function(uglifyToken)
   1019     {
   1020         var token = FormatterWorker.JavaScriptTokensByType[uglifyToken.type];
   1021         if (typeof token === "number")
   1022             return token;
   1023         token = FormatterWorker.JavaScriptTokensByValue[uglifyToken.value];
   1024         if (typeof token === "number")
   1025             return token;
   1026         throw "Unknown token type " + uglifyToken.type;
   1027     }
   1028 }
   1029