DEV Community

Cover image for The Day My Language Started Compiling Itself
Javier Leandro Arancibia
Javier Leandro Arancibia

Posted on • Originally published at blog.intrane.fr

The Day My Language Started Compiling Itself

A year ago I built a programming language. This year it crossed the line that separates a toy from a real compiler: it started compiling itself.

watch machin compile itself

(That GIF is the real thing — machin compiling its own compiler, then that binary reproducing its own source byte-for-byte.)

That sentence sounds like a party trick. It isn't. It's one of the oldest and hardest proofs in systems engineering — and reaching it, with AI as my pair, is the clearest evidence I can offer of how I actually build software.

First, the backstory

machin is a language I designed for a specific user: the AI agent writing the code, not the human reading it. Zero type annotations, one canonical declaration per line, every design choice measured in tokens — then compiled straight through C to a single native binary. Script-like to write, C-class to run. (I wrote about why in an earlier post.)

For a year it grew the way real tools grow — by being used. Web servers, database drivers, a WebSocket client, crypto, even games. But a language is only as trustworthy as its compiler, and machin's compiler was written in Go. So I asked the question every language eventually has to answer:

Could machin compile itself?

What "compiles itself" actually means

Every serious compiler faces a rite of passage called the bootstrap. You rewrite the compiler in its own language, and then you use the original to compile the new one. If the language is real — expressive enough, correct enough, fast enough — the new compiler works. If it isn't, you find out fast.

The gold standard is the fixpoint:

  1. The original compiler builds the self-hosted one.
  2. That self-hosted compiler compiles its own source into a fresh native binary.
  3. That fresh binary compiles the same source again — and the output is byte-for-byte identical.

When those bytes match, you have proof that isn't a matter of opinion. The compiler has reproduced itself exactly. It stands on its own.

Machin now does this. The whole pipeline — lexer, parser, type checker, and C code generator, about 4,000 lines — is written in machin, and it emits the same machine code as the original compiler down to the byte.

How it was actually built (this is the part that matters)

I don't ship "it works on my machine." I ship proof. So every single stage was built against a byte-diff oracle: the original compiler and the new one were run on the same input and their output compared, character for character, across the entire ecosystem of real programs I'd already written. A stage wasn't "done" until that diff was empty — not on a test case, but on every program in the corpus.

That discipline paid for itself immediately. Building the compiler in its own language stress-tested the language harder than a year of app-building had, and it flushed out three genuine bugs in the original compiler that had been hiding in plain sight — a string-comparison that compared memory addresses instead of contents, a quadratic slowdown in string handling, an escaping edge case in code generation. Each was found because the self-hosted compiler and the reference disagreed by a byte, and I refused to wave it away.

Then the honest numbers. The self-hosted compiler doesn't just work — on the full parse-typecheck-codegen of its own source, it runs at about 0.9× the original's speed. Slightly faster than the compiler that built it. (It didn't start there — the first version was 7× slower, and closing that gap meant finding the real bottleneck instead of the obvious-looking one. It was string building, not the "slow lookups" everyone assumes.)

Why I'm telling you this

Because this is what I mean when I say I build with AI.

Not "I prompted a chatbot and shipped whatever came out." The opposite. AI was the engine, but the engineering — the oracles, the byte-level verification, the refusal to accept a passing test over a passing proof, the honesty about what's fast and what isn't — that's the part that makes the output trustworthy. AI without that rigor produces plausible code. AI with it produces a compiler that compiles itself, byte-for-byte, and you can go check the bytes.

That combination — senior engineering judgment as the guardrail, AI as the multiplier — is exactly what I bring to the systems I build for clients at Intrane. Most teams get one or the other: rigor without speed, or speed without rigor. The interesting work lives where they meet.

A language that compiles itself is a hard, verifiable, slightly obsessive proof that they can meet. If that's the kind of engineer you want on your hardest problem, let's talk.


machin is open source (MIT) — the self-hosting compiler lives in selfhost/, and you can reproduce the fixpoint yourself.

Top comments (0)