JavaScript RegExp vs PCRE: Flags and Lookbehind
The live tester is ECMAScript new RegExp, not PCRE2. $ without m does not match before a final newline (/c$/ misses abc\n). Unrecognized \A is the letter A, not start-of-subject. a++ and (?R) throw. (?<=a+) lookbehind works. Flags are gimsuy only. Nothing is uploaded.
A pattern that is legal in PHP preg_match or nginx is not automatically legal here. The live regex tester builds new RegExp(pattern, flags) from the constructor string (not a /…/ literal) and lists exec matches. This page names the dialect gaps that actually change a match or throw — anchors first.
Anchors: $ vs \\Z, and \\A is a letter
$ is the end of the string, not a final newline
MDN and ECMA-262 treat $ as “the next character is out of bounds,” unless m is set (then also before a line terminator). So /c$/ matches abc and misses abc\n. PCRE2 $ (default, without PCRE2_DOLLAR_ENDONLY) also matches before a newline at the end of the subject — the same idea as \Z. PCRE2 \z is only the absolute end. JavaScript has neither escape.
\\A is IdentityEscape for A
PCRE2 \A is start-of-subject even when multiline is on. In ECMAScript without u, an unknown letter escape is an IdentityEscape: \A matches the character A, so it hits Abc and misses abc. With u, the constructor throws Invalid escape. The same rule makes \z mean z. PCRE2 \G and \K do not exist here.
Syntax PCRE2 accepts that this engine rejects
Possessive ++ and atomic groups throw
PCRE2 possessive quantifiers (*+, ++, ?+, {n,m}+) and atomic groups (?>…) consume without giving back. ECMAScript has greedy * / + and lazy *? / +? only. a++ throws Nothing to repeat. (?>a+)b throws Invalid group. Recursion (?R) and free-spacing (?x) are the same invalid-group class. There is no x flag on this page.
Python (?P<name>) and POSIX [:digit:]
PCRE2 named groups accept Perl (?<name>…), (?'name'…), and Python (?P<name>…). JavaScript (ES2018) only implements (?<name>…); (?P<n>a) throws Invalid group. Named captures from a successful exec are match.groups. POSIX [[:digit:]] is not a digit class here — it is the characters :digit inside [], so it does not match 5.
Lookbehind and Unicode that JS does have
Variable-length (?<=a+) is allowed
ES2018 lookbehind can vary in length. (?<=a+)b matches the b in aaab. PCRE2 (since 10.34) allows variable-length lookbehind only when each top-level branch has a maximum; the default cap is 255 characters (pcre2_set_max_varlookbehind, same as Perl). (?<=X+) is rejected there because it has no upper bound. Older PCRE required a fixed length per branch. This page is not a PCRE2 compiler and does not enforce that 255-character cap.
Unicode properties need the u flag
Unicode property escapes (\p{L}) require u. Without it, \p{L} does not match É. \u{41} is a code-point escape only with u; \x41 is a two-digit hex byte in either mode. The live checkboxes are gimsuy only. The d (indices) and v (unicodeSets / class set notation) flags are not offered. Test is a button. Empty patterns are rejected. Copy writes index-end: match lines. This is not replace, not a linter, and not a ReDoS auditor.
Test an ECMAScript pattern
Constructor string, flags gimsuy. Not PCRE2. Nothing is uploaded.
FAQ
- Is the live tester PCRE, PHP preg, or Python re?
- No. Test runs ECMAScript only: new RegExp(pattern, flags) then RegExp.prototype.exec in this browser. It is not PCRE2 (PHP preg_match, nginx, R), not Python re / regex, not POSIX BRE/ERE, and not Java Pattern. A pattern that compiles in PHP can throw SyntaxError here, or match a different substring.
- Why does $ fail on a string that ends with a newline?
- Without the m flag, ECMA-262 $ asserts only that the next character is out of bounds. /c$/ therefore misses "abc\n" and matches "abc". PCRE2 $ (unless PCRE2_DOLLAR_ENDONLY) also matches before a final newline, like \Z. PCRE2 \z is the absolute end. JavaScript has no \z; with the u flag, \z is an invalid escape.
- Why does \A match the letter A instead of the start of the string?
- PCRE2 \A is a start-of-subject assertion (independent of multiline). In ECMAScript without the u flag, an unrecognized letter escape is an IdentityEscape, so \A is the character A. With u, new RegExp("\\A", "u") throws Invalid escape. The same IdentityEscape rule makes \z mean z. This page has no \A / \Z / \z / \G / \K anchors.
- Does Test replace text or run possessive / recursive PCRE syntax?
- No. Test is a button (not as-you-type) and does not call replace or replaceAll. a++ throws Nothing to repeat. (?>a+) and (?R) throw Invalid group. (?P<name>a) is invalid; named groups are (?<name>a) and appear on match.groups. Flags are gimsuy only — no d, v, or PCRE x / xx free-spacing.