Executed through zig-js (Debug ad1ca106, zig-regex 73cc22d) against node v24.4.1. JavaScriptCore agrees with node. Case-insensitive backreferences fold only ASCII, so every non-ASCII pair fails:
| program | node | zig-js |
|---|---|---|
/(à)\1/i.exec("àÀ") | ["àÀ","à"] | null |
/(σ)\1/i.exec("σς"), and with iu | match | null |
/(Σ)\1\1/i.exec("Σσς") | match | null |
/(Dž)\1/i.exec("DžDŽ"), /(Dž)\1/iu.exec("Dždž") | match | null |
/(ſ)\1/iu.exec("ſs"), /(\w)\1/iu.exec("ſS") | match | null |
/(k)\1/iu.exec("KK") (Kelvin sign), /(?<n>k)\k<n>/iu.exec("Kk") | match | null |
/(ß)\1/iu.exec("ßẞ") | match | null |
/(\u{10400})\1/iu.exec("\u{10400}\u{10428}") | match | null |
/(?<=\1(k))x/iu.exec("KKkx") (lookbehind) | ["x","k"] | null |
/(é+)x\1/i.exec("ééxÉÉ") | match | null |
The non-u negative controls already agree with node, which rejects all of them:
/(ß)\1/ion"ßẞ";/(ſ)\1/ion"ſs";/(k)\1/ion"kK";- the astral pair without
u.
Cause: backreferences always run on the backtracker. Its two comparators have two problems:
- They fold only ASCII:
matchCaptureTextat backtrack.zig:1857-1881 (forward) andmatchCaptureTextReverseat :1759-1772 (lookbehind). - They assume both spans have the same byte length.
ſ/s,K/kandẞ/ßdiffer in length.
Fix: compare per character through ECMA-262's Canonicalize (22.2.2.7.3), gated on ecmascript + ignoreCase, with separate cursors so the spans may differ in length.
- With
u/v: Canonicalize is simple case folding (unicode_casefold_data). - Without
u: Canonicalize is the toUppercase rule. It keeps characters whose uppercase has more than one code point, and non-ASCII characters whose uppercase is ASCII (the singletonsſ,K,Å,ẞ, …).