|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.tools.java; |
|
|
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.util.Hashtable; |
|
|
|
/** |
|
* A Scanner for Java tokens. Errors are reported |
|
* to the environment object.<p> |
|
* |
|
* The scanner keeps track of the current token, |
|
* the value of the current token (if any), and the start |
|
* position of the current token.<p> |
|
* |
|
* The scan() method advances the scanner to the next |
|
* token in the input.<p> |
|
* |
|
* The match() method is used to quickly match opening |
|
* brackets (ie: '(', '{', or '[') with their closing |
|
* counter part. This is useful during error recovery.<p> |
|
* |
|
* An position consists of: ((linenr << WHEREOFFSETBITS) | offset) |
|
* this means that both the line number and the exact offset into |
|
* the file are encoded in each position value.<p> |
|
* |
|
* The compiler treats either "\n", "\r" or "\r\n" as the |
|
* end of a line.<p> |
|
* |
|
* WARNING: The contents of this source file are not part of any |
|
* supported API. Code that depends on them does so at its own risk: |
|
* they are subject to change or removal without notice. |
|
* |
|
* @author Arthur van Hoff |
|
*/ |
|
|
|
public |
|
class Scanner implements Constants { |
|
|
|
|
|
*/ |
|
public static final long OFFSETINC = 1; |
|
|
|
|
|
|
|
*/ |
|
public static final long LINEINC = 1L << WHEREOFFSETBITS; |
|
|
|
|
|
|
|
*/ |
|
public static final int EOF = -1; |
|
|
|
|
|
|
|
*/ |
|
public Environment env; |
|
|
|
|
|
|
|
*/ |
|
protected ScannerInputReader in; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean scanComments = false; |
|
|
|
|
|
|
|
*/ |
|
public int token; |
|
|
|
|
|
|
|
*/ |
|
public long pos; |
|
|
|
|
|
|
|
*/ |
|
public long prevPos; |
|
|
|
|
|
|
|
*/ |
|
protected int ch; |
|
|
|
|
|
|
|
*/ |
|
public char charValue; |
|
public int intValue; |
|
public long longValue; |
|
public float floatValue; |
|
public double doubleValue; |
|
public String stringValue; |
|
public Identifier idValue; |
|
public int radix; |
|
|
|
|
|
|
|
*/ |
|
public String docComment; |
|
|
|
|
|
|
|
*/ |
|
private int count; |
|
private char buffer[] = new char[1024]; |
|
private void growBuffer() { |
|
char newBuffer[] = new char[buffer.length * 2]; |
|
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); |
|
buffer = newBuffer; |
|
} |
|
|
|
// The following two methods have been hand-inlined in |
|
// scanDocComment. If you make changes here, you should |
|
|
|
private void putc(int ch) { |
|
if (count == buffer.length) { |
|
growBuffer(); |
|
} |
|
buffer[count++] = (char)ch; |
|
} |
|
|
|
private String bufferString() { |
|
return new String(buffer, 0, count); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Scanner(Environment env, InputStream in) throws IOException { |
|
this.env = env; |
|
useInputStream(in); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
protected void useInputStream(InputStream in) throws IOException { |
|
try { |
|
this.in = new ScannerInputReader(env, in); |
|
} catch (Exception e) { |
|
env.setCharacterEncoding(null); |
|
this.in = new ScannerInputReader(env, in); |
|
} |
|
|
|
ch = this.in.read(); |
|
prevPos = this.in.pos; |
|
|
|
scan(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
protected Scanner(Environment env) { |
|
this.env = env; |
|
// Expect the subclass to call useInputStream at the right time. |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static void defineKeyword(int val) { |
|
Identifier.lookup(opNames[val]).setType(val); |
|
} |
|
|
|
|
|
|
|
*/ |
|
static { |
|
|
|
defineKeyword(FOR); |
|
defineKeyword(IF); |
|
defineKeyword(ELSE); |
|
defineKeyword(WHILE); |
|
defineKeyword(DO); |
|
defineKeyword(SWITCH); |
|
defineKeyword(CASE); |
|
defineKeyword(DEFAULT); |
|
defineKeyword(BREAK); |
|
defineKeyword(CONTINUE); |
|
defineKeyword(RETURN); |
|
defineKeyword(TRY); |
|
defineKeyword(CATCH); |
|
defineKeyword(FINALLY); |
|
defineKeyword(THROW); |
|
|
|
|
|
defineKeyword(BYTE); |
|
defineKeyword(CHAR); |
|
defineKeyword(SHORT); |
|
defineKeyword(INT); |
|
defineKeyword(LONG); |
|
defineKeyword(FLOAT); |
|
defineKeyword(DOUBLE); |
|
defineKeyword(VOID); |
|
defineKeyword(BOOLEAN); |
|
|
|
|
|
defineKeyword(INSTANCEOF); |
|
defineKeyword(TRUE); |
|
defineKeyword(FALSE); |
|
defineKeyword(NEW); |
|
defineKeyword(THIS); |
|
defineKeyword(SUPER); |
|
defineKeyword(NULL); |
|
|
|
|
|
defineKeyword(IMPORT); |
|
defineKeyword(CLASS); |
|
defineKeyword(EXTENDS); |
|
defineKeyword(IMPLEMENTS); |
|
defineKeyword(INTERFACE); |
|
defineKeyword(PACKAGE); |
|
defineKeyword(THROWS); |
|
|
|
|
|
defineKeyword(PRIVATE); |
|
defineKeyword(PUBLIC); |
|
defineKeyword(PROTECTED); |
|
defineKeyword(STATIC); |
|
defineKeyword(TRANSIENT); |
|
defineKeyword(SYNCHRONIZED); |
|
defineKeyword(NATIVE); |
|
defineKeyword(ABSTRACT); |
|
defineKeyword(VOLATILE); |
|
defineKeyword(FINAL); |
|
defineKeyword(STRICTFP); |
|
|
|
|
|
defineKeyword(CONST); |
|
defineKeyword(GOTO); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void skipComment() throws IOException { |
|
while (true) { |
|
switch (ch) { |
|
case EOF: |
|
env.error(pos, "eof.in.comment"); |
|
return; |
|
|
|
case '*': |
|
if ((ch = in.read()) == '/') { |
|
ch = in.read(); |
|
return; |
|
} |
|
break; |
|
|
|
default: |
|
ch = in.read(); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private String scanDocComment() throws IOException { |
|
// Note: this method has been hand-optimized to yield |
|
// better performance. This was done after it was noted |
|
// that javadoc spent a great deal of its time here. |
|
// This should also help the performance of the compiler |
|
// as well -- it scans the doc comments to find |
|
// @deprecated tags. |
|
// |
|
// The logic of the method has been completely rewritten |
|
// to avoid the use of flags that need to be looked at |
|
// for every character read. Members that are accessed |
|
// more than once have been stored in local variables. |
|
// The methods putc() and bufferString() have been |
|
// inlined by hand. Extra cases have been added to |
|
// switch statements to trick the compiler into generating |
|
// a tableswitch instead of a lookupswitch. |
|
// |
|
// This implementation aims to preserve the previous |
|
// behavior of this method. |
|
|
|
int c; |
|
|
|
|
|
final ScannerInputReader in = this.in; |
|
|
|
|
|
char[] buffer = this.buffer; |
|
int count = 0; |
|
|
|
// We are called pointing at the second star of the doc |
|
// comment: |
|
// |
|
// Input: /** the rest of the comment ... */ |
|
// ^ |
|
// |
|
// We rely on this in the code below. |
|
|
|
|
|
while ((c = in.read()) == '*') |
|
; |
|
|
|
|
|
if (c == '/') { |
|
|
|
ch = in.read(); |
|
return ""; |
|
} |
|
|
|
|
|
if (c == '\n') { |
|
c = in.read(); |
|
} |
|
|
|
outerLoop: |
|
// The outerLoop processes the doc comment, looping once |
|
// for each line. For each line, it first strips off |
|
// whitespace, then it consumes any stars, then it |
|
|
|
while (true) { |
|
|
|
// The wsLoop consumes whitespace from the beginning |
|
|
|
wsLoop: |
|
while (true) { |
|
switch (c) { |
|
case ' ': |
|
case '\t': |
|
// We could check for other forms of whitespace |
|
// as well, but this is left as is for minimum |
|
// disturbance of functionality. |
|
// |
|
|
|
c = in.read(); |
|
break; |
|
|
|
// We have added extra cases here to trick the |
|
// compiler into using a tableswitch instead of |
|
// a lookupswitch. They can be removed without |
|
|
|
case 10: case 11: case 12: case 13: case 14: case 15: |
|
case 16: case 17: case 18: case 19: case 20: case 21: |
|
case 22: case 23: case 24: case 25: case 26: case 27: |
|
case 28: case 29: case 30: case 31: |
|
default: |
|
// We've seen something that isn't whitespace, |
|
|
|
break wsLoop; |
|
} |
|
} // end wsLoop. |
|
|
|
// Are there stars here? If so, consume them all |
|
|
|
if (c == '*') { |
|
|
|
do { |
|
c = in.read(); |
|
} while (c == '*'); |
|
|
|
|
|
if (c == '/') { |
|
// We're done with the doc comment. |
|
|
|
ch = in.read(); |
|
break outerLoop; |
|
} |
|
} |
|
|
|
// The textLoop processes the rest of the characters |
|
|
|
textLoop: |
|
while (true) { |
|
switch (c) { |
|
case EOF: |
|
// We've seen a premature EOF. Break out |
|
|
|
env.error(pos, "eof.in.comment"); |
|
ch = EOF; |
|
break outerLoop; |
|
|
|
case '*': |
|
// Is this just a star? Or is this the |
|
|
|
c = in.read(); |
|
if (c == '/') { |
|
// This is the end of the comment, |
|
|
|
ch = in.read(); |
|
break outerLoop; |
|
} |
|
// This is just an ordinary star. Add it to |
|
|
|
if (count == buffer.length) { |
|
growBuffer(); |
|
buffer = this.buffer; |
|
} |
|
buffer[count++] = '*'; |
|
break; |
|
|
|
case '\n': |
|
// We've seen a newline. Add it to our |
|
// buffer and break out of this loop, |
|
|
|
if (count == buffer.length) { |
|
growBuffer(); |
|
buffer = this.buffer; |
|
} |
|
buffer[count++] = '\n'; |
|
c = in.read(); |
|
break textLoop; |
|
|
|
// Again, the extra cases here are a trick |
|
|
|
case 0: case 1: case 2: case 3: case 4: case 5: |
|
case 6: case 7: case 8: case 11: case 12: case 13: |
|
case 14: case 15: case 16: case 17: case 18: case 19: |
|
case 20: case 21: case 22: case 23: case 24: case 25: |
|
case 26: case 27: case 28: case 29: case 30: case 31: |
|
case 32: case 33: case 34: case 35: case 36: case 37: |
|
case 38: case 39: case 40: |
|
default: |
|
|
|
if (count == buffer.length) { |
|
growBuffer(); |
|
buffer = this.buffer; |
|
} |
|
buffer[count++] = (char)c; |
|
c = in.read(); |
|
break; |
|
} |
|
} // end textLoop |
|
} // end outerLoop |
|
|
|
// We have scanned our doc comment. It is stored in |
|
// buffer. The previous implementation of scanDocComment |
|
// stripped off all trailing spaces and stars from the comment. |
|
// We will do this as well, so as to cause a minimum of |
|
|
|
if (count > 0) { |
|
int i = count - 1; |
|
trailLoop: |
|
while (i > -1) { |
|
switch (buffer[i]) { |
|
case ' ': |
|
case '\t': |
|
case '*': |
|
i--; |
|
break; |
|
// And again, the extra cases here are a trick |
|
|
|
case 0: case 1: case 2: case 3: case 4: case 5: |
|
case 6: case 7: case 8: case 10: case 11: case 12: |
|
case 13: case 14: case 15: case 16: case 17: case 18: |
|
case 19: case 20: case 21: case 22: case 23: case 24: |
|
case 25: case 26: case 27: case 28: case 29: case 30: |
|
case 31: case 33: case 34: case 35: case 36: case 37: |
|
case 38: case 39: case 40: |
|
default: |
|
break trailLoop; |
|
} |
|
} |
|
count = i + 1; |
|
|
|
|
|
return new String(buffer, 0, count); |
|
} else { |
|
return ""; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void scanNumber() throws IOException { |
|
boolean seenNonOctal = false; |
|
boolean overflow = false; |
|
boolean seenDigit = false; |
|
radix = (ch == '0' ? 8 : 10); |
|
long value = ch - '0'; |
|
count = 0; |
|
putc(ch); |
|
numberLoop: |
|
for (;;) { |
|
switch (ch = in.read()) { |
|
case '.': |
|
if (radix == 16) |
|
break numberLoop; |
|
scanReal(); |
|
return; |
|
|
|
case '8': case '9': |
|
// We can't yet throw an error if reading an octal. We might |
|
|
|
seenNonOctal = true; |
|
case '0': case '1': case '2': case '3': |
|
case '4': case '5': case '6': case '7': |
|
seenDigit = true; |
|
putc(ch); |
|
if (radix == 10) { |
|
overflow = overflow || (value * 10)/10 != value; |
|
value = (value * 10) + (ch - '0'); |
|
overflow = overflow || (value - 1 < -1); |
|
} else if (radix == 8) { |
|
overflow = overflow || (value >>> 61) != 0; |
|
value = (value << 3) + (ch - '0'); |
|
} else { |
|
overflow = overflow || (value >>> 60) != 0; |
|
value = (value << 4) + (ch - '0'); |
|
} |
|
break; |
|
|
|
case 'd': case 'D': case 'e': case 'E': case 'f': case 'F': |
|
if (radix != 16) { |
|
scanReal(); |
|
return; |
|
} |
|
|
|
case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': |
|
seenDigit = true; |
|
putc(ch); |
|
if (radix != 16) |
|
break numberLoop; |
|
overflow = overflow || (value >>> 60) != 0; |
|
value = (value << 4) + 10 + |
|
Character.toLowerCase((char)ch) - 'a'; |
|
break; |
|
|
|
case 'l': case 'L': |
|
ch = in.read(); |
|
longValue = value; |
|
token = LONGVAL; |
|
break numberLoop; |
|
|
|
case 'x': case 'X': |
|
// if the first character is a '0' and this is the second |
|
|
|
if (count == 1 && radix == 8) { |
|
radix = 16; |
|
seenDigit = false; |
|
break; |
|
} else { |
|
|
|
break numberLoop; |
|
} |
|
|
|
default: |
|
intValue = (int)value; |
|
token = INTVAL; |
|
break numberLoop; |
|
} |
|
} // while true |
|
|
|
// We have just finished reading the number. The next thing better |
|
// not be a letter or digit. |
|
// Note: There will be deprecation warnings against these uses |
|
// of Character.isJavaLetterOrDigit and Character.isJavaLetter. |
|
|
|
if (Character.isJavaLetterOrDigit((char)ch) || ch == '.') { |
|
env.error(in.pos, "invalid.number"); |
|
do { ch = in.read(); } |
|
while (Character.isJavaLetterOrDigit((char)ch) || ch == '.'); |
|
intValue = 0; |
|
token = INTVAL; |
|
} else if (radix == 8 && seenNonOctal) { |
|
|
|
intValue = 0; |
|
token = INTVAL; |
|
env.error(pos, "invalid.octal.number"); |
|
} else if (radix == 16 && seenDigit == false) { |
|
|
|
intValue = 0; |
|
token = INTVAL; |
|
env.error(pos, "invalid.hex.number"); |
|
} else { |
|
if (token == INTVAL) { |
|
// Check for overflow. Note that base 10 literals |
|
|
|
overflow = overflow || |
|
(value & 0xFFFFFFFF00000000L) != 0 || |
|
(radix == 10 && value > 2147483648L); |
|
|
|
if (overflow) { |
|
intValue = 0; |
|
|
|
// Give a specific error message which tells |
|
|
|
switch (radix) { |
|
case 8: |
|
env.error(pos, "overflow.int.oct"); |
|
break; |
|
case 10: |
|
env.error(pos, "overflow.int.dec"); |
|
break; |
|
case 16: |
|
env.error(pos, "overflow.int.hex"); |
|
break; |
|
default: |
|
throw new CompilerError("invalid radix"); |
|
} |
|
} |
|
} else { |
|
if (overflow) { |
|
longValue = 0; |
|
|
|
// Give a specific error message which tells |
|
|
|
switch (radix) { |
|
case 8: |
|
env.error(pos, "overflow.long.oct"); |
|
break; |
|
case 10: |
|
env.error(pos, "overflow.long.dec"); |
|
break; |
|
case 16: |
|
env.error(pos, "overflow.long.hex"); |
|
break; |
|
default: |
|
throw new CompilerError("invalid radix"); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void scanReal() throws IOException { |
|
boolean seenExponent = false; |
|
boolean isSingleFloat = false; |
|
char lastChar; |
|
if (ch == '.') { |
|
putc(ch); |
|
ch = in.read(); |
|
} |
|
|
|
numberLoop: |
|
for ( ; ; ch = in.read()) { |
|
switch (ch) { |
|
case '0': case '1': case '2': case '3': case '4': |
|
case '5': case '6': case '7': case '8': case '9': |
|
putc(ch); |
|
break; |
|
|
|
case 'e': case 'E': |
|
if (seenExponent) |
|
break numberLoop; |
|
putc(ch); |
|
seenExponent = true; |
|
break; |
|
|
|
case '+': case '-': |
|
lastChar = buffer[count - 1]; |
|
if (lastChar != 'e' && lastChar != 'E') |
|
break numberLoop; |
|
putc(ch); |
|
break; |
|
|
|
case 'f': case 'F': |
|
ch = in.read(); |
|
isSingleFloat = true; |
|
break numberLoop; |
|
|
|
case 'd': case 'D': |
|
ch = in.read(); |
|
|
|
default: |
|
break numberLoop; |
|
} // sswitch |
|
} // loop |
|
|
|
// we have just finished reading the number. The next thing better |
|
|
|
if (Character.isJavaLetterOrDigit((char)ch) || ch == '.') { |
|
env.error(in.pos, "invalid.number"); |
|
do { ch = in.read(); } |
|
while (Character.isJavaLetterOrDigit((char)ch) || ch == '.'); |
|
doubleValue = 0; |
|
token = DOUBLEVAL; |
|
} else { |
|
token = isSingleFloat ? FLOATVAL : DOUBLEVAL; |
|
try { |
|
lastChar = buffer[count - 1]; |
|
if (lastChar == 'e' || lastChar == 'E' |
|
|| lastChar == '+' || lastChar == '-') { |
|
env.error(in.pos -1, "float.format"); |
|
} else if (isSingleFloat) { |
|
String string = bufferString(); |
|
floatValue = Float.valueOf(string).floatValue(); |
|
if (Float.isInfinite(floatValue)) { |
|
env.error(pos, "overflow.float"); |
|
} else if (floatValue == 0 && !looksLikeZero(string)) { |
|
env.error(pos, "underflow.float"); |
|
} |
|
} else { |
|
String string = bufferString(); |
|
doubleValue = Double.valueOf(string).doubleValue(); |
|
if (Double.isInfinite(doubleValue)) { |
|
env.error(pos, "overflow.double"); |
|
} else if (doubleValue == 0 && !looksLikeZero(string)) { |
|
env.error(pos, "underflow.double"); |
|
} |
|
} |
|
} catch (NumberFormatException ee) { |
|
env.error(pos, "float.format"); |
|
doubleValue = 0; |
|
floatValue = 0; |
|
} |
|
} |
|
return; |
|
} |
|
|
|
// We have a token that parses as a number. Is this token possibly zero? |
|
|
|
private static boolean looksLikeZero(String token) { |
|
int length = token.length(); |
|
for (int i = 0; i < length; i++) { |
|
switch (token.charAt(i)) { |
|
case 0: case '.': |
|
continue; |
|
case '1': case '2': case '3': case '4': case '5': |
|
case '6': case '7': case '8': case '9': |
|
return false; |
|
case 'e': case 'E': case 'f': case 'F': |
|
return true; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private int scanEscapeChar() throws IOException { |
|
long p = in.pos; |
|
|
|
switch (ch = in.read()) { |
|
case '0': case '1': case '2': case '3': |
|
case '4': case '5': case '6': case '7': { |
|
int n = ch - '0'; |
|
for (int i = 2 ; i > 0 ; i--) { |
|
switch (ch = in.read()) { |
|
case '0': case '1': case '2': case '3': |
|
case '4': case '5': case '6': case '7': |
|
n = (n << 3) + ch - '0'; |
|
break; |
|
|
|
default: |
|
if (n > 0xFF) { |
|
env.error(p, "invalid.escape.char"); |
|
} |
|
return n; |
|
} |
|
} |
|
ch = in.read(); |
|
if (n > 0xFF) { |
|
env.error(p, "invalid.escape.char"); |
|
} |
|
return n; |
|
} |
|
|
|
case 'r': ch = in.read(); return '\r'; |
|
case 'n': ch = in.read(); return '\n'; |
|
case 'f': ch = in.read(); return '\f'; |
|
case 'b': ch = in.read(); return '\b'; |
|
case 't': ch = in.read(); return '\t'; |
|
case '\\': ch = in.read(); return '\\'; |
|
case '\"': ch = in.read(); return '\"'; |
|
case '\'': ch = in.read(); return '\''; |
|
} |
|
|
|
env.error(p, "invalid.escape.char"); |
|
ch = in.read(); |
|
return -1; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void scanString() throws IOException { |
|
token = STRINGVAL; |
|
count = 0; |
|
ch = in.read(); |
|
|
|
|
|
while (true) { |
|
switch (ch) { |
|
case EOF: |
|
env.error(pos, "eof.in.string"); |
|
stringValue = bufferString(); |
|
return; |
|
|
|
case '\r': |
|
case '\n': |
|
ch = in.read(); |
|
env.error(pos, "newline.in.string"); |
|
stringValue = bufferString(); |
|
return; |
|
|
|
case '"': |
|
ch = in.read(); |
|
stringValue = bufferString(); |
|
return; |
|
|
|
case '\\': { |
|
int c = scanEscapeChar(); |
|
if (c >= 0) { |
|
putc((char)c); |
|
} |
|
break; |
|
} |
|
|
|
default: |
|
putc(ch); |
|
ch = in.read(); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void scanCharacter() throws IOException { |
|
token = CHARVAL; |
|
|
|
switch (ch = in.read()) { |
|
case '\\': |
|
int c = scanEscapeChar(); |
|
charValue = (char)((c >= 0) ? c : 0); |
|
break; |
|
|
|
case '\'': |
|
// There are two standard problems this case deals with. One |
|
// is the malformed single quote constant (i.e. the programmer |
|
// uses ''' instead of '\'') and the other is the empty |
|
// character constant (i.e. ''). Just consume any number of |
|
|
|
charValue = 0; |
|
env.error(pos, "invalid.char.constant"); |
|
ch = in.read(); |
|
while (ch == '\'') { |
|
ch = in.read(); |
|
} |
|
return; |
|
|
|
case '\r': |
|
case '\n': |
|
charValue = 0; |
|
env.error(pos, "invalid.char.constant"); |
|
return; |
|
|
|
default: |
|
charValue = (char)ch; |
|
ch = in.read(); |
|
break; |
|
} |
|
|
|
if (ch == '\'') { |
|
ch = in.read(); |
|
} else { |
|
env.error(pos, "invalid.char.constant"); |
|
while (true) { |
|
switch (ch) { |
|
case '\'': |
|
ch = in.read(); |
|
return; |
|
case ';': |
|
case '\n': |
|
case EOF: |
|
return; |
|
default: |
|
ch = in.read(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void scanIdentifier() throws IOException { |
|
count = 0; |
|
|
|
while (true) { |
|
putc(ch); |
|
switch (ch = in.read()) { |
|
case 'a': case 'b': case 'c': case 'd': case 'e': |
|
case 'f': case 'g': case 'h': case 'i': case 'j': |
|
case 'k': case 'l': case 'm': case 'n': case 'o': |
|
case 'p': case 'q': case 'r': case 's': case 't': |
|
case 'u': case 'v': case 'w': case 'x': case 'y': |
|
case 'z': |
|
case 'A': case 'B': case 'C': case 'D': case 'E': |
|
case 'F': case 'G': case 'H': case 'I': case 'J': |
|
case 'K': case 'L': case 'M': case 'N': case 'O': |
|
case 'P': case 'Q': case 'R': case 'S': case 'T': |
|
case 'U': case 'V': case 'W': case 'X': case 'Y': |
|
case 'Z': |
|
case '0': case '1': case '2': case '3': case '4': |
|
case '5': case '6': case '7': case '8': case '9': |
|
case '$': case '_': |
|
break; |
|
|
|
default: |
|
if (!Character.isJavaLetterOrDigit((char)ch)) { |
|
idValue = Identifier.lookup(bufferString()); |
|
token = idValue.getType(); |
|
return; |
|
} |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* The ending position of the current token |
|
*/ |
|
|
|
public long getEndPos() { |
|
return in.pos; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public IdentifierToken getIdToken() { |
|
return (token != IDENT) ? null : new IdentifierToken(pos, idValue); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public long scan() throws IOException { |
|
return xscan(); |
|
} |
|
|
|
protected long xscan() throws IOException { |
|
final ScannerInputReader in = this.in; |
|
long retPos = pos; |
|
prevPos = in.pos; |
|
docComment = null; |
|
while (true) { |
|
pos = in.pos; |
|
|
|
switch (ch) { |
|
case EOF: |
|
token = EOF; |
|
return retPos; |
|
|
|
case '\n': |
|
if (scanComments) { |
|
ch = ' '; |
|
// Avoid this path the next time around. |
|
// Do not just call in.read; we want to present |
|
|
|
token = COMMENT; |
|
return retPos; |
|
} |
|
case ' ': |
|
case '\t': |
|
case '\f': |
|
ch = in.read(); |
|
break; |
|
|
|
case '/': |
|
switch (ch = in.read()) { |
|
case '/': |
|
|
|
while (((ch = in.read()) != EOF) && (ch != '\n')); |
|
if (scanComments) { |
|
token = COMMENT; |
|
return retPos; |
|
} |
|
break; |
|
|
|
case '*': |
|
ch = in.read(); |
|
if (ch == '*') { |
|
docComment = scanDocComment(); |
|
} else { |
|
skipComment(); |
|
} |
|
if (scanComments) { |
|
return retPos; |
|
} |
|
break; |
|
|
|
case '=': |
|
ch = in.read(); |
|
token = ASGDIV; |
|
return retPos; |
|
|
|
default: |
|
token = DIV; |
|
return retPos; |
|
} |
|
break; |
|
|
|
case '"': |
|
scanString(); |
|
return retPos; |
|
|
|
case '\'': |
|
scanCharacter(); |
|
return retPos; |
|
|
|
case '0': case '1': case '2': case '3': case '4': |
|
case '5': case '6': case '7': case '8': case '9': |
|
scanNumber(); |
|
return retPos; |
|
|
|
case '.': |
|
switch (ch = in.read()) { |
|
case '0': case '1': case '2': case '3': case '4': |
|
case '5': case '6': case '7': case '8': case '9': |
|
count = 0; |
|
putc('.'); |
|
scanReal(); |
|
break; |
|
default: |
|
token = FIELD; |
|
} |
|
return retPos; |
|
|
|
case '{': |
|
ch = in.read(); |
|
token = LBRACE; |
|
return retPos; |
|
|
|
case '}': |
|
ch = in.read(); |
|
token = RBRACE; |
|
return retPos; |
|
|
|
case '(': |
|
ch = in.read(); |
|
token = LPAREN; |
|
return retPos; |
|
|
|
case ')': |
|
ch = in.read(); |
|
token = RPAREN; |
|
return retPos; |
|
|
|
case '[': |
|
ch = in.read(); |
|
token = LSQBRACKET; |
|
return retPos; |
|
|
|
case ']': |
|
ch = in.read(); |
|
token = RSQBRACKET; |
|
return retPos; |
|
|
|
case ',': |
|
ch = in.read(); |
|
token = COMMA; |
|
return retPos; |
|
|
|
case ';': |
|
ch = in.read(); |
|
token = SEMICOLON; |
|
return retPos; |
|
|
|
case '?': |
|
ch = in.read(); |
|
token = QUESTIONMARK; |
|
return retPos; |
|
|
|
case '~': |
|
ch = in.read(); |
|
token = BITNOT; |
|
return retPos; |
|
|
|
case ':': |
|
ch = in.read(); |
|
token = COLON; |
|
return retPos; |
|
|
|
case '-': |
|
switch (ch = in.read()) { |
|
case '-': |
|
ch = in.read(); |
|
token = DEC; |
|
return retPos; |
|
|
|
case '=': |
|
ch = in.read(); |
|
token = ASGSUB; |
|
return retPos; |
|
} |
|
token = SUB; |
|
return retPos; |
|
|
|
case '+': |
|
switch (ch = in.read()) { |
|
case '+': |
|
ch = in.read(); |
|
token = INC; |
|
return retPos; |
|
|
|
case '=': |
|
ch = in.read(); |
|
token = ASGADD; |
|
return retPos; |
|
} |
|
token = ADD; |
|
return retPos; |
|
|
|
case '<': |
|
switch (ch = in.read()) { |
|
case '<': |
|
if ((ch = in.read()) == '=') { |
|
ch = in.read(); |
|
token = ASGLSHIFT; |
|
return retPos; |
|
} |
|
token = LSHIFT; |
|
return retPos; |
|
|
|
case '=': |
|
ch = in.read(); |
|
token = LE; |
|
return retPos; |
|
} |
|
token = LT; |
|
return retPos; |
|
|
|
case '>': |
|
switch (ch = in.read()) { |
|
case '>': |
|
switch (ch = in.read()) { |
|
case '=': |
|
ch = in.read(); |
|
token = ASGRSHIFT; |
|
return retPos; |
|
|
|
case '>': |
|
if ((ch = in.read()) == '=') { |
|
ch = in.read(); |
|
token = ASGURSHIFT; |
|
return retPos; |
|
} |
|
token = URSHIFT; |
|
return retPos; |
|
} |
|
token = RSHIFT; |
|
return retPos; |
|
|
|
case '=': |
|
ch = in.read(); |
|
token = GE; |
|
return retPos; |
|
} |
|
token = GT; |
|
return retPos; |
|
|
|
case '|': |
|
switch (ch = in.read()) { |
|
case '|': |
|
ch = in.read(); |
|
token = OR; |
|
return retPos; |
|
|
|
case '=': |
|
ch = in.read(); |
|
token = ASGBITOR; |
|
return retPos; |
|
} |
|
token = BITOR; |
|
return retPos; |
|
|
|
case '&': |
|
switch (ch = in.read()) { |
|
case '&': |
|
ch = in.read(); |
|
token = AND; |
|
return retPos; |
|
|
|
case '=': |
|
ch = in.read(); |
|
token = ASGBITAND; |
|
return retPos; |
|
} |
|
token = BITAND; |
|
return retPos; |
|
|
|
case '=': |
|
if ((ch = in.read()) == '=') { |
|
ch = in.read(); |
|
token = EQ; |
|
return retPos; |
|
} |
|
token = ASSIGN; |
|
return retPos; |
|
|
|
case '%': |
|
if ((ch = in.read()) == '=') { |
|
ch = in.read(); |
|
token = ASGREM; |
|
return retPos; |
|
} |
|
token = REM; |
|
return retPos; |
|
|
|
case '^': |
|
if ((ch = in.read()) == '=') { |
|
ch = in.read(); |
|
token = ASGBITXOR; |
|
return retPos; |
|
} |
|
token = BITXOR; |
|
return retPos; |
|
|
|
case '!': |
|
if ((ch = in.read()) == '=') { |
|
ch = in.read(); |
|
token = NE; |
|
return retPos; |
|
} |
|
token = NOT; |
|
return retPos; |
|
|
|
case '*': |
|
if ((ch = in.read()) == '=') { |
|
ch = in.read(); |
|
token = ASGMUL; |
|
return retPos; |
|
} |
|
token = MUL; |
|
return retPos; |
|
|
|
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': |
|
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': |
|
case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': |
|
case 's': case 't': case 'u': case 'v': case 'w': case 'x': |
|
case 'y': case 'z': |
|
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': |
|
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': |
|
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': |
|
case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': |
|
case 'Y': case 'Z': |
|
case '$': case '_': |
|
scanIdentifier(); |
|
return retPos; |
|
|
|
case '\u001a': |
|
|
|
if ((ch = in.read()) == EOF) { |
|
token = EOF; |
|
return retPos; |
|
} |
|
env.error(pos, "funny.char"); |
|
ch = in.read(); |
|
break; |
|
|
|
|
|
default: |
|
if (Character.isJavaLetter((char)ch)) { |
|
scanIdentifier(); |
|
return retPos; |
|
} |
|
env.error(pos, "funny.char"); |
|
ch = in.read(); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public void match(int open, int close) throws IOException { |
|
int depth = 1; |
|
|
|
while (true) { |
|
scan(); |
|
if (token == open) { |
|
depth++; |
|
} else if (token == close) { |
|
if (--depth == 0) { |
|
return; |
|
} |
|
} else if (token == EOF) { |
|
env.error(pos, "unbalanced.paren"); |
|
return; |
|
} |
|
} |
|
} |
|
} |