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