Skip to main content

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:

FilenameGround language
foo.rs.quiltRust
foo.py.quiltPython
foo.wgsl.rs.quiltRust (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).

GlyphNameMeaning
left-quoteOpen a quote bracket
right-quoteClose a quote bracket
left-unquoteOpen an unquote bracket
right-unquoteClose an unquote bracket
liftConvert a runtime value into a QTerm
reduceEvaluate a QTerm by compiling and running it
emitAppend a term into the surrounding variadic block
⟨T⟩typePlaceholder for Arc<QTerm> in bootstrap source
⟨N⟩nameCreate an identifier node
\↑escape-liftLiteral in source (not a Quilt operator)
\↓escape-reduceLiteral 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) — inside depth levels 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 — calls qlift() on the value, building a term whose code reconstructs it. Integers become integer_literal nodes; strings become string_literal nodes; Arc<QTerm> values lift themselves recursively.
  • Python — similar, but written prefix: ↑(x) expands to the quilt module's qlift(x) function (a method can't hang off builtin ints), or to qlift_html(x) when the lift targets an HTML quote.
  • Heterogeneous — inside a quote of another language (e.g. python↖ … ↙x.↑↘ … ↗), Rust's expands to qlift_to::<L>() for that target language, producing a term in the target's syntax. LiftTo impls exist for Python, WGSL, Zsh, and Bash (see lift.rs); e.g. a Rust Vec<u64> lifts into Python as a list literal, 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 to Arc<QTerm> — the canonical type of a quilt term in Rust meta-code.
  • ⟨N⟩ creates an identifier node 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 ,bar as an unquote splice. Almost every modern language's macro system (Scheme, Clojure, Rust's quote!, 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.