Skip to content

Strings

Strings are immutable UTF-8 text values.

Shape supports these literal forms:

  • "..." simple string
  • """...""" triple-quoted multiline string
  • f"..." formatted string with interpolation
  • f"""...""" formatted multiline string
  • f$"..." / f$"""...""" sigil-formatted strings (${expr})
  • f#"..." / f#"""...""" sigil-formatted strings (#{expr})
let a = "hello";
let b = """
line 1
line 2
""";
let c = f"name={name}";
let d = f"""
name={name}
score={score}
""";
let e = f$"""{"name": ${user.name}}""";
let f = f#"run #{command}";
let s = "plain text";
let escaped = "quote: \" | newline: \n | tab: \t";

Supported escape sequences:

EscapeCharacter
\"Double quote
\\Backslash
\nNewline
\tTab
\rCarriage return
\{Literal { (in f-strings)
\}Literal } (in f-strings)
\$Literal $ (in f$-strings)
\#Literal # (in f#-strings)

Only the escape sequences listed above are recognized. Unknown escape sequences like \q or \a produce a compile-time error. Use \\ for a literal backslash.

Use {{ / }} only in formatted strings. In plain strings they are just characters.

Triple strings are designed for readable multiline source with clean runtime text.

When parsing """...""" (and f"""..."""), Shape does this:

  1. Normalizes line endings to \n.
  2. If the line immediately after opening """ is whitespace-only, it is removed.
  3. If the line immediately before closing """ is whitespace-only, it is removed.
  4. Computes common indentation across non-empty lines.
  5. Removes that common indentation from each non-empty line.
  6. Preserves relative indentation and internal blank lines.

Example:

let s = """
this
is
a
multiline
""";

Runtime value:

this
is
a
multiline

Relative indentation is preserved:

let readme = """
this is
a multiline
string.
-it should indent
-but remove the block spaces
""";
// Runtime value (no leading/trailing newline):
// this is
// a multiline
// string.
// -it should indent
// -but remove the block spaces

Inline form (no newline after opening """):

let inline = """a quick multiline""";
// => "a quick multiline"

Inner quotes are fine as long as they don’t form """:

let quoted = """she said "hello" today""";
// => she said "hello" today

Triple strings do not process escape sequences. Backslash characters are kept as-is. This makes them ideal for templates, regex, and embedded code:

let pattern = """
(\d+)\n(\w+)
""";
// => "(\d+)\n(\w+)" (literal backslash-n, not a newline)

If you need actual escape processing, use a simple string with \n:

let with_newline = "line1\nline2";
// => line1
// line2

Formatted strings interpolate expressions inside {...} and produce a normal string.

let msg = f"name={name}, score={score}";
let report = f"""
user={user.name}
total={total}
""";

Interpolation is a general language feature (not limited to print(...)).

let label = f"{name}:{category}";
let key = f"acct/{account_id}/items/{name}";
let payload = { text: f"item id={id}" };

Escaping braces:

Use doubled braces for literal braces:

let s = f"literal braces: {{value}}";
// => "literal braces: {value}"

A lone } is invalid in formatted strings. Use }} for a literal close brace.

Sigil modes are designed for templates that contain many literal {} pairs (JSON, config, codegen).

  • f$"..." uses ${expr} interpolation.
  • f#"..." uses #{expr} interpolation.
  • Plain { and } are treated as normal text in sigil modes.
let json = f$"""{"name": ${user.name}, "id": ${user.id}}""";
let shell = f#"echo #{path}";

To render a literal opener sequence, double the sigil:

  • In f$, write $${ for literal ${.
  • In f#, write ##{ for literal #{.
  • Each {expr} is parsed as a Shape expression.
  • Empty expressions are invalid.
  • Unclosed interpolation is invalid.
  • Nested braces inside expressions are supported by expression parsing.

Formatted interpolation uses typed specs after ::

  • fixed(N) for numeric precision
  • table(...) for table rendering options
let p = 12.3456
let s = f"p={p:fixed(2)}"
// s == "p=12.35"

Table format options are typed keys and enum-like values (not stringly settings):

let out = f"{rows:table(max_rows=20, align=right, precision=2, border=on)}"

Supported table(...) keys:

KeyValuesDefaultDescription
max_rows<int>unlimitedMaximum rows to display
alignleft, center, rightleftColumn alignment
precision<int>fullDecimal places for numbers
colordefault, red, green, yellow, blue, magenta, cyan, whitedefaultColor hint for output
borderon, offonDraw table borders

Color example:

let gains = results.filter(|r| r.score > 0)
let report = f"{gains:table(color=green, precision=2, border=off)}"

Colors are renderer hints. The CLI maps them to ANSI terminal codes; other renderers (HTML, plain text) may map them differently or ignore them.

Interpolated values use the same runtime formatting path as print(...) and trait-based display.

Strings are Unicode-safe values.

let text = "Hello 世界 👋";
let len = text.length;

Common string methods:

  • length
  • split(sep)
  • join(sep) (on vectors)
  • contains(substr)
  • substring(start, end?)
  • toUpperCase()
  • toLowerCase()
  • trim()
  • replace(old, new)

Snake_case aliases are also available for these methods:

  • to_upper_case() (alias for toUpperCase())
  • to_lower_case() (alias for toLowerCase())
  • trim_start() (alias for trimStart())
  • trim_end() (alias for trimEnd())

Examples:

let csv = "Alice,Bob,Charlie";
let items = csv.split(",");
let normalized = " HELLO ".trim().toLowerCase();
let sentence = ["hello", "shape"].join(" ");

Inside formatted-string interpolation (f"...{expr}..."):

  • completion context is expression-aware (for example, property completion after user.)
  • completion and hover are available for typed format specs after :
  • semantic highlighting includes tokens for the expression portion
  • Strings are immutable.
  • Prefer formatted strings over long + chains for readability.
  • For very large loop-built text, collect fragments and join(...) once.

While f-strings produce plain strings with interpolation, content strings (c"...") produce structured ContentNode values that support styling and structured rendering.

Content strings use the same interpolation modes as f-strings:

  • c"Hello {name}" — brace interpolation
  • c$"Hello ${name}" — dollar-sign interpolation
  • c#"Hello #{name}" — hash interpolation

Content strings additionally support inline styling:

print(c"{price: fg(green), fixed(2)}") // green number with 2 decimals
print(c"Alert: {msg: fg(red), bold}") // red bold text

For full documentation on content strings, styling, tables, and charts, see the Content chapter.