Check whether two regular expressions are equivalent
A regex equivalence check runs two regular expressions against the same generated strings and reports the first string where they disagree, shrunk to the shortest string that still shows the difference.
JavaScript, TypeScript and Python run in your browser. Nothing to install, no account.
How it works
In Code mode, pick Regex (JavaScript) from either language menu and both sides become pattern fields. Each field takes a pattern and its flags, the way JavaScript writes a regex literal. Equivl then generates 1,000 test strings, runs both patterns on every one in a Web Worker in your browser, and compares the results. Nothing is uploaded.
Uniformly random strings almost never reach the part of a pattern where two
versions differ, so the strings are built from the patterns themselves. About
a third are strings sampled from one pattern's structure, roughly matches.
Half are such samples with one to three edits, using characters taken from
both patterns, including the characters on each side of every class boundary
such as [0-5]. The rest string those characters together. The
first string is always the empty string, and a seed makes every run
reproducible.
On eight pairs chosen to differ in these ways (an
optional separator, a class widened to \w, alternation
precedence in ^cat|dogs?$, a 24-hour clock that allows 24, an
optional decimal point, greedy against lazy, groups against a class, and a
backreference), these strings found the difference on each of 50 seeds, never
later than the 179th string. Uniformly random strings found none of the first
five in 1,000 strings on any seed.
What is compared
- Whether it matches, on by default.
- What it matches, on by default: the matched text and where it starts. This is what catches a lazy
.*?swapped for a greedy.*, which match the same strings but not the same text. - Capture groups, off by default: each group's text, by number and by name. Two patterns with a different number of groups can never agree on them, and the page says so before running.
With the g flag every match in the string is compared; without
it, the first. Every string starts at position 0, so y means a
match only at the start.
What you see
- The shortest string the two patterns disagree on, found by deleting characters from the first disagreeing string for as long as the disagreement survives, with each side's match highlighted in place.
- Every test string in a table, with what each pattern matched in it.
- A pattern that does not compile stops the run before any string is tried, with the browser's own message under the field. Two broken patterns never read as equivalent.
- A pattern that backtracks without end is stopped after 2 seconds on one string. That string is shown, and it counts as a difference, because the other pattern answered.
Limits
- No difference found is evidence, not proof. The set of possible strings is infinite, and the generator is guided by the two patterns rather than exhaustive, so a difference on a kind of string neither pattern mentions can go unfound.
- Patterns run under the browser's JavaScript engine. A Python, PCRE or .NET pattern can mean something different there, which is why the language is named Regex (JavaScript).
- Test strings are at most about 200 characters, so a difference that needs a longer string is out of reach.
- Lookarounds, Unicode property escapes and the
vflag's set operations are matched by the real engine but only approximated when strings are built, so differences inside them are found less reliably.
Questions
Can Equivl prove two regular expressions are equivalent?
No. It tests them on 1,000 generated strings by default and shows a string they disagree on if it finds one. No difference found is evidence, not proof: a difference on a kind of string neither pattern mentions can still go unfound.
Which regex flavor does it use?
JavaScript's, as implemented by your browser. A pattern written for Python, PCRE or .NET may behave differently under it.
Are my patterns uploaded?
No. Both patterns run in a Web Worker in your browser. A share link carries the two patterns and the settings in the part of the URL after the #, which browsers do not send to a server.
What happens with a pattern that backtracks catastrophically?
The run stops that pattern after 2 seconds on one string, shows the string, and counts it as a difference. The strings after it are not run on that side.