Compiler design / 09. Templates and TIR

First-class templates keep structure in AST-local TIR, then hand folded strings or neutral runtime data downstream.

Why templates need their own temporary IR

Look at the page fragment again:


    [$html:
        <p>[greet(visitor)]</p>
    ]
    

A template is not a string with holes poked by hope. It contains structure: wrappers, slots, child values, control flow and placement order. Flatten it too early and you lose the skeleton the checker and the runtime both need.

Moth gives templates a first-class home and a temporary Template IR (TIR) that lives only while AST work runs.

Template structure

The fragment wants a paragraph wrapper, an interpolation of a string-producing call and a stable place in the page's fragment order.

If the compiler treated the body as blind concatenation, it could still emit characters on a happy path. It would struggle to:

Structure earns its keep.

Diagram placeholder: Paragraph wrapper contains a dynamic name slot

Caption: teaching structure. Question: what does interpolation mean once structure exists?

TIR phases

One module AST build owns one TemplateIrStore. Parser emission writes text, expressions, child templates, slots, inserts, wrappers and control-flow roots directly into that store.

The phase sequence is:


    Parsed -> Composed -> Formatted -> Finalized
    

Teaching names for the parts:

Exact view identity includes root, phase and value context. Consumers read through that authority rather than inventing parallel overlay stacks.

Folding requires Composed or later. AST-to-HIR handoff requires Finalized.

Diagram placeholder: One template passes through four structural phases

Caption: accepted phase names, simplified records. Question: where does template structure live during AST?

Folded and runtime paths

AST finalisation walks prepared views and chooses a fate:

Compile-time fragments never live in HIR. HIR receives ordinary string and control-flow work for runtime pieces only. The TIR store drops before the completed AST leaves the stage. It is temporary scaffolding, not permanent structure. No TIR id tours a public interface or backend.

For the greeting project, a fully static visitor and prefix can collapse toward the familiar fragment. If visitor were dynamic input, the structure would still check at compile time while runtime handoff carried the moving parts.

Diagram placeholder: Static pieces become a string while dynamic pieces become neutral runtime handoff data

Caption: exact boundary, simplified shape. Question: what reaches HIR?

Source-kind adapters

Moth template .mtf and Markdown .md files do not invent forever-parallel compilers. They adapt into ordinary synthetic content #String declarations.

Later ordering and folding treat both as ordinary compile-time constants. Return to the greeting project after that side path: @page.moth remains the star, and document kinds prove the "one semantic pipeline" bet.

Diagram placeholder: moth, mtf and md reach one ordinary semantic path

Caption: simplified source-kind map. Question: how does document content enter the compiler without a second IR forever?

Reactivity boundary

Reactivity in Moth is a constrained template and UI source-and-sink model. It is not a general closure system and not a second semantic IR.

Durable ownership:

If you need arbitrary higher-order function values, this is the wrong language feature to stretch.

What can fail here

TIR ownership must stay distinct from AST finalisation and HIR validation. Post-TIR parser optimisation plans may discuss performance. They do not redefine the semantic handoff.

Why Moth does it this way

Alternative: general macros, JSX-style external transforms or a separate markup compiler with its own type system.

Choice: first-class constrained templates with AST-local TIR.

Benefit: template structure gains semantic checks without a second backend language.

Cost: TIR needs strict lifetime and handoff rules. It is not a public interface for users or backends.

Roadmap and current status

Exit state

You leave with finalised TIR handoff, folded fragments and neutral runtime template data. Next: how a failure should travel through all these owners.

Compiler design / Previous / Next