# Regex compatibility layer

The validator uses `moonbitlang/regexp@0.3.6` (pinned in `moon.mod`). After
re-reading the pinned engine source (`internal/parse`, `internal/vm`,
`internal/unicode`) and probing behavior on native/JS/Wasm, the adapter now
delegates everything the engine implements natively and translates only the
constructs the engine lacks. Dynamic patterns remain compiled once per schema.
Rejected patterns produce explicit `Unsupported` / typed `PatternError`
results, never a silently substituted non-match.

## Supported profile (engine-native, verified)

- ECMA-262 ASCII shorthand classes `\d \D \w \W \s \S`, including negated and
  shorthand forms inside classes (`[\D]`, `[\W]`, `[\S]`). The engine's range
  tables match ECMA-262 exactly: `\s` covers U+0009, U+000A–U+000D, U+0020,
  U+00A0, U+1680, U+2000–U+200A, U+2028, U+2029, U+202F, U+205F, U+3000,
  U+FEFF; `\w` is `[A-Za-z0-9_]`. The former adapter `shorthand` rewrite shim
  was removed.
- Numeric backreferences `\1`..`\999` and named backreferences `\k<name>` with
  leftmost-first backtracking (the engine enables its backtracking VM for
  patterns containing backreferences). Unset or empty backreferences match
  empty, per ECMA-262. The former `^(body)\1$` equal-halves adapter profile was
  removed; its regression table still passes through the engine unchanged.
- Named capture groups `(?<name>...)`.
- Word boundaries `\b`/`\B` (ASCII word characters, ECMA-262 semantics).
- General_Category Unicode properties: `\p{...}` and negated `\P{...}`,
  including inside classes (`[\P{Lu}]`), resolved through the engine's
  General_Category value-alias table (`Lu`, `Letter`, `digit`, `cntrl`, …).
  The adapter additionally normalizes the `General_Category=` and `gc=`
  property-name prefixes; the engine has no `name=value` form of its own.
- ECMA-262 empty classes: `[]` never matches and `[^]` matches every code
  point, including line terminators.
- Scoped inline flag groups `(?i:...)`, `(?m:...)`, `(?s:...)` and negated
  combinations with engine Unicode case folding; flags are restored after the
  group. Global inline flags `(?i)` are dialect-invalid (the official suite
  asserts this) and surface as typed engine rejections, as do Python/PCRE
  forms `(?P<name>...)`, `(?P=name)`, `(?#...)` and PCRE lookarounds.
- `^`/`$` as strict begin/end-of-text assertions; `$` does not match a
  trailing newline.
- Strict `\xHH` hexadecimal escapes (adapter rewrite), control escapes
  `\cX` for one ASCII letter (adapter rewrite), `\t \n \v \f \r`, `\0`,
  `\uXXXX`/`\u{...}`, quantifiers including lazy forms and `{n,m}`, and
  alternation.
- UTF-16 surrogate-pair correctness for astral characters
  (`optional/non-bmp-regex.json` profile).
- A leading anchored positive/negative lookahead chain (`^(?=...)(?!...)`) is
  still evaluated with separate compiled expressions; nested captures and
  lookarounds inside the chain remain refused because capture numbering across
  separately compiled expressions would diverge from ECMA semantics.

## Adapter rewrites still required (engine gaps, verified in source)

- `.` in ECMA-262 must exclude exactly U+000A, U+000D, U+2028, U+2029. The
  engine's dot excludes U+000A–U+0010 and fails to exclude U+2028/U+2029, so
  the adapter rewrites `.` to `[^\n\r\u{2028}\u{2029}]` outside classes.
- `\xHH` escapes are not engine syntax; the adapter rewrites them to
  `\u00HH`. Non-hex or incomplete forms are typed-unsupported.
- `\cX` control escapes raise the engine's reserved-escape error; the adapter
  maps one ASCII letter to its control code point (`uppercase(letter) & 0x1F`,
  so `\cC`/`\cc` → U+0003). `\c` with a non-letter operand stays
  typed-unsupported: ECMA-262 Unicode mode makes it a SyntaxError while
  Annex B web-compat treats it as literal `c`, and the adapter refuses to
  guess. This closed the four recorded `optional/ecmascript-regex.json`
  unsupported cases (#2/0–#2/1, #3/0–#3/1).

## Typed-unsupported (with reasons)

- Lookbehind `(?<=...)`/`(?<!...)` and non-leading lookarounds: the engine VM
  has no lookahead/lookbehind instruction; only the leading anchored chain
  shim exists. Reasons: "lookbehind is not supported", "only a leading
  lookahead chain is supported".
- Unicode properties beyond General_Category (`\p{Script=Greek}`,
  `\p{Alphabetic}`, …): the engine resolves General_Category aliases only and
  rejects everything else at compile time. No ECMA LooseMatch normalization
  (case/space/underscore-insensitive property names) exists upstream either;
  canonical short/long spellings work.
- Multi-digit backreferences follow the engine's greedy parse: `\10` with
  fewer than ten groups is a typed engine rejection rather than ECMA's
  digit-backtracking interpretation.
- Escaped surrogate code points (`\uD800`–`\uDFFF`) are rejected by the engine
  (lone surrogates cannot be expressed as MoonBit chars); literal astral
  characters work.
- Anything the engine rejects (unbalanced groups/classes, unknown escapes that
  are not Annex B identity escapes, malformed quantifiers, dialect-invalid
  `(?...)` forms) becomes `Unsupported` with reason "pattern rejected by the
  regexp engine".
- Backreference patterns run on the engine's backtracking VM, which has
  exponential worst-case complexity on adversarial input — inherent to
  ECMA-262 semantics, documented here rather than hidden.

## Maintenance markers

Remaining private shims use
`#deprecated(message, skip_current_package=true)` as reminders to review future
upstream releases: `translate` (dot/`\x`/`\c` rewrites), `unicode_property`
(General_Category prefix normalization), and the leading-lookahead parser in
`regex_adapter.mbt`. The `shorthand` class rewriter and the
`repeated_capture_body`/`repeated_capture_matches` equal-halves backreference
profile were deleted because upstream 0.3.6 covers them natively; their
regression cases remain in `regex_adapter_test.mbt`/`regex_core_test.mbt`.
Public APIs (`compile_pattern`, `pattern_matches`, `PatternSupport`,
`PatternError`, `CompiledPattern`) are unchanged.

## Verification and benchmark ownership

This phase's own regression suites: `regex_adapter_test.mbt`,
`regex_core_test.mbt`, `regex_ecma_test.mbt` (official-suite transcriptions) —
19 + 3 + 6 tests, identical results on native, JS and Wasm.

Pinned official corpus at revision
`5b0ee1613e45fcc2bddac00e07c19cd49b00d8a8`, filtered runs through
`cmd/conformance`:

- `optional/ecmascript-regex.json`: 74/74 cases pass (previously 70 pass,
  4 unsupported on the `\c` control-escape groups).
- `optional/non-bmp-regex.json`: 12/12 cases pass (unchanged).

The full-corpus numbers in `benchmark-results/conformance-dialects.json`
(standard 1301/1301; optional 547 passed / 480 mismatch / 6 unsupported /
3 rejected) are the existing recorded report from another workstream and were
not rewritten here. The `optional/format/*` mismatches (including
`format/regex.json` syntax-validation cases) belong to the format-assertion
workstream; the regex adapter itself is not that validator. Use the existing
scripts in `benchmarks/README.md` for the next full campaign; this change
establishes no new performance baseline.
