javassist.compiler.Lex Maven / Gradle / Ivy
The newest version!
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.compiler;
class Token {
public Token next = null;
public int tokenId;
public long longValue;
public double doubleValue;
public String textValue;
}
public class Lex implements TokenId {
private int lastChar;
private StringBuffer textBuffer;
private Token currentToken;
private Token lookAheadTokens;
private String input;
@SuppressWarnings("unused")
private int position, maxlen, lineNumber;
/**
* Constructs a lexical analyzer.
*/
public Lex(String s) {
lastChar = -1;
textBuffer = new StringBuffer();
currentToken = new Token();
lookAheadTokens = null;
input = s;
position = 0;
maxlen = s.length();
lineNumber = 0;
}
public int get() {
if (lookAheadTokens == null)
return get(currentToken);
Token t;
currentToken = t = lookAheadTokens;
lookAheadTokens = lookAheadTokens.next;
return t.tokenId;
}
/**
* Looks at the next token.
*/
public int lookAhead() {
return lookAhead(0);
}
public int lookAhead(int i) {
Token tk = lookAheadTokens;
if (tk == null) {
lookAheadTokens = tk = currentToken; // reuse an object!
tk.next = null;
get(tk);
}
for (; i-- > 0; tk = tk.next)
if (tk.next == null) {
Token tk2;
tk.next = tk2 = new Token();
get(tk2);
}
currentToken = tk;
return tk.tokenId;
}
public String getString() {
return currentToken.textValue;
}
public long getLong() {
return currentToken.longValue;
}
public double getDouble() {
return currentToken.doubleValue;
}
private int get(Token token) {
int t;
do {
t = readLine(token);
} while (t == '\n');
token.tokenId = t;
return t;
}
private int readLine(Token token) {
int c = getNextNonWhiteChar();
if(c < 0)
return c;
else if(c == '\n') {
++lineNumber;
return '\n';
}
else if (c == '\'')
return readCharConst(token);
else if (c == '"')
return readStringL(token);
else if ('0' <= c && c <= '9')
return readNumber(c, token);
else if(c == '.'){
c = getc();
if ('0' <= c && c <= '9') {
StringBuffer tbuf = textBuffer;
tbuf.setLength(0);
tbuf.append('.');
return readDouble(tbuf, c, token);
}
ungetc(c);
return readSeparator('.');
}
else if (Character.isJavaIdentifierStart((char)c))
return readIdentifier(c, token);
return readSeparator(c);
}
private int getNextNonWhiteChar() {
int c;
do {
c = getc();
if (c == '/') {
c = getc();
if (c == '/')
do {
c = getc();
} while (c != '\n' && c != '\r' && c != -1);
else if (c == '*')
while (true) {
c = getc();
if (c == -1)
break;
else if (c == '*')
if ((c = getc()) == '/') {
c = ' ';
break;
}
else
ungetc(c);
}
else {
ungetc(c);
c = '/';
}
}
} while(isBlank(c));
return c;
}
private int readCharConst(Token token) {
int c;
int value = 0;
while ((c = getc()) != '\'')
if (c == '\\')
value = readEscapeChar();
else if (c < 0x20) {
if (c == '\n')
++lineNumber;
return BadToken;
}
else
value = c;
token.longValue = value;
return CharConstant;
}
private int readEscapeChar() {
int c = getc();
if (c == 'n')
c = '\n';
else if (c == 't')
c = '\t';
else if (c == 'r')
c = '\r';
else if (c == 'f')
c = '\f';
else if (c == '\n')
++lineNumber;
return c;
}
private int readStringL(Token token) {
int c;
StringBuffer tbuf = textBuffer;
tbuf.setLength(0);
for (;;) {
while ((c = getc()) != '"') {
if (c == '\\')
c = readEscapeChar();
else if (c == '\n' || c < 0) {
++lineNumber;
return BadToken;
}
tbuf.append((char)c);
}
for (;;) {
c = getc();
if (c == '\n')
++lineNumber;
else if (!isBlank(c))
break;
}
if (c != '"') {
ungetc(c);
break;
}
}
token.textValue = tbuf.toString();
return StringL;
}
private int readNumber(int c, Token token) {
long value = 0;
int c2 = getc();
if (c == '0')
if (c2 == 'X' || c2 == 'x')
for (;;) {
c = getc();
if ('0' <= c && c <= '9')
value = value * 16 + (c - '0');
else if ('A' <= c && c <= 'F')
value = value * 16 + (c - 'A' + 10);
else if ('a' <= c && c <= 'f')
value = value * 16 + (c - 'a' + 10);
else {
token.longValue = value;
if (c == 'L' || c == 'l')
return LongConstant;
ungetc(c);
return IntConstant;
}
}
else if ('0' <= c2 && c2 <= '7') {
value = c2 - '0';
for (;;) {
c = getc();
if ('0' <= c && c <= '7')
value = value * 8 + (c - '0');
else {
token.longValue = value;
if (c == 'L' || c == 'l')
return LongConstant;
ungetc(c);
return IntConstant;
}
}
}
value = c - '0';
while ('0' <= c2 && c2 <= '9') {
value = value * 10 + c2 - '0';
c2 = getc();
}
token.longValue = value;
if (c2 == 'F' || c2 == 'f') {
token.doubleValue = value;
return FloatConstant;
}
else if (c2 == 'E' || c2 == 'e'
|| c2 == 'D' || c2 == 'd' || c2 == '.') {
StringBuffer tbuf = textBuffer;
tbuf.setLength(0);
tbuf.append(value);
return readDouble(tbuf, c2, token);
}
else if (c2 == 'L' || c2 == 'l')
return LongConstant;
else {
ungetc(c2);
return IntConstant;
}
}
private int readDouble(StringBuffer sbuf, int c, Token token) {
if (c != 'E' && c != 'e' && c != 'D' && c != 'd') {
sbuf.append((char)c);
for (;;) {
c = getc();
if ('0' <= c && c <= '9')
sbuf.append((char)c);
else
break;
}
}
if (c == 'E' || c == 'e') {
sbuf.append((char)c);
c = getc();
if (c == '+' || c == '-') {
sbuf.append((char)c);
c = getc();
}
while ('0' <= c && c <= '9') {
sbuf.append((char)c);
c = getc();
}
}
try {
token.doubleValue = Double.parseDouble(sbuf.toString());
}
catch (NumberFormatException e) {
return BadToken;
}
if (c == 'F' || c == 'f')
return FloatConstant;
if (c != 'D' && c != 'd')
ungetc(c);
return DoubleConstant;
}
// !"#$%&'( )*+,-./0 12345678 9:;<=>?
private static final int[] equalOps
= { NEQ, 0, 0, 0, MOD_E, AND_E, 0, 0,
0, MUL_E, PLUS_E, 0, MINUS_E, 0, DIV_E, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, LE, EQ, GE, 0 };
private int readSeparator(int c) {
int c2, c3;
if ('!' <= c && c <= '?') {
int t = equalOps[c - '!'];
if (t == 0)
return c;
c2 = getc();
if (c == c2)
switch (c) {
case '=' :
return EQ;
case '+' :
return PLUSPLUS;
case '-' :
return MINUSMINUS;
case '&' :
return ANDAND;
case '<' :
c3 = getc();
if (c3 == '=')
return LSHIFT_E;
ungetc(c3);
return LSHIFT;
case '>' :
c3 = getc();
if (c3 == '=')
return RSHIFT_E;
else if (c3 == '>') {
c3 = getc();
if (c3 == '=')
return ARSHIFT_E;
ungetc(c3);
return ARSHIFT;
}
else {
ungetc(c3);
return RSHIFT;
}
default :
break;
}
else if (c2 == '=')
return t;
}
else if (c == '^') {
c2 = getc();
if (c2 == '=')
return EXOR_E;
}
else if (c == '|') {
c2 = getc();
if (c2 == '=')
return OR_E;
else if (c2 == '|')
return OROR;
}
else
return c;
ungetc(c2);
return c;
}
private int readIdentifier(int c, Token token) {
StringBuffer tbuf = textBuffer;
tbuf.setLength(0);
do {
tbuf.append((char)c);
c = getc();
} while (Character.isJavaIdentifierPart((char)c));
ungetc(c);
String name = tbuf.toString();
int t = ktable.lookup(name);
if (t >= 0)
return t;
/* tbuf.toString() is executed quickly since it does not
* need memory copy. Using a hand-written extensible
* byte-array class instead of StringBuffer is not a good idea
* for execution speed. Converting a byte array to a String
* object is very slow. Using an extensible char array
* might be OK.
*/
token.textValue = name;
return Identifier;
}
private static final KeywordTable ktable = new KeywordTable();
static {
ktable.append("abstract", ABSTRACT);
ktable.append("boolean", BOOLEAN);
ktable.append("break", BREAK);
ktable.append("byte", BYTE);
ktable.append("case", CASE);
ktable.append("catch", CATCH);
ktable.append("char", CHAR);
ktable.append("class", CLASS);
ktable.append("const", CONST);
ktable.append("continue", CONTINUE);
ktable.append("default", DEFAULT);
ktable.append("do", DO);
ktable.append("double", DOUBLE);
ktable.append("else", ELSE);
ktable.append("extends", EXTENDS);
ktable.append("false", FALSE);
ktable.append("final", FINAL);
ktable.append("finally", FINALLY);
ktable.append("float", FLOAT);
ktable.append("for", FOR);
ktable.append("goto", GOTO);
ktable.append("if", IF);
ktable.append("implements", IMPLEMENTS);
ktable.append("import", IMPORT);
ktable.append("instanceof", INSTANCEOF);
ktable.append("int", INT);
ktable.append("interface", INTERFACE);
ktable.append("long", LONG);
ktable.append("native", NATIVE);
ktable.append("new", NEW);
ktable.append("null", NULL);
ktable.append("package", PACKAGE);
ktable.append("private", PRIVATE);
ktable.append("protected", PROTECTED);
ktable.append("public", PUBLIC);
ktable.append("return", RETURN);
ktable.append("short", SHORT);
ktable.append("static", STATIC);
ktable.append("strictfp", STRICT);
ktable.append("super", SUPER);
ktable.append("switch", SWITCH);
ktable.append("synchronized", SYNCHRONIZED);
ktable.append("this", THIS);
ktable.append("throw", THROW);
ktable.append("throws", THROWS);
ktable.append("transient", TRANSIENT);
ktable.append("true", TRUE);
ktable.append("try", TRY);
ktable.append("void", VOID);
ktable.append("volatile", VOLATILE);
ktable.append("while", WHILE);
}
private static boolean isBlank(int c) {
return c == ' ' || c == '\t' || c == '\f' || c == '\r'
|| c == '\n';
}
@SuppressWarnings("unused")
private static boolean isDigit(int c) {
return '0' <= c && c <= '9';
}
private void ungetc(int c) {
lastChar = c;
}
public String getTextAround() {
int begin = position - 10;
if (begin < 0)
begin = 0;
int end = position + 10;
if (end > maxlen)
end = maxlen;
return input.substring(begin, end);
}
private int getc() {
if (lastChar < 0)
if (position < maxlen)
return input.charAt(position++);
else
return -1;
int c = lastChar;
lastChar = -1;
return c;
}
}