# NanoLang: a small language built to be written by coding LLMs

> NanoLang is an experimental language from jordanhubbard that transpiles to C, ships a verified bytecode VM called NanoISA, and requires a shadow test next to every function. Here is what the repository actually documents, and where it stops.

**jordanhubbard/nanolang** — A tiny experimental language designed to be targeted by coding LLMs 

- Repository: https://github.com/jordanhubbard/nanolang
- Stars: 629 · Forks: 24
- Language: C
- License: Apache-2.0
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/jordanhubbard-nanolang

## What NanoLang is trying to fix

Most languages were shaped by human ergonomics: short keywords, forgiving parsers, decades of idioms. When a model writes code, those same properties become liabilities, because ambiguity is cheap for a person who can ask a colleague and expensive for a generator that cannot. NanoLang's README states the goal directly: a programming language designed for machines to write and humans to read, with mandatory tests and unambiguous syntax. The audience is narrow and honest. It is for people building code generation pipelines, agent runtimes, or sandboxes that need a small typed target with a verifiable core, not for teams shipping a web service next quarter. The topics on the repository (domain-ai, domain-lang, llm, new-language-design, thought-exercise, vibe-coding) make the framing explicit: this is an experiment in language design under LLM authorship, and the README presents it that way rather than as a general-purpose tool.

## C as the production backend, NanoISA as the verified boundary

The architecture has two execution paths and the README is clear about which one is production. NanoLang transpiles to C for native performance; the README calls C the production native path. Alongside it sits NanoISA, a stack-based bytecode VM with 161 portable opcodes in an 8-bit opcode space, and bytecode is verified before it runs. The design intent is that NanoLang and Nano Forth both lower to NanoISA, giving every frontend one typed and verified boundary, with LLVM, WebAssembly and JVM targets planned to translate from NanoISA later. PTX, OpenCL and RISC-V are described as direct experimental targets during that migration. That is a real architectural commitment, and it also explains the Forth session: colon definitions compile to verified NanoISA, with Jackson Core and Core Ext suites vendored as evidence. The README is careful here, stating that no Standard System is claimed. The same restraint appears around the secure runtime: NSI v0 contracts, unforgeable capabilities, a POSIX service fabric, effects-to-policy and a trap journal, with the explicit note that the journal is a tested library and is not hooked into every VM trap in 4.5. That sentence is the most useful one in the README, because it tells you exactly how far the security story currently reaches.

## Installing NanoLang and compiling a first program

The README gives a Quick Start with clone, build, and a hello program. The build uses make, and the repository ships both a Makefile and a GNUmakefile: the Makefile is a bridge that reinvokes gmake on GNUmakefile, and the README notes that BSD users should use gmake instead of make. Start with the clone and build step.

```bash
git clone https://github.com/jordanhubbard/nanolang.git
cd nanolang
make build
```

The README's next step writes a file named hello.nano containing a greet function, a shadow test for it, a main function, and a shadow block for main. Note the prefix arithmetic, the string concatenation with +, and the fact that the test lives in a shadow block rather than a separate file.

```nano
fn greet(name: string) -> string {
    return (+ "Hello, " name)
}

shadow greet {
    assert (== (greet "World") "Hello, World")
}

fn main() -> int {
    (println (greet "World"))
    return 0
}

shadow main { assert true }
```

Compilation goes through the nanoc binary in bin, with -o naming the output executable, and the resulting binary is run directly. According to the README you should see the greeting printed by println.

```bash
./bin/nanoc hello.nano -o hello
./hello
```

That is the whole documented loop. The README points to the User Guide at jordanhubbard.github.io/nanolang as the recommended starting point, and lists docs/GETTING_STARTED.md and docs/QUICK_REFERENCE.md for syntax. The User Guide is where you would go for the tutorial with executable examples; the README itself does not walk through error messages or the shape of compiler diagnostics.

## Shadow tests are a convention the compiler does not yet enforce

The most interesting design choice is the shadow test. A function is supposed to be accompanied by a shadow block containing assertions, and the README says the compiler warns loudly when a function is missing one. Then it adds the limitation in the same bullet: the compiler tracks shadow coverage but does not currently fail the build on its absence. That gap matters more than it first appears. If the premise is that a machine writes the code, then the test is the only artifact a human reviewer can read quickly, and a warning that does not stop the build is a warning that gets ignored in any automated pipeline. A CI job that runs make test will pass with untested functions in the tree. Anyone adopting NanoLang for generated code should treat shadow coverage as something to check themselves, not something the toolchain guarantees. The README's own badge row is consistent with this honesty: the bootstrap badge reads self-hosting in progress, which is a status, not a claim.

## Type inference stops at function boundaries

NanoLang infers types where unambiguous, so let x = 42 works without an annotation. The README is explicit that inference is local and bidirectional, not full Hindley-Milner, and that explicit annotations are required at function boundaries. For LLM-generated code this is a defensible trade: local inference is easier to explain in an error message than a unification failure spanning a module, and a generator that must annotate signatures produces a more readable artifact. It is also a constraint you will hit immediately. If you are used to writing a chain of small helpers without signatures, NanoLang will make you write them out. The language compensates with features the README lists in detail: dual prefix and infix notation, f-string interpolation, a pipe operator, algebraic effects with effect, perform and handle, async and await lowered to a CPS state machine at compile time, and pattern matching with guards, or-patterns, wildcards and exhaustiveness warnings. Memory is reference counted, so there is no free() to call, with a small per-retain/release cost and deterministic pauses; the README notes that NanoVM collects reference cycles in src/nanovm/heap_cycles.c and that generated C already did.

