RE2 compatibility layer#

RE2 compatibility layer, strict — real::compat::re2, header-only, zero-dep. The parts of the RE2 surface listed below, on REAL’s linear engine; every accepted pattern is guaranteed linear, and a construct this layer cannot honor is a clean ok() == false — RE2’s own no-exception contract, never a silent backtrack. RE2 itself is a test-time oracle only, never linked.

Adopt / swap#

#include <real/compat/re2/re2.hpp>
using real::compat::re2::RE2;

std::string user, host;
RE2::PartialMatch("info@example.com", R"((\w+)@(\w+))", &user, &host);
// host == "example" — group 2, via RE2's typed Arg extraction

API offered#

  • Statics — FullMatch / PartialMatch / Consume / FindAndConsume (typed RE2::Arg extraction: one &int, &std::string, … per group), Replace / GlobalReplace, QuoteMeta.

  • Instance — ok() / error() / error_code() / pattern() / NumberOfCapturingGroups().

  • RE2::Options — set_longest_match is honored by every unanchored search, and set_max_mem bounds the compiled program as RE2’s does (a pattern past two thirds of it fails with ErrorPatternTooLarge, counted in bytes of REAL’s program); log_errors() reads true by default, as RE2’s, and an explicit set_log_errors(true) makes a failed compile write RE2’s stderr message (Error parsing '<pattern>': <error>), with ok() / error() unchanged – nothing is written unless it was asked for; options with no REAL-side equivalent are rejected at construction, never silently ignored (details below).

  • RE2::Set — Add / Compile / Match, multi-pattern which-matched on real::regex_set.

  • Spelled as RE2 spells them — the canned option sets (RE2 re(p, RE2::Quiet); Latin1 and POSIX are rejected at construction), RE2::UNANCHORED / ANCHOR_START / ANCHOR_BOTH, RE2::NoError / ErrorPatternTooLarge, and LazyRE2 (static LazyRE2 re = {"a+"};, compiled once on first use, from any thread).

  • Not provided — the low-level RE2::Match, NamedCapturingGroups / CapturingGroupNames, Extract / Rewrite / CheckRewriteString / MaxSubmatch, the *N statics, CRadix / Hex / Octal, error_arg(), the Program* diagnostics, and RE2’s fine-grained error codes (this layer reports ErrorSyntax / ErrorUnsupported / ErrorPatternTooLarge).

One include: <real/compat/re2/re2.hpp>. Object-level reference: RE2 compatibility.

Differences & limitations#

Distilled from the layer’s own contract — the exhaustive version is the file comment of re2.hpp:

  • REAL is a near-total syntax superset — everything RE2 compiles, plus bounded lookarounds and possessive quantifiers (RE2 rejects both outright) and a wider \p{…} set (sc=/scx= and the UCD binary properties RE2’s grammar lacks).

  • RE2’s reading of the syntax — \w, \d, \s are ASCII, \b/\B read ASCII word characters, [[:alpha:]] is a POSIX class, $ without (?m) is the end of the text, and GlobalReplace steps past an empty match as RE2 does. Checked against libre2 over every code point. One orbit differs: under (?i), İ/ı (U+0130, U+0131) fold with i/I, as in Python’s re, where RE2 keeps them apart.

  • Two places REAL is deliberately stricter — duplicate capture names ((?P<n>…)(?P<n>…), ambiguous match-by-name) and surrogate code points in \x{…}/\u/\U/\N. Both a clean ok() == false; both principled divergences, and the only two entries in this layer’s differential-fuzz known-gap ledger.

  • No fallback — RE2 is not a runtime dependency, so there is nothing to delegate to: an unsupported pattern or option rejects immediately with error() explaining why (unlike the std::regex layer’s opt-in fallback).

  • Scope is RE2’s default mode — UTF-8, non-POSIX. posix_syntax, Latin-1, never_nl, never_capture reject at construction; perl_classes / word_boundary / one_line are inert exactly as in real RE2 outside POSIX mode.

  • \C is accepted — one byte, possibly mid-codepoint, safe on this byte-offset API.

Comparison#

Single-pattern numbers against RE2, PCRE2-JIT and std::regex live in the performance ledger; the reading is Performance. RE2 cannot compile the lookaround row at all.

Multi-pattern is covered too: RE2::Set compiles into real::regex_set, a shipped hybrid — per-pattern walks below a calibrated set size (competitive there, sometimes the fastest route), and above it the DFA-eligible members switch automatically onto a fused single-pass scan that stays flat as the set grows. Measured against Google’s RE2::Set on the same host it is near parity — ahead on sparse corpora, a hair behind on dense ones. The residual cuts both ways: lookaround patterns stay on per-pattern walks and degrade at large N — but RE2::Set cannot compile a lookaround at all, and REAL leads on multi-pattern extraction. Numbers in Performance.