Strings
String Literals #
let greeting = "Hello, World!"
let single = "It's a string"
String Interpolation #
Embed expressions inside strings using {}:
let name = "Zolo"
let msg = "Hello, {name}!" // "Hello, Zolo!"
let x = 10
let y = 20
let sum = "{x} + {y} = {x + y}" // "10 + 20 = 30"
Expressions in Interpolation #
Any expression can go inside {}:
let items = [1, 2, 3]
print("Count: {Array.len(items)}") // "Count: 3"
print("Double: {items[0] * 2}") // "Double: 2"
print("Greeting: {if loud { "HI!" } else { "hi" }}")
Nested Interpolation #
Interpolated expressions can contain their own strings:
let result = "User: {get_user("admin").name}"
Multiline Strings #
Use triple quotes """ for multiline strings:
let text = """
This is a
multiline string
with indentation preserved
"""
Tagged Templates #
Tagged templates let you prefix a string with a tag name, enabling DSL-like syntax:
let id = 42
let query = sql"SELECT * FROM users WHERE id = {id}"
let title = "Hello"
let page = html"<h1>{title}</h1>"
How Tagged Templates Work #
A tagged template tag"text {expr} more" compiles to a function call:
// sql"SELECT * FROM users WHERE id = {id}"
// compiles to:
sql({"SELECT * FROM users WHERE id = ", ""}, id)
The tag function receives:
- A table of string literal parts
- The interpolated expression values as additional arguments
Defining Tag Functions #
fn sql(parts: [str], id: int) -> str {
"{parts[0]}{id}{parts[1]}"
}
fn html(parts: [str], content: str) -> str {
"{parts[0]}{content}{parts[1]}"
}
Plain Tagged Templates #
Tagged templates without interpolation:
let path = raw"C:\Users" // raw({"C:\\Users"})
Note:
re"..."is not a tagged template — it is a dedicated regex literal (see "Regex Literals" below). Use any other tag name for custom tagged-template functions.
Regex Literals #
re"pattern"flags? produces a typed regex value (Type::Regex) backed by
the standard regex module. Pattern syntax is validated at compile
time — malformed patterns (unclosed (, trailing %, missing [
after %f, …) are rejected by the compiler before any code runs.
Lua pattern classes (%d, %a, %s, …) flow through verbatim; character
classes [a-z], anchors ^/$, and quantifiers */+/-/? work as
in regex.test.
let pat = re"^[a-z]+$"
pat.test("hello") // true
pat.test("Hello") // false — capital
pat.find("greet hello") // { text: "hello", start: ..., stop: ... }
pat.replace_all("a1b2c3", "%d", "*") // "a*b*c*"
re"%d+".test("answer 42") // true — anonymous use
Flags #
The flag suffix follows the closing " with no whitespace.
| Flag | Effect |
|---|---|
i |
Case-insensitive. Each ASCII letter in the pattern is rewritten at compile time so it matches both cases (a → [aA], [a-z] → [a-zA-Z]). |
re"hello"i.test("HELLO") // true
re"[a-z]+"i.test("MixedCase") // true
Methods and field #
The literal evaluates to a value with one field and eight methods. The typeck recognizes only these — accessing any other field is a compile error.
| Member | Type |
|---|---|
pattern |
str — the (possibly transformed) pattern body |
test(s) |
bool |
find(s) |
any (record-shaped: { text, start, stop, groups } | nil) |
find_all(s) |
[any] |
match_str(s) |
any (matched substring or nil) |
replace(s, r) |
str |
replace_all(s, r) |
str |
split(s) |
[str] |
count(s) |
int |
Each method delegates to the corresponding regex.* stdlib function.
String Operations #
The standard library provides string utilities:
// Trimming
string.trim(" hello ") // "hello"
string.trim_start(" hello") // "hello"
string.trim_end("hello ") // "hello"
// Checking
string.starts_with("hello", "he") // true
string.ends_with("hello", "lo") // true
string.contains("hello", "ell") // true
string.is_empty("") // true
// Transforming
string.split("a,b,c", ",") // ["a", "b", "c"]
string.replace("hello", "l", "r") // "herro"
string.chars("hello") // ["h", "e", "l", "l", "o"]
// Padding
string.pad_start("42", 5, "0") // "00042"
string.pad_end("hi", 5, ".") // "hi..."
See Standard Library for the full API.