@@ -561,9 +561,9 @@ namespace ts {
561
561
return false ;
562
562
}
563
563
564
- function scanConflictMarkerTrivia ( text : string , pos : number , error ?: ErrorCallback ) {
564
+ function scanConflictMarkerTrivia ( text : string , pos : number , error ?: ( diag : DiagnosticMessage , pos ?: number , len ?: number ) => void ) {
565
565
if ( error ) {
566
- error ( Diagnostics . Merge_conflict_marker_encountered , mergeConflictMarkerLength ) ;
566
+ error ( Diagnostics . Merge_conflict_marker_encountered , pos , mergeConflictMarkerLength ) ;
567
567
}
568
568
569
569
const ch = text . charCodeAt ( pos ) ;
@@ -852,34 +852,86 @@ namespace ts {
852
852
scanRange,
853
853
} ;
854
854
855
- function error ( message : DiagnosticMessage , length ?: number ) : void {
855
+ function error ( message : DiagnosticMessage ) : void ;
856
+ function error ( message : DiagnosticMessage , errPos : number , length : number ) : void ;
857
+ function error ( message : DiagnosticMessage , errPos : number = pos , length ?: number ) : void {
856
858
if ( onError ) {
859
+ const oldPos = pos ;
860
+ pos = errPos ;
857
861
onError ( message , length || 0 ) ;
862
+ pos = oldPos ;
858
863
}
859
864
}
860
865
866
+ function scanNumberFragment ( ) : string {
867
+ let start = pos ;
868
+ let allowSeparator = false ;
869
+ let result = "" ;
870
+ while ( true ) {
871
+ const ch = text . charCodeAt ( pos ) ;
872
+ if ( ch === CharacterCodes . _ ) {
873
+ tokenFlags |= TokenFlags . ContainsSeparator ;
874
+ if ( allowSeparator ) {
875
+ allowSeparator = false ;
876
+ result += text . substring ( start , pos ) ;
877
+ }
878
+ else {
879
+ error ( Diagnostics . Numeric_separators_are_not_allowed_here , pos , 1 ) ;
880
+ }
881
+ pos ++ ;
882
+ start = pos ;
883
+ continue ;
884
+ }
885
+ if ( isDigit ( ch ) ) {
886
+ allowSeparator = true ;
887
+ pos ++ ;
888
+ continue ;
889
+ }
890
+ break ;
891
+ }
892
+ if ( text . charCodeAt ( pos - 1 ) === CharacterCodes . _ ) {
893
+ error ( Diagnostics . Numeric_separators_are_not_allowed_here , pos - 1 , 1 ) ;
894
+ }
895
+ return result + text . substring ( start , pos ) ;
896
+ }
897
+
861
898
function scanNumber ( ) : string {
862
899
const start = pos ;
863
- while ( isDigit ( text . charCodeAt ( pos ) ) ) pos ++ ;
900
+ const mainFragment = scanNumberFragment ( ) ;
901
+ let decimalFragment : string ;
902
+ let scientificFragment : string ;
864
903
if ( text . charCodeAt ( pos ) === CharacterCodes . dot ) {
865
904
pos ++ ;
866
- while ( isDigit ( text . charCodeAt ( pos ) ) ) pos ++ ;
905
+ decimalFragment = scanNumberFragment ( ) ;
867
906
}
868
907
let end = pos ;
869
908
if ( text . charCodeAt ( pos ) === CharacterCodes . E || text . charCodeAt ( pos ) === CharacterCodes . e ) {
870
909
pos ++ ;
871
910
tokenFlags |= TokenFlags . Scientific ;
872
911
if ( text . charCodeAt ( pos ) === CharacterCodes . plus || text . charCodeAt ( pos ) === CharacterCodes . minus ) pos ++ ;
873
- if ( isDigit ( text . charCodeAt ( pos ) ) ) {
874
- pos ++ ;
875
- while ( isDigit ( text . charCodeAt ( pos ) ) ) pos ++ ;
876
- end = pos ;
912
+ const preNumericPart = pos ;
913
+ const finalFragment = scanNumberFragment ( ) ;
914
+ if ( ! finalFragment ) {
915
+ error ( Diagnostics . Digit_expected ) ;
877
916
}
878
917
else {
879
- error ( Diagnostics . Digit_expected ) ;
918
+ scientificFragment = text . substring ( end , preNumericPart ) + finalFragment ;
919
+ end = pos ;
920
+ }
921
+ }
922
+ if ( tokenFlags & TokenFlags . ContainsSeparator ) {
923
+ let result = mainFragment ;
924
+ if ( decimalFragment ) {
925
+ result += "." + decimalFragment ;
880
926
}
927
+ if ( scientificFragment ) {
928
+ result += scientificFragment ;
929
+ }
930
+ return "" + + result ;
931
+ }
932
+ else {
933
+ return "" + + ( text . substring ( start , end ) ) ; // No need to use all the fragments; no _ removal needed
881
934
}
882
- return "" + + ( text . substring ( start , end ) ) ;
883
935
}
884
936
885
937
function scanOctalDigits ( ) : number {
@@ -894,23 +946,36 @@ namespace ts {
894
946
* Scans the given number of hexadecimal digits in the text,
895
947
* returning -1 if the given number is unavailable.
896
948
*/
897
- function scanExactNumberOfHexDigits ( count : number ) : number {
898
- return scanHexDigits ( /*minCount*/ count , /*scanAsManyAsPossible*/ false ) ;
949
+ function scanExactNumberOfHexDigits ( count : number , canHaveSeparators : boolean ) : number {
950
+ return scanHexDigits ( /*minCount*/ count , /*scanAsManyAsPossible*/ false , canHaveSeparators ) ;
899
951
}
900
952
901
953
/**
902
954
* Scans as many hexadecimal digits as are available in the text,
903
955
* returning -1 if the given number of digits was unavailable.
904
956
*/
905
- function scanMinimumNumberOfHexDigits ( count : number ) : number {
906
- return scanHexDigits ( /*minCount*/ count , /*scanAsManyAsPossible*/ true ) ;
957
+ function scanMinimumNumberOfHexDigits ( count : number , canHaveSeparators : boolean ) : number {
958
+ return scanHexDigits ( /*minCount*/ count , /*scanAsManyAsPossible*/ true , canHaveSeparators ) ;
907
959
}
908
960
909
- function scanHexDigits ( minCount : number , scanAsManyAsPossible : boolean ) : number {
961
+ function scanHexDigits ( minCount : number , scanAsManyAsPossible : boolean , canHaveSeparators : boolean ) : number {
910
962
let digits = 0 ;
911
963
let value = 0 ;
964
+ let allowSeparator = false ;
912
965
while ( digits < minCount || scanAsManyAsPossible ) {
913
966
const ch = text . charCodeAt ( pos ) ;
967
+ if ( canHaveSeparators && ch === CharacterCodes . _ ) {
968
+ tokenFlags |= TokenFlags . ContainsSeparator ;
969
+ if ( allowSeparator ) {
970
+ allowSeparator = false ;
971
+ }
972
+ else {
973
+ error ( Diagnostics . Numeric_separators_are_not_allowed_here , pos , 1 ) ;
974
+ }
975
+ pos ++ ;
976
+ continue ;
977
+ }
978
+ allowSeparator = canHaveSeparators ;
914
979
if ( ch >= CharacterCodes . _0 && ch <= CharacterCodes . _9 ) {
915
980
value = value * 16 + ch - CharacterCodes . _0 ;
916
981
}
@@ -929,6 +994,9 @@ namespace ts {
929
994
if ( digits < minCount ) {
930
995
value = - 1 ;
931
996
}
997
+ if ( text . charCodeAt ( pos - 1 ) === CharacterCodes . _ ) {
998
+ error ( Diagnostics . Numeric_separators_are_not_allowed_here , pos - 1 , 1 ) ;
999
+ }
932
1000
return value ;
933
1001
}
934
1002
@@ -1097,7 +1165,7 @@ namespace ts {
1097
1165
}
1098
1166
1099
1167
function scanHexadecimalEscape ( numDigits : number ) : string {
1100
- const escapedValue = scanExactNumberOfHexDigits ( numDigits ) ;
1168
+ const escapedValue = scanExactNumberOfHexDigits ( numDigits , /*canHaveSeparators*/ false ) ;
1101
1169
1102
1170
if ( escapedValue >= 0 ) {
1103
1171
return String . fromCharCode ( escapedValue ) ;
@@ -1109,7 +1177,7 @@ namespace ts {
1109
1177
}
1110
1178
1111
1179
function scanExtendedUnicodeEscape ( ) : string {
1112
- const escapedValue = scanMinimumNumberOfHexDigits ( 1 ) ;
1180
+ const escapedValue = scanMinimumNumberOfHexDigits ( 1 , /*canHaveSeparators*/ false ) ;
1113
1181
let isInvalidExtendedEscape = false ;
1114
1182
1115
1183
// Validate the value of the digit
@@ -1162,7 +1230,7 @@ namespace ts {
1162
1230
if ( pos + 5 < end && text . charCodeAt ( pos + 1 ) === CharacterCodes . u ) {
1163
1231
const start = pos ;
1164
1232
pos += 2 ;
1165
- const value = scanExactNumberOfHexDigits ( 4 ) ;
1233
+ const value = scanExactNumberOfHexDigits ( 4 , /*canHaveSeparators*/ false ) ;
1166
1234
pos = start ;
1167
1235
return value ;
1168
1236
}
@@ -1218,8 +1286,22 @@ namespace ts {
1218
1286
// For counting number of digits; Valid binaryIntegerLiteral must have at least one binary digit following B or b.
1219
1287
// Similarly valid octalIntegerLiteral must have at least one octal digit following o or O.
1220
1288
let numberOfDigits = 0 ;
1289
+ let separatorAllowed = false ;
1221
1290
while ( true ) {
1222
1291
const ch = text . charCodeAt ( pos ) ;
1292
+ // Numeric seperators are allowed anywhere within a numeric literal, except not at the beginning, or following another separator
1293
+ if ( ch === CharacterCodes . _ ) {
1294
+ tokenFlags |= TokenFlags . ContainsSeparator ;
1295
+ if ( separatorAllowed ) {
1296
+ separatorAllowed = false ;
1297
+ }
1298
+ else {
1299
+ error ( Diagnostics . Numeric_separators_are_not_allowed_here , pos , 1 ) ;
1300
+ }
1301
+ pos ++ ;
1302
+ continue ;
1303
+ }
1304
+ separatorAllowed = true ;
1223
1305
const valueOfCh = ch - CharacterCodes . _0 ;
1224
1306
if ( ! isDigit ( ch ) || valueOfCh >= base ) {
1225
1307
break ;
@@ -1232,6 +1314,11 @@ namespace ts {
1232
1314
if ( numberOfDigits === 0 ) {
1233
1315
return - 1 ;
1234
1316
}
1317
+ if ( text . charCodeAt ( pos - 1 ) === CharacterCodes . _ ) {
1318
+ // Literal ends with underscore - not allowed
1319
+ error ( Diagnostics . Numeric_separators_are_not_allowed_here , pos - 1 , 1 ) ;
1320
+ return value ;
1321
+ }
1235
1322
return value ;
1236
1323
}
1237
1324
@@ -1435,7 +1522,7 @@ namespace ts {
1435
1522
case CharacterCodes . _0 :
1436
1523
if ( pos + 2 < end && ( text . charCodeAt ( pos + 1 ) === CharacterCodes . X || text . charCodeAt ( pos + 1 ) === CharacterCodes . x ) ) {
1437
1524
pos += 2 ;
1438
- let value = scanMinimumNumberOfHexDigits ( 1 ) ;
1525
+ let value = scanMinimumNumberOfHexDigits ( 1 , /*canHaveSeparators*/ true ) ;
1439
1526
if ( value < 0 ) {
1440
1527
error ( Diagnostics . Hexadecimal_digit_expected ) ;
1441
1528
value = 0 ;
0 commit comments