This library is in flux. APIs may change without notice. Generated from source with koruc 0.1.7 on 8/19/2026.
Regex
~import std/regexKoru Standard Library: Regex — compile-time, DFA-based pattern matching as
regex.kz · 2 tors · ~[comptime]
Koru Standard Library: Regex — compile-time, DFA-based pattern matching as · 40 more lines
Koru Standard Library: Regex — compile-time, DFA-based pattern matching as
CONTROL FLOW. Generalizes the Orisha-router idiom from routes to regex:
std/regex:match(subject) // bare implicit-expression arg
| `[a-z]+@[a-z]+` _ |> handle-email()
| `[0-9]+` _ |> handle-number()
| no-match |> reject()
`match` is NOT a keyword: it is an ordinary module event and must be
invoked qualified (`std/regex:match`). Pattern branches carry the matched
text (bind or discard with `_`); `no-match` carries nothing and binds
nothing.
NAMED GROUPS (`(?<name>...)`) make the pattern the branch's PAYLOAD SCHEMA:
std/regex:match(line)
| `(?<l>[0-9]+)x(?<w>[0-9]+)x(?<h>[0-9]+)` { l: i64, w: i64, h: i64 } |> …
Groups deliver through the shape-destructure at the binding position; a
TYPE on a field is a CONVERSION at the splice (text→int dissolves here).
A plain binding takes ALL groups as one struct of text slices. STRICT 1:1:
destructure fields ↔ named groups, both ways, compile-time — unwanted
captures are spelled `(...)` (non-capturing) in the pattern. Groups under
quantifiers or alternation are rejected (no single span exists — same
doctrine as backrefs). Capture matching is a Pike VM over the tagged NFA:
still linear-time, still zero backtracking, still ReDoS-immune.
Each `…` raw-pattern branch is compiled — at COMPILE TIME — to a specialized
DFA matcher (src/regex_engine.zig). Matching is straight-line, linear-time,
ReDoS-immune by construction (no backtracking, ever). The branches dispatch
like a switch: first matching pattern wins; `no-match` is the fallback.
`match` semantics (cut 1): FULL match — the whole input must match a pattern.
Dispatch: the transform sets `inline_body` to an if/else-if chain over the
compiled matchers, with `__koru_continue_<idx>` markers for the branch
bodies. The `//@koru:inline_stmt` marker routes it down the emitter's
statement path, whose marker resolver hands off to each continuation —
match dispatch IS continuation-passing (first pattern wins, body runs once),
not a union value for the expand-switch. E2E pinned by 640_001.
// The event declares the USER surface: any raw-name branch (the name IS the
// pattern, byte-for-byte) with a bindable payload, plus an optional void
// `no-match` fallback. The shape checker enforces this contract — binding
// rule included — with zero match-specific code; the transform's output
// needs no exemption.
//
// The `[transform]` annotation on the PROC is what makes this a transform:
// a transform IS a proc on an ordinary event. Its machine interface
// (invocation/item/program/allocator → SiteResult) is the proc-return
// convention, not part of the event. The event keeps `[transform]` too —
// downstream passes read it as "implemented by a transform" (deferred
// bindings, comptime-flow skipping, variant emission).
~[comptime|transform] pub tor match {
expr: Expression,
reporter: std/compiler:*ErrorReporter
}
| * *
| ?no-match// SCAN - unanchored, streaming regex: `!` (each) where match uses `|` (once).
// `match` finds ONE whole-input match (anchored); `scan` finds EVERY
// non-overlapping occurrence as an effect stream — it fires its `!` pattern
// branch per hit (with the same named-group destructure match uses), then runs
// the optional `| done` continuation once. This is the terminal layer of the
// parser stack: a lexer in a few lines. Built on the engine's leftmost-longest
// unanchored search: the compiled SEARCH fn locates each span, and the same
// captures matcher `match` uses extracts the groups from that span. The `!`
// branch body is spliced once via `__koru_inline_<idx>` — INSIDE the loop it
// therefore runs per match, which is exactly effect-branch semantics. Single
// pattern for now (multi-pattern scan is pinned as a follow-up); pattern
// branches MUST be effect `!` (dispatch-into-each-match), mirroring the router.
~[comptime|transform] pub tor scan {
input: string,
reporter: std/compiler:*ErrorReporter
}
! * *
| ?done