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 */ 36FormatterWorker.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 44FormatterWorker.JavaScriptFormatter.prototype = { 45 format: function() 46 { 47 this._parseSourceElements(FormatterWorker.JavaScriptTokens.EOS); 48 this._consume(FormatterWorker.JavaScriptTokens.EOS); 49 }, 50 51 /** 52 * @return {string} 53 */ 54 _peek: function() 55 { 56 return this._nextToken.token; 57 }, 58 59 /** 60 * @return {string} 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 {string} 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 {string} 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 {string} 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 */ 748FormatterWorker.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 767FormatterWorker.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 889FormatterWorker.JavaScriptTokens = {}; 890FormatterWorker.JavaScriptTokensByValue = {}; 891 892FormatterWorker.JavaScriptTokens.EOS = 0; 893FormatterWorker.JavaScriptTokens.LPAREN = FormatterWorker.JavaScriptTokensByValue["("] = 1; 894FormatterWorker.JavaScriptTokens.RPAREN = FormatterWorker.JavaScriptTokensByValue[")"] = 2; 895FormatterWorker.JavaScriptTokens.LBRACK = FormatterWorker.JavaScriptTokensByValue["["] = 3; 896FormatterWorker.JavaScriptTokens.RBRACK = FormatterWorker.JavaScriptTokensByValue["]"] = 4; 897FormatterWorker.JavaScriptTokens.LBRACE = FormatterWorker.JavaScriptTokensByValue["{"] = 5; 898FormatterWorker.JavaScriptTokens.RBRACE = FormatterWorker.JavaScriptTokensByValue["}"] = 6; 899FormatterWorker.JavaScriptTokens.COLON = FormatterWorker.JavaScriptTokensByValue[":"] = 7; 900FormatterWorker.JavaScriptTokens.SEMICOLON = FormatterWorker.JavaScriptTokensByValue[";"] = 8; 901FormatterWorker.JavaScriptTokens.PERIOD = FormatterWorker.JavaScriptTokensByValue["."] = 9; 902FormatterWorker.JavaScriptTokens.CONDITIONAL = FormatterWorker.JavaScriptTokensByValue["?"] = 10; 903FormatterWorker.JavaScriptTokens.INC = FormatterWorker.JavaScriptTokensByValue["++"] = 11; 904FormatterWorker.JavaScriptTokens.DEC = FormatterWorker.JavaScriptTokensByValue["--"] = 12; 905FormatterWorker.JavaScriptTokens.ASSIGN = FormatterWorker.JavaScriptTokensByValue["="] = 13; 906FormatterWorker.JavaScriptTokens.ASSIGN_BIT_OR = FormatterWorker.JavaScriptTokensByValue["|="] = 14; 907FormatterWorker.JavaScriptTokens.ASSIGN_BIT_XOR = FormatterWorker.JavaScriptTokensByValue["^="] = 15; 908FormatterWorker.JavaScriptTokens.ASSIGN_BIT_AND = FormatterWorker.JavaScriptTokensByValue["&="] = 16; 909FormatterWorker.JavaScriptTokens.ASSIGN_SHL = FormatterWorker.JavaScriptTokensByValue["<<="] = 17; 910FormatterWorker.JavaScriptTokens.ASSIGN_SAR = FormatterWorker.JavaScriptTokensByValue[">>="] = 18; 911FormatterWorker.JavaScriptTokens.ASSIGN_SHR = FormatterWorker.JavaScriptTokensByValue[">>>="] = 19; 912FormatterWorker.JavaScriptTokens.ASSIGN_ADD = FormatterWorker.JavaScriptTokensByValue["+="] = 20; 913FormatterWorker.JavaScriptTokens.ASSIGN_SUB = FormatterWorker.JavaScriptTokensByValue["-="] = 21; 914FormatterWorker.JavaScriptTokens.ASSIGN_MUL = FormatterWorker.JavaScriptTokensByValue["*="] = 22; 915FormatterWorker.JavaScriptTokens.ASSIGN_DIV = FormatterWorker.JavaScriptTokensByValue["/="] = 23; 916FormatterWorker.JavaScriptTokens.ASSIGN_MOD = FormatterWorker.JavaScriptTokensByValue["%="] = 24; 917FormatterWorker.JavaScriptTokens.COMMA = FormatterWorker.JavaScriptTokensByValue[","] = 25; 918FormatterWorker.JavaScriptTokens.OR = FormatterWorker.JavaScriptTokensByValue["||"] = 26; 919FormatterWorker.JavaScriptTokens.AND = FormatterWorker.JavaScriptTokensByValue["&&"] = 27; 920FormatterWorker.JavaScriptTokens.BIT_OR = FormatterWorker.JavaScriptTokensByValue["|"] = 28; 921FormatterWorker.JavaScriptTokens.BIT_XOR = FormatterWorker.JavaScriptTokensByValue["^"] = 29; 922FormatterWorker.JavaScriptTokens.BIT_AND = FormatterWorker.JavaScriptTokensByValue["&"] = 30; 923FormatterWorker.JavaScriptTokens.SHL = FormatterWorker.JavaScriptTokensByValue["<<"] = 31; 924FormatterWorker.JavaScriptTokens.SAR = FormatterWorker.JavaScriptTokensByValue[">>"] = 32; 925FormatterWorker.JavaScriptTokens.SHR = FormatterWorker.JavaScriptTokensByValue[">>>"] = 33; 926FormatterWorker.JavaScriptTokens.ADD = FormatterWorker.JavaScriptTokensByValue["+"] = 34; 927FormatterWorker.JavaScriptTokens.SUB = FormatterWorker.JavaScriptTokensByValue["-"] = 35; 928FormatterWorker.JavaScriptTokens.MUL = FormatterWorker.JavaScriptTokensByValue["*"] = 36; 929FormatterWorker.JavaScriptTokens.DIV = FormatterWorker.JavaScriptTokensByValue["/"] = 37; 930FormatterWorker.JavaScriptTokens.MOD = FormatterWorker.JavaScriptTokensByValue["%"] = 38; 931FormatterWorker.JavaScriptTokens.EQ = FormatterWorker.JavaScriptTokensByValue["=="] = 39; 932FormatterWorker.JavaScriptTokens.NE = FormatterWorker.JavaScriptTokensByValue["!="] = 40; 933FormatterWorker.JavaScriptTokens.EQ_STRICT = FormatterWorker.JavaScriptTokensByValue["==="] = 41; 934FormatterWorker.JavaScriptTokens.NE_STRICT = FormatterWorker.JavaScriptTokensByValue["!=="] = 42; 935FormatterWorker.JavaScriptTokens.LT = FormatterWorker.JavaScriptTokensByValue["<"] = 43; 936FormatterWorker.JavaScriptTokens.GT = FormatterWorker.JavaScriptTokensByValue[">"] = 44; 937FormatterWorker.JavaScriptTokens.LTE = FormatterWorker.JavaScriptTokensByValue["<="] = 45; 938FormatterWorker.JavaScriptTokens.GTE = FormatterWorker.JavaScriptTokensByValue[">="] = 46; 939FormatterWorker.JavaScriptTokens.INSTANCEOF = FormatterWorker.JavaScriptTokensByValue["instanceof"] = 47; 940FormatterWorker.JavaScriptTokens.IN = FormatterWorker.JavaScriptTokensByValue["in"] = 48; 941FormatterWorker.JavaScriptTokens.NOT = FormatterWorker.JavaScriptTokensByValue["!"] = 49; 942FormatterWorker.JavaScriptTokens.BIT_NOT = FormatterWorker.JavaScriptTokensByValue["~"] = 50; 943FormatterWorker.JavaScriptTokens.DELETE = FormatterWorker.JavaScriptTokensByValue["delete"] = 51; 944FormatterWorker.JavaScriptTokens.TYPEOF = FormatterWorker.JavaScriptTokensByValue["typeof"] = 52; 945FormatterWorker.JavaScriptTokens.VOID = FormatterWorker.JavaScriptTokensByValue["void"] = 53; 946FormatterWorker.JavaScriptTokens.BREAK = FormatterWorker.JavaScriptTokensByValue["break"] = 54; 947FormatterWorker.JavaScriptTokens.CASE = FormatterWorker.JavaScriptTokensByValue["case"] = 55; 948FormatterWorker.JavaScriptTokens.CATCH = FormatterWorker.JavaScriptTokensByValue["catch"] = 56; 949FormatterWorker.JavaScriptTokens.CONTINUE = FormatterWorker.JavaScriptTokensByValue["continue"] = 57; 950FormatterWorker.JavaScriptTokens.DEBUGGER = FormatterWorker.JavaScriptTokensByValue["debugger"] = 58; 951FormatterWorker.JavaScriptTokens.DEFAULT = FormatterWorker.JavaScriptTokensByValue["default"] = 59; 952FormatterWorker.JavaScriptTokens.DO = FormatterWorker.JavaScriptTokensByValue["do"] = 60; 953FormatterWorker.JavaScriptTokens.ELSE = FormatterWorker.JavaScriptTokensByValue["else"] = 61; 954FormatterWorker.JavaScriptTokens.FINALLY = FormatterWorker.JavaScriptTokensByValue["finally"] = 62; 955FormatterWorker.JavaScriptTokens.FOR = FormatterWorker.JavaScriptTokensByValue["for"] = 63; 956FormatterWorker.JavaScriptTokens.FUNCTION = FormatterWorker.JavaScriptTokensByValue["function"] = 64; 957FormatterWorker.JavaScriptTokens.IF = FormatterWorker.JavaScriptTokensByValue["if"] = 65; 958FormatterWorker.JavaScriptTokens.NEW = FormatterWorker.JavaScriptTokensByValue["new"] = 66; 959FormatterWorker.JavaScriptTokens.RETURN = FormatterWorker.JavaScriptTokensByValue["return"] = 67; 960FormatterWorker.JavaScriptTokens.SWITCH = FormatterWorker.JavaScriptTokensByValue["switch"] = 68; 961FormatterWorker.JavaScriptTokens.THIS = FormatterWorker.JavaScriptTokensByValue["this"] = 69; 962FormatterWorker.JavaScriptTokens.THROW = FormatterWorker.JavaScriptTokensByValue["throw"] = 70; 963FormatterWorker.JavaScriptTokens.TRY = FormatterWorker.JavaScriptTokensByValue["try"] = 71; 964FormatterWorker.JavaScriptTokens.VAR = FormatterWorker.JavaScriptTokensByValue["var"] = 72; 965FormatterWorker.JavaScriptTokens.WHILE = FormatterWorker.JavaScriptTokensByValue["while"] = 73; 966FormatterWorker.JavaScriptTokens.WITH = FormatterWorker.JavaScriptTokensByValue["with"] = 74; 967FormatterWorker.JavaScriptTokens.NULL_LITERAL = FormatterWorker.JavaScriptTokensByValue["null"] = 75; 968FormatterWorker.JavaScriptTokens.TRUE_LITERAL = FormatterWorker.JavaScriptTokensByValue["true"] = 76; 969FormatterWorker.JavaScriptTokens.FALSE_LITERAL = FormatterWorker.JavaScriptTokensByValue["false"] = 77; 970FormatterWorker.JavaScriptTokens.NUMBER = 78; 971FormatterWorker.JavaScriptTokens.STRING = 79; 972FormatterWorker.JavaScriptTokens.IDENTIFIER = 80; 973FormatterWorker.JavaScriptTokens.CONST = FormatterWorker.JavaScriptTokensByValue["const"] = 81; 974 975FormatterWorker.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 */ 987FormatterWorker.JavaScriptTokenizer = function(content) 988{ 989 this._readNextToken = parse.tokenizer(content); 990 this._state = this._readNextToken.context(); 991} 992 993FormatterWorker.JavaScriptTokenizer.prototype = { 994 /** 995 * @return {string} 996 */ 997 content: function() 998 { 999 return this._state.text; 1000 }, 1001 1002 /** 1003 * @param {boolean=} forceRegexp 1004 */ 1005 next: function(forceRegexp) 1006 { 1007 var uglifyToken = this._readNextToken(forceRegexp); 1008 uglifyToken.endPos = this._state.pos; 1009 uglifyToken.endLine = this._state.line; 1010 uglifyToken.token = this._convertUglifyToken(uglifyToken); 1011 return uglifyToken; 1012 }, 1013 1014 _convertUglifyToken: function(uglifyToken) 1015 { 1016 var token = FormatterWorker.JavaScriptTokensByType[uglifyToken.type]; 1017 if (typeof token === "number") 1018 return token; 1019 token = FormatterWorker.JavaScriptTokensByValue[uglifyToken.value]; 1020 if (typeof token === "number") 1021 return token; 1022 throw "Unknown token type " + uglifyToken.type; 1023 } 1024} 1025