import { Value } from '../value.mts'; import { isHexDigit, isDecimalDigit, isLineTerminator } from '../parser/Lexer.mts'; import type { ParseNode } from '../parser/ParseNode.mts'; /** https://tc39.es/ecma262/#sec-static-semantics-tv */ export function TV(s: string) { let buffer = ''; for (let i = 0; i < s.length; i += 1) { if (s[i] === '\\') { i += 1; switch (s[i]) { case '$': buffer += '$'; break; case '\\': buffer += '\\'; break; case '`': buffer += '`'; break; case '\'': buffer += '\''; break; case '"': buffer += '"'; break; case 'b': buffer += '\b'; break; case 'f': buffer += '\f'; break; case 'n': buffer += '\n'; break; case 'r': buffer += '\r'; break; case 't': buffer += '\t'; break; case 'v': buffer += '\v'; break; case 'x': i += 1; if (isHexDigit(s[i]) && isHexDigit(s[i + 1])) { const n = Number.parseInt(s.slice(i, i + 2), 16); i += 1; buffer += String.fromCharCode(n); } else { return undefined; } break; case 'u': i += 1; if (s[i] === '{') { i += 1; const start = i; do { i += 1; } while (isHexDigit(s[i])); if (s[i] !== '}') { return undefined; } const n = Number.parseInt(s.slice(start, i), 16); if (n > 0x10FFFF) { return undefined; } buffer += String.fromCodePoint(n); } else if (isHexDigit(s[i]) && isHexDigit(s[i + 1]) && isHexDigit(s[i + 2]) && isHexDigit(s[i + 3])) { const n = Number.parseInt(s.slice(i, i + 4), 16); i += 3; buffer += String.fromCodePoint(n); } else { return undefined; } break; case '0': if (isDecimalDigit(s[i + 1])) { return undefined; } return '\u{0000}'; default: if (isLineTerminator(s)) { return ''; } return undefined; } } else { buffer += s[i]; } } return buffer; } export function TemplateStrings(node: ParseNode.TemplateLiteral, raw: boolean) { if (raw) { return node.TemplateSpanList.map((s) => Value(s)); } return node.TemplateSpanList.map((v) => { const tv = TV(v); if (tv === undefined) { return Value.undefined; } return Value(tv); }); }