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