GitHub - jordanhubbard/nanolang: A tiny experimental language designed to be targeted by coding LLMs

6 min read Original article ↗

nanolang

CI Tests Coverage License Version Bootstrap Type System Parity

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: the compiler is being incrementally self-hosted; see planning/SELF_HOSTING.md for design notes and current status.

Quick Start

Install

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

This builds two tools:

  • bin/nano - Interactive interpreter
  • bin/nanoc - Compiler (transpiles to C)

Hello World

Create hello.nano:

fn greet(name: string) -> void {
    (println (str_concat "Hello, " name))
}

shadow greet {
    greet "World"
    greet "NanoLang"
}

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

shadow main {
    assert (== (main) 0)
}

Run it:

# Option 1: Interpret (instant execution)
./bin/nano hello.nano

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

Key Features

  • 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
  • 100% Parity - Compiler and interpreter support identical features
  • 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

Documentation

Learning Path

  1. Getting Started - 15-minute tutorial
  2. Quick Reference - Syntax cheat sheet
  3. Language Specification - Complete reference
  4. Examples - Working examples (all runnable)

Key Topics

Language Overview

Syntax Basics

# 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)
}

Why Prefix Notation?

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)

Type System

# 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" }

Standard Library

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
}

Examples

Core Examples

Game Examples

See examples/README.md for the complete list.

Modules

NanoLang includes several modules with automatic dependency management:

Graphics & Games

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

AI/ML

  • onnx - Neural network inference (brew install onnxruntime)

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

Building & Testing

# Build compiler and interpreter
make

# Run test suite
make test

# Build all examples
make examples

# Clean build
make clean

# Install to /usr/local/bin
sudo make install

Teaching LLMs NanoLang

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

For LLM Training

  • 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

Quick LLM Bootstrap

  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.

Contributing

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.

Project Status

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

Completed Features

  • ✅ Complete language implementation (lexer, parser, typechecker, transpiler)
  • ✅ Dual execution (interpreter + compiler)
  • ✅ 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.

Why NanoLang?

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

License

Apache License 2.0 - See LICENSE file for details.

Links