Vibe Coding a Compiler: From Natural Language to LLVM IR
I vibe code a compiler for a custom toy language. I didn’t write the tokenizer. I didn’t memorize the LLVM IR syntax. Instead…

Vibe Coding a Compiler: From Natural Language to LLVM IR
There is a misconception that vibe coding — the practice of building software primarily through natural language prompting and LLM iteration — is reserved for frontend, scripts, and boilerplate. The assumption is that for “hard engineering” like compiler construction, you need to hand-roll your lexer and suffer through segmentation faults manually.
I spent the evening proving that wrong.
I vibe coded a compiler for a custom toy language. I didn’t write the tokenizer. I didn’t memorize the LLVM IR syntax. Instead, I acted as the Architect and the Vibe Manager, while the LLM acted as the Implementer.
Here is the technical breakdown of how to build a compiler not by typing code, but by iterating on intent.
The Stack
- Language Implementation: Python
- Target: LLVM IR (to make it a “real” compiler)
- The Vibe Partner: Antigravity with Gemini 3
Step 1: Defining the “Vibe” (The Grammar)
In traditional development, you start with a formal EBNF (Extended Backus–Naur form) grammar. In vibe coding, you start with examples.
I wanted a language that felt like a mix of Rust and Python but extremely minimal. I prompted the LLM not with a spec, but with a “vibe check.”
Prompt: “I want to design a language called ‘VibeLang’. It should be statically typed but use significant indentation. Here is a snippet of how I want it to look:
*fn add(x: int, y: int) -> int:`` return x + y*
*let result = add(5, 10)*
Infer the formal grammar for this language and generate a tokenizer using Python’s
tokenizelibrary."

The LLM hallucinated a grammar. It was mostly correct. We iterated. I didn’t touch the regex; I just complained when the tokenizer failed on edge cases.
Step 2: The Abstract Syntax Tree (AST)
This is where vibe coding shines. Writing AST node classes is tedious boilerplate. It is the definition of “unfun” work.
Prompt: “Based on the grammar we defined, generate a Python class hierarchy for the AST. I need nodes for BinaryOp, FunctionDef, LetBinding, and Return. Make it compatible with the Visitor pattern.”
The model spit out perfect dataclasses. But here is the crucial technical lesson: Vibe coding requires you to know what to ask for. If I hadn’t asked for the “Visitor pattern,” the LLM might have given me a messy, coupled traversing logic. You still need to be the engineer.

Step 3: The Semantic Analysis (The Logic Check)
This is the hardest part to vibe code. A compiler must check types. If I try to add a String to an Int, the compiler must yell at me.
I asked the LLM to write a TypeChecker class. It failed multiple times.
- Attempt 1: It forgot to handle variable scope (shadowing).
- Attempt 2: It couldn’t resolve function return types before the function body was fully parsed.
The Fix: instead of trying to debug the code, I debugged the prompt.
Prompt: “Refactor the TypeChecker. Split it into two passes. Pass 1: Collect all function signatures and store them in a symbol table. Pass 2: Visit the function bodies and validate types against that table.”
The “vibes” aligned. The code worked.
Step 4: Generating LLVM IR (The Dark Magic)
Writing LLVM IR (Intermediate Representation) by hand is like writing assembly logic for a generic processor. It is verbose and brittle.
This is the killer use case for LLMs. They have read the entire LLVM documentation. I haven’t.
Prompt: “Write a CodeGen class that takes our AST and emits LLVM IR using
llvmlite. It needs to compile theaddfunction we defined earlier. Ensure we use standard entry blocks."

The output was shockingly close to working. It handled the alloca, store, and load instructions necessary for SSA (Static Single Assignment) form simulation.
The Workflow: Iterate vs. Write
The process didn’t feel like coding. It felt like code review.
- Generate: Ask for a feature (e.g., “Add support for ‘if’ statements”).
- Verify: Run the test suite (which I also asked the LLM to write).
- Crash: The compiler crashes on nested
ifs. - Refine: “You are handling the basic blocks wrong. After the ‘then’ block, you need to branch to the merge block, not the next instruction.”
- Fix: The LLM corrects the logic.
Conclusion: The Shift in Competence
Vibe coding a compiler demonstrated a shift in what is required of an engineer.
- Old Skill: Knowing the exact regex syntax for a floating-point literal.
- New Skill: Knowing that you need a Lexer before a Parser, and understanding why LLVM requires basic blocks to be terminated.
You cannot vibe code a compiler if you don’t understand how compilers work. The AI is an incredibly fast junior developer with encyclopedic knowledge but zero architectural intuition. You provide the intuition; it provides the syntax.
The compiler works. It compiles VibeLang to a binary executable. And I wrote less than a handful of prompts.
메타데이터
- post_id
- f4e192a49260
- slug
- vibe-coding-a-compiler-from-natural-language-to-llvm-ir-f4e192a49260
- url
- https://medium.com/@fhinkel/vibe-coding-a-compiler-from-natural-language-to-llvm-ir-f4e192a49260
- canonical_url
- https://medium.com/@fhinkel/vibe-coding-a-compiler-from-natural-language-to-llvm-ir-f4e192a49260
- author_url
- https://medium.com/@fhinkel
- status
- ok
- fetched_at
- 2026-06-13 16:00:06