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 }