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 }