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