src/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java

Tue, 09 Oct 2012 19:31:58 -0700

author
jjg
date
Tue, 09 Oct 2012 19:31:58 -0700
changeset 1358
fc123bdeddb8
parent 1352
d4b3cb1ece84
child 1385
75c936d14c6a
permissions
-rw-r--r--

8000208: fix langtools javadoc comment issues
Reviewed-by: bpatel, mcimadamore

     1 /*
     2  * Copyright (c) 1999, 2012, 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  */
    26 package com.sun.tools.javac.parser;
    28 import com.sun.tools.javac.code.Source;
    29 import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
    30 import com.sun.tools.javac.util.*;
    32 import java.nio.CharBuffer;
    34 import static com.sun.tools.javac.parser.Tokens.*;
    35 import static com.sun.tools.javac.util.LayoutCharacters.*;
    37 /** The lexical analyzer maps an input stream consisting of
    38  *  ASCII characters and Unicode escapes into a token sequence.
    39  *
    40  *  <p><b>This is NOT part of any supported API.
    41  *  If you write code that depends on this, you do so at your own risk.
    42  *  This code and its internal interfaces are subject to change or
    43  *  deletion without notice.</b>
    44  */
    45 public class JavaTokenizer {
    47     private static boolean scannerDebug = false;
    49     /** Allow hex floating-point literals.
    50      */
    51     private boolean allowHexFloats;
    53     /** Allow binary literals.
    54      */
    55     private boolean allowBinaryLiterals;
    57     /** Allow underscores in literals.
    58      */
    59     private boolean allowUnderscoresInLiterals;
    61     /** The source language setting.
    62      */
    63     private Source source;
    65     /** The log to be used for error reporting.
    66      */
    67     private final Log log;
    69     /** The token factory. */
    70     private final Tokens tokens;
    72     /** The token kind, set by nextToken().
    73      */
    74     protected TokenKind tk;
    76     /** The token's radix, set by nextToken().
    77      */
    78     protected int radix;
    80     /** The token's name, set by nextToken().
    81      */
    82     protected Name name;
    84     /** The position where a lexical error occurred;
    85      */
    86     protected int errPos = Position.NOPOS;
    88     /** The Unicode reader (low-level stream reader).
    89      */
    90     protected UnicodeReader reader;
    92     protected ScannerFactory fac;
    94     private static final boolean hexFloatsWork = hexFloatsWork();
    95     private static boolean hexFloatsWork() {
    96         try {
    97             Float.valueOf("0x1.0p1");
    98             return true;
    99         } catch (NumberFormatException ex) {
   100             return false;
   101         }
   102     }
   104     /**
   105      * Create a scanner from the input array.  This method might
   106      * modify the array.  To avoid copying the input array, ensure
   107      * that {@code inputLength < input.length} or
   108      * {@code input[input.length -1]} is a white space character.
   109      *
   110      * @param fac the factory which created this Scanner
   111      * @param buf the input, might be modified
   112      * Must be positive and less than or equal to input.length.
   113      */
   114     protected JavaTokenizer(ScannerFactory fac, CharBuffer buf) {
   115         this(fac, new UnicodeReader(fac, buf));
   116     }
   118     protected JavaTokenizer(ScannerFactory fac, char[] buf, int inputLength) {
   119         this(fac, new UnicodeReader(fac, buf, inputLength));
   120     }
   122     protected JavaTokenizer(ScannerFactory fac, UnicodeReader reader) {
   123         this.fac = fac;
   124         this.log = fac.log;
   125         this.tokens = fac.tokens;
   126         this.source = fac.source;
   127         this.reader = reader;
   128         this.allowBinaryLiterals = source.allowBinaryLiterals();
   129         this.allowHexFloats = source.allowHexFloats();
   130         this.allowUnderscoresInLiterals = source.allowUnderscoresInLiterals();
   131     }
   133     /** Report an error at the given position using the provided arguments.
   134      */
   135     protected void lexError(int pos, String key, Object... args) {
   136         log.error(pos, key, args);
   137         tk = TokenKind.ERROR;
   138         errPos = pos;
   139     }
   141     /** Read next character in character or string literal and copy into sbuf.
   142      */
   143     private void scanLitChar(int pos) {
   144         if (reader.ch == '\\') {
   145             if (reader.peekChar() == '\\' && !reader.isUnicode()) {
   146                 reader.skipChar();
   147                 reader.putChar('\\', true);
   148             } else {
   149                 reader.scanChar();
   150                 switch (reader.ch) {
   151                 case '0': case '1': case '2': case '3':
   152                 case '4': case '5': case '6': case '7':
   153                     char leadch = reader.ch;
   154                     int oct = reader.digit(pos, 8);
   155                     reader.scanChar();
   156                     if ('0' <= reader.ch && reader.ch <= '7') {
   157                         oct = oct * 8 + reader.digit(pos, 8);
   158                         reader.scanChar();
   159                         if (leadch <= '3' && '0' <= reader.ch && reader.ch <= '7') {
   160                             oct = oct * 8 + reader.digit(pos, 8);
   161                             reader.scanChar();
   162                         }
   163                     }
   164                     reader.putChar((char)oct);
   165                     break;
   166                 case 'b':
   167                     reader.putChar('\b', true); break;
   168                 case 't':
   169                     reader.putChar('\t', true); break;
   170                 case 'n':
   171                     reader.putChar('\n', true); break;
   172                 case 'f':
   173                     reader.putChar('\f', true); break;
   174                 case 'r':
   175                     reader.putChar('\r', true); break;
   176                 case '\'':
   177                     reader.putChar('\'', true); break;
   178                 case '\"':
   179                     reader.putChar('\"', true); break;
   180                 case '\\':
   181                     reader.putChar('\\', true); break;
   182                 default:
   183                     lexError(reader.bp, "illegal.esc.char");
   184                 }
   185             }
   186         } else if (reader.bp != reader.buflen) {
   187             reader.putChar(true);
   188         }
   189     }
   191     private void scanDigits(int pos, int digitRadix) {
   192         char saveCh;
   193         int savePos;
   194         do {
   195             if (reader.ch != '_') {
   196                 reader.putChar(false);
   197             } else {
   198                 if (!allowUnderscoresInLiterals) {
   199                     lexError(pos, "unsupported.underscore.lit", source.name);
   200                     allowUnderscoresInLiterals = true;
   201                 }
   202             }
   203             saveCh = reader.ch;
   204             savePos = reader.bp;
   205             reader.scanChar();
   206         } while (reader.digit(pos, digitRadix) >= 0 || reader.ch == '_');
   207         if (saveCh == '_')
   208             lexError(savePos, "illegal.underscore");
   209     }
   211     /** Read fractional part of hexadecimal floating point number.
   212      */
   213     private void scanHexExponentAndSuffix(int pos) {
   214         if (reader.ch == 'p' || reader.ch == 'P') {
   215             reader.putChar(true);
   216             skipIllegalUnderscores();
   217             if (reader.ch == '+' || reader.ch == '-') {
   218                 reader.putChar(true);
   219             }
   220             skipIllegalUnderscores();
   221             if ('0' <= reader.ch && reader.ch <= '9') {
   222                 scanDigits(pos, 10);
   223                 if (!allowHexFloats) {
   224                     lexError(pos, "unsupported.fp.lit", source.name);
   225                     allowHexFloats = true;
   226                 }
   227                 else if (!hexFloatsWork)
   228                     lexError(pos, "unsupported.cross.fp.lit");
   229             } else
   230                 lexError(pos, "malformed.fp.lit");
   231         } else {
   232             lexError(pos, "malformed.fp.lit");
   233         }
   234         if (reader.ch == 'f' || reader.ch == 'F') {
   235             reader.putChar(true);
   236             tk = TokenKind.FLOATLITERAL;
   237             radix = 16;
   238         } else {
   239             if (reader.ch == 'd' || reader.ch == 'D') {
   240                 reader.putChar(true);
   241             }
   242             tk = TokenKind.DOUBLELITERAL;
   243             radix = 16;
   244         }
   245     }
   247     /** Read fractional part of floating point number.
   248      */
   249     private void scanFraction(int pos) {
   250         skipIllegalUnderscores();
   251         if ('0' <= reader.ch && reader.ch <= '9') {
   252             scanDigits(pos, 10);
   253         }
   254         int sp1 = reader.sp;
   255         if (reader.ch == 'e' || reader.ch == 'E') {
   256             reader.putChar(true);
   257             skipIllegalUnderscores();
   258             if (reader.ch == '+' || reader.ch == '-') {
   259                 reader.putChar(true);
   260             }
   261             skipIllegalUnderscores();
   262             if ('0' <= reader.ch && reader.ch <= '9') {
   263                 scanDigits(pos, 10);
   264                 return;
   265             }
   266             lexError(pos, "malformed.fp.lit");
   267             reader.sp = sp1;
   268         }
   269     }
   271     /** Read fractional part and 'd' or 'f' suffix of floating point number.
   272      */
   273     private void scanFractionAndSuffix(int pos) {
   274         radix = 10;
   275         scanFraction(pos);
   276         if (reader.ch == 'f' || reader.ch == 'F') {
   277             reader.putChar(true);
   278             tk = TokenKind.FLOATLITERAL;
   279         } else {
   280             if (reader.ch == 'd' || reader.ch == 'D') {
   281                 reader.putChar(true);
   282             }
   283             tk = TokenKind.DOUBLELITERAL;
   284         }
   285     }
   287     /** Read fractional part and 'd' or 'f' suffix of floating point number.
   288      */
   289     private void scanHexFractionAndSuffix(int pos, boolean seendigit) {
   290         radix = 16;
   291         Assert.check(reader.ch == '.');
   292         reader.putChar(true);
   293         skipIllegalUnderscores();
   294         if (reader.digit(pos, 16) >= 0) {
   295             seendigit = true;
   296             scanDigits(pos, 16);
   297         }
   298         if (!seendigit)
   299             lexError(pos, "invalid.hex.number");
   300         else
   301             scanHexExponentAndSuffix(pos);
   302     }
   304     private void skipIllegalUnderscores() {
   305         if (reader.ch == '_') {
   306             lexError(reader.bp, "illegal.underscore");
   307             while (reader.ch == '_')
   308                 reader.scanChar();
   309         }
   310     }
   312     /** Read a number.
   313      *  @param radix  The radix of the number; one of 2, j8, 10, 16.
   314      */
   315     private void scanNumber(int pos, int radix) {
   316         // for octal, allow base-10 digit in case it's a float literal
   317         this.radix = radix;
   318         int digitRadix = (radix == 8 ? 10 : radix);
   319         boolean seendigit = false;
   320         if (reader.digit(pos, digitRadix) >= 0) {
   321             seendigit = true;
   322             scanDigits(pos, digitRadix);
   323         }
   324         if (radix == 16 && reader.ch == '.') {
   325             scanHexFractionAndSuffix(pos, seendigit);
   326         } else if (seendigit && radix == 16 && (reader.ch == 'p' || reader.ch == 'P')) {
   327             scanHexExponentAndSuffix(pos);
   328         } else if (digitRadix == 10 && reader.ch == '.') {
   329             reader.putChar(true);
   330             scanFractionAndSuffix(pos);
   331         } else if (digitRadix == 10 &&
   332                    (reader.ch == 'e' || reader.ch == 'E' ||
   333                     reader.ch == 'f' || reader.ch == 'F' ||
   334                     reader.ch == 'd' || reader.ch == 'D')) {
   335             scanFractionAndSuffix(pos);
   336         } else {
   337             if (reader.ch == 'l' || reader.ch == 'L') {
   338                 reader.scanChar();
   339                 tk = TokenKind.LONGLITERAL;
   340             } else {
   341                 tk = TokenKind.INTLITERAL;
   342             }
   343         }
   344     }
   346     /** Read an identifier.
   347      */
   348     private void scanIdent() {
   349         boolean isJavaIdentifierPart;
   350         char high;
   351         do {
   352             reader.putChar(true);
   353             switch (reader.ch) {
   354             case 'A': case 'B': case 'C': case 'D': case 'E':
   355             case 'F': case 'G': case 'H': case 'I': case 'J':
   356             case 'K': case 'L': case 'M': case 'N': case 'O':
   357             case 'P': case 'Q': case 'R': case 'S': case 'T':
   358             case 'U': case 'V': case 'W': case 'X': case 'Y':
   359             case 'Z':
   360             case 'a': case 'b': case 'c': case 'd': case 'e':
   361             case 'f': case 'g': case 'h': case 'i': case 'j':
   362             case 'k': case 'l': case 'm': case 'n': case 'o':
   363             case 'p': case 'q': case 'r': case 's': case 't':
   364             case 'u': case 'v': case 'w': case 'x': case 'y':
   365             case 'z':
   366             case '$': case '_':
   367             case '0': case '1': case '2': case '3': case '4':
   368             case '5': case '6': case '7': case '8': case '9':
   369             case '\u0000': case '\u0001': case '\u0002': case '\u0003':
   370             case '\u0004': case '\u0005': case '\u0006': case '\u0007':
   371             case '\u0008': case '\u000E': case '\u000F': case '\u0010':
   372             case '\u0011': case '\u0012': case '\u0013': case '\u0014':
   373             case '\u0015': case '\u0016': case '\u0017':
   374             case '\u0018': case '\u0019': case '\u001B':
   375             case '\u007F':
   376                 break;
   377             case '\u001A': // EOI is also a legal identifier part
   378                 if (reader.bp >= reader.buflen) {
   379                     name = reader.name();
   380                     tk = tokens.lookupKind(name);
   381                     return;
   382                 }
   383                 break;
   384             default:
   385                 if (reader.ch < '\u0080') {
   386                     // all ASCII range chars already handled, above
   387                     isJavaIdentifierPart = false;
   388                 } else {
   389                     high = reader.scanSurrogates();
   390                     if (high != 0) {
   391                         reader.putChar(high);
   392                         isJavaIdentifierPart = Character.isJavaIdentifierPart(
   393                             Character.toCodePoint(high, reader.ch));
   394                     } else {
   395                         isJavaIdentifierPart = Character.isJavaIdentifierPart(reader.ch);
   396                     }
   397                 }
   398                 if (!isJavaIdentifierPart) {
   399                     name = reader.name();
   400                     tk = tokens.lookupKind(name);
   401                     return;
   402                 }
   403             }
   404         } while (true);
   405     }
   407     /** Return true if reader.ch can be part of an operator.
   408      */
   409     private boolean isSpecial(char ch) {
   410         switch (ch) {
   411         case '!': case '%': case '&': case '*': case '?':
   412         case '+': case '-': case ':': case '<': case '=':
   413         case '>': case '^': case '|': case '~':
   414         case '@':
   415             return true;
   416         default:
   417             return false;
   418         }
   419     }
   421     /** Read longest possible sequence of special characters and convert
   422      *  to token.
   423      */
   424     private void scanOperator() {
   425         while (true) {
   426             reader.putChar(false);
   427             Name newname = reader.name();
   428             TokenKind tk1 = tokens.lookupKind(newname);
   429             if (tk1 == TokenKind.IDENTIFIER) {
   430                 reader.sp--;
   431                 break;
   432             }
   433             tk = tk1;
   434             reader.scanChar();
   435             if (!isSpecial(reader.ch)) break;
   436         }
   437     }
   439     /** Read token.
   440      */
   441     public Token readToken() {
   443         reader.sp = 0;
   444         name = null;
   445         radix = 0;
   447         int pos = 0;
   448         int endPos = 0;
   449         List<Comment> comments = null;
   451         try {
   452             loop: while (true) {
   453                 pos = reader.bp;
   454                 switch (reader.ch) {
   455                 case ' ': // (Spec 3.6)
   456                 case '\t': // (Spec 3.6)
   457                 case FF: // (Spec 3.6)
   458                     do {
   459                         reader.scanChar();
   460                     } while (reader.ch == ' ' || reader.ch == '\t' || reader.ch == FF);
   461                     processWhiteSpace(pos, reader.bp);
   462                     break;
   463                 case LF: // (Spec 3.4)
   464                     reader.scanChar();
   465                     processLineTerminator(pos, reader.bp);
   466                     break;
   467                 case CR: // (Spec 3.4)
   468                     reader.scanChar();
   469                     if (reader.ch == LF) {
   470                         reader.scanChar();
   471                     }
   472                     processLineTerminator(pos, reader.bp);
   473                     break;
   474                 case 'A': case 'B': case 'C': case 'D': case 'E':
   475                 case 'F': case 'G': case 'H': case 'I': case 'J':
   476                 case 'K': case 'L': case 'M': case 'N': case 'O':
   477                 case 'P': case 'Q': case 'R': case 'S': case 'T':
   478                 case 'U': case 'V': case 'W': case 'X': case 'Y':
   479                 case 'Z':
   480                 case 'a': case 'b': case 'c': case 'd': case 'e':
   481                 case 'f': case 'g': case 'h': case 'i': case 'j':
   482                 case 'k': case 'l': case 'm': case 'n': case 'o':
   483                 case 'p': case 'q': case 'r': case 's': case 't':
   484                 case 'u': case 'v': case 'w': case 'x': case 'y':
   485                 case 'z':
   486                 case '$': case '_':
   487                     scanIdent();
   488                     break loop;
   489                 case '0':
   490                     reader.scanChar();
   491                     if (reader.ch == 'x' || reader.ch == 'X') {
   492                         reader.scanChar();
   493                         skipIllegalUnderscores();
   494                         if (reader.ch == '.') {
   495                             scanHexFractionAndSuffix(pos, false);
   496                         } else if (reader.digit(pos, 16) < 0) {
   497                             lexError(pos, "invalid.hex.number");
   498                         } else {
   499                             scanNumber(pos, 16);
   500                         }
   501                     } else if (reader.ch == 'b' || reader.ch == 'B') {
   502                         if (!allowBinaryLiterals) {
   503                             lexError(pos, "unsupported.binary.lit", source.name);
   504                             allowBinaryLiterals = true;
   505                         }
   506                         reader.scanChar();
   507                         skipIllegalUnderscores();
   508                         if (reader.digit(pos, 2) < 0) {
   509                             lexError(pos, "invalid.binary.number");
   510                         } else {
   511                             scanNumber(pos, 2);
   512                         }
   513                     } else {
   514                         reader.putChar('0');
   515                         if (reader.ch == '_') {
   516                             int savePos = reader.bp;
   517                             do {
   518                                 reader.scanChar();
   519                             } while (reader.ch == '_');
   520                             if (reader.digit(pos, 10) < 0) {
   521                                 lexError(savePos, "illegal.underscore");
   522                             }
   523                         }
   524                         scanNumber(pos, 8);
   525                     }
   526                     break loop;
   527                 case '1': case '2': case '3': case '4':
   528                 case '5': case '6': case '7': case '8': case '9':
   529                     scanNumber(pos, 10);
   530                     break loop;
   531                 case '.':
   532                     reader.scanChar();
   533                     if ('0' <= reader.ch && reader.ch <= '9') {
   534                         reader.putChar('.');
   535                         scanFractionAndSuffix(pos);
   536                     } else if (reader.ch == '.') {
   537                         int savePos = reader.bp;
   538                         reader.putChar('.'); reader.putChar('.', true);
   539                         if (reader.ch == '.') {
   540                             reader.scanChar();
   541                             reader.putChar('.');
   542                             tk = TokenKind.ELLIPSIS;
   543                         } else {
   544                             lexError(savePos, "illegal.dot");
   545                         }
   546                     } else {
   547                         tk = TokenKind.DOT;
   548                     }
   549                     break loop;
   550                 case ',':
   551                     reader.scanChar(); tk = TokenKind.COMMA; break loop;
   552                 case ';':
   553                     reader.scanChar(); tk = TokenKind.SEMI; break loop;
   554                 case '(':
   555                     reader.scanChar(); tk = TokenKind.LPAREN; break loop;
   556                 case ')':
   557                     reader.scanChar(); tk = TokenKind.RPAREN; break loop;
   558                 case '[':
   559                     reader.scanChar(); tk = TokenKind.LBRACKET; break loop;
   560                 case ']':
   561                     reader.scanChar(); tk = TokenKind.RBRACKET; break loop;
   562                 case '{':
   563                     reader.scanChar(); tk = TokenKind.LBRACE; break loop;
   564                 case '}':
   565                     reader.scanChar(); tk = TokenKind.RBRACE; break loop;
   566                 case '/':
   567                     reader.scanChar();
   568                     if (reader.ch == '/') {
   569                         do {
   570                             reader.scanCommentChar();
   571                         } while (reader.ch != CR && reader.ch != LF && reader.bp < reader.buflen);
   572                         if (reader.bp < reader.buflen) {
   573                             comments = addComment(comments, processComment(pos, reader.bp, CommentStyle.LINE));
   574                         }
   575                         break;
   576                     } else if (reader.ch == '*') {
   577                         boolean isEmpty = false;
   578                         reader.scanChar();
   579                         CommentStyle style;
   580                         if (reader.ch == '*') {
   581                             style = CommentStyle.JAVADOC;
   582                             reader.scanCommentChar();
   583                             if (reader.ch == '/') {
   584                                 isEmpty = true;
   585                             }
   586                         } else {
   587                             style = CommentStyle.BLOCK;
   588                         }
   589                         while (!isEmpty && reader.bp < reader.buflen) {
   590                             if (reader.ch == '*') {
   591                                 reader.scanChar();
   592                                 if (reader.ch == '/') break;
   593                             } else {
   594                                 reader.scanCommentChar();
   595                             }
   596                         }
   597                         if (reader.ch == '/') {
   598                             reader.scanChar();
   599                             comments = addComment(comments, processComment(pos, reader.bp, style));
   600                             break;
   601                         } else {
   602                             lexError(pos, "unclosed.comment");
   603                             break loop;
   604                         }
   605                     } else if (reader.ch == '=') {
   606                         tk = TokenKind.SLASHEQ;
   607                         reader.scanChar();
   608                     } else {
   609                         tk = TokenKind.SLASH;
   610                     }
   611                     break loop;
   612                 case '\'':
   613                     reader.scanChar();
   614                     if (reader.ch == '\'') {
   615                         lexError(pos, "empty.char.lit");
   616                     } else {
   617                         if (reader.ch == CR || reader.ch == LF)
   618                             lexError(pos, "illegal.line.end.in.char.lit");
   619                         scanLitChar(pos);
   620                         char ch2 = reader.ch;
   621                         if (reader.ch == '\'') {
   622                             reader.scanChar();
   623                             tk = TokenKind.CHARLITERAL;
   624                         } else {
   625                             lexError(pos, "unclosed.char.lit");
   626                         }
   627                     }
   628                     break loop;
   629                 case '\"':
   630                     reader.scanChar();
   631                     while (reader.ch != '\"' && reader.ch != CR && reader.ch != LF && reader.bp < reader.buflen)
   632                         scanLitChar(pos);
   633                     if (reader.ch == '\"') {
   634                         tk = TokenKind.STRINGLITERAL;
   635                         reader.scanChar();
   636                     } else {
   637                         lexError(pos, "unclosed.str.lit");
   638                     }
   639                     break loop;
   640                 default:
   641                     if (isSpecial(reader.ch)) {
   642                         scanOperator();
   643                     } else {
   644                         boolean isJavaIdentifierStart;
   645                         if (reader.ch < '\u0080') {
   646                             // all ASCII range chars already handled, above
   647                             isJavaIdentifierStart = false;
   648                         } else {
   649                             char high = reader.scanSurrogates();
   650                             if (high != 0) {
   651                                 reader.putChar(high);
   653                                 isJavaIdentifierStart = Character.isJavaIdentifierStart(
   654                                     Character.toCodePoint(high, reader.ch));
   655                             } else {
   656                                 isJavaIdentifierStart = Character.isJavaIdentifierStart(reader.ch);
   657                             }
   658                         }
   659                         if (isJavaIdentifierStart) {
   660                             scanIdent();
   661                         } else if (reader.bp == reader.buflen || reader.ch == EOI && reader.bp + 1 == reader.buflen) { // JLS 3.5
   662                             tk = TokenKind.EOF;
   663                             pos = reader.buflen;
   664                         } else {
   665                             lexError(pos, "illegal.char", String.valueOf((int)reader.ch));
   666                             reader.scanChar();
   667                         }
   668                     }
   669                     break loop;
   670                 }
   671             }
   672             endPos = reader.bp;
   673             switch (tk.tag) {
   674                 case DEFAULT: return new Token(tk, pos, endPos, comments);
   675                 case NAMED: return new NamedToken(tk, pos, endPos, name, comments);
   676                 case STRING: return new StringToken(tk, pos, endPos, reader.chars(), comments);
   677                 case NUMERIC: return new NumericToken(tk, pos, endPos, reader.chars(), radix, comments);
   678                 default: throw new AssertionError();
   679             }
   680         }
   681         finally {
   682             if (scannerDebug) {
   683                     System.out.println("nextToken(" + pos
   684                                        + "," + endPos + ")=|" +
   685                                        new String(reader.getRawCharacters(pos, endPos))
   686                                        + "|");
   687             }
   688         }
   689     }
   690     //where
   691         List<Comment> addComment(List<Comment> comments, Comment comment) {
   692             return comments == null ?
   693                     List.of(comment) :
   694                     comments.prepend(comment);
   695         }
   697     /** Return the position where a lexical error occurred;
   698      */
   699     public int errPos() {
   700         return errPos;
   701     }
   703     /** Set the position where a lexical error occurred;
   704      */
   705     public void errPos(int pos) {
   706         errPos = pos;
   707     }
   709     /**
   710      * Called when a complete comment has been scanned. pos and endPos
   711      * will mark the comment boundary.
   712      */
   713     protected Tokens.Comment processComment(int pos, int endPos, CommentStyle style) {
   714         if (scannerDebug)
   715             System.out.println("processComment(" + pos
   716                                + "," + endPos + "," + style + ")=|"
   717                                + new String(reader.getRawCharacters(pos, endPos))
   718                                + "|");
   719         char[] buf = reader.getRawCharacters(pos, endPos);
   720         return new BasicComment<UnicodeReader>(new UnicodeReader(fac, buf, buf.length), style);
   721     }
   723     /**
   724      * Called when a complete whitespace run has been scanned. pos and endPos
   725      * will mark the whitespace boundary.
   726      */
   727     protected void processWhiteSpace(int pos, int endPos) {
   728         if (scannerDebug)
   729             System.out.println("processWhitespace(" + pos
   730                                + "," + endPos + ")=|" +
   731                                new String(reader.getRawCharacters(pos, endPos))
   732                                + "|");
   733     }
   735     /**
   736      * Called when a line terminator has been processed.
   737      */
   738     protected void processLineTerminator(int pos, int endPos) {
   739         if (scannerDebug)
   740             System.out.println("processTerminator(" + pos
   741                                + "," + endPos + ")=|" +
   742                                new String(reader.getRawCharacters(pos, endPos))
   743                                + "|");
   744     }
   746     /** Build a map for translating between line numbers and
   747      * positions in the input.
   748      *
   749      * @return a LineMap */
   750     public Position.LineMap getLineMap() {
   751         return Position.makeLineMap(reader.getRawCharacters(), reader.buflen, false);
   752     }
   755     /**
   756     * Scan a documentation comment; determine if a deprecated tag is present.
   757     * Called once the initial /, * have been skipped, positioned at the second *
   758     * (which is treated as the beginning of the first line).
   759     * Stops positioned at the closing '/'.
   760     */
   761     protected class BasicComment<U extends UnicodeReader> implements Comment {
   763         CommentStyle cs;
   764         U comment_reader;
   766         protected boolean deprecatedFlag = false;
   767         protected boolean scanned = false;
   769         protected BasicComment(U comment_reader, CommentStyle cs) {
   770             this.comment_reader = comment_reader;
   771             this.cs = cs;
   772         }
   774         public String getText() {
   775             return null;
   776         }
   778         public int getSourcePos(int pos) {
   779             return -1;
   780         }
   782         public CommentStyle getStyle() {
   783             return cs;
   784         }
   786         public boolean isDeprecated() {
   787             if (!scanned && cs == CommentStyle.JAVADOC) {
   788                 scanDocComment();
   789             }
   790             return deprecatedFlag;
   791         }
   793         @SuppressWarnings("fallthrough")
   794         protected void scanDocComment() {
   795             try {
   796                 boolean deprecatedPrefix = false;
   798                 comment_reader.bp += 3; // '/**'
   799                 comment_reader.ch = comment_reader.buf[comment_reader.bp];
   801                 forEachLine:
   802                 while (comment_reader.bp < comment_reader.buflen) {
   804                     // Skip optional WhiteSpace at beginning of line
   805                     while (comment_reader.bp < comment_reader.buflen && (comment_reader.ch == ' ' || comment_reader.ch == '\t' || comment_reader.ch == FF)) {
   806                         comment_reader.scanCommentChar();
   807                     }
   809                     // Skip optional consecutive Stars
   810                     while (comment_reader.bp < comment_reader.buflen && comment_reader.ch == '*') {
   811                         comment_reader.scanCommentChar();
   812                         if (comment_reader.ch == '/') {
   813                             return;
   814                         }
   815                     }
   817                     // Skip optional WhiteSpace after Stars
   818                     while (comment_reader.bp < comment_reader.buflen && (comment_reader.ch == ' ' || comment_reader.ch == '\t' || comment_reader.ch == FF)) {
   819                         comment_reader.scanCommentChar();
   820                     }
   822                     deprecatedPrefix = false;
   823                     // At beginning of line in the JavaDoc sense.
   824                     if (!deprecatedFlag) {
   825                         String deprecated = "@deprecated";
   826                         int i = 0;
   827                         while (comment_reader.bp < comment_reader.buflen && comment_reader.ch == deprecated.charAt(i)) {
   828                             comment_reader.scanCommentChar();
   829                             i++;
   830                             if (i == deprecated.length()) {
   831                                 deprecatedPrefix = true;
   832                                 break;
   833                             }
   834                         }
   835                     }
   837                     if (deprecatedPrefix && comment_reader.bp < comment_reader.buflen) {
   838                         if (Character.isWhitespace(comment_reader.ch)) {
   839                             deprecatedFlag = true;
   840                         } else if (comment_reader.ch == '*') {
   841                             comment_reader.scanCommentChar();
   842                             if (comment_reader.ch == '/') {
   843                                 deprecatedFlag = true;
   844                                 return;
   845                             }
   846                         }
   847                     }
   849                     // Skip rest of line
   850                     while (comment_reader.bp < comment_reader.buflen) {
   851                         switch (comment_reader.ch) {
   852                             case '*':
   853                                 comment_reader.scanCommentChar();
   854                                 if (comment_reader.ch == '/') {
   855                                     return;
   856                                 }
   857                                 break;
   858                             case CR: // (Spec 3.4)
   859                                 comment_reader.scanCommentChar();
   860                                 if (comment_reader.ch != LF) {
   861                                     continue forEachLine;
   862                                 }
   863                             /* fall through to LF case */
   864                             case LF: // (Spec 3.4)
   865                                 comment_reader.scanCommentChar();
   866                                 continue forEachLine;
   867                             default:
   868                                 comment_reader.scanCommentChar();
   869                         }
   870                     } // rest of line
   871                 } // forEachLine
   872                 return;
   873             } finally {
   874                 scanned = true;
   875             }
   876         }
   877     }
   878 }

mercurial