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