# `PtcRunner.Lisp.Runtime.String`
[🔗](https://github.com/andreasronge/ptc_runner/blob/main/lib/ptc_runner/lisp/runtime/string.ex#L1)

String manipulation and parsing operations for PTC-Lisp runtime.

Provides string concatenation, substring, join, split, and parsing functions.

# `blank?`

True if the value is nil, empty, or contains only whitespace.
- (blank? nil) returns true
- (blank? "") returns true
- (blank? "  \t\n") returns true
- (blank? "hello") returns false

# `downcase`

Convert string to lowercase.
- (downcase "HELLO") returns "hello"
- (downcase "") returns ""

# `ends_with?`

Check if string ends with suffix.
- (ends-with? "hello" "lo") returns true
- (ends-with? "hello" "x") returns false
- (ends-with? "hello" "") returns true

# `format_variadic`

Java-style string formatting with `%s`, `%d`, `%f`, `%e`, `%x`, `%o`, `%%`.

Extra args are ignored (Clojure behavior). Too few args raises an error.

- `(format "Hello %s" "world")` returns `"Hello world"`
- `(format "%d items" 5)` returns `"5 items"`
- `(format "%.2f" 3.14159)` returns `"3.14"`
- `(format "100%%")` returns `"100%"`

# `grep`

Return lines matching the pattern (case-insensitive regex).
String patterns are compiled as regex with BRE-to-PCRE translation
(e.g. `\|` becomes `|` for alternation).
Matching is case-insensitive by default.
- (grep "error" text) returns lines containing "error", "Error", "ERROR", etc.
- (grep "error\|warn" text) returns lines matching error or warn (any case)
- (grep "" "a\nb") returns ["a", "b"] (empty pattern matches all)

# `grep_n`

Return lines matching the pattern with 1-based line numbers.
String patterns are compiled as case-insensitive regex with BRE-to-PCRE translation.

An optional `context` parameter (like `grep -C`) includes surrounding lines.
Each result includes a `:match` boolean to distinguish matches from context.

- (grep-n "error" text) returns [{:line 1 :text "error here" :match true} ...]
- (grep-n "error" text 2) includes 2 lines of context around each match

# `includes?`

Check if string contains substring.
- (includes? "hello" "ll") returns true
- (includes? "hello" "x") returns false
- (includes? "hello" "") returns true

# `index_of`

Return the index of the first occurrence of value in s, or nil if not found.
Optionally starts searching from a given index.

Uses grapheme indices (not byte offsets or UTF-16 code units) for consistency
with `subs`, `count`, and other PTC-Lisp string functions.

- (index-of "hello" "l") returns 2
- (index-of "hello" "x") returns nil
- (index-of "hello" "l" 3) returns 3
- (index-of "hello" "" ) returns 0

# `index_of`

# `join`

Join a collection into a string with optional separator.
- (join ["a" "b" "c"]) returns "abc"
- (join ", " ["a" "b" "c"]) returns "a, b, c"
- (join "-" [1 2 3]) returns "1-2-3"
- (join ", " []) returns ""

# `join`

# `last_index_of`

Return the index of the last occurrence of value in s, or nil if not found.
Optionally searches backwards from a given index.

Correctly handles overlapping matches: `(last-index-of "aaa" "aa")` returns 1.

Uses grapheme indices (not byte offsets or UTF-16 code units) for consistency
with `subs`, `count`, and other PTC-Lisp string functions.

- (last-index-of "hello" "l") returns 3
- (last-index-of "hello" "x") returns nil
- (last-index-of "hello" "l" 2) returns 2
- (last-index-of "hello" "") returns 5
- (last-index-of "aaa" "aa") returns 1

# `last_index_of`

# `name`

Returns the name string of a keyword or string.

- `(name :foo)` returns `"foo"`
- `(name "bar")` returns `"bar"`
- Errors on nil, numbers, booleans, special values.

# `parse_boolean`

Parse string to boolean. Returns nil on failure.
Matches Clojure 1.11+ parse-boolean behavior.

# `parse_double`

Parse string to float. Returns nil on failure.
Matches Clojure 1.11+ parse-double behavior.

# `parse_long`

Parse string to integer. Returns nil on failure.
Matches Clojure 1.11+ parse-long behavior.

# `pr_str_variadic`

Return a readable string representation of zero or more values, space-separated.

Like Clojure's `pr-str`, produces output suitable for reading back:
- Strings get wrapped in quotes: `(pr-str "hello")` → `""hello""`
- nil becomes "nil": `(pr-str nil)` → `"nil"`
- Multiple args joined by space: `(pr-str 1 "a")` → `"1 "a""`

# `replace`

Replace all occurrences of a pattern in a string.
- (replace "hello" "l" "L") returns "heLLo"
- (replace "aaa" "a" "b") returns "bbb"

# `split`

Split a string by separator.
- (split "a,b,c" ",") returns ["a" "b" "c"]
- (split "hello" "") returns ["h" "e" "l" "l" "o"]
- (split "a,,b" ",") returns ["a" "" "b"]

Clojure's `split` requires a regex `Pattern` delimiter and raises a
ClassCastException for any plain-string delimiter. PTC-Lisp accepts a regex
delimiter and — under the char≡one-character-string value model (DIV-47,
GAP-S116) — a single-character delimiter, since a char literal is
indistinguishable from a one-character string at runtime. A delimiter that is
a plain string of two or more characters cannot be a char literal, so it is an
invalid program Clojure rejects; PTC-Lisp surfaces a recoverable `:type_error`
signal rather than silently splitting on it (DIV-50, GAP-S74).

# `split_lines`

Split a string into a list of lines.
- (split-lines "line1
line2
line3") returns ["line1" "line2" "line3"]
- Does not return trailing empty lines.

# `starts_with?`

Check if string starts with prefix.
- (starts-with? "hello" "he") returns true
- (starts-with? "hello" "x") returns false
- (starts-with? "hello" "") returns true

# `str_variadic`

Convert zero or more values to string and concatenate.

Used as a `:collect` binding for the `str` builtin.

- `(str)` returns `""`
- `(str 42)` returns `"42"`
- `(str "a" "b")` returns `"ab"`
- `(str nil)` returns `""` (not `"nil"`)
- `(str :keyword)` returns `":keyword"`
- `(str true)` returns `"true"`

# `subs`

Return substring starting at index (2-arity) or from start to end (3-arity).
- (subs "hello" 1) returns "ello"
- (subs "hello" 1 3) returns "el"
- (subs "hello" 0 0) returns ""
- Out of bounds (start > length, end > length) returns truncated result
- Negative start returns "" (signal value — see clojure-conformance-gaps.md DIV-22)

Diverges from Clojure, which raises StringIndexOutOfBoundsException on
out-of-range. PTC-Lisp prefers signal values (empty string, nil, false) over
raising for Clojure-named functions because there's no try/catch. The negative
start → "" rule kills the (.indexOf s "miss") → -1 → subs trap that would
otherwise silently return the whole string.

# `subs`

# `to_str`

# `trim`

Trim leading and trailing whitespace.
- (trim "  hello  ") returns "hello"
- (trim "
	 text 
") returns "text"

# `trim_newline`

Remove all trailing newline (`\n`) or carriage-return (`\r`) characters.
- (trim-newline "hello\n") returns "hello"
- (trim-newline "hello\r\n") returns "hello"
- (trim-newline "hello  ") returns "hello  "

# `triml`

Trim leading whitespace.
- (triml "  hello  ") returns "hello  "

# `trimr`

Trim trailing whitespace.
- (trimr "  hello  ") returns "  hello"

# `upcase`

Convert string to uppercase.
- (upcase "hello") returns "HELLO"
- (upcase "") returns ""

---

*Consult [api-reference.md](api-reference.md) for complete listing*
