Found in the post-#933 survey and re-executed against node v24.4.1, on a Debug build of main (f2bafeb) and with #944's lexer fix applied. The results are the same on both builds. node accepts every program; zig-js rejects each one:
| program | node | zig-js |
|---|---|---|
var i = 1; var x = `${ i++ / 2 }`; x | 0.5 | UnterminatedString |
var x = `${ typeof /}/ }`; x | object | UnterminatedString |
var x = `${ (() => { return /}/.source })() }`; x | } | ExpectedToken |
var x = `${ void /`/ }`; x | undefined | UnterminatedString |
var x = `${ [1].map(function(){ return /"/.source })[0] }`; x | " | UnterminatedString |
var x = `${ "source" in /}/ }`; x | true | UnterminatedString |
var x = `${ (function(v){ switch (v) { case /}/.source: return 12 } })("}") }`; x | 12 | ExpectedToken |
function t(s, v){ return v; } var x = t`${ typeof /}/ }`; x (tagged) | object | UnterminatedString |
s.split("").map(c => { return /"/.test(c) ? """ : c }).join("") inside ${} | a"b | UnterminatedString |
The survey's template probes contain 21 such programs in all. Today 18 of them fail, plus 3 realistic ones (escape/HTML helpers and a counter).
Cause: the end of a ${ … } substitution is found by a byte-level scan (lexTemplate/substEnd in src/lexer.zig) that guesses regex vs division from the last character. It does not know keywords (return, typeof, void, in, case, delete, new, yield, await), postfix ++/--, or whether a } closed a block. Any regex or division whose bytes contain a quote, brace or backtick therefore breaks brace matching. Each nesting level then re-lexes its substitution with a fresh sub-parser.
Cost: as a parse-only probe, a 400 KB array literal nested in D templates takes 0.67 / 0.78 / 1.09 / 1.77 s at D = 32 / 64 / 128 / 256 (one sample each, survey run), against a flat 0.49 / 0.55 / 0.43 / 0.45 s in D parentheses. Each level rescans the payload.
Fix direction: template head/middle/tail tokens emitted into the main token stream, with the substitution lexed by the main lexer rules, as the spec's grammar does. That shares its root with #947 (regex vs division needs the parser's goal symbol), so the two should be designed together. Freeze a D x payload growth row in bench/frontend_parse.zig before changing code.