Regular expressions with full Unicode support, whose patterns can be compiled while your
program compiles. compile builds one at run time; Static builds one at compile time and
types its named groups. find, captures, replace and split work on either.
Regex :: #import"Extensions/Regex";
// Compiled while the program compiles: a bad pattern is a compile error, and named// groups become typed fields (an integer for a run of digits, an enum for a few words).
Date :: Regex.Static("(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})");
date, ok := Regex.parse(Date, "2026-10-11"); // date.year is a u16, date.month a u8// Compiled at run time, for a pattern only known then.
user_pattern := "[a-z]+";
text := "one two";
re, compiled, error := Regex.compile(user_pattern);
for Regex.find_all(*re, text) print("% ", it.text);
Matching takes time linear in the text for every pattern: there are no backreferences and no
look-around, and the engines are automata, never an exponential backtracker.
A regex for another thread: it shares the compiled pattern (parse, programs and one-pass table) with re and has a search
cache of its own, so it costs far less than a second compile. Free it with free_regex, like any other regex.
Regex :: #import"Extensions/Regex";
re, _ := Regex.compile("[0-9]+");
copy := Regex.clone(*re);
Regex.free_regex(*re); // the clone keeps the pattern aliveassert(Regex.is_match(*copy, "room 101"));
Regex.free_regex(*copy);
Releases everything compile allocated for the regex: the parsed pattern, both programs and the search tables. For a regex
made by clone, that is its own search cache, and the pattern goes when the last regex sharing it is freed.
Regex :: #import"Extensions/Regex";
re, ok := Regex.compile("a+b");
assert(ok && Regex.is_match(*re, "caab"));
Regex.free_regex(*re);
assert(re.engine == null);
Every match, left to right, not overlapping. An empty match right where the previous match
ended is skipped, and in Unicode mode empty matches never split a UTF-8 sequence.
Replaces the first match. In replacement, $1 / ${1} is a group's text, $name /
${name} a named group's, and $$ a dollar sign; a group that did not match (or does not
exist) gives "". $name takes the longest run of letters, digits and _, so write
${1}st rather than $1st.
Like parse, into your own struct: each field takes the named group of the same name, and is a
string, integer, float, bool (true / false) or enum (a member's name, any case). A field
with no group or a group with no field is a compile error. False if the text does not match
or a group's text does not fit its field.
Compiles root. reverse builds the program that reads the text backwards (no captures
and no unanchored start: it is only run back from the end of a match).
Runs program over text[start .. end] (looks still see all of text). Anchored searches only
try start. Fills slots (the first slots.count of them, -1 for groups that did not take
part: pass two to find only where the match is) and
returns whether anything matched; the match is the leftmost one, and among those, the one
the pattern prefers.
The first match at exactly start within text[start .. end], like an anchored pike_search,
by depth-first search in priority order. Each (instruction, position) is tried once, so this
is linear too, but it needs instructions * (end - start + 1) bits of memory: use it for short
spans only (backtrack_fits).
Fills slots (-1 where a group did not take part) with the offsets of the match
text[s .. e] that starts at s, which must be the match the pattern prefers there. False if
the table finds no path that ends exactly at e.
The end of the leftmost-first match that starts at or after start (and exactly at start
if anchored), or -1. With earliest, stops at the first position where any match ends,
which answers "is there a match" sooner. gave_up means the DFA grew past its limit and the
caller has to use another matcher.
The step over non-ASCII byte text[at] in a DFA with a Unicode \b, through the column that
knows whether a word character starts at at and whether one ends with it.
For each of the first few positions of a match, the bytes it can have there, up to the
shortest match's length (looks are taken to hold, which only adds bytes).
A rare set of bytes that every match contains, and the bytes a match can have before the
first of them. Tries each ASCII byte together with its other case.
Builds the engine for pattern. The parse tree, the programs and the engine itself are allocated from a pool of their own
(Engine.pool), so free_regex can give them all back at once.
The leftmost-first match at or after start: its start and end, or end -1. earliest only
promises to say whether there is one (the end may be early and the start is not computed).
The letter a case-insensitive class stands for, written in lower case: (?i)y matches Y and y. Other
characters in the class must be outside ASCII (like the Kelvin sign for k).
Whether a node only matches numbers: an optional sign, digits, and for a float a . and more
digits. digits is the most integer digits it can match, -1 for no limit.
Statements that fill out (a *T the caller declared) from text and slots. Every field
must have a named group and every named group a field; anything else is a compile error.
A compiled pattern from compile. Plain copies of the struct share the compiled program and the search cache, which is not
safe to use from two threads at once: give each thread its own clone.
pattern: string
group_count: int
capture groups, not counting group 0 (the whole match)
The program has a Unicode \b, which needs to see whole characters: a non-ASCII byte's
column holds QUIT, and the search decodes the text there and steps through one of the
CHARACTER_COLUMNS copies of the byte's column instead, which says whether a word
character starts at the byte and whether one ends with it.
An unanchored start state usually goes back to itself on most bytes: the search is waiting
for the first byte of a match. A vector search jumps to the next place a match could start.
Without looks, any position will do as a restart (the start state does not depend on the byte
before), so the search can use bytes from further into the match, at a fixed distance from its
start: two of them, if no single one is rare. When every byte near the start is common, a rare
byte that every match must contain will do: find it, then walk back over the bytes a match can
have before it to where the match could start. With looks, it can only jump over bytes that keep
the DFA in its start state.
computed: bool
state: u32
the start state, or UNKNOWN when nothing is skipped
BYTES, SET and PAIR without looks: the bytes a match can have at each of its first
checks positions, as bit sets, checked before the DFA is given a candidate.
Every match is exactly checks bytes, any of the checked bytes at each position: a
candidate that fits the checks is the match, and the DFA need not run.