std/string
std/string
Section titled “std/string”std/string — string receiver methods. Methods are migrated here gradually from the auto-injected magic prelude per docs/PRELUDE-TO-MODULES.md. Files declare import "std/string"; to bring the methods into scope. During the transition the prelude itself does the same import so existing single-file programs keep working unchanged.
is_empty
Section titled “is_empty”pub function (s: string) is_empty(): booleans.is_empty() is a length-zero shorthand. The IR’s len fold
resolves len(s) to a single i32.load at compile time when
s is statically known to be a string, so this typically
folds to one i32.eqz at the call site.
to_string
Section titled “to_string”pub function (s: string) to_string(): string(s: string) to_string() — identity on strings. Lets the
f-string lexer’s blanket (<expr>).to_string() desugar work
uniformly on every interpolant, regardless of whether the
interpolant is already a string. Has no runtime cost: the IR’s
inliner collapses the call to its operand.
repeat
Section titled “repeat”pub function (s: string) repeat(n: i32): strings.repeat(n) — n copies of s concatenated. n <= 0
returns empty; n == 1 returns a fresh independent copy
of s (the + lowering allocates). Quadratic on large n
because each + allocates a fresh buffer; if a benchmark
shows that’s a real bottleneck, drop to a single-pass
memcpy via a __memcpy shim.
starts_with
Section titled “starts_with”pub function (s: string) starts_with(prefix: string): booleanends_with
Section titled “ends_with”pub function (s: string) ends_with(suffix: string): booleancontains
Section titled “contains”pub function (s: string) contains(needle: string): booleanindex_of
Section titled “index_of”pub function (s: string) index_of(needle: string): i32last_index_of
Section titled “last_index_of”pub function (s: string) last_index_of(needle: string): i32s.last_index_of(needle) — rightmost occurrence; -1 if
absent. Empty needle returns len(s) to match Python rfind /
Go LastIndex conventions (the “match at every gap” trick).
Walks bytes from the right; O(n*m) worst case like
index_of.
capitalize
Section titled “capitalize”pub function (s: string) capitalize(): strings.capitalize() — uppercase the first byte, leave the
rest unchanged. ASCII-only: non-letter first bytes pass
through. Different from to_upper (which folds every
letter) and from Python’s str.capitalize (which also
LOWERCASES the rest — our version intentionally preserves
the tail since the lossy fold is rarely what callers
want).
word_count
Section titled “word_count”pub function (s: string) word_count(): i32s.word_count() — number of whitespace-separated words.
Same notion as fields(): runs of any ASCII whitespace
count as a single separator, empty pieces dropped. Empty
/ all-whitespace input returns 0.
escape_html
Section titled “escape_html”pub function (s: string) escape_html(): strings.escape_html() — escape the five characters that have
special meaning in HTML / XML: & < > ” ’. Produces a
string safe to splice into element bodies and attribute
values; matches Go html.EscapeString.
strip_quotes
Section titled “strip_quotes”pub function (s: string) strip_quotes(): Option[string]s.strip_quotes() — if s starts and ends with the same
quote character (either ” or ’), return Some(inner).
Otherwise None. Useful for parsing config files / CLI
flags / JSON strings where quotes are optional.
leading_count
Section titled “leading_count”pub function (s: string) leading_count(b: i32): i32s.leading_count(b) — number of leading bytes equal to b.
” hello”.leading_count(32) == 4 (count of spaces).
Useful for indent detection / column alignment.
trailing_count
Section titled “trailing_count”pub function (s: string) trailing_count(b: i32): i32s.trailing_count(b) — number of trailing bytes equal to b.
“hello “.trailing_count(32) == 3. Symmetric to
leading_count.
hash_fnv32
Section titled “hash_fnv32”pub function (s: string) hash_fnv32(): u32s.hash_fnv32() — FNV-1a 32-bit hash of the byte content.
Non-cryptographic; good for hash-table bucket selection
and fingerprinting. Constant-time per input length (no
data-dependent branches). Reference: http://www.isthe.com/
chongo/tech/comp/fnv/.
Returns u32 because the multiply / xor mixing is defined over unsigned arithmetic.
hash_djb2
Section titled “hash_djb2”pub function (s: string) hash_djb2(): u32(s: string) hash_djb2() — Bernstein’s djb2 hash. Alternate
non-cryptographic hash to FNV-1a; sometimes a better fit
for short keys due to the differing mixing. Initial value
5381; multiplier 33. Returns u32.
escape_c
Section titled “escape_c”pub function (s: string) escape_c(): strings.escape_c() — C-style escape: backslash + double-quote
each become \ ”, and the four whitespace bytes (\n \t
\r \0) become their two-char escapes. Other bytes pass
through unchanged. Standard shape for emitting source-
code-ready string literals.
escape_shell
Section titled “escape_shell”pub function (s: string) escape_shell(): string(s: string) escape_shell() — POSIX-shell-safe quoting.
Wraps s in single quotes and replaces any interior '
with the standard close-quote / escaped-single-quote /
reopen-quote dance: ' → '\''. The result can be
concatenated into a shell command line without word
splitting or substitution.
snake_case
Section titled “snake_case”pub function (s: string) snake_case(): string(s: string) snake_case() / kebab_case() — convert
camelCase or PascalCase to lower-case-with-separator
names. Inserts an underscore (or hyphen) before each
internal capital letter; lowercases everything; leaves
existing separators (_, -, space) alone besides the case
fold.
kebab_case
Section titled “kebab_case”pub function (s: string) kebab_case(): stringis_valid_identifier
Section titled “is_valid_identifier”pub function (s: string) is_valid_identifier(): booleans.is_valid_identifier() — true if s matches lang’s /
C / JS identifier pattern: starts with a letter or
underscore, continues with letters / digits / underscores.
Empty string returns false.
is_ipv4
Section titled “is_ipv4”pub function (s: string) is_ipv4(): booleans.is_ipv4() — true if s parses as a dotted-decimal IPv4
address: four octets, each 0..255, separated by dots. No
leading-zero check; “0.0.0.0” / “255.255.255.255” / common
shapes all pass.
Octet parse is a manual digit-walk rather than parse_int — parse_int uses i64 internally and hits the native i64-comparison-across-i32-boundary bug for small positive inputs (returns None on arm64 / x86-64 for “127”).
is_email_like
Section titled “is_email_like”pub function (s: string) is_email_like(): booleans.is_email_like() — basic syntactic check: exactly one
’@’ with non-empty local + domain parts, and the domain
contains at least one dot. Not RFC 5322 compliant (real
email validation needs a parser); useful as a quick
sanity check before downstream validation.
Tuple fields are bound to locals before passing to len —
len(p.0) directly on a (string, string) tuple-field
access crashes the arm64 backend (the load folds
incorrectly). Binding through var is a safe workaround
pending a codegen fix.
is_url_like
Section titled “is_url_like”pub function (s: string) is_url_like(): booleans.is_url_like() — true if s starts with http:// or
https:// and contains at least one dot after the scheme
(so localhost-style “http://nodot” fails). Very loose
shape check, like is_email_like; defer to a real URL
parser when one lands.
is_json_like
Section titled “is_json_like”pub function (s: string) is_json_like(): booleans.is_json_like() — true if s (after trimming whitespace)
starts and ends with matching {} or []. Not a real
JSON validator — just a fast pre-flight check before
attempting a parse. Empty strings return false.
common_prefix
Section titled “common_prefix”pub function (s: string) common_prefix(other: string): strings.common_prefix(other) — longest shared leading
substring. Returns "" when the first byte differs or
either argument is empty.
common_suffix
Section titled “common_suffix”pub function (s: string) common_suffix(other: string): strings.common_suffix(other) — longest shared trailing
substring. Returns "" when no overlap or either side
is empty.
starts_with_any
Section titled “starts_with_any”pub function (s: string) starts_with_any(prefixes: string[]): booleans.starts_with_any(prefixes) — true if any entry in
prefixes is a prefix of s. Empty prefixes array → false.
An empty-string prefix always matches (consistent with
starts_with("")).
ends_with_any
Section titled “ends_with_any”pub function (s: string) ends_with_any(suffixes: string[]): booleans.ends_with_any(suffixes) — true if any entry in
suffixes is a suffix of s. Empty suffixes array → false.
replace_first
Section titled “replace_first”pub function (s: string) replace_first(old: string, new: string): strings.replace_first(old, new) — replace only the first
occurrence of old with new. Convenience wrapper over
replace_n(old, new, 1). Empty old is a no-op
(matches replace’s semantics).
word_at
Section titled “word_at”pub function (s: string) word_at(i: i32): Option[string]s.word_at(i) — Option[string] of the i-th word in s
using whitespace as separator. Multiple consecutive
whitespace bytes collapse (driven by fields()).
Negative indices and indices >= word_count return None.
word_count_min
Section titled “word_count_min”pub function (s: string) word_count_min(min_len: i32): i32s.word_count_min(min_len) — count of whitespace-
delimited words at least min_len bytes long.
min_len <= 0 counts every word.
longest_word
Section titled “longest_word”pub function (s: string) longest_word(): Option[string]s.longest_word() — Option[string] of the longest
whitespace-delimited word. Ties go to the first
occurrence. Empty / all-whitespace input → None.
is_quoted
Section titled “is_quoted”pub function (s: string) is_quoted(): booleans.is_quoted() — true if s has at least 2 bytes and
the first and last bytes are matching quote characters
(”, `, or ’). Doesn’t validate escaping inside the
quotes — purely a shape predicate. Empty / single-char
strings return false (need at least an opening and
closing quote).
is_kebab_case
Section titled “is_kebab_case”pub function (s: string) is_kebab_case(): booleans.is_kebab_case() — true if s is non-empty,
lowercase / digit / hyphen only, no leading or trailing
hyphen, and no consecutive hyphens. Matches the
“url-slug” shape kebab-case usually denotes.
is_snake_case
Section titled “is_snake_case”pub function (s: string) is_snake_case(): booleans.is_snake_case() — true if s is non-empty,
lowercase / digit / underscore only, no leading or
trailing underscore, and no consecutive underscores.
shift_byte
Section titled “shift_byte”pub function (s: string) shift_byte(delta: i32): strings.shift_byte(delta) — Caesar-style byte shift: each
byte += delta, wrapping at 256. Negative deltas are
supported. Useful for simple obfuscation / toy ciphers;
don’t rely on it for security.
without_chars
Section titled “without_chars”pub function (s: string) without_chars(set: string): strings.without_chars(set) — remove every byte from s that
appears anywhere in set. Empty set → unchanged.
ASCII-level character class semantics.
contains_only
Section titled “contains_only”pub function (s: string) contains_only(set: string): booleans.contains_only(set) — true if every byte in s is in
set. Empty s → true (vacuous). Empty set with non-empty
s → false (no characters allowed). ASCII-level.
count_chars_in
Section titled “count_chars_in”pub function (s: string) count_chars_in(set: string): i32s.count_chars_in(set) — count bytes of s that appear
in set. Counterpart to without_chars / contains_only.
Empty set → 0.
split_at
Section titled “split_at”pub function (s: string) split_at(i: i32): (string, string)s.split_at(i) — split s into two parts at byte index i.
Returns a (string, string) tuple. Negative i clamps to 0;
i >= len(s) puts everything in the first part.
replace_byte
Section titled “replace_byte”pub function (s: string) replace_byte(from: i32, to: i32): strings.replace_byte(from, to) — return a copy of s with every
byte equal to from replaced by to. ASCII-level; for
multi-byte sequences use replace(old, new). Both args
are i32 byte values (use the literal character code or a
helper like b'-' as i32).
to_acronym
Section titled “to_acronym”pub function (s: string) to_acronym(): strings.to_acronym() — first byte of each whitespace-
separated token, uppercased and concatenated. “hello
world” → “HW”. Empty / all-whitespace input → "".
ASCII upper-fold only.
title_case
Section titled “title_case”pub function (s: string) title_case(): strings.title_case() — uppercase the first byte of every
space-separated word, leave the rest untouched. Splits
on the literal ’ ’ byte (preserves multi-space gaps as
trailing spaces on the previous word the way Go’s
strings.Title does). ASCII-only; “FOX” stays “FOX”.
lines_non_empty
Section titled “lines_non_empty”pub function (s: string) lines_non_empty(): string[]s.lines_non_empty() — like lines() but drops empty
entries. Useful for parsing text that uses blank lines
as separators where you don’t care about them.
is_ascii_only
Section titled “is_ascii_only”pub function (s: string) is_ascii_only(): booleanis_numeric
Section titled “is_numeric”pub function (s: string) is_numeric(): booleanis_alpha_only
Section titled “is_alpha_only”pub function (s: string) is_alpha_only(): booleanis_alnum_only
Section titled “is_alnum_only”pub function (s: string) is_alnum_only(): booleanis_int
Section titled “is_int”pub function (s: string) is_int(): booleans.is_int() — true if s parses as a decimal integer
(optional leading + / -, then one-or-more digits).
Doesn’t check for i32 overflow — s.parse_int() is the
validating + parsing form.
is_float
Section titled “is_float”pub function (s: string) is_float(): booleans.is_float() — true if s looks like a decimal float:
optional sign, digits, optional .digits. No exponent
form (1e10) — that’s a follow-up. Pure-int strings are
also accepted (5 → true). Used as a quick “looks numeric”
check before invoking parse_float.
pub function (s: string) wrap(prefix: string, suffix: string): strings.wrap(prefix, suffix) — concatenate prefix + s +
suffix. Saves the multi-+ call site when wrapping a value
in brackets / quotes / tags. "x".wrap("[", "]") →
"[x]".
is_blank
Section titled “is_blank”pub function (s: string) is_blank(): booleans.is_blank() — true if s is empty or contains only ASCII
whitespace. Differs from is_empty by also matching strings
of pure tabs / spaces / newlines. Useful for “skip blank
lines” passes.
is_hex_string
Section titled “is_hex_string”pub function (s: string) is_hex_string(): booleans.is_hex_string() — every byte is a hex digit (case-
insensitive). Empty string returns false (mirrors the
is_numeric / is_alpha_only conventions). Useful for
validating user-supplied hex hashes / hex-color values.
indent
Section titled “indent”pub function (s: string) indent(prefix: string): strings.indent(prefix) — prepend prefix to every line of s.
Empty lines get the prefix too (matches Python textwrap
indent’s default; the predicate-gated variant is a
follow-up). Lines are determined by ‘\n’; CRLF input
gets prefix between the CR and the LF only on the first
line and after each ‘\n’, which matches the bytewise
model.
pub function (s: string) trim(): stringtrim_start
Section titled “trim_start”pub function (s: string) trim_start(): strings.trim_start() / trim_end() — one-sided whitespace
strip. The bare trim already covers both ends; these
are the asymmetric variants useful for trimming a
prompt prefix without eating trailing data, or vice versa.
trim_end
Section titled “trim_end”pub function (s: string) trim_end(): stringrstrip_newline
Section titled “rstrip_newline”pub function (s: string) rstrip_newline(): strings.rstrip_newline() — strip a single trailing newline.
Removes “\n” or “\r\n” (the CRLF pair) but stops there —
runs of multiple newlines are preserved beyond the first
strip. Use trim_end for full whitespace strip.
trim_chars
Section titled “trim_chars”pub function (s: string) trim_chars(chars: string): strings.trim_chars(chars) — strip any byte that appears in
chars from both ends. Empty chars is a no-op. Useful
for stripping a chosen set of delimiters / wrapping
characters in one pass.
trim_start_chars
Section titled “trim_start_chars”pub function (s: string) trim_start_chars(chars: string): strings.trim_start_chars(chars) / s.trim_end_chars(chars) —
one-sided variants of trim_chars. Strip any byte in chars
from the start / end only.
trim_end_chars
Section titled “trim_end_chars”pub function (s: string) trim_end_chars(chars: string): stringstarts_with_ci
Section titled “starts_with_ci”pub function (s: string) starts_with_ci(prefix: string): booleans.starts_with_ci(prefix) / s.ends_with_ci(suffix) —
ASCII case-insensitive variants of starts_with / ends_with.
Folds A-Z to a-z per byte.
ends_with_ci
Section titled “ends_with_ci”pub function (s: string) ends_with_ci(suffix: string): booleanpub function (s: string) bytes(): u8[]s.bytes() — returns a fresh u8[] whose contents are
independently owned. Use s.as_bytes() (slice form) when
you don’t need ownership; that’s the zero-copy companion.
to_lower
Section titled “to_lower”pub function (s: string) to_lower(): stringto_upper
Section titled “to_upper”pub function (s: string) to_upper(): stringpub function (s: string) split(sep: string): string[]s.split(sep) — splits on every non-overlapping
occurrence of sep. Empty pieces around adjacent
separators are preserved (",a,b,".split(",") →
["", "a", "b", ""]). Empty separator splits into
single-byte strings (matches JS’s split("")). Result
is built incrementally via __array_append_string.
splitn
Section titled “splitn”pub function (s: string) splitn(sep: string, n: i32): string[]s.splitn(sep, n) — like split but caps the result at n
pieces; the last piece carries the unsplit tail. n <= 0
returns the empty array; n == 1 returns [s] unchanged.
Useful for “first token, rest” parsing — HTTP request
lines, URL scheme separation, header key: value.
pub function (s: string) first(): Option[i32]s.first() / s.last() — first / last byte as Option[i32].
None on empty. Convenience over s.at(0) / s.at(len(s) - 1)
— the indices are easy to mis-write.
pub function (s: string) last(): Option[i32]pub function (s: string) take(n: i32): strings.take(n) / s.drop(n) — prefix and suffix slicing
with bounds clamping. take(big) returns the whole string;
drop(big) returns empty. take(neg) / drop(neg) treat
the count as 0. Saves the s[0:min(n, len(s))] /
s[min(n, len(s)):len(s)] boilerplate at every call site.
pub function (s: string) drop(n: i32): stringchunks
Section titled “chunks”pub function (s: string) chunks(size: i32): string[]s.chunks(size) — split into consecutive size-byte
chunks. The last chunk may be shorter. size <= 0 returns
an array containing just s (or empty if s is empty).
Useful for base64 line-wrapping, hex-dump formatting,
paged output.
pub function (s: string) lines(): string[]s.lines() — splits on ‘\n’, strips a trailing ‘\r’ from
each line (so CRLF is treated as LF), and drops the trailing
empty line that a final ‘\n’ would otherwise produce. Matches
Python’s str.splitlines() and Go’s bufio.Scanner with
ScanLines:
“a\nb\nc” → [“a”, “b”, “c”] “a\nb\n” → [“a”, “b”] (trailing ‘\n’ eaten) “a\r\nb” → [“a”, “b”] (CR stripped) “\n” → [""] (one empty line) "" → [] (no lines at all)
O(n) single-pass byte walk; uses index-of-byte (s[i]) and
substring slicing rather than the per-byte s[i:i+1] == "\n"
pattern, so the inner loop allocates nothing.
fields
Section titled “fields”pub function (s: string) fields(): string[]s.fields() — split on runs of ASCII whitespace, dropping
empty pieces. Differs from split(" ") which preserves
adjacent-separator empties and only matches the single
space byte. Whitespace classifier matches the trim() one
(space / tab / CR / LF / VT / FF). Result is the run of
non-empty fields in order. Empty / all-whitespace input
returns an empty array.
Matches Go strings.Fields, Roc Str.split (whitespace flavour), Python str.split() (no-arg).
eq_ignore_ascii_case
Section titled “eq_ignore_ascii_case”pub function (s: string) eq_ignore_ascii_case(other: string): booleans.eq_ignore_ascii_case(other) — equality with ASCII A-Z
folded to a-z on both sides. Non-ASCII bytes (>= 0x80) only
match byte-exact — multibyte UTF-8 sequences and Unicode
case folding aren’t handled. Standard shape for HTTP header
keys (“Content-Type” vs “content-type”) where the fold is
strictly ASCII per RFC 7230.
strip_prefix
Section titled “strip_prefix”pub function (s: string) strip_prefix(prefix: string): Option[string]s.strip_prefix(p) — Some(rest) if s starts with p, None
otherwise. The Some payload is s[len(p):], which under lang’s
arena model is a fresh independent slice (string slicing
allocates). Companion to starts_with that ALSO gives you
the trailing chunk in a single call.
strip_suffix
Section titled “strip_suffix”pub function (s: string) strip_suffix(suffix: string): Option[string]s.strip_suffix(s) — Some(head) if s ends with the suffix.
Symmetric to strip_prefix; returns s[0:len(s)-len(suffix)].
remove_prefix
Section titled “remove_prefix”pub function (s: string) remove_prefix(prefix: string): strings.remove_prefix(prefix) / s.remove_suffix(suffix) —
like strip_prefix / strip_suffix but return s unchanged
when the prefix / suffix doesn’t match, instead of
Option. Avoids the match boilerplate when the unchanged
value is the right fallback.
remove_suffix
Section titled “remove_suffix”pub function (s: string) remove_suffix(suffix: string): stringis_uuid
Section titled “is_uuid”pub function (s: string) is_uuid(): booleans.is_uuid() — true if s matches the canonical UUID
format: 8-4-4-4-12 hex digits separated by dashes,
total 36 characters. Doesn’t validate the version or
variant nibble — just the shape.
pad_start
Section titled “pad_start”pub function (s: string) pad_start(n: i32, ch: string): strings.pad_start(n, ch) / s.pad_end(n, ch) — left- and right-
pad the string with ch (the first byte is used) so the
result is at least n bytes long. Already-long strings pass
through unchanged. Standard alignment shape for tables, hex
dumps, fixed-width column output.
ch is a single-byte string; multibyte fill characters
would need codepoint handling we don’t have yet. Empty ch
degenerates to no padding (returns the original).
pad_end
Section titled “pad_end”pub function (s: string) pad_end(n: i32, ch: string): stringsplit_once
Section titled “split_once”pub function (s: string) split_once(sep: string): Option[(string, string)]s.split_once(sep) — splits at the FIRST occurrence of
sep, returning Some((head, tail)). The separator is dropped
from the output. Returns None when sep doesn’t appear or
sep is empty. Useful for “key=value”, “name:port”,
“scheme://rest” patterns where you only want one split.
trim_start_matches
Section titled “trim_start_matches”pub function (s: string) trim_start_matches(sub: string): strings.trim_start_matches(sub) / s.trim_end_matches(sub) —
peel sub off the beginning / end as many times as it
appears, contiguously. Symmetric with the regular trim()
that strips whitespace.
trim_end_matches
Section titled “trim_end_matches”pub function (s: string) trim_end_matches(sub: string): stringcontains_ci
Section titled “contains_ci”pub function (s: string) contains_ci(needle: string): booleans.contains_ci(needle) / index_of_ci(needle) — case-
insensitive ASCII versions of contains / index_of.
Folds A-Z to a-z on both sides per byte. Multibyte UTF-8
matches only as exact bytes (no Unicode case folding).
index_of_ci
Section titled “index_of_ci”pub function (s: string) index_of_ci(needle: string): i32pad_start_str
Section titled “pad_start_str”pub function (s: string) pad_start_str(width: i32, fill: string): strings.pad_start_str(width, fill) / pad_end_str(width, fill)
— left- and right-pad to a minimum BYTE width using the
multi-byte fill string as the padding unit. The pad
repeats; if the byte-count overshoots, the LAST occurrence
is truncated at the boundary. The simpler pad_start(n, ch)
from earlier always uses a single-byte fill — these are
the cousins for prefix dashes / line rules / ”…” kinds
of decorative padding.
pad_end_str
Section titled “pad_end_str”pub function (s: string) pad_end_str(width: i32, fill: string): stringcenter
Section titled “center”pub function (s: string) center(width: i32, ch: string): strings.center(width, ch) — center s within a width-byte field,
padding equally on both sides with the single byte ch.
Odd-padding splits leave the extra byte on the right. ch
is a single-byte string (only first byte used). Already-
long strings pass through unchanged.
reverse_words
Section titled “reverse_words”pub function (s: string) reverse_words(): strings.reverse_words() — split on ASCII whitespace, reverse
the resulting word list, join with a single space. Drops
the original whitespace structure (multiple spaces / tabs
/ newlines all collapse to single ’ ’). Empty input
returns "".
truncate
Section titled “truncate”pub function (s: string) truncate(n: i32, ellipsis: string): strings.truncate(n, ellipsis) — if len(s) > n, return the first
(n - len(ellipsis)) bytes followed by ellipsis. If
len(s) <= n, return s unchanged. If n is smaller than the
ellipsis itself, just hard-truncate s to n bytes (the
ellipsis would otherwise overflow).
ellipsis
Section titled “ellipsis”pub function (s: string) ellipsis(n: i32): strings.ellipsis(n) — fit s into n bytes, marking truncation
with ”…”. When len(s) <= n, returns s unchanged.
Otherwise: if n >= 3, return the first (n-3) bytes
followed by ”…” (so an n=3 cap on a long string is just
”…” — different from truncate(3, "...") which would
hard-truncate the source). If n < 3, return the first n
bytes of ”…” as a degenerate placeholder.
first_line
Section titled “first_line”pub function (s: string) first_line(): Option[string]s.first_line() — Option[string] containing the prefix
up to (but not including) the first ‘\n’. Returns None
for the empty string. If there’s no newline, returns
Some(s).
pub function (s: string) at(i: i32): Option[i32]s.at(i) — bounds-checked byte access. Returns Some(byte)
for in-range i or None if i is out of [0, len). Counterpart
to s[i] which traps on OOB; useful in tokeniser loops
where the end check would otherwise need an explicit bounds
guard at every step.
pub function (s: string) chars(): i32[]s.chars() — byte array of the string’s contents as
i32 values (one per byte). Byte-level, not codepoint —
multibyte UTF-8 sequences come out as separate i32s in
order. Convenience over s.bytes() (which returns u8[])
when callers want to do i32 arithmetic / comparisons on
each byte without manually widening. The bytes() function
stays as the canonical way to get a u8[] slice; chars() is
the i32 sibling.
to_array
Section titled “to_array”pub function (s: string) to_array(): string[]s.to_array() — string[] one element per byte (each a
single-byte string). Companion to chars() but with
string elements; useful when later operations need
string semantics rather than raw byte values.
reverse_bytes
Section titled “reverse_bytes”pub function (s: string) reverse_bytes(): strings.reverse_bytes() — fresh string with the byte order
reversed. NOT a Unicode-aware reverse — multibyte UTF-8
sequences will scramble their internal byte order, producing
invalid UTF-8. The name carries the warning. For pure-ASCII
inputs (HTTP headers, hex / base64 / log lines, parser
fragments) the operation is well-defined.
pub function (s: string) count(sub: string): i32s.count(sub) — number of non-overlapping occurrences of
sub in s. Empty needle returns 0 to avoid infinite-loop
surprises; behaviour mirrors Rust str::matches().count().
count_byte
Section titled “count_byte”pub function (s: string) count_byte(b: i32): i32s.count_byte(b) — number of bytes equal to b. O(n)
single-pass; cheaper than count(byte.to_ascii_string())
when the needle is known to be a single byte.
replace_n
Section titled “replace_n”pub function (s: string) replace_n(old: string, new: string, n: i32): strings.replace_n(old, new, n) — like replace() but caps at
the first n occurrences. Useful when only the leading
substitutions are interesting (e.g. unquoting the first
few escapes, masking PII while keeping later context).
remove_all
Section titled “remove_all”pub function (s: string) remove_all(needle: string): strings.remove_all(needle) — replace every occurrence of
needle with empty. Sugar for replace(needle, "").
before
Section titled “before”pub function (s: string) before(sep: string): strings.before(sep) — substring before the first occurrence
of sep. Returns s unchanged if sep not present, "" if
s starts with sep. Doesn’t include sep in the result.
pub function (s: string) after(sep: string): strings.after(sep) — substring after the first occurrence
of sep. Returns "" if sep not present, s unchanged if sep
is empty. Doesn’t include sep in the result.
between
Section titled “between”pub function (s: string) between(start: string, end: string): Option[string]s.between(start, end) — Some(substring) for the content
between the first start and the first end AFTER that start.
None if either marker is missing (or empty). Useful for
extracting tag bodies, parenthesised content, brace-
delimited fields.
count_lines
Section titled “count_lines”pub function (s: string) count_lines(): i32s.count_lines() — number of lines in s. Empty string
returns 0; lines are separated by ‘\n’. A trailing newline
is NOT counted as an extra empty line (matches Go’s
bufio.Scanner ScanLines + lines() prelude function).
repeat_with_sep
Section titled “repeat_with_sep”pub function (s: string) repeat_with_sep(n: i32, sep: string): strings.repeat_with_sep(n, sep) — like repeat() but with sep
between every pair. "x".repeat_with_sep(3, ", ") →
“x, x, x”. n <= 0 returns empty.
replace
Section titled “replace”pub function (s: string) replace(old: string, new: string): strings.replace(old, new) — substitutes every
non-overlapping occurrence of old with new.
Empty old is a no-op (returns the source unchanged)
to match the JS / Go conventions and avoid the
“insert N copies” pathology. Builds the result by
concatenation; the IR’s + lowering handles the
allocation.
parse_bool
Section titled “parse_bool”pub function (s: string) parse_bool(): Option[boolean](s: string) parse_bool() — accepts the canonical
truthy / falsy spellings: “true” / “1” → Some(true),
“false” / “0” → Some(false). Anything else returns None
(no implicit lowercasing — callers stop their own
canonicalisation at the boundary). Empty string is None.
is_http_safe_method
Section titled “is_http_safe_method”pub function (s: string) is_http_safe_method(): boolean(s: string) is_http_safe_method() — true for GET / HEAD
/ OPTIONS / TRACE (RFC 9110 section 9.2.1). Safe methods
don’t mutate state; ETag caching / replay logic depends on
the distinction.
is_http_idempotent_method
Section titled “is_http_idempotent_method”pub function (s: string) is_http_idempotent_method(): boolean(s: string) is_http_idempotent_method() — true for any
safe method plus PUT / DELETE. Idempotent methods can be
safely retried after a network error without doubling
the effect.
parse_int
Section titled “parse_int”pub function (s: string) parse_int(): Option[i32]s.parse_int() — decimal i32 parser. Optional leading -;
rejects empty input, lone -, any non-digit character,
embedded whitespace, and out-of-range values
(-2^31..=2^31-1). Internal accumulator is i64 so the
bound check is exact. Returns None on failure, Some(v)
on success.
Replaced the ~190-line wat helper that previously lived in
wasm.go; the lang version compiles to roughly the same
code via the IR.
parse_hex_int
Section titled “parse_hex_int”pub function (s: string) parse_hex_int(): Option[i32]s.parse_hex_int() / s.parse_bin_int() — sugar for
parse_int_radix(s, 16) / parse_int_radix(s, 2).
parse_bin_int
Section titled “parse_bin_int”pub function (s: string) parse_bin_int(): Option[i32]parse_float
Section titled “parse_float”pub function (s: string) parse_float(): Option[f32]s.parse_float() — decimal f32 parser. Grammar:
[-]exp_adj keeps the magnitude
right. Final value = (f32) mantissa * 10^exp_adj,
sign applied via 0.0 - v.
repeat_char
Section titled “repeat_char”pub function repeat_char(ch: i32, n: i32): stringrepeat_char(ch, n) — fresh string of n copies of the byte
ch. Faster than (c).chr().repeat(n) would be (no chr
helper exists yet; this is the substitute). n <= 0
returns empty.
replace_at
Section titled “replace_at”pub function (s: string) replace_at(i: i32, new: string): strings.replace_at(i, new) — return a copy of s with the byte at
index i replaced by the first byte of new. If new is empty,
the byte at i is dropped. Out-of-range i (negative or >=
len(s)) is a no-op and returns s unchanged.
without_byte
Section titled “without_byte”pub function (s: string) without_byte(b: i32): strings.without_byte(b) — copy of s with every byte equal to b
removed. ASCII-level; for multi-byte sequences use
replace_byte or replace(old, new).
repeat_to
Section titled “repeat_to”pub function (s: string) repeat_to(target: i32): strings.repeat_to(target) — concatenate copies of s until the
result reaches at least target bytes, then truncate at the
last full copy that fits. Empty s or target <= 0 → "". The
truncate-not-overrun shape matches a common “fill to width
without exceeding” pattern.