@@ -67,11 +67,15 @@ object Scanners {
67
67
68
68
/** Generate an error at the given offset */
69
69
def error (msg : String , off : Offset = offset): Unit = {
70
- ctx.error (msg, source atSpan Span ( off) )
70
+ errorButContinue (msg, off)
71
71
token = ERROR
72
72
errOffset = off
73
73
}
74
74
75
+ def errorButContinue (msg : String , off : Offset = offset): Unit = {
76
+ ctx.error(msg, source atSpan Span (off))
77
+ }
78
+
75
79
/** signal an error where the input ended in the middle of a token */
76
80
def incompleteInputError (msg : String ): Unit = {
77
81
ctx.incompleteInputError(msg, source atSpan Span (offset))
@@ -129,19 +133,22 @@ object Scanners {
129
133
var i = 0
130
134
val len = strVal.length
131
135
while (i < len) {
132
- val d = digit2int(strVal charAt i, base)
133
- if (d < 0 ) {
134
- error(" malformed integer number" )
135
- return 0
136
- }
137
- if (value < 0 ||
138
- limit / (base / divider) < value ||
139
- limit - (d / divider) < value * (base / divider) &&
140
- ! (negated && limit == value * base - 1 + d)) {
141
- error(" integer number too large" )
142
- return 0
136
+ val c = strVal charAt i
137
+ if (! isNumberSeparator(c)) {
138
+ val d = digit2int(c, base)
139
+ if (d < 0 ) {
140
+ error(s " malformed integer number " )
141
+ return 0
142
+ }
143
+ if (value < 0 ||
144
+ limit / (base / divider) < value ||
145
+ limit - (d / divider) < value * (base / divider) &&
146
+ ! (negated && limit == value * base - 1 + d)) {
147
+ error(" integer number too large" )
148
+ return 0
149
+ }
150
+ value = value * base + d
143
151
}
144
- value = value * base + d
145
152
i += 1
146
153
}
147
154
if (negated) - value else value
@@ -150,15 +157,42 @@ object Scanners {
150
157
151
158
def intVal : Long = intVal(false )
152
159
160
+ private val zeroFloat = raw " [0.]+(?:[eE][+-]?[0-9]+)?[fFdD]? " .r
161
+
162
+ /** Convert current strVal, base to double value
163
+ */
164
+ def floatVal (negated : Boolean ): Float = {
165
+ assert(token == FLOATLIT )
166
+ val text = removeNumberSeparators(strVal)
167
+ try {
168
+ val value : Float = java.lang.Float .valueOf(text).floatValue()
169
+ if (value > Float .MaxValue )
170
+ errorButContinue(" floating point number too large" )
171
+
172
+ if (value == 0.0f && ! zeroFloat.pattern.matcher(text).matches)
173
+ errorButContinue(" floating point number too small" )
174
+ if (negated) - value else value
175
+ } catch {
176
+ case _ : NumberFormatException =>
177
+ error(" malformed floating point number" )
178
+ 0.0f
179
+ }
180
+ }
181
+
182
+ def floatVal : Float = floatVal(false )
183
+
153
184
/** Convert current strVal, base to double value
154
185
*/
155
- def floatVal (negated : Boolean ): Double = {
156
- val limit : Double =
157
- if (token == DOUBLELIT ) Double . MaxValue else Float . MaxValue
186
+ def doubleVal (negated : Boolean ): Double = {
187
+ assert(token == DOUBLELIT )
188
+ val text = removeNumberSeparators(strVal)
158
189
try {
159
- val value : Double = java.lang.Double .valueOf(strVal).doubleValue()
160
- if (value > limit)
161
- error(" floating point number too large" )
190
+ val value : Double = java.lang.Double .valueOf(text).doubleValue()
191
+ if (value > Double .MaxValue )
192
+ errorButContinue(" double precision floating point number too large" )
193
+
194
+ if (value == 0.0d && ! zeroFloat.pattern.matcher(text).matches)
195
+ errorButContinue(" double precision floating point number too small" )
162
196
if (negated) - value else value
163
197
} catch {
164
198
case _ : NumberFormatException =>
@@ -167,7 +201,18 @@ object Scanners {
167
201
}
168
202
}
169
203
170
- def floatVal : Double = floatVal(false )
204
+ def doubleVal : Double = doubleVal(false )
205
+
206
+ @ inline def isNumberSeparator (c : Char ): Boolean = c == '_'
207
+
208
+ @ inline def removeNumberSeparators (s : String ): String =
209
+ if (s.indexOf('_' ) > 0 ) s.replaceAllLiterally(" _" , " " ) /* .replaceAll("'","")*/ else s
210
+
211
+ // disallow trailing numeric separator char, but continue lexing
212
+ def checkNoTrailingSeparator (): Unit = {
213
+ if (isNumberSeparator(litBuf.last))
214
+ errorButContinue(" trailing separator is not allowed" , offset + litBuf.length - 1 )
215
+ }
171
216
172
217
}
173
218
@@ -463,6 +508,8 @@ object Scanners {
463
508
if (ch == 'x' || ch == 'X' ) {
464
509
nextChar()
465
510
base = 16
511
+ if (isNumberSeparator(ch))
512
+ errorButContinue(" leading separator is not allowed" , offset + 2 )
466
513
} else {
467
514
/**
468
515
* What should leading 0 be in the future? It is potentially dangerous
@@ -911,27 +958,29 @@ object Scanners {
911
958
*/
912
959
protected def getFraction (): Unit = {
913
960
token = DOUBLELIT
914
- while ('0' <= ch && ch <= '9' ) {
961
+ while ('0' <= ch && ch <= '9' || isNumberSeparator(ch) ) {
915
962
putChar(ch)
916
963
nextChar()
917
964
}
965
+ checkNoTrailingSeparator()
918
966
if (ch == 'e' || ch == 'E' ) {
919
967
val lookahead = lookaheadReader()
920
968
lookahead.nextChar()
921
969
if (lookahead.ch == '+' || lookahead.ch == '-' ) {
922
970
lookahead.nextChar()
923
971
}
924
- if ('0' <= lookahead.ch && lookahead.ch <= '9' ) {
972
+ if ('0' <= lookahead.ch && lookahead.ch <= '9' || isNumberSeparator(ch) ) {
925
973
putChar(ch)
926
974
nextChar()
927
975
if (ch == '+' || ch == '-' ) {
928
976
putChar(ch)
929
977
nextChar()
930
978
}
931
- while ('0' <= ch && ch <= '9' ) {
979
+ while ('0' <= ch && ch <= '9' || isNumberSeparator(ch) ) {
932
980
putChar(ch)
933
981
nextChar()
934
982
}
983
+ checkNoTrailingSeparator()
935
984
}
936
985
token = DOUBLELIT
937
986
}
@@ -954,15 +1003,18 @@ object Scanners {
954
1003
/** Read a number into strVal and set base
955
1004
*/
956
1005
protected def getNumber (): Unit = {
957
- while (digit2int(ch, base) >= 0 ) {
1006
+ while (isNumberSeparator(ch) || digit2int(ch, base) >= 0 ) {
958
1007
putChar(ch)
959
1008
nextChar()
960
1009
}
1010
+ checkNoTrailingSeparator()
961
1011
token = INTLIT
962
1012
if (base == 10 && ch == '.' ) {
963
1013
val lch = lookaheadChar()
964
1014
if ('0' <= lch && lch <= '9' ) {
965
- putChar('.' ); nextChar(); getFraction()
1015
+ putChar('.' )
1016
+ nextChar()
1017
+ getFraction()
966
1018
}
967
1019
} else (ch : @ switch) match {
968
1020
case 'e' | 'E' | 'f' | 'F' | 'd' | 'D' =>
@@ -972,6 +1024,9 @@ object Scanners {
972
1024
token = LONGLIT
973
1025
case _ =>
974
1026
}
1027
+
1028
+ checkNoTrailingSeparator()
1029
+
975
1030
setStrVal()
976
1031
}
977
1032
@@ -1007,7 +1062,7 @@ object Scanners {
1007
1062
case INTLIT => s " int( $intVal) "
1008
1063
case LONGLIT => s " long( $intVal) "
1009
1064
case FLOATLIT => s " float( $floatVal) "
1010
- case DOUBLELIT => s " double( $floatVal ) "
1065
+ case DOUBLELIT => s " double( $doubleVal ) "
1011
1066
case STRINGLIT => s " string( $strVal) "
1012
1067
case STRINGPART => s " stringpart( $strVal) "
1013
1068
case INTERPOLATIONID => s " interpolationid( $name) "
0 commit comments