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