REAL
Regular Expression Algorithmic Library — constexpr C++20 regex
Loading...
Searching...
No Matches
inner_literal.hpp
Go to the documentation of this file.
1
8#ifndef REAL_FRONTEND_INNER_LITERAL_HPP
9#define REAL_FRONTEND_INNER_LITERAL_HPP
10
11#include <real/version.hpp>
12
13#include <array>
14#include <cstdint>
15#include <span>
16#include <string_view>
17#include <vector>
18
19#include <real/engine/prefilter.hpp> // byte_frequency (a lower tier — frontend may use the runtime)
20#include <real/frontend/ast.hpp>
21
22namespace real::detail {
23
28 {
29 std::array<std::uint8_t, 16> bytes {};
30 std::uint8_t len {0};
31 std::uint32_t score {0};
32 std::int32_t prefix_child_count {-1};
36
37 [[nodiscard]] constexpr bool found() const
38 {
39 return len > 0;
40 }
41 };
42
44 inline constexpr std::size_t inner_literal_max {16};
45
46 namespace inner_literal_detail {
47
51 {
52 std::vector<std::uint8_t> run;
54 std::int32_t run_top {-1};
55 std::int32_t best_top {-1};
56 };
57
61 constexpr std::uint32_t score_run(std::span<const std::uint8_t> run)
62 {
63 std::uint32_t s {0};
64 for (const std::uint8_t b : run) {
65 s += static_cast<std::uint32_t>(2000U - byte_frequency(b));
66 }
67 return s;
68 }
69
72 constexpr void flush(walk_state& st)
73 {
74 if (!st.run.empty()) {
75 const std::size_t len {st.run.size() < inner_literal_max ? st.run.size() : inner_literal_max};
76 const std::uint32_t s {score_run(std::span<const std::uint8_t>(st.run.data(), len))};
77 if (s > st.best.score) {
78 st.best.len = static_cast<std::uint8_t>(len);
79 st.best.score = s;
80 for (std::size_t i = 0; i < len; ++i) {
81 st.best.bytes[i] = st.run[i];
82 }
83 st.best_top = st.run_top;
84 }
85 st.run.clear();
86 }
87 }
88
95 constexpr bool walk(const ast& tree,
96 std::int32_t idx,
97 walk_state& st,
98 std::int32_t top_child)
99 {
100 if (idx < 0) {
101 return true;
102 }
103 const ast_node& n {tree.nodes[static_cast<std::size_t>(idx)]};
104 switch (n.kind) {
105 case node_kind::empty:
106 return true;
107 case node_kind::byte:
108 if (st.run.empty()) {
109 st.run_top = top_child; // this byte starts a run — remember where, for the prefix boundary
110 }
111 st.run.push_back(n.byte);
112 return true;
114 // Only the ROOT concat's direct children are top-level (assigned indices in extract_inner_literal);
115 // a nested concat's children are inside a group/repeat, hence not a top-level boundary.
116 for (std::int32_t c = n.child; c >= 0; c = tree.nodes[static_cast<std::size_t>(c)].next) {
117 if (!walk(tree, c, st, -1)) {
118 return false;
119 }
120 }
121 return true;
122 case node_kind::group:
123 // A non-optional group (its optionality would be a `repeat` parent): descend; the run continues
124 // across the group boundary, but a literal that STARTS inside it is nested (top_child -1).
125 return walk(tree, n.child, st, -1);
126 case node_kind::repeat: {
127 if (n.min == 0) {
128 return false; // ? * {0,n}: optional -> DECLINE (conservative v1)
129 }
130 flush(st); // the repeat's width is variable; break the run around it
131 if (!walk(tree, n.child, st, -1)) { // a guaranteed literal inside the first (min) copy, nested
132 return false;
133 }
134 flush(st);
135 return true;
136 }
137 case node_kind::klass:
138 case node_kind::any:
139 flush(st); // a non-byte guaranteed segment breaks the run
140 return true;
144 return false; // DECLINE
145 }
146 return false;
147 }
148 } // namespace inner_literal_detail
149
153 {
155 if (tree.root < 0) {
156 return inner_literal {};
157 }
158 const ast_node& root {tree.nodes[static_cast<std::size_t>(tree.root)]};
159 if (root.kind == node_kind::concat) {
160 std::int32_t i {0};
161 for (std::int32_t c = root.child; c >= 0; c = tree.nodes[static_cast<std::size_t>(c)].next, ++i) {
162 if (!inner_literal_detail::walk(tree, c, st, i)) { // top-level child index = i
163 return inner_literal {};
164 }
165 }
166 }
167 else if (!inner_literal_detail::walk(tree, tree.root, st, 0)) { // a lone top-level node is child 0
168 return inner_literal {};
169 }
170 inner_literal_detail::flush(st); // the final run
172 return st.best;
173 }
174
178 inline ast build_prefix_ast(const ast& tree,
179 std::int32_t count)
180 {
181 ast prefix {tree};
182 const ast_node& root {prefix.nodes[static_cast<std::size_t>(prefix.root)]};
183 std::int32_t c {root.child};
184 for (std::int32_t i = 0; i + 1 < count; ++i) {
185 c = prefix.nodes[static_cast<std::size_t>(c)].next;
186 }
187 prefix.nodes[static_cast<std::size_t>(c)].next = -1; // truncate after the count-th top-level child
188 return prefix;
189 }
190} // namespace real::detail
191
192#endif // REAL_FRONTEND_INNER_LITERAL_HPP
Pattern text → AST, via a constexpr recursive-descent parser.
constexpr std::uint32_t score_run(std::span< const std::uint8_t > run)
Selectivity of a byte run: the sum of per-byte rarity (2000 - byte_frequency). A sum (rather than the...
constexpr bool walk(const ast &tree, std::int32_t idx, walk_state &st, std::int32_t top_child)
Walk one node, appending guaranteed-present literal bytes to st.run. top_child is the top-level conca...
constexpr void flush(walk_state &st)
Score the current byte run and keep it (with its prefix boundary) if it beats best,...
constexpr inner_literal extract_inner_literal(const ast &tree)
Extract the best required inner literal from a pattern's AST (a pure function on the node pool)....
constexpr std::uint16_t byte_frequency(std::uint8_t b)
Approximate static frequency of a byte in mixed English + source text (occurrences per 10000; higher ...
constexpr std::size_t inner_literal_max
The most bytes an inner literal keeps (a longer memmem target is diminishing returns and storage).
@ 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.
@ byte
One exact byte.
@ 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.
@ prefix
Anchored at the start position (Python re.match).
ast build_prefix_ast(const ast &tree, std::int32_t count)
Build the prefix sub-AST: the first count top-level concat children (count >= 1). Copies the tree and...
Search acceleration: pattern analysis and candidate-finding.
One AST node. Active fields depend on kind (noted per field).
Definition ast.hpp:79
std::int32_t child
First child (concat, repeat, alternation, group).
Definition ast.hpp:90
A parsed pattern: the node pool plus side tables.
Definition ast.hpp:164
std::vector< ast_node > nodes
The node pool; root indexes it.
Definition ast.hpp:165
std::int32_t root
Index of the root node.
Definition ast.hpp:170
Extraction state threaded through the walk: the growing byte run, the best literal so far,...
std::int32_t best_top
Top-level child where the winning run began.
std::int32_t run_top
Top-level child where the current run began (-1 = nested).
The best required inner literal of a pattern (the memmem candidate). len == 0 means the pattern decli...
std::uint32_t score
Selectivity: higher = rarer/longer = fewer memmem candidates.
constexpr bool found() const
std::array< std::uint8_t, 16 > bytes
REAL's version macros and the C++20 language-standard guard.