Turmeric's tur/re module (stdlib/re.tur) is a regular expression engine
written entirely in Turmeric. Because it is pure Turmeric with no inline C,
it runs on every backend: the AOT-compiled path, the tur --interpret
tree-walker, and the browser/WASM REPL.
A compiled pattern is a Regex value -- the parsed AST, not a C regex_t.
re/compile returns it directly, so it flows unchanged between compiled and
interpreted code. There is no malloc'd C buffer to manage: re/free and
the *-free shims are no-ops kept for source compatibility.
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 patterns in the test suite. Group captures are not
yet threaded -- re/match returns the whole match only.
The engine implements a POSIX ERE subset:
| Feature | Syntax | Example |
|---|---|---|
| Literals | any non-metacharacter | abc |
| Any character | . |
a.c |
| Character class | [...] |
[a-z] |
| Negated class | [^...] |
[^0-9] |
| POSIX class | [:alpha:] etc. |
[[:digit:]] |
| Star | * |
a* |
| Plus | + |
a+ |
| Optional | ? |
a? |
| Bounded repeat | {n}, {n,}, {n,m} |
a{2,4} |
| Alternation | | |
cat|dog |
| Grouping | (...) |
(ab)+ |
| Begin anchor | ^ |
^abc |
| End anchor | $ |
abc$ |
| Escape | \ + metacharacter |
\. |
Supported POSIX class names: [:alpha:], [:digit:], [:alnum:],
[:upper:], [:lower:], [:space:], [:blank:], [:xdigit:].
Not supported: backreferences, Perl shorthands (\d, \w, \b),
lookaround, non-greedy quantifiers.
;; Compile once, match many times
(def rx (re/compile "[0-9]+"))
(println (re/match? rx "abc123")) ; => true
(println (re/match rx "abc123")) ; => (some "123")
(println (re/find-all rx "a1b22c")) ; => ("1" "22")
(println (re/replace rx "a1b2" "X")) ; => "aXbX"
Compile a pattern string into a Regex value.
(def rx (re/compile "([A-Za-z]+)[0-9]*"))
The Regex is a GC-managed heap value. re/free is a no-op shim kept for
source compatibility -- there is nothing to free.
Test whether a pattern matches anywhere in the input. Returns bool.
(re/match? (re/compile "[0-9]+") "abc123") ; => true
(re/match? (re/compile "[0-9]+") "abc") ; => false
Return the leftmost-longest whole match as an Option cstr. Returns
(some "match") if the pattern matches, None if it does not.
(re/match (re/compile "[0-9]+") "a12b") ; => (some "12")
(re/match (re/compile "[0-9]+") "abc") ; => None
Return all non-overlapping matches, left to right, as an RxStrs list.
Each element is a cstr substring of the input.
(re/find-all (re/compile "[0-9]+") "a1b22c333")
; => ("1" "22" "333")
Replace the first match with a literal replacement string. The replacement
is a literal cstr -- no backreferences. Returns a fresh cstr; if there
is no match, returns a copy of the input.
(re/replace (re/compile "world") "hello world" "WORLD")
; => "hello WORLD"
Replace all non-overlapping matches with a literal replacement string.
(re/replace-all (re/compile "[0-9]") "a1b2c3" "X")
; => "aXbXcX"
Compile an alternation of multiple patterns into a single Regex. Takes a
cons list of cstr patterns and joins them with |, wrapping each in a
parenthesised branch. Returns RNever (a regex that matches nothing) if
the list is empty or contains a null element.
(re/compile-union (cons "[A-Za-z]+" (cons "[0-9]+" 0)))
; matches letters or digits
(def rx (re/compile "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"))
(println (re/find-all rx "contact a@b.com or c@d.io"))
; => ("a@b.com" "c@d.io")
(def rx (re/compile "^[0-9]+$"))
(println (re/match? rx "12345")) ; => true
(println (re/match? rx "12a45")) ; => false
(def rx (re/compile "[[:alpha:]][[:alnum:]]*"))
(println (re/find-all rx "foo 123 bar45 _baz"))
; => ("foo" "bar45" "baz")
(def rx (re/compile "[^0-9]+"))
(println (re/find-all rx "abc123def456"))
; => ("abc" "def")
The engine is a backtracking matcher written in pure Turmeric. The matcher
(re-run-k) walks the parsed Regex AST against the input cstr,
tracking the set of reachable positions (RxPos) at each step. For
RStar, it iterates until the position set stops growing (fixpoint with
deduplication), which prevents infinite loops on patterns like (a*)*.
The parser (re-parse-go) is a single recursive function with six modes
(alternation, concatenation, repeat, atom) that descends through the
pattern string. Bounded repeats ({n}, {n,}, {n,m}) desugar to
concatenations of the atom and optional/star wrappers.
Results are ordinary GC/heap values. The re/free, re/match-free, and
re/find-all-free functions are no-op shims retained for source
compatibility with the old libc regcomp/regexec wrapper -- there is no
regex_t and no malloc'd C buffer to manage.
cstr vs str vs String and when to use each is
covered in strings-guide.md.