StatementParsing.java

1    /* 
2     * Copyright 2000-2005 JetBrains s.r.o. 
3     * 
4     * Licensed under the Apache License, Version 2.0 (the "License"); 
5     * you may not use this file except in compliance with the License. 
6     * You may obtain a copy of the License at 
7     * 
8     * http://www.apache.org/licenses/LICENSE-2.0 
9     * 
10    * Unless required by applicable law or agreed to in writing, software 
11    * distributed under the License is distributed on an "AS IS" BASIS, 
12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13    * See the License for the specific language governing permissions and 
14    * limitations under the License. 
15    */ 
16   package com.intellij.lang.javascript.parsing; 
17    
18   import com.intellij.lang.PsiBuilder; 
19   import com.intellij.lang.javascript.JSElementTypes; 
20   import com.intellij.lang.javascript.JSTokenTypes; 
21   import com.intellij.openapi.diagnostic.Logger; 
22   import com.intellij.psi.tree.IElementType; 
23    
24   /** 
25    * Created by IntelliJ IDEA. 
26    * User: max 
27    * Date: Jan 28, 2005 
28    * Time: 1:19:13 PM 
29    * To change this template use File | Settings | File Templates. 
30    */ 
31   public class StatementParsing extends Parsing{ 
32     private static final Logger LOG = Logger.getInstance("#com.intellij.lang.javascript.parsing.StatementParsing"); 
33     private StatementParsing() { } 
34    
35     public static void parseSourceElement(PsiBuilder builder) { 
36       if (builder.getTokenType() == JSTokenTypes.FUNCTION_KEYWORD) { 
37         FunctionParsing.parseFunctionDeclaration(builder); 
38       } 
39       else { 
40         parseStatement(builder); 
41       } 
42     } 
43    
44     public static void parseStatement(PsiBuilder builder) { 
45       final IElementType firstToken = builder.getTokenType(); 
46    
47       if (firstToken == null) return; 
48    
49       if (firstToken == JSTokenTypes.LBRACE) { 
50         parseBlock(builder); 
51         return; 
52       } 
53    
54       if (firstToken == JSTokenTypes.VAR_KEYWORD || firstToken == JSTokenTypes.CONST_KEYWORD) { 
55         parseVarStatement(builder, false); 
56         return; 
57       } 
58    
59       if (firstToken == JSTokenTypes.SEMICOLON) { 
60         parseEmptyStatement(builder); 
61         return; 
62       } 
63    
64       if (firstToken == JSTokenTypes.IF_KEYWORD) { 
65         parseIfStatement(builder); 
66         return; 
67       } 
68    
69       if (firstToken == JSTokenTypes.DO_KEYWORD || 
70           firstToken == JSTokenTypes.WHILE_KEYWORD || 
71           firstToken == JSTokenTypes.FOR_KEYWORD) { 
72         parseIterationStatement(builder); 
73         return; 
74       } 
75    
76       if (firstToken == JSTokenTypes.CONTINUE_KEYWORD) { 
77         parseContinueStatement(builder); 
78         return; 
79       } 
80    
81       if (firstToken == JSTokenTypes.BREAK_KEYWORD) { 
82         parseBreakStatement(builder); 
83         return; 
84       } 
85    
86       if (firstToken == JSTokenTypes.RETURN_KEYWORD) { 
87         parseReturnStatement(builder); 
88         return; 
89       } 
90    
91       if (firstToken == JSTokenTypes.WITH_KEYWORD) { 
92         parseWithStatement(builder); 
93         return; 
94       } 
95    
96       if (firstToken == JSTokenTypes.SWITCH_KEYWORD) { 
97         parseSwitchStatement(builder); 
98         return; 
99       } 
100   
101      if (firstToken == JSTokenTypes.THROW_KEYWORD) { 
102        parseThrowStatement(builder); 
103        return; 
104      } 
105   
106      if (firstToken == JSTokenTypes.TRY_KEYWORD) { 
107        parseTryStatement(builder); 
108        return; 
109      } 
110   
111      if (firstToken == JSTokenTypes.FUNCTION_KEYWORD) { 
112        FunctionParsing.parseFunctionDeclaration(builder); 
113        return; 
114      } 
115   
116   
117      if (firstToken == JSTokenTypes.IDENTIFIER) { 
118        // Try labeled statement: 
119        final PsiBuilder.Marker labeledStatement = builder.mark(); 
120        builder.advanceLexer(); 
121        if (builder.getTokenType() == JSTokenTypes.COLON) { 
122          builder.advanceLexer(); 
123          parseStatement(builder); 
124          labeledStatement.done(JSElementTypes.LABELED_STATEMENT); 
125          return; 
126        } 
127        else { 
128          labeledStatement.rollbackTo(); 
129        } 
130      } 
131   
132      if (firstToken != JSTokenTypes.LBRACE && firstToken != JSTokenTypes.FUNCTION_KEYWORD) { 
133        // Try expression statement 
134        final PsiBuilder.Marker exprStatement = builder.mark(); 
135        if (ExpressionParsing.parseExpressionOptional(builder)) { 
136          checkForSemicolon(builder); 
137          exprStatement.done(JSElementTypes.EXPRESSION_STATEMENT); 
138          return; 
139        } 
140        else { 
141          exprStatement.drop(); 
142        } 
143      } 
144   
145      builder.advanceLexer(); 
146      builder.error("statement expected"); 
147    } 
148   
149    private static void parseTryStatement(final PsiBuilder builder) { 
150      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.TRY_KEYWORD); 
151      final PsiBuilder.Marker statement = builder.mark(); 
152      builder.advanceLexer(); 
153      parseBlock(builder); 
154   
155      if (builder.getTokenType() == JSTokenTypes.CATCH_KEYWORD) { 
156        parseCatchBlock(builder); 
157      } 
158   
159      if (builder.getTokenType() == JSTokenTypes.FINALLY_KEYWORD) { 
160        builder.advanceLexer(); 
161        parseBlock(builder); 
162      } 
163   
164      statement.done(JSElementTypes.TRY_STATEMENT); 
165    } 
166   
167    private static void parseCatchBlock(final PsiBuilder builder) { 
168      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.CATCH_KEYWORD); 
169      final PsiBuilder.Marker block = builder.mark(); 
170      builder.advanceLexer(); 
171      checkMatches(builder, JSTokenTypes.LPAR, "( expected"); 
172   
173      if (builder.getTokenType() == JSTokenTypes.IDENTIFIER) { 
174        final PsiBuilder.Marker param = builder.mark(); 
175        builder.advanceLexer(); 
176        param.done(JSElementTypes.FORMAL_PARAMETER); 
177      } 
178      else { 
179        builder.error("parameter name expected"); 
180      } 
181   
182      checkMatches(builder, JSTokenTypes.RPAR, ") expected"); 
183   
184      parseBlock(builder); 
185   
186      block.done(JSElementTypes.CATCH_BLOCK); 
187    } 
188   
189    private static void parseThrowStatement(final PsiBuilder builder) { 
190      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.THROW_KEYWORD); 
191      final PsiBuilder.Marker statement = builder.mark(); 
192      builder.advanceLexer(); 
193   
194      ExpressionParsing.parseExpressionOptional(builder); 
195   
196      checkForSemicolon(builder); 
197      statement.done(JSElementTypes.THROW_STATEMENT); 
198    } 
199   
200    private static void parseSwitchStatement(final PsiBuilder builder) { 
201      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.SWITCH_KEYWORD); 
202      final PsiBuilder.Marker statement = builder.mark(); 
203      builder.advanceLexer(); 
204   
205      checkMatches(builder, JSTokenTypes.LPAR, "( expected"); 
206      ExpressionParsing.parseExpression(builder); 
207      checkMatches(builder, JSTokenTypes.RPAR, ") expected"); 
208   
209      checkMatches(builder, JSTokenTypes.LBRACE, "{ expected"); 
210      while (builder.getTokenType() != JSTokenTypes.RBRACE) { 
211        if (builder.eof()) { 
212          builder.error("unexpected end of file"); 
213          statement.done(JSElementTypes.SWITCH_STATEMENT); 
214          return; 
215        } 
216        parseCaseOrDefaultClause(builder); 
217      } 
218   
219      builder.advanceLexer(); 
220      statement.done(JSElementTypes.SWITCH_STATEMENT); 
221    } 
222   
223    private static void parseCaseOrDefaultClause(final PsiBuilder builder) { 
224      final IElementType firstToken = builder.getTokenType(); 
225      final PsiBuilder.Marker clause = builder.mark(); 
226      if (firstToken != JSTokenTypes.CASE_KEYWORD && firstToken != JSTokenTypes.DEFAULT_KEYWORD) { 
227        builder.error("catch or default expected"); 
228      } 
229      builder.advanceLexer(); 
230      if (firstToken == JSTokenTypes.CASE_KEYWORD) { 
231        ExpressionParsing.parseExpression(builder); 
232      } 
233      checkMatches(builder, JSTokenTypes.COLON, ": expected"); 
234      while (true) { 
235        IElementType token = builder.getTokenType(); 
236        if (token == null || token == JSTokenTypes.CASE_KEYWORD || token == JSTokenTypes.DEFAULT_KEYWORD || token == JSTokenTypes.RBRACE) break; 
237        parseStatement(builder); 
238      } 
239      clause.done(JSElementTypes.CASE_CLAUSE); 
240    } 
241   
242    private static void parseWithStatement(final PsiBuilder builder) { 
243      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.WITH_KEYWORD); 
244      final PsiBuilder.Marker statement = builder.mark(); 
245      builder.advanceLexer(); 
246   
247      checkMatches(builder, JSTokenTypes.LPAR, "( expected"); 
248      ExpressionParsing.parseExpression(builder); 
249      checkMatches(builder, JSTokenTypes.RPAR, ") expected"); 
250   
251      parseStatement(builder); 
252   
253      statement.done(JSElementTypes.WITH_STATEMENT); 
254    } 
255   
256    private static void parseReturnStatement(final PsiBuilder builder) { 
257      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.RETURN_KEYWORD); 
258      final PsiBuilder.Marker statement = builder.mark(); 
259      builder.advanceLexer(); 
260   
261      ExpressionParsing.parseExpressionOptional(builder); 
262   
263      checkForSemicolon(builder); 
264      statement.done(JSElementTypes.RETURN_STATEMENT); 
265    } 
266   
267    private static void parseBreakStatement(final PsiBuilder builder) { 
268      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.BREAK_KEYWORD); 
269      final PsiBuilder.Marker statement = builder.mark(); 
270      builder.advanceLexer(); 
271   
272      if (builder.getTokenType() == JSTokenTypes.IDENTIFIER) { 
273        builder.advanceLexer(); 
274      } 
275   
276      if (builder.getTokenType() == JSTokenTypes.SEMICOLON || builder.getTokenType() == JSTokenTypes.SEMANTIC_LINEFEED) { 
277        builder.advanceLexer(); 
278      } 
279   
280      statement.done(JSElementTypes.BREAK_STATEMENT); 
281    } 
282   
283    private static void parseContinueStatement(final PsiBuilder builder) { 
284      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.CONTINUE_KEYWORD); 
285      final PsiBuilder.Marker statement = builder.mark(); 
286      builder.advanceLexer(); 
287      if (builder.getTokenType() == JSTokenTypes.IDENTIFIER) { 
288        builder.advanceLexer(); 
289      } 
290   
291      if (builder.getTokenType() == JSTokenTypes.SEMICOLON || builder.getTokenType() == JSTokenTypes.SEMANTIC_LINEFEED) { 
292        builder.advanceLexer(); 
293      } 
294   
295      statement.done(JSElementTypes.CONTINUE_STATEMENT); 
296    } 
297   
298    private static void parseIterationStatement(final PsiBuilder builder) { 
299      final IElementType tokenType = builder.getTokenType(); 
300      if (tokenType == JSTokenTypes.DO_KEYWORD) { 
301        parseDoWhileStatement(builder); 
302      } 
303      else if (tokenType == JSTokenTypes.WHILE_KEYWORD) { 
304        parseWhileStatement(builder); 
305      } 
306      else if (tokenType == JSTokenTypes.FOR_KEYWORD) { 
307        parseForStatement(builder); 
308      } 
309      else { 
310        LOG.error("Unknown iteration statement"); 
311      } 
312    } 
313   
314    private static void parseForStatement(final PsiBuilder builder) { 
315      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.FOR_KEYWORD); 
316      final PsiBuilder.Marker statement = builder.mark(); 
317      builder.advanceLexer(); 
318      checkMatches(builder, JSTokenTypes.LPAR, "( expected"); 
319      final boolean empty; 
320      if (builder.getTokenType() == JSTokenTypes.VAR_KEYWORD) { 
321        parseVarStatement(builder, true); 
322        empty = false; 
323      } 
324      else { 
325        empty = !ExpressionParsing.parseExpressionOptional(builder, false); 
326      } 
327   
328      boolean forin = false; 
329      if (builder.getTokenType() == JSTokenTypes.SEMICOLON) { 
330        builder.advanceLexer(); 
331        ExpressionParsing.parseExpressionOptional(builder); 
332        checkForSemicolon(builder); 
333        ExpressionParsing.parseExpressionOptional(builder); 
334      } 
335      else if (builder.getTokenType() == JSTokenTypes.IN_KEYWORD) { 
336        forin = true; 
337        if (empty) builder.error("left hand side expression or variable declaration expected before 'in'"); 
338        builder.advanceLexer(); 
339        ExpressionParsing.parseExpression(builder); 
340      } 
341      else { 
342        builder.error("in or ; expected"); 
343      } 
344   
345      checkMatches(builder, JSTokenTypes.RPAR, ") expected"); 
346   
347      parseStatement(builder); 
348      statement.done(forin ? JSElementTypes.FOR_IN_STATEMENT : JSElementTypes.FOR_STATEMENT); 
349    } 
350   
351    private static void parseWhileStatement(final PsiBuilder builder) { 
352      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.WHILE_KEYWORD); 
353      final PsiBuilder.Marker statement = builder.mark(); 
354      builder.advanceLexer(); 
355   
356      checkMatches(builder, JSTokenTypes.LPAR, "( expected"); 
357      ExpressionParsing.parseExpression(builder); 
358      checkMatches(builder, JSTokenTypes.RPAR, ") expected"); 
359   
360      parseStatement(builder); 
361      statement.done(JSElementTypes.WHILE_STATEMENT); 
362    } 
363   
364    private static void parseDoWhileStatement(final PsiBuilder builder) { 
365      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.DO_KEYWORD); 
366      final PsiBuilder.Marker statement = builder.mark(); 
367      builder.advanceLexer(); 
368   
369      parseStatement(builder); 
370      checkMatches(builder, JSTokenTypes.WHILE_KEYWORD, "while expected"); 
371      checkMatches(builder, JSTokenTypes.LPAR, "( expected"); 
372      ExpressionParsing.parseExpression(builder); 
373      checkMatches(builder, JSTokenTypes.RPAR, ") expected"); 
374      checkForSemicolon(builder); 
375   
376      statement.done(JSElementTypes.DOWHILE_STATEMENT); 
377    } 
378   
379    private static void parseIfStatement(final PsiBuilder builder) { 
380      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.IF_KEYWORD); 
381      final PsiBuilder.Marker ifStatement = builder.mark(); 
382      builder.advanceLexer(); 
383   
384      checkMatches(builder, JSTokenTypes.LPAR, "( expected"); 
385      ExpressionParsing.parseExpression(builder); 
386       
387      // handle empty expressions inside 
388      while (builder.getTokenType() == JSTokenTypes.OROR || builder.getTokenType() == JSTokenTypes.EQEQ) { 
389        builder.advanceLexer(); 
390      } 
391       
392      checkMatches(builder, JSTokenTypes.RPAR, ") expected"); 
393   
394      parseStatement(builder); 
395   
396      if (builder.getTokenType() == JSTokenTypes.ELSE_KEYWORD) { 
397        builder.advanceLexer(); 
398        parseStatement(builder); 
399      } 
400   
401      ifStatement.done(JSElementTypes.IF_STATEMENT); 
402    } 
403   
404    private static void parseEmptyStatement(final PsiBuilder builder) { 
405      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.SEMICOLON); 
406      final PsiBuilder.Marker statement = builder.mark(); 
407      builder.advanceLexer(); 
408      statement.done(JSElementTypes.EMPTY_STATEMENT); 
409    } 
410   
411    private static void parseVarStatement(final PsiBuilder builder, final boolean inForInitializationContext) { 
412      final IElementType declType = builder.getTokenType(); 
413      LOG.assertTrue(declType == JSTokenTypes.VAR_KEYWORD || declType == JSTokenTypes.CONST_KEYWORD); 
414      final PsiBuilder.Marker var = builder.mark(); 
415      builder.advanceLexer(); 
416      boolean first = true; 
417      while (true) { 
418        if (first) { 
419          first = false; 
420        } 
421        else { 
422          checkMatches(builder, JSTokenTypes.COMMA, ", expected"); 
423        } 
424   
425        parseVarDeclaration(builder, !inForInitializationContext); 
426   
427        if (builder.getTokenType() != JSTokenTypes.COMMA) { 
428          break; 
429        } 
430      } 
431   
432      if (!inForInitializationContext) { 
433        checkForSemicolon(builder); 
434      } 
435   
436      var.done(JSElementTypes.VAR_STATEMENT); 
437    } 
438   
439    private static void checkForSemicolon(final PsiBuilder builder) { 
440      if (builder.getTokenType() == JSTokenTypes.SEMICOLON) { 
441        builder.advanceLexer(); 
442      } 
443    } 
444   
445    private static void parseVarDeclaration(final PsiBuilder builder, boolean allowIn) { 
446      if (builder.getTokenType() != JSTokenTypes.IDENTIFIER) { 
447        builder.error("variable name expected"); 
448        builder.advanceLexer(); 
449        return; 
450      } 
451      final PsiBuilder.Marker var = builder.mark(); 
452      builder.advanceLexer(); 
453      if (builder.getTokenType() == JSTokenTypes.EQ) { 
454        builder.advanceLexer(); 
455        if (allowIn) { 
456          if (!ExpressionParsing.parseAssignmentExpression(builder)) { 
457            builder.error("expression expected"); 
458          } 
459        } 
460        else { 
461          if (!ExpressionParsing.parseAssignmentExpressionNoIn(builder)) { 
462            builder.error("expression expected"); 
463          } 
464        } 
465      } 
466      var.done(JSElementTypes.VARIABLE); 
467    } 
468   
469    public static void parseBlock(final PsiBuilder builder) { 
470      parseBlockOrFunctionBody(builder, false); 
471    } 
472   
473    public static void parseFunctionBody(final PsiBuilder builder) { 
474      parseBlockOrFunctionBody(builder, true); 
475    } 
476   
477    private static void parseBlockOrFunctionBody(final PsiBuilder builder, boolean functionBody) { 
478      if (builder.getTokenType() != JSTokenTypes.LBRACE) { 
479        builder.error("{ expected"); 
480        return; 
481      } 
482   
483      final PsiBuilder.Marker block = builder.mark(); 
484      builder.advanceLexer(); 
485      while (builder.getTokenType() != JSTokenTypes.RBRACE) { 
486        if (builder.eof()) { 
487          builder.error("missing }"); 
488          block.done(JSElementTypes.BLOCK); 
489          return; 
490        } 
491   
492        if (functionBody) { 
493          parseSourceElement(builder); 
494        } 
495        else { 
496          parseStatement(builder); 
497        } 
498      } 
499   
500      builder.advanceLexer(); 
501      block.done(JSElementTypes.BLOCK); 
502    } 
503  } 
504