ExpressionParsing.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   import org.jetbrains.annotations.Nullable; 
24    
25    
26   /** 
27    * Created by IntelliJ IDEA. 
28    * User: max 
29    * Date: Jan 28, 2005 
30    * Time: 1:18:22 PM 
31    * To change this template use File | Settings | File Templates. 
32    */ 
33   public class ExpressionParsing extends Parsing{ 
34     private static final Logger LOG = Logger.getInstance("#com.intellij.lang.javascript.parsing.ExpressionParsing"); 
35     private ExpressionParsing() { } 
36    
37     private static boolean parsePrimaryExpression(PsiBuilder builder) { 
38       final IElementType firstToken = builder.getTokenType(); 
39       if (firstToken == JSTokenTypes.THIS_KEYWORD) { 
40         buildTokenElement(JSElementTypes.THIS_EXPRESSION, builder); 
41         return true; 
42       } 
43       else if (firstToken == JSTokenTypes.IDENTIFIER) { 
44         buildTokenElement(JSElementTypes.REFERENCE_EXPRESSION, builder); 
45         return true; 
46       } 
47       else if (firstToken == JSTokenTypes.NUMERIC_LITERAL || 
48                firstToken == JSTokenTypes.STRING_LITERAL || 
49                firstToken == JSTokenTypes.REGEXP_LITERAL || 
50                firstToken == JSTokenTypes.NULL_KEYWORD || 
51                firstToken == JSTokenTypes.FALSE_KEYWORD || 
52                firstToken == JSTokenTypes.TRUE_KEYWORD) { 
53         String errorMessage = validateLiteral(builder); 
54         buildTokenElement(JSElementTypes.LITERAL_EXPRESSION, builder); 
55         if (errorMessage != null) { 
56           builder.error(errorMessage); 
57         } 
58         return true; 
59       } 
60       else if (firstToken == JSTokenTypes.LPAR) { 
61         parseParenthesizedExpression(builder); 
62         return true; 
63       } 
64       else if (firstToken == JSTokenTypes.LBRACKET) { 
65         parseArrayLiteralExpression(builder); 
66         return true; 
67       } 
68       else if (firstToken == JSTokenTypes.LBRACE) { 
69         parseObjectLiteralExpression(builder); 
70         return true; 
71       } 
72       else if (firstToken == JSTokenTypes.FUNCTION_KEYWORD) { 
73         FunctionParsing.parseFunctionExpression(builder); 
74         return true; 
75       } 
76       else { 
77         return false; 
78       } 
79     } 
80    
81     @Nullable 
82     private static String validateLiteral(final PsiBuilder builder) { 
83       final IElementType ttype = builder.getTokenType(); 
84       if (ttype == JSTokenTypes.STRING_LITERAL) { 
85         final String ttext = builder.getTokenText(); 
86         assert ttext != null; 
87    
88         if (lastSymbolEscaped(ttext) || 
89             ttext.startsWith("\"") && (!ttext.endsWith("\"") || ttext.length() == 1 ) || 
90             ttext.startsWith("\'") && (!ttext.endsWith("\'") || ttext.length() == 1)) 
91         { 
92           return "Unclosed string literal"; 
93         } 
94       } 
95    
96       return null; 
97     } 
98    
99     private static boolean lastSymbolEscaped(String text) { 
100      boolean escapes = false; 
101      boolean escaped = true; 
102      for (int i = 0; i < text.length(); i++) { 
103        char c = text.charAt(i); 
104        if (escapes) { 
105          escapes = false; 
106          escaped = true; 
107          continue; 
108        } 
109        if (c == '\\') { 
110          escapes = true; 
111        } 
112        escaped = false; 
113      } 
114      return escapes || escaped; 
115    } 
116   
117    private static void parseObjectLiteralExpression(final PsiBuilder builder) { 
118      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.LBRACE); 
119      final PsiBuilder.Marker expr = builder.mark(); 
120      builder.advanceLexer(); 
121   
122      while (builder.getTokenType() != JSTokenTypes.RBRACE) { 
123        parseProperty(builder); 
124   
125        if (builder.getTokenType() != JSTokenTypes.COMMA) break; 
126        builder.advanceLexer(); 
127      } 
128   
129      checkMatches(builder, JSTokenTypes.RBRACE, "} expected"); 
130      expr.done(JSElementTypes.OBJECT_LITERAL_EXPRESSION); 
131    } 
132   
133    private static void parseProperty(final PsiBuilder builder) { 
134      final IElementType nameToken = builder.getTokenType(); 
135      final PsiBuilder.Marker property = builder.mark(); 
136   
137      String propName = nameToken == JSTokenTypes.IDENTIFIER ? builder.getTokenText() : null; 
138      if ("set".equals(propName) || "get".equals(propName)) { 
139        builder.advanceLexer(); 
140        if (builder.getTokenType() == JSTokenTypes.COLON) { 
141          builder.advanceLexer(); 
142          if (!parseAssignmentExpression(builder)) { 
143            builder.error("expression expected"); 
144          } 
145        } 
146        else { 
147          FunctionParsing.parseFunctionDeclaration(builder); 
148        } 
149      } 
150      else { 
151        if (nameToken != JSTokenTypes.IDENTIFIER && nameToken != JSTokenTypes.STRING_LITERAL && nameToken != JSTokenTypes.NUMERIC_LITERAL) { 
152          builder.error("identifier or string literal or numeric literal expected"); 
153        } 
154        builder.advanceLexer(); 
155        checkMatches(builder, JSTokenTypes.COLON, ": expected"); 
156   
157        if (!parseAssignmentExpression(builder)) { 
158          builder.error("expression expected"); 
159        } 
160      } 
161   
162      property.done(JSElementTypes.PROPERTY); 
163    } 
164   
165    private static void parseArrayLiteralExpression(final PsiBuilder builder) { 
166      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.LBRACKET); 
167      final PsiBuilder.Marker expr = builder.mark(); 
168      builder.advanceLexer(); 
169      while (builder.getTokenType() != JSTokenTypes.RBRACKET) { 
170        if (builder.getTokenType() == JSTokenTypes.COMMA) { 
171          builder.advanceLexer(); 
172        } 
173        else if (!parseAssignmentExpression(builder)) { 
174          builder.error("expression or , or ] expected"); 
175          break; 
176        } 
177      } 
178      checkMatches(builder, JSTokenTypes.RBRACKET, "] expected"); 
179      expr.done(JSElementTypes.ARRAY_LITERAL_EXPRESSION); 
180    } 
181   
182    private static void parseParenthesizedExpression(final PsiBuilder builder) { 
183      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.LPAR); 
184      final PsiBuilder.Marker expr = builder.mark(); 
185      builder.advanceLexer(); 
186      parseExpression(builder); 
187      checkMatches(builder, JSTokenTypes.RPAR, ") expected"); 
188      expr.done(JSElementTypes.PARENTHESIZED_EXPRESSION); 
189    } 
190   
191    private static boolean parseMemberExpression(PsiBuilder builder, boolean allowCallSyntax) { 
192      PsiBuilder.Marker expr = builder.mark(); 
193      final boolean isNew; 
194      if (builder.getTokenType() == JSTokenTypes.NEW_KEYWORD) { 
195        isNew = true; 
196        parseNewExpression(builder); 
197      } 
198      else { 
199        isNew = false; 
200        if (!parsePrimaryExpression(builder)) { 
201          expr.drop(); 
202          return false; 
203        } 
204      } 
205   
206      while (true) { 
207        final IElementType tokenType = builder.getTokenType(); 
208        if (tokenType == JSTokenTypes.DOT) { 
209          builder.advanceLexer(); 
210          checkMatches(builder, JSTokenTypes.IDENTIFIER, "name expected"); 
211          expr.done(JSElementTypes.REFERENCE_EXPRESSION); 
212          expr = expr.precede(); 
213        } 
214        else if (tokenType == JSTokenTypes.LBRACKET) { 
215          builder.advanceLexer(); 
216          parseExpression(builder); 
217          checkMatches(builder, JSTokenTypes.RBRACKET, "] expected"); 
218          expr.done(JSElementTypes.INDEXED_PROPERTY_ACCESS_EXPRESSION); 
219          expr = expr.precede(); 
220        } 
221        else if (allowCallSyntax && tokenType == JSTokenTypes.LPAR) { 
222          parseArgumentList(builder); 
223          expr.done(isNew ? JSElementTypes.NEW_EXPRESSION : JSElementTypes.CALL_EXPRESSION); 
224          expr = expr.precede(); 
225        } 
226        else { 
227          expr.drop(); 
228          break; 
229        } 
230      } 
231   
232      return true; 
233    } 
234   
235    private static void parseNewExpression(PsiBuilder builder) { 
236      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.NEW_KEYWORD); 
237      builder.advanceLexer(); 
238   
239      if (!parseMemberExpression(builder, false)) { 
240        builder.error("Expression expected"); 
241      } 
242   
243      if (builder.getTokenType() == JSTokenTypes.LPAR) { 
244        parseArgumentList(builder); 
245      } 
246    } 
247   
248    private static void parseArgumentList(final PsiBuilder builder) { 
249      LOG.assertTrue(builder.getTokenType() == JSTokenTypes.LPAR); 
250      final PsiBuilder.Marker arglist = builder.mark(); 
251      builder.advanceLexer(); 
252      boolean first = true; 
253      while (builder.getTokenType() != JSTokenTypes.RPAR) { 
254        if (first) { 
255          first = false; 
256        } 
257        else { 
258          if (builder.getTokenType() == JSTokenTypes.COMMA) { 
259            builder.advanceLexer(); 
260          } 
261          else { 
262            builder.error(", or ) expected"); 
263            break; 
264          } 
265        } 
266        if (!parseAssignmentExpression(builder)) { 
267          builder.error("expression expected"); 
268        } 
269      } 
270   
271      checkMatches(builder, JSTokenTypes.RPAR, ") expected"); 
272      arglist.done(JSElementTypes.ARGUMENT_LIST); 
273    } 
274   
275    public static void parseExpression(PsiBuilder builder) { 
276      if (!parseExpressionOptional(builder)) { 
277        builder.error("expression expected"); 
278      } 
279    } 
280   
281    public static boolean parseAssignmentExpressionNoIn(final PsiBuilder builder) { 
282      return parseAssignmentExpression(builder, false); 
283    } 
284   
285    public static boolean parseAssignmentExpression(final PsiBuilder builder) { 
286      return parseAssignmentExpression(builder, true); 
287    } 
288   
289    private static boolean parseAssignmentExpression(final PsiBuilder builder, boolean allowIn) { 
290      final PsiBuilder.Marker expr = builder.mark(); 
291      if (!parseConditionalExpression(builder, allowIn)) { 
292        expr.drop(); 
293        return false; 
294      } 
295   
296      if (JSTokenTypes.ASSIGNMENT_OPERATIONS.isInSet(builder.getTokenType())) { 
297        builder.advanceLexer(); 
298        if (!parseAssignmentExpression(builder, allowIn)) { 
299          builder.error("expression expected"); 
300        } 
301        expr.done(JSElementTypes.ASSIGNMENT_EXPRESSION); 
302      } 
303      else { 
304        expr.drop(); 
305      } 
306      return true; 
307    } 
308   
309    private static boolean parseConditionalExpression(final PsiBuilder builder, final boolean allowIn) { 
310      final PsiBuilder.Marker expr = builder.mark(); 
311      if (!parseORExpression(builder, allowIn)) { 
312        expr.drop(); 
313        return false; 
314      } 
315   
316      if (builder.getTokenType() == JSTokenTypes.QUEST) { 
317        builder.advanceLexer(); 
318        if (!parseAssignmentExpression(builder, allowIn)) { 
319          builder.error("expression expected"); 
320        } 
321        checkMatches(builder, JSTokenTypes.COLON, ": expected"); 
322        if (!parseAssignmentExpression(builder, allowIn)) { 
323          builder.error("expression expected"); 
324        } 
325        expr.done(JSElementTypes.CONDITIONAL_EXPRESSION); 
326      } 
327      else { 
328        expr.drop(); 
329      } 
330      return true; 
331    } 
332   
333    private static boolean parseORExpression(final PsiBuilder builder, final boolean allowIn) { 
334      PsiBuilder.Marker expr = builder.mark(); 
335      if (!parseANDExpression(builder, allowIn)) { 
336        expr.drop(); 
337        return false; 
338      } 
339   
340      while (builder.getTokenType() == JSTokenTypes.OROR) { 
341        builder.advanceLexer(); 
342        if (!parseANDExpression(builder, allowIn)) { 
343          builder.error("expression expected"); 
344        } 
345        expr.done(JSElementTypes.BINARY_EXPRESSION); 
346        expr = expr.precede(); 
347      } 
348   
349      expr.drop(); 
350      return true; 
351    } 
352   
353    private static boolean parseANDExpression(final PsiBuilder builder, final boolean allowIn) { 
354      PsiBuilder.Marker expr = builder.mark(); 
355      if (!parseBitwiseORExpression(builder, allowIn)) { 
356        expr.drop(); 
357        return false; 
358      } 
359   
360      while (builder.getTokenType() == JSTokenTypes.ANDAND) { 
361        builder.advanceLexer(); 
362        if (!parseBitwiseORExpression(builder, allowIn)) { 
363          builder.error("expression expected"); 
364        } 
365        expr.done(JSElementTypes.BINARY_EXPRESSION); 
366        expr = expr.precede(); 
367      } 
368   
369      expr.drop(); 
370      return true; 
371    } 
372   
373    private static boolean parseBitwiseORExpression(final PsiBuilder builder, final boolean allowIn) { 
374      PsiBuilder.Marker expr = builder.mark(); 
375      if (!parseBitwiseXORExpression(builder, allowIn)) { 
376        expr.drop(); 
377        return false; 
378      } 
379   
380      while (builder.getTokenType() == JSTokenTypes.OR) { 
381        builder.advanceLexer(); 
382        if (!parseBitwiseXORExpression(builder, allowIn)) { 
383          builder.error("expression expected"); 
384        } 
385        expr.done(JSElementTypes.BINARY_EXPRESSION); 
386        expr = expr.precede(); 
387      } 
388   
389      expr.drop(); 
390      return true; 
391    } 
392   
393    private static boolean parseBitwiseXORExpression(final PsiBuilder builder, final boolean allowIn) { 
394      PsiBuilder.Marker expr = builder.mark(); 
395      if (!parseBitwiseANDExpression(builder, allowIn)) { 
396        expr.drop(); 
397        return false; 
398      } 
399   
400      while (builder.getTokenType() == JSTokenTypes.XOR) { 
401        builder.advanceLexer(); 
402        if (!parseBitwiseANDExpression(builder, allowIn) ) { 
403          builder.error("expression expected"); 
404        } 
405        expr.done(JSElementTypes.BINARY_EXPRESSION); 
406        expr = expr.precede(); 
407      } 
408   
409      expr.drop(); 
410      return true; 
411    } 
412   
413    private static boolean parseBitwiseANDExpression(final PsiBuilder builder, final boolean allowIn) { 
414      PsiBuilder.Marker expr = builder.mark(); 
415      if (!parseEqualityExpression(builder, allowIn)) { 
416        expr.drop(); 
417        return false; 
418      } 
419   
420      while (builder.getTokenType() == JSTokenTypes.AND) { 
421        builder.advanceLexer(); 
422        if (!parseEqualityExpression(builder, allowIn)) { 
423          builder.error("expression expected"); 
424        } 
425        expr.done(JSElementTypes.BINARY_EXPRESSION); 
426        expr = expr.precede(); 
427      } 
428   
429      expr.drop(); 
430      return true; 
431    } 
432   
433    private static boolean parseEqualityExpression(final PsiBuilder builder, final boolean allowIn) { 
434      PsiBuilder.Marker expr = builder.mark(); 
435      if (!parseRelationalExpression(builder, allowIn)) { 
436        expr.drop(); 
437        return false; 
438      } 
439   
440      while (JSTokenTypes.EQUALITY_OPERATIONS.isInSet(builder.getTokenType())) { 
441        builder.advanceLexer(); 
442        if (!parseRelationalExpression(builder, allowIn)) { 
443          builder.error("expression expected"); 
444        } 
445        expr.done(JSElementTypes.BINARY_EXPRESSION); 
446        expr = expr.precede(); 
447      } 
448   
449      expr.drop(); 
450      return true; 
451    } 
452   
453    private static boolean parseRelationalExpression(final PsiBuilder builder, final boolean allowIn) { 
454      PsiBuilder.Marker expr = builder.mark(); 
455      if (!parseShiftExpression(builder)) { 
456        expr.drop(); 
457        return false; 
458      } 
459      while (JSTokenTypes.RELATIONAL_OPERATIONS.isInSet(builder.getTokenType()) && 
460             (allowIn || builder.getTokenType() != JSTokenTypes.IN_KEYWORD)) { 
461        builder.advanceLexer(); 
462        if (!parseShiftExpression(builder)) { 
463          builder.error("expression expected"); 
464        } 
465        expr.done(JSElementTypes.BINARY_EXPRESSION); 
466        expr = expr.precede(); 
467      } 
468   
469      expr.drop(); 
470      return true; 
471    } 
472   
473    private static boolean parseShiftExpression(final PsiBuilder builder) { 
474      PsiBuilder.Marker expr = builder.mark(); 
475      if (!parseAdditiveExpression(builder)) { 
476        expr.drop(); 
477        return false; 
478      } 
479      while (JSTokenTypes.SHIFT_OPERATIONS.isInSet(builder.getTokenType())) { 
480        builder.advanceLexer(); 
481        if (!parseAdditiveExpression(builder)) { 
482          builder.error("expression expected"); 
483        } 
484        expr.done(JSElementTypes.BINARY_EXPRESSION); 
485        expr = expr.precede(); 
486      } 
487   
488      expr.drop(); 
489      return true; 
490    } 
491   
492    private static boolean parseAdditiveExpression(final PsiBuilder builder) { 
493      PsiBuilder.Marker expr = builder.mark(); 
494      if (!parseMultiplicativeExpression(builder)) { 
495        expr.drop(); 
496        return false; 
497      } 
498      while (JSTokenTypes.ADDITIVE_OPERATIONS.isInSet(builder.getTokenType())) { 
499        builder.advanceLexer(); 
500        if (!parseMultiplicativeExpression(builder)) { 
501          builder.error("expression expected"); 
502        } 
503        expr.done(JSElementTypes.BINARY_EXPRESSION); 
504        expr = expr.precede(); 
505      } 
506   
507      expr.drop(); 
508      return true; 
509    } 
510   
511    private static boolean parseMultiplicativeExpression(final PsiBuilder builder) { 
512      PsiBuilder.Marker expr = builder.mark(); 
513      if (!parseUnaryExpression(builder)) { 
514        expr.drop(); 
515        return false; 
516      } 
517   
518      while (JSTokenTypes.MULTIPLICATIVE_OPERATIONS.isInSet(builder.getTokenType())) { 
519        builder.advanceLexer(); 
520        if (!parseUnaryExpression(builder)) { 
521          builder.error("expression expected"); 
522        } 
523        expr.done(JSElementTypes.BINARY_EXPRESSION); 
524        expr = expr.precede(); 
525      } 
526   
527      expr.drop(); 
528      return true; 
529    } 
530   
531    private static boolean parseUnaryExpression(final PsiBuilder builder) { 
532      final IElementType tokenType = builder.getTokenType(); 
533      if (JSTokenTypes.UNARY_OPERATIONS.isInSet(tokenType)) { 
534        final PsiBuilder.Marker expr = builder.mark(); 
535        builder.advanceLexer(); 
536        if (!parseUnaryExpression(builder)) { 
537          builder.error("Expression expected"); 
538        } 
539        expr.done(JSElementTypes.PREFIX_EXPRESSION); 
540        return true; 
541      } 
542      else { 
543        return parsePostfixExpression(builder); 
544      } 
545    } 
546   
547    private static boolean parsePostfixExpression(PsiBuilder builder) { 
548      final PsiBuilder.Marker expr = builder.mark(); 
549      if (!parseMemberExpression(builder, true)) { 
550        expr.drop(); 
551        return false; 
552      } 
553   
554      final IElementType tokenType = builder.getTokenType(); 
555      if (tokenType == JSTokenTypes.PLUSPLUS || tokenType == JSTokenTypes.MINUSMINUS) { 
556        builder.advanceLexer(); 
557        expr.done(JSElementTypes.POSTFIX_EXPRESSION); 
558      } 
559      else { 
560        expr.drop(); 
561      } 
562      return true; 
563    } 
564   
565    public static boolean parseExpressionOptional(final PsiBuilder builder) { 
566      return parseExpressionOptional(builder, true); 
567    } 
568    public static boolean parseExpressionOptionalNoIn(final PsiBuilder builder) { 
569      return parseExpressionOptional(builder, false); 
570    } 
571   
572    public static boolean parseExpressionOptional(final PsiBuilder builder, final boolean allowIn) { 
573      PsiBuilder.Marker expr = builder.mark(); 
574      if (!parseAssignmentExpression(builder, allowIn)) { 
575        expr.drop(); 
576        return false; 
577      } 
578   
579      while (builder.getTokenType() == JSTokenTypes.COMMA) { 
580        builder.advanceLexer(); 
581        if (!parseAssignmentExpression(builder, allowIn)) { 
582          builder.error("expression expected"); 
583        } 
584   
585        expr.done(JSElementTypes.COMMA_EXPRESSION); 
586        expr = expr.precede(); 
587      } 
588   
589      expr.drop(); 
590   
591      return true; 
592    } 
593   
594    private static void buildTokenElement(IElementType type, PsiBuilder builder) { 
595      final PsiBuilder.Marker marker = builder.mark(); 
596      builder.advanceLexer(); 
597      marker.done(type); 
598    } 
599  } 
600