• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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