Back Original

Nanolang: A tiny experimental language designed to be targeted by coding LLMs

CI License Bootstrap Type System Language

A minimal, LLM-friendly programming language with mandatory testing and unambiguous syntax.

NanoLang transpiles to C for native performance while providing a clean, modern syntax optimized for both human readability and AI code generation.

Self-hosting: NanoLang supports true self-hosting via a Stage 0 โ†’ Stage 1 โ†’ Stage 2 bootstrap (make bootstrap); see planning/SELF_HOSTING.md.

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

This builds the compiler:

  • bin/nanoc - NanoLang compiler (transpiles to C)

Create hello.nano:

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

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

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

shadow main {
    assert true
}

Run it:

# Compile to native binary
./bin/nanoc hello.nano -o hello
./hello

Tier 1: Fully Supported โœ…

NanoLang is actively tested and supported on:

  • Ubuntu 22.04+ (x86_64)
  • Ubuntu 24.04 (ARM64) - Raspberry Pi, AWS Graviton, etc.
  • macOS 14+ (ARM64/Apple Silicon)
  • FreeBSD

Tier 2: Windows via WSL ๐ŸชŸ

Windows 10/11 users: NanoLang runs perfectly on Windows via WSL2 (Windows Subsystem for Linux).

# In PowerShell (as Administrator)
wsl --install -d Ubuntu

After installation, restart your computer, then:

# Inside WSL Ubuntu terminal
git clone https://github.com/jordanhubbard/nanolang.git
cd nanolang
make
./bin/nanoc examples/language/nl_hello.nano -o hello
./hello

Why WSL? NanoLang's dependencies (SDL2, ncurses, pkg-config) are Unix/POSIX libraries. WSL2 provides a full Linux environment with near-native performance on Windows.

Note: Native Windows binaries (.exe) are not currently supported, but may be added in a future release via cross-compilation.

Tier 3: Experimental ๐Ÿงช

These platforms should work but are not actively tested in CI:

  • macOS Intel (via Rosetta 2 on Apple Silicon, or native on older Macs)
  • Other Linux distributions (Arch, Fedora, Debian, etc.)
  • OpenBSD (requires manual dependency installation)
  • Prefix Notation - No operator precedence: (+ a (* b c)) is always clear
  • Mandatory Testing - Every function requires a shadow test block
  • Static Typing - Catch errors at compile time
  • Generic Types - Generic unions like Result<T, E> for error handling
  • Compiled Language - Transpiles to C for native performance
  • Immutable by Default - Use let mut for mutability
  • C Interop - Easy FFI via modules with automatic package management
  • Module System - Automatic dependency installation via module.json
  • Standard Library - Growing stdlib with Result<T,E>, string ops, math, and more
  1. User Guide (HTML) - Progressive tutorial + executable snippets
  2. Getting Started - 15-minute tutorial
  3. Quick Reference - Syntax cheat sheet
  4. Language Specification - Complete reference
  5. Examples - Working examples (all runnable)
# Variables (immutable by default)
let x: int = 42
let mut counter: int = 0

# Functions with mandatory tests
fn add(a: int, b: int) -> int {
    return (+ a b)
}

shadow add {
    assert (== (add 2 3) 5)
    assert (== (add -1 1) 0)
}

# Control flow
if (> x 0) {
    (println "positive")
} else {
    (println "negative or zero")
}

# Loops
let mut i: int = 0
while (< i 10) {
    print i
    set i (+ i 1)
}

No operator precedence to remember:

# Crystal clear - no ambiguity
(+ a (* b c))           # a + (b * c)
(and (> x 0) (< x 10))  # x > 0 && x < 10
(/ (+ a b) (- c d))     # (a + b) / (c - d)
# Primitives
int, float, bool, string, void

# Composite types
struct Point { x: int, y: int }
enum Status { Pending = 0, Active = 1, Complete = 2 }

# Generic lists
let numbers: List<int> = (List_int_new)
(List_int_push numbers 42)

# First-class functions
fn double(x: int) -> int { return (* x 2) }
let f: fn(int) -> int = double

# Generic unions (NEW!)
union Result<T, E> {
    Ok { value: T },
    Err { error: E }
}

