1 /* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later, 9 * or the Apache License Version 2.0. 10 * 11 * Software distributed under the License is distributed on an "AS IS" basis, 12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 13 * for the specific language governing rights and limitations under the 14 * License. 15 */ 16 17 package javassist.compiler; 18 19 class Token { 20 public Token next = null; 21 public int tokenId; 22 23 public long longValue; 24 public double doubleValue; 25 public String textValue; 26 } 27 28 public class Lex implements TokenId { 29 private int lastChar; 30 private StringBuffer textBuffer; 31 private Token currentToken; 32 private Token lookAheadTokens; 33 34 private String input; 35 @SuppressWarnings("unused") 36 private int position, maxlen, lineNumber; 37 38 /** 39 * Constructs a lexical analyzer. 40 */ Lex(String s)41 public Lex(String s) { 42 lastChar = -1; 43 textBuffer = new StringBuffer(); 44 currentToken = new Token(); 45 lookAheadTokens = null; 46 47 input = s; 48 position = 0; 49 maxlen = s.length(); 50 lineNumber = 0; 51 } 52 get()53 public int get() { 54 if (lookAheadTokens == null) 55 return get(currentToken); 56 Token t; 57 currentToken = t = lookAheadTokens; 58 lookAheadTokens = lookAheadTokens.next; 59 return t.tokenId; 60 } 61 62 /** 63 * Looks at the next token. 64 */ lookAhead()65 public int lookAhead() { 66 return lookAhead(0); 67 } 68 lookAhead(int i)69 public int lookAhead(int i) { 70 Token tk = lookAheadTokens; 71 if (tk == null) { 72 lookAheadTokens = tk = currentToken; // reuse an object! 73 tk.next = null; 74 get(tk); 75 } 76 77 for (; i-- > 0; tk = tk.next) 78 if (tk.next == null) { 79 Token tk2; 80 tk.next = tk2 = new Token(); 81 get(tk2); 82 } 83 84 currentToken = tk; 85 return tk.tokenId; 86 } 87 getString()88 public String getString() { 89 return currentToken.textValue; 90 } 91 getLong()92 public long getLong() { 93 return currentToken.longValue; 94 } 95 getDouble()96 public double getDouble() { 97 return currentToken.doubleValue; 98 } 99 get(Token token)100 private int get(Token token) { 101 int t; 102 do { 103 t = readLine(token); 104 } while (t == '\n'); 105 token.tokenId = t; 106 return t; 107 } 108 readLine(Token token)109 private int readLine(Token token) { 110 int c = getNextNonWhiteChar(); 111 if(c < 0) 112 return c; 113 else if(c == '\n') { 114 ++lineNumber; 115 return '\n'; 116 } 117 else if (c == '\'') 118 return readCharConst(token); 119 else if (c == '"') 120 return readStringL(token); 121 else if ('0' <= c && c <= '9') 122 return readNumber(c, token); 123 else if(c == '.'){ 124 c = getc(); 125 if ('0' <= c && c <= '9') { 126 StringBuffer tbuf = textBuffer; 127 tbuf.setLength(0); 128 tbuf.append('.'); 129 return readDouble(tbuf, c, token); 130 } 131 ungetc(c); 132 return readSeparator('.'); 133 } 134 else if (Character.isJavaIdentifierStart((char)c)) 135 return readIdentifier(c, token); 136 return readSeparator(c); 137 } 138 getNextNonWhiteChar()139 private int getNextNonWhiteChar() { 140 int c; 141 do { 142 c = getc(); 143 if (c == '/') { 144 c = getc(); 145 if (c == '/') 146 do { 147 c = getc(); 148 } while (c != '\n' && c != '\r' && c != -1); 149 else if (c == '*') 150 while (true) { 151 c = getc(); 152 if (c == -1) 153 break; 154 else if (c == '*') 155 if ((c = getc()) == '/') { 156 c = ' '; 157 break; 158 } 159 else 160 ungetc(c); 161 } 162 else { 163 ungetc(c); 164 c = '/'; 165 } 166 } 167 } while(isBlank(c)); 168 return c; 169 } 170 readCharConst(Token token)171 private int readCharConst(Token token) { 172 int c; 173 int value = 0; 174 while ((c = getc()) != '\'') 175 if (c == '\\') 176 value = readEscapeChar(); 177 else if (c < 0x20) { 178 if (c == '\n') 179 ++lineNumber; 180 181 return BadToken; 182 } 183 else 184 value = c; 185 186 token.longValue = value; 187 return CharConstant; 188 } 189 readEscapeChar()190 private int readEscapeChar() { 191 int c = getc(); 192 if (c == 'n') 193 c = '\n'; 194 else if (c == 't') 195 c = '\t'; 196 else if (c == 'r') 197 c = '\r'; 198 else if (c == 'f') 199 c = '\f'; 200 else if (c == '\n') 201 ++lineNumber; 202 203 return c; 204 } 205 readStringL(Token token)206 private int readStringL(Token token) { 207 int c; 208 StringBuffer tbuf = textBuffer; 209 tbuf.setLength(0); 210 for (;;) { 211 while ((c = getc()) != '"') { 212 if (c == '\\') 213 c = readEscapeChar(); 214 else if (c == '\n' || c < 0) { 215 ++lineNumber; 216 return BadToken; 217 } 218 219 tbuf.append((char)c); 220 } 221 222 for (;;) { 223 c = getc(); 224 if (c == '\n') 225 ++lineNumber; 226 else if (!isBlank(c)) 227 break; 228 } 229 230 if (c != '"') { 231 ungetc(c); 232 break; 233 } 234 } 235 236 token.textValue = tbuf.toString(); 237 return StringL; 238 } 239 readNumber(int c, Token token)240 private int readNumber(int c, Token token) { 241 long value = 0; 242 int c2 = getc(); 243 if (c == '0') 244 if (c2 == 'X' || c2 == 'x') 245 for (;;) { 246 c = getc(); 247 if ('0' <= c && c <= '9') 248 value = value * 16 + (c - '0'); 249 else if ('A' <= c && c <= 'F') 250 value = value * 16 + (c - 'A' + 10); 251 else if ('a' <= c && c <= 'f') 252 value = value * 16 + (c - 'a' + 10); 253 else { 254 token.longValue = value; 255 if (c == 'L' || c == 'l') 256 return LongConstant; 257 ungetc(c); 258 return IntConstant; 259 } 260 } 261 else if ('0' <= c2 && c2 <= '7') { 262 value = c2 - '0'; 263 for (;;) { 264 c = getc(); 265 if ('0' <= c && c <= '7') 266 value = value * 8 + (c - '0'); 267 else { 268 token.longValue = value; 269 if (c == 'L' || c == 'l') 270 return LongConstant; 271 ungetc(c); 272 return IntConstant; 273 } 274 } 275 } 276 277 value = c - '0'; 278 while ('0' <= c2 && c2 <= '9') { 279 value = value * 10 + c2 - '0'; 280 c2 = getc(); 281 } 282 283 token.longValue = value; 284 if (c2 == 'F' || c2 == 'f') { 285 token.doubleValue = value; 286 return FloatConstant; 287 } 288 else if (c2 == 'E' || c2 == 'e' 289 || c2 == 'D' || c2 == 'd' || c2 == '.') { 290 StringBuffer tbuf = textBuffer; 291 tbuf.setLength(0); 292 tbuf.append(value); 293 return readDouble(tbuf, c2, token); 294 } 295 else if (c2 == 'L' || c2 == 'l') 296 return LongConstant; 297 else { 298 ungetc(c2); 299 return IntConstant; 300 } 301 } 302 readDouble(StringBuffer sbuf, int c, Token token)303 private int readDouble(StringBuffer sbuf, int c, Token token) { 304 if (c != 'E' && c != 'e' && c != 'D' && c != 'd') { 305 sbuf.append((char)c); 306 for (;;) { 307 c = getc(); 308 if ('0' <= c && c <= '9') 309 sbuf.append((char)c); 310 else 311 break; 312 } 313 } 314 315 if (c == 'E' || c == 'e') { 316 sbuf.append((char)c); 317 c = getc(); 318 if (c == '+' || c == '-') { 319 sbuf.append((char)c); 320 c = getc(); 321 } 322 323 while ('0' <= c && c <= '9') { 324 sbuf.append((char)c); 325 c = getc(); 326 } 327 } 328 329 try { 330 token.doubleValue = Double.parseDouble(sbuf.toString()); 331 } 332 catch (NumberFormatException e) { 333 return BadToken; 334 } 335 336 if (c == 'F' || c == 'f') 337 return FloatConstant; 338 if (c != 'D' && c != 'd') 339 ungetc(c); 340 341 return DoubleConstant; 342 } 343 344 // !"#$%&'( )*+,-./0 12345678 9:;<=>? 345 private static final int[] equalOps 346 = { NEQ, 0, 0, 0, MOD_E, AND_E, 0, 0, 347 0, MUL_E, PLUS_E, 0, MINUS_E, 0, DIV_E, 0, 348 0, 0, 0, 0, 0, 0, 0, 0, 349 0, 0, 0, LE, EQ, GE, 0 }; 350 readSeparator(int c)351 private int readSeparator(int c) { 352 int c2, c3; 353 if ('!' <= c && c <= '?') { 354 int t = equalOps[c - '!']; 355 if (t == 0) 356 return c; 357 c2 = getc(); 358 if (c == c2) 359 switch (c) { 360 case '=' : 361 return EQ; 362 case '+' : 363 return PLUSPLUS; 364 case '-' : 365 return MINUSMINUS; 366 case '&' : 367 return ANDAND; 368 case '<' : 369 c3 = getc(); 370 if (c3 == '=') 371 return LSHIFT_E; 372 ungetc(c3); 373 return LSHIFT; 374 case '>' : 375 c3 = getc(); 376 if (c3 == '=') 377 return RSHIFT_E; 378 else if (c3 == '>') { 379 c3 = getc(); 380 if (c3 == '=') 381 return ARSHIFT_E; 382 ungetc(c3); 383 return ARSHIFT; 384 } 385 else { 386 ungetc(c3); 387 return RSHIFT; 388 } 389 default : 390 break; 391 } 392 else if (c2 == '=') 393 return t; 394 } 395 else if (c == '^') { 396 c2 = getc(); 397 if (c2 == '=') 398 return EXOR_E; 399 } 400 else if (c == '|') { 401 c2 = getc(); 402 if (c2 == '=') 403 return OR_E; 404 else if (c2 == '|') 405 return OROR; 406 } 407 else 408 return c; 409 410 ungetc(c2); 411 return c; 412 } 413 readIdentifier(int c, Token token)414 private int readIdentifier(int c, Token token) { 415 StringBuffer tbuf = textBuffer; 416 tbuf.setLength(0); 417 418 do { 419 tbuf.append((char)c); 420 c = getc(); 421 } while (Character.isJavaIdentifierPart((char)c)); 422 423 ungetc(c); 424 425 String name = tbuf.toString(); 426 int t = ktable.lookup(name); 427 if (t >= 0) 428 return t; 429 /* tbuf.toString() is executed quickly since it does not 430 * need memory copy. Using a hand-written extensible 431 * byte-array class instead of StringBuffer is not a good idea 432 * for execution speed. Converting a byte array to a String 433 * object is very slow. Using an extensible char array 434 * might be OK. 435 */ 436 token.textValue = name; 437 return Identifier; 438 } 439 440 private static final KeywordTable ktable = new KeywordTable(); 441 442 static { 443 ktable.append("abstract", ABSTRACT); 444 ktable.append("boolean", BOOLEAN); 445 ktable.append("break", BREAK); 446 ktable.append("byte", BYTE); 447 ktable.append("case", CASE); 448 ktable.append("catch", CATCH); 449 ktable.append("char", CHAR); 450 ktable.append("class", CLASS); 451 ktable.append("const", CONST); 452 ktable.append("continue", CONTINUE); 453 ktable.append("default", DEFAULT); 454 ktable.append("do", DO); 455 ktable.append("double", DOUBLE); 456 ktable.append("else", ELSE); 457 ktable.append("extends", EXTENDS); 458 ktable.append("false", FALSE); 459 ktable.append("final", FINAL); 460 ktable.append("finally", FINALLY); 461 ktable.append("float", FLOAT); 462 ktable.append("for", FOR); 463 ktable.append("goto", GOTO); 464 ktable.append("if", IF); 465 ktable.append("implements", IMPLEMENTS); 466 ktable.append("import", IMPORT); 467 ktable.append("instanceof", INSTANCEOF); 468 ktable.append("int", INT); 469 ktable.append("interface", INTERFACE); 470 ktable.append("long", LONG); 471 ktable.append("native", NATIVE); 472 ktable.append("new", NEW); 473 ktable.append("null", NULL); 474 ktable.append("package", PACKAGE); 475 ktable.append("private", PRIVATE); 476 ktable.append("protected", PROTECTED); 477 ktable.append("public", PUBLIC); 478 ktable.append("return", RETURN); 479 ktable.append("short", SHORT); 480 ktable.append("static", STATIC); 481 ktable.append("strictfp", STRICT); 482 ktable.append("super", SUPER); 483 ktable.append("switch", SWITCH); 484 ktable.append("synchronized", SYNCHRONIZED); 485 ktable.append("this", THIS); 486 ktable.append("throw", THROW); 487 ktable.append("throws", THROWS); 488 ktable.append("transient", TRANSIENT); 489 ktable.append("true", TRUE); 490 ktable.append("try", TRY); 491 ktable.append("void", VOID); 492 ktable.append("volatile", VOLATILE); 493 ktable.append("while", WHILE); 494 } 495 isBlank(int c)496 private static boolean isBlank(int c) { 497 return c == ' ' || c == '\t' || c == '\f' || c == '\r' 498 || c == '\n'; 499 } 500 501 @SuppressWarnings("unused") isDigit(int c)502 private static boolean isDigit(int c) { 503 return '0' <= c && c <= '9'; 504 } 505 ungetc(int c)506 private void ungetc(int c) { 507 lastChar = c; 508 } 509 getTextAround()510 public String getTextAround() { 511 int begin = position - 10; 512 if (begin < 0) 513 begin = 0; 514 515 int end = position + 10; 516 if (end > maxlen) 517 end = maxlen; 518 519 return input.substring(begin, end); 520 } 521 getc()522 private int getc() { 523 if (lastChar < 0) 524 if (position < maxlen) 525 return input.charAt(position++); 526 else 527 return -1; 528 int c = lastChar; 529 lastChar = -1; 530 return c; 531 } 532 } 533