groovy for-in parser fix
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / parser / parsing / statements / ForStatement.java
1 /*
2  * Copyright 2000-2009 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
17 package org.jetbrains.plugins.groovy.lang.parser.parsing.statements;
18
19 import com.intellij.lang.PsiBuilder;
20 import org.jetbrains.plugins.groovy.GroovyBundle;
21 import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
22 import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
23 import org.jetbrains.plugins.groovy.lang.parser.GroovyParser;
24 import org.jetbrains.plugins.groovy.lang.parser.parsing.auxiliary.modifiers.Modifiers;
25 import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.declaration.Declaration;
26 import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.StrictContextExpression;
27 import org.jetbrains.plugins.groovy.lang.parser.parsing.statements.expressions.arithmetic.ShiftExpression;
28 import org.jetbrains.plugins.groovy.lang.parser.parsing.types.TypeSpec;
29 import org.jetbrains.plugins.groovy.lang.parser.parsing.util.ParserUtils;
30
31 /**
32  * @autor: ilyas
33  */
34 public class ForStatement implements GroovyElementTypes {
35
36   public static boolean forClauseParse(PsiBuilder builder, GroovyParser parser) {
37     ParserUtils.getToken(builder, mNLS);
38     return forInClauseParse(builder, parser) || tradForClauseParse(builder, parser);
39   }
40
41   private static boolean tradForClauseParse(PsiBuilder builder, GroovyParser parser) {
42     PsiBuilder.Marker marker = builder.mark();
43
44     if (ParserUtils.getToken(builder, mSEMI) || (Declaration.parse(builder, false, parser) && ParserUtils.getToken(builder, mSEMI))) {
45       StrictContextExpression.parse(builder, parser);
46       ParserUtils.getToken(builder, mSEMI, GroovyBundle.message("semi.expected"));
47       ParserUtils.getToken(builder, mNLS);
48       if (!mRPAREN.equals(builder.getTokenType())) {
49         controlExpressionListParse(builder, parser);
50       }
51     }
52     else {
53       marker.rollbackTo();
54       marker = builder.mark();
55       controlExpressionListParse(builder, parser);
56       ParserUtils.getToken(builder, mSEMI, GroovyBundle.message("semi.expected"));
57       StrictContextExpression.parse(builder, parser);
58       ParserUtils.getToken(builder, mSEMI, GroovyBundle.message("semi.expected"));
59       ParserUtils.getToken(builder, mNLS);
60       if (!mRPAREN.equals(builder.getTokenType())) {
61         controlExpressionListParse(builder, parser);
62       }
63     }
64
65     marker.done(FOR_TRADITIONAL_CLAUSE);
66     return true;
67   }
68
69   /*
70    * Parses list of control expression in for condition
71    */
72   private static void controlExpressionListParse(PsiBuilder builder, GroovyParser parser) {
73
74     if (!StrictContextExpression.parse(builder, parser)) return;
75
76     while (mCOMMA.equals(builder.getTokenType())) {
77
78       if (ParserUtils.lookAhead(builder, mCOMMA, mNLS, mRPAREN) || ParserUtils.lookAhead(builder, mCOMMA, mRPAREN)) {
79         ParserUtils.getToken(builder, mCOMMA);
80         builder.error(GroovyBundle.message("expression.expected"));
81       }
82       else {
83         ParserUtils.getToken(builder, mCOMMA);
84       }
85       ParserUtils.getToken(builder, mNLS);
86       if (!StrictContextExpression.parse(builder, parser)) {
87         ParserUtils.getToken(builder, mNLS);
88         if (!mRPAREN.equals(builder.getTokenType()) && !mSEMI.equals(builder.getTokenType())) {
89           builder.error(GroovyBundle.message("expression.expected"));
90         }
91         if (!mRPAREN.equals(builder.getTokenType()) &&
92             !mSEMI.equals(builder.getTokenType()) &&
93             !mCOMMA.equals(builder.getTokenType()) &&
94             !mNLS.equals(builder.getTokenType())) {
95           builder.advanceLexer();
96         }
97       }
98     }
99   }
100
101   /*
102    * Parses Groovy-style 'in' clause
103    */
104   private static boolean forInClauseParse(PsiBuilder builder, GroovyParser parser) {
105     PsiBuilder.Marker marker = builder.mark();
106
107     PsiBuilder.Marker paramMarker = builder.mark();
108
109     Modifiers.parse(builder, parser);
110
111     boolean isBuiltInType = TokenSets.BUILT_IN_TYPE.contains(builder.getTokenType());
112
113     PsiBuilder.Marker typeSpec = builder.mark();
114     TypeSpec.parseStrict(builder);
115
116     if (builder.getTokenType() == mIDENT || isBuiltInType) {
117       typeSpec.drop();
118     }
119     else {
120       typeSpec.rollbackTo();
121     }
122
123     if (TokenSets.FOR_IN_DELIMITERS.contains(builder.getTokenType())) {
124       builder.error(GroovyBundle.message("identifier.expected"));
125       paramMarker.drop();
126     }
127     else if (builder.getTokenType() == mIDENT) {
128       ParserUtils.getToken(builder, mIDENT);
129       paramMarker.done(PARAMETER);
130     }
131     else {
132       paramMarker.drop();
133       marker.rollbackTo();
134       return false;
135     }
136
137     if (!ParserUtils.getToken(builder, kIN) && !ParserUtils.getToken(builder, mCOLON)) {
138       marker.rollbackTo();
139       return false;
140     }
141
142     if (!ShiftExpression.parse(builder, parser)) {
143       builder.error(GroovyBundle.message("expression.expected"));
144     }
145     marker.done(FOR_IN_CLAUSE);
146     return true;
147   }
148 }