Compiler Design

A compiler is a translator that preserves meaning: source text in, a different-but-equivalent representation out. Its structure — front-end, middle-end, back-end — is the backbone every later Phase-2 topic plugs into.

The Three-End Pipeline

Almost every compiler decomposes into the same three stages, each with its own inputs, outputs, and error classes.

Compiler pipeline: front-end, middle-end, back-end, target, with errors at every stage

Figure 1 — Each stage narrows the problem; each reports its own kind of error.

Front-End

Source to validated tree: lexer, parser, and semantic analysis (the Phase-1 topics). All errors here are about the program’s text and meaning.

Middle-End

Tree to intermediate representation, transformed by optimization. This is where compilers earn their keep for large languages; DSLs often skip or shrink it.

Back-End

IR to the target: bytecode for a VM, native code, or a call into a heavy tool like LLVM (Phase 3). DSL back-ends are usually tiny because the IR is tiny.

Design Decisions

Compiler vs. Interpreter

Compile-then-run separates translation from execution (startup cost, later find bugs); interpreting executes the tree directly (faster to build, slower at runtime). The pragmatic DSL choice: compile to bytecode, interpret bytecode, and keep the deck open for later optimization.

Single-Pass vs. Multi-Pass

Single-pass compilers emit as they parse (simple, restrictive); multi-pass compilers build a tree, analyze, then emit (flexible, debuggable). Multi-pass wins for DSLs: readability of each pass beats a few saved clock cycles.

Host and Target

Pick the host language for the compiler (fast to write, easy to embed) and the target (interpreter, bytecode, native, another DSL). Writing your DSL compiler in the DSL itself is elegant later — for a first version, choose the boring, well-tested host.

Correctness Strategy

A compiler is worth exactly as much as its tests: it is a deterministic function from source to code, so almost everything is testable.

Test Ladder

Unit-test each pass on tiny programs; keep golden files (input plus expected output) for every example in the docs; and run a corpus of bad programs to verify each diagnostic still fires.

Property Checks

For expressions, compare the result of running compiled and interpreted versions of the same input (differential testing); for parsers/printers, round-trip source through parse–print and assert identity.

Errors Are a Pipeline Feature

Which Stage Owns Which Error

Lexical and syntax errors belong to the front-end; undefined names and type mismatches to semantic analysis; impossible instructions to the back-end. Every stage must fail loudly, with the message style from Errors & Diagnostics, so the user always knows which contract was broken.

Never Swallow a Stage

A DSL that silently accepts a program it cannot compile teaches users that the tool lies. Prefer an explicit “not yet implemented” diagnostic over a silent wrong answer.

Example: The Driver Loop

The architecture fits in ten lines: each stage is a function from one artifact to the next, and the pipeline is the program.

main_compile() (commented)

# driver.py — the whole pipeline as a function chain
def main_compile(source: str):
    tokens = lex(source)          # front-end 1: characters -> tokens
    tree   = parse(tokens)        # front-end 2: tokens -> AST
    check(tree)                   # front-end 3: names, types (Semantic Analysis)
    ir     = lower(tree)          # middle-end: AST -> three-address IR
    bc     = emit(ir)             # back-end:   IR -> bytecode
    dump(bc)                      # a golden file for the test ladder
    vm_run(bc)                    # bytecode into the Virtual Machine

How to Read It

Every later Phase-2 page fills in exactly one function: Semantic Analysis the check, Intermediate Representations the lower, Bytecode & Codegen the emit, Virtual Machines the vm_run.

Next Steps

Continue Phase 2

Next the tree becomes checked and decorated: Semantic Analysis, then Intermediate Representations.

Resources