REAL
Regular Expression Algorithmic Library — constexpr C++20 regex
Loading...
Searching...
No Matches
real::compat::detail Namespace Reference

Backend routing and format expansion for the compat layer. Not a stable API. More...

Functions

bool grammar_forces_std (regex_constants::syntax_option_type f) noexcept
 Options that still force the std backend AFTER the POSIX translation attempt has declined.
 
bool pattern_forces_std (std::string_view p) noexcept
 Pattern text that real accepts but matches differently from libstdc++ — a both-accept silent divergence, so it must route to std up front (real's accept hides it otherwise).
 
bool format_forces_std (std::string_view fmt) noexcept
 Replacement format text that must route to std: $0. $0 is platform-variant (libstdc++ = the whole match, strict-ECMAScript/MSVC = a literal), so real cannot pick one without risking a silent divergence — route to std, which is authoritative for its own platform. $$ is skipped (an escaped literal $).
 
std::string posix_class_ranges (std::string_view name)
 A POSIX bracket-class name to its ASCII (C-locale) range content, appended inside a [...] during ERE translation. Empty for an unknown name (the caller then falls back to std).
 
bool translate_bracket (std::string_view p, std::size_t &i, std::string &out)
 Translates a POSIX bracket expression [...] — identical syntax in BRE and ERE, so shared by both translators. POSIX classes ([[:alpha:]]) become ASCII ranges; other members pass through. i must point at the opening [; on success it advances past the ] and appends the class to out. Returns false (the caller then declines to std) on an unterminated class or an unknown / collating [[:foo:]] / [.x.] / [=x=].
 
bool append_awk_escape (std::string_view p, std::size_t &i, std::string &out)
 Appends the REAL translation of an awk C-escape at i (which points at the backslash), advancing i; returns false to decline (→ std). awk's escapes beyond ERE: \b is BACKSPACE (0x08) — not a word boundary, the inverse of the ERE decline — \a=BEL, \n\t\r\f\v the usual controls, \/ and \" literals, and a 1-to-3-digit octal \ddd (both std libraries agree; an overflow > 0377 declines). Emitted as \xHH so REAL matches the exact byte.
 
bool has_empty_alternation_branch (std::string_view p)
 Whether p has an empty alternation branch — a | with nothing on one side: (|, |), ||, or a | at the pattern start or end. Two reasons this matters: (a) std::regex rejects these in the POSIX grammars, so translating one would make compat over-accept vs std; (b) REAL's leftmost-first star semantics over an empty-first branch ((|a)*) diverge from re / std on repetition. Conservative — a | merely adjacent to a group boundary or the pattern edge counts, an escaped \| or a | inside a class does not — because a false positive only costs linear coverage (safe) while a false negative is a silent divergence (forbidden).
 
std::optional< std::string > translate_ere (std::string_view p, bool awk=false)
 Translates a POSIX extended (ERE) — or, with awk, an awk — pattern to an equivalent REAL pattern, or nullopt when it uses a construct the two grammars read differently (an ECMAScript shorthand \d\w\s — undefined/literal in ERE; an ambiguous {; an unknown/collating [[:…:]]; an empty alternation branch, which std rejects — see has_empty_alternation_branch). awk adds the C-escapes (see append_awk_escape). POSIX classes become ASCII ranges (C locale); the common productions pass through, since REAL reads them like ERE. Validated by a bounds differential.
 
std::optional< std::string > translate_bre (std::string_view p)
 Translates a POSIX basic (BRE) pattern to an equivalent REAL pattern, or nullopt. BRE differs from ERE: \( \) group and \{n\} quantify, while bare ( ) { } | + ? are LITERALS (escaped for REAL); * at an expression start and ^/$ off the ends are literals too. Declines (→ std) on a backreference \1-\9 (std's residual value), an ECMAScript-ism, a non-strict \{, an unknown / collating class, or a POSIX-undefined corner (^/$/* at a subexpression boundary).
 
template<typename LineFn >
std::optional< std::string > translate_newline_alt (std::string_view p, LineFn translate_line)
 grep / egrep: a newline in the pattern is a top-level alternation of the lines (grep = BRE lines, egrep = ERE lines).
 
std::optional< std::string > translate_posix (std::string_view p, regex_constants::syntax_option_type f)
 Dispatches a single POSIX grammar to its translator, or nullopt (→ std). Exactly one grammar bit must be set, and neither collate nor nosubs (which force std). extended → ERE, basic → BRE, awk → ERE + C-escapes, grep → BRE lines joined by |, egrep → ERE lines joined by |.
 
real::flags to_real (regex_constants::syntax_option_type f) noexcept
 Maps compat options to real::flags (always with bytes|ecma for std-char alignment).
 
std::regex_constants::syntax_option_type to_std (regex_constants::syntax_option_type f) noexcept
 Maps compat options to std::regex syntax flags (the fallback path).
 
bool real_honors (regex_constants::match_flag_type mf) noexcept
 Whether real can honor the requested match flags, so the operation may stay on it.
 
bool replace_stays_real (regex_constants::match_flag_type f) noexcept
 Whether regex_replace can run its substitution on real. The real expander honors only format_first_only / format_no_copy (plus the match_any hint); ANY other bit — a constraining match flag (not_bol, continuous, …) OR format_sed (POSIX syntax) — would be silently ignored by the ECMAScript expander, so the whole substitution routes to std. (This subsumes the explicit format_sed screen; $0 stays content-based.)
 
std::regex_constants::match_flag_type to_std_match (regex_constants::match_flag_type f) noexcept
 Maps compat match/format flags to std::regex_constants — exhaustively.
 
template<typename BidirIt , typename CharT , typename Traits >
bool run (BidirIt first, BidirIt last, match_results< BidirIt > &m, const basic_regex< CharT, Traits > &re, bool anchored, regex_constants::match_flag_type mf)
 Runs the active backend over [first, last) and fills m. anchored selects whole-sequence match (regex_match) vs leftmost search (regex_search). A constraining match flag (see real_honors) routes to std even for a real-backed pattern.
 
template<typename BidirIt , typename CharT , typename Traits >
bool run_nocapture (BidirIt first, BidirIt last, const basic_regex< CharT, Traits > &re, bool anchored, regex_constants::match_flag_type mf)
 Backend run without capturing (no match_results to fill).
 
template<typename RealMatch >
void expand_format (std::string &out, const RealMatch &m, std::string_view fmt, std::string_view text, std::size_t prefix_start)
 Appends one match's ECMAScript-expanded replacement.
 

Variables

template<typename CharT , typename Traits >
constexpr bool real_eligible
 Whether real is even eligible for this basic_regex instantiation. real runs only the char path with default traits; wchar_t/char8_t/… and custom traits are always std. This is a compile-time gate: it must compile real's char-only code (the byte string_view, fill_from_real) out for other CharT, not merely skip it at runtime.
 

Detailed Description

Backend routing and format expansion for the compat layer. Not a stable API.

Function Documentation

◆ append_awk_escape()

bool real::compat::detail::append_awk_escape ( std::string_view  p,
std::size_t &  i,
std::string &  out 
)
inline

Appends the REAL translation of an awk C-escape at i (which points at the backslash), advancing i; returns false to decline (→ std). awk's escapes beyond ERE: \b is BACKSPACE (0x08) — not a word boundary, the inverse of the ERE decline — \a=BEL, \n\t\r\f\v the usual controls, \/ and \" literals, and a 1-to-3-digit octal \ddd (both std libraries agree; an overflow > 0377 declines). Emitted as \xHH so REAL matches the exact byte.

Parameters
[in]pThe pattern being translated.
[in,out]iCursor at the backslash; advanced past the escape on success.
[in,out]outDestination the translated escape is appended to.
Returns
false if the escape is not one awk defines.

◆ expand_format()

template<typename RealMatch >
void real::compat::detail::expand_format ( std::string &  out,
const RealMatch &  m,
std::string_view  fmt,
std::string_view  text,
std::size_t  prefix_start 
)
private

Appends one match's ECMAScript-expanded replacement.

The ECMAScript replacement references: dollar-dollar to a literal $, dollar-ampersand to the whole match, dollar-backtick to the prefix, dollar-quote to the suffix, and $N/$NN to a group. Offsets come from the match's group spans relative to text. The prefix is the unmatched text since the previous match ([prefix_start, start)) and the suffix runs to the end — matching std::regex_replace (which uses match_results prefix/suffix), the parity oracle. A $N/$NN for a non-participating group inserts nothing; an invalid $ is literal.

Parameters
[in,out]outDestination the expansion is appended to.
[in]mThe match whose groups $N refers to.
[in]fmtThe replacement format string.
[in]textThe full subject the match's offsets index into.
[in]prefix_startWhere the unmatched prefix begins — the previous match's end.

◆ format_forces_std()

bool real::compat::detail::format_forces_std ( std::string_view  fmt)
inlinenoexcept

Replacement format text that must route to std: $0. $0 is platform-variant (libstdc++ = the whole match, strict-ECMAScript/MSVC = a literal), so real cannot pick one without risking a silent divergence — route to std, which is authoritative for its own platform. $$ is skipped (an escaped literal $).

Parameters
[in]fmtThe replacement format.
Returns
true if it holds a construct whose meaning is platform-variant, so the replace routes to std.

◆ grammar_forces_std()

bool real::compat::detail::grammar_forces_std ( regex_constants::syntax_option_type  f)
inlinenoexcept

Options that still force the std backend AFTER the POSIX translation attempt has declined.

Order matters, and this predicate is only half the story on its own: the constructor first tries translate_posix, which routes any of the five POSIX grammars onto REAL when the pattern translates. Only a pattern that translation refused reaches this filter, where the grammar bits then do force std. collate and nosubs force it unconditionally — real implements default-traits ECMAScript and reports every group, while std answers nosubs by exposing only group 0, so routing it avoids a structural both-accept divergence.

Parameters
[in]fThe syntax options requested.
Returns
true if, translation having declined, these options cannot be served by REAL.

◆ has_empty_alternation_branch()

bool real::compat::detail::has_empty_alternation_branch ( std::string_view  p)
inline

Whether p has an empty alternation branch — a | with nothing on one side: (|, |), ||, or a | at the pattern start or end. Two reasons this matters: (a) std::regex rejects these in the POSIX grammars, so translating one would make compat over-accept vs std; (b) REAL's leftmost-first star semantics over an empty-first branch ((|a)*) diverge from re / std on repetition. Conservative — a | merely adjacent to a group boundary or the pattern edge counts, an escaped \| or a | inside a class does not — because a false positive only costs linear coverage (safe) while a false negative is a silent divergence (forbidden).

Parameters
[in]pThe pattern text.
Returns
true if some alternation branch is empty — which POSIX grammars reject, so REAL declines to stay equivalent to std.

◆ pattern_forces_std()

bool real::compat::detail::pattern_forces_std ( std::string_view  p)
inlinenoexcept

Pattern text that real accepts but matches differently from libstdc++ — a both-accept silent divergence, so it must route to std up front (real's accept hides it otherwise).

\0 followed by a digit: real reads it as a legacy octal escape (Annex B, e.g. \012 → newline) while libstdc++ reads \0 as NUL then a literal digit. Strict ECMAScript makes \0+ digit a syntax error (no valid production), so neither is "the" spec answer — routing to std keeps compat ≡ its secondary oracle and the contract that no divergence is silent. (\1-\9 already route to std through real's backreference rejection.)

\C (RE2's raw-byte escape): real accepts it here because real::compat always compiles its internal engine with flags::bytes (for byte-per-std::regex-char alignment, see the file header), which is the ONLY gate \C itself checks — an internal implementation detail leaking through as accidental, unintended public surface. \C is not ECMAScript at all (an RE2/real-only extension outside this layer's std::regex contract), and libstdc++ does not accept it either — a genuine both-behavior mismatch (real: matches one byte; std: rejects, or accepts it as some other escape and matches differently), not a case where routing to std even yields agreement. Route to std up front so compat never exposes it.

Parameters
[in]pThe pattern text.
Returns
true if it holds a construct only the std backend can serve.

◆ posix_class_ranges()

std::string real::compat::detail::posix_class_ranges ( std::string_view  name)
inline

A POSIX bracket-class name to its ASCII (C-locale) range content, appended inside a [...] during ERE translation. Empty for an unknown name (the caller then falls back to std).

Parameters
[in]nameA POSIX class name without its brackets, e.g. alpha.
Returns
The equivalent range text for a bracket expression, or empty if the name is unknown.

◆ real_honors()

bool real::compat::detail::real_honors ( regex_constants::match_flag_type  mf)
inlineprivatenoexcept

Whether real can honor the requested match flags, so the operation may stay on it.

Only match_default and the non-constraining match_any hint stay on real (which satisfies match_any by returning the leftmost match, so ignoring it is sound). Any constraining bit — not_bol, not_eol, not_bow, not_eow, not_null, match_continuous, match_prev_avail — is not expressible through real's API, so the operation routes to std, this layer never accepting a flag it would then ignore. Narrowing the set (mapping match_continuous onto real.match(pos), say) is a measured optimisation; partitioning the flags by hand without one only gives the fuzzer more to police.

Parameters
[in]mfThe match flags the caller passed.
Returns
true if every flag in mf is expressible through REAL's API, so the operation may stay on the real backend.

◆ replace_stays_real()

bool real::compat::detail::replace_stays_real ( regex_constants::match_flag_type  f)
inlineprivatenoexcept

Whether regex_replace can run its substitution on real. The real expander honors only format_first_only / format_no_copy (plus the match_any hint); ANY other bit — a constraining match flag (not_bol, continuous, …) OR format_sed (POSIX syntax) — would be silently ignored by the ECMAScript expander, so the whole substitution routes to std. (This subsumes the explicit format_sed screen; $0 stays content-based.)

Parameters
[in]fThe match/format flags the caller passed to regex_replace.
Returns
true if the real expander honors all of them, so the replace may stay on the real backend.

◆ run()

template<typename BidirIt , typename CharT , typename Traits >
bool real::compat::detail::run ( BidirIt  first,
BidirIt  last,
match_results< BidirIt > &  m,
const basic_regex< CharT, Traits > &  re,
bool  anchored,
regex_constants::match_flag_type  mf 
)
private

Runs the active backend over [first, last) and fills m. anchored selects whole-sequence match (regex_match) vs leftmost search (regex_search). A constraining match flag (see real_honors) routes to std even for a real-backed pattern.

Parameters
[in]firstStart of the sequence to run over.
[in]lastOne past its end.
[out]mResult filled on success; left ready-but-unmatched on failure.
[in]reThe pattern, whose backend decides which engine runs.
[in]anchoredWhole-sequence match (regex_match) rather than leftmost search.
[in]mfMatch flags; a constraining one routes to std even for a real-backed pattern.
Returns
true if a match was found and m filled.

◆ run_nocapture()

template<typename BidirIt , typename CharT , typename Traits >
bool real::compat::detail::run_nocapture ( BidirIt  first,
BidirIt  last,
const basic_regex< CharT, Traits > &  re,
bool  anchored,
regex_constants::match_flag_type  mf 
)
private

Backend run without capturing (no match_results to fill).

Parameters
[in]firstStart of the sequence to run over.
[in]lastOne past its end.
[in]reThe pattern, whose backend decides which engine runs.
[in]anchoredWhole-sequence match rather than leftmost search.
[in]mfMatch flags; a constraining one routes to std.
Returns
true if a match exists.

◆ to_real()

real::flags real::compat::detail::to_real ( regex_constants::syntax_option_type  f)
inlinenoexcept

Maps compat options to real::flags (always with bytes|ecma for std-char alignment).

Parameters
[in]fThe compat syntax options.
Returns
The equivalent real::flags.

◆ to_std()

std::regex_constants::syntax_option_type real::compat::detail::to_std ( regex_constants::syntax_option_type  f)
inlinenoexcept

Maps compat options to std::regex syntax flags (the fallback path).

Parameters
[in]fThe compat syntax options.
Returns
The equivalent std::regex_constants::syntax_option_type.

◆ to_std_match()

std::regex_constants::match_flag_type real::compat::detail::to_std_match ( regex_constants::match_flag_type  f)
inlineprivatenoexcept

Maps compat match/format flags to std::regex_constants — exhaustively.

Every compat bit has an entry: a forgotten bit would be silently lost on the std path, which is the one divergence this layer does not allow itself. Both the match-control flags (search/match/iterate) and the format flags (replace) are mapped here.

Parameters
[in]fThe compat flags to translate.
Returns
The equivalent std::regex_constants::match_flag_type.

◆ translate_bracket()

bool real::compat::detail::translate_bracket ( std::string_view  p,
std::size_t &  i,
std::string &  out 
)
inline

Translates a POSIX bracket expression [...] — identical syntax in BRE and ERE, so shared by both translators. POSIX classes ([[:alpha:]]) become ASCII ranges; other members pass through. i must point at the opening [; on success it advances past the ] and appends the class to out. Returns false (the caller then declines to std) on an unterminated class or an unknown / collating [[:foo:]] / [.x.] / [=x=].

Parameters
[in]pThe pattern being translated.
[in,out]iCursor at the opening bracket; advanced past the expression on success.
[in,out]outDestination the translated bracket is appended to.
Returns
false if the bracket expression cannot be translated, leaving out unspecified.

◆ translate_bre()

std::optional< std::string > real::compat::detail::translate_bre ( std::string_view  p)
inline

Translates a POSIX basic (BRE) pattern to an equivalent REAL pattern, or nullopt. BRE differs from ERE: \( \) group and \{n\} quantify, while bare ( ) { } | + ? are LITERALS (escaped for REAL); * at an expression start and ^/$ off the ends are literals too. Declines (→ std) on a backreference \1-\9 (std's residual value), an ECMAScript-ism, a non-strict \{, an unknown / collating class, or a POSIX-undefined corner (^/$/* at a subexpression boundary).

Parameters
[in]pThe BRE pattern.
Returns
The ECMAScript equivalent, or std::nullopt when it cannot be translated.

◆ translate_ere()

std::optional< std::string > real::compat::detail::translate_ere ( std::string_view  p,
bool  awk = false 
)
inline

Translates a POSIX extended (ERE) — or, with awk, an awk — pattern to an equivalent REAL pattern, or nullopt when it uses a construct the two grammars read differently (an ECMAScript shorthand \d\w\s — undefined/literal in ERE; an ambiguous {; an unknown/collating [[:…:]]; an empty alternation branch, which std rejects — see has_empty_alternation_branch). awk adds the C-escapes (see append_awk_escape). POSIX classes become ASCII ranges (C locale); the common productions pass through, since REAL reads them like ERE. Validated by a bounds differential.

Parameters
[in]pThe ERE pattern.
[in]awkWhether awk's extra escapes are in scope.
Returns
The ECMAScript equivalent, or std::nullopt when the pattern cannot be translated.

◆ translate_newline_alt()

template<typename LineFn >
std::optional< std::string > real::compat::detail::translate_newline_alt ( std::string_view  p,
LineFn  translate_line 
)
inline

grep / egrep: a newline in the pattern is a top-level alternation of the lines (grep = BRE lines, egrep = ERE lines).

Each line is translated by translate_line and the results are joined with | — correct precedence by construction, since | is the lowest, and each line's ^/$ stay branch-relative. A line that declines, or an empty line (a blank branch, a std edge best left to std), declines the whole pattern.

Parameters
[in]pThe pattern, whose newlines separate alternatives (grep/egrep).
[in]translate_lineApplied to each line; its std::nullopt fails the whole translation.
Returns
The joined ECMAScript alternation, or std::nullopt if any line failed.

◆ translate_posix()

std::optional< std::string > real::compat::detail::translate_posix ( std::string_view  p,
regex_constants::syntax_option_type  f 
)
inline

Dispatches a single POSIX grammar to its translator, or nullopt (→ std). Exactly one grammar bit must be set, and neither collate nor nosubs (which force std). extended → ERE, basic → BRE, awk → ERE + C-escapes, grep → BRE lines joined by |, egrep → ERE lines joined by |.

Parameters
[in]pThe pattern text.
[in]fThe syntax options, which select the POSIX grammar to translate from.
Returns
The ECMAScript equivalent, or std::nullopt when the options or pattern decline.

Variable Documentation

◆ real_eligible

template<typename CharT , typename Traits >
constexpr bool real::compat::detail::real_eligible
inlineconstexpr
Initial value:
=
std::is_same_v<CharT, char> && std::is_same_v<Traits, std::regex_traits<char>>

Whether real is even eligible for this basic_regex instantiation. real runs only the char path with default traits; wchar_t/char8_t/… and custom traits are always std. This is a compile-time gate: it must compile real's char-only code (the byte string_view, fill_from_real) out for other CharT, not merely skip it at runtime.