Skip to main content

REAL — linear-time (ReDoS-safe) regex engine with an re-compatible API

Project description

REAL

Regular Expression Algorithmic Library — a header-only C++20 regex engine, constexpr from end to end, with an re-compatible Python binding.

  • Linear time, always. The engine is a Pike VM (Thompson NFA simulation): no backtracking, ReDoS-safe by construction.
  • Constexpr-friendly. Patterns known at compile time are parsed, compiled and matched at compile time.
  • Minimal memory. Static (sizes fixed at compile time, zero allocation), dynamic (storage sized exactly once at pattern compilation), or hybrid (compile-time pattern, runtime text, zero heap allocation).
  • Zero dependencies. One include.

Unsupported syntax is rejected with real::regex_error rather than silently diverging. Not yet: lookarounds, backreferences, atomic/possessive groups, Unicode property classes, Unicode case folding, pos/endpos.

Matching is linear in the input length: a Thompson NFA simulation (Pike VM) with marked states, so a pattern such as (a+)+b cannot trigger exponential backtracking. A literal prefilter and several whole-pattern fast paths (literals, fixed-width sequences, ./negated-class runs, alternations of straight-line branches) keep the constant factor low without leaving the linear-time guarantee.

make bench-python compares throughput against Python's re, and make bench-engines compares against std::regex, PCRE2 and RE2 in one C++ process (each engine's match counts are checked equal). Figures depend on the platform, pattern and input; reproduce them locally rather than trusting a number here. A measured baseline, with the exact machine and engine versions, is archived in BENCHMARKS.md.

Supported syntax

