88 std::int32_t
max {-1};
114 for (std::size_t i = 1; i < ranges.size(); ++i) {
115 if (ranges[i].lo <= ranges[i - 1].hi + 1U) {
123 std::sort(ranges.begin(), ranges.end(),
125 std::vector<code_range> merged;
127 if (!merged.empty() && r.lo <= merged.back().hi + 1U) {
128 merged.back().hi = merged.back().hi > r.hi ? merged.back().hi : r.hi;
141 const std::vector<code_range> merged {
coalesce_ranges(std::move(ranges))};
142 std::vector<code_range> gaps;
143 std::uint32_t next {0x80U};
146 gaps.push_back({.lo = next, .hi = r.lo - 1U});
150 if (next <= 0x10FFFFU) {
151 gaps.push_back({.lo = next, .hi = 0x10FFFFU});
205 const auto is_octal {[](
char c) {
return c >=
'0' && c <=
'7'; }};
206 const auto is_digit {[](
char c) {
return c >=
'0' && c <=
'9'; }};
207 if (text[
first] ==
'0') {
209 std::size_t taken {};
210 while (taken < 3 &&
first + taken < text.size() && is_octal(text[
first + taken])) {
211 value = (value * 8U) +
static_cast<unsigned>(text[
first + taken] -
'0');
216 std::size_t length {1};
217 if (
first + 1 < text.size() && is_digit(text[
first + 1])) {
219 if (is_octal(text[
first]) && is_octal(text[
first + 1]) &&
first + 2 < text.size() &&
220 is_octal(text[
first + 2])) {
221 const unsigned value {(
static_cast<unsigned>(text[
first] -
'0') * 8U * 8U) +
222 (
static_cast<unsigned>(text[
first + 1] -
'0') * 8U) +
223 static_cast<unsigned>(text[
first + 2] -
'0')};
231 for (std::size_t k = 0; k < length; ++k) {
250 constexpr explicit parser(std::string_view pattern,
272 fail(
"unbalanced parenthesis");
325 const char ch {
peek()};
326 if (ch ==
' ' || ch ==
'\t' || ch ==
'\n' || ch ==
'\r' || ch ==
'\f' || ch ==
'\v') {
329 else if (ch ==
'#') {
330 while (!
eof() &&
peek() !=
'\n') {
351 template <
typename Error = regex_error>
352 [[noreturn]]
constexpr void fail(
const char* message)
const
354 throw Error(message,
pos_);
360 template <
typename =
void>
369 [[nodiscard]]
constexpr bool eof()
const
377 [[nodiscard]]
constexpr char peek()
const
387 [[nodiscard]]
constexpr bool accept(
char ch)
403 return (ch >=
'0' && ch <=
'9') || (ch >=
'A' && ch <=
'Z') || (ch >=
'a' && ch <=
'z');
418 out.
nodes.push_back(node);
419 return static_cast<std::int32_t
>(out.
nodes.size()) - 1;
435 const std::vector<code_range>& ranges = {},
436 bool codepoint_predicate =
false)
438 out.
classes.push_back({.ascii =
klass, .ranges = ranges, .codepoint_predicate = codepoint_predicate});
439 const auto index {
static_cast<std::int32_t
>(out.
classes.size()) - 1};
462 std::vector<code_range>& ranges,
464 std::span<const code_range> table,
466 bool& property_derived)
const
472 klass.merge(inverted);
475 inverted.invert_ascii();
476 klass.merge(inverted);
480 ranges.insert(ranges.end(), comp.begin(), comp.end());
483 klass.merge(prop_ascii);
485 ranges.insert(ranges.end(), high.begin(), high.end());
490 [[nodiscard]]
constexpr std::vector<code_range>
shorthand_ranges(std::span<const code_range> table)
const
492 std::vector<code_range> out;
500 out.push_back({.lo = r.lo < 0x80U ? 0x80U : r.lo, .hi = r.hi});
541 [[nodiscard]]
constexpr std::string_view
view()
const
550 for (
const char c : s) {
551 if (c ==
'_' || c ==
'-' || c ==
' ') {
555 b.
data[b.
len++] = (c >=
'A' && c <= 'Z') ? static_cast<char>(c -
'A' +
'a') : c;
568 [[nodiscard]]
constexpr std::vector<code_range>
resolve_property(std::string_view name)
const
571 std::string_view value {name};
572 for (std::size_t i = 0; i < name.size(); ++i) {
573 if (name[i] ==
'=') {
574 ns = name.substr(0, i);
575 value = name.substr(i + 1);
580 const std::string_view nk {ns_key.
view()};
581 const bool want_gc {nk.empty() || nk ==
"gc" || nk ==
"generalcategory"};
582 const bool want_script {nk.empty() || nk ==
"sc" || nk ==
"script"};
583 if (!want_gc && !want_script) {
585 fail_unsupported(
"unknown Unicode property namespace in \\p{...} (use gc= or sc=)");
591 const std::span<const code_range> t {
gc_property_ranges[
static_cast<std::size_t
>(prop)]};
592 return {t.begin(), t.end()};
598 std::vector<code_range> out;
601 out.push_back({.lo = r.lo, .hi = r.hi});
609 fail_unsupported(
"unsupported Unicode property in \\p{...} (only General_Category and Script are built in)");
620 fail_unsupported(
"\\p{...} Unicode property classes are not available in bytes mode");
623 std::string_view name;
626 const std::size_t start {
pos_};
627 while (!
eof() &&
peek() !=
'}') {
631 fail(
"unterminated \\p{...} property");
638 fail(
"\\p must be followed by a property name or {name}");
644 fail(
"empty Unicode property name in \\p{}");
654 std::vector<code_range>& high)
const
656 for (
char32_t c = 0; c < 0x80U; ++c) {
658 ascii.set(
static_cast<std::uint8_t
>(c));
665 high.push_back({.lo = r.lo < 0x80U ? 0x80U : r.lo, .hi = r.hi});
679 std::vector<code_range> high;
693 std::vector<code_range>& ranges,
694 const std::vector<code_range>& table,
696 bool& property_derived)
const
699 std::vector<code_range> high;
702 ascii.invert_ascii();
705 ranges.insert(ranges.end(), comp.begin(), comp.end());
709 ranges.insert(ranges.end(), high.begin(), high.end());
711 property_derived =
true;
728 std::int32_t last {
first};
731 out.
nodes[
static_cast<std::size_t
>(last)].next = branch;
735 out.
nodes[
static_cast<std::size_t
>(alt)].child =
first;
746 std::int32_t
first {-1};
747 std::int32_t last {-1};
760 out.
nodes[
static_cast<std::size_t
>(last)].next = atom;
767 if (out.
nodes[
static_cast<std::size_t
>(
first)].next == -1) {
771 out.
nodes[
static_cast<std::size_t
>(seq)].child =
first;
782 const char ch {
peek()};
787 fail(
"nothing to repeat");
797 fail(
"unbalanced parenthesis");
812 if (!
bytes_ &&
static_cast<std::uint8_t
>(ch) >= 0x80U) {
814 if (!decoded.valid) {
815 fail(
"invalid UTF-8 byte in pattern");
817 pos_ += decoded.length;
848 std::int32_t ignored_min {};
849 std::int32_t ignored_max {-1};
851 fail(
"nothing to repeat");
855 std::int32_t max {-1};
876 const bool lazy {
accept(
'?')};
878 const char ch {
peek()};
879 std::int32_t ignored_min {};
880 std::int32_t ignored_max {-1};
881 if (ch ==
'*' || ch ==
'+' || ch ==
'?' ||
883 fail(
"multiple repeat");
900 const std::size_t saved_pos {
pos_};
903 std::int32_t repeat_max {repeat_min};
909 if (!
accept(
'}') || (repeat_min < 0 && repeat_max < 0)) {
913 min = repeat_min < 0 ? 0 : repeat_min;
914 max = (has_comma && repeat_max < 0) ? -1 : repeat_max;
915 if (max != -1 && max < min) {
917 fail(
"min repeat greater than max repeat");
930 std::int32_t value {-1};
932 value = value < 0 ? 0 : value;
933 value = (value * 10) + (
peek() -
'0');
935 fail(
"repetition count too large");
987 return letter ==
'i' || letter ==
'm' || letter ==
's' || letter ==
'a' || letter ==
'x';
994 return static_cast<flags>(
995 static_cast<std::uint8_t
>(
static_cast<unsigned>(value) & ~
static_cast<unsigned>(bit)));
1010 const std::size_t saved_pos {
pos_};
1022 if (!any_letter || !
accept(
')')) {
1054 const std::size_t open_pos {
pos_};
1056 fail(
"pattern nesting too deep");
1059 std::int32_t
group {-1};
1060 bool scoped_flags {
false};
1065 while (!
eof() &&
peek() !=
')') {
1070 fail(
"missing ), unterminated comment");
1083 else if (!
eof() &&
peek() ==
'=') {
1087 fail(
"unknown extension");
1097 else if (!
eof() && (
peek() ==
'=' ||
peek() ==
'!')) {
1100 else if (!
eof() &&
peek() ==
'>') {
1101 fail(
"atomic groups are not supported");
1103 else if (!
eof() &&
peek() ==
'(') {
1104 fail(
"conditional groups are not supported");
1116 bool saw_negative {
false};
1119 saw_negative =
true;
1122 if (!saw_negative) {
1123 fail(
"missing flag after '-'");
1129 fail(
"global flags not at the start of the expression");
1135 scoped_flags =
true;
1138 fail(
"unknown extension");
1150 fail(
"missing ), unterminated subpattern");
1173 std::size_t open_pos)
1178 const bool negative {
peek() ==
'!'};
1185 fail(
"missing ), unterminated subpattern");
1189 .negated = negative,
1190 .direction = direction,
1202 std::size_t open_pos)
1206 fail(
"too many capture groups");
1218 return ch ==
'_' || (ch >=
'A' && ch <=
'Z') || (ch >=
'a' && ch <=
'z');
1230 const std::size_t begin {
pos_};
1232 fail(
"bad character in group name");
1237 const std::size_t end {
pos_};
1238 expect(
'>',
"bad character in group name");
1240 const std::string_view name {
pattern_.substr(begin, end - begin)};
1241 const auto e_begin {
static_cast<std::size_t
>(existing.begin)};
1242 const auto e_end {
static_cast<std::size_t
>(existing.end)};
1243 if (
pattern_.substr(e_begin, e_end - e_begin) == name) {
1244 fail(
"redefinition of group name");
1248 .begin =
static_cast<std::int32_t
>(begin),
1249 .end =
static_cast<std::int32_t
>(end)});
1264 const char ch {
peek()};
1265 if (ch >=
'0' && ch <=
'9') {
1289 if (
ecma_) {
return 'a'; }
1294 const std::int32_t high_nibble {
hex_digit()};
1295 const std::int32_t low_nibble {
hex_digit()};
1296 return (high_nibble * 16) + low_nibble;
1300 if (
static_cast<std::uint8_t
>(ch) < 0x80 && !
is_ascii_alnum(ch)) {
1302 return static_cast<std::uint8_t
>(ch);
1323 return static_cast<std::int32_t
>(decoded.value);
1326 fail(
"octal escape value outside of range 0-0o377");
1339 fail(
"invalid \\x escape: expected two hex digits");
1341 const char ch {
peek()};
1343 if (ch >=
'0' && ch <=
'9') {
1346 if (ch >=
'a' && ch <=
'f') {
1347 return ch -
'a' + 10;
1349 if (ch >=
'A' && ch <=
'F') {
1350 return ch -
'A' + 10;
1353 fail(
"invalid \\x escape: expected two hex digits");
1369 fail(
"\\u and \\U escapes are not allowed in bytes patterns");
1371 const int width {capital ? 8 : 4};
1372 std::int32_t value {};
1373 for (
int i = 0; i < width; ++i) {
1374 std::int32_t digit {-1};
1376 const char ch {
peek()};
1377 if (ch >=
'0' && ch <=
'9') {
1380 else if (ch >=
'a' && ch <=
'f') {
1381 digit = (ch -
'a') + 10;
1383 else if (ch >=
'A' && ch <=
'F') {
1384 digit = (ch -
'A') + 10;
1388 fail(capital ?
"invalid \\U escape: expected 8 hex digits"
1389 :
"invalid \\u escape: expected 4 hex digits");
1391 value = (value * 16) + digit;
1394 if (value >= 0xD800 && value <= 0xDFFF) {
1395 fail(
"invalid Unicode escape: surrogate code point");
1397 if (value > 0x10FFFF) {
1398 fail(
"invalid Unicode escape: code point out of range");
1417 fail(
"\\N escapes are not allowed in bytes patterns");
1420 fail(
"expected '{' after \\N (\\N{U+XXXX})");
1424 fail(
"\\N{...} takes a U+XXXX code point; a character name is resolved by the Python binding");
1428 fail(
"expected '+' in \\N{U+XXXX}");
1431 std::int32_t value {};
1434 const char ch {
peek()};
1435 std::int32_t digit {-1};
1436 if (ch >=
'0' && ch <=
'9') {
1439 else if (ch >=
'a' && ch <=
'f') {
1440 digit = (ch -
'a') + 10;
1442 else if (ch >=
'A' && ch <=
'F') {
1443 digit = (ch -
'A') + 10;
1448 value = (value * 16) + digit;
1453 fail(
"expected 1 to 6 hex digits in \\N{U+XXXX}");
1456 fail(
"unterminated \\N{U+XXXX} (expected '}')");
1459 if (value >= 0xD800 && value <= 0xDFFF) {
1460 fail(
"invalid \\N escape: surrogate code point");
1462 if (value > 0x10FFFF) {
1463 fail(
"invalid \\N escape: code point out of range");
1478 const auto value {
static_cast<std::uint32_t
>(cp)};
1479 if (value < 0x80U) {
1482 std::int32_t
first {-1};
1483 std::int32_t last {-1};
1484 const auto emit_byte {[&](std::uint8_t one) {
1490 out.
nodes[
static_cast<std::size_t
>(last)].next = node;
1494 if (value < 0x800U) {
1495 emit_byte(
static_cast<std::uint8_t
>(0xC0U | (value >> 6U)));
1496 emit_byte(
static_cast<std::uint8_t
>(0x80U | (value & 0x3FU)));
1498 else if (value < 0x10000U) {
1499 emit_byte(
static_cast<std::uint8_t
>(0xE0U | (value >> 12U)));
1500 emit_byte(
static_cast<std::uint8_t
>(0x80U | ((value >> 6U) & 0x3FU)));
1501 emit_byte(
static_cast<std::uint8_t
>(0x80U | (value & 0x3FU)));
1504 emit_byte(
static_cast<std::uint8_t
>(0xF0U | (value >> 18U)));
1505 emit_byte(
static_cast<std::uint8_t
>(0x80U | ((value >> 12U) & 0x3FU)));
1506 emit_byte(
static_cast<std::uint8_t
>(0x80U | ((value >> 6U) & 0x3FU)));
1507 emit_byte(
static_cast<std::uint8_t
>(0x80U | (value & 0x3FU)));
1510 out.
nodes[
static_cast<std::size_t
>(seq)].child =
first;
1527 const bool ascii_letter {(cp >=
'A' && cp <=
'Z') || (cp >=
'a' && cp <=
'z')};
1530 bitmap.
set(
static_cast<std::uint8_t
>(cp));
1533 if (!
bytes_ && cp >= 0x80 &&
1535 const std::vector<code_range> single {
1536 {.lo =
static_cast<std::uint32_t
>(cp), .hi =
static_cast<std::uint32_t
>(cp)}};
1542 if (
bytes_ || cp < 0x80) {
1562 fail(
"dangling backslash");
1637 if (byte_value < 0) {
1643 if (byte_value < 0x80) {
1664 std::vector<code_range>& ranges,
1665 bool& property_derived)
1667 const char ch {
peek()};
1668 if (
static_cast<std::uint8_t
>(ch) >= 0x80) {
1672 fail(
"non-ASCII character class member not supported");
1675 if (!decoded.valid) {
1676 fail(
"invalid UTF-8 byte in character class");
1678 pos_ += decoded.length;
1679 return static_cast<std::int32_t
>(decoded.cp);
1683 return static_cast<std::uint8_t
>(ch);
1687 fail(
"dangling backslash");
1704 const bool negated {
peek() ==
'P'};
1715 const bool capital {
peek() ==
'U'};
1737 std::size_t taken {};
1738 while (taken < 3 && !
eof() &&
peek() >=
'0' &&
peek() <=
'7') {
1739 value = (value * 8U) +
static_cast<unsigned>(
peek() -
'0');
1743 if (value > 0xFFU) {
1744 fail(
"octal escape value out of range (\\0 to \\377)");
1746 return static_cast<std::int32_t
>(value);
1750 fail(
"invalid escape (\\8 and \\9 are not octal and there are no back-references in a class)");
1754 if (byte_value < 0) {
1774 const std::size_t open_pos {
pos_};
1776 const bool negated {
accept(
'^')};
1778 std::vector<code_range> ranges;
1779 bool property_derived {};
1785 const auto add_cp {[&](std::int32_t cp) {
1786 if (
bytes_ || cp < 0x80) {
1787 klass.
set(
static_cast<std::uint8_t
>(cp));
1790 ranges.push_back({
static_cast<std::uint32_t
>(cp),
static_cast<std::uint32_t
>(cp)});
1795 const auto add_range {[&](std::int32_t lo, std::int32_t hi) {
1797 klass.set_range(
static_cast<std::uint8_t
>(lo),
static_cast<std::uint8_t
>(hi));
1799 else if (lo < 0x80) {
1800 klass.set_range(
static_cast<std::uint8_t
>(lo),
static_cast<std::uint8_t
>(hi < 0x80 ? hi : 0x7F));
1802 ranges.push_back({0x80U,
static_cast<std::uint32_t
>(hi)});
1806 ranges.push_back({
static_cast<std::uint32_t
>(lo),
static_cast<std::uint32_t
>(hi)});
1812 fail(
"unterminated character class");
1822 const std::size_t item_pos {
pos_};
1824 if (range_start < 0) {
1832 if (range_end < 0 || range_end < range_start) {
1834 fail(
"bad character range");
1836 add_range(range_start, range_end);
1839 add_cp(range_start);
256-bit byte set with O(1) membership, fully constexpr.
Recursive-descent parser: a pattern string in, an ast out.
constexpr bool eof() const
Returns true if the read offset is at or past the end of the pattern.
constexpr std::vector< code_range > shorthand_ranges(std::span< const code_range > table) const
static constexpr flags without(flags value, flags bit)
value with bit cleared.
constexpr void merge_property(char_class &klass, std::vector< code_range > &ranges, const char_class &prop_ascii, std::span< const code_range > table, bool negated, bool &property_derived) const
Merges an in-class shorthand (\w \d \s or a negated \W \D \S) into the class being built: its ASCII b...
static constexpr loose_buf loose_key(std::string_view s)
std::vector< flags > flag_scopes_
Stack of the flag set in force per nesting level; the top is current. Replaces a global verbose_ read...
constexpr std::vector< code_range > resolve_property(std::string_view name) const
Resolves a \p{...} property name to its code-point ranges, or fails with a clear error....
constexpr std::int32_t parse_atom(ast &out)
Parses one atom: a literal, ., a class, a group, an anchor or an escape.
constexpr char peek() const
Returns the current character without consuming it (undefined at eof()).
constexpr std::int32_t parse_escape(ast &out)
Parses an escape outside a character class.
constexpr std::int32_t parse_digit_escape()
Parses a <digit> escape via the shared decode_digit_escape().
constexpr ast parse()
Parses the whole pattern.
constexpr bool is_ascii_mode() const
True when ascii (re.A) is in force at the current scope (a scoped (?a:...) honoured).
constexpr std::vector< code_range > parse_property_table()
Rejects bytes mode, consumes the p/P and the {Name} (or single letter), and resolves it to the proper...
constexpr std::int32_t parse_lookaround(ast &out, look_dir direction, std::size_t open_pos)
Parses a lookaround after (?= / (?! (ahead) or (?<= / (?<! (behind) — the =/! is not yet consumed.
constexpr std::int32_t add_class_node(ast &out, const char_class &klass, bool negated, const std::vector< code_range > &ranges={}, bool codepoint_predicate=false)
Interns a class bitmap and appends a node_kind::klass node.
constexpr void merge_unicode_property(char_class &klass, std::vector< code_range > &ranges, const std::vector< code_range > &table, bool negated, bool &property_derived) const
Merges a \p{Name} / \P{Name} property into the character class being built (the in-class form) — the ...
constexpr std::int32_t parse_named_codepoint()
Decodes a \N{U+XXXX} named-code-point escape (1–6 hex digits) — the same code-point path as \u/\U,...
constexpr std::int32_t parse_quantifier(ast &out, std::int32_t atom)
Wraps atom in a repeat node if a quantifier follows.
constexpr std::int32_t parse_unicode_property(ast &out, bool negated)
Parses \p{Name} / \P{Name} / \pX (outside a class) into a negatable Unicode code-point class (klass_c...
constexpr std::int32_t parse_group(ast &out)
Parses a group construct.
std::string_view pattern_
The pattern being parsed.
constexpr bool is_verbose() const
True when verbose mode (re.X) is in force here — read from the scope stack, so a scoped (?...
constexpr bool parse_global_flags_prefix(ast &out)
Consumes a leading (?ims) global-flags group, if present.
constexpr std::int32_t parse_byte_escape()
Parses a single-byte escape (valid inside and outside classes).
constexpr std::int32_t parse_sequence(ast &out)
Parses sequence := (atom quantifier?)*, stopping at | or ).
constexpr std::int32_t parse_class_item(char_class &klass, std::vector< code_range > &ranges, bool &property_derived)
Parses one member inside a character class.
std::int32_t depth_
Current group nesting (see max_nesting_depth).
constexpr std::int32_t new_group(ast &out, std::size_t open_pos)
Allocates the next capture group number.
constexpr void parse_group_name(ast &out, std::int32_t group)
Parses ‘name := [A-Za-z_][A-Za-z0-9_]* ’>'` and records it.
bool bytes_
In flags::bytes mode, rejects code-point escapes (\u/\U).
constexpr void fail(const char *message) const
Aborts the parse with a real::regex_error at the current offset.
constexpr std::int32_t parse_class(ast &out)
Parses a bracketed character class [...] or [^...].
constexpr std::int32_t add_node(ast &out, ast_node node)
Appends node to the pool.
bool ecma_
ECMAScript grammar: \A \Z < > are identity-escape literals, not anchors.
static constexpr flags flag_for_letter(char letter)
Maps a flag letter to its flags value.
constexpr std::int32_t parse_unicode_codepoint(bool capital)
Decodes a \uHHHH (4 hex) or \UHHHHHHHH (8 hex) code-point escape (str only).
constexpr std::int32_t parse_alternation(ast &out)
Parses ‘alternation := sequence (’|' sequence)*`.
std::size_t pos_
Current read offset into pattern_.
static constexpr shorthand_spec shorthand_class(char letter)
Maps a shorthand letter to its shorthand_spec. The single place the letter -> (set,...
static constexpr bool is_name_start(char ch)
Returns true if ch may start a group name.
constexpr bool text_shorthand() const
The non-ASCII (>= 0x80) code-point ranges of a Unicode property table (\d/\s/\w), for a text-mode sho...
constexpr std::int32_t emit_literal_codepoint(ast &out, std::int32_t cp)
Emits a code-point literal (code-point provenance: a raw character or \\u/\\U).
bool in_lookaround_
True while parsing a lookaround sub-pattern (rejects nesting).
constexpr void skip_insignificant()
In verbose mode, consumes insignificant whitespace and # comments.
constexpr void fail_unsupported(const char *message) const
Like fail, but tags the error as unsupported (well-formed but beyond REAL's linear engine — a backref...
constexpr void expect(char ch, const char *message)
Consumes ch or fails.
constexpr std::int32_t hex_digit()
Consumes one hexadecimal digit.
constexpr bool accept(char ch)
Consumes the current character if it equals ch.
constexpr bool try_parse_braces(std::int32_t &min, std::int32_t &max)
Tries to parse {n} / {n,} / {,m} / {n,m} starting at {.
constexpr void property_ascii_high(const std::vector< code_range > &table, char_class &ascii, std::vector< code_range > &high) const
Splits a property's ranges into its ASCII bitmap (< 0x80) and its non-ASCII ranges....
constexpr bool is_icase() const
True when icase (re.I) is in force at the current scope (a scoped (?i:...) honoured).
static constexpr bool is_flag_letter(char letter)
Returns true if letter is a flag letter (imsax).
constexpr parser(std::string_view pattern, flags initial_flags=flags::none)
Binds the parser to a pattern and the constructor flags.
constexpr flags current_flags() const
The flag set in force at the current nesting level (the scope-stack top).
constexpr std::int32_t emit_codepoint_utf8(ast &out, std::int32_t cp)
Emits a code point as its 1–4 UTF-8 bytes — the same byte-level form a literal multi-byte character p...
constexpr std::int32_t parse_repeat_count()
Reads an optional decimal repeat count.
static constexpr bool is_ascii_alnum(char ch)
Returns true if ch is in [0-9A-Za-z].
Resource limits guarding against pattern-driven resource exhaustion.
constexpr char_class digit_set()
The ASCII digit set behind \d (Python re.ASCII semantics).
anchor_kind
The specific zero-width assertion of an anchor node (see node_kind::anchor).
@ word_start
< (start of word; REAL extension, not in Python re).
@ caret
^ (text or line start, depending on multiline).
@ word_end
> (end of word; REAL extension, not in Python re).
@ dollar
$ (end, before a trailing \n, or line end with m).
constexpr gc_property resolve_gc(std::string_view loose)
Resolve a loose-normalized General_Category name to its property, or count if unknown.
constexpr decoded_codepoint decode_codepoint_strict(std::string_view text, std::size_t pos)
Strictly decodes and validates the UTF-8 sequence at text[pos].
constexpr digit_escape_result decode_digit_escape(std::string_view text, std::size_t first)
Decodes a <digit> escape per CPython's exact rule (shared by the pattern parser and the replacement-...
constexpr bool cp_in_ranges(std::span< const code_range > ranges, char32_t cp)
Binary-searches a sorted, non-overlapping range table for cp. Returns a bool (not a pointer into the ...
constexpr std::int32_t max_group_count
Maximum capture groups; bounds slot_count = 2 * (groups + 1).
constexpr std::span< const code_range > gc_property_ranges[]
Range table indexed by gc_property (parallel to the enum order).
constexpr char_class word_set()
The ASCII word set behind \w.
constexpr std::size_t unicode_fold_table_size
Number of entries in unicode_fold_table.
digit_escape_kind
What a <digit> escape decoded to (see decode_digit_escape()).
@ octal
An octal byte escape; value is the byte (0-255).
@ group_ref
A decimal group number; value is the group (a back-reference in a pattern).
@ octal_overflow
A 3-octal-digit escape greater than 0o377 (an error in CPython).
constexpr std::vector< code_range > complement_code_ranges(std::vector< code_range > ranges)
Complements a set of code-point ranges within [0x80, 0x10FFFF] (used by negated classes and by an in-...
gc_property
A Unicode General_Category property: the 29 assignable categories then the 7 groups.
constexpr std::vector< code_range > coalesce_ranges(std::vector< code_range > ranges)
Sorts ranges and merges overlapping / adjacent ones into a minimal, sorted set (the same set of code ...
look_dir
Direction of a lookaround sub-pattern.
@ ahead
(?= / (?! — the sub matches starting at the position.
@ behind
(?<= / (?<! — the sub matches ending exactly at the position.
constexpr ast parse(std::string_view pattern, flags initial_flags=flags::none)
Parses pattern into an ast (convenience over parser).
constexpr code_range word_ranges[]
Code-point ranges matched by \w (771 ranges, 142940 code points).
constexpr code_range space_ranges[]
Code-point ranges matched by \s (10 ranges, 29 code points).
constexpr script resolve_script(std::string_view loose)
Resolve a loose-normalized Script name to its value, or count if unknown.
script
A Unicode Script value; Unknown (0) is every code point no script assigns.
node_kind
Kind of an AST node; selects which fields of real::detail::ast_node are meaningful.
@ any
One codepoint, except newline (the . metacharacter).
@ repeat
Child repeated [min, max] times (max -1 = unbounded).
@ lookaround
Bounded lookaround: child = sub-pattern, negated = (?!/(?<!), direction = ahead/behind.
@ concat
Children matched in sequence.
@ alternation
Children are branches, leftmost preferred.
@ anchor
Zero-width assertion; kind in real::detail::ast_node::anchor.
@ klass
One codepoint constrained by classes[klass] (a class_def; negated or not).
@ empty
Matches the empty string.
@ group
Child wrapped in a group; group >= 0 when capturing.
@ word_start
< (non-word/start on the left, word on the right).
@ word_end
> (word on the left, non-word/end on the right).
@ word_boundary
\b (Unicode word-ness in text mode; ASCII in bytes / re.A).
@ text_start
\A, and ^ without multiline.
constexpr code_range digit_ranges[]
Code-point ranges matched by \d (71 ranges, 760 code points).
constexpr char_class space_set()
The ASCII whitespace set behind \s.
constexpr std::int32_t max_repeat_count
Per-quantifier bounded-repeat cap, enforced at parse time.
constexpr script_range script_ranges[]
Script partition — 979 ranges, sorted and disjoint.
constexpr std::int32_t max_nesting_depth
Maximum parser recursion depth; prevents stack overflow on deep nesting.
@ byte
Consume one byte equal to arg8; fall through to pc+1.
@ klass
Consume one byte in classes[arg16]; fall through to pc+1.
constexpr std::size_t find_fold_index(std::uint32_t cp)
Binary-searches unicode_fold_table for cp; returns its index, or unicode_fold_table_size if cp is not...
@ first
Leftmost-first (Perl / Python re / the crate): source-order thread priority decides....
constexpr bool has_flag(flags value, flags flag)
Tests whether flag is set in value.
flags
Compilation flags, mirroring Python's re.I, re.M and re.S.
@ dotall
. also matches \n.
@ verbose
Verbose mode (re.X): ignore unescaped whitespace and # comments outside classes.
@ bytes
Binary mode: . and [^…] match raw bytes, not codepoints.
@ ascii
ASCII mode (re.A): \d \w \s \b stay ASCII and icase folds ASCII only, even in text mode....
@ icase
Case-insensitive (ASCII).
@ ecma
ECMAScript compatibility: $ (no multiline) matches only at the very end (not before a final \n,...
@ multiline
^ and $ also match at line boundaries.
Compiled form of a pattern and the public flags / error types.
One AST node. Active fields depend on kind (noted per field).
bool lazy
repeat: prefer the shortest expansion.
std::int32_t max
repeat: maximum count (-1 = unbounded).
bool negated
klass: written as [^...] / \D \W \S.
std::int32_t klass
klass: index into ast::classes.
std::int32_t group
group: capture number, -1 for (?:...).
anchor_kind anchor
anchor: the assertion kind.
std::uint8_t effective_flags
Flag set in force where this node was parsed (see flags). Stamped from the scope stack; carried for s...
std::int32_t child
First child (concat, repeat, alternation, group).
std::int32_t next
Next sibling in the parent's child list.
std::int32_t min
repeat: minimum count.
look_dir direction
lookaround: ahead (?=/(?! or behind (?<=/(?<!.
node_kind kind
Which fields below are meaningful.
A parsed pattern: the node pool plus side tables.
std::vector< ast_node > nodes
The node pool; root indexes it.
flags inline_flags
Flags from a leading (?ims).
std::int32_t root
Index of the root node.
std::vector< named_group > names
Named capture groups.
std::vector< class_def > classes
Character classes as written, before negation.
std::int32_t group_count
Number of capturing groups.
A set of byte values (0–255) as a 256-bit bitmap.
constexpr void invert()
Full 256-bit complement (binary mode: raw bytes, no UTF-8).
constexpr void set(std::uint8_t byte)
Adds byte byte to the set.
A parsed character class: its ASCII bitmap plus any non-ASCII code-point ranges. Bundling the two (ra...
char_class ascii
ASCII members as a bitmap (all 256 bytes in bytes mode); pre-negation.
bool codepoint_predicate
Emit as a match-time klass_cp (a Unicode shorthand \w/\d/\s in text mode), not the byte-NFA.
std::vector< code_range > ranges
Non-ASCII code-point ranges (code-point mode only; empty otherwise).
An inclusive code-point range [lo, hi]. Shared by character classes (ast.hpp) and the generated Unico...
The result of a strict UTF-8 decode: the code point, its byte length, and validity.
Result of decode_digit_escape().
unsigned value
Octal byte, or decimal group number.
digit_escape_kind kind
Which interpretation applies.
std::size_t length
Characters consumed from the first digit.
A loose-match key (lowercase, no _/-/space; UAX44-LM3-ish) built into a fixed buffer,...
std::array< char, 64 > data
constexpr std::string_view view() const
The classification of a \d \D \w \W \s \S shorthand: its ASCII bitmap, its Unicode range table,...
bool negated
True for the uppercase form (\D \W \S).
std::span< const code_range > ranges
The full Unicode range table (used in text mode).
char_class set
The ASCII bitmap (digit / word / space set).
One code-point range and the Script it belongs to (the table partitions the code space).
Unicode simple case-folding orbits for text-mode flags::icase.
Unicode General_Category ranges for \p{...} (UCD contract; distinct from the re-contract shorthands).
Unicode \w / \d / \s property ranges and their lookups.
Unicode Script ranges for \p{sc=...} (UCD contract, parsed from tools/ucd/Scripts....
UTF-8 position arithmetic for match iteration.
REAL's version macros and the C++20 language-standard guard.