001    /*
002     * JastAddJ is covered by the modified BSD License. You should have received a copy of the
003     * modified BSD license with this compiler.
004     * 
005     * Copyright (c) 2011, Jesper Öqvist <jesper.oqvist@cs.lth.se>
006     * All rights reserved.
007     */
008    
009    /**
010     * Improved error reporting and extended parsing of literals for Java 7.
011     * Adds support for binary literals and underscores in numeric literals.
012     *
013     * Range errors for literals can not be detected until we try to compute
014     * the value of a literal. Other syntax errors for literals can be found
015     * during scanning/parsing.
016     */
017    aspect Literals {
018    
019        /**
020         * @return true if this floating point literal is equivalent to a zero literal
021         */
022        syn lazy boolean FloatingPointLiteral.isZero() {
023                for(int i = 0; i < digits.length(); i++) {
024                        char c = digits.charAt(i);
025                        if (c == 'e' || c == 'p') break;
026                        if (c != '0' && c != '.') {
027                                return false;
028                        }
029                }
030                return true;
031        }
032    
033        /**
034         * @return true if this floating point literal is equivalent to a zero literal
035         */
036        syn lazy boolean DoubleLiteral.isZero() {
037                for(int i = 0; i < digits.length(); i++) {
038                        char c = digits.charAt(i);
039                        if (c == 'e' || c == 'p') break;
040                        if (c != '0' && c != '.') {
041                                return false;
042                        }
043                }
044                return true;
045        }
046    
047        /**
048         * @return a fresh double literal representing the given value
049         */
050        refine BytecodeCONSTANT public static Literal
051        Literal.buildDoubleLiteral(double value) {
052                String digits = Double.toString(value);
053                NumericLiteral lit = new DoubleLiteral(digits);
054                lit.setDigits(digits);
055                lit.setKind(NumericLiteral.DECIMAL);
056                return lit;
057        }
058    
059        /**
060         * @return a fresh float literal representing the given value
061         */
062        refine BytecodeCONSTANT public static Literal
063        Literal.buildFloatLiteral(float value) {
064                String digits = Float.toString(value);
065                NumericLiteral lit = new FloatingPointLiteral(digits);
066                lit.setDigits(digits);
067                lit.setKind(NumericLiteral.DECIMAL);
068                return lit;
069        }
070    
071        /**
072         * @return a fresh integer literal representing the given value
073         */
074        refine BytecodeCONSTANT public static Literal
075        Literal.buildIntegerLiteral(int value) {
076                String digits = Integer.toHexString(value);
077                NumericLiteral lit = new IntegerLiteral("0x"+digits);
078                lit.setDigits(digits.toLowerCase());
079                lit.setKind(NumericLiteral.HEXADECIMAL);
080                return lit;
081        }
082    
083        /**
084         * @return a fresh long literal representing the given value
085         */
086        refine BytecodeCONSTANT public static Literal
087        Literal.buildLongLiteral(long value) {
088                String digits = Long.toHexString(value);
089                NumericLiteral lit = new LongLiteral("0x"+digits);
090                lit.setDigits(digits.toLowerCase());
091                lit.setKind(NumericLiteral.HEXADECIMAL);
092                return lit;
093        }
094    
095        /**
096         * Defer pretty printing to superclass.
097         */
098        refine PrettyPrint public void LongLiteral.toString(StringBuffer s) {
099                super.toString(s);
100       }
101    
102       /**
103        * Defer pretty printing to superclass.
104        */
105       refine PrettyPrint public void FloatingPointLiteral.toString(StringBuffer s) {
106               super.toString(s);
107       }
108    
109       /**
110        * Defer pretty printing to superclass.
111        */
112       refine PrettyPrint public void DoubleLiteral.toString(StringBuffer s) {
113               super.toString(s);
114       }
115    
116       /**
117        * Check for and report literal-out-of-bounds error.
118        * If the constant is error-marked, there exists a literal out of bounds error.
119        */
120       refine TypeCheck public void IntegerLiteral.typeCheck() {
121               if (constant().error)
122                       error("The integer literal \""+getLITERAL()+"\" is too large for type int.");
123    
124       }
125    
126       /**
127        * Check for and report literal-out-of-bounds error.
128        * If the constant is error-marked, there exists a literal out of bounds error.
129        */
130       refine TypeCheck public void LongLiteral.typeCheck() {
131               if(constant().error)
132                       error("The integer literal \""+getLITERAL()+"\" is too large for type long.");
133       }
134    
135       /**
136        * Parse this literal and return a fresh Constant.
137        * @return a fresh Constant representing this IntegerLiteral
138        */
139       eq IntegerLiteral.constant() {
140               long l = 0;
141               try {
142                       l = parseLong();
143               } catch (NumberFormatException e) {
144                       Constant c = Constant.create(0L);
145                       c.error = true;
146                       return c;
147               }
148               Constant c = Constant.create((int)l);
149               if (l != (0xFFFFFFFFL & ((int) l)) &&
150                               l != ((long) ((int) l)) ) {
151                       c.error = true;
152                       //System.err.println("Can not cast to integer: "+l+" ("+((int)l)+")");
153               }
154               return c;
155       }
156    
157       /**
158        * Parse this literal and return a fresh Constant.
159        * @return a fresh Constant representing this LongLiteral
160        */
161       eq LongLiteral.constant() {
162               try {
163                       return Constant.create(parseLong());
164               } catch (NumberFormatException e) {
165                       Constant c = Constant.create(0L);
166                       c.error = true;
167                       return c;
168               }
169       }
170    
171       /**
172        * Parse this literal and return a fresh Constant.
173        * @return a fresh Constant representing this FloatingPointLiteral
174        */
175       eq FloatingPointLiteral.constant() {
176               try {
177                       return Constant.create(Float.parseFloat(getDigits()));
178               }
179               catch (NumberFormatException e) {
180                       Constant c = Constant.create(0.0f);
181                       c.error = true;
182                       return c;
183               }
184       }
185    
186       /**
187        * Parse this literal and return a fresh Constant.
188        * @return a fresh Constant representing this DoubleLiteral
189        */
190       eq DoubleLiteral.constant() {
191               try {
192                       return Constant.create(Double.parseDouble(getDigits()));
193               }
194               catch (NumberFormatException e) {
195                       Constant c = Constant.create(0.0d);
196                       c.error = true;
197                       return c;
198               }
199       }
200    
201       /**
202        * This is a refactored version of Literal.parseLong which supports
203        * binary literals. This version of parseLong is implemented as an
204        * attribute rather than a static method. Perhaps some slight
205        * performance boost could be gained from keeping it static, but with
206        * the loss of declarative- and ReRAGness.
207        *
208        * There exists only a parseLong, and not a parseInteger. Parsing
209        * of regular integer literals works the same, but with stricter
210        * bounds requirements on the resulting parsed value.
211        */
212       syn long NumericLiteral.parseLong() {
213               switch (getKind()) {
214                       case HEXADECIMAL:
215                               return parseLongHexadecimal();
216                       case OCTAL:
217                               return parseLongOctal();
218                       case BINARY:
219                               return parseLongBinary();
220                       case DECIMAL:
221                       default:
222                               return parseLongDecimal();
223               }
224       }
225    
226       /**
227        * Parse a hexadecimal long literal.
228        *
229        * @throws NumberFormatException if the literal is too large.
230        */
231       syn long NumericLiteral.parseLongHexadecimal() {
232               long val = 0;
233               if (digits.length() > 16) {
234                       for (int i = 0; i < digits.length()-16; i++)
235                               if (digits.charAt(i) != '0')
236                                       throw new NumberFormatException("");
237               }
238               for (int i = 0; i < digits.length(); i++) {
239                       int c = digits.charAt(i);
240                       if (c >= 'a' && c <= 'f')
241                               c = c - 'a' + 10;
242                       else
243                               c = c - '0';
244                       val = val * 16 + c;
245               }
246               return val;
247       }
248    
249       /**
250        * Parse an octal long literal.
251        *
252        * @throws NumberFormatException if the literal is too large.
253        */
254       syn long NumericLiteral.parseLongOctal() {
255               long val = 0;
256               if (digits.length() > 21) {
257                       for (int i = 0; i < digits.length() - 21; i++)
258                               if (i == digits.length() - 21 - 1) {
259                                       if(digits.charAt(i) != '0' && digits.charAt(i) != '1')
260                                               throw new NumberFormatException("");
261                               } else {
262                                       if(digits.charAt(i) != '0')
263                                               throw new NumberFormatException("");
264                               }
265               }
266               for (int i = 0; i < digits.length(); i++) {
267                       int c = digits.charAt(i) - '0';
268                       val = val * 8 + c;
269               }
270               return val;
271       }
272    
273       /**
274        * Parse a binary long literal.
275        *
276        * @throws NumberFormatException if the literal is too large.
277        */
278       syn long NumericLiteral.parseLongBinary() {
279               long val = 0;
280               if (digits.length() > 64) {
281                       for (int i = 0; i < digits.length()-64; i++)
282                               if (digits.charAt(i) != '0')
283                                       throw new NumberFormatException("");
284               }
285               for (int i = 0; i < digits.length(); ++i) {
286                       if (digits.charAt(i) == '1')
287                               val |= 1L << (digits.length()-i-1);
288               }
289               return val;
290       }
291    
292       /**
293        * Parse an octal long literal.
294        * @throws NumberFormatException if the literal is too large.
295        */
296       syn long NumericLiteral.parseLongDecimal() {
297               long val = 0;
298               long prev = 0;
299               for (int i = 0; i < digits.length(); i++) {
300                       prev = val;
301                       int c = digits.charAt(i);
302                       if(c >= '0' && c <= '9')
303                               c = c - '0';
304                       else
305                               throw new NumberFormatException("");
306                       val = val * 10 + c;
307                       if (val < prev) {
308                               boolean negMinValue = i == (digits.length()-1) &&
309                                       isNegative() && val == Long.MIN_VALUE;
310                               if (!negMinValue)
311                                       throw new NumberFormatException("");
312                       }
313               }
314               if (val == Long.MIN_VALUE)
315                       return val;
316               if (val < 0)
317                       throw new NumberFormatException("");
318               return isNegative() ? -val : val;
319       }
320    
321       public static final int NumericLiteral.DECIMAL = 0;
322       public static final int NumericLiteral.HEXADECIMAL = 1;
323       public static final int NumericLiteral.OCTAL = 2;
324       public static final int NumericLiteral.BINARY = 3;
325    
326       /**
327        * Utility attribute for literal rewriting.
328        * Any of the NumericLiteral subclasses have already
329        * been rewritten and/or parsed, and should not be
330        * rewritten again.
331        *
332        * @return true if this literal is a "raw", not-yet-parsed NumericLiteral
333        */
334       syn boolean NumericLiteral.needsRewrite() = true;
335       eq IntegerLiteral.needsRewrite() = false;
336       eq LongLiteral.needsRewrite() = false;
337       eq FloatingPointLiteral.needsRewrite() = false;
338       eq DoubleLiteral.needsRewrite() = false;
339    
340       rewrite MinusExpr {
341               when(getOperand() instanceof IntegerLiteral && ((IntegerLiteral)getOperand()).isDecimal() && getOperand().isPositive())
342               to IntegerLiteral {
343                       IntegerLiteral original = (IntegerLiteral) getOperand();
344                       IntegerLiteral literal = new IntegerLiteral("-"+
345                                       original.getLITERAL());
346                       literal.setDigits(original.getDigits());
347                       literal.setKind(original.getKind());
348                       return literal;
349               }
350       }
351    
352       rewrite MinusExpr {
353               when(getOperand() instanceof LongLiteral && ((LongLiteral)getOperand()).isDecimal() && getOperand().isPositive())
354               to LongLiteral {
355                       LongLiteral original = (LongLiteral) getOperand();
356                       LongLiteral literal = new LongLiteral("-"+
357                                       original.getLITERAL());
358                       literal.setDigits(original.getDigits());
359                       literal.setKind(original.getKind());
360                       return literal;
361               }
362       }
363    
364       rewrite NumericLiteral {
365               when (needsRewrite())
366               to Literal {
367                       return parse();
368               }
369       }
370    
371       syn boolean NumericLiteral.isNegative() = getLITERAL().charAt(0) == '-';
372    
373       /**
374        * Get the trimmed digits of this literal, excluding
375        * underscore, prefix and suffix.
376        */
377       syn String NumericLiteral.getDigits() = digits;
378    
379       /**
380        * The trimmed digits.
381        */
382       protected String NumericLiteral.digits = "";
383    
384       /**
385        * Sets the trimmed digits of this literal.
386        */
387       public void NumericLiteral.setDigits(String digits) {
388               this.digits = digits;
389       }
390    
391       /**
392        * The literal kind tells which kind of literal it is;
393        * it's either a DECIMAL, HEXADECIMAL, OCTAL or BINARY literal.
394        */
395       syn int NumericLiteral.getKind() = kind;
396    
397       /**
398        * The literal kind tells which kind of literal it is;
399        * it's either a DECIMAL, HEXADECIMAL, OCTAL or BINARY literal.
400        */
401       protected int NumericLiteral.kind = NumericLiteral.DECIMAL;
402    
403       /**
404        * Sets the literal kind.
405        */
406       public void NumericLiteral.setKind(int kind) {
407               this.kind = kind;
408       }
409    
410       /**
411        * Get the radix of this literal.
412        * @return 16 (hex), 10 (decimal), 8 (octal) or 2 (binary)
413        */
414       syn int NumericLiteral.getRadix() {
415               switch (kind) {
416                       case HEXADECIMAL:
417                               return 16;
418                       case OCTAL:
419                               return 8;
420                       case BINARY:
421                               return 2;
422                       case DECIMAL:
423                       default:
424                               return 10;
425               }
426       }
427    
428       /**
429        * @return true if the literal is a decimal literal
430        */
431       syn boolean NumericLiteral.isDecimal() = kind == DECIMAL;
432    
433       /**
434        * @return true if the literal is a hexadecimal literal
435        */
436       syn boolean NumericLiteral.isHex() = kind == HEXADECIMAL;
437    
438       /**
439        * @return true if the literal is an octal literal
440        */
441       syn boolean NumericLiteral.isOctal() = kind == OCTAL;
442    
443       /**
444        * @return true if the literal is a binary literal
445        */
446       syn boolean NumericLiteral.isBinary() = kind == BINARY;
447    
448       /**
449        * Fetches the immediately enclosing compilation unit.
450        */
451       inh CompilationUnit ASTNode.compilationUnit();
452    
453       /**
454        * The type of an IllegalLiteral does not matter,
455        * as it is only a placeholder literal for error messages.
456        */
457       eq IllegalLiteral.type() = unknownType();
458    
459       /**
460        * The type of a NumericLiteral is undefined.
461        * The literal must be parsed before it can have a type.
462        */
463       eq NumericLiteral.type() = unknownType();
464    
465       /**
466        * Error processing for literals.
467        * Include the token range from parsing.
468        */
469       public void IllegalLiteral.collectErrors() {
470               int line = getLine(LITERALstart);
471               int column = getColumn(LITERALstart);
472               int endLine = getLine(LITERALend);
473               int endColumn = getColumn(LITERALend);
474               compilationUnit().errors.add(new Problem(sourceFile(),
475                                       getLITERAL(), line, column, endLine, endColumn,
476                                       Problem.Severity.ERROR, Problem.Kind.LEXICAL));
477       }
478    
479       /**
480        * The NumericLiteral class is used to parse the "value characters"
481        * from a numeric literal. Value characters are those that have a
482        * value in the literal, and are not fillers such as underscore.
483        *
484        * NumericLiteral's parseLiteral function also does error checking
485        * on numeric literals and returns an IllegalLiteral if any syntax
486        * error is detected in the literal.
487        */
488       public class NumericLiteral {
489               private StringBuffer buf = new StringBuffer();
490               private int idx = 0;
491               private boolean whole;// have whole part?
492               private boolean fraction;// have fraction part?
493               private boolean exponent;// have exponent part?
494               private boolean floating;// is floating point?
495               private boolean isFloat;
496               private boolean isLong;
497    
498               /**
499                * @return a readable name to describe this literal.
500                */
501               private String name() {
502                       String name;
503                       switch (kind) {
504                               case DECIMAL:
505                                       name = "decimal";
506                                       break;
507                               case HEXADECIMAL:
508                                       name = "hexadecimal";
509                                       break;
510                               case OCTAL:
511                                       name = "octal";
512                                       break;
513                               case BINARY:
514                               default:
515                                       name = "binary";
516                                       break;
517                       }
518                       if (floating)
519                               return name+" floating point";
520                       else
521                               return name;
522               }
523    
524               /**
525                * The next character in the literal is a significant character;
526                * push it onto the buffer.
527                */
528               private void pushChar() {
529                       buf.append(getLITERAL().charAt(idx++));
530               }
531    
532               /**
533                * Skip ahead n chracters in the literal.
534                */
535               private void skip(int n) {
536                       idx += n;
537               }
538    
539               /**
540                * @return true if there exists at least n more characters
541                * in the literal
542                */
543               private boolean have(int n) {
544                       return getLITERAL().length() >= idx+n;
545               }
546    
547               /**
548                * Look at the n'th next character.
549                */
550               private char peek(int n) {
551                       return getLITERAL().charAt(idx+n);
552               }
553    
554               /**
555                * @return true if the character c is a decimal digit
556                */
557               private static final boolean isDecimalDigit(char c) {
558                       return c == '_' || c >= '0' && c <= '9';
559               }
560    
561               /**
562                * @return true if the character c is a hexadecimal digit
563                */
564               private static final boolean isHexadecimalDigit(char c) {
565                       return c == '_' || c >= '0' && c <= '9' ||
566                               c >= 'a' && c <= 'f' ||
567                               c >= 'A' && c <= 'F';
568               }
569    
570               /**
571                * @return true if the character c is a binary digit
572                */
573               private static final boolean isBinaryDigit(char c) {
574                       return c == '_' || c == '0' || c == '1';
575               }
576    
577               /**
578                * @return true if the character c is an underscore
579                */
580               private static final boolean isUnderscore(char c) {
581                       return c == '_';
582               }
583    
584               /**
585                * Parse a literal. If there is a syntax error in the literal,
586                * an IllegalLiteral will be returned.
587                */
588               public Literal parse() {
589                       if (getLITERAL().length() == 0)
590                               throw new IllegalStateException("Empty NumericLiteral");
591    
592                       kind = classifyLiteral();
593    
594                       Literal literal;
595                       if (!floating)
596                               literal = parseDigits();
597                       else
598                               literal = parseFractionPart();
599                       literal.setStart(LITERALstart);
600                       literal.setEnd(LITERALend);
601                       return literal;
602               }
603    
604               /**
605                * Classify the literal.
606                *
607                * @return either DECIMAL, HEXADECIMAL or BINARY
608                */
609               private int classifyLiteral() {
610                       if (peek(0) == '.') {
611                               floating = true;
612                               return DECIMAL;
613                       } else if (peek(0) == '0') {
614                               if (!have(2)) {
615                                       // the only 1-length string that starts with 0 (obvious!)
616                                       return DECIMAL;
617                               } else if (peek(1) == 'x' || peek(1) == 'X') {
618                                       skip(2);
619                                       return HEXADECIMAL;
620                               } else if (peek(1) == 'b' || peek(1) == 'B') {
621                                       skip(2);
622                                       return BINARY;
623                               } else {
624                                       return DECIMAL;
625                               }
626                       } else {
627                               return DECIMAL;
628                       }
629               }
630    
631               /**
632                * If the current character is an underscore, the previous and next
633                * characters need to be valid digits or underscores.
634                *
635                * @return true if the underscore is misplaced
636                */
637               private boolean misplacedUnderscore() {
638                       // first and last characters are never allowed to be an underscore
639                       if (idx == 0 || idx+1 == getLITERAL().length())
640                               return true;
641    
642                       switch (kind) {
643                               case DECIMAL:
644                                       return !(isDecimalDigit(peek(-1)) && isDecimalDigit(peek(1)));
645                               case HEXADECIMAL:
646                                       return !(isHexadecimalDigit(peek(-1)) && isHexadecimalDigit(peek(1)));
647                               case BINARY:
648                                       return !(isBinaryDigit(peek(-1)) && isBinaryDigit(peek(1)));
649                       }
650                       throw new IllegalStateException("Unexpected literal kind");
651               }
652    
653               /**
654                * Report an illegal digit.
655                */
656               private Literal syntaxError(String msg) {
657                       String err = "in "+name()+" literal "+
658                               "\""+getLITERAL()+"\""+": "+msg;
659                       return new IllegalLiteral(err);
660               }
661    
662               private Literal unexpectedCharacter(char c) {
663                       return syntaxError("unexpected character '"+c+"'; not a valid digit");
664               }
665    
666               /**
667                * Returns a string of only the lower case digits of the
668                * parsed numeric literal.
669                */
670               private String getLiteralString() {
671                       return buf.toString().toLowerCase();
672               }
673    
674               /**
675                * Parse and build an IntegerLiteral, LongLiteral,
676                * FloatingPointLiteral or DoubleLiteral. Returns an
677                * IllegalLiteral if the numeric literal can not be
678                * parsed.
679                *
680                * Note: does not perform bounds checks.
681                *
682                * @return a concrete literal on success, or an IllegalLiteral if there is a syntax error
683                */
684               private Literal buildLiteral() {
685                       NumericLiteral literal;
686                       setDigits(buf.toString().toLowerCase());
687    
688                       if (!floating) {
689                               if (!whole)
690                                       return syntaxError("at least one digit is required");
691    
692                               // check if the literal is octal, and if so report illegal digits
693                               if (kind == DECIMAL) {
694                                       if (digits.charAt(0) == '0') {
695                                               kind = OCTAL;
696                                               for (int idx = 1; idx < digits.length(); ++idx) {
697                                                       char c = digits.charAt(idx);
698                                                       if (c < '0' || c > '7')
699                                                               return unexpectedCharacter(c);
700                                               }
701                                       }
702                               }
703                               
704                               if (isLong)
705                                       literal = new LongLiteral(getLITERAL());
706                               else
707                                       literal = new IntegerLiteral(getLITERAL());
708                       } else {
709                               if (kind == HEXADECIMAL && !exponent)
710                                       return syntaxError("exponent is required");
711    
712                               if (!(whole || fraction))
713                                       return syntaxError("at least one digit is required in "+
714                                                       "either the whole or fraction part");
715    
716                               if (kind == HEXADECIMAL)
717                                       digits = "0x"+digits;// digits parsed with Float or Double
718    
719                               if (isFloat)
720                                       literal = new FloatingPointLiteral(getLITERAL());
721                               else
722                                       literal = new DoubleLiteral(getLITERAL());
723                       }
724    
725                       literal.setDigits(getDigits());
726                       literal.setKind(getKind());
727                       return literal;
728               }
729    
730               private Literal parseDigits() {
731                       // while we have at least one more character/digit
732                       while (have(1)) {
733                               char c = peek(0);
734                               switch (c) {
735                                       case '_':
736                                               if (misplacedUnderscore())
737                                                       return syntaxError("misplaced underscore - underscores may only "+
738                                                                       "be used within sequences of digits");
739                                               skip(1);
740                                               continue;
741                                       case '.':
742                                               if (kind != DECIMAL && kind != HEXADECIMAL)
743                                                       return unexpectedCharacter(c);
744                                               return parseFractionPart();
745                                       case 'l':
746                                       case 'L':
747                                               if (have(2))
748                                                       return syntaxError("extra digits/characters "+
749                                                               "after suffix "+c);
750                                               isLong = true;
751                                               skip(1);
752                                               continue;
753                                       case 'f':
754                                       case 'F':
755                                               if (kind == BINARY)
756                                                       return unexpectedCharacter(c);
757                                               isFloat = true;
758                                       case 'd':
759                                       case 'D':
760                                               if (kind == BINARY)
761                                                       return unexpectedCharacter(c);
762                                               if (kind != HEXADECIMAL) {
763                                                       if (have(2))
764                                                               return syntaxError("extra digits/characters "+
765                                                                               "after type suffix "+c);
766                                                       floating = true;
767                                                       skip(1);
768                                               } else {
769                                                       whole = true;
770                                                       pushChar();
771                                               }
772                                               continue;
773                               }
774    
775                               switch (kind) {
776                                       case DECIMAL:
777                                               if (c == 'e' || c == 'E') {
778                                                       return parseExponentPart();
779    
780                                               } else if (c == 'f' || c == 'F') {
781                                                       if (have(2))
782                                                               return syntaxError("extra digits/characters "+
783                                                                               "after type suffix "+c);
784                                                       floating = true;
785                                                       isFloat = true;
786                                                       skip(1);
787                                               } else if (c == 'd' || c == 'D') {
788                                                       if (have(2))
789                                                               return syntaxError("extra digits/characters "+
790                                                                               "after type suffix "+c);
791                                                       floating = true;
792                                                       skip(1);
793                                               } else {
794                                                       if (!isDecimalDigit(c))
795                                                               return unexpectedCharacter(c);
796                                                       whole = true;
797                                                       pushChar();
798                                               }
799                                               continue;
800                                       case HEXADECIMAL:
801                                               if (c == 'p' || c == 'P')
802                                                       return parseExponentPart();
803    
804                                               if (!isHexadecimalDigit(c))
805                                                       return unexpectedCharacter(c);
806                                               whole = true;
807                                               pushChar();
808                                               continue;
809                                       case BINARY:
810                                               if (!isBinaryDigit(c))
811                                                       return unexpectedCharacter(c);
812                                               whole = true;
813                                               pushChar();
814                                               continue;
815                               }
816                       }
817    
818                       return buildLiteral();
819               }
820    
821               private Literal parseFractionPart() {
822                       floating = true;
823    
824                       // current char is the decimal period
825                       pushChar();
826    
827                       // while we have at least one more character/digit
828                       while (have(1)) {
829                               char c = peek(0);
830                               switch (c) {
831                                       case '_':
832                                               if (misplacedUnderscore())
833                                                       return syntaxError("misplaced underscore - underscores may only "+
834                                                                       "be used as separators within sequences of valid digits");
835                                               skip(1);
836                                               continue;
837                                       case '.':
838                                               return syntaxError("multiple decimal periods are not allowed");
839                               }
840    
841                               if (kind == DECIMAL) {
842                                       if (c == 'e' || c == 'E') {
843                                               return parseExponentPart();
844    
845                                       } else if (c == 'f' || c == 'F') {
846                                               if (have(2))
847                                                       return syntaxError("extra digits/characters "+
848                                                                       "after type suffix "+c);
849                                               floating = true;
850                                               isFloat = true;
851                                               skip(1);
852                                       } else if (c == 'd' || c == 'D') {
853                                               if (have(2))
854                                                       return syntaxError("extra digits/characters "+
855                                                                       "after type suffix "+c);
856                                               floating = true;
857                                               skip(1);
858                                       } else {
859                                               if (!isDecimalDigit(c))
860                                                       return unexpectedCharacter(c);
861                                               pushChar();
862                                               fraction = true;
863                                       }
864                               } else { // kind == HEXADECIMAL
865                                       if (c == 'p' || c == 'P')
866                                               return parseExponentPart();
867    
868                                       if (!isHexadecimalDigit(c))
869                                               return unexpectedCharacter(c);
870                                       fraction = true;
871                                       pushChar();
872                               }
873                       }
874    
875                       return buildLiteral();
876               }
877    
878               private Literal parseExponentPart() {
879                       floating = true;
880    
881                       // current char is the exponent specifier char
882                       pushChar();
883    
884                       // exponent sign
885                       if (have(1) && (peek(0) == '+' || peek(0) == '-'))
886                               pushChar();
887    
888                       // while we have at least one more character/digit
889                       while (have(1)) {
890                               char c = peek(0);
891                               switch (c) {
892                                       case '_':
893                                               if (misplacedUnderscore())
894                                                       return syntaxError("misplaced underscore - underscores may only "+
895                                                                       "be used as separators within sequences of valid digits");
896                                               skip(1);
897                                               continue;
898                                       case '-':
899                                       case '+':
900                                               return syntaxError("exponent sign character is only allowed as "+
901                                                               "the first character of the exponent part of a "+
902                                                               "floating point literal");
903                                       case '.':
904                                               return syntaxError("multiple decimal periods are not allowed");
905                                       case 'p':
906                                       case 'P':
907                                               return syntaxError("multiple exponent specifiers are not allowed");
908                                       case 'f':
909                                       case 'F':
910                                               isFloat = true;
911                                       case 'd':
912                                       case 'D':
913                                               if (have(2))
914                                                       return syntaxError("extra digits/characters "+
915                                                                       "after type suffix "+c);
916                                               skip(1);
917                                               continue;
918                               }
919    
920                               // exponent is a signed integer
921                               if (!isDecimalDigit(c))
922                                       return unexpectedCharacter(c);
923                               pushChar();
924                               exponent = true;
925                       }
926    
927                       return buildLiteral();
928               }
929       }
930    }