English · Español · 简体中文 · 日本語 · 한국어 · Français · Deutsch
Nexium is a language complete enough to build everything in, that is also the best thing to adopt for one piece of something else.
Documentation & the Topo tutorial → londopy.github.io/nexium
Start with the Topo · Language reference · Install · Standard library · Embedding
Nexium compiles to native code through C, has automatic reference counting without a tracing collector, a machine-checked effect system that says whether a function allocates, blocks, or can panic, and a compiler that turns one source tree into a C library, a Python wheel, a Rust crate, or a command line tool.
|
One file |
Every target >>> import hasher
>>> hasher.checksum(b"hello")
1335831723 |
The effect signature decides the C ABI: checksum is proven !panics, so it
gets a plain uint32_t checksum(const uint8_t*, size_t). A function that can
fail returns a status code, and a Nexium panic inside it is converted at the
boundary rather than aborting the host process.
| 🧾 Effects, inferred and checked | allocates refcounts blocks shared_mutable nondeterministic panics ffi. Declare !allocates and the compiler points at the exact line that would break it, through calls. |
| 🛡 Memory safety without a garbage collector | Collections move, .clone() copies, ref class values are reference counted, weak breaks cycles, and a slice or pointer never outlives the storage it points into: the view rules are checked through calls, loops and branches, with no lifetimes to write. Where the fix is mechanical, nx fix makes it. |
| 🔬 Binary patterns | <<version:4, ihl:4, len:16/big, rest:bytes>> matches and builds packets with checked sizes. |
| 🧵 Parallel loops, arenas, trait objects | for parallel, using arena { }, dyn Trait !allocates. |
| 🔌 C without bindings | @cImport("header.h") reads the header directly; artifact link compiles vendored C into the program; if comptime @target().0 == "windows" builds only the branch the platform takes. |
| 📦 Ship from one source | nx ship produces C headers and libraries, Python wheels, and Rust crates with safe wrappers. |
| 🐞 Debug it, measure it | nx debug stops gdb or lldb at .nx lines and shows strings, lists, maps and optionals as values; bench "name" { } blocks sit beside the tests; --sanitize address,undefined puts AddressSanitizer and UBSan under any build. |
| 🧭 Learn it in the browser | The Topo, the tutorial, runs its programs in the page, the compiler compiled to WebAssembly, and grades its exercises as nx topo does in the terminal. nx repl is a prompt. |
| 🪞 Written in itself | The compiler is Nexium, built from one C file by any C compiler; a large program's debug build recompiles only the modules that changed. |
| 🖼 A GUI, in Nexium | gui/: an immediate-mode GUI (buttons, sliders, text fields) with a software rasterizer and bitmap font, all Nexium over a 200-line C window layer. |
| 🛠 Tooling in the box | fmt, fix, doc, lsp (definition, hover and rename from the checker), debug, bench, size, layout, leaks, refcounts, effects, explain, audit, repl. Zero dependencies. |
Windows: download and run the installer from the
Releases page. It installs
nx, a bundled Zig toolchain (the C compiler nx uses), the standard
library, examples, docs, and the VS Code extension, and adds nx to your
PATH. Nothing else to install.
macOS and Linux:
curl -fsSL https://raw.githubusercontent.com/Londopy/nexium/main/installers/install.sh | shIt verifies the download against the release checksums, installs to
~/.nexium, sets up a C compiler (the Xcode tools on macOS; Zig is downloaded
on Linux when nothing is found), and adds nx to your PATH.
Windows, from PowerShell: irm https://raw.githubusercontent.com/Londopy/nexium/main/installers/install.ps1 | iex
(the portable build, verified, on the PATH; no wizard).
pip or npm: pip install nexium-lang (PyPI) or npm install -g nexium-lang (npm); the binary, per platform; a C compiler as usual.
Docker: docker run --rm -v "$PWD":/work ghcr.io/londopy/nexium run hello.nx
(Debian; :alpine too; amd64 and arm64).
Chocolatey and winget: choco install nexium (the package) and winget install Londopy.Nexium, each once its registry has approved the first version (the status).
Debian, RPM, Nix, mise: every release attaches .deb and .rpm packages (sudo dpkg -i nexium_*_amd64.deb); nix run github:Londopy/nexium builds it from the one C file; mise use -g "ubi:Londopy/nexium[exe=nx]" installs the release binary. Every asset carries signed provenance: gh attestation verify nx --owner Londopy. All the roads.
In the browser: open the repository in a Codespace and nx run examples/hello.nx runs in a minute, nothing installed.
In a notebook: in Google Colab or Jupyter, !pip install -q nexium-lang, then !nx run hello.nx on a file a %%writefile cell wrote (the three cells).
Homebrew and Scoop: the repository is its own tap, and Scoop's bucket is Londopy/scoop-bucket, kept current by Scoop's own updater.
brew tap londopy/tap https://github.com/Londopy/nexium && brew install londopy/tap/nexiumscoop bucket add londopy https://github.com/Londopy/scoop-bucket && scoop install nexiumThen, in a new console, nx doctor shows what will be used. All the details,
including verifying checksums and every environment variable, are in
docs/install.md.
Or build from source with nothing but a C compiler (Zig on the PATH, or
CC), which builds the compiler written in Nexium from its C seed:
git clone https://github.com/Londopy/nexium && cd nexium && sh bootstrap/build.shThe result is nx-out/bootstrap/nx2 (build.ps1 on Windows). Then:
nx run examples/hello.nxstruct Point derive(Eq) { x: f64, y: f64 }
enum Shape { Circle(f64), Rect { w: f64, h: f64 }, Empty }
fn area(s: Shape) -> f64 {
match s {
.Circle(r) => math.PI * r * r,
.Rect(w, h) => w * h,
.Empty => 0.0,
}
}
error ParseError { Empty, NotANumber }
fn parse_num(text: []u8) -> ParseError!i64 {
if text.len == 0 { return error.Empty }
var total: i64 = 0
for c in text {
if c < '0' or c > '9' { return error.NotANumber }
total = total * 10 + (c - '0') as i64
}
return total
}
fn max(comptime T: type where T: Ord, a: T, b: T) -> T {
return if a > b { a } else { b }
}
fn main() -> !void {
let n = try parse_num("1234")
let bad = parse_num("12x") catch |e| {
println("caught {}", .{e})
-1
}
var xs = List(i32).new()
for i in 0..10 { xs.append((i * i) as i32) }
let found = outer: {
for x, i in xs { if x > 30 { break :outer i as i64 } }
-1
}
println("{} {} {} {} {}", .{n, bad, max(3, 9), xs.len, found})
}
Binary pattern matching
fn parse_ipv4(packet: []u8) -> Net!Ipv4 {
match packet {
<<version:4, ihl:4, dscp:6, ecn:2, total_len:16/big,
id:16, flags:3, frag_off:13, ttl:8, proto:8,
checksum:16, src:32, dst:32, rest:bytes>> => {
return Ipv4{ .version = version, .ihl = ihl, .total_len = total_len,
.ttl = ttl, .proto = proto, .src = src, .dst = dst }
}
_ => return error.Truncated,
}
}
let written = try <<4:4, 5:4, 0:8, 1500:16/big, "ab">> into buf[..]
Effects are inferred and checked
fn hot(xs: []i32) -> i32 !allocates !panics {
var list = List(i32).new()
list.append(1)
return helper(xs) + list.len as i32
}
error: function `hot` is declared `!allocates` but has the `allocates` effect
--> examples/effects_bad.nx:7:26
note: the effect is introduced here: appending to a List may grow it
--> examples/effects_bad.nx:9:5
A view never outlives its storage
fn main() {
var names = List(String).new()
names.append(String.from("ada"))
let first = names[0][..]
names.append(String.from("grace"))
println("{}", .{first})
}
error: `first` is a view into `names`, which changed on line 5 after the view
was taken; its storage may have moved (rule V3); take the view after the
change, or keep an owned copy of the container (`.clone()`) taken before it
--> views.nx:6:21
A slice or pointer is checked against the storage it points into (SPEC 5.6, rules V1 to V5): no garbage collector, and no lifetimes to write.
Tests and benchmarks side by side
fn sum_to(n: i64) -> i64 {
var s: i64 = 0
for i in 0..n { s += i }
return s
}
test "sums" {
expect(sum_to(4) == 6)
}
bench "sum to 1000" {
sum_to(1000)
}
$ nx bench sums.nx
bench sum to 1000 189 ns/iter (min 188 ns, max 197 ns; 21 samples of 63856)
1 benchmark(s), safe mode
nx test runs the tests; nx bench builds the file optimized, calibrates
the iterations, and keeps the block's value from the optimizer.
Calling C is a header import away
const libc = @cImport("string.h")
const cv = @cImport("cvendor.h")
artifact link { c_sources = ["cvendor.c"] }
unsafe { println("{}", .{libc.strlen(@cstr("hello"))}) }
No binding generator, no build step: the header is the single source of truth
for layout, and foreign calls carry the ffi effect.
Parallel loops and arenas
for parallel p, i in positions {
out[i] = integrate(p) // no shared_mutable allowed in here
}
using arena {
var scratch = List(Frame).new() // bump allocated, freed all at once
...
}
Documentation
- Specification: the language as implemented, with planned parts marked.
- Roadmap: phases, exit criteria, and what is not planned.
- How Nexium works: the pipeline from source to binary, effects inference, ownership, the runtime, and shipping.
- Language reference: every construct the compiler implements.
- Embedding: calling shipped libraries from Python, Rust, and C.
- The interactive session:
nxat a prompt, likepython. - Stability and platforms: what a version promises, the deprecation cycle,
nx fix, the tiers. - The Topo: the tutorial, from installing the compiler to a neural network, a GUI and a shipped library, with exercises the compiler grades (on the page, or
nx topoin the terminal); the source istopo/. All of the above, rendered, is at londopy.github.io/nexium. - Installing: the Windows installer, the macOS/Linux script, source builds, checksums, and how
nxfinds a C compiler. - Packages:
nexium.toml,nx add,nx fetch, git or path dependencies, the lock file. - Standard library: the modules written in Nexium (
std.strings,std.lists,std.bytes,std.num,std.json,std.args,std.fs,std.time,std.regex,std.text,std.testing,std.stream,std.net,std.http,std.thread,std.process,std.sort,std.heap,std.set,std.deque,std.hash). - The numbers: four programs in five languages, measured weekly on one runner.
- nexium-gui: the immediate-mode GUI library and how to write a widget.
- Releasing your program: binaries for three platforms from a tag, installers optional.
- Editor support: VS Code, Vim, Neovim, Helix, Zed, Emacs, Kate, JetBrains, Sublime Text, Notepad++, nano, and
nx lspfor the rest. - Linguist: the ready-to-apply pull request that will make GitHub recognize
.nxonce the usage bar is met. - Translations: this README in six languages; the language reference and the architecture tour in Spanish, Chinese, and Japanese.
- Release names: every release is a place on a mountain; the scheme, the ledger, and the names still to use.
- Decisions: every call made where the specification was open.
- Known issues: open bugs, gaps and limitations, with repros.
Four programs written the same way in five languages, about a second each in the compiled ones, timed on a GitHub runner (2026-09-25; the median of seven runs, or three for anything over five seconds; in seconds, smaller is better):
| program | Nexium safe | Nexium fast | C | Rust | Go | Python |
|---|---|---|---|---|---|---|
fib (calls) |
1.32 | 0.78 | 0.39 | 0.78 | 1.34 | 28.87 |
nbody (floats) |
0.66 | 0.66 | 0.59 | 0.68 | 0.71 | 48.54 |
sieve (arrays) |
0.62 | 0.60 | 0.48 | 0.53 | 0.53 | 4.29 |
words (maps and strings) |
1.16 | 1.11 | 0.57 | 0.97 | 1.12 | 3.60 |
On floats and arrays Nexium takes at most a third longer than C: a little
ahead of Rust and Go on floats, a little behind them on arrays. On calls
fast matches Rust at twice C's time, and safe's overflow checks add 70% to
that. Maps and strings run at Go's pace, about twice C's time. Python takes 3
to 74 times as long as Nexium fast.
safe keeps the overflow and bounds checks, as nx ship and nx bench
build unless told otherwise; fast (--mode fast) leaves them out. The
numbers page has each time as a multiple of C's, the versions and
the rules; the Bench workflow measures
again every week and at every release, and fails when Nexium's time as a
multiple of C's grows by a quarter from one run to the next.
Programs and packages outside this repository that are written in Nexium:
| project | what Nexium does there |
|---|---|
| statusmith, Discord Rich Presence from the tray | its SDK is a Nexium package: nx add discord_rpc --git https://github.com/Londopy/statusmith --tag sdk-v0.1.0 --dir nexium sets a presence from any Nexium program (the page) |
| Point of Origin, a platformer where the puzzle is the ground | the whole build is Nexium: build.nx drives the Odin simulation's DLL, tools/bindgen.nx reads the Odin exports and writes the C# bindings Unity calls so the two sides cannot drift, tools/levels.nx compiles the level maps into the JSON the game loads (every level grown by the same simulation, so it is solvable by construction), tools/chapters.nx writes the docs from them |
| QNI, net reminders, check-in help and a net control tutorial for the Cal Poly Amateur Radio Club's Discord (W6BHZ) | the whole program is Nexium: slash commands and buttons answered over webhooks, with no bot user and no permissions; every request checked for Discord's Ed25519 signature before anything else is read (through nxtls); net cards, a practice mode for net control, and net logs in the officers' own sheet format; tested end to end against a fake Discord |
| nxtls, cryptography in pure Nexium | SHA-2, HMAC, HKDF with TLS 1.3's labels, and Ed25519 verification, with no C and no unsafe, tested against the standards' published vectors; a package: nx add nxtls --git https://github.com/Londopy/nxtls --tag v0.1.0; a TLS 1.3 client is the plan |
Using Nexium somewhere? Open an issue or a pull request and it goes here.
| command | what it does |
|---|---|
nx build file.nx |
compile to an executable (or object when there is no main) |
nx run file.nx |
build and run; --watch runs again whenever a file of the program changes |
nx test file.nx [filter] |
run the test "..." blocks; --watch too |
nx check file.nx |
type-check and report effect violations |
nx effects file.nx |
print the inferred effects of every function |
nx explain file.nx f effect |
why f has the effect: the calls that carry it in, down to the primitive, as a tree |
nx audit file.nx |
list unsafe blocks and mutable globals; --lock writes the effects lockfile, --check fails on a gained effect |
nx ship file.nx |
produce every declared artifact |
nx init, nx add, nx fetch, nx update |
a package's manifest, dependencies from git or a path, the lock file (docs/packages.md) |
nx emit-c file.nx |
print the generated C |
nx tir file.nx [--sigs] |
the checked program as S-expressions (the compiler's own tests read it) |
nx fmt file.nx [--check] |
canonical formatting |
nx debug file.nx |
build for debugging and run under gdb or lldb: .nx lines, and formatters for String, List, Map, slices and optionals |
nx bench file.nx |
measure the bench "name" { } blocks: an optimized build, the median time per iteration |
nx fix file.nx |
make the checker's mechanical fixes (.clone(), @escape(...), _ = ) and migrate deprecated forms; see docs/stability.md |
nx doc file.nx |
HTML documentation with inferred effects |
nx size file.nx |
attribute binary bytes to declarations |
nx layout file.nx [Type...] |
offsets, sizes and padding of a struct or enum, and the order by alignment that would shrink it |
nx upgrade |
the latest release in place of this executable, verified; --check only reports |
nx install [DIR] |
this copy, with what sits beside it, into the user's place and onto the PATH (the portable zip installing itself) |
nx refcounts file.nx |
every retain and release site |
nx leaks file.nx |
run with allocation tracking and report leaks |
nx lsp |
language server over stdio |
nx doctor |
which C compiler will be used, and whether the installation works |
nx version |
the version and its release name |
nx completions <shell>, nx man |
completions for bash, zsh, fish and PowerShell, and the manual page |
nx repl, or just nx |
an interactive session: type code, see values, keep bindings |
nx -e CODE, nx -p EXPR |
a line run as at the prompt; -p prints its value |
nx play [file.nx] |
check a whole program and run it in the interpreter, nothing compiled; stdin when no file (the command the site's playground runs) |
nx topo [<chapter>|check|hint|solution|quiz] |
the Topo's exercises in the terminal, graded by the compiler, progress kept |
Options: --mode debug|safe|fast|small, --target x86_64-linux-gnu (any
target zig cc knows), --cpu baseline|native|<name> (baseline by default,
so a binary runs on any machine of its architecture), --out-dir,
--keep-c, --cc, --strict (warnings are errors), --sanitize address,undefined (the C compiler's sanitizers; address needs gcc or
clang), and for C interop -I, --link, --link-path, --c-source.
1.3: language-stable, the toolchain grown up. The language changes only
by addition under the stability policy; the compiler
is written in Nexium and builds itself; every example, spec case and
tutorial program runs in CI on three platforms, under the sanitizers and
the fuzzer, and gdb and lldb are driven through nx debug there too.
Memory safety is the view rules, errors since 1.3. 1.4, a standard library
people stop supplementing, is under way: collections (std.sort,
std.heap, std.set, std.deque) and std.hash are in, twenty-one
modules in all, and the HTTP client with TLS, websockets and time zones are
next. What Nexium is not yet, and where each is answered, is the first
section of the roadmap: the only benchmark numbers are
the numbers page, and the ecosystem is one maintainer
and four projects outside the tree (above).
KNOWN_ISSUES.md lists every open bug with its fix;
DECISIONS.md every call made where the specification was
open.
Every major version is the summit of a mountain, in the order the fourteen
8000-metre peaks were first climbed: X.0.0 is Mountain: Summit. The
climb starts halfway through the line before, at its .5, and goes up the
first-ascent route camp by camp; the minors after a summit, up to .4, are
the mountain's other routes and the way down; patches take the members of
the first-ascent expedition. The 0.x line was the approach and the camps of
Annapurna, the first 8000er climbed (1950), so 1.0.0 is
Annapurna: Summit; 0.7.0, where the compiler started building itself, is
Annapurna: Camp V, the last camp before the summit push, and from 1.5 the
releases climb Everest toward 2.0.0.
The name is in
the changelog, the release title and nx version;
docs/release-names.md has the rule, the ledger,
and the mountains still to climb.
The compiler is written in Nexium, under self/, and builds itself.
A machine with no nx builds one from bootstrap/nx.c,
the C the compiler emits for itself, with any C compiler and no Rust:
sh bootstrap/build.sh # nx.c -> nx0; nx0 builds self/nx.nx -> nx1; nx1 rebuilds itself to the same C -> nx2| stage | file | lines |
|---|---|---|
| lexer | self/lexer.nx |
tokens |
| parser | self/parser.nx |
an id-arena syntax tree |
| checker | self/check.nx, self/check_*.nx, self/cimport.nx |
types, effects, ownership, generics, the compile-time interpreter, C header import, every diagnostic |
| C emitter | self/cgen.nx |
one C file per program, or one per module for a large program's debug build, the unchanged ones reused |
| driver | self/nx.nx |
build, run, test, bench, debug, check, emit-c, tir; the standard library embedded |
| tools | self/fmt.nx, self/doc.nx, self/tools.nx, self/size.nx, self/manifest.nx, self/ship.nx, self/lsp.nx, self/lsp_index.nx, self/fix.nx, self/repl.nx |
the formatter, the documentation generator, the reports, packages, ship, the language server and its index of the checked program, nx fix, the REPL |
Every example, every spec case and every compile-fail case runs through the
bootstrapped compiler, driven by a test harness that is itself a Nexium
program (nx run tests/run.nx), in CI on three platforms with no Rust
toolchain at all. The first compiler, in Rust, drove the port and was
deleted at 1.0 (decision 90).
Non-blank lines of code, excluding build output, dependencies, and generated
files (bootstrap/nx.c, the tree-sitter parser, gui/font.bin, lock files):
| language | lines | share | what it is |
|---|---|---|---|
| Nexium | 48,804 | 87.2% | the compiler and its tools (33,000 lines under self/), the standard library (21 modules), the test harness and the fuzzer, the examples, the tutorial's programs, the GUI, the site generator, four benchmarks |
| C | 2,542 | 4.5% | the runtime nx_rt.h, the GUI window layer, vendored test C, a benchmark |
| Python | 1,492 | 2.7% | the release scripts (notes, package manifests, wheels and npm packages, the std docs), the gdb and lldb formatters, the benchmark runner and four benchmarks |
| editor files | 1,103 | 2.0% | tree-sitter queries, Emacs Lisp, Vim script, Lua for Neovim, a Pygments lexer, and the 25 lines of Rust that Zed requires of an extension |
| JavaScript, TypeScript | 939 | 1.7% | the VS Code extension, the tree-sitter grammar, and the playground's WASI layer |
| Inno Setup, shell, PowerShell | 855 | 1.5% | the Windows installer script, install.sh, install.ps1, the Chocolatey scripts, the bootstrap scripts |
| Rust, Go, Ruby | 213 | 0.4% | four benchmarks each in Rust and Go, and the Homebrew formula |
There is no Rust in the compiler: the first compiler drove the port and
was deleted at 1.0 (decision 90). The Rust that remains is the glue of the
Zed extension, which Zed compiles to WebAssembly, and four benchmark
programs written to be measured against, beside their Go twins. Zig is not in
the table because there is no Zig source in the tree: zig cc is the C
compiler nx runs (bundled by the Windows installer, downloaded by the
install script), the same way a C compiler is used and not written.
bootstrap/ the C seed the compiler is built from, and the build scripts
runtime/ nx_rt.h, embedded into every generated C file; the gdb and lldb formatters of nx debug
std/ the standard library in Nexium, embedded in the compiler
self/ the compiler in Nexium, stage by stage
gui/ nexium-gui: immediate-mode GUI in Nexium, demo, and the C platform layer
editors/ VS Code extension, tree-sitter grammar, and the files for ten more editors
examples/ programs with recorded output, run by the tests
topo/ the tutorial: chapters, and the programs they show (run by the tests)
site/ the documentation site generator, a Nexium program
tests/ the harness (run.nx), the spec conformance suite (tests/spec), compile-fail cases, the debugger check
docs/ how it works, language reference, embedding guide, i18n/ translations
bench/ four programs in five languages behind the numbers page
installers/ the Windows installer script, install.sh and install.ps1, the winget and Chocolatey manifests
docker/ the compiler images for ghcr.io (Debian and Alpine)
Formula/, bucket/ this repository as a Homebrew tap and a Scoop bucket (written at each release)
scripts/ release notes, package manifests, wheels and npm packages, the std docs
assets/ logo, banner and the social preview
nexium-spec.txt the design
nexium-systems-spec.txt the archived systems language; sections 4 to 9 are the syntax reference
DECISIONS.md decisions made where the specification was open
KNOWN_ISSUES.md open bugs and limitations; fixes move to the changelog
See CONTRIBUTING.md. Bugs and proposals go through GitHub
issues; a language change must name the hard constraint in section 3 of the
specification that it serves. Pull requests pass the tests on three
platforms, the formatters, a changelog check and the Contributor License
Agreement before they merge; you keep your copyright.
MIT. Copyright (c) 2026 Londopy.