0N/A/*
815N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
553N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
553N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
553N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
553N/A * or visit www.oracle.com if you need additional information or have any
553N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.tools.javac.parser;
0N/A
0N/Aimport java.nio.*;
0N/A
49N/Aimport com.sun.tools.javac.code.Source;
49N/Aimport com.sun.tools.javac.file.JavacFileManager;
0N/Aimport com.sun.tools.javac.util.*;
0N/A
0N/A
0N/Aimport static com.sun.tools.javac.parser.Token.*;
0N/Aimport static com.sun.tools.javac.util.LayoutCharacters.*;
0N/A
0N/A/** The lexical analyzer maps an input stream consisting of
0N/A * ASCII characters and Unicode escapes into a token sequence.
0N/A *
580N/A * <p><b>This is NOT part of any supported API.
580N/A * If you write code that depends on this, you do so at your own risk.
0N/A * This code and its internal interfaces are subject to change or
0N/A * deletion without notice.</b>
0N/A */
0N/Apublic class Scanner implements Lexer {
0N/A
0N/A private static boolean scannerDebug = false;
0N/A
0N/A /* Output variables; set by nextToken():
0N/A */
0N/A
0N/A /** The token, set by nextToken().
0N/A */
0N/A private Token token;
0N/A
0N/A /** Allow hex floating-point literals.
0N/A */
0N/A private boolean allowHexFloats;
0N/A
408N/A /** Allow binary literals.
408N/A */
408N/A private boolean allowBinaryLiterals;
408N/A
408N/A /** Allow underscores in literals.
408N/A */
408N/A private boolean allowUnderscoresInLiterals;
408N/A
408N/A /** The source language setting.
408N/A */
408N/A private Source source;
408N/A
0N/A /** The token's position, 0-based offset from beginning of text.
0N/A */
0N/A private int pos;
0N/A
0N/A /** Character position just after the last character of the token.
0N/A */
0N/A private int endPos;
0N/A
0N/A /** The last character position of the previous token.
0N/A */
0N/A private int prevEndPos;
0N/A
0N/A /** The position where a lexical error occurred;
0N/A */
0N/A private int errPos = Position.NOPOS;
0N/A
0N/A /** The name of an identifier or token:
0N/A */
0N/A private Name name;
0N/A
0N/A /** The radix of a numeric literal token.
0N/A */
0N/A private int radix;
0N/A
0N/A /** Has a @deprecated been encountered in last doc comment?
0N/A * this needs to be reset by client.
0N/A */
0N/A protected boolean deprecatedFlag = false;
0N/A
0N/A /** A character buffer for literals.
0N/A */
0N/A private char[] sbuf = new char[128];
0N/A private int sp;
0N/A
0N/A /** The input buffer, index of next chacter to be read,
0N/A * index of one past last character in buffer.
0N/A */
0N/A private char[] buf;
0N/A private int bp;
0N/A private int buflen;
0N/A private int eofPos;
0N/A
0N/A /** The current character.
0N/A */
0N/A private char ch;
0N/A
0N/A /** The buffer index of the last converted unicode character
0N/A */
0N/A private int unicodeConversionBp = -1;
0N/A
0N/A /** The log to be used for error reporting.
0N/A */
0N/A private final Log log;
0N/A
0N/A /** The name table. */
112N/A private final Names names;
0N/A
0N/A /** The keyword table. */
0N/A private final Keywords keywords;
0N/A
0N/A /** Common code for constructors. */
694N/A private Scanner(ScannerFactory fac) {
408N/A log = fac.log;
408N/A names = fac.names;
408N/A keywords = fac.keywords;
408N/A source = fac.source;
408N/A allowBinaryLiterals = source.allowBinaryLiterals();
408N/A allowHexFloats = source.allowHexFloats();
858N/A allowUnderscoresInLiterals = source.allowUnderscoresInLiterals();
0N/A }
0N/A
0N/A private static final boolean hexFloatsWork = hexFloatsWork();
0N/A private static boolean hexFloatsWork() {
0N/A try {
0N/A Float.valueOf("0x1.0p1");
0N/A return true;
0N/A } catch (NumberFormatException ex) {
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A /** Create a scanner from the input buffer. buffer must implement
0N/A * array() and compact(), and remaining() must be less than limit().
0N/A */
694N/A protected Scanner(ScannerFactory fac, CharBuffer buffer) {
0N/A this(fac, JavacFileManager.toArray(buffer), buffer.limit());
0N/A }
0N/A
0N/A /**
0N/A * Create a scanner from the input array. This method might
0N/A * modify the array. To avoid copying the input array, ensure
0N/A * that {@code inputLength < input.length} or
0N/A * {@code input[input.length -1]} is a white space character.
0N/A *
0N/A * @param fac the factory which created this Scanner
0N/A * @param input the input, might be modified
0N/A * @param inputLength the size of the input.
0N/A * Must be positive and less than or equal to input.length.
0N/A */
694N/A protected Scanner(ScannerFactory fac, char[] input, int inputLength) {
0N/A this(fac);
0N/A eofPos = inputLength;
0N/A if (inputLength == input.length) {
0N/A if (input.length > 0 && Character.isWhitespace(input[input.length - 1])) {
0N/A inputLength--;
0N/A } else {
0N/A char[] newInput = new char[inputLength + 1];
0N/A System.arraycopy(input, 0, newInput, 0, input.length);
0N/A input = newInput;
0N/A }
0N/A }
0N/A buf = input;
0N/A buflen = inputLength;
0N/A buf[buflen] = EOI;
0N/A bp = -1;
0N/A scanChar();
0N/A }
0N/A
0N/A /** Report an error at the given position using the provided arguments.
0N/A */
0N/A private void lexError(int pos, String key, Object... args) {
0N/A log.error(pos, key, args);
0N/A token = ERROR;
0N/A errPos = pos;
0N/A }
0N/A
0N/A /** Report an error at the current token position using the provided
0N/A * arguments.
0N/A */
0N/A private void lexError(String key, Object... args) {
0N/A lexError(pos, key, args);
0N/A }
0N/A
0N/A /** Convert an ASCII digit from its base (8, 10, or 16)
0N/A * to its value.
0N/A */
0N/A private int digit(int base) {
0N/A char c = ch;
0N/A int result = Character.digit(c, base);
0N/A if (result >= 0 && c > 0x7f) {
0N/A lexError(pos+1, "illegal.nonascii.digit");
0N/A ch = "0123456789abcdef".charAt(result);
0N/A }
0N/A return result;
0N/A }
0N/A
0N/A /** Convert unicode escape; bp points to initial '\' character
0N/A * (Spec 3.3).
0N/A */
0N/A private void convertUnicode() {
0N/A if (ch == '\\' && unicodeConversionBp != bp) {
0N/A bp++; ch = buf[bp];
0N/A if (ch == 'u') {
0N/A do {
0N/A bp++; ch = buf[bp];
0N/A } while (ch == 'u');
0N/A int limit = bp + 3;
0N/A if (limit < buflen) {
0N/A int d = digit(16);
0N/A int code = d;
0N/A while (bp < limit && d >= 0) {
0N/A bp++; ch = buf[bp];
0N/A d = digit(16);
0N/A code = (code << 4) + d;
0N/A }
0N/A if (d >= 0) {
0N/A ch = (char)code;
0N/A unicodeConversionBp = bp;
0N/A return;
0N/A }
0N/A }
0N/A lexError(bp, "illegal.unicode.esc");
0N/A } else {
0N/A bp--;
0N/A ch = '\\';
0N/A }
0N/A }
0N/A }
0N/A
0N/A /** Read next character.
0N/A */
0N/A private void scanChar() {
0N/A ch = buf[++bp];
0N/A if (ch == '\\') {
0N/A convertUnicode();
0N/A }
0N/A }
0N/A
0N/A /** Read next character in comment, skipping over double '\' characters.
0N/A */
0N/A private void scanCommentChar() {
0N/A scanChar();
0N/A if (ch == '\\') {
0N/A if (buf[bp+1] == '\\' && unicodeConversionBp != bp) {
0N/A bp++;
0N/A } else {
0N/A convertUnicode();
0N/A }
0N/A }
0N/A }
0N/A
0N/A /** Append a character to sbuf.
0N/A */
0N/A private void putChar(char ch) {
0N/A if (sp == sbuf.length) {
0N/A char[] newsbuf = new char[sbuf.length * 2];
0N/A System.arraycopy(sbuf, 0, newsbuf, 0, sbuf.length);
0N/A sbuf = newsbuf;
0N/A }
0N/A sbuf[sp++] = ch;
0N/A }
0N/A
0N/A /** Read next character in character or string literal and copy into sbuf.
0N/A */
751N/A private void scanLitChar() {
0N/A if (ch == '\\') {
0N/A if (buf[bp+1] == '\\' && unicodeConversionBp != bp) {
0N/A bp++;
0N/A putChar('\\');
0N/A scanChar();
0N/A } else {
0N/A scanChar();
0N/A switch (ch) {
0N/A case '0': case '1': case '2': case '3':
0N/A case '4': case '5': case '6': case '7':
0N/A char leadch = ch;
0N/A int oct = digit(8);
0N/A scanChar();
0N/A if ('0' <= ch && ch <= '7') {
0N/A oct = oct * 8 + digit(8);
0N/A scanChar();
0N/A if (leadch <= '3' && '0' <= ch && ch <= '7') {
0N/A oct = oct * 8 + digit(8);
0N/A scanChar();
0N/A }
0N/A }
0N/A putChar((char)oct);
0N/A break;
0N/A case 'b':
0N/A putChar('\b'); scanChar(); break;
0N/A case 't':
0N/A putChar('\t'); scanChar(); break;
0N/A case 'n':
0N/A putChar('\n'); scanChar(); break;
0N/A case 'f':
0N/A putChar('\f'); scanChar(); break;
0N/A case 'r':
0N/A putChar('\r'); scanChar(); break;
0N/A case '\'':
0N/A putChar('\''); scanChar(); break;
0N/A case '\"':
0N/A putChar('\"'); scanChar(); break;
0N/A case '\\':
0N/A putChar('\\'); scanChar(); break;
0N/A default:
0N/A lexError(bp, "illegal.esc.char");
0N/A }
0N/A }
0N/A } else if (bp != buflen) {
0N/A putChar(ch); scanChar();
0N/A }
0N/A }
0N/A
408N/A private void scanDigits(int digitRadix) {
408N/A char saveCh;
408N/A int savePos;
408N/A do {
408N/A if (ch != '_') {
408N/A putChar(ch);
408N/A } else {
408N/A if (!allowUnderscoresInLiterals) {
596N/A lexError("unsupported.underscore.lit", source.name);
408N/A allowUnderscoresInLiterals = true;
408N/A }
408N/A }
408N/A saveCh = ch;
408N/A savePos = bp;
408N/A scanChar();
408N/A } while (digit(digitRadix) >= 0 || ch == '_');
408N/A if (saveCh == '_')
408N/A lexError(savePos, "illegal.underscore");
408N/A }
408N/A
0N/A /** Read fractional part of hexadecimal floating point number.
0N/A */
0N/A private void scanHexExponentAndSuffix() {
0N/A if (ch == 'p' || ch == 'P') {
0N/A putChar(ch);
0N/A scanChar();
408N/A skipIllegalUnderscores();
0N/A if (ch == '+' || ch == '-') {
0N/A putChar(ch);
0N/A scanChar();
0N/A }
408N/A skipIllegalUnderscores();
0N/A if ('0' <= ch && ch <= '9') {
408N/A scanDigits(10);
0N/A if (!allowHexFloats) {
408N/A lexError("unsupported.fp.lit", source.name);
0N/A allowHexFloats = true;
0N/A }
0N/A else if (!hexFloatsWork)
0N/A lexError("unsupported.cross.fp.lit");
0N/A } else
0N/A lexError("malformed.fp.lit");
0N/A } else {
0N/A lexError("malformed.fp.lit");
0N/A }
0N/A if (ch == 'f' || ch == 'F') {
0N/A putChar(ch);
0N/A scanChar();
0N/A token = FLOATLITERAL;
0N/A } else {
0N/A if (ch == 'd' || ch == 'D') {
0N/A putChar(ch);
0N/A scanChar();
0N/A }
0N/A token = DOUBLELITERAL;
0N/A }
0N/A }
0N/A
0N/A /** Read fractional part of floating point number.
0N/A */
0N/A private void scanFraction() {
408N/A skipIllegalUnderscores();
408N/A if ('0' <= ch && ch <= '9') {
408N/A scanDigits(10);
0N/A }
0N/A int sp1 = sp;
0N/A if (ch == 'e' || ch == 'E') {
0N/A putChar(ch);
0N/A scanChar();
408N/A skipIllegalUnderscores();
0N/A if (ch == '+' || ch == '-') {
0N/A putChar(ch);
0N/A scanChar();
0N/A }
408N/A skipIllegalUnderscores();
0N/A if ('0' <= ch && ch <= '9') {
408N/A scanDigits(10);
0N/A return;
0N/A }
0N/A lexError("malformed.fp.lit");
0N/A sp = sp1;
0N/A }
0N/A }
0N/A
0N/A /** Read fractional part and 'd' or 'f' suffix of floating point number.
0N/A */
0N/A private void scanFractionAndSuffix() {
0N/A this.radix = 10;
0N/A scanFraction();
0N/A if (ch == 'f' || ch == 'F') {
0N/A putChar(ch);
0N/A scanChar();
0N/A token = FLOATLITERAL;
0N/A } else {
0N/A if (ch == 'd' || ch == 'D') {
0N/A putChar(ch);
0N/A scanChar();
0N/A }
0N/A token = DOUBLELITERAL;
0N/A }
0N/A }
0N/A
0N/A /** Read fractional part and 'd' or 'f' suffix of floating point number.
0N/A */
0N/A private void scanHexFractionAndSuffix(boolean seendigit) {
0N/A this.radix = 16;
815N/A Assert.check(ch == '.');
0N/A putChar(ch);
0N/A scanChar();
408N/A skipIllegalUnderscores();
408N/A if (digit(16) >= 0) {
0N/A seendigit = true;
408N/A scanDigits(16);
0N/A }
0N/A if (!seendigit)
0N/A lexError("invalid.hex.number");
0N/A else
0N/A scanHexExponentAndSuffix();
0N/A }
0N/A
408N/A private void skipIllegalUnderscores() {
408N/A if (ch == '_') {
408N/A lexError(bp, "illegal.underscore");
408N/A while (ch == '_')
408N/A scanChar();
408N/A }
408N/A }
408N/A
0N/A /** Read a number.
408N/A * @param radix The radix of the number; one of 2, j8, 10, 16.
0N/A */
0N/A private void scanNumber(int radix) {
0N/A this.radix = radix;
0N/A // for octal, allow base-10 digit in case it's a float literal
408N/A int digitRadix = (radix == 8 ? 10 : radix);
0N/A boolean seendigit = false;
408N/A if (digit(digitRadix) >= 0) {
0N/A seendigit = true;
408N/A scanDigits(digitRadix);
0N/A }
0N/A if (radix == 16 && ch == '.') {
0N/A scanHexFractionAndSuffix(seendigit);
0N/A } else if (seendigit && radix == 16 && (ch == 'p' || ch == 'P')) {
0N/A scanHexExponentAndSuffix();
408N/A } else if (digitRadix == 10 && ch == '.') {
0N/A putChar(ch);
0N/A scanChar();
0N/A scanFractionAndSuffix();
408N/A } else if (digitRadix == 10 &&
0N/A (ch == 'e' || ch == 'E' ||
0N/A ch == 'f' || ch == 'F' ||
0N/A ch == 'd' || ch == 'D')) {
0N/A scanFractionAndSuffix();
0N/A } else {
0N/A if (ch == 'l' || ch == 'L') {
0N/A scanChar();
0N/A token = LONGLITERAL;
0N/A } else {
0N/A token = INTLITERAL;
0N/A }
0N/A }
0N/A }
0N/A
0N/A /** Read an identifier.
0N/A */
0N/A private void scanIdent() {
0N/A boolean isJavaIdentifierPart;
0N/A char high;
0N/A do {
0N/A if (sp == sbuf.length) putChar(ch); else sbuf[sp++] = ch;
0N/A // optimization, was: putChar(ch);
0N/A
0N/A scanChar();
0N/A switch (ch) {
0N/A case 'A': case 'B': case 'C': case 'D': case 'E':
0N/A case 'F': case 'G': case 'H': case 'I': case 'J':
0N/A case 'K': case 'L': case 'M': case 'N': case 'O':
0N/A case 'P': case 'Q': case 'R': case 'S': case 'T':
0N/A case 'U': case 'V': case 'W': case 'X': case 'Y':
0N/A case 'Z':
0N/A case 'a': case 'b': case 'c': case 'd': case 'e':
0N/A case 'f': case 'g': case 'h': case 'i': case 'j':
0N/A case 'k': case 'l': case 'm': case 'n': case 'o':
0N/A case 'p': case 'q': case 'r': case 's': case 't':
0N/A case 'u': case 'v': case 'w': case 'x': case 'y':
0N/A case 'z':
0N/A case '$': case '_':
0N/A case '0': case '1': case '2': case '3': case '4':
0N/A case '5': case '6': case '7': case '8': case '9':
0N/A case '\u0000': case '\u0001': case '\u0002': case '\u0003':
0N/A case '\u0004': case '\u0005': case '\u0006': case '\u0007':
0N/A case '\u0008': case '\u000E': case '\u000F': case '\u0010':
0N/A case '\u0011': case '\u0012': case '\u0013': case '\u0014':
0N/A case '\u0015': case '\u0016': case '\u0017':
0N/A case '\u0018': case '\u0019': case '\u001B':
0N/A case '\u007F':
0N/A break;
0N/A case '\u001A': // EOI is also a legal identifier part
0N/A if (bp >= buflen) {
0N/A name = names.fromChars(sbuf, 0, sp);
0N/A token = keywords.key(name);
0N/A return;
0N/A }
0N/A break;
0N/A default:
0N/A if (ch < '\u0080') {
0N/A // all ASCII range chars already handled, above
0N/A isJavaIdentifierPart = false;
0N/A } else {
0N/A high = scanSurrogates();
0N/A if (high != 0) {
0N/A if (sp == sbuf.length) {
0N/A putChar(high);
0N/A } else {
0N/A sbuf[sp++] = high;
0N/A }
0N/A isJavaIdentifierPart = Character.isJavaIdentifierPart(
0N/A Character.toCodePoint(high, ch));
0N/A } else {
0N/A isJavaIdentifierPart = Character.isJavaIdentifierPart(ch);
0N/A }
0N/A }
0N/A if (!isJavaIdentifierPart) {
0N/A name = names.fromChars(sbuf, 0, sp);
0N/A token = keywords.key(name);
0N/A return;
0N/A }
0N/A }
0N/A } while (true);
0N/A }
0N/A
0N/A /** Are surrogates supported?
0N/A */
0N/A final static boolean surrogatesSupported = surrogatesSupported();
0N/A private static boolean surrogatesSupported() {
0N/A try {
0N/A Character.isHighSurrogate('a');
0N/A return true;
0N/A } catch (NoSuchMethodError ex) {
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A /** Scan surrogate pairs. If 'ch' is a high surrogate and
0N/A * the next character is a low surrogate, then put the low
0N/A * surrogate in 'ch', and return the high surrogate.
0N/A * otherwise, just return 0.
0N/A */
0N/A private char scanSurrogates() {
0N/A if (surrogatesSupported && Character.isHighSurrogate(ch)) {
0N/A char high = ch;
0N/A
0N/A scanChar();
0N/A
0N/A if (Character.isLowSurrogate(ch)) {
0N/A return high;
0N/A }
0N/A
0N/A ch = high;
0N/A }
0N/A
0N/A return 0;
0N/A }
0N/A
0N/A /** Return true if ch can be part of an operator.
0N/A */
0N/A private boolean isSpecial(char ch) {
0N/A switch (ch) {
0N/A case '!': case '%': case '&': case '*': case '?':
0N/A case '+': case '-': case ':': case '<': case '=':
0N/A case '>': case '^': case '|': case '~':
0N/A case '@':
0N/A return true;
0N/A default:
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A /** Read longest possible sequence of special characters and convert
0N/A * to token.
0N/A */
0N/A private void scanOperator() {
0N/A while (true) {
0N/A putChar(ch);
0N/A Name newname = names.fromChars(sbuf, 0, sp);
0N/A if (keywords.key(newname) == IDENTIFIER) {
0N/A sp--;
0N/A break;
0N/A }
0N/A name = newname;
0N/A token = keywords.key(newname);
0N/A scanChar();
0N/A if (!isSpecial(ch)) break;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Scan a documention comment; determine if a deprecated tag is present.
0N/A * Called once the initial /, * have been skipped, positioned at the second *
0N/A * (which is treated as the beginning of the first line).
0N/A * Stops positioned at the closing '/'.
0N/A */
0N/A @SuppressWarnings("fallthrough")
0N/A private void scanDocComment() {
0N/A boolean deprecatedPrefix = false;
0N/A
0N/A forEachLine:
0N/A while (bp < buflen) {
0N/A
0N/A // Skip optional WhiteSpace at beginning of line
0N/A while (bp < buflen && (ch == ' ' || ch == '\t' || ch == FF)) {
0N/A scanCommentChar();
0N/A }
0N/A
0N/A // Skip optional consecutive Stars
0N/A while (bp < buflen && ch == '*') {
0N/A scanCommentChar();
0N/A if (ch == '/') {
0N/A return;
0N/A }
0N/A }
0N/A
0N/A // Skip optional WhiteSpace after Stars
0N/A while (bp < buflen && (ch == ' ' || ch == '\t' || ch == FF)) {
0N/A scanCommentChar();
0N/A }
0N/A
0N/A deprecatedPrefix = false;
0N/A // At beginning of line in the JavaDoc sense.
0N/A if (bp < buflen && ch == '@' && !deprecatedFlag) {
0N/A scanCommentChar();
0N/A if (bp < buflen && ch == 'd') {
0N/A scanCommentChar();
0N/A if (bp < buflen && ch == 'e') {
0N/A scanCommentChar();
0N/A if (bp < buflen && ch == 'p') {
0N/A scanCommentChar();
0N/A if (bp < buflen && ch == 'r') {
0N/A scanCommentChar();
0N/A if (bp < buflen && ch == 'e') {
0N/A scanCommentChar();
0N/A if (bp < buflen && ch == 'c') {
0N/A scanCommentChar();
0N/A if (bp < buflen && ch == 'a') {
0N/A scanCommentChar();
0N/A if (bp < buflen && ch == 't') {
0N/A scanCommentChar();
0N/A if (bp < buflen && ch == 'e') {
0N/A scanCommentChar();
0N/A if (bp < buflen && ch == 'd') {
0N/A deprecatedPrefix = true;
0N/A scanCommentChar();
0N/A }}}}}}}}}}}
0N/A if (deprecatedPrefix && bp < buflen) {
0N/A if (Character.isWhitespace(ch)) {
0N/A deprecatedFlag = true;
0N/A } else if (ch == '*') {
0N/A scanCommentChar();
0N/A if (ch == '/') {
0N/A deprecatedFlag = true;
0N/A return;
0N/A }
0N/A }
0N/A }
0N/A
0N/A // Skip rest of line
0N/A while (bp < buflen) {
0N/A switch (ch) {
0N/A case '*':
0N/A scanCommentChar();
0N/A if (ch == '/') {
0N/A return;
0N/A }
0N/A break;
0N/A case CR: // (Spec 3.4)
0N/A scanCommentChar();
0N/A if (ch != LF) {
0N/A continue forEachLine;
0N/A }
0N/A /* fall through to LF case */
0N/A case LF: // (Spec 3.4)
0N/A scanCommentChar();
0N/A continue forEachLine;
0N/A default:
0N/A scanCommentChar();
0N/A }
0N/A } // rest of line
0N/A } // forEachLine
0N/A return;
0N/A }
0N/A
0N/A /** The value of a literal token, recorded as a string.
0N/A * For integers, leading 0x and 'l' suffixes are suppressed.
0N/A */
0N/A public String stringVal() {
0N/A return new String(sbuf, 0, sp);
0N/A }
0N/A
0N/A /** Read token.
0N/A */
0N/A public void nextToken() {
0N/A
0N/A try {
0N/A prevEndPos = endPos;
0N/A sp = 0;
0N/A
0N/A while (true) {
0N/A pos = bp;
0N/A switch (ch) {
0N/A case ' ': // (Spec 3.6)
0N/A case '\t': // (Spec 3.6)
0N/A case FF: // (Spec 3.6)
0N/A do {
0N/A scanChar();
0N/A } while (ch == ' ' || ch == '\t' || ch == FF);
0N/A endPos = bp;
0N/A processWhiteSpace();
0N/A break;
0N/A case LF: // (Spec 3.4)
0N/A scanChar();
0N/A endPos = bp;
0N/A processLineTerminator();
0N/A break;
0N/A case CR: // (Spec 3.4)
0N/A scanChar();
0N/A if (ch == LF) {
0N/A scanChar();
0N/A }
0N/A endPos = bp;
0N/A processLineTerminator();
0N/A break;
0N/A case 'A': case 'B': case 'C': case 'D': case 'E':
0N/A case 'F': case 'G': case 'H': case 'I': case 'J':
0N/A case 'K': case 'L': case 'M': case 'N': case 'O':
0N/A case 'P': case 'Q': case 'R': case 'S': case 'T':
0N/A case 'U': case 'V': case 'W': case 'X': case 'Y':
0N/A case 'Z':
0N/A case 'a': case 'b': case 'c': case 'd': case 'e':
0N/A case 'f': case 'g': case 'h': case 'i': case 'j':
0N/A case 'k': case 'l': case 'm': case 'n': case 'o':
0N/A case 'p': case 'q': case 'r': case 's': case 't':
0N/A case 'u': case 'v': case 'w': case 'x': case 'y':
0N/A case 'z':
0N/A case '$': case '_':
0N/A scanIdent();
0N/A return;
0N/A case '0':
0N/A scanChar();
0N/A if (ch == 'x' || ch == 'X') {
0N/A scanChar();
408N/A skipIllegalUnderscores();
0N/A if (ch == '.') {
0N/A scanHexFractionAndSuffix(false);
0N/A } else if (digit(16) < 0) {
0N/A lexError("invalid.hex.number");
0N/A } else {
0N/A scanNumber(16);
0N/A }
408N/A } else if (ch == 'b' || ch == 'B') {
408N/A if (!allowBinaryLiterals) {
408N/A lexError("unsupported.binary.lit", source.name);
408N/A allowBinaryLiterals = true;
408N/A }
408N/A scanChar();
408N/A skipIllegalUnderscores();
422N/A if (digit(2) < 0) {
422N/A lexError("invalid.binary.number");
422N/A } else {
422N/A scanNumber(2);
422N/A }
0N/A } else {
0N/A putChar('0');
408N/A if (ch == '_') {
408N/A int savePos = bp;
408N/A do {
408N/A scanChar();
408N/A } while (ch == '_');
408N/A if (digit(10) < 0) {
408N/A lexError(savePos, "illegal.underscore");
408N/A }
408N/A }
0N/A scanNumber(8);
0N/A }
0N/A return;
0N/A case '1': case '2': case '3': case '4':
0N/A case '5': case '6': case '7': case '8': case '9':
0N/A scanNumber(10);
0N/A return;
0N/A case '.':
0N/A scanChar();
0N/A if ('0' <= ch && ch <= '9') {
0N/A putChar('.');
0N/A scanFractionAndSuffix();
0N/A } else if (ch == '.') {
0N/A putChar('.'); putChar('.');
0N/A scanChar();
0N/A if (ch == '.') {
0N/A scanChar();
0N/A putChar('.');
0N/A token = ELLIPSIS;
0N/A } else {
0N/A lexError("malformed.fp.lit");
0N/A }
0N/A } else {
0N/A token = DOT;
0N/A }
0N/A return;
0N/A case ',':
0N/A scanChar(); token = COMMA; return;
0N/A case ';':
0N/A scanChar(); token = SEMI; return;
0N/A case '(':
0N/A scanChar(); token = LPAREN; return;
0N/A case ')':
0N/A scanChar(); token = RPAREN; return;
0N/A case '[':
0N/A scanChar(); token = LBRACKET; return;
0N/A case ']':
0N/A scanChar(); token = RBRACKET; return;
0N/A case '{':
0N/A scanChar(); token = LBRACE; return;
0N/A case '}':
0N/A scanChar(); token = RBRACE; return;
0N/A case '/':
0N/A scanChar();
0N/A if (ch == '/') {
0N/A do {
0N/A scanCommentChar();
0N/A } while (ch != CR && ch != LF && bp < buflen);
0N/A if (bp < buflen) {
0N/A endPos = bp;
0N/A processComment(CommentStyle.LINE);
0N/A }
0N/A break;
0N/A } else if (ch == '*') {
0N/A scanChar();
0N/A CommentStyle style;
0N/A if (ch == '*') {
0N/A style = CommentStyle.JAVADOC;
0N/A scanDocComment();
0N/A } else {
0N/A style = CommentStyle.BLOCK;
0N/A while (bp < buflen) {
0N/A if (ch == '*') {
0N/A scanChar();
0N/A if (ch == '/') break;
0N/A } else {
0N/A scanCommentChar();
0N/A }
0N/A }
0N/A }
0N/A if (ch == '/') {
0N/A scanChar();
0N/A endPos = bp;
0N/A processComment(style);
0N/A break;
0N/A } else {
0N/A lexError("unclosed.comment");
0N/A return;
0N/A }
0N/A } else if (ch == '=') {
0N/A name = names.slashequals;
0N/A token = SLASHEQ;
0N/A scanChar();
0N/A } else {
0N/A name = names.slash;
0N/A token = SLASH;
0N/A }
0N/A return;
0N/A case '\'':
0N/A scanChar();
0N/A if (ch == '\'') {
0N/A lexError("empty.char.lit");
0N/A } else {
0N/A if (ch == CR || ch == LF)
0N/A lexError(pos, "illegal.line.end.in.char.lit");
0N/A scanLitChar();
0N/A if (ch == '\'') {
0N/A scanChar();
0N/A token = CHARLITERAL;
0N/A } else {
0N/A lexError(pos, "unclosed.char.lit");
0N/A }
0N/A }
0N/A return;
0N/A case '\"':
0N/A scanChar();
0N/A while (ch != '\"' && ch != CR && ch != LF && bp < buflen)
0N/A scanLitChar();
0N/A if (ch == '\"') {
0N/A token = STRINGLITERAL;
0N/A scanChar();
0N/A } else {
0N/A lexError(pos, "unclosed.str.lit");
0N/A }
0N/A return;
0N/A default:
0N/A if (isSpecial(ch)) {
0N/A scanOperator();
0N/A } else {
0N/A boolean isJavaIdentifierStart;
0N/A if (ch < '\u0080') {
0N/A // all ASCII range chars already handled, above
0N/A isJavaIdentifierStart = false;
0N/A } else {
0N/A char high = scanSurrogates();
0N/A if (high != 0) {
0N/A if (sp == sbuf.length) {
0N/A putChar(high);
0N/A } else {
0N/A sbuf[sp++] = high;
0N/A }
0N/A
0N/A isJavaIdentifierStart = Character.isJavaIdentifierStart(
0N/A Character.toCodePoint(high, ch));
0N/A } else {
0N/A isJavaIdentifierStart = Character.isJavaIdentifierStart(ch);
0N/A }
0N/A }
0N/A if (isJavaIdentifierStart) {
0N/A scanIdent();
0N/A } else if (bp == buflen || ch == EOI && bp+1 == buflen) { // JLS 3.5
0N/A token = EOF;
0N/A pos = bp = eofPos;
0N/A } else {
0N/A lexError("illegal.char", String.valueOf((int)ch));
0N/A scanChar();
0N/A }
0N/A }
0N/A return;
0N/A }
0N/A }
0N/A } finally {
0N/A endPos = bp;
0N/A if (scannerDebug)
0N/A System.out.println("nextToken(" + pos
0N/A + "," + endPos + ")=|" +
0N/A new String(getRawCharacters(pos, endPos))
0N/A + "|");
0N/A }
0N/A }
0N/A
0N/A /** Return the current token, set by nextToken().
0N/A */
0N/A public Token token() {
0N/A return token;
0N/A }
0N/A
0N/A /** Sets the current token.
1056N/A * This method is primarily used to update the token stream when the
1056N/A * parser is handling the end of nested type arguments such as
1056N/A * {@code List<List<String>>} and needs to disambiguate between
1056N/A * repeated use of ">" and relation operators such as ">>" and ">>>". Noting
1056N/A * that this does not handle arbitrary tokens containing Unicode escape
1056N/A * sequences.
0N/A */
0N/A public void token(Token token) {
1056N/A pos += this.token.name.length() - token.name.length();
1056N/A prevEndPos = pos;
0N/A this.token = token;
0N/A }
0N/A
0N/A /** Return the current token's position: a 0-based
0N/A * offset from beginning of the raw input stream
0N/A * (before unicode translation)
0N/A */
0N/A public int pos() {
0N/A return pos;
0N/A }
0N/A
0N/A /** Return the last character position of the current token.
0N/A */
0N/A public int endPos() {
0N/A return endPos;
0N/A }
0N/A
0N/A /** Return the last character position of the previous token.
0N/A */
0N/A public int prevEndPos() {
0N/A return prevEndPos;
0N/A }
0N/A
0N/A /** Return the position where a lexical error occurred;
0N/A */
0N/A public int errPos() {
0N/A return errPos;
0N/A }
0N/A
0N/A /** Set the position where a lexical error occurred;
0N/A */
0N/A public void errPos(int pos) {
0N/A errPos = pos;
0N/A }
0N/A
0N/A /** Return the name of an identifier or token for the current token.
0N/A */
0N/A public Name name() {
0N/A return name;
0N/A }
0N/A
0N/A /** Return the radix of a numeric literal token.
0N/A */
0N/A public int radix() {
0N/A return radix;
0N/A }
0N/A
0N/A /** Has a @deprecated been encountered in last doc comment?
0N/A * This needs to be reset by client with resetDeprecatedFlag.
0N/A */
0N/A public boolean deprecatedFlag() {
0N/A return deprecatedFlag;
0N/A }
0N/A
0N/A public void resetDeprecatedFlag() {
0N/A deprecatedFlag = false;
0N/A }
0N/A
0N/A /**
0N/A * Returns the documentation string of the current token.
0N/A */
0N/A public String docComment() {
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Returns a copy of the input buffer, up to its inputLength.
0N/A * Unicode escape sequences are not translated.
0N/A */
0N/A public char[] getRawCharacters() {
0N/A char[] chars = new char[buflen];
0N/A System.arraycopy(buf, 0, chars, 0, buflen);
0N/A return chars;
0N/A }
0N/A
0N/A /**
0N/A * Returns a copy of a character array subset of the input buffer.
0N/A * The returned array begins at the <code>beginIndex</code> and
0N/A * extends to the character at index <code>endIndex - 1</code>.
0N/A * Thus the length of the substring is <code>endIndex-beginIndex</code>.
0N/A * This behavior is like
0N/A * <code>String.substring(beginIndex, endIndex)</code>.
0N/A * Unicode escape sequences are not translated.
0N/A *
0N/A * @param beginIndex the beginning index, inclusive.
0N/A * @param endIndex the ending index, exclusive.
0N/A * @throws IndexOutOfBounds if either offset is outside of the
0N/A * array bounds
0N/A */
0N/A public char[] getRawCharacters(int beginIndex, int endIndex) {
0N/A int length = endIndex - beginIndex;
0N/A char[] chars = new char[length];
0N/A System.arraycopy(buf, beginIndex, chars, 0, length);
0N/A return chars;
0N/A }
0N/A
0N/A public enum CommentStyle {
0N/A LINE,
0N/A BLOCK,
0N/A JAVADOC,
0N/A }
0N/A
0N/A /**
0N/A * Called when a complete comment has been scanned. pos and endPos
0N/A * will mark the comment boundary.
0N/A */
0N/A protected void processComment(CommentStyle style) {
0N/A if (scannerDebug)
0N/A System.out.println("processComment(" + pos
0N/A + "," + endPos + "," + style + ")=|"
0N/A + new String(getRawCharacters(pos, endPos))
0N/A + "|");
0N/A }
0N/A
0N/A /**
0N/A * Called when a complete whitespace run has been scanned. pos and endPos
0N/A * will mark the whitespace boundary.
0N/A */
0N/A protected void processWhiteSpace() {
0N/A if (scannerDebug)
0N/A System.out.println("processWhitespace(" + pos
0N/A + "," + endPos + ")=|" +
0N/A new String(getRawCharacters(pos, endPos))
0N/A + "|");
0N/A }
0N/A
0N/A /**
0N/A * Called when a line terminator has been processed.
0N/A */
0N/A protected void processLineTerminator() {
0N/A if (scannerDebug)
0N/A System.out.println("processTerminator(" + pos
0N/A + "," + endPos + ")=|" +
0N/A new String(getRawCharacters(pos, endPos))
0N/A + "|");
0N/A }
0N/A
0N/A /** Build a map for translating between line numbers and
0N/A * positions in the input.
0N/A *
0N/A * @return a LineMap */
0N/A public Position.LineMap getLineMap() {
0N/A return Position.makeLineMap(buf, buflen, false);
0N/A }
0N/A
0N/A}