()
| 431 | } |
| 432 | |
| 433 | scanString() { |
| 434 | let quote = this.ch; |
| 435 | this.next(); // Skip initial quote. |
| 436 | |
| 437 | let buffer; |
| 438 | let marker = this.idx; |
| 439 | |
| 440 | while (this.ch !== quote) { |
| 441 | if (this.ch === /*\*/0x5C) { |
| 442 | if (!buffer) { |
| 443 | buffer = []; |
| 444 | } |
| 445 | |
| 446 | buffer.push(this.src.slice(marker, this.idx)); |
| 447 | |
| 448 | this.next(); |
| 449 | |
| 450 | let unescaped; |
| 451 | |
| 452 | if (this.ch === /*u*/0x75) { |
| 453 | this.next(); |
| 454 | |
| 455 | if (this.idx + 4 < this.len) { |
| 456 | let hex = this.src.slice(this.idx, this.idx + 4); |
| 457 | |
| 458 | if (!/[A-Z0-9]{4}/i.test(hex)) { |
| 459 | this.err(`Invalid unicode escape [\\u${hex}]`); |
| 460 | } |
| 461 | |
| 462 | unescaped = parseInt(hex, 16); |
| 463 | this.idx += 4; |
| 464 | this.ch = this.src.charCodeAt(this.idx); |
| 465 | } else { |
| 466 | this.err(); |
| 467 | } |
| 468 | } else { |
| 469 | unescaped = unescape(this.ch); |
| 470 | this.next(); |
| 471 | } |
| 472 | |
| 473 | buffer.push(fromCharCode(unescaped)); |
| 474 | marker = this.idx; |
| 475 | } else if (this.ch === /*EOF*/0 || this.idx >= this.len) { |
| 476 | this.err('Unterminated quote'); |
| 477 | } else { |
| 478 | this.next(); |
| 479 | } |
| 480 | } |
| 481 | |
| 482 | let last = this.src.slice(marker, this.idx); |
| 483 | this.next(); // Skip terminating quote. |
| 484 | |
| 485 | // Compute the unescaped string value. |
| 486 | let unescaped = last; |
| 487 | |
| 488 | if (buffer !== null && buffer !== undefined) { |
| 489 | buffer.push(last); |
| 490 | unescaped = buffer.join(''); |
no test coverage detected