Syntax Meaning
abc literal bytes (UTF-8 patterns match their UTF-8 bytes)
\. \* \\ escaped metacharacter, matched literally
. any codepoint except \n
[abc] [a-z] [^abc] character class (members must be ASCII); [^…] matches any codepoint outside the set
\d \D \w \W \s \S digit / word / space classes (ASCII sets, like Python's re.ASCII)
\n \t \r \f \v \a \0 \xHH control and hex escapes
x* x+ x? quantifiers (greedy; append ? for lazy)
x{n} x{n,} x{,m} x{n,m} counted repetition (greedy or lazy; counts capped at 1000)
a|b alternation, leftmost branch preferred
(…) (?:…) capturing / non-capturing group
(?P<name>…) (?<name>…) named capturing group (Python and .NET styles)
^ $ line/text anchors (Python semantics: $ also matches before a final \n)
\A \Z strict text start / end
\b \B word boundary / non-boundary (ASCII word characters)
\< \> start / end of word (REAL extension, not in Python re)
(?imsx) prefix global flags: i case-insensitive (ASCII), m multiline, s dotall, x verbose (ignore unescaped whitespace and # comments outside classes) — also real::flags on the constructor

Unicode model: matching is UTF-8 byte-based, but every construct consumes whole codepoints (multi-byte sequences compile to byte-level alternatives), so match boundaries never split a character. Class members and the \d \w \s sets are ASCII by design; [^…], \D \W \S and . do match non-ASCII codepoints.

Divergence from Python: when a nullable loop body ends with an empty iteration — e.g. (a*)* on "aa" — Python captures that final empty iteration (''); REAL, like Perl/PCRE, keeps the last non-empty one ("aa"). Group 0 is identical either way.

C++ API

#include <real/real.hpp>

real::regex rx("hello");     // runtime pattern, storage sized exactly once
rx.match("hello world");     // anchored at the start   (Python re.match)
rx.fullmatch("hello");       // whole text              (Python re.fullmatch)
rx.search("say hello");      // leftmost match anywhere (Python re.search)

match/fullmatch/search return a real::match_result: matched(), operator bool, start(g), end(g), m[g] (a std::string_view into the searched text, which must outlive the result), and the same accessors by group name (m["year"], group_index).

for (auto& m : rx.find_iter(text)) {  }      // lazy, Python finditer rules
rx.find_all(text);                            // eager vector<match_result>
rx.replace(text, "$2:$1");                    // $&, $1…, ${name}, $$ — re.sub
rx.replace(text, "#", 2);                     // count limit
rx.split(text);                               // Python re.split, with groups

Empty matches follow Python's rules: they are yielded (even right after a non-empty match) and the scan then advances one whole codepoint. find_iter/find_all cannot be called on a temporary regex, and match/search/split cannot take a temporary std::string.

Three memory modes

// Static: pattern compiled at compile time into exactly-sized constexpr
// arrays; an invalid pattern is a *compile error*.
constexpr real::static_regex<"(\\d{4})-(\\d{2})"> date;
static_assert(date.search("on 2026-06-10")[1] == "2026");  // constexpr match

// Hybrid: compile-time pattern, runtime text — matching performs zero heap
// allocations (state lives on the stack).
date.search(runtime_text);

// Dynamic: everything at runtime; the program is sized exactly once at
// compilation, match state is per-run scratch.
real::regex rx2(user_pattern, real::flags::icase);

DFA over a rule set (opt-in)

#include <real/dfa.hpp>   // opt-in: not pulled in by <real/real.hpp>

const std::array patterns {real::regex("\\s+"), real::regex("[0-9]+"),
                           real::regex("[A-Za-z_][A-Za-z0-9_]*")};
real::dfa d(std::span<const real::regex>(patterns));   // built once, then immutable
auto hit = d.match("foo");   // -> {rule_index = 2, length = 3}; std::nullopt if none

real::dfa fuses a set of patterns into one capture-free, maximal-munch DFA: a single left-to-right pass recognizes the winning rule (longest match; ties to the earliest pattern; empty excluded) instead of running each pattern in turn — linear-time and ReDoS-safe like the engine, built at run time and then immutable. It is the accelerated rule dispatch a lexer wants (SciLex's dfa_modes is built on it). A pattern carrying a zero-width assertion no DFA can represent ($, \b, multiline ^/$) throws real::dfa_error; lazy and greedy accept the same language, so feed it longest-match-faithful rules.

The pure library is standard C++20 with no platform dependencies. real::real is the CMake target, available three ways — add_subdirectory, FetchContent, or an installed config package:

# After `cmake --install <build> --prefix <prefix>`:
find_package(real CONFIG REQUIRED)
target_link_libraries(app PRIVATE real::real)

Python binding

An re-compatible module backed by the C++ engine (CPython Limited API, one abi3 extension, zero dependencies):

import real

real.search(r"(?P<y>\d{4})-(?P<m>\d{2})", "on 2026-06-10").groupdict()
real.compile(r"\w+").findall(text)         # findall/finditer/split/sub/subn
real.sub(r"\s+", " ", text)                # templates: \1, \g<name>, callables
real.compile(rb"[^;]+").findall(raw)       # bytes patterns: raw-byte semantics

str matching is UTF-8 with character indices in start/end/span; bytes patterns get re's exact raw-byte semantics. Unsupported re features raise real.error at compile time. Build with make python && make python-test.

pip install real-regex installs one cp310-abi3 wheel per platform (CPython 3.10+; the self-contained sdist compiles where no wheel matches).

Embedding the C++ library through the Python package

The wheel also ships the C++ headers, so a project can compile against REAL located through its Python install — the convention used by petsc4py and slepc4py:

c++ -std=c++20 $(python -c "import real; print(real.get_include())") app.cpp

real.get_config() returns the version, the include directory and the required C++ standard.

Releasing. Run make release. It computes the next calendar version YYYY.M.PATCH — the patch resets each month, the first release of a month is .0 (PEP 440 drops leading zeros, so 2026.6.1, never 2026.06.001) — bumps it in pyproject.toml and python/real/__init__.py, then commits, tags and pushes. The tag drives release.yml, which checks the tag matches the version, builds abi3 wheels (cibuildwheel, Linux/macOS/Windows) and the sdist, and publishes to PyPI via Trusted Publishing (OIDC, no stored secret). The pushed tag is the single thing that triggers a publish.

Development

make help        # list all targets
make test        # build and run the test suite
make coverage    # line coverage report (LLVM)
make sanitize    # tests under ASan + UBSan
make lint        # clang-tidy
make misra       # MISRA C++:2023-oriented analysis
make fuzz        # libFuzzer robustness fuzzing (clang)
make doc         # API reference (Doxygen)
make format      # Uncrustify, in place
make format-check  # Uncrustify, dry-run; exits non-zero on diff

The API reference is published at https://reche23.github.io/real-regex/.

Select the compiler with make test CXX=g++-14. Every behaviour is tested at runtime and in constexpr (static_assert) under Clang and GCC; an equivalence suite checks the prefilter and fast paths never change results; a parity suite and a randomized differential fuzzer compare Python outputs against re.

Coverage bar. REAL holds a high line-coverage bar (mid-90s on include/), checked with make coverage. It deliberately does not adopt the 100%-on-all-four-dimensions (lines, functions, regions, and branches) gate used by the SciLang-stack libraries built on top of it: as the oldest and most complex engine here, its dual runtime/constexpr execution and Pike-VM branch structure leave some regions and branches impractical to drive to 100% without contrived tests. That lower-but-still-high bar is a deliberate, documented exception, not an oversight — REAL keeps its own gate (above) and its broad public CI.

CI exercises:

Platform Architecture Compiler
Linux x86-64 GCC, Clang
Linux AArch64 GCC
macOS Apple Silicon (arm64) Apple Clang
Windows x86-64 MSVC

IntelLLVM (icpx), x86-64 macOS and the BSDs share the Clang flag set and are supported by the build configuration but not exercised in CI.

License

MIT — Copyright (c) 2026 René Chenard

Author

René Chenard

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

real_regex-2026.6.10.tar.gz (85.4 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

real_regex-2026.6.10-cp310-abi3-win_amd64.whl (300.7 kB view details)

Uploaded CPython 3.10+Windows x86-64

real_regex-2026.6.10-cp310-abi3-win32.whl (297.5 kB view details)

Uploaded CPython 3.10+Windows x86

real_regex-2026.6.10-cp310-abi3-musllinux_1_2_x86_64.whl (1.6 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ x86-64

real_regex-2026.6.10-cp310-abi3-musllinux_1_2_aarch64.whl (1.5 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARM64

real_regex-2026.6.10-cp310-abi3-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (558.1 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.24+ x86-64manylinux: glibc 2.28+ x86-64

real_regex-2026.6.10-cp310-abi3-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl (549.2 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.24+ ARM64manylinux: glibc 2.28+ ARM64

real_regex-2026.6.10-cp310-abi3-macosx_11_0_x86_64.whl (113.5 kB view details)

Uploaded CPython 3.10+macOS 11.0+ x86-64

real_regex-2026.6.10-cp310-abi3-macosx_11_0_arm64.whl (109.6 kB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

File details

Details for the file real_regex-2026.6.10.tar.gz.

File metadata

  • Download URL: real_regex-2026.6.10.tar.gz
  • Upload date:
  • Size: 85.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for real_regex-2026.6.10.tar.gz
Algorithm Hash digest
SHA256 2cdbb285b296fe6c2e69ee4750f93d6dc9c536ae52006c9c9f61e6d97a9936d0
MD5 b229a81f3a8b639f5e49f8d05a85b575
BLAKE2b-256 92eb9c2e87f9364748baf12e77371e081ae156c8a49cbd31894b3e1697bb219d

See more details on using hashes here.

Provenance

The following attestation bundles were made for real_regex-2026.6.10.tar.gz:

Publisher: release.yml on RECHE23/real-regex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file real_regex-2026.6.10-cp310-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for real_regex-2026.6.10-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 32b8ae33981c88f1daac2fff3645dd82d87ade595a1f5aba8b936ecc58fe3875
MD5 8e4acacac27e188e3141f6b720c36119
BLAKE2b-256 c09a1417a1fc6e092d2f12a75b0308fe1372a716c562653e688136b8580d4d62

See more details on using hashes here.

Provenance

The following attestation bundles were made for real_regex-2026.6.10-cp310-abi3-win_amd64.whl:

Publisher: release.yml on RECHE23/real-regex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file real_regex-2026.6.10-cp310-abi3-win32.whl.

File metadata

  • Download URL: real_regex-2026.6.10-cp310-abi3-win32.whl
  • Upload date:
  • Size: 297.5 kB
  • Tags: CPython 3.10+, Windows x86
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for real_regex-2026.6.10-cp310-abi3-win32.whl
Algorithm Hash digest
SHA256 384d7e3d58297bce4e99adaaee27896565b59808f69bab8d0d71529ba133f295
MD5 9abd9ac255908dd33bc0d7024a6f6820
BLAKE2b-256 51e2caf3f95e603fdc4232c35446af0cb5b665b9f087a7c71228d259f06a9103

See more details on using hashes here.

Provenance

The following attestation bundles were made for real_regex-2026.6.10-cp310-abi3-win32.whl:

Publisher: release.yml on RECHE23/real-regex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file real_regex-2026.6.10-cp310-abi3-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for real_regex-2026.6.10-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 50e7742bee306059e584c6f74e0d5bfc96c4ccb360d60c4720e3cf17666f071e
MD5 f15d88414d533d0a81e48cf732882bb5
BLAKE2b-256 1639d2d988db462ecb1ffba6bffa7c439016cdbf03c5902bb2cfc2965e85031c

See more details on using hashes here.

Provenance

The following attestation bundles were made for real_regex-2026.6.10-cp310-abi3-musllinux_1_2_x86_64.whl:

Publisher: release.yml on RECHE23/real-regex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file real_regex-2026.6.10-cp310-abi3-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for real_regex-2026.6.10-cp310-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 ce4d3945b6ef7ba2847acafd397749820a47193241e4a55f0a3118c467953d5f
MD5 191ff8904e1d2729b71509a2c25d3c95
BLAKE2b-256 9aef9bca13ed0d4f76a5fe615df2f30638909d791c61522aa4672cdbc350fe33

See more details on using hashes here.

Provenance

The following attestation bundles were made for real_regex-2026.6.10-cp310-abi3-musllinux_1_2_aarch64.whl:

Publisher: release.yml on RECHE23/real-regex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file real_regex-2026.6.10-cp310-abi3-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for real_regex-2026.6.10-cp310-abi3-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 94f193514a84ce613390c1b586cd7f3fa580bc5659a38680203fefee6824cef3
MD5 ebf4a8daf857e58f4cb1b499868b4bf2
BLAKE2b-256 3aeea410ec23869a5db27d4346a485733915652e42580ec26a95c5a775c385f0

See more details on using hashes here.

Provenance

The following attestation bundles were made for real_regex-2026.6.10-cp310-abi3-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl:

Publisher: release.yml on RECHE23/real-regex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file real_regex-2026.6.10-cp310-abi3-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for real_regex-2026.6.10-cp310-abi3-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 7163b65ac73e0af027ab43e06a143baea76a21628b68567d426aabef29787455
MD5 f8ab6cc7474c2f9552dc5039c361b44a
BLAKE2b-256 a2a948a22621f50a0b5cc135a8f42bd69630c43cfc30f7afa9243cc831ccb439

See more details on using hashes here.

Provenance

The following attestation bundles were made for real_regex-2026.6.10-cp310-abi3-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl:

Publisher: release.yml on RECHE23/real-regex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file real_regex-2026.6.10-cp310-abi3-macosx_11_0_x86_64.whl.

File metadata

File hashes

Hashes for real_regex-2026.6.10-cp310-abi3-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 30bdeda5514cb9e9dd26e076975b6b94a91c57ccc01ea57eda70859e6fd5d9a3
MD5 c85819407487899ffb094ee8fa50747a
BLAKE2b-256 ec7206781757936cd08f7a22c66198e50aea879d23a99acfbdb55d8be96d1579

See more details on using hashes here.

Provenance

The following attestation bundles were made for real_regex-2026.6.10-cp310-abi3-macosx_11_0_x86_64.whl:

Publisher: release.yml on RECHE23/real-regex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file real_regex-2026.6.10-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for real_regex-2026.6.10-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 357e27de75cd1f8815d1041218adb081de7902922dafd82a84a9248245b283e5
MD5 fc557042718b72b25e56bf042e62dbe8
BLAKE2b-256 de4cc70bd9f332a786a04c36e7b1bab696addddd5b46cbac056d56e6dbeb823b

See more details on using hashes here.

Provenance

The following attestation bundles were made for real_regex-2026.6.10-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on RECHE23/real-regex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page