Dea/L0 v0.9.0: A Self-Hosted Systems Language
Updated 2026-05-22: Dea/L0 has since reached v1.0.0. See the Dea/L0 v1.0.0 release announcement for the current release notes.
Updated 2026-05-13: Fixed links that broke after the monorepo restructure and main repository rename.
Today I’m releasing v0.9.0 of Dea/L0, the first level of the Dea systems programming language. This is the first public release, and the compiler is self-hosted: it compiles itself. Dea/L0 is designed to be practical for writing compilers and other low-level tools.
What is Dea?
Dea is a systems programming language built through staged bootstrapping. The language grows in levels: Level 0 compiles itself, Level 1 will be compiled by Level 0, and so on. At each stage the compiler is written in the language it compiles.
Dea/L0 is the foundation: a small language with C-family syntax, a C99 backend, and a strict no-undefined-behavior policy. Every operation is well-defined, rejected at compile time, or produces a named runtime error. There is no third option.
Why another language?
Writing a compiler is a systems programming task. You need to manage memory, represent complex data structures, and have precise control over performance. Many modern languages either remove that kind of control or bury it under runtime machinery.
Dea/L0 keeps a small, explicit core aimed at bootstrap-friendly implementation work. The type system has sum types and pattern matching, because a compiler without them is fighting its own data. It has explicit nullable types, because implicit null is a specification gap. It has deterministic resource management without a garbage collector. It has pointers without undefined behavior. It doesn’t need much else.
The surface syntax is C-family deliberately: braces, explicit types, return. Familiarity is load-bearing when the language is also the implementation vehicle.
A quick look
A hello world:
module hello;
import std.io;
func main() {
printl_s("Hello, Dea!");
}
A more involved example showing sum types and pattern matching:
module demo;
import std.io;
import std.string;
import std.text;
import std.system;
enum Expr {
Num(value: int);
Add(left: Expr*, right: Expr*);
Mul(left: Expr*, right: Expr*);
}
func eval(e: Expr*) -> int {
match (*e) {
Num(value) => { return value; }
Add(left, right) => { return eval(left) + eval(right); }
Mul(left, right) => { return eval(left) * eval(right); }
}
}
Expr is a sum type (tagged union). match is exhaustive. Pointers are non-null by default (Expr*); nullable pointers require an explicit Expr*?. Memory allocated with new is freed with drop, and with/cleanup blocks provide deterministic scoped resource management. The full demo.l0 example also uses heap allocation, recursive descent parsing, and scoped cleanup.
Strings are ARC-managed value types: you assign, pass, and return them like integers, and the runtime handles the reference counting.
Self-hosting and the triple bootstrap
The Dea/L0 compiler has two stages:
- Stage 1 is written in Python. It was the bootstrap implementation, and it still kicks off the build chain.
- Stage 2 is written in Dea/L0 itself. It has full CLI parity with Stage 1 and is the compiler that ships in this release.
Self-hosting is verified through a strict triple-bootstrap test:
- Compiler 0 (Stage 1) compiles Stage 2 source into Compiler 1.
- Compiler 1 compiles Stage 2 source into Compiler 2.
- Compiler 2 compiles Stage 2 source into Compiler 3.
The important point is that the compiler converges to a stable self-built artifact.
Compilers 2 and 3 must match byte-for-byte in the retained generated C. On stable host toolchains, the normalized native binaries must match as well. This is the fixed-point proof: the compiler has reached a stable self-hosted form.
The installed binary is Compiler 2, not Compiler 1. In other words, the release ships the compiler built by Stage 2 itself, not the direct Stage 1 bootstrap output.
What’s in the release
This release is the first complete public snapshot of the language, compiler, tooling, and release pipeline.
On the language side, Dea/L0 includes the core pieces needed to write a compiler in itself: int, bool, byte, and ARC-managed string; structs and enums with payloads; pointers and explicit nullable types; if, while, C-style for, match, case, and with/cleanup; and a simple module system with dotted module names and module::Symbol qualified access. The semantics stay deliberately tight: no undefined behavior, no implicit narrowing, and no assignment as an expression.
On the tooling side, l0c compiles to C99 and then invokes a host C compiler such as gcc, clang, or tcc, with overrides available through --c-compiler. The main modes are --build, --run, --check, and --gen, with additional AST, token, and type dump modes for development. The compiler also has built-in observability flags, --trace-arc and --trace-memory, and --version reports build provenance including git commit, host platform, build timestamp, and host compiler.
The release pipeline builds and tests Dea/L0 across Linux x86_64, macOS arm64, macOS x86_64, and Windows x86_64 with MinGW-w64 via MSYS2. GitHub Actions runs on every PR, tagged builds produce multi-platform release artifacts with SHA-256 checksums, and there is also a manual snapshot pre-release workflow.
Documentation is part of the release story too: generated API docs are published as HTML on GitHub Pages, the PDF reference manual is available as a release asset, and the release archive includes the reference docs used by the compiler project.
Getting started
Download a binary archive from the GitHub Releases page. Unpack it, source the environment, and compile:
1
2
3
tar xzf dea-l0-lang_linux-x86_64_*.tar.gz
source dea-l0/bin/l0-env.sh
l0c --run dea-l0/examples/hello.l0
Or build from source. From a checkout of the repository, you can build and run it like this:
1
2
3
make use-dev-stage2
source build/dea/bin/l0-env.sh
l0c --run examples/hello.l0
For development setup, testing, and contributor workflow, see CONTRIBUTING.md.
The examples/ directory covers most language features.
hamurabi.l0 is a faithful port of the 1968 resource-management game.
demo.l0 is a prefix notation calculator that exercises sum types, recursive descent parsing, and scoped cleanup. If you want to start with code, begin there; if you want the full surface, read the language reference.
What’s next
Dea/L0 is the compiler base that will be used to build Dea/L1. The idea is that L0 stays small and stable while L1 extends the language on top of it.
If that kind of staged bootstrap work interests you, the project is open to contributors. See CONTRIBUTING.md for setup and guidelines. AI-assisted contributions are encouraged.
Links
- Repository: github.com/dea-lang/dea
- License: MIT or Apache 2.0, at your option
- Author blog: googlielmo.github.io