tur/re
pure-Turmeric regular expressions (POSIX ERE subset).
A small backtracking regex engine written entirely in Turmeric, so it runs on every path -- the AOT-compiled backend, the tur --interpret tree-walker, and the browser/WASM REPL (which shares the interpreter). The historical libc regcomp/regexec wrapper has been retired; there is no regex_t and no inline C here.
A compiled pattern is a Regex value (the parsed AST). re/compile returns it directly, so it flows unchanged between compiled and interpreted code. re/free / re/match-free / re/find-all-free survive as no-op shims for source compatibility -- results are ordinary GC/heap values now, not malloc'd C buffers.
Supported syntax (POSIX ERE subset): literals, ., character classes [...] / [^...] including [a-z] ranges and POSIX names ([:alpha:], [:digit:], [:alnum:], [:upper:], [:lower:], [:space:], [:blank:], [:xdigit:]), quantifiers * + ? and bounded {n} / {n,} / {n,m}, alternation |, grouping (...), anchors ^ $, and backslash-escaping of a metacharacter (\.). Not supported: backreferences, Perl shorthands (\d \w \b), lookaround, non-greedy.
Match semantics note: the engine reports leftmost-longest per start position (it collects every reachable end and takes the maximum), which coincides with POSIX leftmost-longest for the anchored/simple patterns in the test suite. Group captures are not yet threaded -- re/match returns the whole match only (Phase 3 of the port plan).
Since: Phase B1 (libc); pure-Turmeric rewrite 2026-07
RxCls
(defdata RxCls :copy (RxClsNil) (RxClsRange :int :int :RxCls))
Regex
(defdata Regex :copy (RChar :int) (RAny) (RClass :bool :RxCls) (RStar :Regex) (RPlus :Regex) (ROpt :Regex) (RConcat :Regex :Regex) (RAlt :Regex :Regex) (RGroup :Regex) (REmpty) (RNever) (RBegin) (REnd))
RxPos
(defdata RxPos :copy (RxNil) (RxCons :int :RxPos))
RxPair
(defdata RxPair (RxIP :int :int))
RxParse
(defdata RxParse (RxPR :Regex :int))
RxStrs
(defdata RxStrs (RxStrsNil) (RxStrsCons :cstr :RxStrs))
re-pos-append
(re-pos-append [a :RxPos b :RxPos] :RxPos)
re-pos-contains?
(re-pos-contains? [a :RxPos v :int] :bool)
re-pos-empty?
(re-pos-empty? [a :RxPos] :bool)
re-pos-len
(re-pos-len [a :RxPos] :int)
re-pos-max
(re-pos-max [a :RxPos acc :int] :int)
re-pos-dedup-go
(re-pos-dedup-go [a :RxPos seen :RxPos] :RxPos)
re-pos-dedup
(re-pos-dedup [a :RxPos] :RxPos)
re-range-list
(re-range-list [lo :int hi :int] :RxPos)
re-cstr-eq-loop
(re-cstr-eq-loop [a :cstr b :cstr i :int n :int] :bool)
re-cstr-eq?
(re-cstr-eq? [a :cstr b :cstr] :bool)
re-class-match?
(re-class-match? [items :RxCls b :int] :bool)
re-step-char
(re-step-char [starts :RxPos input :cstr len :int c :int] :RxPos)
re-step-any
(re-step-any [starts :RxPos input :cstr len :int] :RxPos)
re-step-class
(re-step-class [starts :RxPos input :cstr len :int neg :bool items :RxCls] :RxPos)
re-step-begin
(re-step-begin [starts :RxPos] :RxPos)
re-step-end
(re-step-end [starts :RxPos len :int] :RxPos)
re-run-k
(re-run-k [re :Regex starts :RxPos input :cstr len :int] :RxPos)
re-digit?
(re-digit? [c :int] :bool)
re-read-int
(re-read-int [input :cstr len :int pos :int acc :int] :RxPair)
re-repeat-n
(re-repeat-n [atom :Regex n :int] :Regex)
re-repeat-opt
(re-repeat-opt [atom :Regex n :int] :Regex)
re-parse-brace
(re-parse-brace [input :cstr len :int atom :Regex pos :int] :RxParse)
re-apply-quant
(re-apply-quant [input :cstr len :int ra :Regex pos :int] :RxParse)
re-parse-escape
(re-parse-escape [input :cstr len :int pos :int] :RxParse)
re-scan-name-end
(re-scan-name-end [input :cstr len :int pos :int] :int)
re-posix-ranges
(re-posix-ranges [name :cstr acc :RxCls] :RxCls)
re-posix-open?
(re-posix-open? [input :cstr len :int pos :int] :bool)
re-range-here?
(re-range-here? [input :cstr len :int pos :int] :bool)
re-re-parse-class-items
(re-re-parse-class-items [input :cstr len :int pos :int neg :bool acc :RxCls] :RxParse)
re-parse-class
(re-parse-class [input :cstr len :int pos :int] :RxParse)
re-concat-end?
(re-concat-end? [input :cstr len :int pos :int] :bool)
re-parse-go
(re-parse-go [input :cstr len :int pos :int mode :int acc :Regex] :RxParse)
re-find-from
(re-find-from [re :Regex input :cstr len :int from :int] :RxPair)
re-find-all-go
(re-find-all-go [re :Regex input :cstr len :int from :int] :RxStrs)
re-replace-go
(re-replace-go [re :Regex input :cstr len :int from :int src :ptr<void> repl :ptr<void> sb :ptr<void>] :void)
re/compile
(re/compile [pattern :cstr] :Regex)
compile a regular expression pattern into a Regex.
| pattern | NUL-terminated regex pattern cstr (POSIX ERE subset) |
A Regex value (the parsed AST). Parsing is total: a malformed pattern is parsed best-effort rather than rejected, so there is no error return.
(re/compile "([0-9]+)") ; => <Regex>
Since: Phase B2
re/match?
(re/match? [re :Regex input :cstr] :bool)
test whether a pattern matches anywhere in input.
| re | compiled Regex from re/compile | |
| input | input string cstr |
true if the pattern matches at some position, false otherwise.
(re/match? (re/compile "[0-9]+") "abc123") ; => true
Since: Phase B2
re/match
(re/match [re :Regex input :cstr] :(Option cstr))
return the leftmost-longest whole match as an Option.
Group captures are not yet threaded through the engine, so only the whole match is returned (index 0 in the old libc API). Use re/match? when you only need a boolean.
| re | compiled Regex from re/compile | |
| input | input string cstr |
(some matched-substring) on a match, or (none) if there is no match.
(re/match (re/compile "[0-9]+") "a12b") ; => (some "12")
Since: Phase B2 (pure-Turmeric rewrite)
re/find-all
(re/find-all [re :Regex input :cstr] :RxStrs)
all non-overlapping matches, left to right.
Advances past each match (by one byte on an empty match to avoid stalling), collecting the matched substrings.
| re | compiled Regex from re/compile | |
| input | input string cstr |
An RxStrs cons list of matched substrings (empty list if none).
(re/find-all (re/compile "[0-9]+") "a1b22c") ; => ("1" "22")
Since: Phase B2 (pure-Turmeric rewrite)
re/replace
(re/replace [re :Regex input :cstr replacement :cstr] :cstr)
replace the first match with a literal replacement.
| re | compiled Regex from re/compile | |
| input | input string cstr | |
| replacement | literal replacement cstr (no backreferences) |
A fresh cstr with the first match replaced, or a copy of input if there is no match.
(re/replace (re/compile "world") "hello world" "WORLD") ; => "hello WORLD"
Since: Phase B2
re/replace-all
(re/replace-all [re :Regex input :cstr replacement :cstr] :cstr)
replace all non-overlapping matches with a literal.
| re | compiled Regex from re/compile | |
| input | input string cstr | |
| replacement | literal replacement cstr (no backreferences) |
A fresh cstr with every match replaced.
(re/replace-all (re/compile "[0-9]") "a1b2c3" "X") ; => "aXbXcX"
Since: Phase B2
re/free
(re/free [re :Regex] :void)
no-op shim (a Regex is a GC/heap value, not a malloc'd handle).
| re | a Regex (ignored) |
Since: Phase B2 (no-op since the pure-Turmeric rewrite)
re/match-free
(re/match-free [m :(Option cstr)] :void)
no-op shim; the Option result needs no manual free.
re/find-all-free
(re/find-all-free [lst :RxStrs] :void)
no-op shim; the RxStrs result needs no manual free.
re/wrap-paren
(re/wrap-paren [p :cstr] :cstr)
internal: wrap one pattern as "(p)" (heap; caller frees).
re/union-acc
(re/union-acc [acc :cstr patterns :int] :cstr)
internal: right-fold the rest onto acc, joining with "|".
re/union-patterns
(re/union-patterns [patterns :int] :cstr)
build an alternation regex string from a cons list of patterns. Each pattern is wrapped in a parenthesised branch and joined with |. Returns a fresh cstr (caller frees), or 0 if the list is empty or contains a null element.
| patterns | :int cons list of cstr patterns (head = cstr pointer) |
A heap cstr containing the unioned pattern, or 0.
(re/union-patterns (cons "[A-Za-z]+" (cons "[0-9]+" 0))) ; => "([A-Za-z]+)|([0-9]+)"
Since: Phase RU1
re/compile-union
(re/compile-union [patterns :int] :Regex)
compile an alternation of multiple patterns into a Regex.
Convenience wrapper around re/union-patterns followed by re/compile. The intermediate pattern buffer is freed before returning.
| patterns | :int cons list of cstr patterns (head = cstr pointer) |
A compiled Regex on success, or (RNever) -- a regex that matches nothing -- if the input list is empty or contains a null element.
(re/compile-union (cons "[A-Za-z]+" (cons "[0-9]+" 0))) ; => <Regex>
Since: Phase RU2
re-autolink-hint
(re-autolink-hint)
internal marker triggering tur_string.c compilation.