@@ -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
@@ -153,10 +160,11 @@ object Scanners {
153160 /** Convert current strVal, base to double value
154161 */
155162 def floatVal (negated : Boolean ): Double = {
163+ val text = removeNumberSeparators(strVal)
156164 val limit : Double =
157165 if (token == DOUBLELIT ) Double .MaxValue else Float .MaxValue
158166 try {
159- val value : Double = java.lang.Double .valueOf(strVal ).doubleValue()
167+ val value : Double = java.lang.Double .valueOf(text ).doubleValue()
160168 if (value > limit)
161169 error(" floating point number too large" )
162170 if (negated) - value else value
@@ -169,6 +177,17 @@ object Scanners {
169177
170178 def floatVal : Double = floatVal(false )
171179
180+ @ inline def isNumberSeparator (c : Char ): Boolean = c == '_'
181+
182+ @ inline def removeNumberSeparators (s : String ): String =
183+ if (s.indexOf('_' ) > 0 ) s.replaceAllLiterally(" _" , " " ) /* .replaceAll("'","")*/ else s
184+
185+ // disallow trailing numeric separator char, but continue lexing
186+ def checkNoTrailingSeparator (): Unit = {
187+ if (isNumberSeparator(litBuf.last))
188+ errorButContinue(" trailing separator is not allowed" , offset + litBuf.length - 1 )
189+ }
190+
172191 }
173192
174193 class Scanner (source : SourceFile , override val startFrom : Offset = 0 )(implicit ctx : Context ) extends ScannerCommon (source)(ctx) {
@@ -911,27 +930,29 @@ object Scanners {
911930 */
912931 protected def getFraction (): Unit = {
913932 token = DOUBLELIT
914- while ('0' <= ch && ch <= '9' ) {
933+ while ('0' <= ch && ch <= '9' || isNumberSeparator(ch) ) {
915934 putChar(ch)
916935 nextChar()
917936 }
937+ checkNoTrailingSeparator()
918938 if (ch == 'e' || ch == 'E' ) {
919939 val lookahead = lookaheadReader()
920940 lookahead.nextChar()
921941 if (lookahead.ch == '+' || lookahead.ch == '-' ) {
922942 lookahead.nextChar()
923943 }
924- if ('0' <= lookahead.ch && lookahead.ch <= '9' ) {
944+ if ('0' <= lookahead.ch && lookahead.ch <= '9' || isNumberSeparator(ch) ) {
925945 putChar(ch)
926946 nextChar()
927947 if (ch == '+' || ch == '-' ) {
928948 putChar(ch)
929949 nextChar()
930950 }
931- while ('0' <= ch && ch <= '9' ) {
951+ while ('0' <= ch && ch <= '9' || isNumberSeparator(ch) ) {
932952 putChar(ch)
933953 nextChar()
934954 }
955+ checkNoTrailingSeparator()
935956 }
936957 token = DOUBLELIT
937958 }
@@ -954,15 +975,18 @@ object Scanners {
954975 /** Read a number into strVal and set base
955976 */
956977 protected def getNumber (): Unit = {
957- while (digit2int(ch, base) >= 0 ) {
978+ while (isNumberSeparator(ch) || digit2int(ch, base) >= 0 ) {
958979 putChar(ch)
959980 nextChar()
960981 }
982+ checkNoTrailingSeparator()
961983 token = INTLIT
962984 if (base == 10 && ch == '.' ) {
963985 val lch = lookaheadChar()
964986 if ('0' <= lch && lch <= '9' ) {
965- putChar('.' ); nextChar(); getFraction()
987+ putChar('.' )
988+ nextChar()
989+ getFraction()
966990 }
967991 } else (ch : @ switch) match {
968992 case 'e' | 'E' | 'f' | 'F' | 'd' | 'D' =>
@@ -972,6 +996,9 @@ object Scanners {
972996 token = LONGLIT
973997 case _ =>
974998 }
999+
1000+ checkNoTrailingSeparator()
1001+
9751002 setStrVal()
9761003 }
9771004
0 commit comments