()
| 447 | } |
| 448 | |
| 449 | private readEscapeSequence(): number | number[] | null { |
| 450 | const byte = this.scanner.peek(); |
| 451 | |
| 452 | if (byte === -1) { |
| 453 | return null; |
| 454 | } |
| 455 | |
| 456 | this.scanner.advance(); |
| 457 | |
| 458 | switch (byte) { |
| 459 | case 0x6e: |
| 460 | return LF; // \n -> LF |
| 461 | case 0x72: |
| 462 | return CR; // \r -> CR |
| 463 | case 0x74: |
| 464 | return TAB; // \t -> TAB |
| 465 | case 0x62: |
| 466 | return BS; // \b -> BS |
| 467 | case 0x66: |
| 468 | return FF; // \f -> FF |
| 469 | case CHAR_PARENTHESIS_OPEN: |
| 470 | return CHAR_PARENTHESIS_OPEN; // \( -> ( |
| 471 | case CHAR_PARENTHESIS_CLOSE: |
| 472 | return CHAR_PARENTHESIS_CLOSE; // \) -> ) |
| 473 | case CHAR_BACKSLASH: |
| 474 | return CHAR_BACKSLASH; // \\ -> \ |
| 475 | case CR: |
| 476 | // Line continuation: \ at end of line |
| 477 | if (this.scanner.peek() === LF) { |
| 478 | this.scanner.advance(); |
| 479 | } |
| 480 | |
| 481 | return null; |
| 482 | case LF: |
| 483 | // Line continuation |
| 484 | return null; |
| 485 | default: |
| 486 | // Check for octal |
| 487 | if (byte >= DIGIT_0 && byte <= 0x37) { |
| 488 | return this.readOctalEscape(byte); |
| 489 | } |
| 490 | |
| 491 | // Unknown escape - return literal character |
| 492 | return byte; |
| 493 | } |
| 494 | } |
| 495 | |
| 496 | private readOctalEscape(firstDigit: number): number { |
| 497 | let value = firstDigit - DIGIT_0; |
no test coverage detected