Surfaced by `test-files/test_gap_regexp_advanced.ts` running under the new feature-matrix runner (#801).
Repro
const result = \"hello world foo\".replace(
/(\\w+)/g,
(match: string) => match.charAt(0).toUpperCase() + match.slice(1)
);
console.log(result);
Expected (Node)
Actual (Perry v0.5.914)
(Two literal spaces — the whitespace between matches survives, but each matched word is replaced with an empty string instead of the replacer-function's return value.)
Narrowing
- The same shape using `match` only (`(match) => match.toUpperCase()`) reproduces the same empty-replacement behavior.
- Returning a hard-coded string from the replacer also returns empty:
\"aXbXc\".replace(/X/g, () => \"Y\")
TODO confirm — but the `offset` test case in the same file does work (`(_match, offset) => { withOffsets.push(offset); return "Y"; }` printed "5 8" correctly while the replacement string was dropped).
So the bug is specifically: the return value of the replacer function is not threaded back through into the output string.
Why this matters
`str.replace(re, fn)` is one of the most common JS idioms — every parser-lite, URL slugifier, syntax-highlighter, sanitizer uses it. With this bug they all silently produce empty/whitespace output.
Part of #793 (axis 1 — language/runtime semantics). Surfaced by #801.
Surfaced by `test-files/test_gap_regexp_advanced.ts` running under the new feature-matrix runner (#801).
Repro
Expected (Node)
Actual (Perry v0.5.914)
(Two literal spaces — the whitespace between matches survives, but each matched word is replaced with an empty string instead of the replacer-function's return value.)
Narrowing
So the bug is specifically: the return value of the replacer function is not threaded back through into the output string.
Why this matters
`str.replace(re, fn)` is one of the most common JS idioms — every parser-lite, URL slugifier, syntax-highlighter, sanitizer uses it. With this bug they all silently produce empty/whitespace output.
Part of #793 (axis 1 — language/runtime semantics). Surfaced by #801.