| |
| |
| |
| |
| |
| "use strict"; |
|
|
| |
| |
| |
| |
| class CodeUnit { |
| constructor(start, source) { |
| this.start = start; |
| this.source = source; |
| } |
|
|
| get end() { |
| return this.start + this.length; |
| } |
|
|
| get length() { |
| return this.source.length; |
| } |
| } |
|
|
| |
| |
| |
| class TextReader { |
| constructor(source) { |
| this.source = source; |
| this.pos = 0; |
| } |
|
|
| |
| |
| |
| |
| |
| advance(length) { |
| this.pos += length; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| read(offset = 0, length = 1) { |
| const start = offset + this.pos; |
|
|
| return this.source.slice(start, start + length); |
| } |
| } |
|
|
| const SIMPLE_ESCAPE_SEQUENCES = { |
| __proto__: null, |
| b: "\b", |
| f: "\f", |
| n: "\n", |
| r: "\r", |
| t: "\t", |
| v: "\v", |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| function readHexSequence(reader, length) { |
| const str = reader.read(0, length); |
| const charCode = parseInt(str, 16); |
|
|
| reader.advance(length); |
| return String.fromCharCode(charCode); |
| } |
|
|
| |
| |
| |
| |
| |
| function readUnicodeSequence(reader) { |
| const regExp = /\{(?<hexDigits>[\dA-F]+)\}/iuy; |
|
|
| regExp.lastIndex = reader.pos; |
| const match = regExp.exec(reader.source); |
|
|
| if (match) { |
| const codePoint = parseInt(match.groups.hexDigits, 16); |
|
|
| reader.pos = regExp.lastIndex; |
| return String.fromCodePoint(codePoint); |
| } |
| return readHexSequence(reader, 4); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| function readOctalSequence(reader, maxLength) { |
| const [octalStr] = reader.read(-1, maxLength).match(/^[0-7]+/u); |
|
|
| reader.advance(octalStr.length - 1); |
| const octal = parseInt(octalStr, 8); |
|
|
| return String.fromCharCode(octal); |
| } |
|
|
| |
| |
| |
| |
| |
| function readEscapeSequenceOrLineContinuation(reader) { |
| const char = reader.read(1); |
|
|
| reader.advance(2); |
| const unitChar = SIMPLE_ESCAPE_SEQUENCES[char]; |
|
|
| if (unitChar) { |
| return unitChar; |
| } |
| switch (char) { |
| case "x": |
| return readHexSequence(reader, 2); |
| case "u": |
| return readUnicodeSequence(reader); |
| case "\r": |
| if (reader.read() === "\n") { |
| reader.advance(1); |
| } |
|
|
| |
| case "\n": |
| case "\u2028": |
| case "\u2029": |
| return ""; |
| case "0": |
| case "1": |
| case "2": |
| case "3": |
| return readOctalSequence(reader, 3); |
| case "4": |
| case "5": |
| case "6": |
| case "7": |
| return readOctalSequence(reader, 2); |
| default: |
| return char; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| function* mapEscapeSequenceOrLineContinuation(reader) { |
| const start = reader.pos; |
| const str = readEscapeSequenceOrLineContinuation(reader); |
| const end = reader.pos; |
| const source = reader.source.slice(start, end); |
|
|
| switch (str.length) { |
| case 0: |
| break; |
| case 1: |
| yield new CodeUnit(start, source); |
| break; |
| default: |
| yield new CodeUnit(start, source); |
| yield new CodeUnit(start, source); |
| break; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| function parseStringLiteral(source) { |
| const reader = new TextReader(source); |
| const quote = reader.read(); |
|
|
| reader.advance(1); |
| const codeUnits = []; |
|
|
| for (;;) { |
| const char = reader.read(); |
|
|
| if (char === quote) { |
| break; |
| } |
| if (char === "\\") { |
| codeUnits.push(...mapEscapeSequenceOrLineContinuation(reader)); |
| } else { |
| codeUnits.push(new CodeUnit(reader.pos, char)); |
| reader.advance(1); |
| } |
| } |
| return codeUnits; |
| } |
|
|
| |
| |
| |
| |
| |
| function parseTemplateToken(source) { |
| const reader = new TextReader(source); |
|
|
| reader.advance(1); |
| const codeUnits = []; |
|
|
| for (;;) { |
| const char = reader.read(); |
|
|
| if (char === "`" || (char === "$" && reader.read(1) === "{")) { |
| break; |
| } |
| if (char === "\\") { |
| codeUnits.push(...mapEscapeSequenceOrLineContinuation(reader)); |
| } else { |
| let unitSource; |
|
|
| if (char === "\r" && reader.read(1) === "\n") { |
| unitSource = "\r\n"; |
| } else { |
| unitSource = char; |
| } |
| codeUnits.push(new CodeUnit(reader.pos, unitSource)); |
| reader.advance(unitSource.length); |
| } |
| } |
| return codeUnits; |
| } |
|
|
| module.exports = { parseStringLiteral, parseTemplateToken }; |
|
|