Systems language · Self-hosted · Windows x64

The language that compiles itself — and outruns C.

No LLVM. No gcc. No C. zc carries its own assembler and PE linker, rebuilds itself in 0.12 seconds, and emits binaries that import exactly one DLL.

See the benchmarks $ zc --rt hello.zeph hello.exe
Scroll
zc
No LLVM.
No gcc.
No C. Anywhere.
10,000 lines of Zephyr
that rebuild themselves in 0.12s.
byte-for-byte reproducible · one DLL import: kernel32.dll
The toolchain

Source to executable. Nothing borrowed in between.

hello.zeph
zc · written in Zephyr
lexer parser types codegen assembler PE linker
hello.exe
0 ms
Frontend pass over 10,000 lines.
0 s
Rebuilds itself from source.
1:1
Byte-for-byte reproducible rebuilds.
0
DLL import: kernel32.dll. Nothing else.
The language

Reads like you'd write it.

and / or / not, string interpolation, no semicolons. No pointers, no null, bounds-checked indexing — and a garbage collector written in Zephyr itself.

example.zeph
enum Color { Red, Green, Blue }fn map_list(xs: [int], f: fn(int) -> int) -> [int] {    var out: [int] = []    for x in xs { out.push(f(x)) }    return out}var ages: [str: int] = ["alice": 30]print(ages.get("bob").or(0))            // 0 — absence without a panicprint("distance is {sqrt(9.0 + 16.0)}") // distance is 5
A real build, in real time

Blink and it's linked.

This is the full compile of the compiler, by the compiler — every stage in-house, replayed at actual speed.

terminal

      
New in v0.2

Generics and closures — no trait system, no ceremony.

The two features people bounce off missing are in. Type parameters are inferred from the arguments, closures capture by value, and the standard library is now ordinary Zephyr instead of compiler builtins.

Generics — std/list.zeph
fn contains[T](xs: [T], x: T) -> bool {    for v in xs { if v == x { return true } }    return false}contains([1, 2, 3], 2)          // T = intcontains(["a", "b"], "b")       // T = str — same source, no annotation

Type parameters are inferred from the arguments — never written at the call site. Inference looks through [T], [K: V], T? and fn(T) -> U. Each instantiation is type-checked with T bound: contains gets the right == for int, str, or an enum, and an element type with no == is rejected. No trait system anywhere.

Closures — by-value capture
fn adder(k: int) -> fn(int) -> int {    return fn(x: int) -> int { return x + k }   // captures k by value}let add10 = adder(10)print(add10(5))                 // 15

Function values are {code, env} cells. Capture is by value and transitive — outer(100)(20)(3) threads a captured name across two closure boundaries — and loop closures each keep their own copy of the loop variable. One deliberate rule: assigning to a captured variable is a compile error, because by-value capture would make it silently wrong.

The standard library is written in Zephyr

contains, map, filter, fold, sort_by and friends used to be welded into the compiler. They now live in std/list.zeph as ordinary generic code — the snippet above is the real implementation. Nothing about lists is special-cased in the compiler anymore.

v0.2 — Generics (inferred monomorphization) and closures (by-value capture).
       Standard library moved out of the compiler into std/list.zeph.

Not yet

Zephyr is v0.2 and honest about it.

  • ·Assigning to a captured variable is a compile error — by design.
  • ·No function or operator overloading.
  • ·Windows / x86-64 only.

Build something that builds itself.

Download zc Read the docs