--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java Mon Oct 19 19:15:16 2015 +0200
@@ -0,0 +1,885 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.jshell;
+
+import com.sun.tools.javac.code.Source;
+import com.sun.tools.javac.parser.Scanner;
+import com.sun.tools.javac.parser.ScannerFactory;
+import com.sun.tools.javac.parser.Tokens.Token;
+import com.sun.tools.javac.parser.Tokens.TokenKind;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.JCDiagnostic;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
+import com.sun.tools.javac.util.Log;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.EnumMap;
+import java.util.Iterator;
+import jdk.jshell.SourceCodeAnalysis.Completeness;
+import com.sun.source.tree.Tree;
+import static jdk.jshell.CompletenessAnalyzer.TK.*;
+import jdk.jshell.TaskFactory.ParseTask;
+import java.util.List;
+
+/**
+ * Low level scanner to determine completeness of input.
+ * @author Robert Field
+ */
+class CompletenessAnalyzer {
+
+ private final ScannerFactory scannerFactory;
+ private final JShell proc;
+
+ private static Completeness error() {
+ return Completeness.UNKNOWN; // For breakpointing
+ }
+
+ static class CaInfo {
+
+ CaInfo(Completeness status, int unitEndPos) {
+ this.status = status;
+ this.unitEndPos = unitEndPos;
+ }
+ final int unitEndPos;
+ final Completeness status;
+ }
+
+ CompletenessAnalyzer(JShell proc) {
+ this.proc = proc;
+ Context context = new Context();
+ Log log = CaLog.createLog(context);
+ context.put(Log.class, log);
+ context.put(Source.class, Source.JDK1_9);
+ scannerFactory = ScannerFactory.instance(context);
+ }
+
+ CaInfo scan(String s) {
+ try {
+ Scanner scanner = scannerFactory.newScanner(s, false);
+ Matched in = new Matched(scanner);
+ Parser parser = new Parser(in, proc, s);
+ Completeness stat = parser.parseUnit();
+ int endPos = stat == Completeness.UNKNOWN
+ ? s.length()
+ : in.prevCT.endPos;
+ return new CaInfo(stat, endPos);
+ } catch (SyntaxException ex) {
+ return new CaInfo(error(), s.length());
+ }
+ }
+
+ @SuppressWarnings("serial") // serialVersionUID intentionally omitted
+ private static class SyntaxException extends RuntimeException {
+ }
+
+ private static void die() {
+ throw new SyntaxException();
+ }
+
+ /**
+ * Subclass of Log used by compiler API to die on error and ignore
+ * other messages
+ */
+ private static class CaLog extends Log {
+
+ private static CaLog createLog(Context context) {
+ PrintWriter pw = new PrintWriter(new StringWriter());
+ CaLog log = new CaLog(context, pw, pw, pw);
+ context.put(outKey, pw);
+ context.put(logKey, log);
+ return log;
+ }
+
+ private CaLog(Context context, PrintWriter errWriter, PrintWriter warnWriter, PrintWriter noticeWriter) {
+ super(context, errWriter, warnWriter, noticeWriter);
+ }
+
+ @Override
+ public void error(String key, Object... args) {
+ die();
+ }
+
+ @Override
+ public void error(DiagnosticPosition pos, String key, Object... args) {
+ die();
+ }
+
+ @Override
+ public void error(DiagnosticFlag flag, DiagnosticPosition pos, String key, Object... args) {
+ die();
+ }
+
+ @Override
+ public void error(int pos, String key, Object... args) {
+ die();
+ }
+
+ @Override
+ public void error(DiagnosticFlag flag, int pos, String key, Object... args) {
+ die();
+ }
+
+ @Override
+ public void report(JCDiagnostic diagnostic) {
+ // Ignore
+ }
+ }
+
+ // Location position kinds -- a token is ...
+ private static final int XEXPR = 0b1; // OK in expression (not first)
+ private static final int XDECL = 0b10; // OK in declaration (not first)
+ private static final int XSTMT = 0b100; // OK in statement framework (not first)
+ private static final int XEXPR1o = 0b1000; // OK first in expression
+ private static final int XDECL1o = 0b10000; // OK first in declaration
+ private static final int XSTMT1o = 0b100000; // OK first or only in statement framework
+ private static final int XEXPR1 = XEXPR1o | XEXPR; // OK in expression (anywhere)
+ private static final int XDECL1 = XDECL1o | XDECL; // OK in declaration (anywhere)
+ private static final int XSTMT1 = XSTMT1o | XSTMT; // OK in statement framework (anywhere)
+ private static final int XANY1 = XEXPR1o | XDECL1o | XSTMT1o; // Mask: first in statement, declaration, or expression
+ private static final int XTERM = 0b100000000; // Can terminate (last before EOF)
+ private static final int XSTART = 0b1000000000; // Boundary, must be XTERM before
+ private static final int XERRO = 0b10000000000; // Is an error
+
+ /**
+ * An extension of the compiler's TokenKind which adds our combined/processed
+ * kinds. Also associates each TK with a union of acceptable kinds of code
+ * position it can occupy. For example: IDENTIFER is XEXPR1|XDECL1|XTERM,
+ * meaning it can occur in expressions or declarations (but not in the
+ * framework of a statement and that can be the final (terminating) token
+ * in a snippet.
+ * <P>
+ * There must be a TK defined for each compiler TokenKind, an exception
+ * will
+ * be thrown if a TokenKind is defined and a corresponding TK is not. Add a
+ * new TK in the appropriate category. If it is like an existing category
+ * (e.g. a new modifier or type this may be all that is needed. If it
+ * is bracketing or modifies the acceptable positions of other tokens,
+ * please closely examine the needed changes to this scanner.
+ */
+ static enum TK {
+
+ // Special
+ EOF(TokenKind.EOF, 0), //
+ ERROR(TokenKind.ERROR, XERRO), //
+ IDENTIFIER(TokenKind.IDENTIFIER, XEXPR1|XDECL1|XTERM), //
+ UNDERSCORE(TokenKind.UNDERSCORE, XERRO), // _
+ CLASS(TokenKind.CLASS, XEXPR|XDECL1|XTERM), // class decl and .class
+ MONKEYS_AT(TokenKind.MONKEYS_AT, XEXPR|XDECL1), // @
+ IMPORT(TokenKind.IMPORT, XDECL1|XSTART), // import -- consider declaration
+ SEMI(TokenKind.SEMI, XSTMT1|XTERM|XSTART), // ;
+
+ // Shouldn't see -- error
+ PACKAGE(TokenKind.PACKAGE, XERRO), // package
+ CONST(TokenKind.CONST, XERRO), // reserved keyword -- const
+ GOTO(TokenKind.GOTO, XERRO), // reserved keyword -- goto
+ CUSTOM(TokenKind.CUSTOM, XERRO), // No uses
+
+ // Declarations
+ ENUM(TokenKind.ENUM, XDECL1), // enum
+ IMPLEMENTS(TokenKind.IMPLEMENTS, XDECL), // implements
+ INTERFACE(TokenKind.INTERFACE, XDECL1), // interface
+ THROWS(TokenKind.THROWS, XDECL), // throws
+
+ // Primarive type names
+ BOOLEAN(TokenKind.BOOLEAN, XEXPR|XDECL1), // boolean
+ BYTE(TokenKind.BYTE, XEXPR|XDECL1), // byte
+ CHAR(TokenKind.CHAR, XEXPR|XDECL1), // char
+ DOUBLE(TokenKind.DOUBLE, XEXPR|XDECL1), // double
+ FLOAT(TokenKind.FLOAT, XEXPR|XDECL1), // float
+ INT(TokenKind.INT, XEXPR|XDECL1), // int
+ LONG(TokenKind.LONG, XEXPR|XDECL1), // long
+ SHORT(TokenKind.SHORT, XEXPR|XDECL1), // short
+ VOID(TokenKind.VOID, XEXPR|XDECL1), // void
+
+ // Modifiers keywords
+ ABSTRACT(TokenKind.ABSTRACT, XDECL1), // abstract
+ FINAL(TokenKind.FINAL, XDECL1), // final
+ NATIVE(TokenKind.NATIVE, XDECL1), // native
+ STATIC(TokenKind.STATIC, XDECL1), // static
+ STRICTFP(TokenKind.STRICTFP, XDECL1), // strictfp
+ PRIVATE(TokenKind.PRIVATE, XDECL1), // private
+ PROTECTED(TokenKind.PROTECTED, XDECL1), // protected
+ PUBLIC(TokenKind.PUBLIC, XDECL1), // public
+ TRANSIENT(TokenKind.TRANSIENT, XDECL1), // transient
+ VOLATILE(TokenKind.VOLATILE, XDECL1), // volatile
+
+ // Declarations and type parameters (thus expressions)
+ EXTENDS(TokenKind.EXTENDS, XEXPR|XDECL), // extends
+ COMMA(TokenKind.COMMA, XEXPR|XDECL|XSTART), // ,
+ AMP(TokenKind.AMP, XEXPR|XDECL), // &
+ GT(TokenKind.GT, XEXPR|XDECL), // >
+ LT(TokenKind.LT, XEXPR|XDECL1), // <
+ LTLT(TokenKind.LTLT, XEXPR|XDECL1), // <<
+ GTGT(TokenKind.GTGT, XEXPR|XDECL), // >>
+ GTGTGT(TokenKind.GTGTGT, XEXPR|XDECL), // >>>
+ QUES(TokenKind.QUES, XEXPR|XDECL), // ?
+ DOT(TokenKind.DOT, XEXPR|XDECL), // .
+ STAR(TokenKind.STAR, XEXPR|XDECL|XTERM), // * -- import foo.* //TODO handle these case separately, XTERM
+
+ // Statement keywords
+ ASSERT(TokenKind.ASSERT, XSTMT1|XSTART), // assert
+ BREAK(TokenKind.BREAK, XSTMT1|XTERM|XSTART), // break
+ CATCH(TokenKind.CATCH, XSTMT1|XSTART), // catch
+ CONTINUE(TokenKind.CONTINUE, XSTMT1|XTERM|XSTART), // continue
+ DO(TokenKind.DO, XSTMT1|XSTART), // do
+ ELSE(TokenKind.ELSE, XSTMT1|XTERM|XSTART), // else
+ FINALLY(TokenKind.FINALLY, XSTMT1|XSTART), // finally
+ FOR(TokenKind.FOR, XSTMT1|XSTART), // for
+ IF(TokenKind.IF, XSTMT1|XSTART), // if
+ RETURN(TokenKind.RETURN, XSTMT1|XTERM|XSTART), // return
+ SWITCH(TokenKind.SWITCH, XSTMT1|XSTART), // switch
+ SYNCHRONIZED(TokenKind.SYNCHRONIZED, XSTMT1|XDECL), // synchronized
+ THROW(TokenKind.THROW, XSTMT1|XSTART), // throw
+ TRY(TokenKind.TRY, XSTMT1|XSTART), // try
+ WHILE(TokenKind.WHILE, XSTMT1|XSTART), // while
+
+ // Statement keywords that we shouldn't see -- inside braces
+ CASE(TokenKind.CASE, XSTMT|XSTART), // case
+ DEFAULT(TokenKind.DEFAULT, XSTMT|XSTART), // default method, default case -- neither we should see
+
+ // Expressions (can terminate)
+ INTLITERAL(TokenKind.INTLITERAL, XEXPR1|XTERM), //
+ LONGLITERAL(TokenKind.LONGLITERAL, XEXPR1|XTERM), //
+ FLOATLITERAL(TokenKind.FLOATLITERAL, XEXPR1|XTERM), //
+ DOUBLELITERAL(TokenKind.DOUBLELITERAL, XEXPR1|XTERM), //
+ CHARLITERAL(TokenKind.CHARLITERAL, XEXPR1|XTERM), //
+ STRINGLITERAL(TokenKind.STRINGLITERAL, XEXPR1|XTERM), //
+ TRUE(TokenKind.TRUE, XEXPR1|XTERM), // true
+ FALSE(TokenKind.FALSE, XEXPR1|XTERM), // false
+ NULL(TokenKind.NULL, XEXPR1|XTERM), // null
+ THIS(TokenKind.THIS, XEXPR1|XTERM), // this -- shouldn't see
+
+ // Expressions maybe terminate //TODO handle these case separately
+ PLUSPLUS(TokenKind.PLUSPLUS, XEXPR1|XTERM), // ++
+ SUBSUB(TokenKind.SUBSUB, XEXPR1|XTERM), // --
+
+ // Expressions cannot terminate
+ INSTANCEOF(TokenKind.INSTANCEOF, XEXPR), // instanceof
+ NEW(TokenKind.NEW, XEXPR1), // new
+ SUPER(TokenKind.SUPER, XEXPR1|XDECL), // super -- shouldn't see as rec. But in type parameters
+ ARROW(TokenKind.ARROW, XEXPR), // ->
+ COLCOL(TokenKind.COLCOL, XEXPR), // ::
+ LPAREN(TokenKind.LPAREN, XEXPR), // (
+ RPAREN(TokenKind.RPAREN, XEXPR), // )
+ LBRACE(TokenKind.LBRACE, XEXPR), // {
+ RBRACE(TokenKind.RBRACE, XEXPR), // }
+ LBRACKET(TokenKind.LBRACKET, XEXPR), // [
+ RBRACKET(TokenKind.RBRACKET, XEXPR), // ]
+ ELLIPSIS(TokenKind.ELLIPSIS, XEXPR), // ...
+ EQ(TokenKind.EQ, XEXPR), // =
+ BANG(TokenKind.BANG, XEXPR1), // !
+ TILDE(TokenKind.TILDE, XEXPR1), // ~
+ COLON(TokenKind.COLON, XEXPR|XTERM), // :
+ EQEQ(TokenKind.EQEQ, XEXPR), // ==
+ LTEQ(TokenKind.LTEQ, XEXPR), // <=
+ GTEQ(TokenKind.GTEQ, XEXPR), // >=
+ BANGEQ(TokenKind.BANGEQ, XEXPR), // !=
+ AMPAMP(TokenKind.AMPAMP, XEXPR), // &&
+ BARBAR(TokenKind.BARBAR, XEXPR), // ||
+ PLUS(TokenKind.PLUS, XEXPR1), // +
+ SUB(TokenKind.SUB, XEXPR1), // -
+ SLASH(TokenKind.SLASH, XEXPR), // /
+ BAR(TokenKind.BAR, XEXPR), // |
+ CARET(TokenKind.CARET, XEXPR), // ^
+ PERCENT(TokenKind.PERCENT, XEXPR), // %
+ PLUSEQ(TokenKind.PLUSEQ, XEXPR), // +=
+ SUBEQ(TokenKind.SUBEQ, XEXPR), // -=
+ STAREQ(TokenKind.STAREQ, XEXPR), // *=
+ SLASHEQ(TokenKind.SLASHEQ, XEXPR), // /=
+ AMPEQ(TokenKind.AMPEQ, XEXPR), // &=
+ BAREQ(TokenKind.BAREQ, XEXPR), // |=
+ CARETEQ(TokenKind.CARETEQ, XEXPR), // ^=
+ PERCENTEQ(TokenKind.PERCENTEQ, XEXPR), // %=
+ LTLTEQ(TokenKind.LTLTEQ, XEXPR), // <<=
+ GTGTEQ(TokenKind.GTGTEQ, XEXPR), // >>=
+ GTGTGTEQ(TokenKind.GTGTGTEQ, XEXPR), // >>>=
+
+ // combined/processed kinds
+ UNMATCHED(XERRO),
+ PARENS(XEXPR1|XDECL|XSTMT|XTERM),
+ BRACKETS(XEXPR|XDECL|XTERM),
+ BRACES(XSTMT1|XEXPR|XTERM);
+
+ static final EnumMap<TokenKind,TK> tokenKindToTKMap = new EnumMap<>(TokenKind.class);
+
+ final TokenKind tokenKind;
+ final int belongs;
+
+ TK(int b) {
+ this.tokenKind = null;
+ this.belongs = b;
+ }
+
+ TK(TokenKind tokenKind, int b) {
+ this.tokenKind = tokenKind;
+ this.belongs = b;
+ }
+
+ private static TK tokenKindToTK(TokenKind kind) {
+ TK tk = tokenKindToTKMap.get(kind);
+ if (tk == null) {
+ System.err.printf("No corresponding %s for %s: %s\n",
+ TK.class.getCanonicalName(),
+ TokenKind.class.getCanonicalName(),
+ kind);
+ throw new InternalError("No corresponding TK for TokenKind: " + kind);
+ }
+ return tk;
+ }
+
+ boolean isOkToTerminate() {
+ return (belongs & XTERM) != 0;
+ }
+
+ boolean isExpression() {
+ return (belongs & XEXPR) != 0;
+ }
+
+ boolean isDeclaration() {
+ return (belongs & XDECL) != 0;
+ }
+
+ boolean isError() {
+ return (belongs & XERRO) != 0;
+ }
+
+ boolean isStart() {
+ return (belongs & XSTART) != 0;
+ }
+
+ /**
+ * After construction, check that all compiler TokenKind values have
+ * corresponding TK values.
+ */
+ static {
+ for (TK tk : TK.values()) {
+ if (tk.tokenKind != null) {
+ tokenKindToTKMap.put(tk.tokenKind, tk);
+ }
+ }
+ for (TokenKind kind : TokenKind.values()) {
+ tokenKindToTK(kind); // assure they can be retrieved without error
+ }
+ }
+ }
+
+ /**
+ * A completeness scanner token.
+ */
+ private static class CT {
+
+ /** The token kind */
+ public final TK kind;
+
+ /** The end position of this token */
+ public final int endPos;
+
+ /** The error message **/
+ public final String message;
+
+ private CT(TK tk, Token tok, String msg) {
+ this.kind = tk;
+ this.endPos = tok.endPos;
+ this.message = msg;
+ //throw new InternalError(msg); /* for debugging */
+ }
+
+ private CT(TK tk, Token tok) {
+ this.kind = tk;
+ this.endPos = tok.endPos;
+ this.message = null;
+ }
+
+ private CT(TK tk, int endPos) {
+ this.kind = tk;
+ this.endPos = endPos;
+ this.message = null;
+ }
+ }
+
+ /**
+ * Look for matching tokens (like parens) and other special cases, like "new"
+ */
+ private static class Matched implements Iterator<CT> {
+
+ private final Scanner scanner;
+ private Token current;
+ private CT prevCT;
+ private CT currentCT;
+ private final Deque<Token> stack = new ArrayDeque<>();
+
+ Matched(Scanner scanner) {
+ this.scanner = scanner;
+ advance();
+ prevCT = currentCT = new CT(SEMI, 0); // So is valid for testing
+ }
+
+ @Override
+ public boolean hasNext() {
+ return currentCT.kind != EOF;
+ }
+
+ private Token advance() {
+ Token prev = current;
+ scanner.nextToken();
+ current = scanner.token();
+ return prev;
+ }
+
+ @Override
+ public CT next() {
+ prevCT = currentCT;
+ currentCT = nextCT();
+ return currentCT;
+ }
+
+ private CT match(TK tk, TokenKind open) {
+ Token tok = advance();
+ db("match desired-tk=%s, open=%s, seen-tok=%s", tk, open, tok.kind);
+ if (stack.isEmpty()) {
+ return new CT(ERROR, tok, "Encountered '" + tok + "' with no opening '" + open + "'");
+ }
+ Token p = stack.pop();
+ if (p.kind != open) {
+ return new CT(ERROR, tok, "No match for '" + p + "' instead encountered '" + tok + "'");
+ }
+ return new CT(tk, tok);
+ }
+
+ private void db(String format, Object ... args) {
+// System.err.printf(format, args);
+// System.err.printf(" -- stack(");
+// if (stack.isEmpty()) {
+//
+// } else {
+// for (Token tok : stack) {
+// System.err.printf("%s ", tok.kind);
+// }
+// }
+// System.err.printf(") current=%s / currentCT=%s\n", current.kind, currentCT.kind);
+ }
+
+ /**
+ * @return the next scanner token
+ */
+ private CT nextCT() {
+ // TODO Annotations?
+ TK prevTK = currentCT.kind;
+ while (true) {
+ db("nextCT");
+ CT ct;
+ switch (current.kind) {
+ case EOF:
+ db("eof");
+ if (stack.isEmpty()) {
+ ct = new CT(EOF, current);
+ } else {
+ TokenKind unmatched = stack.pop().kind;
+ stack.clear(); // So we will get EOF next time
+ ct = new CT(UNMATCHED, current, "Unmatched " + unmatched);
+ }
+ break;
+ case LPAREN:
+ case LBRACE:
+ case LBRACKET:
+ stack.push(advance());
+ prevTK = SEMI; // new start
+ continue;
+ case RPAREN:
+ ct = match(PARENS, TokenKind.LPAREN);
+ break;
+ case RBRACE:
+ ct = match(BRACES, TokenKind.LBRACE);
+ break;
+ case RBRACKET:
+ ct = match(BRACKETS, TokenKind.LBRACKET);
+ break;
+ default:
+ ct = new CT(TK.tokenKindToTK(current.kind), advance());
+ break;
+ }
+ if (ct.kind.isStart() && !prevTK.isOkToTerminate()) {
+ return new CT(ERROR, current, "No '" + prevTK + "' before '" + ct.kind + "'");
+ }
+ if (stack.isEmpty() || ct.kind.isError()) {
+ return ct;
+ }
+ prevTK = ct.kind;
+ }
+ }
+ }
+
+ /**
+ * Fuzzy parser based on token kinds
+ */
+ private static class Parser {
+
+ final Matched in;
+ CT token;
+ Completeness checkResult;
+
+ final JShell proc;
+ final String scannedInput;
+
+
+
+ Parser(Matched in, JShell proc, String scannedInput) {
+ this.in = in;
+ nextToken();
+
+ this.proc = proc;
+ this.scannedInput = scannedInput;
+ }
+
+ final void nextToken() {
+ in.next();
+ token = in.currentCT;
+ }
+
+ boolean shouldAbort(TK tk) {
+ if (token.kind == tk) {
+ nextToken();
+ return false;
+ }
+ switch (token.kind) {
+ case EOF:
+ checkResult = ((tk == SEMI) && in.prevCT.kind.isOkToTerminate())
+ ? Completeness.COMPLETE_WITH_SEMI
+ : Completeness.DEFINITELY_INCOMPLETE;
+ return true;
+ case UNMATCHED:
+ checkResult = Completeness.DEFINITELY_INCOMPLETE;
+ return true;
+ default:
+ checkResult = error();
+ return true;
+
+ }
+ }
+
+ Completeness lastly(TK tk) {
+ if (shouldAbort(tk)) return checkResult;
+ return Completeness.COMPLETE;
+ }
+
+ Completeness optionalFinalSemi() {
+ if (!shouldAbort(SEMI)) return Completeness.COMPLETE;
+ if (checkResult == Completeness.COMPLETE_WITH_SEMI) return Completeness.COMPLETE;
+ return checkResult;
+ }
+
+ boolean shouldAbort(Completeness flags) {
+ checkResult = flags;
+ return flags != Completeness.COMPLETE;
+ }
+
+ public Completeness parseUnit() {
+ //System.err.printf("%s: belongs %o XANY1 %o\n", token.kind, token.kind.belongs, token.kind.belongs & XANY1);
+ switch (token.kind.belongs & XANY1) {
+ case XEXPR1o:
+ return parseExpressionOptionalSemi();
+ case XSTMT1o: {
+ Completeness stat = parseSimpleStatement();
+ return stat==null? error() : stat;
+ }
+ case XDECL1o:
+ return parseDeclaration();
+ case XSTMT1o | XDECL1o:
+ case XEXPR1o | XDECL1o:
+ return disambiguateDeclarationVsExpression();
+ case 0:
+ if ((token.kind.belongs & XERRO) != 0) {
+ return parseExpressionStatement(); // Let this gen the status
+ }
+ return error();
+ default:
+ throw new InternalError("Case not covered " + token.kind.belongs + " in " + token.kind);
+ }
+ }
+
+ public Completeness parseDeclaration() {
+ boolean isImport = token.kind == IMPORT;
+ while (token.kind.isDeclaration()) {
+ nextToken();
+ }
+ switch (token.kind) {
+ case EQ:
+ // Check for array initializer
+ nextToken();
+ if (token.kind == BRACES) {
+ nextToken();
+ return lastly(SEMI);
+ }
+ return parseExpressionStatement();
+ case BRACES:
+ case SEMI:
+ nextToken();
+ return Completeness.COMPLETE;
+ case UNMATCHED:
+ nextToken();
+ return Completeness.DEFINITELY_INCOMPLETE;
+ case EOF:
+ switch (in.prevCT.kind) {
+ case BRACES:
+ case SEMI:
+ return Completeness.COMPLETE;
+ case IDENTIFIER:
+ case BRACKETS:
+ return Completeness.COMPLETE_WITH_SEMI;
+ case STAR:
+ if (isImport) {
+ return Completeness.COMPLETE_WITH_SEMI;
+ } else {
+ return Completeness.DEFINITELY_INCOMPLETE;
+ }
+ default:
+ return Completeness.DEFINITELY_INCOMPLETE;
+ }
+ default:
+ return error();
+ }
+ }
+
+ public Completeness disambiguateDeclarationVsExpression() {
+ // String folding messes up position information.
+ ParseTask pt = proc.taskFactory.new ParseTask(scannedInput);
+ List<? extends Tree> units = pt.units();
+ if (units.isEmpty()) {
+ return error();
+ }
+ Tree unitTree = units.get(0);
+ switch (unitTree.getKind()) {
+ case EXPRESSION_STATEMENT:
+ return parseExpressionOptionalSemi();
+ case LABELED_STATEMENT:
+ if (shouldAbort(IDENTIFIER)) return checkResult;
+ if (shouldAbort(COLON)) return checkResult;
+ return parseStatement();
+ case VARIABLE:
+ case IMPORT:
+ case CLASS:
+ case ENUM:
+ case ANNOTATION_TYPE:
+ case INTERFACE:
+ case METHOD:
+ return parseDeclaration();
+ default:
+ return error();
+ }
+ }
+
+// public Status parseExpressionOrDeclaration() {
+// if (token.kind == IDENTIFIER) {
+// nextToken();
+// switch (token.kind) {
+// case IDENTIFIER:
+// return parseDeclaration();
+// }
+// }
+// while (token.kind.isExpressionOrDeclaration()) {
+// if (!token.kind.isExpression()) {
+// return parseDeclaration();
+// }
+// if (!token.kind.isDeclaration()) {
+// // Expression not declaration
+// if (token.kind == EQ) {
+// // Check for array initializer
+// nextToken();
+// if (token.kind == BRACES) {
+// nextToken();
+// return lastly(SEMI);
+// }
+// }
+// return parseExpressionStatement();
+// }
+// nextToken();
+// }
+// switch (token.kind) {
+// case BRACES:
+// case SEMI:
+// nextToken();
+// return Status.COMPLETE;
+// case UNMATCHED:
+// nextToken();
+// return Status.DEFINITELY_INCOMPLETE;
+// case EOF:
+// if (in.prevCT.kind.isOkToTerminate()) {
+// return Status.COMPLETE_WITH_SEMI;
+// } else {
+// return Status.DEFINITELY_INCOMPLETE;
+// }
+// default:
+// return error();
+// }
+// }
+
+ public Completeness parseExpressionStatement() {
+ if (shouldAbort(parseExpression())) return checkResult;
+ return lastly(SEMI);
+ }
+
+ public Completeness parseExpressionOptionalSemi() {
+ if (shouldAbort(parseExpression())) return checkResult;
+ return optionalFinalSemi();
+ }
+
+ public Completeness parseExpression() {
+ while (token.kind.isExpression())
+ nextToken();
+ return Completeness.COMPLETE;
+ }
+
+ public Completeness parseStatement() {
+ Completeness stat = parseSimpleStatement();
+ if (stat == null) {
+ return parseExpressionStatement();
+ }
+ return stat;
+ }
+
+ /**
+ * Statement = Block | IF ParExpression Statement [ELSE Statement] | FOR
+ * "(" ForInitOpt ";" [Expression] ";" ForUpdateOpt ")" Statement | FOR
+ * "(" FormalParameter : Expression ")" Statement | WHILE ParExpression
+ * Statement | DO Statement WHILE ParExpression ";" | TRY Block (
+ * Catches | [Catches] FinallyPart ) | TRY "(" ResourceSpecification
+ * ";"opt ")" Block [Catches] [FinallyPart] | SWITCH ParExpression "{"
+ * SwitchBlockStatementGroups "}" | SYNCHRONIZED ParExpression Block |
+ * RETURN [Expression] ";" | THROW Expression ";" | BREAK [Ident] ";" |
+ * CONTINUE [Ident] ";" | ASSERT Expression [ ":" Expression ] ";" | ";"
+ */
+ public Completeness parseSimpleStatement() {
+ switch (token.kind) {
+ case BRACES:
+ return lastly(BRACES);
+ case IF: {
+ nextToken();
+ if (shouldAbort(PARENS)) return checkResult;
+ Completeness thenpart = parseStatement();
+ if (shouldAbort(thenpart)) return thenpart;
+ if (token.kind == ELSE) {
+ nextToken();
+ return parseStatement();
+ }
+ return thenpart;
+
+ }
+ case FOR: {
+ nextToken();
+ if (shouldAbort(PARENS)) return checkResult;
+ if (shouldAbort(parseStatement())) return checkResult;
+ return Completeness.COMPLETE;
+ }
+ case WHILE: {
+ nextToken();
+ if (shouldAbort(PARENS)) return error();
+ return parseStatement();
+ }
+ case DO: {
+ nextToken();
+ switch (parseStatement()) {
+ case DEFINITELY_INCOMPLETE:
+ case CONSIDERED_INCOMPLETE:
+ case COMPLETE_WITH_SEMI:
+ return Completeness.DEFINITELY_INCOMPLETE;
+ case UNKNOWN:
+ return error();
+ case COMPLETE:
+ break;
+ }
+ if (shouldAbort(WHILE)) return checkResult;
+ if (shouldAbort(PARENS)) return checkResult;
+ return lastly(SEMI);
+ }
+ case TRY: {
+ boolean hasResources = false;
+ nextToken();
+ if (token.kind == PARENS) {
+ nextToken();
+ hasResources = true;
+ }
+ if (shouldAbort(BRACES)) return checkResult;
+ if (token.kind == CATCH || token.kind == FINALLY) {
+ while (token.kind == CATCH) {
+ if (shouldAbort(CATCH)) return checkResult;
+ if (shouldAbort(PARENS)) return checkResult;
+ if (shouldAbort(BRACES)) return checkResult;
+ }
+ if (token.kind == FINALLY) {
+ if (shouldAbort(FINALLY)) return checkResult;
+ if (shouldAbort(BRACES)) return checkResult;
+ }
+ } else if (!hasResources) {
+ if (token.kind == EOF) {
+ return Completeness.DEFINITELY_INCOMPLETE;
+ } else {
+ return error();
+ }
+ }
+ return Completeness.COMPLETE;
+ }
+ case SWITCH: {
+ nextToken();
+ if (shouldAbort(PARENS)) return checkResult;
+ return lastly(BRACES);
+ }
+ case SYNCHRONIZED: {
+ nextToken();
+ if (shouldAbort(PARENS)) return checkResult;
+ return lastly(BRACES);
+ }
+ case THROW: {
+ nextToken();
+ if (shouldAbort(parseExpression())) return checkResult;
+ return lastly(SEMI);
+ }
+ case SEMI:
+ return lastly(SEMI);
+ case ASSERT:
+ nextToken();
+ // Crude expression parsing just happily eats the optional colon
+ return parseExpressionStatement();
+ case RETURN:
+ case BREAK:
+ case CONTINUE:
+ nextToken();
+ return parseExpressionStatement();
+ // What are these doing here?
+ case ELSE:
+ case FINALLY:
+ case CATCH:
+ return error();
+ case EOF:
+ return Completeness.CONSIDERED_INCOMPLETE;
+ default:
+ return null;
+ }
+ }
+ }
+}