real

package module
v0.1.29 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: MIT Imports: 6 Imported by: 0

README

real (Go)

Go bindings to REAL, a linear-time (ReDoS-safe) regex engine with bounded lookarounds, over its C ABI (bindings/c/real_capi.h) via cgo.

v0.1 — cgo required, macOS-arm64 and linux-x86-64 only. Cross-compilation and Windows/MSVC are explicitly out of scope for this version.

The one thing to know before migrating from regexp

\w, \d, and \s are Unicode-aware by default here — regexp (RE2)'s are ASCII-only by default. \w+ on "café" matches all of it (café, 5 bytes: c, a, f, é=2 bytes) under this package; under regexp, \w+ matches only "caf" (é is not \w under RE2's default ASCII scope, so it is simply skipped, not part of any match). This is REAL following Python re's own default, not a bug on either side — see TestFlavorDivergence_WordShorthandIsUnicodeByDefault.

Why this exists (beyond another regexp)

REAL is a strict syntax superset of RE2/regexp on the shared core: every pattern regexp accepts, this package accepts identically (differential-tested against the stdlib, see Test_Differential_*). On top of that, REAL supports constructs RE2 rejects outright at compile time: bounded lookahead/lookbehind ((?=...), (?<=...), ...) and possessive quantifiers (a++), both in linear time (no backtracking, no ReDoS exposure — REAL's whole design point). A regexp user migrates without rewriting existing patterns, then gains access to constructs they could not express before.

API surface (v0.1)

This package regexp equivalent Notes
Compile / MustCompile same byte-oriented pattern/subject, no separate rune handling needed
QuoteMeta same delegates to regexp.QuoteMeta, not C++ compat::re2::QuoteMeta (that one escapes a larger set)
Match / MatchString (package) same compile + search; the handle is closed before return
(*Regexp) String same the source text, kept on the Go value (the C ABI has no getter); Close does not clear it
(*Regexp) Close (none — GC only) releases the C++ object explicitly; a finalizer is a safety net, not a substitute
(*Regexp) NumSubexp / SubexpNames same
(*Regexp) Match / MatchString same a search, not a full-string match — see FullMatch
(*Regexp) Find / FindString / FindIndex / FindStringIndex same leftmost match; Find is nil on no match
(*Regexp) FindAll / FindAllString / FindAllStringIndex same n as in regexp (0 → nil, <0 → all)
(*Regexp) FindAllIndex FindAllIndex(text, -1) byte offsets, [start,end), group 0 only
(*Regexp) FindSubmatch / FindStringSubmatch same groups as bytes/strings; unset group is nil / ""
(*Regexp) FindAllSubmatch / FindAllStringSubmatch same n as in regexp (0 → nil, <0 → all)
(*Regexp) FindSubmatchIndex / FindAllSubmatchIndex same every group's span; unset group is -1,-1
(*Regexp) Split same slices on matches; n as in regexp
(*Regexp) FullMatch no equivalent the whole ABI's real_match(REAL_MODE_FULLMATCH)regexp.MatchString is really a search
(*Regexp) ReplaceAll ReplaceAll template sigil differs: this package uses REAL/Python-style \1/\g<name>; regexp $1/$name/${name} is an error, not a silent literal; $$ is left as two dollars (regexp collapses it to one) — not translated
RegexSet (CompileSet, IsMatch, Matches, Size) no equivalent multi-pattern which-matched set — wraps real::regex_set (Stage-1 N-walks, or a fused single-pass DFA once enough members are DFA-eligible) directly, mirrors the Python binding's own native RegexSet
bounded lookaround, possessive quantifiers regexp.Compile rejects these patterns outright REAL-only; confirmed empirically in Test_BeyondRE2_*

Flags

REAL's native flag bitmask (bindings/c/real_capi.h's own documented numbering table) has no regexp-equivalent constants — regexp has no flags parameter at all (inline (?i)-style modifiers instead). Not yet exposed in this package's Go API (v0.1 always compiles with no flags); a future version would need its own named Go constants, not borrowed from either engine's convention.

Vendoring

vendor_include/ and real_capi.{h,cpp} in this directory are a generated, committed snapshot of ../../include/real and ../../bindings/c/real_capi.{h,cpp} — required (not just a convenience) because a module fetched via go get has no access to the rest of the monorepo. Never edit them directly:

