CompletenessAnalyzer.java revision 3062:15bdc18525ff
1/* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.jshell; 27 28import com.sun.tools.javac.code.Source; 29import com.sun.tools.javac.parser.Scanner; 30import com.sun.tools.javac.parser.ScannerFactory; 31import com.sun.tools.javac.parser.Tokens.Token; 32import com.sun.tools.javac.parser.Tokens.TokenKind; 33import com.sun.tools.javac.util.Context; 34import com.sun.tools.javac.util.JCDiagnostic; 35import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; 36import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 37import com.sun.tools.javac.util.Log; 38import java.io.PrintWriter; 39import java.io.StringWriter; 40import java.util.ArrayDeque; 41import java.util.Deque; 42import java.util.EnumMap; 43import java.util.Iterator; 44import jdk.jshell.SourceCodeAnalysis.Completeness; 45import com.sun.source.tree.Tree; 46import static jdk.jshell.CompletenessAnalyzer.TK.*; 47import jdk.jshell.TaskFactory.ParseTask; 48import java.util.List; 49 50/** 51 * Low level scanner to determine completeness of input. 52 * @author Robert Field 53 */ 54class CompletenessAnalyzer { 55 56 private final ScannerFactory scannerFactory; 57 private final JShell proc; 58 59 private static Completeness error() { 60 return Completeness.UNKNOWN; // For breakpointing 61 } 62 63 static class CaInfo { 64 65 CaInfo(Completeness status, int unitEndPos) { 66 this.status = status; 67 this.unitEndPos = unitEndPos; 68 } 69 final int unitEndPos; 70 final Completeness status; 71 } 72 73 CompletenessAnalyzer(JShell proc) { 74 this.proc = proc; 75 Context context = new Context(); 76 Log log = CaLog.createLog(context); 77 context.put(Log.class, log); 78 context.put(Source.class, Source.JDK1_9); 79 scannerFactory = ScannerFactory.instance(context); 80 } 81 82 CaInfo scan(String s) { 83 try { 84 Scanner scanner = scannerFactory.newScanner(s, false); 85 Matched in = new Matched(scanner); 86 Parser parser = new Parser(in, proc, s); 87 Completeness stat = parser.parseUnit(); 88 int endPos = stat == Completeness.UNKNOWN 89 ? s.length() 90 : in.prevCT.endPos; 91 return new CaInfo(stat, endPos); 92 } catch (SyntaxException ex) { 93 return new CaInfo(error(), s.length()); 94 } 95 } 96 97 @SuppressWarnings("serial") // serialVersionUID intentionally omitted 98 private static class SyntaxException extends RuntimeException { 99 } 100 101 private static void die() { 102 throw new SyntaxException(); 103 } 104 105 /** 106 * Subclass of Log used by compiler API to die on error and ignore 107 * other messages 108 */ 109 private static class CaLog extends Log { 110 111 private static CaLog createLog(Context context) { 112 PrintWriter pw = new PrintWriter(new StringWriter()); 113 CaLog log = new CaLog(context, pw, pw, pw); 114 context.put(outKey, pw); 115 context.put(logKey, log); 116 return log; 117 } 118 119 private CaLog(Context context, PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter) { 120 super(context, errWriter, warnWriter, noticeWriter); 121 } 122 123 @Override 124 public void error(String key, Object... args) { 125 die(); 126 } 127 128 @Override 129 public void error(DiagnosticPosition pos, String key, Object... args) { 130 die(); 131 } 132 133 @Override 134 public void error(DiagnosticFlag flag, DiagnosticPosition pos, String key, Object... args) { 135 die(); 136 } 137 138 @Override 139 public void error(int pos, String key, Object... args) { 140 die(); 141 } 142 143 @Override 144 public void error(DiagnosticFlag flag, int pos, String key, Object... args) { 145 die(); 146 } 147 148 @Override 149 public void report(JCDiagnostic diagnostic) { 150 // Ignore 151 } 152 } 153 154 // Location position kinds -- a token is ... 155 private static final int XEXPR = 0b1; // OK in expression (not first) 156 private static final int XDECL = 0b10; // OK in declaration (not first) 157 private static final int XSTMT = 0b100; // OK in statement framework (not first) 158 private static final int XEXPR1o = 0b1000; // OK first in expression 159 private static final int XDECL1o = 0b10000; // OK first in declaration 160 private static final int XSTMT1o = 0b100000; // OK first or only in statement framework 161 private static final int XEXPR1 = XEXPR1o | XEXPR; // OK in expression (anywhere) 162 private static final int XDECL1 = XDECL1o | XDECL; // OK in declaration (anywhere) 163 private static final int XSTMT1 = XSTMT1o | XSTMT; // OK in statement framework (anywhere) 164 private static final int XANY1 = XEXPR1o | XDECL1o | XSTMT1o; // Mask: first in statement, declaration, or expression 165 private static final int XTERM = 0b100000000; // Can terminate (last before EOF) 166 private static final int XSTART = 0b1000000000; // Boundary, must be XTERM before 167 private static final int XERRO = 0b10000000000; // Is an error 168 169 /** 170 * An extension of the compiler's TokenKind which adds our combined/processed 171 * kinds. Also associates each TK with a union of acceptable kinds of code 172 * position it can occupy. For example: IDENTIFER is XEXPR1|XDECL1|XTERM, 173 * meaning it can occur in expressions or declarations (but not in the 174 * framework of a statement and that can be the final (terminating) token 175 * in a snippet. 176 * <P> 177 * There must be a TK defined for each compiler TokenKind, an exception 178 * will 179 * be thrown if a TokenKind is defined and a corresponding TK is not. Add a 180 * new TK in the appropriate category. If it is like an existing category 181 * (e.g. a new modifier or type this may be all that is needed. If it 182 * is bracketing or modifies the acceptable positions of other tokens, 183 * please closely examine the needed changes to this scanner. 184 */ 185 static enum TK { 186 187 // Special 188 EOF(TokenKind.EOF, 0), // 189 ERROR(TokenKind.ERROR, XERRO), // 190 IDENTIFIER(TokenKind.IDENTIFIER, XEXPR1|XDECL1|XTERM), // 191 UNDERSCORE(TokenKind.UNDERSCORE, XERRO), // _ 192 CLASS(TokenKind.CLASS, XEXPR|XDECL1|XTERM), // class decl and .class 193 MONKEYS_AT(TokenKind.MONKEYS_AT, XEXPR|XDECL1), // @ 194 IMPORT(TokenKind.IMPORT, XDECL1|XSTART), // import -- consider declaration 195 SEMI(TokenKind.SEMI, XSTMT1|XTERM|XSTART), // ; 196 197 // Shouldn't see -- error 198 PACKAGE(TokenKind.PACKAGE, XERRO), // package 199 CONST(TokenKind.CONST, XERRO), // reserved keyword -- const 200 GOTO(TokenKind.GOTO, XERRO), // reserved keyword -- goto 201 CUSTOM(TokenKind.CUSTOM, XERRO), // No uses 202 203 // Declarations 204 ENUM(TokenKind.ENUM, XDECL1), // enum 205 IMPLEMENTS(TokenKind.IMPLEMENTS, XDECL), // implements 206 INTERFACE(TokenKind.INTERFACE, XDECL1), // interface 207 THROWS(TokenKind.THROWS, XDECL), // throws 208 209 // Primarive type names 210 BOOLEAN(TokenKind.BOOLEAN, XEXPR|XDECL1), // boolean 211 BYTE(TokenKind.BYTE, XEXPR|XDECL1), // byte 212 CHAR(TokenKind.CHAR, XEXPR|XDECL1), // char 213 DOUBLE(TokenKind.DOUBLE, XEXPR|XDECL1), // double 214 FLOAT(TokenKind.FLOAT, XEXPR|XDECL1), // float 215 INT(TokenKind.INT, XEXPR|XDECL1), // int 216 LONG(TokenKind.LONG, XEXPR|XDECL1), // long 217 SHORT(TokenKind.SHORT, XEXPR|XDECL1), // short 218 VOID(TokenKind.VOID, XEXPR|XDECL1), // void 219 220 // Modifiers keywords 221 ABSTRACT(TokenKind.ABSTRACT, XDECL1), // abstract 222 FINAL(TokenKind.FINAL, XDECL1), // final 223 NATIVE(TokenKind.NATIVE, XDECL1), // native 224 STATIC(TokenKind.STATIC, XDECL1), // static 225 STRICTFP(TokenKind.STRICTFP, XDECL1), // strictfp 226 PRIVATE(TokenKind.PRIVATE, XDECL1), // private 227 PROTECTED(TokenKind.PROTECTED, XDECL1), // protected 228 PUBLIC(TokenKind.PUBLIC, XDECL1), // public 229 TRANSIENT(TokenKind.TRANSIENT, XDECL1), // transient 230 VOLATILE(TokenKind.VOLATILE, XDECL1), // volatile 231 232 // Declarations and type parameters (thus expressions) 233 EXTENDS(TokenKind.EXTENDS, XEXPR|XDECL), // extends 234 COMMA(TokenKind.COMMA, XEXPR|XDECL|XSTART), // , 235 AMP(TokenKind.AMP, XEXPR|XDECL), // & 236 GT(TokenKind.GT, XEXPR|XDECL), // > 237 LT(TokenKind.LT, XEXPR|XDECL1), // < 238 LTLT(TokenKind.LTLT, XEXPR|XDECL1), // << 239 GTGT(TokenKind.GTGT, XEXPR|XDECL), // >> 240 GTGTGT(TokenKind.GTGTGT, XEXPR|XDECL), // >>> 241 QUES(TokenKind.QUES, XEXPR|XDECL), // ? 242 DOT(TokenKind.DOT, XEXPR|XDECL), // . 243 STAR(TokenKind.STAR, XEXPR|XDECL|XTERM), // * -- import foo.* //TODO handle these case separately, XTERM 244 245 // Statement keywords 246 ASSERT(TokenKind.ASSERT, XSTMT1|XSTART), // assert 247 BREAK(TokenKind.BREAK, XSTMT1|XTERM|XSTART), // break 248 CATCH(TokenKind.CATCH, XSTMT1|XSTART), // catch 249 CONTINUE(TokenKind.CONTINUE, XSTMT1|XTERM|XSTART), // continue 250 DO(TokenKind.DO, XSTMT1|XSTART), // do 251 ELSE(TokenKind.ELSE, XSTMT1|XTERM|XSTART), // else 252 FINALLY(TokenKind.FINALLY, XSTMT1|XSTART), // finally 253 FOR(TokenKind.FOR, XSTMT1|XSTART), // for 254 IF(TokenKind.IF, XSTMT1|XSTART), // if 255 RETURN(TokenKind.RETURN, XSTMT1|XTERM|XSTART), // return 256 SWITCH(TokenKind.SWITCH, XSTMT1|XSTART), // switch 257 SYNCHRONIZED(TokenKind.SYNCHRONIZED, XSTMT1|XDECL), // synchronized 258 THROW(TokenKind.THROW, XSTMT1|XSTART), // throw 259 TRY(TokenKind.TRY, XSTMT1|XSTART), // try 260 WHILE(TokenKind.WHILE, XSTMT1|XSTART), // while 261 262 // Statement keywords that we shouldn't see -- inside braces 263 CASE(TokenKind.CASE, XSTMT|XSTART), // case 264 DEFAULT(TokenKind.DEFAULT, XSTMT|XSTART), // default method, default case -- neither we should see 265 266 // Expressions (can terminate) 267 INTLITERAL(TokenKind.INTLITERAL, XEXPR1|XTERM), // 268 LONGLITERAL(TokenKind.LONGLITERAL, XEXPR1|XTERM), // 269 FLOATLITERAL(TokenKind.FLOATLITERAL, XEXPR1|XTERM), // 270 DOUBLELITERAL(TokenKind.DOUBLELITERAL, XEXPR1|XTERM), // 271 CHARLITERAL(TokenKind.CHARLITERAL, XEXPR1|XTERM), // 272 STRINGLITERAL(TokenKind.STRINGLITERAL, XEXPR1|XTERM), // 273 TRUE(TokenKind.TRUE, XEXPR1|XTERM), // true 274 FALSE(TokenKind.FALSE, XEXPR1|XTERM), // false 275 NULL(TokenKind.NULL, XEXPR1|XTERM), // null 276 THIS(TokenKind.THIS, XEXPR1|XTERM), // this -- shouldn't see 277 278 // Expressions maybe terminate //TODO handle these case separately 279 PLUSPLUS(TokenKind.PLUSPLUS, XEXPR1|XTERM), // ++ 280 SUBSUB(TokenKind.SUBSUB, XEXPR1|XTERM), // -- 281 282 // Expressions cannot terminate 283 INSTANCEOF(TokenKind.INSTANCEOF, XEXPR), // instanceof 284 NEW(TokenKind.NEW, XEXPR1), // new 285 SUPER(TokenKind.SUPER, XEXPR1|XDECL), // super -- shouldn't see as rec. But in type parameters 286 ARROW(TokenKind.ARROW, XEXPR), // -> 287 COLCOL(TokenKind.COLCOL, XEXPR), // :: 288 LPAREN(TokenKind.LPAREN, XEXPR), // ( 289 RPAREN(TokenKind.RPAREN, XEXPR), // ) 290 LBRACE(TokenKind.LBRACE, XEXPR), // { 291 RBRACE(TokenKind.RBRACE, XEXPR), // } 292 LBRACKET(TokenKind.LBRACKET, XEXPR), // [ 293 RBRACKET(TokenKind.RBRACKET, XEXPR), // ] 294 ELLIPSIS(TokenKind.ELLIPSIS, XEXPR), // ... 295 EQ(TokenKind.EQ, XEXPR), // = 296 BANG(TokenKind.BANG, XEXPR1), // ! 297 TILDE(TokenKind.TILDE, XEXPR1), // ~ 298 COLON(TokenKind.COLON, XEXPR|XTERM), // : 299 EQEQ(TokenKind.EQEQ, XEXPR), // == 300 LTEQ(TokenKind.LTEQ, XEXPR), // <= 301 GTEQ(TokenKind.GTEQ, XEXPR), // >= 302 BANGEQ(TokenKind.BANGEQ, XEXPR), // != 303 AMPAMP(TokenKind.AMPAMP, XEXPR), // && 304 BARBAR(TokenKind.BARBAR, XEXPR), // || 305 PLUS(TokenKind.PLUS, XEXPR1), // + 306 SUB(TokenKind.SUB, XEXPR1), // - 307 SLASH(TokenKind.SLASH, XEXPR), // / 308 BAR(TokenKind.BAR, XEXPR), // | 309 CARET(TokenKind.CARET, XEXPR), // ^ 310 PERCENT(TokenKind.PERCENT, XEXPR), // % 311 PLUSEQ(TokenKind.PLUSEQ, XEXPR), // += 312 SUBEQ(TokenKind.SUBEQ, XEXPR), // -= 313 STAREQ(TokenKind.STAREQ, XEXPR), // *= 314 SLASHEQ(TokenKind.SLASHEQ, XEXPR), // /= 315 AMPEQ(TokenKind.AMPEQ, XEXPR), // &= 316 BAREQ(TokenKind.BAREQ, XEXPR), // |= 317 CARETEQ(TokenKind.CARETEQ, XEXPR), // ^= 318 PERCENTEQ(TokenKind.PERCENTEQ, XEXPR), // %= 319 LTLTEQ(TokenKind.LTLTEQ, XEXPR), // <<= 320 GTGTEQ(TokenKind.GTGTEQ, XEXPR), // >>= 321 GTGTGTEQ(TokenKind.GTGTGTEQ, XEXPR), // >>>= 322 323 // combined/processed kinds 324 UNMATCHED(XERRO), 325 PARENS(XEXPR1|XDECL|XSTMT|XTERM), 326 BRACKETS(XEXPR|XDECL|XTERM), 327 BRACES(XSTMT1|XEXPR|XTERM); 328 329 static final EnumMap<TokenKind,TK> tokenKindToTKMap = new EnumMap<>(TokenKind.class); 330 331 final TokenKind tokenKind; 332 final int belongs; 333 334 TK(int b) { 335 this.tokenKind = null; 336 this.belongs = b; 337 } 338 339 TK(TokenKind tokenKind, int b) { 340 this.tokenKind = tokenKind; 341 this.belongs = b; 342 } 343 344 private static TK tokenKindToTK(TokenKind kind) { 345 TK tk = tokenKindToTKMap.get(kind); 346 if (tk == null) { 347 System.err.printf("No corresponding %s for %s: %s\n", 348 TK.class.getCanonicalName(), 349 TokenKind.class.getCanonicalName(), 350 kind); 351 throw new InternalError("No corresponding TK for TokenKind: " + kind); 352 } 353 return tk; 354 } 355 356 boolean isOkToTerminate() { 357 return (belongs & XTERM) != 0; 358 } 359 360 boolean isExpression() { 361 return (belongs & XEXPR) != 0; 362 } 363 364 boolean isDeclaration() { 365 return (belongs & XDECL) != 0; 366 } 367 368 boolean isError() { 369 return (belongs & XERRO) != 0; 370 } 371 372 boolean isStart() { 373 return (belongs & XSTART) != 0; 374 } 375 376 /** 377 * After construction, check that all compiler TokenKind values have 378 * corresponding TK values. 379 */ 380 static { 381 for (TK tk : TK.values()) { 382 if (tk.tokenKind != null) { 383 tokenKindToTKMap.put(tk.tokenKind, tk); 384 } 385 } 386 for (TokenKind kind : TokenKind.values()) { 387 tokenKindToTK(kind); // assure they can be retrieved without error 388 } 389 } 390 } 391 392 /** 393 * A completeness scanner token. 394 */ 395 private static class CT { 396 397 /** The token kind */ 398 public final TK kind; 399 400 /** The end position of this token */ 401 public final int endPos; 402 403 /** The error message **/ 404 public final String message; 405 406 private CT(TK tk, Token tok, String msg) { 407 this.kind = tk; 408 this.endPos = tok.endPos; 409 this.message = msg; 410 //throw new InternalError(msg); /* for debugging */ 411 } 412 413 private CT(TK tk, Token tok) { 414 this.kind = tk; 415 this.endPos = tok.endPos; 416 this.message = null; 417 } 418 419 private CT(TK tk, int endPos) { 420 this.kind = tk; 421 this.endPos = endPos; 422 this.message = null; 423 } 424 } 425 426 /** 427 * Look for matching tokens (like parens) and other special cases, like "new" 428 */ 429 private static class Matched implements Iterator<CT> { 430 431 private final Scanner scanner; 432 private Token current; 433 private CT prevCT; 434 private CT currentCT; 435 private final Deque<Token> stack = new ArrayDeque<>(); 436 437 Matched(Scanner scanner) { 438 this.scanner = scanner; 439 advance(); 440 prevCT = currentCT = new CT(SEMI, 0); // So is valid for testing 441 } 442 443 @Override 444 public boolean hasNext() { 445 return currentCT.kind != EOF; 446 } 447 448 private Token advance() { 449 Token prev = current; 450 scanner.nextToken(); 451 current = scanner.token(); 452 return prev; 453 } 454 455 @Override 456 public CT next() { 457 prevCT = currentCT; 458 currentCT = nextCT(); 459 return currentCT; 460 } 461 462 private CT match(TK tk, TokenKind open) { 463 Token tok = advance(); 464 db("match desired-tk=%s, open=%s, seen-tok=%s", tk, open, tok.kind); 465 if (stack.isEmpty()) { 466 return new CT(ERROR, tok, "Encountered '" + tok + "' with no opening '" + open + "'"); 467 } 468 Token p = stack.pop(); 469 if (p.kind != open) { 470 return new CT(ERROR, tok, "No match for '" + p + "' instead encountered '" + tok + "'"); 471 } 472 return new CT(tk, tok); 473 } 474 475 private void db(String format, Object ... args) { 476// System.err.printf(format, args); 477// System.err.printf(" -- stack("); 478// if (stack.isEmpty()) { 479// 480// } else { 481// for (Token tok : stack) { 482// System.err.printf("%s ", tok.kind); 483// } 484// } 485// System.err.printf(") current=%s / currentCT=%s\n", current.kind, currentCT.kind); 486 } 487 488 /** 489 * @return the next scanner token 490 */ 491 private CT nextCT() { 492 // TODO Annotations? 493 TK prevTK = currentCT.kind; 494 while (true) { 495 db("nextCT"); 496 CT ct; 497 switch (current.kind) { 498 case EOF: 499 db("eof"); 500 if (stack.isEmpty()) { 501 ct = new CT(EOF, current); 502 } else { 503 TokenKind unmatched = stack.pop().kind; 504 stack.clear(); // So we will get EOF next time 505 ct = new CT(UNMATCHED, current, "Unmatched " + unmatched); 506 } 507 break; 508 case LPAREN: 509 case LBRACE: 510 case LBRACKET: 511 stack.push(advance()); 512 prevTK = SEMI; // new start 513 continue; 514 case RPAREN: 515 ct = match(PARENS, TokenKind.LPAREN); 516 break; 517 case RBRACE: 518 ct = match(BRACES, TokenKind.LBRACE); 519 break; 520 case RBRACKET: 521 ct = match(BRACKETS, TokenKind.LBRACKET); 522 break; 523 default: 524 ct = new CT(TK.tokenKindToTK(current.kind), advance()); 525 break; 526 } 527 if (ct.kind.isStart() && !prevTK.isOkToTerminate()) { 528 return new CT(ERROR, current, "No '" + prevTK + "' before '" + ct.kind + "'"); 529 } 530 if (stack.isEmpty() || ct.kind.isError()) { 531 return ct; 532 } 533 prevTK = ct.kind; 534 } 535 } 536 } 537 538 /** 539 * Fuzzy parser based on token kinds 540 */ 541 private static class Parser { 542 543 final Matched in; 544 CT token; 545 Completeness checkResult; 546 547 final JShell proc; 548 final String scannedInput; 549 550 551 552 Parser(Matched in, JShell proc, String scannedInput) { 553 this.in = in; 554 nextToken(); 555 556 this.proc = proc; 557 this.scannedInput = scannedInput; 558 } 559 560 final void nextToken() { 561 in.next(); 562 token = in.currentCT; 563 } 564 565 boolean shouldAbort(TK tk) { 566 if (token.kind == tk) { 567 nextToken(); 568 return false; 569 } 570 switch (token.kind) { 571 case EOF: 572 checkResult = ((tk == SEMI) && in.prevCT.kind.isOkToTerminate()) 573 ? Completeness.COMPLETE_WITH_SEMI 574 : Completeness.DEFINITELY_INCOMPLETE; 575 return true; 576 case UNMATCHED: 577 checkResult = Completeness.DEFINITELY_INCOMPLETE; 578 return true; 579 default: 580 checkResult = error(); 581 return true; 582 583 } 584 } 585 586 Completeness lastly(TK tk) { 587 if (shouldAbort(tk)) return checkResult; 588 return Completeness.COMPLETE; 589 } 590 591 Completeness optionalFinalSemi() { 592 if (!shouldAbort(SEMI)) return Completeness.COMPLETE; 593 if (checkResult == Completeness.COMPLETE_WITH_SEMI) return Completeness.COMPLETE; 594 return checkResult; 595 } 596 597 boolean shouldAbort(Completeness flags) { 598 checkResult = flags; 599 return flags != Completeness.COMPLETE; 600 } 601 602 public Completeness parseUnit() { 603 //System.err.printf("%s: belongs %o XANY1 %o\n", token.kind, token.kind.belongs, token.kind.belongs & XANY1); 604 switch (token.kind.belongs & XANY1) { 605 case XEXPR1o: 606 return parseExpressionOptionalSemi(); 607 case XSTMT1o: { 608 Completeness stat = parseSimpleStatement(); 609 return stat==null? error() : stat; 610 } 611 case XDECL1o: 612 return parseDeclaration(); 613 case XSTMT1o | XDECL1o: 614 case XEXPR1o | XDECL1o: 615 return disambiguateDeclarationVsExpression(); 616 case 0: 617 if ((token.kind.belongs & XERRO) != 0) { 618 return parseExpressionStatement(); // Let this gen the status 619 } 620 return error(); 621 default: 622 throw new InternalError("Case not covered " + token.kind.belongs + " in " + token.kind); 623 } 624 } 625 626 public Completeness parseDeclaration() { 627 boolean isImport = token.kind == IMPORT; 628 while (token.kind.isDeclaration()) { 629 nextToken(); 630 } 631 switch (token.kind) { 632 case EQ: 633 // Check for array initializer 634 nextToken(); 635 if (token.kind == BRACES) { 636 nextToken(); 637 return lastly(SEMI); 638 } 639 return parseExpressionStatement(); 640 case BRACES: 641 case SEMI: 642 nextToken(); 643 return Completeness.COMPLETE; 644 case UNMATCHED: 645 nextToken(); 646 return Completeness.DEFINITELY_INCOMPLETE; 647 case EOF: 648 switch (in.prevCT.kind) { 649 case BRACES: 650 case SEMI: 651 return Completeness.COMPLETE; 652 case IDENTIFIER: 653 case BRACKETS: 654 return Completeness.COMPLETE_WITH_SEMI; 655 case STAR: 656 if (isImport) { 657 return Completeness.COMPLETE_WITH_SEMI; 658 } else { 659 return Completeness.DEFINITELY_INCOMPLETE; 660 } 661 default: 662 return Completeness.DEFINITELY_INCOMPLETE; 663 } 664 default: 665 return error(); 666 } 667 } 668 669 public Completeness disambiguateDeclarationVsExpression() { 670 // String folding messes up position information. 671 ParseTask pt = proc.taskFactory.new ParseTask(scannedInput); 672 List<? extends Tree> units = pt.units(); 673 if (units.isEmpty()) { 674 return error(); 675 } 676 Tree unitTree = units.get(0); 677 switch (unitTree.getKind()) { 678 case EXPRESSION_STATEMENT: 679 return parseExpressionOptionalSemi(); 680 case LABELED_STATEMENT: 681 if (shouldAbort(IDENTIFIER)) return checkResult; 682 if (shouldAbort(COLON)) return checkResult; 683 return parseStatement(); 684 case VARIABLE: 685 case IMPORT: 686 case CLASS: 687 case ENUM: 688 case ANNOTATION_TYPE: 689 case INTERFACE: 690 case METHOD: 691 return parseDeclaration(); 692 default: 693 return error(); 694 } 695 } 696 697// public Status parseExpressionOrDeclaration() { 698// if (token.kind == IDENTIFIER) { 699// nextToken(); 700// switch (token.kind) { 701// case IDENTIFIER: 702// return parseDeclaration(); 703// } 704// } 705// while (token.kind.isExpressionOrDeclaration()) { 706// if (!token.kind.isExpression()) { 707// return parseDeclaration(); 708// } 709// if (!token.kind.isDeclaration()) { 710// // Expression not declaration 711// if (token.kind == EQ) { 712// // Check for array initializer 713// nextToken(); 714// if (token.kind == BRACES) { 715// nextToken(); 716// return lastly(SEMI); 717// } 718// } 719// return parseExpressionStatement(); 720// } 721// nextToken(); 722// } 723// switch (token.kind) { 724// case BRACES: 725// case SEMI: 726// nextToken(); 727// return Status.COMPLETE; 728// case UNMATCHED: 729// nextToken(); 730// return Status.DEFINITELY_INCOMPLETE; 731// case EOF: 732// if (in.prevCT.kind.isOkToTerminate()) { 733// return Status.COMPLETE_WITH_SEMI; 734// } else { 735// return Status.DEFINITELY_INCOMPLETE; 736// } 737// default: 738// return error(); 739// } 740// } 741 742 public Completeness parseExpressionStatement() { 743 if (shouldAbort(parseExpression())) return checkResult; 744 return lastly(SEMI); 745 } 746 747 public Completeness parseExpressionOptionalSemi() { 748 if (shouldAbort(parseExpression())) return checkResult; 749 return optionalFinalSemi(); 750 } 751 752 public Completeness parseExpression() { 753 while (token.kind.isExpression()) 754 nextToken(); 755 return Completeness.COMPLETE; 756 } 757 758 public Completeness parseStatement() { 759 Completeness stat = parseSimpleStatement(); 760 if (stat == null) { 761 return parseExpressionStatement(); 762 } 763 return stat; 764 } 765 766 /** 767 * Statement = Block | IF ParExpression Statement [ELSE Statement] | FOR 768 * "(" ForInitOpt ";" [Expression] ";" ForUpdateOpt ")" Statement | FOR 769 * "(" FormalParameter : Expression ")" Statement | WHILE ParExpression 770 * Statement | DO Statement WHILE ParExpression ";" | TRY Block ( 771 * Catches | [Catches] FinallyPart ) | TRY "(" ResourceSpecification 772 * ";"opt ")" Block [Catches] [FinallyPart] | SWITCH ParExpression "{" 773 * SwitchBlockStatementGroups "}" | SYNCHRONIZED ParExpression Block | 774 * RETURN [Expression] ";" | THROW Expression ";" | BREAK [Ident] ";" | 775 * CONTINUE [Ident] ";" | ASSERT Expression [ ":" Expression ] ";" | ";" 776 */ 777 public Completeness parseSimpleStatement() { 778 switch (token.kind) { 779 case BRACES: 780 return lastly(BRACES); 781 case IF: { 782 nextToken(); 783 if (shouldAbort(PARENS)) return checkResult; 784 Completeness thenpart = parseStatement(); 785 if (shouldAbort(thenpart)) return thenpart; 786 if (token.kind == ELSE) { 787 nextToken(); 788 return parseStatement(); 789 } 790 return thenpart; 791 792 } 793 case FOR: { 794 nextToken(); 795 if (shouldAbort(PARENS)) return checkResult; 796 if (shouldAbort(parseStatement())) return checkResult; 797 return Completeness.COMPLETE; 798 } 799 case WHILE: { 800 nextToken(); 801 if (shouldAbort(PARENS)) return error(); 802 return parseStatement(); 803 } 804 case DO: { 805 nextToken(); 806 switch (parseStatement()) { 807 case DEFINITELY_INCOMPLETE: 808 case CONSIDERED_INCOMPLETE: 809 case COMPLETE_WITH_SEMI: 810 return Completeness.DEFINITELY_INCOMPLETE; 811 case UNKNOWN: 812 return error(); 813 case COMPLETE: 814 break; 815 } 816 if (shouldAbort(WHILE)) return checkResult; 817 if (shouldAbort(PARENS)) return checkResult; 818 return lastly(SEMI); 819 } 820 case TRY: { 821 boolean hasResources = false; 822 nextToken(); 823 if (token.kind == PARENS) { 824 nextToken(); 825 hasResources = true; 826 } 827 if (shouldAbort(BRACES)) return checkResult; 828 if (token.kind == CATCH || token.kind == FINALLY) { 829 while (token.kind == CATCH) { 830 if (shouldAbort(CATCH)) return checkResult; 831 if (shouldAbort(PARENS)) return checkResult; 832 if (shouldAbort(BRACES)) return checkResult; 833 } 834 if (token.kind == FINALLY) { 835 if (shouldAbort(FINALLY)) return checkResult; 836 if (shouldAbort(BRACES)) return checkResult; 837 } 838 } else if (!hasResources) { 839 if (token.kind == EOF) { 840 return Completeness.DEFINITELY_INCOMPLETE; 841 } else { 842 return error(); 843 } 844 } 845 return Completeness.COMPLETE; 846 } 847 case SWITCH: { 848 nextToken(); 849 if (shouldAbort(PARENS)) return checkResult; 850 return lastly(BRACES); 851 } 852 case SYNCHRONIZED: { 853 nextToken(); 854 if (shouldAbort(PARENS)) return checkResult; 855 return lastly(BRACES); 856 } 857 case THROW: { 858 nextToken(); 859 if (shouldAbort(parseExpression())) return checkResult; 860 return lastly(SEMI); 861 } 862 case SEMI: 863 return lastly(SEMI); 864 case ASSERT: 865 nextToken(); 866 // Crude expression parsing just happily eats the optional colon 867 return parseExpressionStatement(); 868 case RETURN: 869 case BREAK: 870 case CONTINUE: 871 nextToken(); 872 return parseExpressionStatement(); 873 // What are these doing here? 874 case ELSE: 875 case FINALLY: 876 case CATCH: 877 return error(); 878 case EOF: 879 return Completeness.CONSIDERED_INCOMPLETE; 880 default: 881 return null; 882 } 883 } 884 } 885} 886