Add support for Enum type for mgc version and also add default constructor. Comment...
[IRC.git] / Robust / src / Lex / Lexer.java
1 package Lex;
2
3 import java.io.IOException;
4 import java.io.Reader;
5 import java.io.LineNumberReader;
6 import Parse.Sym;
7
8 /* Java lexer.
9  * Copyright (C) 2002 C. Scott Ananian <cananian@alumni.princeton.edu>
10  * This program is released under the terms of the GPL; see the file
11  * COPYING for more details.  There is NO WARRANTY on this code.
12  */
13
14 public class Lexer {
15   LineNumberReader reader;
16   boolean isJava12;
17   boolean isJava14;
18   boolean isJava15;
19   String line = null;
20   int line_pos = 1;
21     public int line_num = 0;
22   LineList lineL = new LineList(-line_pos, null); // sentinel for line #0
23
24   public Lexer(Reader reader) {
25     this.reader = new LineNumberReader(new EscapedUnicodeReader(reader));
26     this.isJava12 = true;
27     this.isJava14 = true;
28   }
29
30   public java_cup.runtime.Symbol nextToken() throws java.io.IOException {
31     java_cup.runtime.Symbol sym =
32       lookahead==null ? _nextToken() : lookahead.get();
33     last = sym;
34     return sym;
35   }
36   private boolean shouldBePLT() throws java.io.IOException {
37     // look ahead to see if this LT should be changed to a PLT
38     if (last==null || last.sym!=Sym.IDENTIFIER)
39       return false;
40     if (lookahead==null) lookahead = new FIFO(new FIFO.Getter() {
41                                                 java_cup.runtime.Symbol next() throws java.io.IOException
42                                                 { return _nextToken(); }
43                                               });
44     int i=0;
45     // skip past IDENTIFIER (DOT IDENTIFIER)*
46     if (lookahead.peek(i++).sym != Sym.IDENTIFIER)
47       return false;
48     while (lookahead.peek(i).sym == Sym.DOT) {
49       i++;
50       if (lookahead.peek(i++).sym != Sym.IDENTIFIER)
51         return false;
52     }
53     // skip past (LBRACK RBRACK)*
54     while (lookahead.peek(i).sym == Sym.LBRACK) {
55       i++;
56       if (lookahead.peek(i++).sym != Sym.RBRACK)
57         return false;
58     }
59     // now the next sym has to be one of LT GT COMMA EXTENDS IMPLEMENTS
60     switch(lookahead.peek(i).sym) {
61     default:
62       return false;
63
64     case Sym.LT:
65     case Sym.GT:
66     case Sym.COMMA:
67     case Sym.EXTENDS:
68       return true;
69     }
70   }
71   private java_cup.runtime.Symbol last = null;
72   private FIFO lookahead = null;
73   public java_cup.runtime.Symbol _nextToken() throws java.io.IOException {
74     /* tokens are:
75      *  Identifiers/Keywords/true/false/null (start with java letter)
76      *  numeric literal (start with number)
77      *  character literal (start with single quote)
78      *  string (start with double quote)
79      *  separator (parens, braces, brackets, semicolon, comma, period)
80      *  operator (equals, plus, minus, etc)
81      *  whitespace
82      *  comment (start with slash)
83      */
84     InputElement ie;
85     int startpos, endpos;
86     do {
87       startpos = lineL.head + line_pos;
88       ie = getInputElement();
89       if (ie instanceof DocumentationComment)
90         comment = ((Comment)ie).getComment();
91     } while (!(ie instanceof Token));
92     endpos = lineL.head + line_pos - 1;
93
94     // System.out.println(ie.toString()); // uncomment to debug lexer.
95     java_cup.runtime.Symbol sym = ((Token)ie).token();
96     // fix up left/right positions.
97     sym.left = startpos; sym.right = endpos;
98     // return token.
99     return sym;
100   }
101   public boolean debug_lex() throws java.io.IOException {
102     InputElement ie = getInputElement();
103     System.out.println(ie);
104     return !(ie instanceof EOF);
105   }
106
107   String comment;
108   public String lastComment() {
109     return comment;
110   }
111   public void clearComment() {
112     comment="";
113   }
114
115   InputElement getInputElement() throws java.io.IOException {
116     if (line_num == 0)
117       nextLine();
118     if (line==null)
119       return new EOF();
120     if (line.length()<=line_pos) {      // end of line.
121       nextLine();
122       if (line==null)
123         return new EOF();
124     }
125
126     switch (line.charAt(line_pos)) {
127
128       // White space:
129     case ' ':    // ASCII SP
130     case '\t':    // ASCII HT
131     case '\f':    // ASCII FF
132     case '\n':    // LineTerminator
133       return new WhiteSpace(consume());
134
135       // EOF character:
136     case '\020': // ASCII SUB
137       consume();
138       return new EOF();
139
140       // Comment prefix:
141     case '/':
142       return getComment();
143
144       // else, a Token
145     default:
146       return getToken();
147     }
148   }
149   // May get Token instead of Comment.
150   InputElement getComment() throws java.io.IOException {
151     String comment;
152     // line.charAt(line_pos+0) is '/'
153     switch (line.charAt(line_pos+1)) {
154     case '/': // EndOfLineComment
155       comment = line.substring(line_pos+2);
156       line_pos = line.length();
157       return new EndOfLineComment(comment);
158
159     case '*': // TraditionalComment or DocumentationComment
160       line_pos += 2;
161       if (line.charAt(line_pos)=='*') { // DocumentationComment
162         return snarfComment(new DocumentationComment());
163       } else { // TraditionalComment
164         return snarfComment(new TraditionalComment());
165       }
166
167     default: // it's a token, not a comment.
168       return getToken();
169     }
170   }
171
172   Comment snarfComment(Comment c) throws java.io.IOException {
173     StringBuffer text=new StringBuffer();
174     while(true) { // Grab CommentTail
175       while (line.charAt(line_pos)!='*') { // Add NotStar to comment.
176         int star_pos = line.indexOf('*', line_pos);
177         if (star_pos<0) {
178           text.append(line.substring(line_pos));
179           c.appendLine(text.toString()); text.setLength(0);
180           line_pos = line.length();
181           nextLine();
182           if (line==null)
183             throw new IOException("Unterminated comment at end of file.");
184         } else {
185           text.append(line.substring(line_pos, star_pos));
186           line_pos=star_pos;
187         }
188       }
189       // At this point, line.charAt(line_pos)=='*'
190       // Grab CommentTailStar starting at line_pos+1.
191       if (line.charAt(line_pos+1)=='/') { // safe because line ends with '\n'
192         c.appendLine(text.toString()); line_pos+=2; return c;
193       }
194       text.append(line.charAt(line_pos++)); // add the '*'
195     }
196   }
197
198   Token getToken() throws java.io.IOException {
199     // Tokens are: Identifiers, Keywords, Literals, Separators, Operators.
200     switch (line.charAt(line_pos)) {
201       // Separators: (period is a special case)
202     case '(':
203     case ')':
204     case '{':
205     case '}':
206     case '[':
207     case ']':
208     case ';':
209     case ',':
210       return new Separator(consume());
211
212       // Operators:
213     case '=':
214     case '>':
215     case '<':
216     case '!':
217     case '~':
218     case '?':
219     case ':':
220     case '&':
221     case '|':
222     case '+':
223     case '-':
224     case '*':
225     case '/':
226     case '^':
227     case '%':
228       return getOperator();
229
230     case '\'':
231       return getCharLiteral();
232
233     case '\"':
234       return getStringLiteral();
235
236       // a period is a special case:
237     case '.':
238       if (Character.digit(line.charAt(line_pos+1),10)!=-1)
239         return getNumericLiteral();
240       else if (isJava15 &&
241                line.charAt(line_pos+1)=='.' &&
242                line.charAt(line_pos+2)=='.') {
243         consume(); consume(); consume();
244         return new Separator('\u2026'); // unicode ellipsis character.
245       } else return new Separator(consume());
246
247     default:
248       break;
249     }
250     if (Character.isJavaIdentifierStart(line.charAt(line_pos)))
251       return getIdentifier();
252     if (Character.isDigit(line.charAt(line_pos)))
253       return getNumericLiteral();
254     throw new IOException("Illegal character on line "+line_num);
255   }
256
257   static final String[] keywords = new String[] {
258     "abstract", "assert", "atomic", "boolean", "break", "byte", "case", "catch", "char",
259     "class", "const", "continue",
260     "default", "disjoint", "do", "double",
261     "else", "enum",
262     "extends", "external", "final", "finally",
263     "flag", //keyword for failure aware computation
264     "float", "for", "genreach", "getoffset", "global", "goto", "if",
265     "implements",
266     "import", "instanceof", "int",
267     "interface",
268     "isavailable",
269     "long",
270     "native", "new", "optional", "package", "private", "protected", "public", 
271     "rblock", "return",
272     "scratch", "sese", "short", "static", "strictfp", "super", "switch", "synchronized",
273     "tag", "task", "taskexit", //keywords for failure aware computation
274     "this", "throw", "throws", "transient", "try", "void",
275     "volatile", "while"
276   };
277   Token getIdentifier() throws java.io.IOException {
278     // Get id string.
279     StringBuffer sb = new StringBuffer().append(consume());
280
281     if (!Character.isJavaIdentifierStart(sb.charAt(0)))
282       throw new IOException("Invalid Java Identifier on line "+line_num);
283     while (Character.isJavaIdentifierPart(line.charAt(line_pos)))
284       sb.append(consume());
285     String s = sb.toString();
286     // Now check against boolean literals and null literal.
287     if (s.equals("null")) return new NullLiteral();
288     if (s.equals("true")) return new BooleanLiteral(true);
289     if (s.equals("false")) return new BooleanLiteral(false);
290     // Check against keywords.
291     //  pre-java 1.5 compatibility:
292     //if (!isJava15 && s.equals("enum")) return new Identifier(s);
293     //  pre-java 1.4 compatibility:
294     if (!isJava14 && s.equals("assert")) return new Identifier(s);
295     //  pre-java 1.2 compatibility:
296     if (!isJava12 && s.equals("strictfp")) return new Identifier(s);
297     // use binary search.
298     for (int l=0, r=keywords.length; r > l; ) {
299       int x = (l+r)/2, cmp = s.compareTo(keywords[x]);
300       if (cmp < 0) r=x;else l=x+1;
301       if (cmp== 0) return new Keyword(s);
302     }
303     // not a keyword.
304     return new Identifier(s);
305   }
306   NumericLiteral getNumericLiteral() throws java.io.IOException {
307     int i;
308     // leading decimal indicates float.
309     if (line.charAt(line_pos)=='.')
310       return getFloatingPointLiteral();
311     // 0x indicates Hex.
312     if (line.charAt(line_pos)=='0' &&
313         (line.charAt(line_pos+1)=='x' ||
314          line.charAt(line_pos+1)=='X')) {
315       line_pos+=2; return getIntegerLiteral(/*base*/ 16);
316     }
317     // otherwise scan to first non-numeric
318     for (i=line_pos; Character.digit(line.charAt(i),10)!=-1; )
319       i++;
320     switch(line.charAt(i)) { // discriminate based on first non-numeric
321     case '.':
322     case 'f':
323     case 'F':
324     case 'd':
325     case 'D':
326     case 'e':
327     case 'E':
328       return getFloatingPointLiteral();
329
330     case 'L':
331     case 'l':
332     default:
333       if (line.charAt(line_pos)=='0')
334         return getIntegerLiteral(/*base*/ 8);
335       return getIntegerLiteral(/*base*/ 10);
336     }
337   }
338   NumericLiteral getIntegerLiteral(int radix) throws java.io.IOException {
339     long val=0;
340     while (Character.digit(line.charAt(line_pos),radix)!=-1)
341       val = (val*radix) + Character.digit(consume(),radix);
342     if (line.charAt(line_pos) == 'l' ||
343         line.charAt(line_pos) == 'L') {
344       consume();
345       return new LongLiteral(val);
346     }
347     // we compare MAX_VALUE against val/2 to allow constants like
348     // 0xFFFF0000 to get past the test. (unsigned long->signed int)
349     if ((val/2) > Integer.MAX_VALUE ||
350         val    < Integer.MIN_VALUE)
351       throw new IOException("Constant does not fit in integer on line "+line_num);
352     return new IntegerLiteral((int)val);
353   }
354   NumericLiteral getFloatingPointLiteral() throws java.io.IOException {
355     String rep = getDigits();
356     if (line.charAt(line_pos)=='.')
357       rep+=consume() + getDigits();
358     if (line.charAt(line_pos)=='e' ||
359         line.charAt(line_pos)=='E') {
360       rep+=consume();
361       if (line.charAt(line_pos)=='+' ||
362           line.charAt(line_pos)=='-')
363         rep+=consume();
364       rep+=getDigits();
365     }
366     try {
367       switch (line.charAt(line_pos)) {
368       case 'f':
369       case 'F':
370         consume();
371         return new FloatLiteral(Float.valueOf(rep).floatValue());
372
373       case 'd':
374       case 'D':
375         consume();
376
377         /* falls through */
378       default:
379         return new DoubleLiteral(Double.valueOf(rep).doubleValue());
380       }
381     } catch (NumberFormatException e) {
382       throw new IOException("Illegal floating-point on line "+line_num+": "+e);
383     }
384   }
385   String getDigits() {
386     StringBuffer sb = new StringBuffer();
387     while (Character.digit(line.charAt(line_pos),10)!=-1)
388       sb.append(consume());
389     return sb.toString();
390   }
391
392   Operator getOperator() {
393     char first = consume();
394     char second= line.charAt(line_pos);
395
396     switch(first) {
397       // single-character operators.
398     case '~':
399     case '?':
400     case ':':
401       return new Operator(new String(new char[] {first}));
402
403       // doubled operators
404     case '+':
405     case '-':
406     case '&':
407     case '|':
408       if (first==second)
409         return new Operator(new String(new char[] {first, consume()}));
410
411     default:
412       break;
413     }
414     // Check for trailing '='
415     if (second=='=')
416       return new Operator(new String(new char[] {first, consume()}));
417
418     // Special-case '<<', '>>' and '>>>'
419     if ((first=='<' && second=='<') || // <<
420         (first=='>' && second=='>')) {  // >>
421       String op = new String(new char[] {first, consume()});
422       if (first=='>' && line.charAt(line_pos)=='>') // >>>
423         op += consume();
424       if (line.charAt(line_pos)=='=') // <<=, >>=, >>>=
425         op += consume();
426       return new Operator(op);
427     }
428
429     // Otherwise return single operator.
430     return new Operator(new String(new char[] {first}));
431   }
432
433   CharacterLiteral getCharLiteral() throws java.io.IOException {
434     char firstquote = consume();
435     char val;
436     switch (line.charAt(line_pos)) {
437     case '\\':
438       val = getEscapeSequence();
439       break;
440
441     case '\'':
442       throw new IOException("Invalid character literal on line "+line_num);
443
444     case '\n':
445       throw new IOException("Invalid character literal on line "+line_num);
446
447     default:
448       val = consume();
449       break;
450     }
451     char secondquote = consume();
452     if (firstquote != '\'' || secondquote != '\'')
453       throw new IOException("Invalid character literal on line "+line_num);
454     return new CharacterLiteral(val);
455   }
456   StringLiteral getStringLiteral() throws java.io.IOException {
457     char openquote = consume();
458     StringBuffer val = new StringBuffer();
459     while (line.charAt(line_pos)!='\"') {
460       switch(line.charAt(line_pos)) {
461       case '\\':
462         val.append(getEscapeSequence());
463         break;
464
465       case '\n':
466         throw new IOException("Invalid string literal on line " + line_num);
467
468       default:
469         val.append(consume());
470         break;
471       }
472     }
473     char closequote = consume();
474     if (openquote != '\"' || closequote != '\"')
475       throw new IOException("Invalid string literal on line " + line_num);
476
477     return new StringLiteral(val.toString().intern());
478   }
479
480   char getEscapeSequence() throws java.io.IOException {
481     if (consume() != '\\')
482       throw new IOException("Invalid escape sequence on line " + line_num);
483     switch(line.charAt(line_pos)) {
484     case 'b':
485       consume(); return '\b';
486
487     case 't':
488       consume(); return '\t';
489
490     case 'n':
491       consume(); return '\n';
492
493     case 'f':
494       consume(); return '\f';
495
496     case 'r':
497       consume(); return '\r';
498
499     case '\"':
500       consume(); return '\"';
501
502     case '\'':
503       consume(); return '\'';
504
505     case '\\':
506       consume(); return '\\';
507
508     case '0':
509     case '1':
510     case '2':
511     case '3':
512       return (char) getOctal(3);
513
514     case '4':
515     case '5':
516     case '6':
517     case '7':
518       return (char) getOctal(2);
519
520     default:
521       throw new IOException("Invalid escape sequence on line " + line_num);
522     }
523   }
524   int getOctal(int maxlength) throws java.io.IOException {
525     int i, val=0;
526     for (i=0; i<maxlength; i++)
527       if (Character.digit(line.charAt(line_pos), 8)!=-1) {
528         val = (8*val) + Character.digit(consume(), 8);
529       } else break;
530     if ((i==0) || (val>0xFF)) // impossible.
531       throw new IOException("Invalid octal escape sequence in line " + line_num);
532     return val;
533   }
534
535   char consume() {
536     return line.charAt(line_pos++);
537   }
538   void nextLine() throws java.io.IOException {
539     line=reader.readLine();
540     if (line!=null) line=line+'\n';
541     lineL = new LineList(lineL.head+line_pos, lineL); // for error reporting
542     line_pos=0;
543     line_num++;
544   }
545
546   // Deal with error messages.
547   public void errorMsg(String msg, java_cup.runtime.Symbol info) {
548     int n=line_num, c=info.left-lineL.head;
549     for (LineList p = lineL; p!=null; p=p.tail, n--)
550       if (p.head<=info.left) {
551         c=info.left-p.head; break;
552       }
553     System.err.println(msg+" at line "+n);
554     num_errors++;
555   }
556   private int num_errors = 0;
557   public int numErrors() {
558     return num_errors;
559   }
560
561   class LineList {
562     int head;
563     LineList tail;
564     LineList(int head, LineList tail) {
565       this.head = head; this.tail = tail;
566     }
567   }
568 }