@@ -453,18 +453,15 @@ trait Scanners extends ScannersCommon {
453
453
getOperatorRest()
454
454
}
455
455
case '0' =>
456
- def fetchZero () = {
457
- putChar(ch)
456
+ def fetchLeadingZero (): Unit = {
458
457
nextChar()
459
- if (ch == 'x' || ch == 'X' ) {
460
- nextChar()
461
- base = 16
462
- } else {
463
- base = 8
458
+ ch match {
459
+ case 'x' | 'X' => base = 16 ; nextChar()
460
+ case _ => base = 8 // single decimal zero, perhaps
464
461
}
465
- getNumber()
466
462
}
467
- fetchZero()
463
+ fetchLeadingZero()
464
+ getNumber()
468
465
case '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' =>
469
466
base = 10
470
467
getNumber()
@@ -902,62 +899,61 @@ trait Scanners extends ScannersCommon {
902
899
*/
903
900
def charVal : Char = if (strVal.length > 0 ) strVal.charAt(0 ) else 0
904
901
905
- /** Convert current strVal, base to long value
902
+ /** Convert current strVal, base to long value.
906
903
* This is tricky because of max negative value.
904
+ *
905
+ * Conversions in base 10 and 16 are supported. As a permanent migration
906
+ * path, attempts to write base 8 literals except `0` emit a verbose error.
907
907
*/
908
908
def intVal (negated : Boolean ): Long = {
909
- if (token == CHARLIT && ! negated) {
910
- charVal.toLong
911
- } else {
912
- var value : Long = 0
913
- val divider = if (base == 10 ) 1 else 2
914
- val limit : Long =
915
- if (token == LONGLIT ) Long .MaxValue else Int .MaxValue
916
- var i = 0
909
+ def malformed : Long = {
910
+ if (base == 8 ) syntaxError(" Decimal integer literals may not have a leading zero. (Octal syntax is obsolete.)" )
911
+ else syntaxError(" malformed integer number" )
912
+ 0
913
+ }
914
+ def tooBig : Long = {
915
+ syntaxError(" integer number too large" )
916
+ 0
917
+ }
918
+ def intConvert : Long = {
917
919
val len = strVal.length
918
- while (i < len) {
919
- val d = digit2int(strVal charAt i, base)
920
- if (d < 0 ) {
921
- syntaxError(" malformed integer number" )
922
- return 0
923
- }
924
- if (value < 0 ||
925
- limit / (base / divider) < value ||
926
- limit - (d / divider) < value * (base / divider) &&
927
- ! (negated && limit == value * base - 1 + d)) {
928
- syntaxError(" integer number too large" )
929
- return 0
930
- }
931
- value = value * base + d
932
- i += 1
920
+ if (len == 0 ) {
921
+ if (base != 8 ) syntaxError(" missing integer number" ) // e.g., 0x;
922
+ 0
923
+ } else {
924
+ val divider = if (base == 10 ) 1 else 2
925
+ val limit : Long = if (token == LONGLIT ) Long .MaxValue else Int .MaxValue
926
+ @ tailrec def convert (value : Long , i : Int ): Long =
927
+ if (i >= len) value
928
+ else {
929
+ val d = digit2int(strVal charAt i, base)
930
+ if (d < 0 )
931
+ malformed
932
+ else if (value < 0 ||
933
+ limit / (base / divider) < value ||
934
+ limit - (d / divider) < value * (base / divider) &&
935
+ ! (negated && limit == value * base - 1 + d))
936
+ tooBig
937
+ else
938
+ convert(value * base + d, i + 1 )
939
+ }
940
+ val result = convert(0 , 0 )
941
+ if (base == 8 ) malformed else if (negated) - result else result
933
942
}
934
- if (negated) - value else value
935
943
}
944
+ if (token == CHARLIT && ! negated) charVal.toLong else intConvert
936
945
}
937
946
938
947
def intVal : Long = intVal(negated = false )
939
948
940
949
/** Convert current strVal, base to double value
941
- */
950
+ */
942
951
def floatVal (negated : Boolean ): Double = {
943
-
944
- val limit : Double =
945
- if (token == DOUBLELIT ) Double .MaxValue else Float .MaxValue
952
+ val limit : Double = if (token == DOUBLELIT ) Double .MaxValue else Float .MaxValue
946
953
try {
947
954
val value : Double = java.lang.Double .valueOf(strVal).doubleValue()
948
- def isDeprecatedForm = {
949
- val idx = strVal indexOf '.'
950
- (idx == strVal.length - 1 ) || (
951
- (idx >= 0 )
952
- && (idx + 1 < strVal.length)
953
- && (! Character .isDigit(strVal charAt (idx + 1 )))
954
- )
955
- }
956
955
if (value > limit)
957
956
syntaxError(" floating point number too large" )
958
- if (isDeprecatedForm)
959
- syntaxError(" floating point number is missing digit after dot" )
960
-
961
957
if (negated) - value else value
962
958
} catch {
963
959
case _ : NumberFormatException =>
@@ -968,86 +964,44 @@ trait Scanners extends ScannersCommon {
968
964
969
965
def floatVal : Double = floatVal(negated = false )
970
966
971
- def checkNoLetter (): Unit = {
967
+ def checkNoLetter (): Unit = {
972
968
if (isIdentifierPart(ch) && ch >= ' ' )
973
969
syntaxError(" Invalid literal number" )
974
970
}
975
971
976
- /** Read a number into strVal and set base */
977
- protected def getNumber (): Unit = {
978
- val base1 = if (base < 10 ) 10 else base
979
- // Read 8,9's even if format is octal, produce a malformed number error afterwards.
980
- // At this point, we have already read the first digit, so to tell an innocent 0 apart
981
- // from an octal literal 0123... (which we want to disallow), we check whether there
982
- // are any additional digits coming after the first one we have already read.
983
- var notSingleZero = false
984
- while (digit2int(ch, base1) >= 0 ) {
985
- putChar(ch)
986
- nextChar()
987
- notSingleZero = true
988
- }
989
- token = INTLIT
990
-
991
- /* When we know for certain it's a number after using a touch of lookahead */
992
- def restOfNumber () = {
993
- putChar(ch)
994
- nextChar()
972
+ /** Read a number into strVal.
973
+ *
974
+ * The `base` can be 8, 10 or 16, where base 8 flags a leading zero.
975
+ * For ints, base 8 is legal only for the case of exactly one zero.
976
+ */
977
+ protected def getNumber (): Unit = {
978
+ // consume digits of a radix
979
+ def consumeDigits (radix : Int ): Unit =
980
+ while (digit2int(ch, radix) >= 0 ) {
981
+ putChar(ch)
982
+ nextChar()
983
+ }
984
+ // adding decimal point is always OK because `Double valueOf "0."` is OK
985
+ def restOfNonIntegralNumber (): Unit = {
986
+ putChar('.' )
987
+ if (ch == '.' ) nextChar()
995
988
getFraction()
996
989
}
997
- def restOfUncertainToken () = {
998
- def isEfd = ch match { case 'e' | 'E' | 'f' | 'F' | 'd' | 'D' => true ; case _ => false }
999
- def isL = ch match { case 'l' | 'L' => true ; case _ => false }
1000
-
1001
- if (base <= 10 && isEfd)
1002
- getFraction()
1003
- else {
1004
- // Checking for base == 8 is not enough, because base = 8 is set
1005
- // as soon as a 0 is read in `case '0'` of method fetchToken.
1006
- if (base == 8 && notSingleZero) syntaxError(" Non-zero integral values may not have a leading zero." )
1007
- setStrVal()
1008
- if (isL) {
1009
- nextChar()
1010
- token = LONGLIT
1011
- }
1012
- else checkNoLetter()
990
+ // after int: 5e7f, 42L, 42.toDouble but not 42b. Repair 0d.
991
+ def restOfNumber (): Unit = {
992
+ ch match {
993
+ case 'e' | 'E' | 'f' | 'F' |
994
+ 'd' | 'D' => if (cbuf.isEmpty) putChar('0' ); restOfNonIntegralNumber()
995
+ case 'l' | 'L' => token = LONGLIT ; setStrVal() ; nextChar()
996
+ case _ => token = INTLIT ; setStrVal() ; checkNoLetter()
1013
997
}
1014
998
}
1015
999
1016
- if (base > 10 || ch != '.' )
1017
- restOfUncertainToken()
1018
- else {
1019
- val lookahead = lookaheadReader
1020
- val c = lookahead.getc()
1021
-
1022
- /* Prohibit 1. */
1023
- if (! isDigit(c))
1024
- return setStrVal()
1025
-
1026
- val isDefinitelyNumber = (c : @ switch) match {
1027
- /** Another digit is a giveaway. */
1028
- case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' =>
1029
- true
1000
+ // consume leading digits, provisionally an Int
1001
+ consumeDigits(if (base == 16 ) 16 else 10 )
1030
1002
1031
- /* Backquoted idents like 22.`foo`. */
1032
- case '`' =>
1033
- return setStrVal() /* * Note the early return */
1034
-
1035
- /* These letters may be part of a literal, or a method invocation on an Int.
1036
- */
1037
- case 'd' | 'D' | 'f' | 'F' =>
1038
- ! isIdentifierPart(lookahead.getc())
1039
-
1040
- /* A little more special handling for e.g. 5e7 */
1041
- case 'e' | 'E' =>
1042
- val ch = lookahead.getc()
1043
- ! isIdentifierPart(ch) || (isDigit(ch) || ch == '+' || ch == '-' )
1044
-
1045
- case x =>
1046
- ! isIdentifierStart(x)
1047
- }
1048
- if (isDefinitelyNumber) restOfNumber()
1049
- else restOfUncertainToken()
1050
- }
1003
+ val detectedFloat : Boolean = base != 16 && ch == '.' && isDigit(lookaheadReader.getc)
1004
+ if (detectedFloat) restOfNonIntegralNumber() else restOfNumber()
1051
1005
}
1052
1006
1053
1007
/** Parse character literal if current character is followed by \',
0 commit comments