let success: Result<int, string> = Result.Ok { value: 42 }
let failure: Result<int, string> = Result.Err { error: "oops" }

NanoLang includes a growing standard library:

union Result<T, E> {
    Ok { value: T },
    Err { error: E }
}

fn divide(a: int, b: int) -> Result<int, string> {
    if (== b 0) {
        return Result.Err { error: "Division by zero" }
    }
    return Result.Ok { value: (/ a b) }
}

fn main() -> int {
    let result: Result<int, string> = (divide 10 2)

    /* Note: Result helper functions (is_ok/unwrap/etc) are planned once
     * generic functions are supported. For now, use match.
     */
    match result {
        Ok(v) => (println v.value),
        Err(e) => (println e.error)
    }

    return 0
}

See examples/README.md for the complete list.

NanoLang includes several modules with automatic dependency management:

  • ncurses - Terminal UI (interactive games, text interfaces)
  • sdl - 2D graphics, windows, input (brew install sdl2)
  • sdl_mixer - Audio playback (brew install sdl2_mixer)
  • sdl_ttf - Font rendering (brew install sdl2_ttf)
  • glfw - OpenGL window management (brew install glfw)

Modules automatically install dependencies via package managers (Homebrew, apt, etc.) when first used. See docs/MODULE_SYSTEM.md for details.

# Build (3-stage component bootstrap)
make build

# Run full test suite
make test

# Quick test (language tests only)
make test-quick

# Build all examples
make examples

# Launch the examples browser
make examples-launcher

# Validate user guide snippets (extract โ†’ compile โ†’ run)
make userguide-check

# Build static HTML for the user guide
make userguide-html
# Options:
#   CMD_TIMEOUT=600              # per-command timeout (seconds)
#   USERGUIDE_TIMEOUT=600        # build timeout (seconds)
#   USERGUIDE_BUILD_API_DOCS=1   # regenerate API reference
#   NANO_USERGUIDE_HIGHLIGHT=0   # disable highlighting (CI default)

# Serve the user guide locally (dev)
make -C userguide serve

# Clean build
make clean

# Install to /usr/local/bin (override with PREFIX=...)
sudo make install

On BSD systems (FreeBSD/OpenBSD/NetBSD), use GNU make: gmake build, gmake test, etc.

NanoLang is designed to be LLM-friendly with unambiguous syntax and mandatory testing. To teach an AI system to code in NanoLang:

  • MEMORY.md - Complete LLM training reference with patterns, idioms, debugging workflows, and common errors
  • spec.json - Formal language specification (types, stdlib, syntax, operations)
  • Examples - Runnable examples demonstrating all features
  1. Read MEMORY.md first - covers syntax, patterns, testing, debugging
  2. Reference spec.json for stdlib functions and type details
  3. Study examples for idiomatic usage patterns

The combination of MEMORY.md (practical guidance) + spec.json (formal reference) provides complete coverage for code generation and understanding.

We welcome contributions! Areas where you can help:

  • Add examples and tutorials
  • Improve documentation
  • Report bugs or suggest features
  • Create new modules
  • Implement standard library functions

See CONTRIBUTING.md for guidelines.

Current: Production-ready compiler with full self-hosting support.

  • โœ… Complete language implementation (lexer, parser, typechecker, transpiler)
  • โœ… Compiled language (transpiles to C for native performance)
  • โœ… Static typing with inference
  • โœ… Structs, enums, unions, generics
  • โœ… Module system with auto-dependency management
  • โœ… 49+ standard library functions
  • โœ… 90+ working examples
  • โœ… Shadow-test framework
  • โœ… FFI support for C libraries
  • โœ… Memory safety features

See docs/ROADMAP.md for future plans.

NanoLang solves three problems:

  1. LLM Code Generation - Unambiguous syntax reduces AI errors
  2. Testing Discipline - Mandatory tests improve code quality
  3. Simple & Fast - Minimal syntax, native performance

Design Philosophy:

  • Minimal syntax (18 keywords vs 32 in C)
  • One obvious way to do things
  • Tests are part of the language, not an afterthought
  • Transpile to C for maximum compatibility

Apache License 2.0 - See LICENSE file for details.