initial import
[vuplus_webkit] / Source / WebCore / inspector / front-end / JavaScriptFormatter.js
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 function FormattedContentBuilder(content, mapping, originalOffset, formattedOffset)
32 {
33     this._originalContent = content;
34     this._originalOffset = originalOffset;
35     this._lastOriginalPosition = 0;
36
37     this._formattedContent = [];
38     this._formattedContentLength = 0;
39     this._formattedOffset = formattedOffset;
40     this._lastFormattedPosition = 0;
41
42     this._mapping = mapping;
43
44     this._lineNumber = 0;
45     this._nestingLevelLevel = 0;
46 }
47
48 FormattedContentBuilder.prototype = {
49     addToken: function(token)
50     {
51         for (var i = 0; i < token.comments_before.length; ++i)
52             this._addComment(token.comments_before[i]);
53
54         while (this._lineNumber < token.line) {
55             this._addText("\n");
56             this._addIndent();
57             this._needNewLine = false;
58             this._lineNumber += 1;
59         }
60
61         if (this._needNewLine) {
62             this._addText("\n");
63             this._addIndent();
64             this._needNewLine = false;
65         }
66
67         this._addMappingIfNeeded(token.pos);
68         this._addText(this._originalContent.substring(token.pos, token.endPos));
69         this._lineNumber = token.endLine;
70     },
71
72     addSpace: function()
73     {
74         this._addText(" ");
75     },
76
77     addNewLine: function()
78     {
79         this._needNewLine = true;
80     },
81
82     increaseNestingLevel: function()
83     {
84         this._nestingLevelLevel += 1;
85     },
86
87     decreaseNestingLevel: function()
88     {
89         this._nestingLevelLevel -= 1;
90     },
91
92     content: function()
93     {
94         return this._formattedContent.join("");
95     },
96
97     mapping: function()
98     {
99         return { original: this._originalPositions, formatted: this._formattedPositions };
100     },
101
102     _addIndent: function()
103     {
104         for (var i = 0; i < this._nestingLevelLevel * 4; ++i)
105             this.addSpace();
106     },
107
108     _addComment: function(comment)
109     {
110         if (this._lineNumber < comment.line) {
111             for (var j = this._lineNumber; j < comment.line; ++j)
112                 this._addText("\n");
113             this._lineNumber = comment.line;
114             this._needNewLine = false;
115             this._addIndent();
116         } else
117             this.addSpace();
118
119         this._addMappingIfNeeded(comment.pos);
120         if (comment.type === "comment1")
121             this._addText("//");
122         else
123             this._addText("/*");
124
125         this._addText(comment.value);
126
127         if (comment.type !== "comment1") {
128             this._addText("*/");
129             var position;
130             while ((position = comment.value.indexOf("\n", position + 1)) !== -1)
131                 this._lineNumber += 1;
132         }
133     },
134
135     _addText: function(text)
136     {
137         this._formattedContent.push(text);
138         this._formattedContentLength += text.length;
139     },
140
141     _addMappingIfNeeded: function(originalPosition)
142     {
143         if (originalPosition - this._lastOriginalPosition === this._formattedContentLength - this._lastFormattedPosition)
144             return;
145         this._mapping.original.push(this._originalOffset + originalPosition);
146         this._lastOriginalPosition = originalPosition;
147         this._mapping.formatted.push(this._formattedOffset + this._formattedContentLength);
148         this._lastFormattedPosition = this._formattedContentLength;
149     }
150 }
151
152 var tokens = [
153     ["EOS"],
154     ["LPAREN", "("], ["RPAREN", ")"], ["LBRACK", "["], ["RBRACK", "]"], ["LBRACE", "{"], ["RBRACE", "}"], ["COLON", ":"], ["SEMICOLON", ";"], ["PERIOD", "."], ["CONDITIONAL", "?"],
155     ["INC", "++"], ["DEC", "--"],
156     ["ASSIGN", "="], ["ASSIGN_BIT_OR", "|="], ["ASSIGN_BIT_XOR", "^="], ["ASSIGN_BIT_AND", "&="], ["ASSIGN_SHL", "<<="], ["ASSIGN_SAR", ">>="], ["ASSIGN_SHR", ">>>="],
157     ["ASSIGN_ADD", "+="], ["ASSIGN_SUB", "-="], ["ASSIGN_MUL", "*="], ["ASSIGN_DIV", "/="], ["ASSIGN_MOD", "%="],
158     ["COMMA", ","], ["OR", "||"], ["AND", "&&"], ["BIT_OR", "|"], ["BIT_XOR", "^"], ["BIT_AND", "&"], ["SHL", "<<"], ["SAR", ">>"], ["SHR", ">>>"],
159     ["ADD", "+"], ["SUB", "-"], ["MUL", "*"], ["DIV", "/"], ["MOD", "%"],
160     ["EQ", "=="], ["NE", "!="], ["EQ_STRICT", "==="], ["NE_STRICT", "!=="], ["LT", "<"], ["GT", ">"], ["LTE", "<="], ["GTE", ">="],
161     ["INSTANCEOF", "instanceof"], ["IN", "in"], ["NOT", "!"], ["BIT_NOT", "~"], ["DELETE", "delete"], ["TYPEOF", "typeof"], ["VOID", "void"],
162     ["BREAK", "break"], ["CASE", "case"], ["CATCH", "catch"], ["CONTINUE", "continue"], ["DEBUGGER", "debugger"], ["DEFAULT", "default"], ["DO", "do"], ["ELSE", "else"], ["FINALLY", "finally"],
163     ["FOR", "for"], ["FUNCTION", "function"], ["IF", "if"], ["NEW", "new"], ["RETURN", "return"], ["SWITCH", "switch"], ["THIS", "this"], ["THROW", "throw"], ["TRY", "try"], ["VAR", "var"],
164     ["WHILE", "while"], ["WITH", "with"], ["NULL_LITERAL", "null"], ["TRUE_LITERAL", "true"], ["FALSE_LITERAL", "false"], ["NUMBER"], ["STRING"], ["IDENTIFIER"], ["CONST", "const"]
165 ];
166
167 var Tokens = {};
168 for (var i = 0; i < tokens.length; ++i)
169     Tokens[tokens[i][0]] = i;
170
171 var TokensByValue = {};
172 for (var i = 0; i < tokens.length; ++i) {
173     if (tokens[i][1])
174         TokensByValue[tokens[i][1]] = i;
175 }
176
177 var TokensByType = {
178     "eof": Tokens.EOS,
179     "name": Tokens.IDENTIFIER,
180     "num": Tokens.NUMBER,
181     "regexp": Tokens.DIV,
182     "string": Tokens.STRING
183 };
184
185 function Tokenizer(content)
186 {
187     this._readNextToken = parse.tokenizer(content);
188     this._state = this._readNextToken.context();
189 }
190
191 Tokenizer.prototype = {
192     content: function()
193     {
194         return this._state.text;
195     },
196
197     next: function(forceRegexp)
198     {
199         var uglifyToken = this._readNextToken(forceRegexp);
200         uglifyToken.endPos = this._state.pos;
201         uglifyToken.endLine = this._state.line;
202         uglifyToken.token = this._convertUglifyToken(uglifyToken);
203         return uglifyToken;
204     },
205
206     _convertUglifyToken: function(uglifyToken)
207     {
208         var token = TokensByType[uglifyToken.type];
209         if (typeof token === "number")
210             return token;
211         token = TokensByValue[uglifyToken.value];
212         if (typeof token === "number")
213             return token;
214         throw "Unknown token type " + uglifyToken.type;
215     }
216 }
217
218 function JavaScriptFormatter(tokenizer, builder)
219 {
220     this._tokenizer = tokenizer;
221     this._builder = builder;
222     this._token = null;
223     this._nextToken = this._tokenizer.next();
224 }
225
226 JavaScriptFormatter.prototype = {
227     format: function()
228     {
229         this._parseSourceElements(Tokens.EOS);
230         this._consume(Tokens.EOS);
231     },
232
233     _peek: function()
234     {
235         return this._nextToken.token;
236     },
237
238     _next: function()
239     {
240         if (this._token && this._token.token === Tokens.EOS)
241             throw "Unexpected EOS token";
242
243         this._builder.addToken(this._nextToken);
244         this._token = this._nextToken;
245         this._nextToken = this._tokenizer.next(this._forceRegexp);
246         this._forceRegexp = false;
247         return this._token.token;
248     },
249
250     _consume: function(token)
251     {
252         var next = this._next();
253         if (next !== token)
254             throw "Unexpected token in consume: expected " + token + ", actual " + next;
255     },
256
257     _expect: function(token)
258     {
259         var next = this._next();
260         if (next !== token)
261             throw "Unexpected token: expected " + token + ", actual " + next;
262     },
263
264     _expectSemicolon: function()
265     {
266         if (this._peek() === Tokens.SEMICOLON)
267             this._consume(Tokens.SEMICOLON);
268     },
269
270     _hasLineTerminatorBeforeNext: function()
271     {
272         return this._nextToken.nlb;
273     },
274
275     _parseSourceElements: function(endToken)
276     {
277         while (this._peek() !== endToken) {
278             this._parseStatement();
279             this._builder.addNewLine();
280         }
281     },
282
283     _parseStatementOrBlock: function()
284     {
285         if (this._peek() === Tokens.LBRACE) {
286             this._builder.addSpace();
287             this._parseBlock();
288             return true;
289         }
290
291         this._builder.addNewLine();
292         this._builder.increaseNestingLevel();
293         this._parseStatement();
294         this._builder.decreaseNestingLevel();
295     },
296
297     _parseStatement: function()
298     {
299         switch (this._peek()) {
300         case Tokens.LBRACE:
301             return this._parseBlock();
302         case Tokens.CONST:
303         case Tokens.VAR:
304             return this._parseVariableStatement();
305         case Tokens.SEMICOLON:
306             return this._next();
307         case Tokens.IF:
308             return this._parseIfStatement();
309         case Tokens.DO:
310             return this._parseDoWhileStatement();
311         case Tokens.WHILE:
312             return this._parseWhileStatement();
313         case Tokens.FOR:
314             return this._parseForStatement();
315         case Tokens.CONTINUE:
316             return this._parseContinueStatement();
317         case Tokens.BREAK:
318             return this._parseBreakStatement();
319         case Tokens.RETURN:
320             return this._parseReturnStatement();
321         case Tokens.WITH:
322             return this._parseWithStatement();
323         case Tokens.SWITCH:
324             return this._parseSwitchStatement();
325         case Tokens.THROW:
326             return this._parseThrowStatement();
327         case Tokens.TRY:
328             return this._parseTryStatement();
329         case Tokens.FUNCTION:
330             return this._parseFunctionDeclaration();
331         case Tokens.DEBUGGER:
332             return this._parseDebuggerStatement();
333         default:
334             return this._parseExpressionOrLabelledStatement();
335         }
336     },
337
338     _parseFunctionDeclaration: function()
339     {
340         this._expect(Tokens.FUNCTION);
341         this._builder.addSpace();
342         this._expect(Tokens.IDENTIFIER);
343         this._parseFunctionLiteral()
344     },
345
346     _parseBlock: function()
347     {
348         this._expect(Tokens.LBRACE);
349         this._builder.addNewLine();
350         this._builder.increaseNestingLevel();
351         while (this._peek() !== Tokens.RBRACE) {
352             this._parseStatement();
353             this._builder.addNewLine();
354         }
355         this._builder.decreaseNestingLevel();
356         this._expect(Tokens.RBRACE);
357     },
358
359     _parseVariableStatement: function()
360     {
361         this._parseVariableDeclarations();
362         this._expectSemicolon();
363     },
364
365     _parseVariableDeclarations: function()
366     {
367         if (this._peek() === Tokens.VAR)
368             this._consume(Tokens.VAR);
369         else
370             this._consume(Tokens.CONST)
371         this._builder.addSpace();
372
373         var isFirstVariable = true;
374         do {
375             if (!isFirstVariable) {
376                 this._consume(Tokens.COMMA);
377                 this._builder.addSpace();
378             }
379             isFirstVariable = false;
380             this._expect(Tokens.IDENTIFIER);
381             if (this._peek() === Tokens.ASSIGN) {
382                 this._builder.addSpace();
383                 this._consume(Tokens.ASSIGN);
384                 this._builder.addSpace();
385                 this._parseAssignmentExpression();
386             }
387         } while (this._peek() === Tokens.COMMA);
388     },
389
390     _parseExpressionOrLabelledStatement: function()
391     {
392         this._parseExpression();
393         if (this._peek() === Tokens.COLON) {
394             this._expect(Tokens.COLON);
395             this._builder.addSpace();
396             this._parseStatement();
397         }
398         this._expectSemicolon();
399     },
400
401     _parseIfStatement: function()
402     {
403         this._expect(Tokens.IF);
404         this._builder.addSpace();
405         this._expect(Tokens.LPAREN);
406         this._parseExpression();
407         this._expect(Tokens.RPAREN);
408
409         var isBlock = this._parseStatementOrBlock();
410         if (this._peek() === Tokens.ELSE) {
411             if (isBlock)
412                 this._builder.addSpace();
413             else
414                 this._builder.addNewLine();
415             this._next();
416
417             if (this._peek() === Tokens.IF) {
418                 this._builder.addSpace();
419                 this._parseStatement();
420             } else
421                 this._parseStatementOrBlock();
422         }
423     },
424
425     _parseContinueStatement: function()
426     {
427         this._expect(Tokens.CONTINUE);
428         var token = this._peek();
429         if (!this._hasLineTerminatorBeforeNext() && token !== Tokens.SEMICOLON && token !== Tokens.RBRACE && token !== Tokens.EOS) {
430             this._builder.addSpace();
431             this._expect(Tokens.IDENTIFIER);
432         }
433         this._expectSemicolon();
434     },
435
436     _parseBreakStatement: function()
437     {
438         this._expect(Tokens.BREAK);
439         var token = this._peek();
440         if (!this._hasLineTerminatorBeforeNext() && token !== Tokens.SEMICOLON && token !== Tokens.RBRACE && token !== Tokens.EOS) {
441             this._builder.addSpace();
442             this._expect(Tokens.IDENTIFIER);
443         }
444         this._expectSemicolon();
445     },
446
447     _parseReturnStatement: function()
448     {
449         this._expect(Tokens.RETURN);
450         var token = this._peek();
451         if (!this._hasLineTerminatorBeforeNext() && token !== Tokens.SEMICOLON && token !== Tokens.RBRACE && token !== Tokens.EOS) {
452             this._builder.addSpace();
453             this._parseExpression();
454         }
455         this._expectSemicolon();
456     },
457
458     _parseWithStatement: function()
459     {
460         this._expect(Tokens.WITH);
461         this._builder.addSpace();
462         this._expect(Tokens.LPAREN);
463         this._parseExpression();
464         this._expect(Tokens.RPAREN);
465         this._parseStatementOrBlock();
466     },
467
468     _parseCaseClause: function()
469     {
470         if (this._peek() === Tokens.CASE) {
471             this._expect(Tokens.CASE);
472             this._builder.addSpace();
473             this._parseExpression();
474         } else
475             this._expect(Tokens.DEFAULT);
476         this._expect(Tokens.COLON);
477         this._builder.addNewLine();
478
479         this._builder.increaseNestingLevel();
480         while (this._peek() !== Tokens.CASE && this._peek() !== Tokens.DEFAULT && this._peek() !== Tokens.RBRACE) {
481             this._parseStatement();
482             this._builder.addNewLine();
483         }
484         this._builder.decreaseNestingLevel();
485     },
486
487     _parseSwitchStatement: function()
488     {
489         this._expect(Tokens.SWITCH);
490         this._builder.addSpace();
491         this._expect(Tokens.LPAREN);
492         this._parseExpression();
493         this._expect(Tokens.RPAREN);
494         this._builder.addSpace();
495
496         this._expect(Tokens.LBRACE);
497         this._builder.addNewLine();
498         this._builder.increaseNestingLevel();
499         while (this._peek() !== Tokens.RBRACE)
500             this._parseCaseClause();
501         this._builder.decreaseNestingLevel();
502         this._expect(Tokens.RBRACE);
503     },
504
505     _parseThrowStatement: function()
506     {
507         this._expect(Tokens.THROW);
508         this._builder.addSpace();
509         this._parseExpression();
510         this._expectSemicolon();
511     },
512
513     _parseTryStatement: function()
514     {
515         this._expect(Tokens.TRY);
516         this._builder.addSpace();
517         this._parseBlock();
518
519         var token = this._peek();
520         if (token === Tokens.CATCH) {
521             this._builder.addSpace();
522             this._consume(Tokens.CATCH);
523             this._builder.addSpace();
524             this._expect(Tokens.LPAREN);
525             this._expect(Tokens.IDENTIFIER);
526             this._expect(Tokens.RPAREN);
527             this._builder.addSpace();
528             this._parseBlock();
529             token = this._peek();
530         }
531
532         if (token === Tokens.FINALLY) {
533             this._consume(Tokens.FINALLY);
534             this._builder.addSpace();
535             this._parseBlock();
536         }
537     },
538
539     _parseDoWhileStatement: function()
540     {
541         this._expect(Tokens.DO);
542         var isBlock = this._parseStatementOrBlock();
543         if (isBlock)
544             this._builder.addSpace();
545         else
546             this._builder.addNewLine();
547         this._expect(Tokens.WHILE);
548         this._builder.addSpace();
549         this._expect(Tokens.LPAREN);
550         this._parseExpression();
551         this._expect(Tokens.RPAREN);
552         this._expectSemicolon();
553     },
554
555     _parseWhileStatement: function()
556     {
557         this._expect(Tokens.WHILE);
558         this._builder.addSpace();
559         this._expect(Tokens.LPAREN);
560         this._parseExpression();
561         this._expect(Tokens.RPAREN);
562         this._parseStatementOrBlock();
563     },
564
565     _parseForStatement: function()
566     {
567         this._expect(Tokens.FOR);
568         this._builder.addSpace();
569         this._expect(Tokens.LPAREN);
570         if (this._peek() !== Tokens.SEMICOLON) {
571             if (this._peek() === Tokens.VAR || this._peek() === Tokens.CONST) {
572                 this._parseVariableDeclarations();
573                 if (this._peek() === Tokens.IN) {
574                     this._builder.addSpace();
575                     this._consume(Tokens.IN);
576                     this._builder.addSpace();
577                     this._parseExpression();
578                 }
579             } else
580                 this._parseExpression();
581         }
582
583         if (this._peek() !== Tokens.RPAREN) {
584             this._expect(Tokens.SEMICOLON);
585             this._builder.addSpace();
586             if (this._peek() !== Tokens.SEMICOLON)
587                 this._parseExpression();
588             this._expect(Tokens.SEMICOLON);
589             this._builder.addSpace();
590             if (this._peek() !== Tokens.RPAREN)
591                 this._parseExpression();
592         }
593         this._expect(Tokens.RPAREN);
594
595         this._parseStatementOrBlock();
596     },
597
598     _parseExpression: function()
599     {
600         this._parseAssignmentExpression();
601         while (this._peek() === Tokens.COMMA) {
602             this._expect(Tokens.COMMA);
603             this._builder.addSpace();
604             this._parseAssignmentExpression();
605         }
606     },
607
608     _parseAssignmentExpression: function()
609     {
610         this._parseConditionalExpression();
611         var token = this._peek();
612         if (Tokens.ASSIGN <= token && token <= Tokens.ASSIGN_MOD) {
613             this._builder.addSpace();
614             this._next();
615             this._builder.addSpace();
616             this._parseAssignmentExpression();
617         }
618     },
619
620     _parseConditionalExpression: function()
621     {
622         this._parseBinaryExpression();
623         if (this._peek() === Tokens.CONDITIONAL) {
624             this._builder.addSpace();
625             this._consume(Tokens.CONDITIONAL);
626             this._builder.addSpace();
627             this._parseAssignmentExpression();
628             this._builder.addSpace();
629             this._expect(Tokens.COLON);
630             this._builder.addSpace();
631             this._parseAssignmentExpression();
632         }
633     },
634
635     _parseBinaryExpression: function()
636     {
637         this._parseUnaryExpression();
638         var token = this._peek();
639         while (Tokens.OR <= token && token <= Tokens.IN) {
640             this._builder.addSpace();
641             this._next();
642             this._builder.addSpace();
643             this._parseBinaryExpression();
644             token = this._peek();
645         }
646     },
647
648     _parseUnaryExpression: function()
649     {
650         var token = this._peek();
651         if ((Tokens.NOT <= token && token <= Tokens.VOID) || token === Tokens.ADD || token === Tokens.SUB || token ===  Tokens.INC || token === Tokens.DEC) {
652             this._next();
653             if (token === Tokens.DELETE || token === Tokens.TYPEOF || token === Tokens.VOID)
654                 this._builder.addSpace();
655             this._parseUnaryExpression();
656         } else
657             return this._parsePostfixExpression();
658     },
659
660     _parsePostfixExpression: function()
661     {
662         this._parseLeftHandSideExpression();
663         var token = this._peek();
664         if (!this._hasLineTerminatorBeforeNext() && (token === Tokens.INC || token === Tokens.DEC))
665             this._next();
666     },
667
668     _parseLeftHandSideExpression: function()
669     {
670         if (this._peek() === Tokens.NEW)
671             this._parseNewExpression();
672         else
673             this._parseMemberExpression();
674
675         while (true) {
676             switch (this._peek()) {
677             case Tokens.LBRACK:
678                 this._consume(Tokens.LBRACK);
679                 this._parseExpression();
680                 this._expect(Tokens.RBRACK);
681                 break;
682
683             case Tokens.LPAREN:
684                 this._parseArguments();
685                 break;
686
687             case Tokens.PERIOD:
688                 this._consume(Tokens.PERIOD);
689                 this._expect(Tokens.IDENTIFIER);
690                 break;
691
692             default:
693                 return;
694             }
695         }
696     },
697
698     _parseNewExpression: function()
699     {
700         this._expect(Tokens.NEW);
701         this._builder.addSpace();
702         if (this._peek() === Tokens.NEW)
703             this._parseNewExpression();
704         else
705             this._parseMemberExpression();
706     },
707
708     _parseMemberExpression: function()
709     {
710         if (this._peek() === Tokens.FUNCTION) {
711             this._expect(Tokens.FUNCTION);
712             if (this._peek() === Tokens.IDENTIFIER) {
713                 this._builder.addSpace();
714                 this._expect(Tokens.IDENTIFIER);
715             }
716             this._parseFunctionLiteral();
717         } else
718             this._parsePrimaryExpression();
719
720         while (true) {
721             switch (this._peek()) {
722             case Tokens.LBRACK:
723                 this._consume(Tokens.LBRACK);
724                 this._parseExpression();
725                 this._expect(Tokens.RBRACK);
726                 break;
727
728             case Tokens.PERIOD:
729                 this._consume(Tokens.PERIOD);
730                 this._expect(Tokens.IDENTIFIER);
731                 break;
732
733             case Tokens.LPAREN:
734                 this._parseArguments();
735                 break;
736
737             default:
738                 return;
739             }
740         }
741     },
742
743     _parseDebuggerStatement: function()
744     {
745         this._expect(Tokens.DEBUGGER);
746         this._expectSemicolon();
747     },
748
749     _parsePrimaryExpression: function()
750     {
751         switch (this._peek()) {
752         case Tokens.THIS:
753             return this._consume(Tokens.THIS);
754         case Tokens.NULL_LITERAL:
755             return this._consume(Tokens.NULL_LITERAL);
756         case Tokens.TRUE_LITERAL:
757             return this._consume(Tokens.TRUE_LITERAL);
758         case Tokens.FALSE_LITERAL:
759             return this._consume(Tokens.FALSE_LITERAL);
760         case Tokens.IDENTIFIER:
761             return this._consume(Tokens.IDENTIFIER);
762         case Tokens.NUMBER:
763             return this._consume(Tokens.NUMBER);
764         case Tokens.STRING:
765             return this._consume(Tokens.STRING);
766         case Tokens.ASSIGN_DIV:
767             return this._parseRegExpLiteral();
768         case Tokens.DIV:
769             return this._parseRegExpLiteral();
770         case Tokens.LBRACK:
771             return this._parseArrayLiteral();
772         case Tokens.LBRACE:
773             return this._parseObjectLiteral();
774         case Tokens.LPAREN:
775             this._consume(Tokens.LPAREN);
776             this._parseExpression();
777             this._expect(Tokens.RPAREN);
778             return;
779         default:
780             return this._next();
781         }
782     },
783
784     _parseArrayLiteral: function()
785     {
786         this._expect(Tokens.LBRACK);
787         this._builder.increaseNestingLevel();
788         while (this._peek() !== Tokens.RBRACK) {
789             if (this._peek() !== Tokens.COMMA)
790                 this._parseAssignmentExpression();
791             if (this._peek() !== Tokens.RBRACK) {
792                 this._expect(Tokens.COMMA);
793                 this._builder.addSpace();
794             }
795         }
796         this._builder.decreaseNestingLevel();
797         this._expect(Tokens.RBRACK);
798     },
799
800     _parseObjectLiteralGetSet: function()
801     {
802         var token = this._peek();
803         if (token === Tokens.IDENTIFIER || token === Tokens.NUMBER || token === Tokens.STRING ||
804             Tokens.DELETE <= token && token <= Tokens.FALSE_LITERAL ||
805             token === Tokens.INSTANCEOF || token === Tokens.IN || token === Tokens.CONST) {
806             this._next();
807             this._parseFunctionLiteral();
808         }
809     },
810
811     _parseObjectLiteral: function()
812     {
813         this._expect(Tokens.LBRACE);
814         this._builder.increaseNestingLevel();
815         while (this._peek() !== Tokens.RBRACE) {
816             var token = this._peek();
817             switch (token) {
818             case Tokens.IDENTIFIER:
819                 this._consume(Tokens.IDENTIFIER);
820                 var name = this._token.value;
821                 if ((name === "get" || name === "set") && this._peek() !== Tokens.COLON) {
822                     this._builder.addSpace();
823                     this._parseObjectLiteralGetSet();
824                     if (this._peek() !== Tokens.RBRACE) {
825                         this._expect(Tokens.COMMA);
826                     }
827                     continue;
828                 }
829                 break;
830
831             case Tokens.STRING:
832                 this._consume(Tokens.STRING);
833                 break;
834
835             case Tokens.NUMBER:
836                 this._consume(Tokens.NUMBER);
837                 break;
838
839             default:
840                 this._next();
841             }
842
843             this._expect(Tokens.COLON);
844             this._builder.addSpace();
845             this._parseAssignmentExpression();
846             if (this._peek() !== Tokens.RBRACE) {
847                 this._expect(Tokens.COMMA);
848             }
849         }
850         this._builder.decreaseNestingLevel();
851
852         this._expect(Tokens.RBRACE);
853     },
854
855     _parseRegExpLiteral: function()
856     {
857         if (this._nextToken.type === "regexp")
858             this._next();
859         else {
860             this._forceRegexp = true;
861             this._next();
862         }
863     },
864
865     _parseArguments: function()
866     {
867         this._expect(Tokens.LPAREN);
868         var done = (this._peek() === Tokens.RPAREN);
869         while (!done) {
870             this._parseAssignmentExpression();
871             done = (this._peek() === Tokens.RPAREN);
872             if (!done) {
873                 this._expect(Tokens.COMMA);
874                 this._builder.addSpace();
875             }
876         }
877         this._expect(Tokens.RPAREN);
878     },
879
880     _parseFunctionLiteral: function()
881     {
882         this._expect(Tokens.LPAREN);
883         var done = (this._peek() === Tokens.RPAREN);
884         while (!done) {
885             this._expect(Tokens.IDENTIFIER);
886             done = (this._peek() === Tokens.RPAREN);
887             if (!done) {
888                 this._expect(Tokens.COMMA);
889                 this._builder.addSpace();
890             }
891         }
892         this._expect(Tokens.RPAREN);
893         this._builder.addSpace();
894
895         this._expect(Tokens.LBRACE);
896         this._builder.addNewLine();
897         this._builder.increaseNestingLevel();
898         this._parseSourceElements(Tokens.RBRACE);
899         this._builder.decreaseNestingLevel();
900         this._expect(Tokens.RBRACE);
901     }
902 }