The q* stdlib docs
qregex: regular expression matching
qregex is a compact regular-expression engine written from scratch with zero third-party dependencies. The pipeline is a recursive-descent parser that builds an AST, a compiler that emits a flat bytecode program, and a backtracking VM that executes it. It exposes three QQL built-ins — regex.test, regex.find, and regex.replace — and supports a practical subset of regex syntax.
Public built-ins#
| Function | Arguments | Returns | Behavior |
|---|---|---|---|
| regex.test | pattern, text | Bool | True if the pattern matches anywhere in the text. |
| regex.find | pattern, text | Str | The first matching substring, or "" if none. |
| regex.replace | pattern, text, replacement | Str | Every non-overlapping match replaced by the replacement string. |
Arguments are read as strings (an Int argument is stringified). All three search for an unanchored match unless you use the ^ and $ anchors. If the pattern fails to compile, regex.test/find return no match (false / ""), and regex.replace returns the text unchanged (identity).
Supported syntax#
- Literals and . (Any)
- Ordinary characters match themselves; . matches any single character.
- Character classes
- [a-z], [^...] negated; ranges with -; class members may be escapes like \d.
- Anchors
- ^ matches start of text, $ matches end of text.
- Alternation
- a|b chooses between alternatives.
- Groups
- (...) groups; (?:...) non-capturing prefix is accepted and ignored.
- Quantifiers
- * + ? are greedy; append ? for lazy (*?, +?, ??).
- Counted repetition
- {n}, {n,}, {n,m}; greedy or lazy with a trailing ?. An invalid brace is treated as a literal {.
- Escapes
- \d \w \s and their negations \D \W \S; whitespace escapes \n \t \r; any other escaped char is that literal character.
Class semantics: \d is ASCII digits, \w is alphanumeric or underscore (Unicode-aware alphanumeric), \s is whitespace; the uppercase forms are the negations. The whole pattern must parse to the end — trailing unparsed input such as a stray ) makes compilation fail.
How it runs#
pattern string
|
recursive-descent parser -> AST (Char, Any, Class, Alt, Star, Repeat, ...)
|
emit -> bytecode (Char, Any, Class, Bol, Eol, Jmp, Split, Match)
|
backtracking VM -> run from each start position until MatchThe VM is a backtracking interpreter: Split tries its first branch recursively and falls back to the second, which is how greedy vs lazy quantifiers and alternation are encoded. search tries each start offset from 0 to the end of the text and returns the first match span. To guard against pathological (catastrophic-backtracking) patterns, a single run is capped at 5,000,000 steps; exceeding that returns no match.
regex.replace scans left to right: at each position it tries to match; on a non-empty match it appends the replacement and skips past the match, otherwise it copies one character and advances. This replaces all non-overlapping matches.
Resource budget#
qregex is the heaviest stdlib package: each call compiles the pattern to a program, materializes the text as a Vec<char>, runs a backtracking search, and (for replace) builds a fresh output string. Cost scales with pattern plus text size, so every regex.* function declares an 8192-byte (8 KB) memory budget and zero storage.
Examples#
regex.find returns the first run of digits in the text.
Request
regex.find("\\d+", "abc42def")
# wire form:
{ "0": { "Str": "\\d+" }, "1": { "Str": "abc42def" } }Response
{ "result": { "Str": "42" } }Output
42Request
regex.test("^\\w+@\\w+\\.\\w+$", "a@b.com")
regex.replace("\\d+", "a1b22c333", "#")Response
{ "result": { "Bool": true } }
{ "result": { "Str": "a#b#c#" } }Output
true
a#b#c#