Skip to content

lex: triple-quoted string literals with PEP-257 dedent#565

Merged
danieljohnmorris merged 3 commits into
mainfrom
fix/multiline-strings
May 21, 2026
Merged

lex: triple-quoted string literals with PEP-257 dedent#565
danieljohnmorris merged 3 commits into
mainfrom
fix/multiline-strings

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

Pending #34: triple-quoted ("""...""") multi-line string literals. Same surface as "..." (escapes, {name} interpolation) plus raw newlines inside the literal. When the closing """ sits on its own line, the leading newline is dropped and the common leading whitespace is stripped from each content line; the terminating \n of the last content line is preserved (Python PEP 257 / Rust indoc! convention).

Manifesto framing: agents writing multi-line content today resort to cat-concatenation with embedded \n escapes, which is verbose and easy to get wrong. """...""" lets the body carry raw newlines and applies indent stripping, so indented source produces clean output.

Repro

Before:

banner>t cat "line one\n" "line two\n"

After:

banner>t
  """
  line one
  line two
  """

Both produce "line one\nline two\n". The second form is shorter and reads as the value it produces.

What's in the diff

  • lex: triple-quoted string literals with PEP-257 dedent - lexer pre-pass that detects """, scans for the matching close, and emits a synthesised single-quoted form with raw newlines preserved. strip_triple_indent handles the dedent and leading-newline drop when the closing """ is on its own line. Span attribution maps every emitted byte back to its source byte for diagnostic accuracy.
  • test: cross-engine coverage for triple-quoted strings - 9 regression tests pinning behaviour across VM and Cranelift JIT (single-line, inline multi-line, dedented multi-line, content bytes, escapes, single and multi-line interpolation, empty body, embedded single quote). Adds examples/triple-quoted-strings.ilo so the examples_engines harness exercises the feature on every engine.
  • doc: document triple-quoted strings in SPEC and ai.txt - SPEC.md gains a subsection under String Literals; ai.txt gets the token-minimal agent-spec entry inline.

Test plan

  • cargo test --release --features cranelift --test regression_triple_quoted_strings - 9 passing
  • cargo test --release --features cranelift --test examples_engines - example runs on every engine
  • cargo fmt && cargo clippy --release --features cranelift --all-targets -- -D warnings - clean
  • Full suite passes except pre-existing regression_reserved_names_doc::spec_reserved_short_names_match_builtin_registry (b64/hex SPEC drift, unrelated to this PR)

Follow-ups

  • SKILL.md doesn't currently have a string-literals section; once the agent skill grows one, the triple-quoted form belongs there too.

Triple-quoted ("""...""") strings desugar to the existing single-
quoted literal in the lexer, so logos' string regex consumes them and
every downstream stage (parser, interpolation, escape decoding) stays
unchanged. A scanning pass finds the closing """ and emits a
synthesised single-quoted form with raw newlines preserved.

When the closing """ sits on its own line, strip_triple_indent drops
the leading newline and removes the common leading whitespace from each
content line, matching Python PEP 257 and Rust's indoc! macro. The
terminating newline of the last content line is preserved. Inline form
(closing on a content line) keeps the body verbatim with no dedent.

Span attribution maps every emitted byte back to its original source
byte so diagnostics still point at the right location.
Nine regression tests pin behaviour across the VM and Cranelift JIT:
single-line form, inline multi-line, dedented multi-line, content-byte
verification, escape decoding, {name} interpolation (single and multi-
line), empty body, and embedded single quote. A backend drift can't
silently re-break the surface.

Adds examples/triple-quoted-strings.ilo so the examples_engines harness
exercises the feature on every engine and so agents reading the
examples directory see the canonical shape in context.
SPEC.md gains a Triple-quoted strings subsection under String Literals
covering raw newlines, dedent rules, escape passthrough, interpolation
parity, and the embedded-single-quote edge case. ai.txt gets the
token-minimal agent-spec entry inline next to the existing escape
table.
@danieljohnmorris danieljohnmorris merged commit bff5062 into main May 21, 2026
1 of 4 checks passed
@danieljohnmorris danieljohnmorris deleted the fix/multiline-strings branch May 21, 2026 18:05
@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
4727 2 4725 0
View the top 2 failed test(s) by shortest run time
ilo::examples_engines::examples_all_engines
Stack Traces | 8.35s run time
thread 'examples_all_engines' (42021) panicked at tests/examples_engines.rs:235:9:
9/1153 multi-engine example test(s) failed:

crypto-primitives.ilo [vm] (line 42): `ilo .../ilo/examples/crypto-primitives.ilo --vm sha-empty`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43255) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 44): `ilo .../ilo/examples/crypto-primitives.ilo --vm sha-abc`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43262) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 46): `ilo .../ilo/examples/crypto-primitives.ilo --vm hmac-rfc2`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43269) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 48): `ilo .../ilo/examples/crypto-primitives.ilo --vm b64-roundtrip`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43274) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 50): `ilo .../ilo/examples/crypto-primitives.ilo --vm b64-padded`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43280) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 52): `ilo .../ilo/examples/crypto-primitives.ilo --vm hex-abc`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43284) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 54): `ilo .../ilo/examples/crypto-primitives.ilo --vm ct-eq-yes`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43288) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 56): `ilo .../ilo/examples/crypto-primitives.ilo --vm ct-eq-no`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43292) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 58): `ilo .../ilo/examples/crypto-primitives.ilo --vm ct-eq-len`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43298) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
ilo::examples::examples
Stack Traces | 8.38s run time
thread 'examples' (42020) panicked at tests/examples.rs:158:9:
9/1167 example test(s) failed:

crypto-primitives.ilo (line 42): `ilo .../ilo/examples/crypto-primitives.ilo sha-empty`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43112) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 44): `ilo .../ilo/examples/crypto-primitives.ilo sha-abc`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43119) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 46): `ilo .../ilo/examples/crypto-primitives.ilo hmac-rfc2`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43126) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 48): `ilo .../ilo/examples/crypto-primitives.ilo b64-roundtrip`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43132) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 50): `ilo .../ilo/examples/crypto-primitives.ilo b64-padded`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43138) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 52): `ilo .../ilo/examples/crypto-primitives.ilo hex-abc`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43146) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 54): `ilo .../ilo/examples/crypto-primitives.ilo ct-eq-yes`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43152) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 56): `ilo .../ilo/examples/crypto-primitives.ilo ct-eq-no`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43158) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 58): `ilo .../ilo/examples/crypto-primitives.ilo ct-eq-len`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43164) panicked at src/vm/mod.rs:1877:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant