(start: number)
| 464 | } |
| 465 | |
| 466 | private scanNumber(start: number): Token { |
| 467 | let simple = this.index === start; |
| 468 | let hasSeparators = false; |
| 469 | this.advance(); // Skip initial digit. |
| 470 | while (true) { |
| 471 | if (chars.isDigit(this.peek)) { |
| 472 | // Do nothing. |
| 473 | } else if (this.peek === chars.$_) { |
| 474 | // Separators are only valid when they're surrounded by digits. E.g. `1_0_1` is |
| 475 | // valid while `_101` and `101_` are not. The separator can't be next to the decimal |
| 476 | // point or another separator either. Note that it's unlikely that we'll hit a case where |
| 477 | // the underscore is at the start, because that's a valid identifier and it will be picked |
| 478 | // up earlier in the parsing. We validate for it anyway just in case. |
| 479 | if ( |
| 480 | !chars.isDigit(this.input.charCodeAt(this.index - 1)) || |
| 481 | !chars.isDigit(this.input.charCodeAt(this.index + 1)) |
| 482 | ) { |
| 483 | return this.error('Invalid numeric separator', 0); |
| 484 | } |
| 485 | hasSeparators = true; |
| 486 | } else if (this.peek === chars.$PERIOD) { |
| 487 | simple = false; |
| 488 | } else if (isExponentStart(this.peek)) { |
| 489 | this.advance(); |
| 490 | if (isExponentSign(this.peek)) this.advance(); |
| 491 | if (!chars.isDigit(this.peek)) return this.error('Invalid exponent', -1); |
| 492 | simple = false; |
| 493 | } else { |
| 494 | break; |
| 495 | } |
| 496 | this.advance(); |
| 497 | } |
| 498 | |
| 499 | let str = this.input.substring(start, this.index); |
| 500 | if (hasSeparators) { |
| 501 | str = str.replace(/_/g, ''); |
| 502 | } |
| 503 | const value = simple ? parseIntAutoRadix(str) : parseFloat(str); |
| 504 | return newNumberToken(start, this.index, value); |
| 505 | } |
| 506 | |
| 507 | private scanString(): Token { |
| 508 | const start = this.index; |
no test coverage detected