summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala192
-rw-r--r--test/files/neg/literals.check40
-rw-r--r--test/files/neg/literals.scala36
-rw-r--r--test/files/run/literals.check69
-rw-r--r--test/files/run/literals.flags1
-rw-r--r--test/files/run/literals.scala39
6 files changed, 175 insertions, 202 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index 9ebc94b5fc..92833d647b 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -453,18 +453,15 @@ trait Scanners extends ScannersCommon {
getOperatorRest()
}
case '0' =>
- def fetchZero() = {
- putChar(ch)
+ def fetchLeadingZero(): Unit = {
nextChar()
- if (ch == 'x' || ch == 'X') {
- nextChar()
- base = 16
- } else {
- base = 8
+ ch match {
+ case 'x' | 'X' => base = 16 ; nextChar()
+ case _ => base = 8 // single decimal zero, perhaps
}
- getNumber()
}
- fetchZero()
+ fetchLeadingZero()
+ getNumber()
case '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' =>
base = 10
getNumber()
@@ -902,62 +899,61 @@ trait Scanners extends ScannersCommon {
*/
def charVal: Char = if (strVal.length > 0) strVal.charAt(0) else 0
- /** Convert current strVal, base to long value
+ /** Convert current strVal, base to long value.
* This is tricky because of max negative value.
+ *
+ * Conversions in base 10 and 16 are supported. As a permanent migration
+ * path, attempts to write base 8 literals except `0` emit a verbose error.
*/
def intVal(negated: Boolean): Long = {
- if (token == CHARLIT && !negated) {
- charVal.toLong
- } else {
- var value: Long = 0
- val divider = if (base == 10) 1 else 2
- val limit: Long =
- if (token == LONGLIT) Long.MaxValue else Int.MaxValue
- var i = 0
+ def malformed: Long = {
+ if (base == 8) syntaxError("Decimal integer literals may not have a leading zero. (Octal syntax is obsolete.)")
+ else syntaxError("malformed integer number")
+ 0
+ }
+ def tooBig: Long = {
+ syntaxError("integer number too large")
+ 0
+ }
+ def intConvert: Long = {
val len = strVal.length
- while (i < len) {
- val d = digit2int(strVal charAt i, base)
- if (d < 0) {
- syntaxError("malformed integer number")
- return 0
- }
- if (value < 0 ||
- limit / (base / divider) < value ||
- limit - (d / divider) < value * (base / divider) &&
- !(negated && limit == value * base - 1 + d)) {
- syntaxError("integer number too large")
- return 0
- }
- value = value * base + d
- i += 1
+ if (len == 0) {
+ if (base != 8) syntaxError("missing integer number") // e.g., 0x;
+ 0
+ } else {
+ val divider = if (base == 10) 1 else 2
+ val limit: Long = if (token == LONGLIT) Long.MaxValue else Int.MaxValue
+ @tailrec def convert(value: Long, i: Int): Long =
+ if (i >= len) value
+ else {
+ val d = digit2int(strVal charAt i, base)
+ if (d < 0)
+ malformed
+ else if (value < 0 ||
+ limit / (base / divider) < value ||
+ limit - (d / divider) < value * (base / divider) &&
+ !(negated && limit == value * base - 1 + d))
+ tooBig
+ else
+ convert(value * base + d, i + 1)
+ }
+ val result = convert(0, 0)
+ if (base == 8) malformed else if (negated) -result else result
}
- if (negated) -value else value
}
+ if (token == CHARLIT && !negated) charVal.toLong else intConvert
}
def intVal: Long = intVal(negated = false)
/** Convert current strVal, base to double value
- */
+ */
def floatVal(negated: Boolean): Double = {
-
- val limit: Double =
- if (token == DOUBLELIT) Double.MaxValue else Float.MaxValue
+ val limit: Double = if (token == DOUBLELIT) Double.MaxValue else Float.MaxValue
try {
val value: Double = java.lang.Double.valueOf(strVal).doubleValue()
- def isDeprecatedForm = {
- val idx = strVal indexOf '.'
- (idx == strVal.length - 1) || (
- (idx >= 0)
- && (idx + 1 < strVal.length)
- && (!Character.isDigit(strVal charAt (idx + 1)))
- )
- }
if (value > limit)
syntaxError("floating point number too large")
- if (isDeprecatedForm)
- syntaxError("floating point number is missing digit after dot")
-
if (negated) -value else value
} catch {
case _: NumberFormatException =>
@@ -968,86 +964,44 @@ trait Scanners extends ScannersCommon {
def floatVal: Double = floatVal(negated = false)
- def checkNoLetter(): Unit = {
+ def checkNoLetter(): Unit = {
if (isIdentifierPart(ch) && ch >= ' ')
syntaxError("Invalid literal number")
}
- /** Read a number into strVal and set base */
- protected def getNumber(): Unit = {
- val base1 = if (base < 10) 10 else base
- // Read 8,9's even if format is octal, produce a malformed number error afterwards.
- // At this point, we have already read the first digit, so to tell an innocent 0 apart
- // from an octal literal 0123... (which we want to disallow), we check whether there
- // are any additional digits coming after the first one we have already read.
- var notSingleZero = false
- while (digit2int(ch, base1) >= 0) {
- putChar(ch)
- nextChar()
- notSingleZero = true
- }
- token = INTLIT
-
- /* When we know for certain it's a number after using a touch of lookahead */
- def restOfNumber() = {
- putChar(ch)
- nextChar()
+ /** Read a number into strVal.
+ *
+ * The `base` can be 8, 10 or 16, where base 8 flags a leading zero.
+ * For ints, base 8 is legal only for the case of exactly one zero.
+ */
+ protected def getNumber(): Unit = {
+ // consume digits of a radix
+ def consumeDigits(radix: Int): Unit =
+ while (digit2int(ch, radix) >= 0) {
+ putChar(ch)
+ nextChar()
+ }
+ // adding decimal point is always OK because `Double valueOf "0."` is OK
+ def restOfNonIntegralNumber(): Unit = {
+ putChar('.')
+ if (ch == '.') nextChar()
getFraction()
}
- def restOfUncertainToken() = {
- def isEfd = ch match { case 'e' | 'E' | 'f' | 'F' | 'd' | 'D' => true ; case _ => false }
- def isL = ch match { case 'l' | 'L' => true ; case _ => false }
-
- if (base <= 10 && isEfd)
- getFraction()
- else {
- // Checking for base == 8 is not enough, because base = 8 is set
- // as soon as a 0 is read in `case '0'` of method fetchToken.
- if (base == 8 && notSingleZero) syntaxError("Non-zero integral values may not have a leading zero.")
- setStrVal()
- if (isL) {
- nextChar()
- token = LONGLIT
- }
- else checkNoLetter()
+ // after int: 5e7f, 42L, 42.toDouble but not 42b. Repair 0d.
+ def restOfNumber(): Unit = {
+ ch match {
+ case 'e' | 'E' | 'f' | 'F' |
+ 'd' | 'D' => if (cbuf.isEmpty) putChar('0'); restOfNonIntegralNumber()
+ case 'l' | 'L' => token = LONGLIT ; setStrVal() ; nextChar()
+ case _ => token = INTLIT ; setStrVal() ; checkNoLetter()
}
}
- if (base > 10 || ch != '.')
- restOfUncertainToken()
- else {
- val lookahead = lookaheadReader
- val c = lookahead.getc()
-
- /* Prohibit 1. */
- if (!isDigit(c))
- return setStrVal()
-
- val isDefinitelyNumber = (c: @switch) match {
- /** Another digit is a giveaway. */
- case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' =>
- true
+ // consume leading digits, provisionally an Int
+ consumeDigits(if (base == 16) 16 else 10)
- /* Backquoted idents like 22.`foo`. */
- case '`' =>
- return setStrVal() /** Note the early return */
-
- /* These letters may be part of a literal, or a method invocation on an Int.
- */
- case 'd' | 'D' | 'f' | 'F' =>
- !isIdentifierPart(lookahead.getc())
-
- /* A little more special handling for e.g. 5e7 */
- case 'e' | 'E' =>
- val ch = lookahead.getc()
- !isIdentifierPart(ch) || (isDigit(ch) || ch == '+' || ch == '-')
-
- case x =>
- !isIdentifierStart(x)
- }
- if (isDefinitelyNumber) restOfNumber()
- else restOfUncertainToken()
- }
+ val detectedFloat: Boolean = base != 16 && ch == '.' && isDigit(lookaheadReader.getc)
+ if (detectedFloat) restOfNonIntegralNumber() else restOfNumber()
}
/** Parse character literal if current character is followed by \',
diff --git a/test/files/neg/literals.check b/test/files/neg/literals.check
new file mode 100644
index 0000000000..148a9346c5
--- /dev/null
+++ b/test/files/neg/literals.check
@@ -0,0 +1,40 @@
+literals.scala:6: error: missing integer number
+ def missingHex: Int = { 0x } // line 4: was: not reported, taken as zero
+ ^
+literals.scala:8: error: Decimal integer literals may not have a leading zero. (Octal syntax is obsolete.)
+ def leadingZeros: Int = { 01 } // line 6: no leading zero
+ ^
+literals.scala:10: error: Decimal integer literals may not have a leading zero. (Octal syntax is obsolete.)
+ def tooManyZeros: Int = { 00 } // line 8: no leading zero
+ ^
+literals.scala:12: error: Decimal integer literals may not have a leading zero. (Octal syntax is obsolete.)
+ def zeroOfNine: Int = { 09 } // line 10: no leading zero
+ ^
+literals.scala:16: error: Decimal integer literals may not have a leading zero. (Octal syntax is obsolete.)
+ def zeroOfNineDot: Int = { 09. } // line 14: malformed integer, ident expected
+ ^
+literals.scala:23: error: missing integer number
+ def missingHex: Int = 0x // line 22: was: not reported, taken as zero
+ ^
+literals.scala:27: error: Decimal integer literals may not have a leading zero. (Octal syntax is obsolete.)
+ def tooManyZeros: Int = 00 // line 26: no leading zero
+ ^
+literals.scala:14: error: identifier expected but '}' found.
+ def orphanDot: Int = { 9. } // line 12: ident expected
+ ^
+literals.scala:16: error: identifier expected but '}' found.
+ def zeroOfNineDot: Int = { 09. } // line 14: malformed integer, ident expected
+ ^
+literals.scala:18: error: ';' expected but double literal found.
+ def noHexFloat: Double = { 0x1.2 } // line 16: ';' expected but double literal found.
+ ^
+literals.scala:25: error: ';' expected but 'def' found.
+ def leadingZeros: Int = 01 // line 24: no leading zero
+ ^
+literals.scala:29: error: ';' expected but 'def' found.
+ def zeroOfNine: Int = 09 // line 28: no leading zero
+ ^
+literals.scala:33: error: identifier expected but 'def' found.
+ def zeroOfNineDot: Int = 09. // line 32: malformed integer, ident expected
+ ^
+13 errors found
diff --git a/test/files/neg/literals.scala b/test/files/neg/literals.scala
new file mode 100644
index 0000000000..3df7f0b408
--- /dev/null
+++ b/test/files/neg/literals.scala
@@ -0,0 +1,36 @@
+
+/* This took me literally all day.
+*/
+trait RejectedLiterals {
+
+ def missingHex: Int = { 0x } // line 4: was: not reported, taken as zero
+
+ def leadingZeros: Int = { 01 } // line 6: no leading zero
+
+ def tooManyZeros: Int = { 00 } // line 8: no leading zero
+
+ def zeroOfNine: Int = { 09 } // line 10: no leading zero
+
+ def orphanDot: Int = { 9. } // line 12: ident expected
+
+ def zeroOfNineDot: Int = { 09. } // line 14: malformed integer, ident expected
+
+ def noHexFloat: Double = { 0x1.2 } // line 16: ';' expected but double literal found.
+}
+
+trait Braceless {
+
+ def missingHex: Int = 0x // line 22: was: not reported, taken as zero
+
+ def leadingZeros: Int = 01 // line 24: no leading zero
+
+ def tooManyZeros: Int = 00 // line 26: no leading zero
+
+ def zeroOfNine: Int = 09 // line 28: no leading zero
+
+ def orphanDot: Int = 9. // line 30: ident expected
+
+ def zeroOfNineDot: Int = 09. // line 32: malformed integer, ident expected
+
+ def noHexFloat: Double = 0x1.2 // line 34: ';' expected but double literal found.
+}
diff --git a/test/files/run/literals.check b/test/files/run/literals.check
index 62c5fd68ae..092340eead 100644
--- a/test/files/run/literals.check
+++ b/test/files/run/literals.check
@@ -1,57 +1,12 @@
-warning: there were 5 deprecation warnings; re-run with -deprecation for details
-test '\u0024' == '$' was successful
-test '\u005f' == '_' was successful
-test 65.asInstanceOf[Char] == 'A' was successful
-test "\141\142" == "ab" was successful
-test "\0x61\0x62".trim() == "x61\0x62" was successful
-
-test (65 : Byte) == 'A' was successful
-
-test 0X01 == 1 was successful
-test 0x01 == 1 was successful
-test 0x10 == 16 was successful
-test 0xa == 10 was successful
-test 0x0a == 10 was successful
-test +0x01 == 1 was successful
-test +0x10 == 16 was successful
-test +0xa == 10 was successful
-test +0x0a == 10 was successful
-test -0x01 == -1 was successful
-test -0x10 == -16 was successful
-test -0xa == -10 was successful
-test -0x0a == -10 was successful
-test 0x7fffffff == 2147483647 was successful
-test 0x80000000 == -2147483648 was successful
-test 0xffffffff == -1 was successful
-
-test 1l == 1L was successful
-test 1L == 1l was successful
-test 1.asInstanceOf[Long] == 1l was successful
-test 0x7fffffffffffffffL == 9223372036854775807L was successful
-test 0x8000000000000000L == -9223372036854775808L was successful
-test 0xffffffffffffffffL == -1L was successful
-
-test 1e1f == 10.0f was successful
-test .3f == 0.3f was successful
-test 0f == 0.0f was successful
-test 01.23f == 1.23f was successful
-test 3.14f == 3.14f was successful
-test 6.022e23f == 6.022e23f was successful
-test 09f == 9.0f was successful
-test 1.asInstanceOf[Float] == 1.0 was successful
-test 1l.asInstanceOf[Float] == 1.0 was successful
-
-test 1e1 == 10.0 was successful
-test .3 == 0.3 was successful
-test 0.0 == 0.0 was successful
-test 0d == 0.0 was successful
-test 01.23 == 1.23 was successful
-test 01.23d == 1.23d was successful
-test 3.14 == 3.14 was successful
-test 1e-9d == 1.0e-9 was successful
-test 1e137 == 1.0e137 was successful
-test 1.asInstanceOf[Double] == 1.0 was successful
-test 1l.asInstanceOf[Double] == 1.0 was successful
-
-test "".length() was successful
-test ggg == 3 was successful
+literals.scala:34: warning: Octal escape literals are deprecated, use \u0061 instead.
+ check_success("\"\\141\\142\" == \"ab\"", "\141\142", "ab")
+ ^
+literals.scala:34: warning: Octal escape literals are deprecated, use \u0062 instead.
+ check_success("\"\\141\\142\" == \"ab\"", "\141\142", "ab")
+ ^
+literals.scala:37: warning: Octal escape literals are deprecated, use \u0000 instead.
+ "\0x61\0x62".getBytes(io.Codec.UTF8.charSet) sameElements Array[Byte](0, 120, 54, 49, 0, 120, 54, 50),
+ ^
+literals.scala:37: warning: Octal escape literals are deprecated, use \u0000 instead.
+ "\0x61\0x62".getBytes(io.Codec.UTF8.charSet) sameElements Array[Byte](0, 120, 54, 49, 0, 120, 54, 50),
+ ^
diff --git a/test/files/run/literals.flags b/test/files/run/literals.flags
new file mode 100644
index 0000000000..dcc59ebe32
--- /dev/null
+++ b/test/files/run/literals.flags
@@ -0,0 +1 @@
+-deprecation
diff --git a/test/files/run/literals.scala b/test/files/run/literals.scala
index 5f23e6b492..13fda05876 100644
--- a/test/files/run/literals.scala
+++ b/test/files/run/literals.scala
@@ -14,21 +14,16 @@ object Test {
def \u03b1\u03b1(that: GGG) = i + that.i
}
- def check_success[a](name: String, closure: => a, expected: a) {
- print("test " + name)
- try {
- val actual: a = closure
- if (actual == expected) {
- print(" was successful");
- } else {
- print(" failed: expected "+ expected +", found "+ actual);
+ def check_success[A](name: String, closure: => A, expected: A) {
+ val res: Option[String] =
+ try {
+ val actual: A = closure
+ if (actual == expected) None //print(" was successful")
+ else Some(s" failed: expected $expected, found $actual")
+ } catch {
+ case exception: Throwable => Some(s" raised exception $exception")
}
- } catch {
- case exception: Throwable => {
- print(" raised exception " + exception);
- }
- }
- println
+ for (e <- res) println(s"test $name $e")
}
def main(args: Array[String]) {
@@ -37,15 +32,14 @@ object Test {
check_success("'\\u005f' == '_'", '\u005f', '_')
check_success("65.asInstanceOf[Char] == 'A'", 65.asInstanceOf[Char], 'A')
check_success("\"\\141\\142\" == \"ab\"", "\141\142", "ab")
- check_success("\"\\0x61\\0x62\".trim() == \"x61\\0x62\"", "\0x61\0x62".substring(1), "x61\0x62")
-
- println
+ //check_success("\"\\0x61\\0x62\".trim() == \"x61\\0x62\"", "\0x61\0x62".substring(1), "x61\0x62")
+ check_success(""""\0x61\0x62".getBytes == Array(0, 120, ...)""",
+ "\0x61\0x62".getBytes(io.Codec.UTF8.charSet) sameElements Array[Byte](0, 120, 54, 49, 0, 120, 54, 50),
+ true)
// boolean
check_success("(65 : Byte) == 'A'", (65: Byte) == 'A', true) // contrib #176
- println
-
// int
check_success("0X01 == 1", 0X01, 1)
check_success("0x01 == 1", 0x01, 1)
@@ -67,8 +61,6 @@ object Test {
check_success("0x80000000 == -2147483648", 0x80000000, -2147483648)
check_success("0xffffffff == -1", 0xffffffff, -1)
- println
-
// long
check_success("1l == 1L", 1l, 1L)
check_success("1L == 1l", 1L, 1l)
@@ -81,8 +73,6 @@ object Test {
check_success("0xffffffffffffffffL == -1L",
0xffffffffffffffffL, -1L)
- println
-
// see JLS at address:
// http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html#230798
@@ -97,8 +87,6 @@ object Test {
check_success("1.asInstanceOf[Float] == 1.0", 1.asInstanceOf[Float], 1.0f)
check_success("1l.asInstanceOf[Float] == 1.0", 1l.asInstanceOf[Float], 1.0f)
- println
-
// double
check_success("1e1 == 10.0", 1e1, 10.0)
check_success(".3 == 0.3", .3, 0.3)
@@ -112,7 +100,6 @@ object Test {
check_success("1.asInstanceOf[Double] == 1.0", 1.asInstanceOf[Double], 1.0)
check_success("1l.asInstanceOf[Double] == 1.0", 1l.asInstanceOf[Double], 1.0)
- println
check_success("\"\".length()", "\u001a".length(), 1)
val ggg = GGG(1) \u03b1\u03b1 GGG(2)