← All posts

What Happens When You Hit Run

You type some Swift, press Run, and a moment later it's executing — printing numbers, drawing a view, formatting a date. No Xcode. No Apple toolchain. No server doing the work behind your back. Often it's running right inside the browser tab.

So how does that actually work?

This post is the friendly tour. No file paths, no line numbers — just the journey your code takes, from the text you typed to a program that runs. (There's a deep, file-by-file version too, for the people who want to build one of these.)

The whole thing in one breath

A compiler is a translator. And like any good translator, it does not work letter by letter. If you translate a novel into another language, you read the words, understand the sentences, grasp the meaning, and only then rewrite it in the target language. MiniSwift does exactly that, in six steps:

  1. Read the words — chop the soup of characters into tokens.
  2. Figure out the grammar — arrange the words into a sentence-diagram (a tree).
  3. Figure out the meaning — work out what everything is (types) and catch nonsense.
  4. Boil it down — rewrite the rich Swift as a tiny, dumb to-do list.
  5. Translate to the target — turn that to-do list into either real machine code or WebAssembly.
  6. Bring the batteries — link in the standard library so print, arrays, and dates actually do something.

Let's walk a single line of Swift through all six. Our victim:

let total = 2 + 3 * price

1. Read the words

To a computer, that line is just a stream of characters: l, e, t, space, t, o… The first job — the lexer — is to group those characters into meaningful chunks, the way your eye instantly groups letters into words instead of reading one letter at a time.

Out comes a flat list of tokens:

let   total   =   2   +   3   *   price
kw    name   op  num op num op  name

That's it. The lexer doesn't understand math or types yet. It just knows "this clump is a keyword, this one's a number, this one's a name." It's fast and dumb on purpose — and in MiniSwift it's hand-written and SIMD-accelerated, chewing through identifiers eight bytes at a time.

2. Figure out the grammar

A list of words isn't a sentence. The parser arranges the tokens into a tree that captures structure — including the rule you learned in school that * binds tighter than +:

        let total =
             │
            (+)
           ╱   ╲
          2    (*)
              ╱   ╲
             3   price

This tree (compiler people call it an AST — Abstract Syntax Tree) is the real shape of your program. 3 * price is one branch; the whole thing is "assign the result of 2 + (3 * price) to total." Every if, every function, every loop becomes a branch in this tree.

At this point the program is structured — but still meaningless. The parser doesn't know what price is, or whether any of this makes sense.

3. Figure out the meaning

This is where it gets clever. The semantic pass walks the tree and works out what everything actually is:

  • price — is that an Int? a Double? It looks it up.
  • 2 + 3 * price — if price is a Double, the whole expression is a Double, so total is a Double.
  • It also catches nonsense: you can't add a number to a window. If you tried, this is the stage that stops you with an error.

It's also the stage that makes the magic of "no Apple SDK installed" possible. When you write import UIKit and reference UIView, how does MiniSwift know UIView is a real type? It ships with a vocabulary — a baked-in catalog of ~361 Apple frameworks and the names inside them. So UIView, CGRect, NumberFormatter all resolve as real types, with zero gigabytes of SDK on disk.

The output isn't a new file — it's the same tree, now with a little tag stapled to every branch saying what type it is. A typed tree.

4. Boil it down to a to-do list

Here's the key trick of every serious compiler. Swift is a huge language — generics, closures, optionals, protocols, property wrappers. Translating all of that richness directly into machine code would be a nightmare.

So instead, MiniSwift first rewrites your program into a tiny, boring, unambiguous to-do list — an intermediate representation (the IR). It has maybe two dozen kinds of instruction, all dead simple:

InstructionMeans
load / storeread or write a variable
add, mul, cmpdo one piece of arithmetic or a comparison
callcall a function
jump / branchgo to another spot (this is how if and loops are built)
retreturn from a function

Our one line of Swift becomes a handful of these:

t0 = load price
t1 = mul 3, t0      ;  3 * price
t2 = add 2, t1      ;  2 + (3 * price)
store t2, total

Why bother? Two reasons. First, simple is translatable: turning two dozen dumb instructions into a real machine is way easier than turning all of Swift into one. Second, it's the fork in the road — this same to-do list can be sent down two completely different roads, which is the whole reason MiniSwift can target both a Mac and a browser.

(Before the fork, the compiler also does a cleanup pass: it stamps out generics into concrete versions, and — if you ask for optimization — simplifies the to-do list, folds constants, deletes dead steps. Think of it as proofreading the recipe before cooking.)

5. Translate to the target

Now the to-do list gets turned into something a machine can actually run. MiniSwift has two translators, and you pick one:

Native (the "real" road)WebAssembly (the "anywhere" road)
What it makesC code → a real native binarya .wasm file
Runs onyour Mac, full speedany browser tab, instantly
Howhands the C to clanghand-assembled bytes, no toolchain
Used formatching real Swift, serious runsthe live Studio

The native road rewrites each to-do instruction as a line of C, then lets the battle-tested clang compiler turn that into a fast native executable. This is the primary path — it's how we check that MiniSwift's answers match real Apple Swift.

The WebAssembly road assembles the to-do list directly into a .wasm module — the portable bytecode every modern browser runs natively. No external assembler, no install. This is the road that lets you run Swift in a browser tab on the studio.

Both roads start from the exact same to-do list, so — by design — they give the same answer. If they ever disagree, that's a bug, and we hunt it down.

6. Bring the batteries

One last thing. Your program says print(total) — but where does print actually live? It's not something the compiler invents. print, arrays, dictionaries, dates, string formatting — these are a small runtime library that ships alongside.

On the native road it's a C library linked into your binary. On the WebAssembly road it's a companion .wasm (plus a thin sprinkle of JavaScript for things like the current date). Same idea both ways: the compiler emits a call to print, and the runtime is what makes that call do something.

Wait — the compiler runs in my browser?

Here's the part that breaks people's brains, and it's worth sitting with for a second.

MiniSwift's compiler is itself a C program. And one of its targets is… WebAssembly. So we point the toolchain at itself and compile the compiler into a .wasm.

That means when you use the studio, your browser downloads a WebAssembly compiler, and that compiler — running inside your tab — takes the Swift you just typed and compiles it to WebAssembly, which then runs, also in your tab. Swift, compiled to WebAssembly, by a Swift compiler that is itself WebAssembly. No server ever sees your code. It's turtles all the way down, and it's genuinely one of the coolest things about the project.

See it for yourself

Every stage we just walked through is something you can actually look at. The compiler can dump each one:

Ask for……and you see
the tokensstep 1, the words
the ASTstep 2, the grammar tree
the IRstep 4, the to-do list
native C / WebAssemblystep 5, the final translation

Or skip straight to the fun part: paste some Swift into the studio, hit Run, and know that in the half-second before your output appears, all six of these steps just happened — inside the tab you're looking at.

That's the whole trick. Read the words, understand the grammar, work out the meaning, boil it down to a dumb little list, translate that list to a real machine or to the browser, and bring the batteries. A compiler isn't magic. It's just a very, very careful translator.

← All posts