328 lines
7.3 KiB
JavaScript
328 lines
7.3 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", {
|
||
|
value: true
|
||
|
});
|
||
|
exports.readCodePoint = readCodePoint;
|
||
|
exports.readInt = readInt;
|
||
|
exports.readStringContents = readStringContents;
|
||
|
|
||
|
var _isDigit = function isDigit(code) {
|
||
|
return code >= 48 && code <= 57;
|
||
|
};
|
||
|
|
||
|
const forbiddenNumericSeparatorSiblings = {
|
||
|
decBinOct: new Set([46, 66, 69, 79, 95, 98, 101, 111]),
|
||
|
hex: new Set([46, 88, 95, 120])
|
||
|
};
|
||
|
const isAllowedNumericSeparatorSibling = {
|
||
|
bin: ch => ch === 48 || ch === 49,
|
||
|
oct: ch => ch >= 48 && ch <= 55,
|
||
|
dec: ch => ch >= 48 && ch <= 57,
|
||
|
hex: ch => ch >= 48 && ch <= 57 || ch >= 65 && ch <= 70 || ch >= 97 && ch <= 102
|
||
|
};
|
||
|
|
||
|
function readStringContents(type, input, pos, lineStart, curLine, errors) {
|
||
|
const initialPos = pos;
|
||
|
const initialLineStart = lineStart;
|
||
|
const initialCurLine = curLine;
|
||
|
let out = "";
|
||
|
let containsInvalid = false;
|
||
|
let chunkStart = pos;
|
||
|
const {
|
||
|
length
|
||
|
} = input;
|
||
|
|
||
|
for (;;) {
|
||
|
if (pos >= length) {
|
||
|
errors.unterminated(initialPos, initialLineStart, initialCurLine);
|
||
|
out += input.slice(chunkStart, pos);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
const ch = input.charCodeAt(pos);
|
||
|
|
||
|
if (isStringEnd(type, ch, input, pos)) {
|
||
|
out += input.slice(chunkStart, pos);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (ch === 92) {
|
||
|
out += input.slice(chunkStart, pos);
|
||
|
let escaped;
|
||
|
({
|
||
|
ch: escaped,
|
||
|
pos,
|
||
|
lineStart,
|
||
|
curLine
|
||
|
} = readEscapedChar(input, pos, lineStart, curLine, type === "template", errors));
|
||
|
|
||
|
if (escaped === null) {
|
||
|
containsInvalid = true;
|
||
|
} else {
|
||
|
out += escaped;
|
||
|
}
|
||
|
|
||
|
chunkStart = pos;
|
||
|
} else if (ch === 8232 || ch === 8233) {
|
||
|
++pos;
|
||
|
++curLine;
|
||
|
lineStart = pos;
|
||
|
} else if (ch === 10 || ch === 13) {
|
||
|
if (type === "template") {
|
||
|
out += input.slice(chunkStart, pos) + "\n";
|
||
|
++pos;
|
||
|
|
||
|
if (ch === 13 && input.charCodeAt(pos) === 10) {
|
||
|
++pos;
|
||
|
}
|
||
|
|
||
|
++curLine;
|
||
|
chunkStart = lineStart = pos;
|
||
|
} else {
|
||
|
errors.unterminated(initialPos, initialLineStart, initialCurLine);
|
||
|
}
|
||
|
} else {
|
||
|
++pos;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
pos,
|
||
|
str: out,
|
||
|
containsInvalid,
|
||
|
lineStart,
|
||
|
curLine
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function isStringEnd(type, ch, input, pos) {
|
||
|
if (type === "template") {
|
||
|
return ch === 96 || ch === 36 && input.charCodeAt(pos + 1) === 123;
|
||
|
}
|
||
|
|
||
|
return ch === (type === "double" ? 34 : 39);
|
||
|
}
|
||
|
|
||
|
function readEscapedChar(input, pos, lineStart, curLine, inTemplate, errors) {
|
||
|
const throwOnInvalid = !inTemplate;
|
||
|
pos++;
|
||
|
|
||
|
const res = ch => ({
|
||
|
pos,
|
||
|
ch,
|
||
|
lineStart,
|
||
|
curLine
|
||
|
});
|
||
|
|
||
|
const ch = input.charCodeAt(pos++);
|
||
|
|
||
|
switch (ch) {
|
||
|
case 110:
|
||
|
return res("\n");
|
||
|
|
||
|
case 114:
|
||
|
return res("\r");
|
||
|
|
||
|
case 120:
|
||
|
{
|
||
|
let code;
|
||
|
({
|
||
|
code,
|
||
|
pos
|
||
|
} = readHexChar(input, pos, lineStart, curLine, 2, false, throwOnInvalid, errors));
|
||
|
return res(code === null ? null : String.fromCharCode(code));
|
||
|
}
|
||
|
|
||
|
case 117:
|
||
|
{
|
||
|
let code;
|
||
|
({
|
||
|
code,
|
||
|
pos
|
||
|
} = readCodePoint(input, pos, lineStart, curLine, throwOnInvalid, errors));
|
||
|
return res(code === null ? null : String.fromCodePoint(code));
|
||
|
}
|
||
|
|
||
|
case 116:
|
||
|
return res("\t");
|
||
|
|
||
|
case 98:
|
||
|
return res("\b");
|
||
|
|
||
|
case 118:
|
||
|
return res("\u000b");
|
||
|
|
||
|
case 102:
|
||
|
return res("\f");
|
||
|
|
||
|
case 13:
|
||
|
if (input.charCodeAt(pos) === 10) {
|
||
|
++pos;
|
||
|
}
|
||
|
|
||
|
case 10:
|
||
|
lineStart = pos;
|
||
|
++curLine;
|
||
|
|
||
|
case 8232:
|
||
|
case 8233:
|
||
|
return res("");
|
||
|
|
||
|
case 56:
|
||
|
case 57:
|
||
|
if (inTemplate) {
|
||
|
return res(null);
|
||
|
} else {
|
||
|
errors.strictNumericEscape(pos - 1, lineStart, curLine);
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
if (ch >= 48 && ch <= 55) {
|
||
|
const startPos = pos - 1;
|
||
|
const match = input.slice(startPos, pos + 2).match(/^[0-7]+/);
|
||
|
let octalStr = match[0];
|
||
|
let octal = parseInt(octalStr, 8);
|
||
|
|
||
|
if (octal > 255) {
|
||
|
octalStr = octalStr.slice(0, -1);
|
||
|
octal = parseInt(octalStr, 8);
|
||
|
}
|
||
|
|
||
|
pos += octalStr.length - 1;
|
||
|
const next = input.charCodeAt(pos);
|
||
|
|
||
|
if (octalStr !== "0" || next === 56 || next === 57) {
|
||
|
if (inTemplate) {
|
||
|
return res(null);
|
||
|
} else {
|
||
|
errors.strictNumericEscape(startPos, lineStart, curLine);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return res(String.fromCharCode(octal));
|
||
|
}
|
||
|
|
||
|
return res(String.fromCharCode(ch));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function readHexChar(input, pos, lineStart, curLine, len, forceLen, throwOnInvalid, errors) {
|
||
|
const initialPos = pos;
|
||
|
let n;
|
||
|
({
|
||
|
n,
|
||
|
pos
|
||
|
} = readInt(input, pos, lineStart, curLine, 16, len, forceLen, false, errors));
|
||
|
|
||
|
if (n === null) {
|
||
|
if (throwOnInvalid) {
|
||
|
errors.invalidEscapeSequence(initialPos, lineStart, curLine);
|
||
|
} else {
|
||
|
pos = initialPos - 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
code: n,
|
||
|
pos
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function readInt(input, pos, lineStart, curLine, radix, len, forceLen, allowNumSeparator, errors) {
|
||
|
const start = pos;
|
||
|
const forbiddenSiblings = radix === 16 ? forbiddenNumericSeparatorSiblings.hex : forbiddenNumericSeparatorSiblings.decBinOct;
|
||
|
const isAllowedSibling = radix === 16 ? isAllowedNumericSeparatorSibling.hex : radix === 10 ? isAllowedNumericSeparatorSibling.dec : radix === 8 ? isAllowedNumericSeparatorSibling.oct : isAllowedNumericSeparatorSibling.bin;
|
||
|
let invalid = false;
|
||
|
let total = 0;
|
||
|
|
||
|
for (let i = 0, e = len == null ? Infinity : len; i < e; ++i) {
|
||
|
const code = input.charCodeAt(pos);
|
||
|
let val;
|
||
|
|
||
|
if (code === 95 && allowNumSeparator !== "bail") {
|
||
|
const prev = input.charCodeAt(pos - 1);
|
||
|
const next = input.charCodeAt(pos + 1);
|
||
|
|
||
|
if (!allowNumSeparator) {
|
||
|
errors.numericSeparatorInEscapeSequence(pos, lineStart, curLine);
|
||
|
} else if (Number.isNaN(next) || !isAllowedSibling(next) || forbiddenSiblings.has(prev) || forbiddenSiblings.has(next)) {
|
||
|
errors.unexpectedNumericSeparator(pos, lineStart, curLine);
|
||
|
}
|
||
|
|
||
|
++pos;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (code >= 97) {
|
||
|
val = code - 97 + 10;
|
||
|
} else if (code >= 65) {
|
||
|
val = code - 65 + 10;
|
||
|
} else if (_isDigit(code)) {
|
||
|
val = code - 48;
|
||
|
} else {
|
||
|
val = Infinity;
|
||
|
}
|
||
|
|
||
|
if (val >= radix) {
|
||
|
if (val <= 9 && errors.invalidDigit(pos, lineStart, curLine, radix)) {
|
||
|
val = 0;
|
||
|
} else if (forceLen) {
|
||
|
val = 0;
|
||
|
invalid = true;
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
++pos;
|
||
|
total = total * radix + val;
|
||
|
}
|
||
|
|
||
|
if (pos === start || len != null && pos - start !== len || invalid) {
|
||
|
return {
|
||
|
n: null,
|
||
|
pos
|
||
|
};
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
n: total,
|
||
|
pos
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function readCodePoint(input, pos, lineStart, curLine, throwOnInvalid, errors) {
|
||
|
const ch = input.charCodeAt(pos);
|
||
|
let code;
|
||
|
|
||
|
if (ch === 123) {
|
||
|
++pos;
|
||
|
({
|
||
|
code,
|
||
|
pos
|
||
|
} = readHexChar(input, pos, lineStart, curLine, input.indexOf("}", pos) - pos, true, throwOnInvalid, errors));
|
||
|
++pos;
|
||
|
|
||
|
if (code !== null && code > 0x10ffff) {
|
||
|
if (throwOnInvalid) {
|
||
|
errors.invalidCodePoint(pos, lineStart, curLine);
|
||
|
} else {
|
||
|
return {
|
||
|
code: null,
|
||
|
pos
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
({
|
||
|
code,
|
||
|
pos
|
||
|
} = readHexChar(input, pos, lineStart, curLine, 4, false, throwOnInvalid, errors));
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
code,
|
||
|
pos
|
||
|
};
|
||
|
}
|