std.re
import std.re
re.test("^a.c$", "abc") # true
re.find("[0-9]+", "abc 123 def") # "123"
re.find_all("[0-9]+", "1 a 2 b 3") # ["1", "2", "3"]
re.captures("(\\w+)@(\\w+)", "ada@ecko") # ["ada@ecko", "ada", "ecko"]
re.captures_all(pattern, text)
re.split("[,;]", "a,b;c") # ["a", "b", "c"]
re.replace("[0-9]", "#", "a1b2") # "a#b#"
re.replace_first("[0-9]", "#", "a1b2") # "a#b2"
Captures
captures returns a list where index 0 is the whole match and the rest are the groups, in order. A pattern that does not match returns null, so check before indexing:
c = re.captures(pattern, text)
unless c == null { print(c[1]) }
captures_all returns one such list per match.
Escaping in patterns
A regex backslash needs escaping in a normal Ecko string, so \d is written "\\d". Raw strings avoid the doubling:
re.test(r"\d+", "123")
Raw strings also suppress {expr} interpolation, which matters because {2,3} is a regex repetition and would otherwise be read as an interpolation.
Errors
An invalid pattern raises { kind: "parse" }, catchable like any other operational failure. Compile-time validation is not possible for a pattern built at runtime, so validate patterns that come from configuration or user input:
try { re.test(user_pattern, s) } catch (e) { reject("bad pattern") }
When not to reach for regex
Parsing HTML, XML or JSON. Use html, xml or std.json - nested structure is not a regular language, and a pattern that appears to work will fail on real input.
For fixed-string work, contains, starts_with, split and string.partition are faster and clearer than a pattern.