Concepts
The .quilt file format
A .quilt file is ordinary source code in some ground language plus Quilt bracket syntax layered on top. The ground language is inferred from the file's inner extension:
| Filename | Ground language |
|---|---|
foo.rs.quilt | Rust |
foo.py.quilt | Python |
foo.wgsl.rs.quilt | Rust (ground), WGSL (default for bare quotes) |
When expanded, the .quilt suffix is stripped to produce the output file name.
Operator glyphs
All Quilt operators are Unicode characters. The VS Code extension provides chord keybindings so they are easy to type (see Editor Setup).
| Glyph | Name | Meaning |
|---|---|---|
↖ | left-quote | Open a quote bracket |
↗ | right-quote | Close a quote bracket |
↙ | left-unquote | Open an unquote bracket |
↘ | right-unquote | Close an unquote bracket |
↑ | lift | Convert a runtime value into a QTerm |
↓ | reduce | Evaluate a QTerm by compiling and running it |
← | emit | Append a term into the surrounding variadic block |
⟨T⟩ | type | Placeholder for Arc<QTerm> in bootstrap source |
⟨N⟩ | name | Create an identifier node |
\↑ | escape-lift | Literal ↑ in source (not a Quilt operator) |
\↓ | escape-reduce | Literal ↓ in source (not a Quilt operator) |
Quilt-level line comments are written ⟨//⟩ ... and block comments ⟨/*⟩ ... ⟨*/⟩. They are stripped during parsing and never appear in the output.
Quote and unquote
Quote ↖…↗
A quote bracket wraps a code fragment and turns it into a value — a QTerm that can be inspected, transformed, and emitted later:
// In a .rs.quilt file:
let expr: Arc<QTerm> = ↖1 + 2↗;
This expands to Rust code that constructs the 1 + 2 AST at runtime using the QTermBuilder API:
let expr: Arc<QTerm> = tb("binary_expression")
.c(&leaf("integer_literal", "1"))
.w(" + ")
.c(&leaf("integer_literal", "2"))
.b();
An annotation before ↖ specifies the language of the quoted fragment:
let html_frag = html↖<p>Hello</p>↗;
let wgsl_frag = wgsl↖vec4<f32>(1.0, 0.0, 0.0, 1.0)↗;
An un-annotated ↖…↗ defaults to the host language, or to the second language in the file's extension chain (e.g. shaders.wgsl.rs.quilt → bare quotes default to WGSL).
Unquote ↙…↘
An unquote bracket splices an already-built term into a quote:
let one = ↖1↗;
let expr = ↖↙one↘ + 2↗; // splices `one` at the hole position
The inner content of an unquote is code in the ground language evaluated at code-generation time.
Pattern matching let ↖…↗ = …
A quote in the binding position of a Rust ground let is a pattern: instead of building a term, it destructures the value term by matching its shape. Each ground unquote inside the pattern must be a plain identifier and becomes a metavariable that binds the matching part of the value:
let py↖def f(↙args↘): pass↗ = py↖def f(x: int): pass↗;
// args now holds a term that coparses to "x: int"
The statement expands to a destructuring of qmatch_n (see qmatch.rs):
let [args] = qmatch_n(&/* pattern with mvar("args") markers */, &/* value */);
Matching is syntactic: both sides are compared as the source text they coparse to, anchored at both ends and whitespace-sensitive. Each metavariable binds the text between the surrounding literals (leftmost-shortest when ambiguous; two adjacent metavariables are rejected) as a leaf term that splices back verbatim. A failed match panics at generation time — the expanded let is irrefutable.
This is distinct from rewrite_naive (term-equality rewriting): pattern lets match during expansion, like macro patterns in Rust or Racket.
Quasi-quoting and staging
Quilt is a two-level language. The ground level runs at code-generation time; the sky level (inside ↖…↗) is data that will be serialized to the output file.
Stage in the engine tracks the current depth:
Stage::Ground— executing code. Quotes capture their content and reduce become evaluations.Stage::Sky(lang, depth)— insidedepthlevels of nested quotes.
Nested quotes increment the depth; unquotes decrement it. An unquote whose depth would reach zero is escaped back to ground and its content is evaluated.
Indentation and whitespace
Quilt automatically dedents the content of a quote block. Leading whitespace common to all lines inside ↖\n…\n↗ is stripped, so deeply-nested quoted code stays readable:
let block = ↖
for i in 0..10 {
println!("{i}");
}
↗;
// The common four-space indent is stripped before parsing.
Variadic nodes and emit ←
Some language nodes accept an arbitrary number of children — block and source_file in Rust, for example. Inside a variadic quote, ← (emit) appends a term to the growing list:
let program = ↖{
↙{
for i in 0..n {
↖println!("{i}");↗.←;
}
}↘
}↗;
The unquote ↙{…}↘ runs a Rust loop at generation time; each iteration emits one println! statement into the outer block.
Lift ↑ and reduce ↓
↑ (lift) converts a value into a QTerm. What "lift" means is language-specific:
- Rust
↑— callsqlift()on the value, building a term whose code reconstructs it. Integers becomeinteger_literalnodes; strings becomestring_literalnodes;Arc<QTerm>values lift themselves recursively. - Python
↑— similar, but written prefix:↑(x)expands to thequiltmodule'sqlift(x)function (a method can't hang off builtin ints), or toqlift_html(x)when the lift targets an HTML quote. - Heterogeneous
↑— inside a quote of another language (e.g.python↖ … ↙x.↑↘ … ↗), Rust's↑expands toqlift_to::<L>()for that target language, producing a term in the target's syntax.LiftToimpls exist for Python, WGSL, Zsh, and Bash (seelift.rs); e.g. a RustVec<u64>lifts into Python as alistliteral, and a Rust string lifts into zsh as a properly escaped double-quoted word.
The operators ↑ (lift), ↓ (reduce), and ← (emit) are staged like the brackets around them. An operator only spells itself out (qlift(…), reduce(), …) once it reaches ground — when its enclosing unquotes bring it back to the running stage. An operator still nested inside an unresolved quote belongs to a later stage, so it is deferred: it survives expansion as its literal glyph, which the generated code spells out when it runs. So a nested generator emits a real operator for its output to expand — e.g. py↖ … py↖ … ↙↑(a)↘ … ↗ … ↗ coparses with a literal ↙↑(a)↘, not a prematurely-spelled ↙qlift(a)↘.
↓ (reduce) evaluates a QTerm by compiling it (via rust-script for Rust) and deserializing the result using postcard:
let n: i32 = ↓↖21 + 21↗; // evaluates to 42 at generation time
The ⟨T⟩ and ⟨N⟩ operators
These are used internally in bootstrap source:
⟨T⟩expands toArc<QTerm>— the canonical type of a quilt term in Rust meta-code.⟨N⟩creates anidentifiernode from a string — useful when building code that references a named variable.
Why the arrows?
The quote glyphs ↖↗ and unquote glyphs ↙↘ are a synthesis of the two historical notations for quasi-quotation:
- Quine corners
⌜⌝— introduced by W.V.O. Quine in Mathematical Logic (1940) as the standard logical notation for quasi-quotation. The corner bracket signals "treat the contents as a name of an expression, with holes where values can be substituted." - Lisp backtick
`— the quasi-quote operator used in Lisp since the 1970s, where`(foo ,bar)quotes the list with,baras an unquote splice. Almost every modern language's macro system (Scheme, Clojure, Rust'squote!, Julia's:(...)) descends from this convention.
Both traditions use distinct paired delimiters to mark the boundary between "code that runs" and "code that is data." Quilt's arrows extend this to two pairs — ↖↗ for quote and ↙↘ for unquote — so both levels are visually present at once. The direction mirrors the lift/reduce operators: the upward-pointing quote arrows (↖↗) lift code up into a term, just as ↑ does to a value; the downward-pointing unquote arrows (↙↘) bring a term back down into running code, just as ↓ evaluates one.