Formats.md — Strings, Templates, and Format Tags in U
The string model
U has three string forms and one reserved:
"hello" → S (literal, no interpolation)
`hello {{name}}` → S (interpolation, escapes)
Tag`hello {{name}}` → template function → Formats.*
'...' → reserved (compile error)
One rule: double quotes are literal, backticks interpolate. Tags produce typed template functions. Single quotes are reserved for future use.
1. Literal strings: "..."
Double-quoted strings are plain byte sequences. Escape sequences (\n, \t, \\, \") are processed. No interpolation — {{ inside double quotes is literal text.
name = "Greg" // → S
path = "C:\\Users\\Greg" // → S (backslash-escaped)
msg = "price is {{amount}}" // → S literally containing "price is {{amount}}"
Literal strings are for fixed text: identifiers, paths, format strings passed to other systems, keys, labels. When you write "hello", you get exactly hello — no surprises.
2. Interpolating strings: `...`
Backtick strings process escapes AND interpolate {{expr}} slots:
greeting = `Hello, {{name}}!` // → S (name resolved from scope)
report = `{{user.name}} has {{count}} items` // → S
Each {{expr}} calls .__string__() on the value. The result is S.
Why backticks?
Backticks free up " and ' as plain characters inside the string:
page = HTML`<div class="container" id='main'>
<h1>{{title}}</h1>
<p onclick='alert("hello")'>{{body}}</p>
</div>`
No \", no \', no heredoc. Two levels of quoting for free. The only character that needs escaping inside backticks is the backtick itself (\`), which rarely appears in HTML, SQL, CSS, or prose.
This solves the same problem PHP's <<<HEREDOC was invented for — multi-line strings with embedded quotes — but with interpolation and type safety built in.
3. Tagged templates: Tag...``
A tag prefix produces a typed template function instead of a plain string:
q = SQL`SELECT * FROM users WHERE id = {{id}}` // → ({id: I}) -> Formats.SQL
page = HTML`<p>{{text}}</p>` // → ({text: S}) -> Formats.HTML
style = CSS`color: {{c}}` // → ({c: Color}) -> Formats.CSS
Stored vs invoked
Every tagged template is a function by default. Call it to get the value:
// Stored — compile once, call many times:
q = SQL`SELECT * FROM users WHERE id = {{id}}`
result1 = q({id: 5})
result2 = q({id: 12})
// Invoked — compile and call now (parens around backtick):
id = 5
result = SQL(`SELECT * FROM users WHERE id = {{id}}`)
// id captured from scope
The rule: Tag...`` = function. Tag(...) = call.
Adapter qualification: Tag[Conn]...``
Some tags need to know the target at compile time:
q_pg = SQL[pg]`SELECT * WHERE id = {{id}}` // → $1
q_my = SQL[mysql]`SELECT * WHERE id = {{id}}` // → ?
q_lite = SQL[sqlite]`SELECT * WHERE id = {{id}}` // → ?
The bracket selects the dialect. This is a compile-time decision — the placeholder syntax is baked into the SQL text.
The adapter also validates dialect-specific features:
SQL[pg]`SELECT * WHERE name ILIKE {{pattern}}` // ✅ Postgres has ILIKE
SQL[mysql]`SELECT * WHERE name ILIKE {{pattern}}` // ❌ compile error
4. Byte literals: '...'
Single-quoted strings produce bytes, not text. ASCII only.
'A' // → N8 (65 — single character)
'\n' // → N8 (10 — newline byte)
'\xff' // → N8 (255 — hex byte)
'\0' // → N8 (0 — null byte)
'hello' // → [N8] (5 bytes: 104, 101, 108, 108, 111)
'hello\0' // → [N8] (6 bytes — null-terminated, C-ready)
'\x89PNG' // → [N8] (4 bytes — PNG magic number)
'\x01\x00\xff' // → [N8] (3 bytes — binary data)
'café' // → ❌ compile error: é is not ASCII
'\0' // → ❌ compile error: 0 is not a natural number (N8 starts from 1)
The rule: single quotes only accept ASCII (0x01–0x7F, no null byte) and escape sequences (\n, \t, \xHH (0x01–0xFF), \\, \'). One character → N8. Multiple characters → [N8].
This is the byte world. "hello" is UTF-8 text (S). 'hello' is raw bytes ([N8]). Functions that take S reject [N8]. Explicit conversion: "hello".bytes() → [N8], and S.from_utf8(bytes) → S (with validation).
Use cases: C interop (null-terminated byte arrays), binary protocols (wire formats), file magic numbers, byte-level comparison (memcmp instead of Unicode-aware).
N8 and B: safe bytes vs raw bytes
U has two byte types:
| Type | Range | Null bytes | Use case |
|---|---|---|---|
N8 |
1–255 | ❌ impossible | ASCII chars, safe byte literals |
B |
0–255 | ✅ allowed | Raw binary buffers, wire formats |
Conversion between them:
// N8 → B: always safe (widening, implicit)
ch: N8 = 'A'
raw: B = ch // 65 → 65, no check needed
// B → N8+N: per-value nullable (0 → none)
raw: B = read_byte()
ch: N8+N = raw.to_n8() // → N8 if ≥ 1, none if 0
// [N8] → [B]: always safe (same memory layout)
ascii: [N8] = 'hello'
buf: [B] = ascii.to_bytes() // zero-cost cast
// [B] → [N8+N]: per-element nullable, ALWAYS succeeds
buf: [B] = read_packet()
safe: [N8+N] = buf.to_n8() // each byte: 0→none, 1-255→N8
// [B] → [N8]: assertion, THROWS if any byte is 0
safe: [N8] = buf.assert_n8() // throws on null bytes
// Implicit [B] → [N8]: compile WARNING + runtime exception
safe: [N8] = buf // ⚠️ compiler warns, runtime checks
Encoding and display:
buf.to_hex() // → "89504e470d0a"
buf.to_base64() // → "iVBORw0K..."
buf.to_base64url() // → URL-safe, no padding
buf.to_utf8() // → S+N (none if invalid UTF-8)
buf.display() // → "[B](6 bytes: 8950…0d0a)"
S.from_hex("89504e47") // → [B]+N
S.from_base64("iVBORw0K") // → [B]+N
S.from_utf8(buf) // → S+N
The '...' literal syntax produces N8/[N8] — the safe form. Binary protocol code that needs null bytes uses [B] and constructs byte arrays numerically:
// PNG magic number (contains null byte at position 4):
png_magic: [B] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
// Null-terminated C string for interop:
c_str: [B] = 'hello'.to_bytes() + [0x00]
For raw text strings (no escape processing), use the raw tag:
raw`C:\Users\Greg\Documents` // → S (backslashes are literal)
regex`\d{4}-\d{2}-\d{2}` // → Regex (backslashes are regex syntax)
5. Escaping
Inside backtick strings and templates:
| Source | Output | Why |
|---|---|---|
{{name}} |
interpolates name |
normal interpolation |
\{{name}} |
literal {{name}} |
backslash escapes opening {{ |
\}} |
literal }} |
backslash escapes closing |
\` |
literal backtick | backslash escapes backtick |
\n |
newline | standard escape |
\t |
tab | standard escape |
{name} |
literal {name} |
single braces never interpolate |
Single braces never interpolate because { collides with CSS rules, regex quantifiers, JSON, and every other format U templates embed.
6. Format specifications
Inside any interpolation, a colon applies Python-style formatting:
HTML`<td>{{price:.2f}}</td>` // "42.50" then HTML-escaped
`Name: {{name:>20}}` // right-aligned, width 20
`Count: {{n:04d}}` // zero-padded to 4 digits
7. The complete tag table
| Tag | Input S |
Same-type input | Returns | External file |
|---|---|---|---|---|
(bare) `...` |
.__string__() |
— | S |
— |
HTML...`` |
HTML-escaped | Formats.HTML passed through |
Formats.HTML |
Formats.HTML.load("f.html", {...}) |
SQL...`` |
Parameterized | Formats.SQL composed |
Formats.SQL |
Formats.SQL.load("f.sql", {...}) |
CSS...`` |
Typed (Color, N…) | Formats.CSS composed |
Formats.CSS |
Formats.CSS.load("f.css", {...}) |
URL...`` |
URL-encoded | Formats.URL composed |
Formats.URL |
Formats.URL.load("f.txt", {...}) |
SVG...`` |
HTML-escaped | Formats.HTML/Formats.SVG |
Formats.HTML |
Formats.SVG.load("f.svg", {...}) |
Regex...flags |
Compile error | Regex composed |
Regex |
— |
Markdown...`` |
Escaped then rendered | Formats.HTML passed through |
Formats.HTML |
Formats.Markdown.load("f.md", {...}) |
JSON...`` |
JSON string-escaped | Formats.JSON composed |
Formats.JSON |
Formats.JSON.load("f.json", {...}) |
JS...`` |
JS-escaped | Formats.JS composed |
Formats.JS |
Formats.JS.load("f.js", {...}) |
Shell...`` |
Single-quoted | Formats.Shell composed |
Formats.Shell |
Formats.Shell.load("f.sh", {...}) |
Command...`` |
Single-quoted | Formats.Command composed |
Formats.Command |
— |
Handlebars...`` |
HTML-escaped | Formats.HTML passed through |
Formats.Handlebars |
— |
GraphQL...`` |
JSON string-escaped | Formats.GraphQL composed |
Formats.GraphQL |
— |
Mermaid...`` |
Quoted label | Formats.Mermaid appended |
Formats.Mermaid |
— |
Protobuf...`` |
— | Formats.Protobuf composed |
Formats.Protobuf |
— |
Config[T]path`` |
— | — | T |
— |
raw...`` |
— | — | S (no escapes) |
— |
8. Security model
Each tag is the sole point of responsibility for its domain. You cannot construct a Formats.HTML value without going through HTML.... You cannot issue a parameterized query without going through `SQL`.... The compiler enforces the chokepoint.
A function that accepts Formats.SQL will reject plain S at compile time:
f safe_query(q: Formats.SQL) -> [Row]
safe_query(`SELECT * WHERE id = ` + id) // ❌ compile error: S is not Formats.SQL
safe_query(SQL`SELECT * WHERE id = {{id}}`) // ✅ parameterized
Capability gating
Template tags run at compile time but are capability-gated. A SQL...`` tag with no capabilities can parse and type-check queries — nothing else. No tag can modify another module's types, inject code, or escalate beyond its declared capabilities.
SQL statement type routing
SQLINSERT INTO users ... doesn't emit a generic `execute()`. The tag parses the SQL, determines the statement type, and emits `conn.insert()`. A function with only `+E(DbRead)` that writes `SQL`INSERT ... gets a compile error — DbRead doesn't grant DbWrite.
Dangerous combination warnings
The compiler flags capability pairs that create attack vectors:
DbRead + NetFetch→ data exfiltration via HTTPDbRead + Email→ data exfiltration via emailCryptoSign + NetFetch→ key theft
9. External file loading
page = Formats.HTML.load("templates/page.html", { title, content, user })
report = Formats.SQL.load("queries/report.sql", { start_date: D, end_date: D })
theme = Formats.CSS.load("styles/theme.css", { brand: Color, spacing: R })
icon = Formats.SVG.load("assets/star.svg", { size: R, fill: Color })
Locale = Formats.Locale.load("i18n/")
Files are read and validated at compile time. Missing placeholders, type mismatches, and syntax errors are compile errors. The runtime has no file I/O — content is baked into the binary.
10. Summary
"hello" // literal S — no interpolation
`hello {{name}}` // interpolating S — resolves from scope
Tag`hello {{name}}` // stored template function → Formats.*
Tag(`hello {{name}}`) // invoked template — resolves from scope, returns value
Tag[adapter]`hello {{name}}` // dialect-specific template function
'A' // I8 (single ASCII byte — 65)
'hello' // [I8] (ASCII byte array)
'\xff\x00' // [I8] (binary data)
raw`C:\path\to` // raw S — no escape processing
There is one way to write a literal string. There is one way to interpolate. There is one way to get a typed template. Four forms. "..." is text. '...' is bytes. Backtick interpolates. Tags produce typed templates. The type system enforces which is required.