REAL
Regular Expression Algorithmic Library — constexpr C++20 regex
Loading...
Searching...
No Matches
compiler.hpp File Reference

AST → NFA program, via Thompson construction. More...

#include "real/version.hpp"
#include <algorithm>
#include <array>
#include <cstdint>
#include <vector>
#include "real/frontend/ast.hpp"
#include "real/core/charclass.hpp"
#include "real/core/config.hpp"
#include "real/engine/prefilter.hpp"
#include "real/frontend/inner_literal.hpp"
#include "real/core/program.hpp"
#include "real/unicode/unicode_fold.hpp"
#include "real/automata/utf8_ranges.hpp"
Include dependency graph for compiler.hpp:

Classes

class  real::detail::compiler
 Compiles an ast into a dynamic_program (NFA bytecode). More...
 

Namespaces

namespace  real
 REAL's public API: real::regex, real::static_regex, real::flags and the match/iterator types built on them.
 
namespace  real::detail
 DFA construction internals: subset construction over a flattened NFA. Not a stable API.
 

Functions

constexpr bool real::detail::is_any_non_ascii (const std::vector< code_range > &ranges)
 Whether ranges is exactly the whole non-ASCII space [U+0080, U+10FFFF] — the "any non-ASCII code point" shape emitted by compiler::emit_any_codepoint_class.
 
constexpr class_def real::detail::unicode_casefold (const class_def &in)
 Expands a character class to its Unicode simple case-fold closure (text-mode icase).
 
constexpr bool real::detail::node_nullable (const ast &tree, std::int32_t idx)
 True if the AST subtree rooted at idx can match the empty string. concat: every child nullable; alternation: some branch nullable; group: its body nullable; repeat: min == 0 or its body nullable; byte/klass/any: never (they always consume exactly one unit). empty/anchor/lookaround are always zero-width by construction — never consuming input as part of the surrounding match — so they are always nullable here; not an approximation for those three, the exact contribution of those node kinds to the enclosing match's width. Used by ast_has_nullable_captured_repeat to decide whether a capturing group's body is nullable.
 
constexpr bool real::detail::subtree_has_nullable_capturing_group (const ast &tree, std::int32_t idx)
 True if the AST subtree rooted at idx contains a CAPTURING group (group >= 0, i.e. not (?:...)) whose own body is nullable (node_nullable). Descends through every node kind that can nest a group (including a further repeat/lookaround) so a group need not be the direct child of the repeat this is called from — only transitively underneath it. Used only from ast_has_nullable_captured_repeat, on a repeat node's subtree.
 
constexpr bool real::detail::ast_has_nullable_captured_repeat (const ast &tree, std::int32_t idx)
 True if the AST rooted at idx contains a capturing group with a nullable body, transitively under a quantifier (any quantifier, ? included) — the frontend source of pattern_hints::nullable_captured_repeat (compiler::compile() reads this after analyze_program, the same AST-derived-hint slot as the inner-literal fields below). At each repeat node, checks its whole subtree for a nullable capturing group (subtree_has_nullable_capturing_group) — the group need not be the repeat's immediate child — and independently keeps walking for any other repeat elsewhere in the tree. Safe over-approximation: it does not prove the loop's empty iteration actually surfaces a divergent capture, only that the shape can (e.g. (\b|x)+ counts: \b is nullable by node_nullable, conservatively, same posture as empty_match_possible).
 
constexpr dynamic_program real::detail::compile (const ast &tree, flags compile_flags)
 Compiles tree to an NFA program (convenience over compiler).
 

Detailed Description

AST → NFA program, via Thompson construction.

The emitted program always has the shape save 0, <body>, save 1, match, so slots 0/1 delimit group 0 (the whole match).

Multi-codepoint semantics are compiled down to byte-level alternatives (RE2-style): . and negated classes expand to UTF-8 lead/continuation byte classes joined by split/jump, so the engine itself only ever steps one byte at a time, in lock-step — which preserves linear time.

Branch targets are emitted as placeholders and patched only through the patch_primary / patch_secondary helpers, never by rewriting emitted instructions wholesale.