Cross-Engine Regex Compatibility CheckerOne regex, seven engines — will it survive the port?
Regex testers let you pick a flavor. None of them tell you what happens when the pattern you wrote for one language meets another. This feature runs your regex, flags and test string through seven engine paths simultaneously — real PCRE2, Go and Rust engines compiled to WebAssembly, native JS RegExp, and a .NET interpreter — groups the engines by whether they produce identical match sets, and flags every rejection, divergence and untestable construct per row. Free, in your browser, nothing uploaded.
The portability problem
"Regular expressions" is not one language. It's a family of dialects that look almost identical and disagree in exactly the places you don't look until production breaks: \w is Unicode-aware by default in Python 3, PCRE2 and .NET, but ASCII-only in JavaScript. Lookbehind must be fixed-width in PCRE2; .NET accepts variable width; RE2-based engines reject lookbehind entirely. Possessive quantifiers, atomic groups, \K, named-group syntax (?<name>…) vs(?P<name>…) — every one of these is a line in some engine's grammar and an error (or worse, a silent behavior change) in another's.
The standard workflow makes this invisible. You test the pattern in one tester with one flavor selected, it works, you paste it into another language, and the failure shows up far away from the regex — a log line that never matches, a validation that rejects valid input, an import script that silently skips rows. By the time anyone connects the symptom back to the pattern, the regex has been through three people and a rewrite.
A flavor selector doesn't solve this — you still have to try every flavor by hand and compare results from memory. What you need is every engine answering at once, side by side, on the same input. That's what the Compat button does.
A 60-second tour
Type your pattern and a representative test string, then hit the Compat button in the toolbar — or share the deep link/compat directly:

The report opens and every engine starts working immediately; rows stream in as each one resolves. For a well-behaved pattern the outcome is short and sweet:

Each row carries a badge naming its execution path — native, WASM engine, interpreter or emulated — so you always know what kind of evidence you're looking at. Click a row to expand the engine's own error message or a preview of its matches.
Same regex, different matches
Here is the case that makes the feature worth having. The pattern is as innocent as regexes get: \w+. The input is héllo wörld. Both compile everywhere. Both "work". And they split the engines into two camps with different outputs:

JavaScript's \w is [A-Za-z0-9_] unless the u flag with property escapes comes into play, so it finds h, llo,w, rld — four matches. PCRE2, Python, .NET, Go and Rust treat \w as Unicode-aware (Go and Rust by design, the others by default), matching héllo and wörld whole — two matches. Java's default \w is ASCII-only too, which is why it clusters with JavaScript.
No error is thrown anywhere. Port this pattern between any two of these engines and the behavior changes silently. The report makes the split visible in one glance: the verdict banner, the two colored cluster dots, and the differs marker on the minority rows.
The three ways a port fails
A pattern that uses \K — PCRE's "keep" escape, which resets where the reported match starts — fails differently in different engines. The report shows all three failure shapes in one view:

