feat: add multi-engine regex flavour support

This commit is contained in:
2026-07-27 11:43:51 +02:00
parent 7079cde15f
commit 7f3a91ad37
340 changed files with 643286 additions and 483 deletions

View File

@@ -1,75 +1,87 @@
# Flavour support
| Flavour | Syntax | Execution | Replacement | Captures | Trace | Analysis | Generated cases | Native offsets |
| ---------------------- | ------------------------------------------------------------ | --------------------------------------------------------- | ---------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------- | -------------- |
| ECMAScript | regexpp 4.12.2; explanations partial/feature-matrix tested | Current browser `RegExp`; conformance tested | Bounded ECMAScript `GetSubstitution` | Named/numbered; final repeated capture; no history | Unavailable | Experimental static heuristics + bounded native probes | Bounded normalized-AST sampling; actual-engine verified | UTF-16 |
| PCRE2 10.47 | Application lexical provider partial; compiler authoritative | Pinned 8-bit WASM; bounded/killable/conformance tested | Native bounded `pcre2_substitute()` loop | Native named/numbered records; no capture history | Bounded reported automatic callouts; derived movement labels | Unavailable; ECMAScript heuristics are never applied | Unavailable; structural syntax provider is partial | UTF-8 bytes |
| Python `re` | Application lexical provider partial; compiler authoritative | CPython 3.14.2 in self-hosted Pyodide 314.0.3 | Native bounded `re` expansion | Native named/numbered spans; no capture history | Unavailable | Unavailable; ECMAScript heuristics are never applied | Unavailable; structural syntax provider is partial | Code points |
| Java `java.util.regex` | Application lexical provider partial; compiler authoritative | TeaVM 0.15.0 class library; bounded/killable; not OpenJDK | TeaVM single-digit `$n`; no `${name}` | Native numbered ranges; lexical names; no history | Unavailable | Unavailable; ECMAScript heuristics are never applied | Unavailable; structural syntax provider is partial | UTF-16 |
| PCRE | Unavailable | Unavailable | Unavailable | Unavailable | Unavailable | Unavailable | Unavailable | Unavailable |
| Go `regexp` | Unavailable | Unavailable | Unavailable | Unavailable | Unavailable | Unavailable | Unavailable | Planned bytes |
| Rust `regex` | Unavailable | Unavailable | Unavailable | Unavailable | Unavailable | Unavailable | Unavailable | Planned bytes |
| .NET | Unavailable | Unavailable | Unavailable | Planned capture history | Unavailable | Unavailable | Unavailable | Planned UTF-16 |
Version 0.4.0 exposes twelve executable profiles. “Partial lexical provider”
means that Regex Tools recognizes common structure and replacement tokens for
explanation only; the named runtime compiler and replacement operation remain
authoritative.
ECMAScript tests cover its complete exposed flag matrix, internal indices,
zero-length iteration, captures, lookbehind, backreferences, Unicode properties
and bounded replacement.
| Profile | Syntax and execution identity | Replacement | Captures and native offsets |
| ----------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| JavaScript (ECMAScript) | regexpp 4.12.2 with ECMAScript 2025 syntax; current browser `RegExp` executes | Bounded ECMAScript `GetSubstitution` | Named/numbered, final repeated capture; UTF-16 |
| PCRE2 | Partial provider; official standalone PCRE2 10.47 8-bit WASM executes in mandatory UTF/UCP mode | Native bounded `pcre2_substitute()` loop | Native named/numbered records, no history; UTF-8 bytes |
| PHP | Partial provider; actual PHP 8.5.8 `preg_*` in `@php-wasm/web-8-5` 3.1.46, reporting PCRE2 10.44 | Fixed bounded PHP-token expansion; load-time `preg_replace()` parity | Native named/numbered records, no history; UTF-8 bytes |
| Perl | Partial provider; actual Perl 5.28.1 through legacy WebPerl 0.09-beta | Fixed bounded literal/`$$`/`$n`/`${name}` expansion; arbitrary Perl expressions unavailable | Native numbered spans plus bounded name annotations, no history; Unicode code points |
| Python | Partial provider; CPython 3.14.2 `re` in Pyodide 314.0.3 | Native bounded `re` expansion | Native named/numbered spans, no history; Unicode code points |
| Ruby | Partial provider; CRuby 4.0.0 `Regexp` in ruby.wasm 2.9.3-2.9.4 | Native `gsub`/`sub` iteration plus bounded CRuby 4.0.0-compatible expansion | Native named/numbered spans, no history; Unicode code points |
| Java | Partial provider; TeaVM 0.15.0 `java.util.regex`, not OpenJDK | TeaVM single-digit `$n`; no `${name}` | Native numbered ranges plus lexical names, no history; UTF-16 |
| C++ | Partial provider; Emscripten 6.0.4 libc++ `std::wregex`; modified ECMAScript grammar by default | Fixed bounded formatter; load-time libc++ `match.format` parity | Numbered captures only, no history; 32-bit `wchar_t` code points |
| Go | Partial provider; Go 1.26.5 standard-library `regexp` with RE2 syntax | Fixed bounded expansion; load-time `regexp.ExpandString` parity | Native named/numbered records, no history; UTF-8 bytes |
| Rust | Partial provider; Rust `regex` crate 1.13.1, built with rustc 1.97.1 | Fixed bounded expansion; load-time `Captures::expand` parity | Native named/numbered records, no history; UTF-8 bytes |
| .NET | Partial provider; .NET 10.0.10 `System.Text.RegularExpressions`, invariant culture | Fixed streaming .NET-token expansion with load-time native `Match.Result` parity | Named/numbered final records, no capture history in this adapter; UTF-16 |
| Scala/JVM compatibility | Partial provider; explicitly shares TeaVM 0.15.0 `java.util.regex`; no Scala runtime | Same TeaVM single-digit `$n` subset; no `${name}` | Same ranges as the TeaVM Java adapter; UTF-16 |
PCRE2 tests cover `g`, `i`, `m`, `s`, `x`, `U`, `J`, mandatory UTF/UCP,
branch-reset groups, recursion, `\K`, duplicate names, native substitution,
astral byte/UTF-16 ranges, zero-length iteration, compile errors, match limits
and result/output truncation. Trace tests cover reported automatic callouts,
bounded mark copying, native stop at the complete-event cap, compile ranges,
worker loading and browser rendering.
## Flags and options
Python tests cover application-level `g`, CPython flags `a`, `i`, `m`, `s` and
`x`, Python-only named-group/replacement syntax, native `re` substitution,
astral code-point/UTF-16 normalization, zero-length iteration, compile errors,
result limits, runtime identity and worker recovery.
- JavaScript exposes `g`, `i`, `m`, `s`, `u`, `v`, `y` and `d`; `u` and `v`
are mutually exclusive.
- Standalone PCRE2 exposes application iteration `g` plus `i`, `m`, `s`, `x`,
`U` and `J`, with match-step, depth and heap limits. UTF/UCP is mandatory.
- PHP exposes application iteration `g` plus the reviewed `preg` modifiers
`i`, `m`, `s`, `x`, `u`, `U`, `A`, `D`, `J`, `n` and `r`. PHP's `u` is not
conflated with standalone PCRE2's mandatory UTF/UCP configuration.
- Legacy Perl exposes `g`, `i`, `m`, `s`, `x`, `n` and one of `a`, `d`, `l` or
`u`. The two-character `/aa` mode is not exposed.
- Python exposes application iteration `g` plus `a`, `i`, `m`, `s` and `x`.
- Ruby exposes application iteration `g` plus `i`, `m` and `x`; Ruby `m`
includes dot-all behavior.
- Java and Scala compatibility expose application iteration `g` plus TeaVM
`Pattern` flags `i`, `d`, `m`, `s`, `u`, `x` and the documented `U`
compatibility request.
- C++ exposes application iteration `g` plus `i`, `n`, `o` and `c`, and a
required grammar option: `ECMAScript`, `basic`, `extended`, `awk`, `grep` or
`egrep`.
- Go exposes application iteration `g` plus `i`, `m`, `s` and `U`.
- Rust exposes application iteration `g` plus `i`, `m`, `s`, `U`, `u`, `x` and
`R`; Unicode is enabled by default.
- .NET exposes application iteration `g` plus `i`, `m`, `s`, `n`, `x`, `r`,
`c` and `b`. Execution is always culture invariant; `c` makes that fixed
compatibility choice explicit.
Java tests cover application-level `g`, TeaVM `Pattern` flags, Java named-group
syntax, TeaVM's native single-digit `$n` `Matcher` replacement, explicit
rejection of unsupported `${name}`, astral UTF-16 ranges, zero-length
iteration, compile errors, result limits, module identity and worker recovery.
The runtime and UI explicitly report that TeaVM 0.15.0 is not OpenJDK and does
not implement OpenJDK `Pattern.UNICODE_CHARACTER_CLASS`; `U` is a compatibility
request rather than an exact implementation of that flag.
## Verified boundaries
ECMAScript static findings and generated-input timing observations are
advisory. They describe only the selected ECMAScript 2025 pattern, browser
runtime and bounded inputs; they are not proofs of safety or general
complexity.
Every adapter has focused identity, request and result-contract tests.
Conformance and browser suites exercise compilation errors, bounded matching,
zero-length iteration, range normalization, replacement and worker recovery
across the profile set. Pack verifiers additionally check self-hosted assets,
metadata, closed-file-set checksums and applicable licences/notices.
ECMAScript generated-case version 1 uses only a complete accepted normalized
AST, an explicit deterministic seed and bounded sampling strategies. Every
retained positive or negative label is confirmed by the actual selected
browser engine. PCRE2, Python and Java generation are unavailable because
their current lexical providers are intentionally partial; ECMAScript behavior
is never relabelled as another flavour.
TeaVM supplies an Apache Harmony-derived class library. It is not OpenJDK and
does not implement OpenJDK `Pattern.UNICODE_CHARACTER_CLASS`; `U` means the
documented TeaVM compatibility behavior in both the Java and Scala profiles.
The Scala profile also does not implement `scala.util.matching.Regex` wrapper
or extractor APIs.
The PCRE2, Python and Java explanation providers deliberately do not invent
full structural coverage. PCRE2 branch-reset numbering is omitted from the
syntax tree, while actual capture numbers, names and ranges still come from the
selected runtime result.
The Perl target is intentionally legacy. Its unmodified WebPerl loader contains
one `eval(code)` site in an optional Perl-to-JavaScript interoperability import.
The fixed Regex Tools bridge does not load that facility, accepts pattern and
replacement values only as JSON data, uses `no re 'eval'`, rejects `(?{...})`
and `(??{...})`, and passes the documented CSP smoke without broad
`'unsafe-eval'`.
ECMAScript and PCRE2 support exact-request comparison for syntax acceptance,
engine compilation, matches, captures and engine-native replacement. Results
align by normalized UTF-16 ranges while retaining each native offset model.
Timeout, cancellation, rejection and truncation are never presented as
equivalent results.
## Feature scope
Pattern formatting is available only for an exact accepted ECMAScript regexpp
snapshot. It canonicalizes literal slashes and raw control/line-separator
representations, then requires paired actual-engine and applicable exact-test
validation before apply. PCRE2, Python and Java formatting is unavailable
because those providers are deliberately partial; their syntax is never
treated as ECMAScript.
Static findings, bounded growth probes, benchmarks, generated cases and
formatting are ECMAScript-only. Generated-case version 1 requires the complete
accepted regexpp AST and verifies every retained label through the actual
browser engine. No partial provider is passed through that generator or
formatter.
Subject minimization accepts only the verified ECMAScript and PCRE2 paths.
Python and Java selections are disabled rather than being minimized through a
different flavour. Semantic comparison is likewise an explicit
ECMAScript/PCRE2 operation; it is not a generic four-way equivalence checker.
Automatic-callout tracing and reviewed C17 code generation are standalone
PCRE2-only. The PHP profile does not reuse the standalone PCRE2 10.47 trace or
code-generation path because its authoritative engine is PHP `preg` with
PCRE2 10.44.
PCRE2 is the only current code-generation target. The reviewed C17 generator is
pinned to the PCRE2 10.47 8-bit API; other language generators remain
unavailable until they pass equivalent escaping and named-toolchain gates.
Exact-request semantic comparison and subject minimization support only their
reviewed ECMAScript/standalone-PCRE2 paths. Other selections are disabled
rather than routed through another profile. Results never claim generic
twelve-way equivalence.