@@ -67,11 +67,15 @@ object Scanners {
6767
6868 /** Generate an error at the given offset */
6969 def error (msg : String , off : Offset = offset): Unit = {
70- ctx.error (msg, source atSpan Span ( off) )
70+ errorButContinue (msg, off)
7171 token = ERROR
7272 errOffset = off
7373 }
7474
75+ def errorButContinue (msg : String , off : Offset = offset): Unit = {
76+ ctx.error(msg, source atSpan Span (off))
77+ }
78+
7579 /** signal an error where the input ended in the middle of a token */
7680 def incompleteInputError (msg : String ): Unit = {
7781 ctx.incompleteInputError(msg, source atSpan Span (offset))
@@ -129,19 +133,22 @@ object Scanners {
129133 var i = 0
130134 val len = strVal.length
131135 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
143151 }
144- value = value * base + d
145152 i += 1
146153 }
147154 if (negated) - value else value
@@ -150,15 +157,42 @@ object Scanners {
150157
151158 def intVal : Long = intVal(false )
152159
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+
153184 /** Convert current strVal, base to double value
154185 */
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)
158189 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" )
162196 if (negated) - value else value
163197 } catch {
164198 case _ : NumberFormatException =>
@@ -167,7 +201,18 @@ object Scanners {
167201 }
168202 }
169203
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+ }
171216
172217 }
173218
@@ -463,6 +508,8 @@ object Scanners {
463508 if (ch == 'x' || ch == 'X' ) {
464509 nextChar()
465510 base = 16
511+ if (isNumberSeparator(ch))
512+ errorButContinue(" leading separator is not allowed" , offset + 2 )
466513 } else {
467514 /**
468515 * What should leading 0 be in the future? It is potentially dangerous
@@ -911,27 +958,29 @@ object Scanners {
911958 */
912959 protected def getFraction (): Unit = {
913960 token = DOUBLELIT
914- while ('0' <= ch && ch <= '9' ) {
961+ while ('0' <= ch && ch <= '9' || isNumberSeparator(ch) ) {
915962 putChar(ch)
916963 nextChar()
917964 }
965+ checkNoTrailingSeparator()
918966 if (ch == 'e' || ch == 'E' ) {
919967 val lookahead = lookaheadReader()
920968 lookahead.nextChar()
921969 if (lookahead.ch == '+' || lookahead.ch == '-' ) {
922970 lookahead.nextChar()
923971 }
924- if ('0' <= lookahead.ch && lookahead.ch <= '9' ) {
972+ if ('0' <= lookahead.ch && lookahead.ch <= '9' || isNumberSeparator(ch) ) {
925973 putChar(ch)
926974 nextChar()
927975 if (ch == '+' || ch == '-' ) {
928976 putChar(ch)
929977 nextChar()
930978 }
931- while ('0' <= ch && ch <= '9' ) {
979+ while ('0' <= ch && ch <= '9' || isNumberSeparator(ch) ) {
932980 putChar(ch)
933981 nextChar()
934982 }
983+ checkNoTrailingSeparator()
935984 }
936985 token = DOUBLELIT
937986 }
@@ -954,15 +1003,18 @@ object Scanners {
9541003 /** Read a number into strVal and set base
9551004 */
9561005 protected def getNumber (): Unit = {
957- while (digit2int(ch, base) >= 0 ) {
1006+ while (isNumberSeparator(ch) || digit2int(ch, base) >= 0 ) {
9581007 putChar(ch)
9591008 nextChar()
9601009 }
1010+ checkNoTrailingSeparator()
9611011 token = INTLIT
9621012 if (base == 10 && ch == '.' ) {
9631013 val lch = lookaheadChar()
9641014 if ('0' <= lch && lch <= '9' ) {
965- putChar('.' ); nextChar(); getFraction()
1015+ putChar('.' )
1016+ nextChar()
1017+ getFraction()
9661018 }
9671019 } else (ch : @ switch) match {
9681020 case 'e' | 'E' | 'f' | 'F' | 'd' | 'D' =>
@@ -972,6 +1024,9 @@ object Scanners {
9721024 token = LONGLIT
9731025 case _ =>
9741026 }
1027+
1028+ checkNoTrailingSeparator()
1029+
9751030 setStrVal()
9761031 }
9771032
@@ -1007,7 +1062,7 @@ object Scanners {
10071062 case INTLIT => s " int( $intVal) "
10081063 case LONGLIT => s " long( $intVal) "
10091064 case FLOATLIT => s " float( $floatVal) "
1010- case DOUBLELIT => s " double( $floatVal ) "
1065+ case DOUBLELIT => s " double( $doubleVal ) "
10111066 case STRINGLIT => s " string( $strVal) "
10121067 case STRINGPART => s " stringpart( $strVal) "
10131068 case INTERPOLATIONID => s " interpolationid( $name) "
0 commit comments