The PCRE2 Matcher
PCRE2 is ripgrep’s optional regex backend for patterns that need features beyond the default Rust regex engine, especially look-around and backreferences. It is exposed through the grep_pcre2 crate only when the pcre2 feature is enabled.
The backend adapts PCRE2 matching to ripgrep’s common matcher interface, including ordinary matches, iteration, capture locations, and named-capture lookup. Runtime engine selection can be explicit or automatic, but the supplied excerpts do not show the Cargo or linker implementation for the PCRE2 C library.
Sources: crates/core/flags/defs.rs:5730-5743, crates/pcre2/src/matcher.rs:302-305, crates/pcre2/src/matcher.rs:319-329, crates/pcre2/src/matcher.rs:339-341
Core concepts
PCRE2 backend
The PCRE2 backend is the opt-in engine selected with --pcre2 or --engine=pcre2, primarily to support look-around and backreferences.
Sources: crates/core/flags/defs.rs:5730-5739
Default engine
The default engine is Rust’s regex implementation, technically backed by regex-automata; it is the first engine attempted by automatic selection.
Sources: crates/core/flags/lowargs.rs:541-551
Automatic engine choice
Automatic selection means ripgrep first tries the default engine and switches to PCRE2 when the pattern does not compile there, if PCRE2 is available.
Sources: crates/core/flags/lowargs.rs:549-554
Matcher adapter
The matcher adapter is RegexMatcher, which stores a compiled Regex and a map from capture names to capture indexes.
Sources: crates/pcre2/src/matcher.rs:302-305
Optional availability
PCRE2 availability is a build-time property represented by the pcre2 feature; without that feature, requesting PCRE2 returns an error.
Sources: crates/core/flags/hiargs.rs:415-423
How ripgrep chooses an engine
The engine modes form a small policy surface: the default mode stays with Rust’s engine, Auto retries with PCRE2 after a default-engine compilation failure, and PCRE2 requests the optional backend directly.
| Mode | User-facing choice | Behavior |
|---|---|---|
Default | --engine=default | Uses Rust’s default regex engine. |
Auto | --engine=auto or --auto-hybrid-regex | Tries the default engine, then PCRE2 if available and needed. |
PCRE2 | --engine=pcre2 or --pcre2 | Uses PCRE2 if the build includes it. |
The automatic choice is made from whether the pattern compiles, not from observed behavior while searching files.
The choice applies to every regex supplied to ripgrep, including patterns provided through multiple --regexp or --file options.
The workflow shows the two engine attempts described by EngineChoice; the selected result is represented by PatternMatcher in the flag-resolution path.
Sources: crates/core/flags/lowargs.rs:541-555, crates/core/flags/defs.rs:341-345
What the PCRE2 matcher adds
PCRE2 enables look-around and backreferences that the default engine does not support in the relevant selection flow.
The backend also exposes configuration for case handling, line behavior, word and whole-line matching, Unicode modes, and JIT operation. These options configure the builder rather than changing the engine-selection policy.
| Builder option | Plain-language effect |
|---|---|
caseless | Enables case-insensitive matching. |
case_smart | Enables smart case selection based on uppercase literals. |
multi_line | Configures multiline matching. |
crlf | Configures CRLF line-terminator handling. |
word | Requests word-boundary matching. |
whole_line | Requests whole-line matching. |
ucp | Configures Unicode character-property behavior. |
utf | Configures UTF matching. |
jit | Enables JIT configuration. |
jit_if_available | Enables JIT when available. |
build_many combines multiple patterns with alternation, optionally escapes them as fixed strings, applies whole-line or word wrappers, and then compiles the resulting expression.
let mut singlepat = if patterns.is_empty() {
r"[^\S\s]".to_string()
} else {
pats.join("|")
};
if self.case_smart && !has_uppercase_literal(&singlepat) {
builder.caseless(true);
}
if self.whole_line {
singlepat = format!(r"(?m:^)(?:{})(?m:$)", singlepat);
} else if self.word {
singlepat = format!(r"(?<!\w)(?:{})(?!\w)", singlepat);
}This is the important normalization step: user-facing matching modes become part of the compiled PCRE2 pattern before compilation.
case_smart enables caseless matching only when has_uppercase_literal finds no uppercase literal, while escaped characters are skipped during that scan.
The tests demonstrate that word(true) matches -2 in abc -2 foo, while an explicit \b-2\b pattern does not; they also show that crlf(true) changes $ handling at a CRLF boundary.
Sources: crates/pcre2/src/matcher.rs:94-97, crates/pcre2/src/matcher.rs:148-151, crates/pcre2/src/matcher.rs:160-163, crates/pcre2/src/matcher.rs:191-194, crates/pcre2/src/matcher.rs:205-208, crates/pcre2/src/matcher.rs:225-228, crates/pcre2/src/matcher.rs:258-261, crates/pcre2/src/matcher.rs:273-276, crates/pcre2/src/matcher.rs:45-85, crates/pcre2/src/matcher.rs:49-76, crates/pcre2/src/matcher.rs:422-432, crates/pcre2/src/matcher.rs:443-451, crates/pcre2/src/matcher.rs:455-477
How matching reaches the common interface
RegexMatcherBuilder::new starts with a PCRE2 RegexBuilder and default values for the adapter’s extra flags. A single pattern then flows through build into build_many.
Evidence
- callercrates/pcre2/src/matcher.rs:37
- matcher-buildercrates/pcre2/src/matcher.rs:12
- matcher-buildercrates/pcre2/src/matcher.rs:37
- many-buildercrates/pcre2/src/matcher.rs:45
- compiled-matchercrates/pcre2/src/matcher.rs:45
- compiled-matchercrates/pcre2/src/matcher.rs:302
The resulting RegexMatcher implements the matcher operations used by ripgrep: find_at calls the compiled regex, try_find_iter streams matches to a callback, and captures_at fills reusable capture locations.
Named captures are resolved through the names map, while RegexCaptures exposes capture count and indexed locations through len and get.
Compilation failures are represented as ErrorKind::Regex, and the underlying error text is preserved for formatting.
Sources: crates/pcre2/src/matcher.rs:22-30, crates/pcre2/src/matcher.rs:37-39, crates/pcre2/src/matcher.rs:319-329, crates/pcre2/src/matcher.rs:343-360, crates/pcre2/src/matcher.rs:362-373, crates/pcre2/src/matcher.rs:302-305, crates/pcre2/src/matcher.rs:339-341, crates/pcre2/src/matcher.rs:391-394, crates/pcre2/src/matcher.rs:397-399, crates/pcre2/src/matcher.rs:401-403, crates/pcre2/src/error.rs:25-32, crates/pcre2/src/error.rs:43-47
How it connects
The high-level grep facade re-exports grep_pcre2 as pcre2 only when the pcre2 feature is enabled, alongside the matcher, regex, searcher, printer, and CLI crates.
For workspace placement and the facade’s role, see Repository and Crate Map. For the CLI’s resolved engine flags, see CLI Entry and Flag Parsing. For the shared matcher contract implemented by RegexMatcher, see The Matcher Trait and Internal Iteration.
The supplied excerpts establish optional feature gating and matcher behavior, but not how the PCRE2 C library is linked or when a build falls back to compiling it from source. Those build and static-linking mechanics belong with Building and Installing.
Sources: crates/grep/src/lib.rs:15-21, crates/core/flags/hiargs.rs:415-423
Key takeaways
- PCRE2 is optional and adds look-around and backreference support.
Autotries the default engine first and switches to PCRE2 when compilation fails, if PCRE2 is available.RegexMatcherBuildernormalizes patterns and matching modes before compilingRegexMatcher.- The adapter exposes ordinary matches, iteration, captures, and named-capture lookup.
- Linking and source-build details are not shown in this source pack; consult Building and Installing.
Sources: crates/core/flags/defs.rs:5730-5743, crates/core/flags/lowargs.rs:549-554, crates/pcre2/src/matcher.rs:12-18, crates/pcre2/src/matcher.rs:45-85, crates/pcre2/src/matcher.rs:302-305, crates/pcre2/src/matcher.rs:319-329, crates/pcre2/src/matcher.rs:339-341, crates/pcre2/src/matcher.rs:343-360, crates/pcre2/src/matcher.rs:362-373