< prev index next >

src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java

Print this page




  35 import com.sun.tools.javac.util.JCDiagnostic;
  36 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
  37 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
  38 import com.sun.tools.javac.util.JCDiagnostic.Error;
  39 import com.sun.tools.javac.util.Log;
  40 import java.io.PrintWriter;
  41 import java.io.StringWriter;
  42 import java.util.ArrayDeque;
  43 import java.util.Deque;
  44 import java.util.EnumMap;
  45 import java.util.Iterator;
  46 import jdk.jshell.SourceCodeAnalysis.Completeness;
  47 import com.sun.source.tree.Tree;
  48 import static jdk.jshell.CompletenessAnalyzer.TK.*;
  49 import jdk.jshell.TaskFactory.ParseTask;
  50 import jdk.jshell.TaskFactory.Worker;
  51 import java.util.List;
  52 import java.util.function.Function;
  53 import java.util.function.Supplier;
  54 


  55 /**
  56  * Low level scanner to determine completeness of input.
  57  * @author Robert Field
  58  */
  59 class CompletenessAnalyzer {
  60 
  61     private final ScannerFactory scannerFactory;
  62     private final JShell proc;

  63 
  64     private static Completeness error() {
  65         return Completeness.UNKNOWN;  // For breakpointing
  66     }
  67 
  68     static class CaInfo {
  69 
  70         CaInfo(Completeness status, int unitEndPos) {
  71             this.status = status;
  72             this.unitEndPos = unitEndPos;
  73         }
  74         final int unitEndPos;
  75         final Completeness status;
  76     }
  77 
  78     CompletenessAnalyzer(JShell proc) {
  79         this.proc = proc;
  80         Context context = new Context();
  81         Log log = CaLog.createLog(context);
  82         context.put(Log.class, log);
  83         context.put(Source.class, Source.JDK9);

  84         scannerFactory = ScannerFactory.instance(context);
  85     }
  86 
  87     CaInfo scan(String s) {
  88         try {
  89             Parser parser = new Parser(
  90                     () -> new Matched(scannerFactory.newScanner(s, false)),

  91                     worker -> proc.taskFactory.parse(s, worker));
  92             Completeness stat = parser.parseUnit();
  93             int endPos = stat == Completeness.UNKNOWN
  94                     ? s.length()
  95                     : parser.endPos();
  96             return new CaInfo(stat, endPos);
  97         } catch (SyntaxException ex) {
  98             return new CaInfo(error(), s.length());
  99         }
 100     }
 101 
 102     @SuppressWarnings("serial")             // serialVersionUID intentionally omitted
 103     private static class SyntaxException extends RuntimeException {
 104     }
 105 
 106     private static void die() {
 107         throw new SyntaxException();
 108     }
 109 
 110     /**


 144         public void report(JCDiagnostic diagnostic) {
 145             // Ignore
 146         }
 147     }
 148 
 149     // Location position kinds -- a token is ...
 150     private static final int XEXPR         = 0b1;                       // OK in expression (not first)
 151     private static final int XDECL         = 0b10;                      // OK in declaration (not first)
 152     private static final int XSTMT         = 0b100;                     // OK in statement framework (not first)
 153     private static final int XEXPR1o       = 0b1000;                    // OK first in expression
 154     private static final int XDECL1o       = 0b10000;                   // OK first in declaration
 155     private static final int XSTMT1o       = 0b100000;                  // OK first or only in statement framework
 156     private static final int XEXPR1        = XEXPR1o | XEXPR;           // OK in expression (anywhere)
 157     private static final int XDECL1        = XDECL1o | XDECL;           // OK in declaration (anywhere)
 158     private static final int XSTMT1        = XSTMT1o | XSTMT;           // OK in statement framework (anywhere)
 159     private static final int XANY1         = XEXPR1o | XDECL1o | XSTMT1o;  // Mask: first in statement, declaration, or expression
 160     private static final int XTERM         = 0b100000000;               // Can terminate (last before EOF)
 161     private static final int XSTART        = 0b1000000000;              // Boundary, must be XTERM before
 162     private static final int XERRO         = 0b10000000000;             // Is an error
 163     private static final int XBRACESNEEDED = 0b100000000000;            // Expect {ANY} LBRACE

 164 
 165     /**
 166      * An extension of the compiler's TokenKind which adds our combined/processed
 167      * kinds. Also associates each TK with a union of acceptable kinds of code
 168      * position it can occupy.  For example: IDENTIFER is XEXPR1|XDECL1|XTERM,
 169      * meaning it can occur in expressions or declarations (but not in the
 170      * framework of a statement and that can be the final (terminating) token
 171      * in a snippet.
 172      * <P>
 173      * There must be a TK defined for each compiler TokenKind, an exception
 174      * will
 175      * be thrown if a TokenKind is defined and a corresponding TK is not. Add a
 176      * new TK in the appropriate category. If it is like an existing category
 177      * (e.g. a new modifier or type this may be all that is needed.  If it
 178      * is bracketing or modifies the acceptable positions of other tokens,
 179      * please closely examine the needed changes to this scanner.
 180      */
 181     static enum TK {
 182 
 183         // Special
 184         EOF(TokenKind.EOF, 0),  //
 185         ERROR(TokenKind.ERROR, XERRO),  //
 186         IDENTIFIER(TokenKind.IDENTIFIER, XEXPR1|XDECL1|XTERM),  //
 187         UNDERSCORE(TokenKind.UNDERSCORE, XERRO),  //  _
 188         CLASS(TokenKind.CLASS, XEXPR|XDECL1|XBRACESNEEDED),  //  class decl (MAPPED: DOTCLASS)



 189         MONKEYS_AT(TokenKind.MONKEYS_AT, XEXPR|XDECL1),  //  @
 190         IMPORT(TokenKind.IMPORT, XDECL1|XSTART),  //  import -- consider declaration
 191         SEMI(TokenKind.SEMI, XSTMT1|XTERM|XSTART),  //  ;
 192 
 193         // Shouldn't see -- error
 194         PACKAGE(TokenKind.PACKAGE, XERRO),  //  package
 195         CONST(TokenKind.CONST, XERRO),  //  reserved keyword -- const
 196         GOTO(TokenKind.GOTO, XERRO),  //  reserved keyword -- goto
 197         CUSTOM(TokenKind.CUSTOM, XERRO),  // No uses
 198 
 199         // Declarations
 200         ENUM(TokenKind.ENUM, XDECL1|XBRACESNEEDED),  //  enum
 201         IMPLEMENTS(TokenKind.IMPLEMENTS, XDECL),  //  implements
 202         INTERFACE(TokenKind.INTERFACE, XDECL1|XBRACESNEEDED),  //  interface
 203         THROWS(TokenKind.THROWS, XDECL|XBRACESNEEDED),  //  throws
 204 
 205         // Primarive type names
 206         BOOLEAN(TokenKind.BOOLEAN, XEXPR1|XDECL1),  //  boolean
 207         BYTE(TokenKind.BYTE, XEXPR1|XDECL1),  //  byte
 208         CHAR(TokenKind.CHAR, XEXPR1|XDECL1),  //  char
 209         DOUBLE(TokenKind.DOUBLE, XEXPR1|XDECL1),  //  double
 210         FLOAT(TokenKind.FLOAT, XEXPR1|XDECL1),  //  float
 211         INT(TokenKind.INT, XEXPR1|XDECL1),  //  int
 212         LONG(TokenKind.LONG, XEXPR1|XDECL1),  //  long
 213         SHORT(TokenKind.SHORT, XEXPR1|XDECL1),  //  short
 214         VOID(TokenKind.VOID, XEXPR1|XDECL1),  //  void

 215 
 216         // Modifiers keywords
 217         ABSTRACT(TokenKind.ABSTRACT, XDECL1),  //  abstract
 218         FINAL(TokenKind.FINAL, XDECL1),  //  final
 219         NATIVE(TokenKind.NATIVE, XDECL1),  //  native
 220         STATIC(TokenKind.STATIC, XDECL1),  //  static
 221         STRICTFP(TokenKind.STRICTFP, XDECL1),  //  strictfp
 222         PRIVATE(TokenKind.PRIVATE, XDECL1),  //  private
 223         PROTECTED(TokenKind.PROTECTED, XDECL1),  //  protected
 224         PUBLIC(TokenKind.PUBLIC, XDECL1),  //  public
 225         TRANSIENT(TokenKind.TRANSIENT, XDECL1),  //  transient
 226         VOLATILE(TokenKind.VOLATILE, XDECL1),  //  volatile
 227 
 228         // Declarations and type parameters (thus expressions)
 229         EXTENDS(TokenKind.EXTENDS, XEXPR|XDECL),  //  extends
 230         COMMA(TokenKind.COMMA, XEXPR|XDECL),  //  ,
 231         AMP(TokenKind.AMP, XEXPR|XDECL, true),  //  &
 232         GT(TokenKind.GT, XEXPR|XDECL, true),  //  >
 233         LT(TokenKind.LT, XEXPR|XDECL1, true),  //  <
 234         LTLT(TokenKind.LTLT, XEXPR|XDECL1, true),  //  <<
 235         GTGT(TokenKind.GTGT, XEXPR|XDECL, true),  //  >>
 236         GTGTGT(TokenKind.GTGTGT, XEXPR|XDECL, true),  //  >>>
 237         QUES(TokenKind.QUES, XEXPR|XDECL, true),  //  ?
 238         DOT(TokenKind.DOT, XEXPR|XDECL),  //  .
 239         STAR(TokenKind.STAR, XEXPR, true),  //  * (MAPPED: DOTSTAR)
 240 
 241         // Statement keywords
 242         ASSERT(TokenKind.ASSERT, XSTMT1|XSTART),  //  assert
 243         BREAK(TokenKind.BREAK, XSTMT1|XTERM|XSTART),  //  break
 244         CATCH(TokenKind.CATCH, XSTMT1|XSTART),  //  catch
 245         CONTINUE(TokenKind.CONTINUE, XSTMT1|XTERM|XSTART),  //  continue
 246         DO(TokenKind.DO, XSTMT1|XSTART),  //  do


 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 


 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 Function<Worker<ParseTask, Completeness>, Completeness> parseFactory;
 565         private Matched in;
 566         private CT token;
 567         private Completeness checkResult;

 568 
 569         Parser(Supplier<Matched> matchedFactory,

 570                Function<Worker<ParseTask, Completeness>, Completeness> parseFactory) {
 571             this.matchedFactory = matchedFactory;
 572             this.parseFactory = parseFactory;

 573             resetInput();
 574         }
 575 
 576         final void resetInput() {
 577             this.in = matchedFactory.get();
 578             nextToken();
 579         }
 580 
 581         final void nextToken() {
 582             in.next();
 583             token = in.currentCT;
 584         }
 585 
 586         boolean shouldAbort(TK tk) {
 587             if (token.kind == tk) {
 588                 nextToken();
 589                 return false;
 590             }
 591             switch (token.kind) {
 592                 case EOF:


 635                 }
 636                 case XDECL1o:
 637                     return parseDeclaration();
 638                 case XSTMT1o | XDECL1o:
 639                 case XEXPR1o | XDECL1o:
 640                     return disambiguateDeclarationVsExpression();
 641                 case 0:
 642                     if ((token.kind.belongs & XERRO) != 0) {
 643                         return parseExpressionStatement(); // Let this gen the status
 644                     }
 645                     return error();
 646                 case XSTMT1o | XEXPR1o:
 647                     return disambiguateStatementVsExpression();
 648                 default:
 649                     throw new InternalError("Case not covered " + token.kind.belongs + " in " + token.kind);
 650             }
 651         }
 652 
 653         public Completeness parseDeclaration() {
 654             boolean isImport = token.kind == IMPORT;


 655             boolean isBracesNeeded = false;
 656             while (token.kind.isDeclaration()) {
 657                 isBracesNeeded |= token.kind.isBracesNeeded();


 658                 nextToken();
 659             }
 660             switch (token.kind) {
 661                 case EQ:
 662                     nextToken();
 663                     return parseExpressionStatement();
 664                 case BRACES:
 665                 case SEMI:
 666                     nextToken();
 667                     return Completeness.COMPLETE;
 668                 case UNMATCHED:
 669                     nextToken();
 670                     return Completeness.DEFINITELY_INCOMPLETE;
 671                 case EOF:
 672                     switch (in.prevCT.kind) {
 673                         case BRACES:
 674                         case SEMI:
 675                             return Completeness.COMPLETE;

 676                         case IDENTIFIER:
 677                             return isBracesNeeded
 678                                     ? Completeness.DEFINITELY_INCOMPLETE
 679                                     : Completeness.COMPLETE_WITH_SEMI;
 680                         case BRACKETS:
 681                             return Completeness.COMPLETE_WITH_SEMI;






 682                         case DOTSTAR:
 683                             if (isImport) {
 684                                 return Completeness.COMPLETE_WITH_SEMI;
 685                             } else {
 686                                 return Completeness.UNKNOWN;
 687                             }
 688                         default:
 689                             return Completeness.DEFINITELY_INCOMPLETE;
 690                     }
 691                 default:
 692                     return error();
 693             }
 694         }
 695 
 696         public Completeness disambiguateStatementVsExpression() {
 697             if (token.kind == SWITCH) {
 698                 nextToken();
 699                 switch (token.kind) {
 700                     case PARENS:
 701                         nextToken();




  35 import com.sun.tools.javac.util.JCDiagnostic;
  36 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
  37 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
  38 import com.sun.tools.javac.util.JCDiagnostic.Error;
  39 import com.sun.tools.javac.util.Log;
  40 import java.io.PrintWriter;
  41 import java.io.StringWriter;
  42 import java.util.ArrayDeque;
  43 import java.util.Deque;
  44 import java.util.EnumMap;
  45 import java.util.Iterator;
  46 import jdk.jshell.SourceCodeAnalysis.Completeness;
  47 import com.sun.source.tree.Tree;
  48 import static jdk.jshell.CompletenessAnalyzer.TK.*;
  49 import jdk.jshell.TaskFactory.ParseTask;
  50 import jdk.jshell.TaskFactory.Worker;
  51 import java.util.List;
  52 import java.util.function.Function;
  53 import java.util.function.Supplier;
  54 
  55 import com.sun.tools.javac.util.Names;
  56 
  57 /**
  58  * Low level scanner to determine completeness of input.
  59  * @author Robert Field
  60  */
  61 class CompletenessAnalyzer {
  62 
  63     private final ScannerFactory scannerFactory;
  64     private final JShell proc;
  65     private final Names names;
  66 
  67     private static Completeness error() {
  68         return Completeness.UNKNOWN;  // For breakpointing
  69     }
  70 
  71     static class CaInfo {
  72 
  73         CaInfo(Completeness status, int unitEndPos) {
  74             this.status = status;
  75             this.unitEndPos = unitEndPos;
  76         }
  77         final int unitEndPos;
  78         final Completeness status;
  79     }
  80 
  81     CompletenessAnalyzer(JShell proc) {
  82         this.proc = proc;
  83         Context context = new Context();
  84         Log log = CaLog.createLog(context);
  85         context.put(Log.class, log);
  86         context.put(Source.class, Source.JDK9);
  87         names = Names.instance(context);
  88         scannerFactory = ScannerFactory.instance(context);
  89     }
  90 
  91     CaInfo scan(String s) {
  92         try {
  93             Parser parser = new Parser(
  94                     () -> new Matched(scannerFactory.newScanner(s, false)),
  95                     names,
  96                     worker -> proc.taskFactory.parse(s, worker));
  97             Completeness stat = parser.parseUnit();
  98             int endPos = stat == Completeness.UNKNOWN
  99                     ? s.length()
 100                     : parser.endPos();
 101             return new CaInfo(stat, endPos);
 102         } catch (SyntaxException ex) {
 103             return new CaInfo(error(), s.length());
 104         }
 105     }
 106 
 107     @SuppressWarnings("serial")             // serialVersionUID intentionally omitted
 108     private static class SyntaxException extends RuntimeException {
 109     }
 110 
 111     private static void die() {
 112         throw new SyntaxException();
 113     }
 114 
 115     /**


 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     private static final int XBRACESNEEDED = 0b100000000000;            // Expect {ANY} LBRACE
 169     private static final int XMODIFIER     = 0b1000000000000;           // Modifier
 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         RECORD(TokenKind.RECORD, XEXPR|XDECL1),  //  record decl (MAPPED: DOTCLASS)
 196         SEALED(TokenKind.SEALED, XEXPR|XDECL1),  //  sealed class decl (MAPPED: DOTCLASS)
 197         PERMITS(TokenKind.PERMITS, XEXPR|XDECL),  // permits classlist
 198         MONKEYS_AT(TokenKind.MONKEYS_AT, XEXPR|XDECL1),  //  @
 199         IMPORT(TokenKind.IMPORT, XDECL1|XSTART),  //  import -- consider declaration
 200         SEMI(TokenKind.SEMI, XSTMT1|XTERM|XSTART),  //  ;
 201 
 202         // Shouldn't see -- error
 203         PACKAGE(TokenKind.PACKAGE, XERRO),  //  package
 204         CONST(TokenKind.CONST, XERRO),  //  reserved keyword -- const
 205         GOTO(TokenKind.GOTO, XERRO),  //  reserved keyword -- goto
 206         CUSTOM(TokenKind.CUSTOM, XERRO),  // No uses
 207 
 208         // Declarations
 209         ENUM(TokenKind.ENUM, XDECL1|XBRACESNEEDED),  //  enum
 210         IMPLEMENTS(TokenKind.IMPLEMENTS, XDECL),  //  implements
 211         INTERFACE(TokenKind.INTERFACE, XDECL1|XBRACESNEEDED),  //  interface
 212         THROWS(TokenKind.THROWS, XDECL|XBRACESNEEDED),  //  throws
 213 
 214         // Primarive type names
 215         BOOLEAN(TokenKind.BOOLEAN, XEXPR1|XDECL1),  //  boolean
 216         BYTE(TokenKind.BYTE, XEXPR1|XDECL1),  //  byte
 217         CHAR(TokenKind.CHAR, XEXPR1|XDECL1),  //  char
 218         DOUBLE(TokenKind.DOUBLE, XEXPR1|XDECL1),  //  double
 219         FLOAT(TokenKind.FLOAT, XEXPR1|XDECL1),  //  float
 220         INT(TokenKind.INT, XEXPR1|XDECL1),  //  int
 221         LONG(TokenKind.LONG, XEXPR1|XDECL1),  //  long
 222         SHORT(TokenKind.SHORT, XEXPR1|XDECL1),  //  short
 223         VOID(TokenKind.VOID, XEXPR1|XDECL1),  //  void
 224         VAR(TokenKind.VAR, XEXPR1|XDECL1|XTERM),  //  var
 225 
 226         // Modifiers keywords
 227         ABSTRACT(TokenKind.ABSTRACT, XDECL1 | XMODIFIER),  //  abstract
 228         FINAL(TokenKind.FINAL, XDECL1 | XMODIFIER),  //  final
 229         NATIVE(TokenKind.NATIVE, XDECL1 | XMODIFIER),  //  native
 230         STATIC(TokenKind.STATIC, XDECL1 | XMODIFIER),  //  static
 231         STRICTFP(TokenKind.STRICTFP, XDECL1 | XMODIFIER),  //  strictfp
 232         PRIVATE(TokenKind.PRIVATE, XDECL1 | XMODIFIER),  //  private
 233         PROTECTED(TokenKind.PROTECTED, XDECL1 | XMODIFIER),  //  protected
 234         PUBLIC(TokenKind.PUBLIC, XDECL1 | XMODIFIER),  //  public
 235         TRANSIENT(TokenKind.TRANSIENT, XDECL1 | XMODIFIER),  //  transient
 236         VOLATILE(TokenKind.VOLATILE, XDECL1 | XMODIFIER),  //  volatile
 237 
 238         // Declarations and type parameters (thus expressions)
 239         EXTENDS(TokenKind.EXTENDS, XEXPR|XDECL),  //  extends
 240         COMMA(TokenKind.COMMA, XEXPR|XDECL),  //  ,
 241         AMP(TokenKind.AMP, XEXPR|XDECL, true),  //  &
 242         GT(TokenKind.GT, XEXPR|XDECL, true),  //  >
 243         LT(TokenKind.LT, XEXPR|XDECL1, true),  //  <
 244         LTLT(TokenKind.LTLT, XEXPR|XDECL1, true),  //  <<
 245         GTGT(TokenKind.GTGT, XEXPR|XDECL, true),  //  >>
 246         GTGTGT(TokenKind.GTGTGT, XEXPR|XDECL, true),  //  >>>
 247         QUES(TokenKind.QUES, XEXPR|XDECL, true),  //  ?
 248         DOT(TokenKind.DOT, XEXPR|XDECL),  //  .
 249         STAR(TokenKind.STAR, XEXPR, true),  //  * (MAPPED: DOTSTAR)
 250 
 251         // Statement keywords
 252         ASSERT(TokenKind.ASSERT, XSTMT1|XSTART),  //  assert
 253         BREAK(TokenKind.BREAK, XSTMT1|XTERM|XSTART),  //  break
 254         CATCH(TokenKind.CATCH, XSTMT1|XSTART),  //  catch
 255         CONTINUE(TokenKind.CONTINUE, XSTMT1|XTERM|XSTART),  //  continue
 256         DO(TokenKind.DO, XSTMT1|XSTART),  //  do


 379         boolean isExpression() {
 380             return (belongs & XEXPR) != 0;
 381         }
 382 
 383         boolean isDeclaration() {
 384             return (belongs & XDECL) != 0;
 385         }
 386 
 387         boolean isError() {
 388             return (belongs & XERRO) != 0;
 389         }
 390 
 391         boolean isStart() {
 392             return (belongs & XSTART) != 0;
 393         }
 394 
 395         boolean isBracesNeeded() {
 396             return (belongs & XBRACESNEEDED) != 0;
 397         }
 398 
 399         boolean isModifier() {
 400             return (belongs & XMODIFIER) != 0;
 401         }
 402 
 403         /**
 404          * After construction, check that all compiler TokenKind values have
 405          * corresponding TK values.
 406          */
 407         static {
 408             for (TK tk : TK.values()) {
 409                 if (tk.tokenKind != null) {
 410                     tokenKindToTKMap.put(tk.tokenKind, tk);
 411                 }
 412             }
 413             for (TokenKind kind : TokenKind.values()) {
 414                 tokenKindToTK(null, kind); // assure they can be retrieved without error
 415             }
 416             // Mappings of disambiguated contexts
 417             STAR.mapping  = prev -> prev == DOT ? DOTSTAR : STAR;
 418             NEW.mapping   = prev -> prev == COLCOL ? COLCOLNEW : NEW;
 419             CLASS.mapping = prev -> prev == DOT ? DOTCLASS : CLASS;
 420         }
 421     }
 422 
 423     /**
 424      * A completeness scanner token.
 425      */
 426     private static class CT {
 427 
 428         /** The token kind */
 429         public final TK kind;
 430 
 431         /** The end position of this token */
 432         public final int endPos;
 433 
 434         /** The error message **/
 435         public final String message;
 436 
 437         public final Token tok;
 438 
 439         private CT(TK tk, Token tok, String msg) {
 440             this.kind = tk;
 441             this.endPos = tok.endPos;
 442             this.message = msg;
 443             this.tok = tok;
 444             //throw new InternalError(msg); /* for debugging */
 445         }
 446 
 447         private CT(TK tk, Token tok) {
 448             this.kind = tk;
 449             this.endPos = tok.endPos;
 450             this.message = null;
 451             this.tok = tok;
 452         }
 453 
 454         private CT(TK tk, int endPos) {
 455             this.kind = tk;
 456             this.endPos = endPos;
 457             this.message = null;
 458             this.tok = null;
 459         }
 460     }
 461 
 462     /**
 463      * Look for matching tokens (like parens) and other special cases, like "new"
 464      */
 465     private static class Matched implements Iterator<CT> {
 466 
 467         private final Scanner scanner;
 468         private Token current;
 469         private CT prevCT;
 470         private CT currentCT;
 471         private final Deque<Token> stack = new ArrayDeque<>();
 472 
 473         Matched(Scanner scanner) {
 474             this.scanner = scanner;
 475             advance();
 476             prevCT = currentCT = new CT(SEMI, 0); // So is valid for testing
 477         }
 478 


 567                     return new CT(ERROR, current, "No '" + prevTK + "' before '" + ct.kind + "'");
 568                 }
 569                 if (stack.isEmpty() || ct.kind.isError()) {
 570                     return ct;
 571                 }
 572                 prevTK = ct.kind;
 573             }
 574         }
 575     }
 576 
 577     /**
 578      * Fuzzy parser based on token kinds
 579      */
 580     private static class Parser {
 581 
 582         private final Supplier<Matched> matchedFactory;
 583         private final Function<Worker<ParseTask, Completeness>, Completeness> parseFactory;
 584         private Matched in;
 585         private CT token;
 586         private Completeness checkResult;
 587         private final Names names;
 588 
 589         Parser(Supplier<Matched> matchedFactory,
 590                Names names,
 591                Function<Worker<ParseTask, Completeness>, Completeness> parseFactory) {
 592             this.matchedFactory = matchedFactory;
 593             this.parseFactory = parseFactory;
 594             this.names = names;
 595             resetInput();
 596         }
 597 
 598         final void resetInput() {
 599             this.in = matchedFactory.get();
 600             nextToken();
 601         }
 602 
 603         final void nextToken() {
 604             in.next();
 605             token = in.currentCT;
 606         }
 607 
 608         boolean shouldAbort(TK tk) {
 609             if (token.kind == tk) {
 610                 nextToken();
 611                 return false;
 612             }
 613             switch (token.kind) {
 614                 case EOF:


 657                 }
 658                 case XDECL1o:
 659                     return parseDeclaration();
 660                 case XSTMT1o | XDECL1o:
 661                 case XEXPR1o | XDECL1o:
 662                     return disambiguateDeclarationVsExpression();
 663                 case 0:
 664                     if ((token.kind.belongs & XERRO) != 0) {
 665                         return parseExpressionStatement(); // Let this gen the status
 666                     }
 667                     return error();
 668                 case XSTMT1o | XEXPR1o:
 669                     return disambiguateStatementVsExpression();
 670                 default:
 671                     throw new InternalError("Case not covered " + token.kind.belongs + " in " + token.kind);
 672             }
 673         }
 674 
 675         public Completeness parseDeclaration() {
 676             boolean isImport = token.kind == IMPORT;
 677             boolean isDatum = false;
 678             boolean afterModifiers = false;
 679             boolean isBracesNeeded = false;
 680             while (token.kind.isDeclaration()) {
 681                 isBracesNeeded |= token.kind.isBracesNeeded();
 682                 isDatum |= !afterModifiers && token.kind == TK.IDENTIFIER && token.tok.name() == names.record;
 683                 afterModifiers |= !token.kind.isModifier();
 684                 nextToken();
 685             }
 686             switch (token.kind) {
 687                 case EQ:
 688                     nextToken();
 689                     return parseExpressionStatement();
 690                 case BRACES:
 691                 case SEMI:
 692                     nextToken();
 693                     return Completeness.COMPLETE;
 694                 case UNMATCHED:
 695                     nextToken();
 696                     return Completeness.DEFINITELY_INCOMPLETE;
 697                 case EOF:
 698                     switch (in.prevCT.kind) {
 699                         case BRACES:
 700                         case SEMI:
 701                             return Completeness.COMPLETE;
 702                         case VAR:
 703                         case IDENTIFIER:
 704                             return isBracesNeeded
 705                                     ? Completeness.DEFINITELY_INCOMPLETE
 706                                     : Completeness.COMPLETE_WITH_SEMI;
 707                         case BRACKETS:
 708                             return Completeness.COMPLETE_WITH_SEMI;
 709                         case PARENS:
 710                             if (isDatum) {
 711                                 return Completeness.COMPLETE_WITH_SEMI;
 712                             } else {
 713                                 return Completeness.DEFINITELY_INCOMPLETE;
 714                             }
 715                         case DOTSTAR:
 716                             if (isImport) {
 717                                 return Completeness.COMPLETE_WITH_SEMI;
 718                             } else {
 719                                 return Completeness.UNKNOWN;
 720                             }
 721                         default:
 722                             return Completeness.DEFINITELY_INCOMPLETE;
 723                     }
 724                 default:
 725                     return error();
 726             }
 727         }
 728 
 729         public Completeness disambiguateStatementVsExpression() {
 730             if (token.kind == SWITCH) {
 731                 nextToken();
 732                 switch (token.kind) {
 733                     case PARENS:
 734                         nextToken();


< prev index next >