Standard Library
namespace regex
Regular expressions: the Regex library and its namespace-level helpers.
since 0.1.0-alpha.1linuxwindowswasm
Overview
The regex namespace holds one-call conveniences that compile a pattern (through a small cache of recently used patterns) and run it: compile, isMatch, find, matches, replace, split and count, each taking the input string first and the pattern second, with an optional RegexOptions or flag string ("i", "m", "s") after. escape turns literal text into a pattern that matches exactly that text. It also holds the lower-level compiled-program boundary (compileProgram and the program... functions) for patterns that are compiled once, ahead of time.
Matching is byte-oriented and runs in time linear in the input: there is no backtracking, so no pattern can make a match run away. Backreferences and lookaround are not supported.
A pattern that is a literal in the source is code: a malformed one throws RegexException. A pattern that arrives as data should go through regex::compile, which returns None instead of throwing.
Description
The regex namespace holds the matching engine behind Regex, plus helper functions that take the
pattern as an argument. It is written entirely in Leviathan and needs no native support, so it
behaves the same on every platform. Most code uses the Regex type (see its entry) and reaches this
namespace only for the helpers and for compile-time patterns.
Linear time. The engine never backtracks. Matching takes time proportional to the length of the
input times the size of the compiled pattern, whatever the pattern is, so a pattern supplied by a
user cannot trigger runaway matching. A pattern such as ^(a+)+$, which freezes a backtracking
engine on a string of many as followed by !, is answered at once.
A pattern that would defeat a backtracking engine
Regex nested = Regex("^(a+)+$");
string input = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!";
console.writeln(nested.isMatch(input));
Regex clash = Regex("(x+x+)+y");
console.writeln(clash.isMatch("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
false
false
Syntax. The engine reads these constructs:
- literals and backslash escapes such as
\.,\(and\{; ., which matches any one byte except a newline unless thedotAlloption is on;- character classes
[abc],[a-z],[^abc], and the shorthands\d,\wand\swith their negations\D,\Wand\S, which also work inside a class ([\w-]); - alternation
a|b; - groups: capturing
(...), non-capturing(?:...)and named(?<name>...); - greedy quantifiers
*,+,?,{n},{n,},{n,m}, and their lazy forms with a trailing?; - anchors
^and$, which also match at line breaks in multiline mode, and the word boundary\b.
Backreferences and lookaround ((?=, (?!, (?<=) are not supported: a lookaround is rejected as a
malformed pattern, and \1 is not a backreference (it matches the digit 1 itself). Inline option groups such as (?i) are also
rejected; pass options through RegexOptions or the flags string.
Everything is byte-oriented. Offsets and lengths count bytes, and . matches one byte, so a
character that takes two bytes in UTF-8 is matched by ^..$ and not by ^.$.
Syntax and byte semantics
console.writeln(regex::isMatch("foo bar", "\\bbar\\b"));
console.writeln(regex::isMatch("foobar", "\\bbar\\b"));
Match? lazy = regex::find("aaa", "a+?");
if (lazy != None) { console.writeln(lazy.value); }
Match? greedy = regex::find("aaa", "a+");
if (greedy != None) { console.writeln(greedy.value); }
console.writeln(regex::isMatch("a-b", "^[\\w-]+$"));
console.writeln(regex::isMatch("é", "^.$"));
console.writeln(regex::isMatch("é", "^..$"));
console.writeln(regex::compile("a(?=b)") == None);
true
false
a
aaa
true
false
true
true
One-call helpers
The functions compile, isMatch, find, matches, replace, split and count take the
string first and the pattern second: regex::isMatch(s, pattern). Each also has a form with a
RegexOptions value and a form with a flags string after the pattern, and replace also accepts a
(Match) => string function in place of a replacement string. They compile the pattern on every call,
helped by a small cache of recently used patterns, so for a pattern used in a loop, build one Regex
instead. As with the Regex constructor, a malformed pattern passed to any helper other than
compile throws RegexException; regex::compile returns None.
regex::escape(s) returns s with every pattern metacharacter quoted, so it can be embedded as
literal text in a larger pattern.
Helpers and escape
console.writeln(regex::count("a1b2c3", "[0-9]"));
console.writeln(regex::isMatch("ABC", "abc"));
console.writeln(regex::isMatch("ABC", "abc", "i"));
console.writeln(regex::replace("a b c", "\\s+", "_"));
console.writeln(regex::split("a1b22c", "\\d+"));
console.writeln(regex::escape("1+1=2 (really)?"));
3
false
true
a_b_c
[a, b, c]
1\+1=2 \(really\)\?
Compiling a constant pattern at build time
regex::compileProgram(pattern, flags) turns a pattern into a flat Array<int> program, and
Regex::FromProgram(program) wraps a program as a Regex in constant time. Because
compileProgram is pure, a comptime declaration runs it while the program is being built. A
malformed constant pattern then fails the build at the line of the pattern, not as a surprise at run
time, and no parsing is left to do when the program starts. A compiled Regex also has
isMatchAll and countAll, which test a whole array of strings with one prepared engine.
A comptime pattern applied to a column
comptime Array<int> EMAIL_P = regex::compileProgram("^[\\w.+-]+@[\\w-]+\\.[\\w.]+$", "");
Regex emailRe = Regex::FromProgram(EMAIL_P);
console.writeln(emailRe.isMatch("ann@example.com"));
console.writeln(emailRe.isMatch("not an email"));
Array<string> column = ["a@b.co", "nope", "x@y.org"];
console.writeln(emailRe.isMatchAll(column));
console.writeln(emailRe.countAll(column));
true
false
[true, false, true]
[1, 0, 1]
Rules
- Matching time is linear in the input length times the compiled pattern's size; there is no backtracking.
- Backreferences and lookaround are not supported; inline
(?i)-style groups are rejected. - Offsets and lengths are in bytes.
compileProgramandRegex::FromProgramare the boundary between pattern text and matching; a program is anArray<int>and can be built once and reused.- A constant pattern passed to a
comptimecall is checked when the program is built.
Examples
Where a quantifier belongs
Array<string> words = regex::split("one, two,three ,four", "\\s*,\\s*");
console.writeln(words);
console.writeln(regex::count("banana", "an"));
console.writeln(regex::replace("2026-10-02", "-", "/"));
[one, two, three, four]
2
2026/10/02
Notes
The pattern language is deliberately close to the common core of Perl, C# and JavaScript, minus the features that need backtracking.
Examples
Conveniences in one place
console.writeln(regex::isMatch("order 66", r"\d+"));
Match? m = regex::find("key=value", r"(\w+)=(\w+)");
if (m != None) {
console.writeln(m.group(1).value);
console.writeln(m.group(2).value);
}
console.writeln(regex::replace("a1b22c", r"\d+", "#"));
console.writeln(regex::split("a, b;c", r"[,;]\s*"));
console.writeln(regex::count("banana", "an"));
console.writeln(regex::escape("1+1=2?"));
true
key
value
a#b#c
[a, b, c]
2
1\+1=2\?
Functions
compile
compile(string pattern) -> Regex | NoneCompile a pattern that comes from data, without throwing.
Use it for patterns read from a file, a configuration or user input. For a pattern written in the source, the Regex constructor is the better choice because it fails loudly.
Parameters
- pattern
- The pattern text.
Returns
The compiled Regex, or None when the pattern is malformed.
Examples
A good and a bad pattern
Regex? r = regex::compile(r"(\d+)");
if (r != None) console.writeln(r.groupCount());
console.writeln(regex::compile("(") == None);
Regex? ci = regex::compile("abc", "i");
if (ci != None) console.writeln(ci.isMatch("xABCx"));
1
true
true
compile(string pattern, RegexOptions options) -> Regex | NoneCompile a pattern that comes from data, with options, without throwing.
Parameters
- pattern
- The pattern text.
- options
- The options to apply.
Returns
The compiled Regex, or None when the pattern is malformed.
compile(string pattern, string flags) -> Regex | NoneCompile a pattern that comes from data, with a flag string, without throwing.
Parameters
- pattern
- The pattern text.
- flags
- Any of the letters
i,mands, in any order;""for none.
Returns
The compiled Regex, or None when the pattern is malformed or flags contains another character.
See also: Regex
compileProgram
compileProgram(string pattern, string flags) -> Array<int>Compile a pattern into a self-contained program that the program... functions run.
The result is an ordinary Array<int> holding everything the matcher needs, so it can be stored, passed around and reused for any number of inputs. The function is pure, and a call with constant arguments is evaluated when the program is built: written as a comptime value, a malformed constant pattern is reported at the source line of the pattern instead of when the program runs. Most code should use Regex and Match instead and reach this function only for ahead-of-time compilation (wrap the result with Regex::FromProgram).
The syntax covers literals and escapes, ., character classes, \d \w \s (and the uppercase negations), alternation, capturing, non-capturing and named groups, greedy and lazy quantifiers, anchors, word boundaries. Backreferences and lookaround are not supported. Offsets and lengths are counted in bytes.
Parameters
- pattern
- The pattern text.
- flags
- Zero or more of
i(ignore ASCII case),m(^and$also match at line breaks) ands(.also matches a line break), in any order;""for none.
Returns
The compiled program.
Throws
RuntimeException- when the pattern is malformed or
flagscontains another character; the message names the problem and ends withat offset N.
Examples
Compile once, then match
Array<int> prog = regex::compileProgram(r"(\d+)-(\d+)", "");
console.writeln(regex::programIsMatch(prog, "call 555-1234 now"));
console.writeln(regex::programIsMatch(prog, "no digits"));
Array<int> ic = regex::compileProgram("hello", "i");
console.writeln(regex::programIsMatch(ic, "Say HELLO"));
try {
regex::compileProgram("a(b", "");
} catch (RuntimeException e) {
console.writeln("caught: ${e.message}");
}
true
false
true
caught: regex: unterminated group at offset 3
See also: FromProgram, programIsMatch
count
count(string s, string pattern) -> intCount the non-overlapping matches of a pattern in a string.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
Returns
The number of matches.
Throws
RegexException- when the pattern is malformed.
Examples
Counting
console.writeln(regex::count("a1b22", r"\d"));
console.writeln(regex::count("banana", "an"));
console.writeln(regex::count("AaA", "a", "i"));
3
2
3
count(string s, string pattern, RegexOptions o) -> intCount the non-overlapping matches of a pattern in a string, with options.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
- o
- The options to apply.
Returns
The number of matches.
Throws
RegexException- when the pattern is malformed.
count(string s, string pattern, string flags) -> intCount the non-overlapping matches of a pattern in a string, with a flag string.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
- flags
- Any of the letters
i,mands, in any order;""for none.
Returns
The number of matches.
Throws
RegexException- when the pattern is malformed or
flagscontains another character.
escape
escape(string literal) -> stringTurn text into a pattern that matches exactly that text.
Every metacharacter (\ . ^ $ | ( ) [ ] { } * + ?) is preceded by a backslash. Use it to put user-supplied text inside a larger pattern.
Parameters
- literal
- The text to match literally.
Returns
The escaped pattern.
Examples
Matching text that contains metacharacters
console.writeln(regex::escape("1+1=2?"));
Regex e = Regex(regex::escape("1+1=2"));
console.writeln(e.isMatch("so 1+1=2 ok"));
console.writeln(regex::isMatch("11=2", regex::escape("1+1=2")));
1\+1=2\?
true
false
find
find(string s, string pattern) -> Match | NoneFind the first match of a pattern in a string.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
Returns
The first match, or None when there is none.
Throws
RegexException- when the pattern is malformed.
Examples
Finding with and without options
Match? m = regex::find("key=value", r"(\w+)=(\w+)");
if (m != None) console.writeln(m.group(2).value);
Match? line = regex::find("a\nb", "^b", "m");
if (line != None) console.writeln(line.index);
Match? dot = regex::find("a\nb", "a.b", RegexOptions(dotAll: true));
if (dot != None) console.writeln(dot.length);
console.writeln(regex::find("zzz", r"\d") == None);
value
2
3
true
find(string s, string pattern, RegexOptions o) -> Match | NoneFind the first match of a pattern in a string, with options.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
- o
- The options to apply.
Returns
The first match, or None when there is none.
Throws
RegexException- when the pattern is malformed.
find(string s, string pattern, string flags) -> Match | NoneFind the first match of a pattern in a string, with a flag string.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
- flags
- Any of the letters
i,mands, in any order;""for none.
Returns
The first match, or None when there is none.
Throws
RegexException- when the pattern is malformed or
flagscontains another character.
isMatch
isMatch(string s, string pattern) -> boolTest whether a pattern matches anywhere in a string.
The pattern is compiled on each call; compiled patterns are kept in a small cache, so repeating the same pattern is cheap. For many strings and one pattern, a Regex object is clearer.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
Returns
true when some part of s matches.
Throws
RegexException- when the pattern is malformed.
Examples
Testing with and without options
console.writeln(regex::isMatch("order 66", r"\d+"));
console.writeln(regex::isMatch("HELLO", "hello"));
console.writeln(regex::isMatch("HELLO", "hello", "i"));
console.writeln(regex::isMatch("HELLO", "hello", RegexOptions(ignoreCase: true)));
try {
regex::isMatch("x", "(");
} catch (RegexException e) {
console.writeln(e.message);
}
true
false
true
true
regex: unterminated group at offset 1
isMatch(string s, string pattern, RegexOptions o) -> boolTest whether a pattern matches anywhere in a string, with options.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
- o
- The options to apply.
Returns
true when some part of s matches.
Throws
RegexException- when the pattern is malformed.
isMatch(string s, string pattern, string flags) -> boolTest whether a pattern matches anywhere in a string, with a flag string.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
- flags
- Any of the letters
i,mands, in any order;""for none.
Returns
true when some part of s matches.
Throws
RegexException- when the pattern is malformed or
flagscontains another character.
matches
Find every non-overlapping match of a pattern in a string.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
Returns
The matches in order; empty when there are none.
Throws
RegexException- when the pattern is malformed.
Examples
All matches
console.writeln(regex::matches("a1b22", r"\d+").length());
console.writeln(regex::matches("A1b22", "[a-z]", "i").length());
for (Match m in regex::matches("x=1, y=22", r"\d+")) console.writeln(m.value);
2
2
1
22
matches(string s, string pattern, RegexOptions o) -> Array<Match>Find every non-overlapping match of a pattern in a string, with options.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
- o
- The options to apply.
Returns
The matches in order; empty when there are none.
Throws
RegexException- when the pattern is malformed.
Find every non-overlapping match of a pattern in a string, with a flag string.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
- flags
- Any of the letters
i,mands, in any order;""for none.
Returns
The matches in order; empty when there are none.
Throws
RegexException- when the pattern is malformed or
flagscontains another character.
programCount
programCount(Array<int> program, string input) -> intCount the non-overlapping matches of a compiled program in the input.
An empty match is counted once at its position and the scan then moves one byte forward, so the count always terminates.
Parameters
- program
- A program from
compileProgram. - input
- The text to search.
Returns
The number of matches.
Throws
RuntimeException- when
programis not a program produced bycompileProgram.
Examples
Counting numbers
Array<int> prog = regex::compileProgram(r"\d+", "");
console.writeln(regex::programCount(prog, "a1 b22 c333"));
console.writeln(regex::programCount(prog, "none"));
3
0
See also: count
programCountAll
Count the matches of a compiled program in every string of an array.
The program is set up once and reused for the whole array.
Parameters
- program
- A program from
compileProgram. - rows
- The strings to search.
Returns
One count per row, in order.
Throws
RuntimeException- when
programis not a program produced bycompileProgram.
Examples
Counting words per row
Array<int> words = regex::compileProgram(r"[a-z]+", "");
console.writeln(regex::programCountAll(words, ["one two", "", "a b c"]));
[2, 0, 3]
See also: countAll
programFind
Find the first match of a compiled program and return its capture positions.
The result holds start and end byte offsets in pairs: slots 0 and 1 are the whole match, then two slots for each capture group in order. A group that did not take part in the match has -1 in both of its slots. When there is no match the array is empty. Most code should use Regex.find, which wraps these numbers in a Match.
Parameters
- program
- A program from
compileProgram. - input
- The text to search.
Returns
The capture positions, or an empty array when nothing matches.
Throws
RuntimeException- when
programis not a program produced bycompileProgram.
Examples
Reading capture positions
Array<int> prog = regex::compileProgram(r"(\d+)-(\d+)", "");
console.writeln(regex::programFind(prog, "call 555-1234 now"));
console.writeln(regex::programFind(prog, "none").length());
[5, 13, 5, 8, 9, 13]
0
See also: find
programIsMatch
programIsMatch(Array<int> program, string input) -> boolTest whether a compiled program matches anywhere in the input.
Parameters
- program
- A program from
compileProgram. - input
- The text to search.
Returns
true when some part of input matches.
Throws
RuntimeException- when
programis not a program produced bycompileProgram.
Examples
Testing two inputs
Array<int> prog = regex::compileProgram(r"^\d{3}$", "");
console.writeln(regex::programIsMatch(prog, "123"));
console.writeln(regex::programIsMatch(prog, "1234"));
true
false
See also: compileProgram
programIsMatchAll
Test a compiled program against every string of an array.
The program is set up once and reused for the whole array, which is cheaper than testing the strings one by one.
Parameters
- program
- A program from
compileProgram. - rows
- The strings to test.
Returns
One bool per row, in order: true where the row matches.
Throws
RuntimeException- when
programis not a program produced bycompileProgram.
Examples
Filtering a column
Array<int> prog = regex::compileProgram(r"^\d{3}$", "");
Array<string> rows = ["123", "12", "abc", "999"];
console.writeln(regex::programIsMatchAll(prog, rows));
[true, false, false, true]
See also: isMatchAll
replace
replace(string s, string pattern, (Match) => string fn) -> stringReplace every match of a pattern using a function that builds each replacement.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
- fn
- Called once per match; returns the replacement text.
Returns
The text with every match replaced.
Throws
RegexException- when the pattern is malformed.
Examples
Replacing with a function
console.writeln(regex::replace("a1b22", r"\d+", (Match m) => "<${m.value}>"));
console.writeln(regex::replace("a1B22", "b", (Match m) => "[${m.value}]", "i"));
a<1>b<22>
a1[B]22
replace(string s, string pattern, (Match) => string fn, RegexOptions o) -> stringReplace every match of a pattern using a function, with options.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
- fn
- Called once per match; returns the replacement text.
- o
- The options to apply.
Returns
The text with every match replaced.
Throws
RegexException- when the pattern is malformed.
replace(string s, string pattern, (Match) => string fn, string flags) -> stringReplace every match of a pattern using a function, with a flag string.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
- fn
- Called once per match; returns the replacement text.
- flags
- Any of the letters
i,mands, in any order;""for none.
Returns
The text with every match replaced.
Throws
RegexException- when the pattern is malformed or
flagscontains another character.
replace(string s, string pattern, string replacement) -> stringReplace every match of a pattern with a replacement string.
The replacement string may refer to groups: $0 is the whole match, $1 to $99 are groups by number (a two-digit number is read when that group exists), ${name} is a named group, $$ is a literal $, and a $ followed by anything else, or at the end, is a literal $. A group that did not take part is replaced by "". In Leviathan source write the replacement as a raw string (r"${name}") or escape it ("\${name}"), because ${...} inside an ordinary string is interpolation.
Parameters
- s
- The text to search.
- pattern
- The pattern text.
- replacement
- The replacement text, with the
$references described above.
Returns
The text with every match replaced.
Throws
RegexException- when the pattern is malformed, or when the replacement names a group the pattern does not declare, refers to a group number the pattern does not have, or has a
${with no closing}.
Examples
Replacing text
console.writeln(regex::replace("a1b22c", r"\d+", "#"));
console.writeln(regex::replace("Hello hello", "hello", "bye", "i"));
console.writeln(regex::replace("a=1 b=2", r"(\w+)=(\w+)", r"$2=$1"));
console.writeln(regex::replace("cost 5", r"\d", r"$$$0"));
a#b#c
bye bye
1=a 2=b
cost $5
replace(string s, string pattern, string replacement, RegexOptions o) -> stringReplace every match of a pattern with a replacement string, with options.
The replacement string uses the same $ references as replace(s, pattern, replacement).
Parameters
- s
- The text to search.
- pattern
- The pattern text.
- replacement
- The replacement text, with
$references. - o
- The options to apply.
Returns
The text with every match replaced.
Throws
RegexException- when the pattern is malformed, or when the replacement names a group the pattern does not declare, refers to a group number the pattern does not have, or has a
${with no closing}.
replace(string s, string pattern, string replacement, string flags) -> stringReplace every match of a pattern with a replacement string, with a flag string.
The replacement string uses the same $ references as replace(s, pattern, replacement).
Parameters
- s
- The text to search.
- pattern
- The pattern text.
- replacement
- The replacement text, with
$references. - flags
- Any of the letters
i,mands, in any order;""for none.
Returns
The text with every match replaced.
Throws
RegexException- when the pattern is malformed or
flagscontains another character, or when the replacement names a group the pattern does not declare, refers to a group number the pattern does not have, or has a${with no closing}.
split
split(string s, string pattern) -> Array<string>Split a string at every match of a pattern.
When the pattern has capture groups, the text of each group is put into the result after the piece that precedes the match.
Parameters
- s
- The text to split.
- pattern
- The pattern text.
Returns
The pieces between the matches; a string with no match gives one piece, the whole string.
Throws
RegexException- when the pattern is malformed.
Examples
Splitting
console.writeln(regex::split("a1b2c", r"\d"));
console.writeln(regex::split("aXbxc", "x", "i"));
console.writeln(regex::split("a, b;c", r"[,;]\s*"));
[a, b, c]
[a, b, c]
[a, b, c]
split(string s, string pattern, RegexOptions o) -> Array<string>Split a string at every match of a pattern, with options.
Parameters
- s
- The text to split.
- pattern
- The pattern text.
- o
- The options to apply.
Returns
The pieces between the matches.
Throws
RegexException- when the pattern is malformed.
split(string s, string pattern, string flags) -> Array<string>Split a string at every match of a pattern, with a flag string.
Parameters
- s
- The text to split.
- pattern
- The pattern text.
- flags
- Any of the letters
i,mands, in any order;""for none.
Returns
The pieces between the matches.
Throws
RegexException- when the pattern is malformed or
flagscontains another character.
See also
- Regex — A compiled regular expression.
- Match — A successful match: where it is, what it matched, and its capture groups.
- RegexException — The exception thrown for a malformed pattern, and for a malformed or out-of-range reference in a replacement string.