- Works as intended. PCRE2 is the only engine doing what the author meant — one match, starting after \K. Its row is marked differs because it's the minority result.
- Rejected outright. .NET refuses the pattern — expand the row and you get the engine's own error, not a paraphrase. You find this out here, instead of in a CI log three languages away from where the regex was written.
- Silently does nothing. The remaining engines accept the pattern and match nothing — the strict \K is consumed as a literal K, and there is no K after the @. No error, no match: the worst failure mode, because nothing announces it.
One row reads valid · untestable here: the dialect parser accepts the syntax, but this site has no faithful execution path for it, so the row refuses to guess. That's a deliberate honesty rule — an untestable engine is labeled untestable, never quietly approximated.
How agreement is decided
Each engine's full match set is reduced to a signature: every match's position, matched text and capture groups. Engines with equal signatures are clustered under one colored dot; the cluster with the most engines is treated as the majority, and any ok-row outside it gets the differs marker. Signatures include positions and groups — not just match counts — because two engines can agree on how many matches while disagreeing on where they are, and that distinction matters when you port.
The verdict banner summarizes the state in one line, in one of three shapes:
- Fully compatible — identical match results in all 7 engines — the signature test passed everywhere.
- Compiles everywhere, but match results differ… — accepted by all, with two or more distinct outcomes.
- 5/7 engines accept this pattern · … — a mixed report: acceptances, divergences, untestables and rejections, counted.
And one consistency guarantee by construction: every row runs through the same execution path the editor itself uses when you select that flavor manually. There is no separate "compatibility mode" — the report can never disagree with what you see when you switch flavors by hand.
The seven engine paths
Seven rows cover the eight flavors the tester supports (PCRE v1 rides along with PCRE2 — they share the dialect and engine here). The badge on each row says which of the four path types it is:
| Row | Badge | What actually runs |
|---|---|---|
| PCRE2 | WASM engine | PCRE2 10.x compiled to WebAssembly in a worker — also covers PCRE v1 |
| Go | WASM engine | The Go regexp (RE2-lineage) engine compiled to WASM |
| Rust | WASM engine | The Rust regex crate compiled to WASM |
| JavaScript | native | Your browser’s own RegExp engine (V8 and friends) |
| .NET | interpreter | A TypeScript interpreter of System.Text.RegularExpressions semantics — balancing groups, conditionals, variable-length lookbehind |
| Python | emulated | Validity from the dialect parser; matches from a normalized equivalent executed on the JS engine |
| Java | emulated | Same split: parser verdict + normalized execution |
The emulated rows are labeled emulated because pretending they're real engines would be the one unforgivable sin in a compatibility report: they answer "is this valid in the dialect, and what does the normalized equivalent match?" — which is what the editor shows for those flavors — not "what does CPython's or the JVM's engine do?" Every other row runs the production engine binary.
Exporting the report
One click copies the whole report as Markdown — verdict, per-engine outcomes with their execution paths, and the distinct-result groups when engines disagree. Paste it into a pull request, a migration ticket or an architecture-decision record, and the reviewer sees the portability evidence instead of taking your word for it:
## Regex Compatibility Report Pattern `\\w+` · flags `gi` · 11-char test string · 2026-10-12, 09:41 **Compiles everywhere, but match results differ across engines (2 distinct outcomes) — see the groups below.** | Engine | Execution path | Result | |---|---|---| | ✅ JavaScript | V8 RegExp (browser native) | 4 matches | | ✅ PCRE2 | PCRE2 10.x WASM · also covers PCRE v1 | 2 matches | | ✅ Python | dialect parser + normalized execution | 2 matches | | ✅ Java | dialect parser + normalized execution | 4 matches | | ✅ .NET | .NET semantics interpreter (TS) | 2 matches | | ✅ Go | Go regexp (RE2) WASM | 2 matches | | ✅ Rust | Rust regex crate WASM | 2 matches | Distinct outcome groups: - JavaScript, Java → 4 matches - PCRE2, Python, .NET, Go, Rust → 2 matches
Because the report includes each row's execution path, the evidence stays auditable months later: readers can tell real-engine results from emulated ones without reverse-engineering the tool.
How it compares
Multi-flavor testers exist — you pick a flavor, the tester runs it. Running all flavors on one pattern and diffing the outputs is the part that has always been manual. This feature was built to delete that manual step:
| Capability | Typical regex testers | This compatibility checker |
|---|---|---|
| Engines per run | One — whichever flavor you selected | Seven paths simultaneously, results streaming in per row |
| Result comparison | You switch flavors and compare from memory | Match-set signatures cluster engines into agreeing groups automatically; minority rows marked differs |
| Real engines | Varies; often one engine approximating the rest | PCRE2, Go and Rust as real WASM-compiled engines, .NET semantics interpreter, native JS RegExp |
| Honesty labels | All flavors presented as equals | Every row badges its path: native / WASM engine / interpreter / emulated — untestable rows say so |
| Evidence export | Screenshot or nothing | Copyable Markdown report with verdict, outcomes and execution paths |
| Price | Multi-engine anything tends to sit in a Pro tier | Free, no account, runs entirely in your browser |
Also in the box: deep link at /compat for sharing the report entry point, per-row engine error messages on expand, and a "How to read this report" primer built into the panel.
FAQ
Does the compatibility check send my patterns anywhere?›
No. All seven paths run locally in your browser — PCRE2, Go and Rust as WebAssembly, JavaScript natively, .NET through a TypeScript interpreter, Python and Java via the in-page dialect parser and normalized execution. Nothing is uploaded.
Why does the same regex match differently in different engines?›
Because the dialects genuinely differ: \w is Unicode-aware by default in Python 3, PCRE2 and .NET but ASCII-only in JavaScript and Java; \K exists only in the PCRE family; lookbehind width rules vary; RE2-lineage engines reject backtracking constructs outright. Same source text, different grammars.
What does "valid · untestable here" mean?›
The dialect parser accepts the syntax, but this site has no faithful execution path for it — for example a construct the normalizer cannot translate without changing its meaning. The row refuses to guess rather than showing an approximation as if it were the engine’s answer.
Do the Python and Java rows run the real engines?›
No — and they are labeled "emulated" for that reason. Their validity comes from the dialect parser and their matches from a normalized equivalent, exactly what the editor runs for those flavors. Treat them as a strong syntax check, not a CPython/JVM execution.
How is "identical results" defined?›
Two engines agree only if their full match sets match exactly: same positions, same matched text, same capture groups. Match counts alone are not enough — four matches at four different positions is a divergence, and the report treats it as one.
Does "fully compatible" guarantee identical production behavior?›
No. Flags, Unicode tables and engine versions all influence behavior, and a production engine may reject a construct this build tolerates. The report is the fastest way to surface portability bugs before they ship — final authority is always your target engine’s own tests.
Your regex works here. Will it work there?
Open the tester, paste your pattern, press Compat. Ten seconds later you'll know which engines accept it, which ones agree, and exactly where the port breaks.
Open the Compatibility Checker