/*
 * 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.
/** * 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.
/** * 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; } }