Skip to content

Latest commit

 

History

367 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Nexium

English · Español · 简体中文 · 日本語 · 한국어 · Français · Deutsch

CI Release Docs License: MIT Zig Platforms

PyPI npm Open VSX Chocolatey Docker Open in GitHub Codespaces OpenSSF Scorecard

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

fn checksum(data: []u8) -> u32 export(c) {
    var h: u32 = 2166136261
    for b in data {
        h ^= b as u32
        h *%= 16777619
    }
    return h
}

artifact cabi   { name = "hasher" }
artifact python { name = "hasher" }

Every target

$ nx ship hasher.nx
shipped 4 artifact file(s) for x86_64-windows:
  nx-out/hasher/hasher.dll
  nx-out/hasher/hasher.lib
  nx-out/hasher/hasher.h
  nx-out/hasher/hasher-0.1.0-py3-none-win_amd64.whl
>>> 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.

Highlights

🧾 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.

Install

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 | sh

It 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/nexium
scoop bucket add londopy https://github.com/Londopy/scoop-bucket && scoop install nexium

Then, 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.sh

The result is nx-out/bootstrap/nx2 (build.ps1 on Windows). Then:

nx run examples/hello.nx

A tour

struct 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: nx at a prompt, like python.
  • 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 topo in the terminal); the source is topo/. All of the above, rendered, is at londopy.github.io/nexium.
  • Installing: the Windows installer, the macOS/Linux script, source builds, checksums, and how nx finds 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 lsp for the rest.
  • Linguist: the ready-to-apply pull request that will make GitHub recognize .nx once 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.

Speed

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.

In the wild

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.

Commands

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.

Status

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.

Release names

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.

Self-hosting

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).

Languages in the repository

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.

Layout

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

Contributing

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.

License

MIT. Copyright (c) 2026 Londopy.

About

The Nexium Programming Language

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages