This roadmap describes where NURL is today and where it is going. It is forward-looking and deliberately concise — the full, reverse-chronological record of what shipped when lives in CHANGELOG.md. Anything marked done here has a regression test in compiler/tests/ and is covered by the bootstrap fixed point.
_Last reviewed: 2026-08-12 · Current release: 0.39.0 · Language: **Grammar v2.4** (spec/grammar.ebnf)._
NURL is a small systems language with a regular prefix-arity grammar, a self-hosted compiler written in NURL, and an LLVM backend. The compiler bootstraps to a byte-identical fixed point on its own source (stage1 ≡ stage2). The only build dependency is clang / LLVM 15+.
What is solid today:
|) and product types (structs),
generics over structs and functions (incl. generics over option/result types), pattern matching with match guards, or-patterns, and N-ary payloads, trait bounds on type parameters ([A: Ord]), **compile-time constant folding** (const_eval_int), a full numeric type set (i = i64 and u = byte/u8, plus sized i8/i16/i32, u16/u32/u64, f = f64 and f32), tail-call optimization, and variadic FFI (the printf family callable directly). The grammar decision for prefix-arity (no grouping delimiter) is formally locked, and since 0.37.0 the n-ary &/| arity trap it makes possible is a **hard error by default** (--no-strict-arity demotes it to a warning) — the shape compiled to working, wrong code before.
scope exit — no GC, no hidden boxing. A **static borrow checker, on by default** (--no-borrowck to disable, --strict-borrowck to tighten), catches use-after-move, alias double-free, escaping closure captures, interprocedural/return escape, loop-carried double-frees, and iterator invalidation as hard errors without changing generated code.
async/await colouring** — ordinary code runs unchanged under the scheduler — plus threads/mutex/cond, typed channels, and Go-style ?? channel select.
spanning collections, hashing, serialization, a full HTTP/1.1+2 + WebSocket stack, database clients, distributed systems (p2p overlay, CRDTs), MCP, and the Anthropic Claude API.
bootstrap fixed point + the Windows golden corpus on every push and PR), macOS ARM64 (CI-tested on Apple Silicon: bootstrap fixed point + the full corpus against the same goldens as Linux, on every push and PR; needs Homebrew LLVM, no prebuilt toolchain) and macOS x86_64 (cross-compiled Mach-O, no CI), wasm32-wasi, static Linux ARM64 / RISC-V64 (musl), and bootable unikernel images — a NURL program as its own kernel on x86_64, AArch64 and RISC-V64, no host OS and no libc. Tier definitions: docs/PLATFORMS.md.
nurlc (compiler), nurlfmt (canonical formatter), nurl-lsp
(language server), nurlpkg (package manager + test/bench runner), nurldoc (API-doc generator), tools/repl, DWARF debug info (--g), a VS Code extension, and nurlapi — a compiler-as-a-service container that powers the public playground and MCP endpoint.
The path to 1.0 is hardening, documentation precision, and external validation rather than new language surface — see Toward 1.0 below.
A high-level map of what exists. Dates and per-feature detail are in CHANGELOG.md.
compiler/nurlc.nu) with a deterministic, byte-identical
bootstrap; stage-0 links the committed nurlc_lastgood.ll snapshot (no Python in the toolchain).
spec/). v2.x added:
visibility (pub) enforcement across functions, types, consts, and enum variants; trait bounds; match guards + or-patterns; const folding; channel select; dynamic trait objects (%Trait + ( dyn Trait v ), v2.3); break / continue as reserved identifiers (v2.4); and locked the prefix-arity grouping decision.
conversions. Sized integer/float types with **signedness carried in the type representation itself** (u/u16/u32/u64 distinct from the signed types end to end — no flag side-channels); explicit # casts with correct sext/zext/trunc/fpext/fptrunc.
monomorphs, including behind */? prefixes), generic nesting (Channel[A], Vec[Thread]), and generics over ?T / !T E.
Drop for boxed enum/struct payloads,
% Drop user destructors, move/borrow analysis (incl. interprocedural and loop-carried escape detection). Model and known gaps: docs/MEMORY.md.
identifiers, call-arity mismatches, unbalanced braces / stray top-level tokens, and visibility violations are hard errors with source locations — nothing malformed reaches the backend silently. Since 0.39.0 that claim is measured corpus-wide: a mutation probe (one realistic mistake injected into each of the ~790 test programs, ~5 400 mutants) produces zero compiler hangs, zero crashes, and zero broken programs reaching the LLVM verifier or linker; every return path is type-checked (implicit fall-off and closure tails included), and errors inside generic/trait re-parses point at the template's real file:line with the instantiation named.
check_diag_coverage.sh reports
which of the compiler's ~230 messages a test has ever made it print; check_diag_anchor.sh gates that every baselined diagnostic points at the mistake rather than at the token after it; diag_mutate.py injects one realistic error into a working program and reads the answer. Between them they have found messages that were false, messages that were unreachable, and programs the compiler accepted and miscompiled.
main can reach the .ll
(--no-dce to emit everything). Reachability is computed over the finished IR, so closures, monomorphs, drop glue and dyn vtable thunks need no special casing — worth 30–40% of the clang step on a stdlib-heavy program. What survives is then emitted as several independent modules (--split=N) that the driver lowers concurrently and links with ThinLTO, taking the clang step on the compiler's own 3.2 MB of IR from 11.3 s to 2.0 s — at a measured 3.4% of the built program's own speed, which is why nurl.sh splits your program and build.sh does not split the compiler it installs (NURL_SPLIT=0 opts out). Both passes: docs/BUILDING.md.
nurlc --g) with ptype/print over structs.
Organised as core/ (language essentials), std/ (general-purpose), and ext/ (external-format / network / service bindings). All pure NURL except a small C runtime (stdlib/runtime.c) for the bootstrap surface and a few platform-specific shims.
string, vec, option, result, errors, char, slice,
pair, box, cell, mem, io, symtab, posix.
hashmap, set, deque, heap,
ordmap, btree, lru, bitset, iter, sort, cmp, bytes, bufio, fmt, int, float, bigint (arbitrary-precision integers), decimal (exact fixed-point).
async, thread, channel, arc, rc,
arena, signal, panic/recover, process, unixsock (local IPC), log (text + JSON), time (incl. timezone/DST, HTTP/RFC 2822 dates), args (CLI parser), term (POSIX termios, ANSI).
hash (SHA-1/256/512, MD5, HMAC),
hash_blake3, encode (hex, base64, base32), random (OS CSPRNG), rng (seedable, deterministic xoshiro256**).
fs (incl. streaming + readlink), path (typed),
net (TCP/TLS), udp, dns, dos.
json, toml, csv, msgpack, cbor, xml, yaml,
serde, regex.
auth, JWT bearer-auth, cookies, forms, multipart, router, middleware, access log + Prometheus metrics, DoS caps, graceful shutdown, per-request timeouts, panic recovery), HTTP client (with cookie jar), TLS (SNI + ALPN + mTLS + live cert reload; the pure ChaCha20-Poly1305 record path serves past gigabit wire speed), HTTP/2 (RFC 9113 + HPACK, server and client), WebSocket (RFC 6455, **server and client, with permessage-deflate** compression — RFC 7692), reverse proxy with binary-safe streaming. The stack has had a dedicated security-hardening pass (path-traversal, SSRF, request-smuggling, HTTP/2 CONTINUATION-flood + stream-accounting, and clean cross-thread listener shutdown) with regression tests, and its serve path is peer-benchmarked against Rust hyper and Node (bench/HTTP_RESULTS.md: ahead of hyper at low concurrency, an HTTP request served in 2 syscalls).
sqlite (production-hardened), mqtt 5.0 client,
smtp (mail submission). Postgres and Redis clients live in the registry packages psql and redis (pure NURL — no libpq, no hiredis).
mcp (+ client, http, session, stdio,
registry) and anthropic (Claude Messages API incl. streaming SSE + tool-use deltas).
semver, manifest, lockfile, resolver,
registry_index, pkg_fetch, pkg_publish (the nurlpkg backend).
compress (zlib/zstd/gzip), zip (archives), tar, uuid (v4/v7),
credentials, env.
NAT traversal, DERP relay, SWIM membership, state-based CRDTs (PN-Counter, LWW-Register, OR-Set), gossip replicator, consistent-hash ring, distributed computation (Crown).
ARM64 (CI-tested on Apple Silicon; needs Homebrew LLVM, no prebuilt toolchain), macOS x86_64 (cross-compiled, not CI-tested).
wasm32-wasi (WASI SDK), including the compiler itself running
in the browser playground, and **whole neural networks running client-side in the browser** — the pure-NURL ONNX runtime compiled to wasm, executing on the CPU (precompiled kernels) or on the visitor's GPU via WebGPU (the CUDA-C kernels translated to WGSL compute shaders; the gpu package's third backend). Live YOLOE segmentation and tiny-YOLOv2 detection run in a tab with no server inference (packages/yoloe-demo, the playground objdet demo).
packages/wasmtime) that
decodes and executes real wasm32-wasi modules (full int/float instruction set, linear/bulk memory, tables + call_indirect, WASI + --dir file ops), with no external runtime — and the compiler self-hosts on wasm: nurlc compiled to wasm32-wasi recompiles nurlc.nu to byte-identical IR, both under the reference wasmtime and under this pure-NURL runtime.
C906) validated on-device.
libc, no interpreter — on three architectures: x86_64 (QEMU microvm, and the same PVH image boots under Firecracker and cloud-hypervisor), AArch64 and RISC-V64 (QEMU virt; on AArch64 a flat Image wrapper covers Firecracker/cloud-hypervisor). Per-arch QEMU gates run the hosted corpus against the same goldens (15/15 each), with virtio net/rng drivers, TLS handshakes in the guest, native fiber switches on all three ISAs, and CI booting every architecture on every commit. The playground builds and boots these images (POST /build_unikernel + target dropdown), and agents do the same over MCP (nurl_build_unikernel).
nurlfmt canonical formatter (idempotent, IR-preserving), nurl-lsp
(completion, references, unused-symbol lint), nurlpkg package manager, DWARF debugging, VS Code extension, and the nurlapi compiler-as-a-service container (playground + cross-compile endpoints + public MCP server).
targets in examples/, Milk-V Duo programs in duo/, and a Push-To-Talk distributed voice app (pttvoice/).
nurllama + gguf):
pull a GGUF model from HuggingFace (resumable, content-addressed store), then run, chat, or serve an ollama-compatible API that existing clients speak unchanged. A hostile-input GGUF parser, a tokenizer read from the model's own metadata, and a llama forward pass whose matvec kernels decode quantised blocks inside the matmul — Q4_0…Q8_0 and the K-quants (Q4_K/Q5_K/Q6_K) — so weights never expand to f32 on the device, and the same kernel sources run on the CPU backend byte-identically. Verified against independent references at every layer: token IDs vs a SentencePiece implementation, logits and greedy text vs a numpy forward pass, dequantisation bit-identical to an independent decoder. Architectures span llama / qwen2 / gemma3 / phi3 and, since 0.21.0, diffusion language models: LLaDA2.x, a Mixture-of-Experts llada2 model that converts from its Hugging Face checkpoint (nurllama convert, streaming a model larger than RAM to GGUF at constant memory) and generates by block denoising — parallel commits with token editing, not left-to-right — its logits matched to a numpy forward and its decoded ids to a reference-faithful loop.
whisper + audio + safetensor +
tokenizer): a WAV goes in and text comes out — *word for word what Hugging Face transformers produces from the same model*, on whisper-tiny and on distil-large-v3, on the GPU or the CPU. Every stage is verified against an independent implementation rather than against our own understanding of it: the resampler against scipy, the log-mel against HF's own feature extractor (r = 1.00000000, on both 80 and 128 bands), the 400-point FFT against numpy (Bluestein, because 400 is not a power of two and padding it computes a different transform), the weights bit-exact, the vocabulary against HF's tokenizer, the encoder against HF's encoder, and the transcription against HF's. Whisper is not the llama shape — LayerNorm rather than RMSNorm, error-function GELU rather than tanh, two conv1d layers, cross-attention, and the ecosystem's first non-causal attention.
reg.nurl-lang.org,
nurlpkg install): GPU compute (gpu — CUDA driver + NVRTC, a CPU fallback backend, a static-kernel backend, and a WebGPU / WGSL backend; gpukit, tensor), pure-NURL vision and ML (image PNG/JPEG codecs, onnx runtime, objdet, yoloe, iforest, anomaly, and mlp — the first trainable package, a deterministic sklearn-faithful MLP regressor), local LLMs (nurllama, gguf, tokenizer, safetensor), speech (whisper, audio), distributed compute (swarm, swarm-mcp), web (template HTML templating, http), database clients (psql, redis — pure NURL), and application scaffolding (cli, cas, wasmbuilder, nurl-mcp). Installed tools carry runtime data via the manifest's [install] assets mechanism (staged into <prefix>/share/<name>/).
packages/registry): a
self-hostable server speaking the exact nurlpkg wire protocol — bearer-authenticated publish with server-side checksums, name ownership and version immutability, yank/revoke, search, and a server-rendered catalog that renders each package's README straight from its published tarball. SQLite as the single source of truth; built on the http, template and md2html packages and proven end-to-end against the real client. (The public reg.nurl-lang.org cutover from the Cloudflare Worker is pending deployment.)
published tarball with a project Ed25519 key; nurlpkg pins the public key and verifies the detached minisign signature — using a pure-NURL BLAKE2b + minisign implementation (std/hash_blake2b, std/minisign) — before unpacking, mandatory and fail-closed. Release archives are signed too; the installers always verify the checksum fail-closed and verify the minisign signature against a pinned key when minisign is available.
The remaining work to declare a stable 1.0 is mostly precision and proof, not new language features.
checker rejects vs. tolerates, with no implied Rust-equivalence (docs/MEMORY.md, docs/LIMITATIONS.md).
interprocedural escape analysis and *T raw-pointer flows are on the roadmap or out of scope by design; the checker is currently incomplete there by design. (Resolved: interprocedural escape and return-escape implemented.)
arm-local fall-through bindings, allocations inside a recover scope). (Resolved: leaks fixed.)
These convert a hypothesis into measured results.
characters) for NURL vs Python/Rust/JS across 8 matched programs (bench/TOKEN_EFFICIENCY.md). *Result: the raw token-count claim did not survive measurement — on today's tokenisers NURL is ~1.7× Python's tokens (median), losing to out-of-distribution glyph fragmentation. The claim was retired; the defensible arguments are grammar regularity and first-pass compile success.*
correctness, NURL vs Python/Rust, across four models (Sonnet 4.6 / Opus 4.8 / Haiku 4.5 / mercury-2 diffusion), primed with NURL's one-page reference since the training corpus contains zero NURL (bench/genacc/, results in bench/genacc/RESULTS.md). *Result: from a single page a model reaches the Python/Rust ballpark on several tasks but not parity first-try; failures are out-of-distribution habits (imports, then grouping-parens, then mutability) that targeted primer cues fix in turn. The follow-up measured the agentic half of the claim: with ONE round of compiler-diagnostic feedback (bench/genacc/repair.py — the model sees only its program and the compiler's stderr, never the expected output), Sonnet and Opus reach **exact Python/Rust parity — 100% compile, 100% correct** — Haiku reaches 100% compile, and the diffusion model quadruples its score. The diagnostic-first compiler is the load-bearing artifact, and its value is now measured, not asserted.*
project copy — "an agent can drive the toolchain over MCP" is a tooling win, not evidence the language is better for LLMs.
maintainer and publish a short governance note (license stays MIT OR Apache-2.0).
Not blocking 1.0; ordered roughly by likely value.
no_std-styleembedded profile. The RISC-V / ARM64 static cross-compiles already prove the shape; these extend it.
stdlib/runtime.c is
split into stdlib/runtime_core.c (bootstrap core) and stdlib/runtime_ffi.c (stdlib FFI shims), stitched by a thin aggregator so the single runtime.o build is unchanged. The core compiles standalone and defines the symbol set the no_std profile links.
Ideas under consideration, no committed timeline.
suggestions, leaning on the regular grammar and local error semantics.
the simple IR, but are not on the near-term path.
Deliberate exclusions, to set expectations:
model; rc/arc are opt-in library types.
async/await function colouring. Concurrency is via stackful fibers;ordinary code is scheduler-agnostic.
whole point.
(#).
recover — only
explicit panic is recoverable; faults remain process aborts.
compiler/tests/ and survives
the bootstrap fixed point; details in CHANGELOG.md.
docs/spec.md (normative) and
spec/grammar.ebnf (authoritative grammar).