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