Blu — A Compiled Programming Language 🛠 In Progress

A compiled programming language built from scratch. Blu has a full compiler pipeline, runs on Linux, produces native x86-64 assembly, and has forms, closures, file I/O, and 400 regression tests. Written because the right tool for the job didn't exist yet.

compiler language c++ x86 linux from-scratch
Blu — a compiled programming language written from scratch, targeting x86-64 Linux

Build Story

1. Why Build a Language

There is a particular kind of madness that visits certain programmers. It arrives late at night as a simple question: what if I just built my own language?
I had a problem the available tools weren't solving cleanly. The right language for the job didn't exist. So I started writing Blu.

2. Phases 1–3 — The Foundation

The early work was clean and methodical. Lexer first — make it deterministic. Parser next — make it reliable. Then semantic analysis: catch undefined variables, wrong argument counts, duplicate declarations. The kind of work nobody sees but everybody feels when it's missing.
Then freeze the semantics. Decide what the language actually is. Forms instead of structs. give instead of return. show instead of print. when instead of if. Small choices, but they add up to a voice. Blu began to sound like itself.
Then the standard library: JSON, CSV, databases as key-value arrays, binary encoding. The things you need the moment you try to do anything real.

3. Phase 4 — Regression Infrastructure

By phase 4 Blu had opinions. It had a style guide. It had regression tests — hundreds of them, organized by category, run by shell scripts that report pass rates. No phase closes until every test is green. This discipline is what keeps the language from quietly breaking while new features go in.

4. Phase 5 — Closures, Forms, and a Namespace War

Phase 5 is where the ambition showed its teeth. Closures — nested functions that capture variables from their enclosing scope — required environment objects, closure allocation, and a whole new family of IR instructions. Forms required type inference from usage patterns across the entire call graph.
Then the build broke. A cascade of errors. The cause: #include directives had been placed after the namespace blu { opening brace. The namespace had swallowed the standard library.
The fix was to delete ir_builder.cpp and rewrite it from scratch — pulling out layers of accumulated corruption, duplicate function bodies, orphaned copy-paste blocks. Each fix revealed the next problem. Each problem was small. Together they were a wall.
Forms edge cases: 113/113. Closure regression: 10/10. Phase 5 closed.

5. Phase 6 — File I/O and What Comes Next

Phase 6 started with the obvious gap: Blu couldn't touch the filesystem. file.read, file.write, file.append, file.exists. Four C symbols, four arity checks, four codegen entries. The test program wrote a file, read it back, and confirmed it existed. Small thing. Not a small thing.
What comes next: a map type, a garbage collector, better error messages with line numbers, a formatter, a REPL, a module system, a C FFI bridge, and eventually a platform layer. Eighteen phases in the plan. Five complete.
The tests are green. The compiler compiles. The binary runs.