make go-vendor         # regenerate from the source of truth
make go-check-vendor   # CI gate: fails if the committed snapshot has drifted

Versioning

This module is tagged independently of the engine's own CalVer releases (v2026.7.x), using Go's monorepo tag-prefix convention: bindings/go/vX.Y.Z. go get github.com/RECHE23/real-regex/bindings/go@vX.Y.Z resolves against that tag, not the engine's own tags — the two version sequences are unrelated by design.

Documentation

Overview

Package real provides Go bindings to the REAL regex engine (linear-time, ReDoS-safe) via cgo, over the C ABI at bindings/c/real_capi.h (v2026.7.39+, frozen-additive — vendored here, see vendor_include/ and the Makefile's `vendor`/`check-vendor` targets).

v0.1: a regexp-idiomatic subset (Compile/MustCompile/QuoteMeta/Match/MatchString (package and methods)/String/Find/FindString/FindIndex/FindAll/FindAllString/Split/FindAllIndex/ FindSubmatch/FindStringSubmatch/FindSubmatchIndex/ReplaceAll/RegexSet) plus REAL extensions regexp has no equivalent for at all (FullMatch, bounded lookarounds, possessive quantifiers). See README.md for the full method-to-C-ABI mapping and documented flavor divergences from Go's regexp (RE2) — most importantly: \w, \d, and \s are Unicode-aware by default here (Python re parity), where regexp's are ASCII-only by default. That is the single most likely surprise for a regexp migrator.

Cross-compilation and Windows/MSVC are explicitly out of scope for v0 (cgo + a vendored C++20 TU; proven on darwin/arm64 and linux/amd64 only).

Index

Examples

Constants

This section is empty.

Variables

View Source
var EngineVersion = C.GoString(C.real_go_engine_version())

EngineVersion is the vendored REAL C++ engine's version (e.g. "2026.7.40") — the engine version actually compiled into this build. Intentionally decoupled from this module's own semver (bindings/go/vX.Y.Z, Go's Semantic Import Versioning) — see README.md's Versioning section: a CalVer major (e.g. "2026") baked into the Go import path would force a breaking import-path change every year, which is exactly what the tag-prefix scheme avoids.

Functions

func Match added in v0.1.29

func Match(pattern string, b []byte) (bool, error)

Match reports whether b contains any match of pattern, like regexp.Match. The compiled handle is released before return.

func MatchString added in v0.1.29

func MatchString(pattern, s string) (bool, error)

MatchString reports whether s contains any match of pattern, like regexp.MatchString. It is a search, not a full-string match — see FullMatch.

func QuoteMeta added in v0.1.29

func QuoteMeta(s string) string

QuoteMeta escapes regexp metacharacters in s so the result matches it literally, like regexp.QuoteMeta. Delegates to regexp.QuoteMeta — not real::compat::re2::QuoteMeta, which escapes a larger set and would return a different string (`user-id` vs `user\-id`) for the same input.

Types

type RegexSet

type RegexSet struct {
	// contains filtered or unexported fields
}

RegexSet is a multi-pattern which-matched set (real::regex_set — Stage-1 N-walks, or a fused Stage-2 single-pass DFA once enough members are DFA-eligible; see regex_set.hpp). Go's own regexp package has no equivalent — there is nothing to map this onto beyond REAL's own C++/Python bindings, which have the identical shape. A value copy of RegexSet still shares the C pointer — unlike regexp.Regexp, *s is not a safe clone. noCopy makes that copy a go vet -copylocks diagnostic; the compiler will still accept it.

func CompileSet

func CompileSet(patterns []string) (*RegexSet, error)

CompileSet compiles patterns into a set. Fails (with an error, no partial/silent skip) if any pattern is invalid. Bitset order in IsMatch/Matches is construction order.

func (*RegexSet) Close

func (s *RegexSet) Close() error

Close releases the compiled set. Idempotent.

func (*RegexSet) IsMatch

func (s *RegexSet) IsMatch(text []byte) bool

IsMatch reports whether any pattern matches text at least once (stops at the first hit).

func (*RegexSet) Matches

func (s *RegexSet) Matches(text []byte) []bool

Matches returns which patterns match text at least once, in construction order.

func (*RegexSet) Size

func (s *RegexSet) Size() int

Size returns the number of patterns in the set.

type Regexp

type Regexp struct {
	// contains filtered or unexported fields
}

Regexp wraps a compiled real_regex handle. Safe for concurrent use by multiple goroutines (the C ABI's own thread-safety contract: a const handle only reads). Close releases the underlying C++ object; a runtime.SetFinalizer is a safety net, not a substitute for it.

expr is the source text, kept here because the C ABI has no pattern getter. String() reads this field; it is not recovered from the handle. A value copy of Regexp still shares the C pointer — unlike regexp.Regexp, *r is not a safe clone. noCopy makes that copy a go vet -copylocks diagnostic; the compiler will still accept it.

func Compile

func Compile(pattern string) (*Regexp, error)

Compile compiles pattern. Mirrors regexp.Compile's signature and error contract.

func MustCompile

func MustCompile(pattern string) *Regexp

MustCompile is like Compile but panics on error, mirroring regexp.MustCompile.

Example

ExampleMustCompile is the landing page's Go quickstart: compile a pattern and MatchString, the first method a regexp tutorial writes.

package main

import (
	"fmt"

	real "github.com/RECHE23/real-regex/bindings/go"
)

func main() {
	// Flush-left (not tab-indented like the rest of this function): the marked region below is
	// reproduced byte-for-byte by the landing page's Go tab (see the package doc comment above)
	// — Go does not care about statement indentation, so this stays copy-pasteable as-is.
	// [quickstart-body]
	re := real.MustCompile(`\d+`)
	fmt.Print(re.MatchString("x42"))
	// [/quickstart-body]

}
Output:
true

func (*Regexp) Close

func (r *Regexp) Close() error

Close releases the compiled pattern. Idempotent.

Post-Close contract: the handle is nilled; subsequent method calls must not crash and return zero-values (NumSubexp → 0; SubexpNames → empty slice; Find*/FindAll*/FindSubmatchIndex → nil or ""; Match/MatchString/FullMatch → false; ReplaceAll → error). Split on a closed handle is the no-match path (the whole string). String is the exception: the source text lives on this value, not on the handle, so Close leaves it. Prefer not to use a closed Regexp for matching — the guarantee is crash-freedom and stable sentinels, not a second valid lifetime.

func (*Regexp) Find added in v0.1.28

func (r *Regexp) Find(b []byte) []byte

Find returns the leftmost match in b, or nil if there is none — like regexp.Regexp.Find. An empty match is a non-nil empty slice.

func (*Regexp) FindAll added in v0.1.28

func (r *Regexp) FindAll(b []byte, n int) [][]byte

FindAll returns successive matches in b, like regexp.Regexp.FindAll. n is the cap (0 → nil, <0 → all).

func (*Regexp) FindAllIndex

func (r *Regexp) FindAllIndex(text []byte) [][]int

FindAllIndex returns the [start,end) byte-offset pairs of every non-overlapping match in text, in order — same shape as regexp.Regexp.FindAllIndex(text, -1) (whole match only, group 0; see FindAllSubmatchIndex for every group). Byte offsets throughout — Go's own regexp package is byte-oriented too, so there is no char/byte translation layer here at all, unlike the Python binding.

func (*Regexp) FindAllString added in v0.1.28

func (r *Regexp) FindAllString(s string, n int) []string

FindAllString is FindAll on a string, like regexp.Regexp.FindAllString.

func (*Regexp) FindAllStringIndex added in v0.1.28

func (r *Regexp) FindAllStringIndex(s string, n int) [][]int

FindAllStringIndex is FindAllIndex with regexp's n, on a string.

func (*Regexp) FindAllStringSubmatch added in v0.1.28

func (r *Regexp) FindAllStringSubmatch(s string, n int) [][]string

FindAllStringSubmatch is FindAllSubmatch on a string, like regexp.Regexp.FindAllStringSubmatch.

func (*Regexp) FindAllSubmatch added in v0.1.28

func (r *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte

FindAllSubmatch returns successive matches and their groups, like regexp.Regexp.FindAllSubmatch. n is the cap (0 → nil, <0 → all).

func (*Regexp) FindAllSubmatchIndex

func (r *Regexp) FindAllSubmatchIndex(text []byte) [][]int

FindAllSubmatchIndex returns every non-overlapping match's full span plus every group's span, in order — the same shape as regexp.Regexp.FindAllSubmatchIndex(text, -1).

func (*Regexp) FindIndex added in v0.1.28

func (r *Regexp) FindIndex(b []byte) []int

FindIndex returns the [start,end) of the leftmost match in b, or nil, like regexp.Regexp.FindIndex.

func (*Regexp) FindString added in v0.1.28

func (r *Regexp) FindString(s string) string

FindString returns the leftmost match in s, or "" if there is none — like regexp.Regexp.FindString. An empty match and no match are indistinguishable.

func (*Regexp) FindStringIndex added in v0.1.28

func (r *Regexp) FindStringIndex(s string) []int

FindStringIndex is FindIndex on a string.

func (*Regexp) FindStringSubmatch added in v0.1.28

func (r *Regexp) FindStringSubmatch(s string) []string

FindStringSubmatch is FindSubmatch on a string, like regexp.Regexp.FindStringSubmatch. No match is nil, not an empty slice — that is the FAIL a tutorial hits first.

func (*Regexp) FindSubmatch added in v0.1.28

func (r *Regexp) FindSubmatch(b []byte) [][]byte

FindSubmatch returns the leftmost match and its groups as sub-slices of b, or nil — like regexp.Regexp.FindSubmatch. An unset group is nil; an empty participating group is a non-nil empty slice.

func (*Regexp) FindSubmatchIndex

func (r *Regexp) FindSubmatchIndex(text []byte) []int

FindSubmatchIndex returns the leftmost match's full span plus every group's span (2*(NumSubexp()+1) ints: start0,end0,start1,end1,...), or nil if there is no match — the same shape as regexp.Regexp.FindSubmatchIndex. A group that did not participate is -1,-1.

func (*Regexp) FullMatch

func (r *Regexp) FullMatch(text []byte) bool

FullMatch reports whether the ENTIRE text matches — a REAL extension over Go's standard regexp package, which has no fullmatch mode at all (only MatchString, which is really a search: `re.MatchString("x")` on pattern "ab" against "xaby" returns true). Exercises real_match's REAL_MODE_FULLMATCH.

func (*Regexp) Match added in v0.1.28

func (r *Regexp) Match(b []byte) bool

Match reports whether b contains any match of the expression, like regexp.Regexp.Match.

func (*Regexp) MatchString added in v0.1.28

func (r *Regexp) MatchString(s string) bool

MatchString reports whether s contains any match of the expression, like regexp.Regexp.MatchString. It is a search, not a full-string match — see FullMatch.

func (*Regexp) NumSubexp

func (r *Regexp) NumSubexp() int

NumSubexp returns the number of capturing groups (excluding group 0), like regexp.Regexp.NumSubexp. After Close it returns 0 (not −1).

func (*Regexp) ReplaceAll

func (r *Regexp) ReplaceAll(text, repl []byte) ([]byte, error)

ReplaceAll applies repl (a REAL/re-style template: \1, \g<name>, ...) to every non-overlapping match, mirroring regexp.Regexp.ReplaceAll's shape — though Go's stdlib uses $name-style templates where REAL uses \1-style. A `$1` / `$name` / `${name}` template is an error, not a silent no-op (see README.md). Two-call convention: size, then fill.

func (*Regexp) Split added in v0.1.28

func (r *Regexp) Split(s string, n int) []string

Split slices s at matches of the expression, like regexp.Regexp.Split. n == 0 returns nil; n < 0 returns all substrings; n > 0 returns at most n.

func (*Regexp) String added in v0.1.26

func (r *Regexp) String() string

String returns the source text used to compile the expression, like regexp.Regexp.String. The text lives on this value; Close does not clear it.

func (*Regexp) SubexpNames

func (r *Regexp) SubexpNames() []string

SubexpNames returns each group's name in group-index order; index 0 (the whole match) and any unnamed group are "" — the same shape as regexp.Regexp.SubexpNames. Names are fetched with the C ABI two-call protocol (length query, then exact buffer) so long names are never truncated or read out of bounds.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL