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

Fri, 04 Nov 2011 12:36:40 +0000

author
mcimadamore
date
Fri, 04 Nov 2011 12:36:40 +0000
changeset 1125
56830d5cb5bb
parent 1113
d346ab55031b
child 1145
3343b22e2761
permissions
-rw-r--r--

7104201: Refactor DocCommentScanner
Summary: Add new Comment helper class to parse contents of comments in source code
Reviewed-by: jjg

     1 /*
     2  * Copyright (c) 1999, 2011, 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 input the input, might be modified
   112      * @param inputLength the size of the input.
   113      * Must be positive and less than or equal to input.length.
   114      */
   115     protected JavaTokenizer(ScannerFactory fac, CharBuffer buf) {
   116         this(fac, new UnicodeReader(fac, buf));
   117     }
   119     protected JavaTokenizer(ScannerFactory fac, char[] buf, int inputLength) {
   120         this(fac, new UnicodeReader(fac, buf, inputLength));
   121     }
   123     protected JavaTokenizer(ScannerFactory fac, UnicodeReader reader) {
   124         this.fac = fac;
   125         this.log = fac.log;
   126         this.tokens = fac.tokens;
   127         this.source = fac.source;
   128         this.reader = reader;
   129         this.allowBinaryLiterals = source.allowBinaryLiterals();
   130         this.allowHexFloats = source.allowHexFloats();
   131         this.allowUnderscoresInLiterals = source.allowUnderscoresInLiterals();
   132     }
   134     /** Report an error at the given position using the provided arguments.
   135      */
   136     protected void lexError(int pos, String key, Object... args) {
   137         log.error(pos, key, args);
   138         tk = TokenKind.ERROR;
   139         errPos = pos;
   140     }
   142     /** Read next character in character or string literal and copy into sbuf.
   143      */
   144     private void scanLitChar(int pos) {
   145         if (reader.ch == '\\') {
   146             if (reader.peekChar() == '\\' && !reader.isUnicode()) {
   147                 reader.skipChar();
   148                 reader.putChar('\\', true);
   149             } else {
   150                 reader.scanChar();
   151                 switch (reader.ch) {
   152                 case '0': case '1': case '2': case '3':
   153                 case '4': case '5': case '6': case '7':
   154                     char leadch = reader.ch;
   155                     int oct = reader.digit(pos, 8);
   156                     reader.scanChar();
   157                     if ('0' <= reader.ch && reader.ch <= '7') {
   158                         oct = oct * 8 + reader.digit(pos, 8);
   159                         reader.scanChar();
   160                         if (leadch <= '3' && '0' <= reader.ch && reader.ch <= '7') {
   161                             oct = oct * 8 + reader.digit(pos, 8);
   162                             reader.scanChar();
   163                         }
   164                     }
   165                     reader.putChar((char)oct);
   166                     break;
   167                 case 'b':
   168                     reader.putChar('\b', true); break;
   169                 case 't':
   170                     reader.putChar('\t', true); break;
   171                 case 'n':
   172                     reader.putChar('\n', true); break;
   173                 case 'f':
   174                     reader.putChar('\f', true); break;
   175                 case 'r':
   176                     reader.putChar('\r', true); break;
   177                 case '\'':
   178                     reader.putChar('\'', true); break;
   179                 case '\"':
   180                     reader.putChar('\"', true); break;
   181                 case '\\':
   182                     reader.putChar('\\', true); break;
   183                 default:
   184                     lexError(reader.bp, "illegal.esc.char");
   185                 }
   186             }
   187         } else if (reader.bp != reader.buflen) {
   188             reader.putChar(true);
   189         }
   190     }
   192     private void scanDigits(int pos, int digitRadix) {
   193         char saveCh;
   194         int savePos;
   195         do {
   196             if (reader.ch != '_') {
   197                 reader.putChar(false);
   198             } else {
   199                 if (!allowUnderscoresInLiterals) {
   200                     lexError(pos, "unsupported.underscore.lit", source.name);
   201                     allowUnderscoresInLiterals = true;
   202                 }
   203             }
   204             saveCh = reader.ch;
   205             savePos = reader.bp;
   206             reader.scanChar();
   207         } while (reader.digit(pos, digitRadix) >= 0 || reader.ch == '_');
   208         if (saveCh == '_')
   209             lexError(savePos, "illegal.underscore");
   210     }
   212     /** Read fractional part of hexadecimal floating point number.
   213      */
   214     private void scanHexExponentAndSuffix(int pos) {
   215         if (reader.ch == 'p' || reader.ch == 'P') {
   216             reader.putChar(true);
   217             skipIllegalUnderscores();
   218             if (reader.ch == '+' || reader.ch == '-') {
   219                 reader.putChar(true);
   220             }
   221             skipIllegalUnderscores();
   222             if ('0' <= reader.ch && reader.ch <= '9') {
   223                 scanDigits(pos, 10);
   224                 if (!allowHexFloats) {
   225                     lexError(pos, "unsupported.fp.lit", source.name);
   226                     allowHexFloats = true;
   227                 }
   228                 else if (!hexFloatsWork)
   229                     lexError(pos, "unsupported.cross.fp.lit");
   230             } else
   231                 lexError(pos, "malformed.fp.lit");
   232         } else {
   233             lexError(pos, "malformed.fp.lit");
   234         }
   235         if (reader.ch == 'f' || reader.ch == 'F') {
   236             reader.putChar(true);
   237             tk = TokenKind.FLOATLITERAL;
   238             radix = 16;
   239         } else {
   240             if (reader.ch == 'd' || reader.ch == 'D') {
   241                 reader.putChar(true);
   242             }
   243             tk = TokenKind.DOUBLELITERAL;
   244             radix = 16;
   245         }
   246     }
   248     /** Read fractional part of floating point number.
   249      */
   250     private void scanFraction(int pos) {
   251         skipIllegalUnderscores();
   252         if ('0' <= reader.ch && reader.ch <= '9') {
   253             scanDigits(pos, 10);
   254         }
   255         int sp1 = reader.sp;
   256         if (reader.ch == 'e' || reader.ch == 'E') {
   257             reader.putChar(true);
   258             skipIllegalUnderscores();
   259             if (reader.ch == '+' || reader.ch == '-') {
   260                 reader.putChar(true);
   261             }
   262             skipIllegalUnderscores();
   263             if ('0' <= reader.ch && reader.ch <= '9') {
   264                 scanDigits(pos, 10);
   265                 return;
   266             }
   267             lexError(pos, "malformed.fp.lit");
   268             reader.sp = sp1;
   269         }
   270     }
   272     /** Read fractional part and 'd' or 'f' suffix of floating point number.
   273      */
   274     private void scanFractionAndSuffix(int pos) {
   275         radix = 10;
   276         scanFraction(pos);
   277         if (reader.ch == 'f' || reader.ch == 'F') {
   278             reader.putChar(true);
   279             tk = TokenKind.FLOATLITERAL;
   280         } else {
   281             if (reader.ch == 'd' || reader.ch == 'D') {
   282                 reader.putChar(true);
   283             }
   284             tk = TokenKind.DOUBLELITERAL;
   285         }
   286     }
   288     /** Read fractional part and 'd' or 'f' suffix of floating point number.
   289      */
   290     private void scanHexFractionAndSuffix(int pos, boolean seendigit) {
   291         radix = 16;
   292         Assert.check(reader.ch == '.');
   293         reader.putChar(true);
   294         skipIllegalUnderscores();
   295         if (reader.digit(pos, 16) >= 0) {
   296             seendigit = true;
   297             scanDigits(pos, 16);
   298         }
   299         if (!seendigit)
   300             lexError(pos, "invalid.hex.number");
   301         else
   302             scanHexExponentAndSuffix(pos);
   303     }
   305     private void skipIllegalUnderscores() {
   306         if (reader.ch == '_') {
   307             lexError(reader.bp, "illegal.underscore");
   308             while (reader.ch == '_')
   309                 reader.scanChar();
   310         }
   311     }
   313     /** Read a number.
   314      *  @param radix  The radix of the number; one of 2, j8, 10, 16.
   315      */
   316     private void scanNumber(int pos, int radix) {
   317         // for octal, allow base-10 digit in case it's a float literal
   318         this.radix = radix;
   319         int digitRadix = (radix == 8 ? 10 : radix);
   320         boolean seendigit = false;
   321         if (reader.digit(pos, digitRadix) >= 0) {
   322             seendigit = true;
   323             scanDigits(pos, digitRadix);
   324         }
   325         if (radix == 16 && reader.ch == '.') {
   326             scanHexFractionAndSuffix(pos, seendigit);
   327         } else if (seendigit && radix == 16 && (reader.ch == 'p' || reader.ch == 'P')) {
   328             scanHexExponentAndSuffix(pos);
   329         } else if (digitRadix == 10 && reader.ch == '.') {
   330             reader.putChar(true);
   331             scanFractionAndSuffix(pos);
   332         } else if (digitRadix == 10 &&
   333                    (reader.ch == 'e' || reader.ch == 'E' ||
   334                     reader.ch == 'f' || reader.ch == 'F' ||
   335                     reader.ch == 'd' || reader.ch == 'D')) {
   336             scanFractionAndSuffix(pos);
   337         } else {
   338             if (reader.ch == 'l' || reader.ch == 'L') {
   339                 reader.scanChar();
   340                 tk = TokenKind.LONGLITERAL;
   341             } else {
   342                 tk = TokenKind.INTLITERAL;
   343             }
   344         }
   345     }
   347     /** Read an identifier.
   348      */
   349     private void scanIdent() {
   350         boolean isJavaIdentifierPart;
   351         char high;
   352         do {
   353             reader.putChar(true);
   354             switch (reader.ch) {
   355             case 'A': case 'B': case 'C': case 'D': case 'E':
   356             case 'F': case 'G': case 'H': case 'I': case 'J':
   357             case 'K': case 'L': case 'M': case 'N': case 'O':
   358             case 'P': case 'Q': case 'R': case 'S': case 'T':
   359             case 'U': case 'V': case 'W': case 'X': case 'Y':
   360             case 'Z':
   361             case 'a': case 'b': case 'c': case 'd': case 'e':
   362             case 'f': case 'g': case 'h': case 'i': case 'j':
   363             case 'k': case 'l': case 'm': case 'n': case 'o':
   364             case 'p': case 'q': case 'r': case 's': case 't':
   365             case 'u': case 'v': case 'w': case 'x': case 'y':
   366             case 'z':
   367             case '$': case '_':
   368             case '0': case '1': case '2': case '3': case '4':
   369             case '5': case '6': case '7': case '8': case '9':
   370             case '\u0000': case '\u0001': case '\u0002': case '\u0003':
   371             case '\u0004': case '\u0005': case '\u0006': case '\u0007':
   372             case '\u0008': case '\u000E': case '\u000F': case '\u0010':
   373             case '\u0011': case '\u0012': case '\u0013': case '\u0014':
   374             case '\u0015': case '\u0016': case '\u0017':
   375             case '\u0018': case '\u0019': case '\u001B':
   376             case '\u007F':
   377                 break;
   378             case '\u001A': // EOI is also a legal identifier part
   379                 if (reader.bp >= reader.buflen) {
   380                     name = reader.name();
   381                     tk = tokens.lookupKind(name);
   382                     return;
   383                 }
   384                 break;
   385             default:
   386                 if (reader.ch < '\u0080') {
   387                     // all ASCII range chars already handled, above
   388                     isJavaIdentifierPart = false;
   389                 } else {
   390                     high = reader.scanSurrogates();
   391                     if (high != 0) {
   392                         reader.putChar(high);
   393                         isJavaIdentifierPart = Character.isJavaIdentifierPart(
   394                             Character.toCodePoint(high, reader.ch));
   395                     } else {
   396                         isJavaIdentifierPart = Character.isJavaIdentifierPart(reader.ch);
   397                     }
   398                 }
   399                 if (!isJavaIdentifierPart) {
   400                     name = reader.name();
   401                     tk = tokens.lookupKind(name);
   402                     return;
   403                 }
   404             }
   405         } while (true);
   406     }
   408     /** Return true if reader.ch can be part of an operator.
   409      */
   410     private boolean isSpecial(char ch) {
   411         switch (ch) {
   412         case '!': case '%': case '&': case '*': case '?':
   413         case '+': case '-': case ':': case '<': case '=':
   414         case '>': case '^': case '|': case '~':
   415         case '@':
   416             return true;
   417         default:
   418             return false;
   419         }
   420     }
   422     /** Read longest possible sequence of special characters and convert
   423      *  to token.
   424      */
   425     private void scanOperator() {
   426         while (true) {
   427             reader.putChar(false);
   428             Name newname = reader.name();
   429             TokenKind tk1 = tokens.lookupKind(newname);
   430             if (tk1 == TokenKind.IDENTIFIER) {
   431                 reader.sp--;
   432                 break;
   433             }
   434             tk = tk1;
   435             reader.scanChar();
   436             if (!isSpecial(reader.ch)) break;
   437         }
   438     }
   440     /** Read token.
   441      */
   442     public Token readToken() {
   444         reader.sp = 0;
   445         name = null;
   446         radix = 0;
   448         int pos = 0;
   449         int endPos = 0;
   450         List<Comment> comments = null;
   452         try {
   453             loop: while (true) {
   454                 pos = reader.bp;
   455                 switch (reader.ch) {
   456                 case ' ': // (Spec 3.6)
   457                 case '\t': // (Spec 3.6)
   458                 case FF: // (Spec 3.6)
   459                     do {
   460                         reader.scanChar();
   461                     } while (reader.ch == ' ' || reader.ch == '\t' || reader.ch == FF);
   462                     processWhiteSpace(pos, reader.bp);
   463                     break;
   464                 case LF: // (Spec 3.4)
   465                     reader.scanChar();
   466                     processLineTerminator(pos, reader.bp);
   467                     break;
   468                 case CR: // (Spec 3.4)
   469                     reader.scanChar();
   470                     if (reader.ch == LF) {
   471                         reader.scanChar();
   472                     }
   473                     processLineTerminator(pos, reader.bp);
   474                     break;
   475                 case 'A': case 'B': case 'C': case 'D': case 'E':
   476                 case 'F': case 'G': case 'H': case 'I': case 'J':
   477                 case 'K': case 'L': case 'M': case 'N': case 'O':
   478                 case 'P': case 'Q': case 'R': case 'S': case 'T':
   479                 case 'U': case 'V': case 'W': case 'X': case 'Y':
   480                 case 'Z':
   481                 case 'a': case 'b': case 'c': case 'd': case 'e':
   482                 case 'f': case 'g': case 'h': case 'i': case 'j':
   483                 case 'k': case 'l': case 'm': case 'n': case 'o':
   484                 case 'p': case 'q': case 'r': case 's': case 't':
   485                 case 'u': case 'v': case 'w': case 'x': case 'y':
   486                 case 'z':
   487                 case '$': case '_':
   488                     scanIdent();
   489                     break loop;
   490                 case '0':
   491                     reader.scanChar();
   492                     if (reader.ch == 'x' || reader.ch == 'X') {
   493                         reader.scanChar();
   494                         skipIllegalUnderscores();
   495                         if (reader.ch == '.') {
   496                             scanHexFractionAndSuffix(pos, false);
   497                         } else if (reader.digit(pos, 16) < 0) {
   498                             lexError(pos, "invalid.hex.number");
   499                         } else {
   500                             scanNumber(pos, 16);
   501                         }
   502                     } else if (reader.ch == 'b' || reader.ch == 'B') {
   503                         if (!allowBinaryLiterals) {
   504                             lexError(pos, "unsupported.binary.lit", source.name);
   505                             allowBinaryLiterals = true;
   506                         }
   507                         reader.scanChar();
   508                         skipIllegalUnderscores();
   509                         if (reader.digit(pos, 2) < 0) {
   510                             lexError(pos, "invalid.binary.number");
   511                         } else {
   512                             scanNumber(pos, 2);
   513                         }
   514                     } else {
   515                         reader.putChar('0');
   516                         if (reader.ch == '_') {
   517                             int savePos = reader.bp;
   518                             do {
   519                                 reader.scanChar();
   520                             } while (reader.ch == '_');
   521                             if (reader.digit(pos, 10) < 0) {
   522                                 lexError(savePos, "illegal.underscore");
   523                             }
   524                         }
   525                         scanNumber(pos, 8);
   526                     }
   527                     break loop;
   528                 case '1': case '2': case '3': case '4':
   529                 case '5': case '6': case '7': case '8': case '9':
   530                     scanNumber(pos, 10);
   531                     break loop;
   532                 case '.':
   533                     reader.scanChar();
   534                     if ('0' <= reader.ch && reader.ch <= '9') {
   535                         reader.putChar('.');
   536                         scanFractionAndSuffix(pos);
   537                     } else if (reader.ch == '.') {
   538                         reader.putChar('.'); reader.putChar('.', true);
   539                         if (reader.ch == '.') {
   540                             reader.scanChar();
   541                             reader.putChar('.');
   542                             tk = TokenKind.ELLIPSIS;
   543                         } else {
   544                             lexError(pos, "malformed.fp.lit");
   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 = addDocReader(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 = addDocReader(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> addDocReader(List<Comment> docReaders, Comment docReader) {
   692             return docReaders == null ?
   693                     List.of(docReader) :
   694                     docReaders.prepend(docReader);
   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 CommentStyle getStyle() {
   779             return cs;
   780         }
   782         public boolean isDeprecated() {
   783             if (!scanned && cs == CommentStyle.JAVADOC) {
   784                 scanDocComment();
   785             }
   786             return deprecatedFlag;
   787         }
   789         @SuppressWarnings("fallthrough")
   790         protected void scanDocComment() {
   791             try {
   792                 boolean deprecatedPrefix = false;
   794                 comment_reader.bp += 3; // '/**'
   795                 comment_reader.ch = comment_reader.buf[comment_reader.bp];
   797                 forEachLine:
   798                 while (comment_reader.bp < comment_reader.buflen) {
   800                     // Skip optional WhiteSpace at beginning of line
   801                     while (comment_reader.bp < comment_reader.buflen && (comment_reader.ch == ' ' || comment_reader.ch == '\t' || comment_reader.ch == FF)) {
   802                         comment_reader.scanCommentChar();
   803                     }
   805                     // Skip optional consecutive Stars
   806                     while (comment_reader.bp < comment_reader.buflen && comment_reader.ch == '*') {
   807                         comment_reader.scanCommentChar();
   808                         if (comment_reader.ch == '/') {
   809                             return;
   810                         }
   811                     }
   813                     // Skip optional WhiteSpace after Stars
   814                     while (comment_reader.bp < comment_reader.buflen && (comment_reader.ch == ' ' || comment_reader.ch == '\t' || comment_reader.ch == FF)) {
   815                         comment_reader.scanCommentChar();
   816                     }
   818                     deprecatedPrefix = false;
   819                     // At beginning of line in the JavaDoc sense.
   820                     if (!deprecatedFlag) {
   821                         String deprecated = "@deprecated";
   822                         int i = 0;
   823                         while (comment_reader.bp < comment_reader.buflen && comment_reader.ch == deprecated.charAt(i)) {
   824                             comment_reader.scanCommentChar();
   825                             i++;
   826                             if (i == deprecated.length()) {
   827                                 deprecatedPrefix = true;
   828                                 break;
   829                             }
   830                         }
   831                     }
   833                     if (deprecatedPrefix && comment_reader.bp < comment_reader.buflen) {
   834                         if (Character.isWhitespace(comment_reader.ch)) {
   835                             deprecatedFlag = true;
   836                         } else if (comment_reader.ch == '*') {
   837                             comment_reader.scanCommentChar();
   838                             if (comment_reader.ch == '/') {
   839                                 deprecatedFlag = true;
   840                                 return;
   841                             }
   842                         }
   843                     }
   845                     // Skip rest of line
   846                     while (comment_reader.bp < comment_reader.buflen) {
   847                         switch (comment_reader.ch) {
   848                             case '*':
   849                                 comment_reader.scanCommentChar();
   850                                 if (comment_reader.ch == '/') {
   851                                     return;
   852                                 }
   853                                 break;
   854                             case CR: // (Spec 3.4)
   855                                 comment_reader.scanCommentChar();
   856                                 if (comment_reader.ch != LF) {
   857                                     continue forEachLine;
   858                                 }
   859                             /* fall through to LF case */
   860                             case LF: // (Spec 3.4)
   861                                 comment_reader.scanCommentChar();
   862                                 continue forEachLine;
   863                             default:
   864                                 comment_reader.scanCommentChar();
   865                         }
   866                     } // rest of line
   867                 } // forEachLine
   868                 return;
   869             } finally {
   870                 scanned = true;
   871             }
   872         }
   873     }
   874 }

mercurial