## Where NanoLang is the wrong tool

Three cases stand out. First, if you need a stable ABI and a large ecosystem of libraries, NanoLang is not it: C interop goes through modules, and the README describes isolating those calls in a separate process, which is a safety feature rather than a convenience. Second, if you need a hardened sandbox today, read the runtime section closely. The README states plainly that NanoLang does not claim a kernel, AES or PKI, and that the trap journal is not hooked into every VM trap in 4.5. Those are the author's own boundaries, and they should bound your expectations. Third, if your team cannot read prefix notation comfortably, the dual notation helps but does not remove the underlying form: the README notes that prefix calls are the unambiguous ones, which is the whole point of the design. Finally, the formal verification story is about core semantics, not about your program. The Coq proofs cover type soundness, progress, determinism and the big-step to small-step equivalence, and the README says they are complete with no Axiom declarations and no Admitted. That is a statement about the language definition in formal/, not a guarantee that a service you write on top of it is correct.

## How it compares to a general-purpose scripting target

The closest familiar comparison is a small language that targets an existing runtime rather than defining its own semantics. Lua, for instance, is a compact language designed to be embedded, and it earns its place by being small, stable and easy to bind to C. NanoLang shares the embedding instinct but inverts the priority: instead of minimizing the language, it maximizes what can be proved about it, adding a verifier in front of the bytecode and a Coq development behind the semantics. The practical difference is where the effort goes. With an embedded scripting language you spend your time on bindings and on the host application; with NanoLang you spend it on the toolchain, on shadow tests, and on reading the specification. The README's own framing supports that reading: it points to docs/SPECIFICATION.md as the complete technical definition and to formal/README.md for the proof suite, which suggests the specification is meant to be consulted, not skimmed. If your goal is to ship an application, that is overhead. If your goal is to study or build a generation target with a verified core, it is the point.

## Maintenance, licensing and what to check before adopting

The repository is not archived, and the last push was on 2026-09-10. Releases v3.5.0, v4.0.0 and v4.5.0 landed in the weeks before that, so the project is moving, though the README notes that the last public GitHub Release was v4.0.0 while docs/RELEASE_4.5.md covers 4.1 through 4.5. That mismatch is worth knowing if you pin versions. Upgrade cost is real in a project at this stage: the README describes 4.0 as adding NanoISA v2 and a verifier, and 4.5 as adding versioned service contracts, capabilities, a POSIX fabric and the trap journal, which is a lot of surface area changing between minor versions. The licence is Apache-2.0, which permits commercial use and modification and includes an explicit patent grant; the LICENSE file in the repository root is the authoritative text, and if you redistribute a modified compiler you should read its notice requirements rather than assume. One repository detail worth flagging for anyone automating installs: package.json at the root is described as dependency metadata for GitHub dependency analysis, private and versioned 4.5.0, and it only pulls in vscode-languageclient and TypeScript tooling for the editor extension. It is not the build system. The build is make, and on BSD that means gmake.

## Conclusion

Adopt NanoLang if you want to study what a language designed around LLM-generated code looks like in practice, or if you need a small typed target that lowers to C and to a verified bytecode VM. Do not adopt it as a drop-in replacement for a production systems language: the README states the compiler tracks shadow coverage but does not fail the build when a test is missing, the trap journal is not wired into every VM trap in 4.5, and the bootstrap badge still reads self-hosting in progress. Before committing, clone the repository, run make build, compile the hello.nano example, and then check whether the shadow warning appears when you delete a shadow block. That single test tells you how much the compiler actually enforces.

## FAQ

### What is NanoLang?

NanoLang is an experimental programming language from jordanhubbard designed to be written by coding LLMs and read by humans. It transpiles to C, runs a verified bytecode VM called NanoISA, and expects a shadow test block next to each function.

### How do I install and build NanoLang?

Clone the repository and run make build; the README shows git clone, cd nanolang, make build. BSD users should use gmake instead of make, since the top-level Makefile is a bridge that reinvokes gmake on GNUmakefile.

### Does NanoLang fail the build when a function has no shadow test?

No. The README states the compiler warns loudly when a function is missing its shadow test block, tracks shadow coverage, but does not currently fail the build on its absence.

### Is NanoLang self-hosting?

The README's badge row describes bootstrap as self-hosting in progress, so the project does not present full self-hosting as complete.

## Sources

- [Issues](https://github.com/jordanhubbard/nanolang/issues)
- [jordanhubbard/nanolang on GitHub](https://github.com/jordanhubbard/nanolang)
- [License: Apache-2.0](https://github.com/jordanhubbard/nanolang/blob/main/LICENSE)
- [README](https://github.com/jordanhubbard/nanolang/blob/main/README.md)
- [Releases](https://github.com/jordanhubbard/nanolang/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jordanhubbard-nanolang
