PanzerPeter/Neuro

An AOT compiled programming language using LLVM

5

stars

221

commits

Rust

primary language

Sep 11, 2026

updated

ai
compiler
compiler-design
enzyme
inkwell
llvm
llvm-20
machine-learning
machinelearning
mlir
programming
programming-language
programming-languages
rust

README

Neuro Programming Language

An AOT-compiled language for high-performance AI development.

neurc type-checks, compiles, and runs a Neuro program in under a second

License: Neuro Shared Source License v2.1 Documentation LLVM CI

Status: Alpha. Phase 1 (Core Language) is complete: the full general-purpose language surface compiles and runs. Phase 2 (Tensors and MLIR) is now open. Per-phase status lives in one place: the Quick Roadmap.


Table of Contents


Overview

Neuro is an Ahead-of-Time (AOT) compiled language for AI workloads. Python is interpreted and leans on C libraries for anything fast; Neuro compiles to native code through an LLVM 20 backend instead. Planned on top of that backend:

  • MLIR-based tensor operations, for static shape-verified tensor types
  • IR-level automatic differentiation via Enzyme
  • GPU acceleration via MLIR GPU dialects (nvgpu, rocdl, Triton)

Quick Example

A single perceptron with ReLU activation; uses structs, impl blocks, associated functions, instance methods, if-expressions, implicit returns, and println. This file compiles and runs today.

struct Neuron {
    weight: f64,
    bias: f64
}

impl Neuron {
    func new(weight: f64, bias: f64) -> Neuron {
        Neuron { weight: weight, bias: bias }
    }

    // ReLU activation: pass-through if positive, clamp to zero otherwise
    func activate(&self, input: f64) -> f64 {
        val z = (input * self.weight) + self.bias
        if z > 0.0 { z } else { 0.0 }
    }

    func is_active(&self, input: f64) -> bool {
        val z = (input * self.weight) + self.bias
        z > 0.0
    }
}

func main() -> i32 {
    val neuron = Neuron::new(0.5, -0.1)

    val dead = neuron.activate(0.0)         // 0.0 * 0.5 − 0.1 = −0.1 → clamped to 0.0
    val dead_fires = neuron.is_active(0.0)
    println("input 0.0 -> {dead:.2}  fires: {dead_fires}")

    val active = neuron.activate(1.0)       // 1.0 * 0.5 − 0.1 =  0.4 → passes through
    val active_fires = neuron.is_active(1.0)
    println("input 1.0 -> {active:.2}  fires: {active_fires}")

    if dead > 0.0 { return 1 }

    return (active * 10.0) as i32           // 4
}
input 0.0 -> 0.00  fires: false
input 1.0 -> 0.40  fires: true

Current Capabilities

Every row below is implemented, tested, and usable today. Depth lives elsewhere: the documentation site and docs/ for reference material, CHANGELOG.md for the per-release detail, and the Quick Roadmap for what is still ahead.

FeatureSummary
Types & inferencei8 through u64, f16/bf16/f32/f64, bool, char, string; literal suffixes, digit separators, as casts, type aliases, .is_nan()
Functions & control flowRecursion, forward refs, implicit returns, named arguments with external labels (clamp(x, min: 0.0)); if/elif/else, while, loop, range-for, for (i, x) in xs.enumerate(), labelled break/continue, block-as-value; for over any type implementing the prelude's IntoIterator / Iterator protocol, plus .map(f) / .filter(p) head adapters
GenericsGeneric functions, structs, and impls plus const generics, where clauses, and turbofish, all fully monomorphized at zero runtime cost
Traits & dispatchRequired and default methods, associated types (type Item / Self::Item) and Trait<Assoc = T> bounds, operator traits, impl Trait (static) and dyn Trait (vtable) dispatch with object-safety checks
Closures & lambdas|x: i32| x * x, move closures, (T) -> R function types, higher-order functions; compiled to { fn_ptr, env_ptr }, no heap
Structs & methodsFields, shorthand init, functional update ..base, impl blocks with &self / &mut self methods and associated functions; @derive(Copy, Clone, Debug, PartialEq) for copying, {p:?} rendering, and structural equality
Enums & newtypesUnit, tuple, and struct-field variants; generic enums monomorphized per type argument; newtype for distinct nominal wrappers
Arrays, tuples & collectionsFixed-size [T; N] and anonymous tuples over Copy elements; borrowed slices &[T] / &mut [T] with zero-copy .slice(range) over an array or a Vec; heap-backed Vec<T>, HashMap<K, V>, BTreeMap<K, V>, String that move on assignment and free at scope exit; statically shaped Tensor<T, [d0, ...]> built from an annotated nested literal or Tensor::<T, [...]>::zeros() / ones() / identity() / random_normal() / scalar() / from(), owning its buffer with .clone(), .to(device), and in-place += / -= / *= / /= / %=
Pattern matchingExhaustive match expressions over variant / literal / or / range / wildcard patterns with if guards, plus val Point { x, y } = p and val [a, ..rest] = arr destructuring
Option / ResultOption<T> and Result<T, E> from the implicit prelude. They are ordinary generic enums, available with no declaration and no import, variants included; ?? unwraps either with a lazy fallback; ? propagates the failure to the caller; val-else unwraps or exits the scope; checked_add / checked_sub / checked_mul report integer overflow as Option::None
Ownership & borrowsMove-by-default, Copy, deterministic Drop, &T / &mut T with flow-sensitive exclusivity, lifetime elision and annotations
StringsImmutable fat-pointer string with escapes, &string slices, ==, + concatenation, .len() / .clone() / .slice(a..b) / .char_slice(a..b), codepoint iteration with .chars() and .char_indices(), interpolation "{x:.2}", triple-quoted """ blocks with dedent; growable String buffer for building text: push_str / clear / to_string
Modules & visibilityMulti-file programs: every .nr file is a module and mod.nr directories nest; inline module { } blocks group within one file; import math::{sqrt}, import ./utils, as renames, module aliases, variant imports, and export import re-export facades; declarations and struct fields are private until export opts them in; an implicit prelude puts Option / Result and Some / None / Ok / Err in every module, with @no_prelude to opt out
ToolchainNative binaries via inkwell 0.10 / LLVM 20; neurc check and neurc compile; buffered print / println to stdout, line-buffered on a terminal and drained on every exit path; panic / assert / unreachable runtime with located diagnostics, covering array bounds, string slices, a zero divisor, and debug-build integer overflow, all outlined off the hot path

Current Memory Model

Alpha memory warning. Stack values are reclaimed on return and string literals live in .rodata, so neither leaks. Move semantics, borrows, deterministic Drop, and the owning collections have landed, so a Vec, HashMap, BTreeMap, or String frees its buffer at scope exit. A heap string (the one + concatenation and interpolation produce) is freed too when the compiler can prove who owns it: a temporary the statement consumes, or a binding whose initializer allocated it. A loop that formats output therefore holds steady rather than growing.

What still leaks is a heap string that escapes what the compiler can follow: one stored into a collection or a struct field, one returned from a function, and the prior value of a reassigned binding. The ownership test answers conservatively by design, since freeing a .rodata literal would be far worse than holding a buffer.

This block is removed once those results are tracked too. Until then, do not assume memory-safety semantics beyond what the table above claims.

If memory-safety semantics and compiler backend design are your thing, this is exactly where contributors are needed.


Performance

neurc compile -O 3 hands the module to the same LLVM 20 optimization pipeline clang -O2 uses, so compute-bound code lands in the same range as C++ rather than somewhere between C++ and Python.

Best of nine runs on one machine, lower is better. Reproduce with python benchmarks/run.py, which builds all three implementations of each program and refuses to report timings if they disagree on output:

BenchmarkWhat it stressesNeuro -O 3clang -O2Python 3.14
mandelbrotscalar f64 in a tight loop166 ms166 ms5791 ms
vector_sumVec push, indexed sweep25 ms26 ms10068 ms
call_overheadrecursion, call and inline cost45 ms51 ms1389 ms
print_linesinteger holes to standard output13 ms22 ms110 ms
format_floatsf64 holes at a fixed precision118 ms109 ms214 ms
int_divideguarded / and %, opaque divisor96 ms89 ms1318 ms

Absolute times belong to the machine rather than to the language, and the Python column to whichever python3 is on your PATH, which is why the version is named. Two rows are worth a word. print_lines beats C because an integer hole renders through a digit loop instead of snprintf; int_divide is the one place the compiler spends rather than saves, since / and % guard the operand pairs the hardware instruction leaves undefined and an opaque divisor keeps those guards in the loop.

The default is -O 0: checked arithmetic, no optimization pipeline. Pass -O 3 before drawing any conclusion about speed.


Installation

Prerequisites

RequirementVersionNotes
Rust1.85+Install via rustup
LLVM 2020.x with dev libsPlatform instructions below
C linkeranygcc/clang on Linux/macOS; MSVC on Windows

Step 1: LLVM 20

This is the only step that differs between systems. Add the export to your shell profile (~/.bashrc, ~/.zshrc) so it survives a new terminal.

Arch Linux / CachyOS

sudo pacman -S llvm20
export LLVM_SYS_201_PREFIX=/usr/lib/llvm20

Ubuntu / Debian

wget -qO- https://apt.llvm.org/llvm.sh | sudo bash -s -- 20
# or the full dev package set:
# sudo apt-get install llvm-20 llvm-20-dev llvm-20-tools libpolly-20-dev
export LLVM_SYS_201_PREFIX=/usr/lib/llvm-20

macOS (Homebrew)

brew install llvm@20
export LLVM_SYS_201_PREFIX="$(brew --prefix llvm@20)"

Windows 10 / 11 (x64) needs a longer walkthrough; see below.

Step 2: Build

With LLVM in place and Rust installed from rustup.rs:

git clone https://github.com/PanzerPeter/Neuro.git
cd Neuro
cargo build --release
cargo test --workspace

cargo install --path compiler/neurc   # optional, puts neurc on your PATH

On Windows the same four commands run unchanged in PowerShell, and cargo install places neurc.exe in %USERPROFILE%\.cargo\bin, which rustup has already added to PATH.


Windows 10 / 11 (x64)

Windows needs the MSVC toolchain, not GNU, and LLVM does not come from a package manager. Four extra steps, after which Step 2 above runs unchanged.

Install Visual Studio Build Tools. Download from visualstudio.microsoft.com/downloads under Tools for Visual StudioBuild Tools for Visual Studio 2022, and select the Desktop development with C++ workload. 2019 or later works.

Install Rust. Run rustup-init.exe from rustup.rs and choose 1) Proceed with standard installation, which selects the stable-x86_64-pc-windows-msvc toolchain. Open a new PowerShell window afterwards so cargo and rustc are on PATH.

Install LLVM 20 to a path without spaces (the NSIS installer enforces this):

$version = "20.1.8"
$url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-$version/LLVM-$version-win64.exe"
curl.exe -fsSL -o "$env:TEMP\llvm-installer.exe" $url
Start-Process "$env:TEMP\llvm-installer.exe" -ArgumentList "/S /D=C:\LLVM" -Wait -PassThru | Out-Null

The installer is also downloadable by hand from the LLVM releases page.

Point the build at it. No admin rights needed:

[Environment]::SetEnvironmentVariable(
    "LLVM_SYS_201_PREFIX", "C:\LLVM",
    [EnvironmentVariableTarget]::User
)
$current = [Environment]::GetEnvironmentVariable("Path", "User")
[Environment]::SetEnvironmentVariable("Path", "$current;C:\LLVM\bin", "User")

Close and reopen PowerShell, then check with llvm-config --version, which should print 20.x.y.

Troubleshooting Windows build errors

  • llvm-sys build script cannot find LLVM: confirm LLVM_SYS_201_PREFIX is set in the current shell session (echo $env:LLVM_SYS_201_PREFIX) and points to a directory that contains bin\llvm-config.exe.
  • link.exe not found: the MSVC Build Tools are not on PATH. Run the build from a Developer PowerShell / x64 Native Tools Command Prompt or install the C++ build tools workload as described above.
  • Version mismatch (llvm-sys-201 requires LLVM 20): an older LLVM is on PATH. Set LLVM_SYS_201_PREFIX explicitly to the LLVM 20 prefix and ensure C:\LLVM\bin precedes any other LLVM entries in PATH.

Usage

# Type-check a source file (no binary produced)
cargo run -p neurc -- check examples/basics/hello.nr

# Compile to a native executable
cargo run -p neurc -- compile examples/basics/factorial.nr

# Run the compiled binary (emitted next to the source file)
./examples/basics/factorial

# After cargo install --path compiler/neurc:
neurc compile examples/basics/factorial.nr

Language Syntax

Variables and Types

// Immutable by default
val x: i32 = 42
val name: string = "Neuro"

// Mutable with reassignment
mut counter: i32 = 0
counter = counter + 1

// Type inference works for both val and mut
val pi = 3.14159   // inferred f64
val n  = 100       // inferred i32
mut count = 0      // inferred i32; type annotation optional

Functions

// Explicit return
func add(a: i32, b: i32) -> i32 {
    return a + b
}

// Expression-based implicit return (trailing expression)
func multiply(a: i32, b: i32) -> i32 {
    a * b
}

Control Flow

func fizzbuzz(n: i32) -> i32 {
    mut i: i32 = 1
    while i <= n {
        i = i + 1
    }
    i
}

func sum(n: i32) -> i32 {
    mut total: i32 = 0
    for i in 0..n {
        total = total + i
    }
    total
}

Structs

struct Point {
    x: f64,
    y: f64
}

func distance(p: Point) -> f64 {
    // field read
    val dx = p.x
    val dy = p.y
    dx * dx + dy * dy   // placeholder (no sqrt yet)
}

func main() -> i32 {
    val origin = Point { x: 0.0, y: 0.0 }

    // field mutation requires mut binding
    mut cursor = Point { x: 3.0, y: 4.0 }
    cursor.x = 1.0

    return 0
}

Closures and Higher-Order Functions

Verbatim from examples/showcase/closures.nr. It compiles, links, prints the three results below, and exits with code 90.

// Apply `f` to each element of a 4-element array and sum the results.
func map_sum(xs: [i32; 4], f: (i32) -> i32) -> i32 {
    mut total: i32 = 0
    mut i: i32 = 0
    while i < 4 {
        total += f(xs[i])
        i += 1
    }
    return total
}

struct Scaler {
    factor: i32
}

impl Scaler {
    func apply(&self, x: i32) -> i32 {
        x * self.factor
    }
}

func main() -> i32 {
    val data: [i32; 4] = [1, 2, 3, 4]

    // A closure capturing a Copy local (`bias`) by value.
    val bias = 10
    val biased = map_sum(data, |x: i32| x + bias)   // 11+12+13+14 = 50

    // A `move` closure with a block body and early return.
    val scale = 3
    val scaled = map_sum(data, move |x: i32| -> i32 {
        val y = x * scale
        return y
    })                                              // 3+6+9+12 = 30

    // A struct method still resolves alongside closures.
    val s = Scaler { factor: 2 }
    val doubled = s.apply(5)                         // 10

    println("capture by value  |x| x + bias      = {biased}")
    println("move closure      move |x| x * scale = {scaled}")
    println("struct method     s.apply(5)         = {doubled}")

    val total = biased + scaled + doubled
    println("total                                = {total}")
    total                                            // 50 + 30 + 10 = 90
}

Every runnable program in examples/showcase/ combines several features at once and is pinned twice: to an expected exit code in examples/expected.txt, and to the exact text it prints in a sibling .out file. By-value tensor arithmetic, @grad, and GPU kernels are not shown here because they do not exist yet; tensor construction does, in showcase/model_shapes.nr, and the in-place update in showcase/optimizer_step.nr. See the Quick Roadmap.


Architecture

Neuro follows Vertical Slice Architecture (VSA): the code is organized by language feature, not by technical layer.

Workspace Layout

compiler/
├── infrastructure/          # Shared, zero-business-logic crates
│   ├── ast-types/           #   AST node definitions
│   ├── diagnostics/         #   Error / warning types + rendering
│   ├── project-config/      #   Project / manifest configuration
│   ├── shared-types/        #   Primitives shared across slices
│   ├── source-location/     #   Spans, positions, source files
│   └── neuro-hir/           #   Typed High-Level IR (frontend ↔ backend contract)
├── lexical-analysis/        # Tokenizer (logos, Unicode XID)
├── syntax-parsing/          # Pratt + statement parser → AST
├── semantic-analysis/       # Type checker, scope analysis
├── control-flow/            # CFG data structures; no caller yet
├── hir-lowering/            # Type-checked AST → typed HIR
├── llvm-backend/            # HIR → object code (inkwell 0.10 / LLVM 20)
├── mlir-backend/            # HIR → MLIR scaffold (off-by-default `mlir` feature)
└── neurc/                   # CLI compiler driver (pipeline orchestration)

Compilation Pipeline

Today:

Source (.nr)
  → Lexical Analysis   (tokens)
  → Syntax Parsing     (AST)
  → Semantic Analysis  (type-checked AST)
  → HIR Lowering       (typed High-Level IR, neuro-hir)
  → LLVM Backend       (object code via inkwell / LLVM 20)
  → System Linker      (native executable)

Planned extension (Phase 2+):

Tensor/AI path: typed High-Level IR (neuro-hir)
  → MLIR (linalg/tensor/func/arith, LLVM 20 / MLIR 20)
  → Enzyme MLIR AD pass (@grad)
  → GPU dialects (nvgpu/rocdl/Triton) or llvm dialect
  → inkwell → native code

Quick Roadmap

Each numbered phase is a MAJOR-version milestone: completing Phase N ships v(N+1).0.0. Phase 1 is complete and we are now in Phase 2. A phase is divided into lettered sub-phases.

PhaseGoalStatus
1Core Language: types, control flow, LLVM backend, ownership and borrow checking, generics, traits and dispatch, closures, enums and pattern matching, error handling, modules and prelude, string interpolationComplete
2Tensors and MLIR: first-class tensor types lowered through MLIR Linalg, plus the pool allocator. Finishing it ships v3.0.0In progress
2AStandard I/O and spec stragglers: print / println, .is_nan(), codepoint string APIs, .enumerate(), borrowed slices &[T], the iterator protocol, @derive(Debug, PartialEq)Complete
2BTensor core: Tensor<T, [...]>, literal coercion, move semantics, DLPack, slicing, shape generics, named dims, dynamic shapes, reductionsIn progress
2CMLIR lowering: tensor arithmetic to Linalg, broadcasting, matmul behind @, end-to-end HIR → MLIR → LLVMPlanned
2DPool allocator: pool blocks, PoolAware, LIFO release at scope exitPlanned
2EFunctional sugar: pipeline |>, composition >>, einstein notation, functional tensor opsPlanned
3Automatic differentiation: Enzyme MLIR pass, @grad(wrt: ...), .backward() / .zero_grad(), higher-order derivatives, SGDPlanned
4GPU acceleration: MLIR GPU dialects (nvgpu / rocdl / Triton), @gpu, KernelOut<T> aliasing model, device memory pool, CPU fallbackPlanned
5Neural network standard library: TrainableTensor, ParameterList, optimizers, @model, Dense / Conv2d / Attention, .nrm serializationPlanned
6Async runtime: async func, Future<T>, spawn, JoinHandle, join / race, executor for data-loader / I/O overlapPlanned
7Interop and advanced features: Python FFI via DLPack, spread operator, advanced pattern matching, custom attributes, deferPlanned
8Developer experience: Language Server Protocol, diagnostics polish, formatter, @test runnerPlanned
9Package manager and distribution: neurpm, cross-OS installer / uninstaller / self-updater, signed release binaries, optimization passes (loop unrolling, AD-aware inlining, LTO)Planned

Development

Set LLVM_SYS_201_PREFIX for your platform before running any Cargo command (see Installation for the correct path per OS).

# Build the full workspace
cargo build --workspace

# Run all tests
cargo test --workspace

# Lint
cargo clippy --workspace --all-targets -- -D warnings

# Format check
cargo fmt --all -- --check

# Apply formatting
cargo fmt --all

On Windows, use PowerShell or a Developer Command Prompt. The env var must be set in the current session; prefix it inline if needed:

$env:LLVM_SYS_201_PREFIX = "C:\LLVM"
cargo build --workspace

VSCode Extension

Syntax highlighting for .nr files is included in neuro-language-support/.

cd neuro-language-support
npm install -g @vscode/vsce      # once
vsce package                     # -> neuro-language-support-<version>.vsix
code --install-extension neuro-language-support-*.vsix --force

Reload the VS Code window afterwards (Developer: Reload Window). A grammar change does not apply to already-open editors. During grammar work, symlinking the folder into ~/.vscode/extensions/ avoids repackaging: a window reload then picks up every edit.


File Extensions

ExtensionPurpose
.nrNeuro source files
.nrlCompiled library modules
.nrmSerialized model/matrix data
.nrpPackage definitions

Contributing

See CONTRIBUTING.md for architecture guidelines, coding standards, and the pull request process. Confirmed open defects live in docs/BUGS.md. Fixing one is the best way to start.

The project is in early alpha, so breaking changes are expected. Contributions should focus on Phase 2 (Tensors and MLIR); the Quick Roadmap marks which phase is currently open.


Why Neuro?

AI development is stuck in a fragmented paradigm: developers iterate in an interpreted glue language (Python), while underlying libraries are written in unmanaged, safety-critical systems languages (C++/CUDA).

Neuro is built to unify this stack:

  1. True native performance. Compiled AOT via LLVM 20, with no heavy runtime interpreter and no global interpreter lock (GIL). Measured against C++ and Python on compute-bound programs.
  2. AI-First Type System: Native compile-time shape verification for tensors using MLIR (Phase 2), preventing runtime dimension mismatches before a single line of training executes.
  3. Immutability by Default: A modern val/mut paradigm to ensure highly parallelized tensor computations are thread-safe by design.

License

Licensed under the Neuro Shared Source License v2.1.

Why not MIT/Apache 2.0 right now? Neuro is in a critical pre-stabilization phase. The license protects against three specific risks: commercial re-packaging of the compiler before the language spec is stable, AI-assisted reproduction of the compiler for a competing product, and misleading forks that fragment the early ecosystem. None of these restrictions affect normal use.

What you can do freely:

  • Use, study, and modify the compiler for any personal or internal purpose
  • Write Neuro programs and distribute or sell the compiled output under any terms you choose. programs you compile are wholly exempt from this license
  • Build tools, plugins, and editor integrations that call into the compiler
  • Contribute code back to the project

What requires a commercial license:

  • Redistributing the Neuro compiler itself (or a fork of it) as part of a commercial product

See LICENSE for full terms.

Acknowledgments

Inspired by Rust (ownership, type system), Python (AI ecosystem simplicity), Swift (language ergonomics), and Mojo (AI-first design). Built with inkwell, logos, and the LLVM infrastructure.

Contributors

PanzerPeter

221 commits

PanzerPeter/Neuro

An AOT compiled programming language using LLVM

5

stars

221

commits

Rust

primary language

Sep 11, 2026

updated

ai
compiler
compiler-design
enzyme
inkwell
llvm
llvm-20
machine-learning
machinelearning
mlir
programming
programming-language
programming-languages
rust

README

Neuro Programming Language

An AOT-compiled language for high-performance AI development.

neurc type-checks, compiles, and runs a Neuro program in under a second

License: Neuro Shared Source License v2.1 Documentation LLVM CI

Status: Alpha. Phase 1 (Core Language) is complete: the full general-purpose language surface compiles and runs. Phase 2 (Tensors and MLIR) is now open. Per-phase status lives in one place: the Quick Roadmap.


Table of Contents


Overview

Neuro is an Ahead-of-Time (AOT) compiled language for AI workloads. Python is interpreted and leans on C libraries for anything fast; Neuro compiles to native code through an LLVM 20 backend instead. Planned on top of that backend:

  • MLIR-based tensor operations, for static shape-verified tensor types
  • IR-level automatic differentiation via Enzyme
  • GPU acceleration via MLIR GPU dialects (nvgpu, rocdl, Triton)

Quick Example

A single perceptron with ReLU activation; uses structs, impl blocks, associated functions, instance methods, if-expressions, implicit returns, and println. This file compiles and runs today.

struct Neuron {
    weight: f64,
    bias: f64
}

impl Neuron {
    func new(weight: f64, bias: f64) -> Neuron {
        Neuron { weight: weight, bias: bias }
    }

    // ReLU activation: pass-through if positive, clamp to zero otherwise
    func activate(&self, input: f64) -> f64 {
        val z = (input * self.weight) + self.bias
        if z > 0.0 { z } else { 0.0 }
    }

    func is_active(&self, input: f64) -> bool {
        val z = (input * self.weight) + self.bias
        z > 0.0
    }
}

func main() -> i32 {
    val neuron = Neuron::new(0.5, -0.1)

    val dead = neuron.activate(0.0)         // 0.0 * 0.5 − 0.1 = −0.1 → clamped to 0.0
    val dead_fires = neuron.is_active(0.0)
    println("input 0.0 -> {dead:.2}  fires: {dead_fires}")

    val active = neuron.activate(1.0)       // 1.0 * 0.5 − 0.1 =  0.4 → passes through
    val active_fires = neuron.is_active(1.0)
    println("input 1.0 -> {active:.2}  fires: {active_fires}")

    if dead > 0.0 { return 1 }

    return (active * 10.0) as i32           // 4
}
input 0.0 -> 0.00  fires: false
input 1.0 -> 0.40  fires: true

Current Capabilities

Every row below is implemented, tested, and usable today. Depth lives elsewhere: the documentation site and docs/ for reference material, CHANGELOG.md for the per-release detail, and the Quick Roadmap for what is still ahead.

FeatureSummary
Types & inferencei8 through u64, f16/bf16/f32/f64, bool, char, string; literal suffixes, digit separators, as casts, type aliases, .is_nan()
Functions & control flowRecursion, forward refs, implicit returns, named arguments with external labels (clamp(x, min: 0.0)); if/elif/else, while, loop, range-for, for (i, x) in xs.enumerate(), labelled break/continue, block-as-value; for over any type implementing the prelude's IntoIterator / Iterator protocol, plus .map(f) / .filter(p) head adapters
GenericsGeneric functions, structs, and impls plus const generics, where clauses, and turbofish, all fully monomorphized at zero runtime cost
Traits & dispatchRequired and default methods, associated types (type Item / Self::Item) and Trait<Assoc = T> bounds, operator traits, impl Trait (static) and dyn Trait (vtable) dispatch with object-safety checks
Closures & lambdas|x: i32| x * x, move closures, (T) -> R function types, higher-order functions; compiled to { fn_ptr, env_ptr }, no heap
Structs & methodsFields, shorthand init, functional update ..base, impl blocks with &self / &mut self methods and associated functions; @derive(Copy, Clone, Debug, PartialEq) for copying, {p:?} rendering, and structural equality
Enums & newtypesUnit, tuple, and struct-field variants; generic enums monomorphized per type argument; newtype for distinct nominal wrappers
Arrays, tuples & collectionsFixed-size [T; N] and anonymous tuples over Copy elements; borrowed slices &[T] / &mut [T] with zero-copy .slice(range) over an array or a Vec; heap-backed Vec<T>, HashMap<K, V>, BTreeMap<K, V>, String that move on assignment and free at scope exit; statically shaped Tensor<T, [d0, ...]> built from an annotated nested literal or Tensor::<T, [...]>::zeros() / ones() / identity() / random_normal() / scalar() / from(), owning its buffer with .clone(), .to(device), and in-place += / -= / *= / /= / %=
Pattern matchingExhaustive match expressions over variant / literal / or / range / wildcard patterns with if guards, plus val Point { x, y } = p and val [a, ..rest] = arr destructuring
Option / ResultOption<T> and Result<T, E> from the implicit prelude. They are ordinary generic enums, available with no declaration and no import, variants included; ?? unwraps either with a lazy fallback; ? propagates the failure to the caller; val-else unwraps or exits the scope; checked_add / checked_sub / checked_mul report integer overflow as Option::None
Ownership & borrowsMove-by-default, Copy, deterministic Drop, &T / &mut T with flow-sensitive exclusivity, lifetime elision and annotations
StringsImmutable fat-pointer string with escapes, &string slices, ==, + concatenation, .len() / .clone() / .slice(a..b) / .char_slice(a..b), codepoint iteration with .chars() and .char_indices(), interpolation "{x:.2}", triple-quoted """ blocks with dedent; growable String buffer for building text: push_str / clear / to_string
Modules & visibilityMulti-file programs: every .nr file is a module and mod.nr directories nest; inline module { } blocks group within one file; import math::{sqrt}, import ./utils, as renames, module aliases, variant imports, and export import re-export facades; declarations and struct fields are private until export opts them in; an implicit prelude puts Option / Result and Some / None / Ok / Err in every module, with @no_prelude to opt out
ToolchainNative binaries via inkwell 0.10 / LLVM 20; neurc check and neurc compile; buffered print / println to stdout, line-buffered on a terminal and drained on every exit path; panic / assert / unreachable runtime with located diagnostics, covering array bounds, string slices, a zero divisor, and debug-build integer overflow, all outlined off the hot path

Current Memory Model

Alpha memory warning. Stack values are reclaimed on return and string literals live in .rodata, so neither leaks. Move semantics, borrows, deterministic Drop, and the owning collections have landed, so a Vec, HashMap, BTreeMap, or String frees its buffer at scope exit. A heap string (the one + concatenation and interpolation produce) is freed too when the compiler can prove who owns it: a temporary the statement consumes, or a binding whose initializer allocated it. A loop that formats output therefore holds steady rather than growing.

What still leaks is a heap string that escapes what the compiler can follow: one stored into a collection or a struct field, one returned from a function, and the prior value of a reassigned binding. The ownership test answers conservatively by design, since freeing a .rodata literal would be far worse than holding a buffer.

This block is removed once those results are tracked too. Until then, do not assume memory-safety semantics beyond what the table above claims.

If memory-safety semantics and compiler backend design are your thing, this is exactly where contributors are needed.


Performance

neurc compile -O 3 hands the module to the same LLVM 20 optimization pipeline clang -O2 uses, so compute-bound code lands in the same range as C++ rather than somewhere between C++ and Python.

Best of nine runs on one machine, lower is better. Reproduce with python benchmarks/run.py, which builds all three implementations of each program and refuses to report timings if they disagree on output:

BenchmarkWhat it stressesNeuro -O 3clang -O2Python 3.14
mandelbrotscalar f64 in a tight loop166 ms166 ms5791 ms
vector_sumVec push, indexed sweep25 ms26 ms10068 ms
call_overheadrecursion, call and inline cost45 ms51 ms1389 ms
print_linesinteger holes to standard output13 ms22 ms110 ms
format_floatsf64 holes at a fixed precision118 ms109 ms214 ms
int_divideguarded / and %, opaque divisor96 ms89 ms1318 ms

Absolute times belong to the machine rather than to the language, and the Python column to whichever python3 is on your PATH, which is why the version is named. Two rows are worth a word. print_lines beats C because an integer hole renders through a digit loop instead of snprintf; int_divide is the one place the compiler spends rather than saves, since / and % guard the operand pairs the hardware instruction leaves undefined and an opaque divisor keeps those guards in the loop.

The default is -O 0: checked arithmetic, no optimization pipeline. Pass -O 3 before drawing any conclusion about speed.


Installation

Prerequisites

RequirementVersionNotes
Rust1.85+Install via rustup
LLVM 2020.x with dev libsPlatform instructions below
C linkeranygcc/clang on Linux/macOS; MSVC on Windows

Step 1: LLVM 20

This is the only step that differs between systems. Add the export to your shell profile (~/.bashrc, ~/.zshrc) so it survives a new terminal.

Arch Linux / CachyOS

sudo pacman -S llvm20
export LLVM_SYS_201_PREFIX=/usr/lib/llvm20

Ubuntu / Debian

wget -qO- https://apt.llvm.org/llvm.sh | sudo bash -s -- 20
# or the full dev package set:
# sudo apt-get install llvm-20 llvm-20-dev llvm-20-tools libpolly-20-dev
export LLVM_SYS_201_PREFIX=/usr/lib/llvm-20

macOS (Homebrew)

brew install llvm@20
export LLVM_SYS_201_PREFIX="$(brew --prefix llvm@20)"

Windows 10 / 11 (x64) needs a longer walkthrough; see below.

Step 2: Build

With LLVM in place and Rust installed from rustup.rs:

git clone https://github.com/PanzerPeter/Neuro.git
cd Neuro
cargo build --release
cargo test --workspace

cargo install --path compiler/neurc   # optional, puts neurc on your PATH

On Windows the same four commands run unchanged in PowerShell, and cargo install places neurc.exe in %USERPROFILE%\.cargo\bin, which rustup has already added to PATH.


Windows 10 / 11 (x64)

Windows needs the MSVC toolchain, not GNU, and LLVM does not come from a package manager. Four extra steps, after which Step 2 above runs unchanged.

Install Visual Studio Build Tools. Download from visualstudio.microsoft.com/downloads under Tools for Visual StudioBuild Tools for Visual Studio 2022, and select the Desktop development with C++ workload. 2019 or later works.

Install Rust. Run rustup-init.exe from rustup.rs and choose 1) Proceed with standard installation, which selects the stable-x86_64-pc-windows-msvc toolchain. Open a new PowerShell window afterwards so cargo and rustc are on PATH.

Install LLVM 20 to a path without spaces (the NSIS installer enforces this):

$version = "20.1.8"
$url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-$version/LLVM-$version-win64.exe"
curl.exe -fsSL -o "$env:TEMP\llvm-installer.exe" $url
Start-Process "$env:TEMP\llvm-installer.exe" -ArgumentList "/S /D=C:\LLVM" -Wait -PassThru | Out-Null

The installer is also downloadable by hand from the LLVM releases page.

Point the build at it. No admin rights needed:

[Environment]::SetEnvironmentVariable(
    "LLVM_SYS_201_PREFIX", "C:\LLVM",
    [EnvironmentVariableTarget]::User
)
$current = [Environment]::GetEnvironmentVariable("Path", "User")
[Environment]::SetEnvironmentVariable("Path", "$current;C:\LLVM\bin", "User")

Close and reopen PowerShell, then check with llvm-config --version, which should print 20.x.y.

Troubleshooting Windows build errors

  • llvm-sys build script cannot find LLVM: confirm LLVM_SYS_201_PREFIX is set in the current shell session (echo $env:LLVM_SYS_201_PREFIX) and points to a directory that contains bin\llvm-config.exe.
  • link.exe not found: the MSVC Build Tools are not on PATH. Run the build from a Developer PowerShell / x64 Native Tools Command Prompt or install the C++ build tools workload as described above.
  • Version mismatch (llvm-sys-201 requires LLVM 20): an older LLVM is on PATH. Set LLVM_SYS_201_PREFIX explicitly to the LLVM 20 prefix and ensure C:\LLVM\bin precedes any other LLVM entries in PATH.

Usage

# Type-check a source file (no binary produced)
cargo run -p neurc -- check examples/basics/hello.nr

# Compile to a native executable
cargo run -p neurc -- compile examples/basics/factorial.nr

# Run the compiled binary (emitted next to the source file)
./examples/basics/factorial

# After cargo install --path compiler/neurc:
neurc compile examples/basics/factorial.nr

Language Syntax

Variables and Types

// Immutable by default
val x: i32 = 42
val name: string = "Neuro"

// Mutable with reassignment
mut counter: i32 = 0
counter = counter + 1

// Type inference works for both val and mut
val pi = 3.14159   // inferred f64
val n  = 100       // inferred i32
mut count = 0      // inferred i32; type annotation optional

Functions

// Explicit return
func add(a: i32, b: i32) -> i32 {
    return a + b
}

// Expression-based implicit return (trailing expression)
func multiply(a: i32, b: i32) -> i32 {
    a * b
}

Control Flow

func fizzbuzz(n: i32) -> i32 {
    mut i: i32 = 1
    while i <= n {
        i = i + 1
    }
    i
}

func sum(n: i32) -> i32 {
    mut total: i32 = 0
    for i in 0..n {
        total = total + i
    }
    total
}

Structs

struct Point {
    x: f64,
    y: f64
}

func distance(p: Point) -> f64 {
    // field read
    val dx = p.x
    val dy = p.y
    dx * dx + dy * dy   // placeholder (no sqrt yet)
}

func main() -> i32 {
    val origin = Point { x: 0.0, y: 0.0 }

    // field mutation requires mut binding
    mut cursor = Point { x: 3.0, y: 4.0 }
    cursor.x = 1.0

    return 0
}

Closures and Higher-Order Functions

Verbatim from examples/showcase/closures.nr. It compiles, links, prints the three results below, and exits with code 90.

// Apply `f` to each element of a 4-element array and sum the results.
func map_sum(xs: [i32; 4], f: (i32) -> i32) -> i32 {
    mut total: i32 = 0
    mut i: i32 = 0
    while i < 4 {
        total += f(xs[i])
        i += 1
    }
    return total
}

struct Scaler {
    factor: i32
}

impl Scaler {
    func apply(&self, x: i32) -> i32 {
        x * self.factor
    }
}

func main() -> i32 {
    val data: [i32; 4] = [1, 2, 3, 4]

    // A closure capturing a Copy local (`bias`) by value.
    val bias = 10
    val biased = map_sum(data, |x: i32| x + bias)   // 11+12+13+14 = 50

    // A `move` closure with a block body and early return.
    val scale = 3
    val scaled = map_sum(data, move |x: i32| -> i32 {
        val y = x * scale
        return y
    })                                              // 3+6+9+12 = 30

    // A struct method still resolves alongside closures.
    val s = Scaler { factor: 2 }
    val doubled = s.apply(5)                         // 10

    println("capture by value  |x| x + bias      = {biased}")
    println("move closure      move |x| x * scale = {scaled}")
    println("struct method     s.apply(5)         = {doubled}")

    val total = biased + scaled + doubled
    println("total                                = {total}")
    total                                            // 50 + 30 + 10 = 90
}

Every runnable program in examples/showcase/ combines several features at once and is pinned twice: to an expected exit code in examples/expected.txt, and to the exact text it prints in a sibling .out file. By-value tensor arithmetic, @grad, and GPU kernels are not shown here because they do not exist yet; tensor construction does, in showcase/model_shapes.nr, and the in-place update in showcase/optimizer_step.nr. See the Quick Roadmap.


Architecture

Neuro follows Vertical Slice Architecture (VSA): the code is organized by language feature, not by technical layer.

Workspace Layout

compiler/
├── infrastructure/          # Shared, zero-business-logic crates
│   ├── ast-types/           #   AST node definitions
│   ├── diagnostics/         #   Error / warning types + rendering
│   ├── project-config/      #   Project / manifest configuration
│   ├── shared-types/        #   Primitives shared across slices
│   ├── source-location/     #   Spans, positions, source files
│   └── neuro-hir/           #   Typed High-Level IR (frontend ↔ backend contract)
├── lexical-analysis/        # Tokenizer (logos, Unicode XID)
├── syntax-parsing/          # Pratt + statement parser → AST
├── semantic-analysis/       # Type checker, scope analysis
├── control-flow/            # CFG data structures; no caller yet
├── hir-lowering/            # Type-checked AST → typed HIR
├── llvm-backend/            # HIR → object code (inkwell 0.10 / LLVM 20)
├── mlir-backend/            # HIR → MLIR scaffold (off-by-default `mlir` feature)
└── neurc/                   # CLI compiler driver (pipeline orchestration)

Compilation Pipeline

Today:

Source (.nr)
  → Lexical Analysis   (tokens)
  → Syntax Parsing     (AST)
  → Semantic Analysis  (type-checked AST)
  → HIR Lowering       (typed High-Level IR, neuro-hir)
  → LLVM Backend       (object code via inkwell / LLVM 20)
  → System Linker      (native executable)

Planned extension (Phase 2+):

Tensor/AI path: typed High-Level IR (neuro-hir)
  → MLIR (linalg/tensor/func/arith, LLVM 20 / MLIR 20)
  → Enzyme MLIR AD pass (@grad)
  → GPU dialects (nvgpu/rocdl/Triton) or llvm dialect
  → inkwell → native code

Quick Roadmap

Each numbered phase is a MAJOR-version milestone: completing Phase N ships v(N+1).0.0. Phase 1 is complete and we are now in Phase 2. A phase is divided into lettered sub-phases.

PhaseGoalStatus
1Core Language: types, control flow, LLVM backend, ownership and borrow checking, generics, traits and dispatch, closures, enums and pattern matching, error handling, modules and prelude, string interpolationComplete
2Tensors and MLIR: first-class tensor types lowered through MLIR Linalg, plus the pool allocator. Finishing it ships v3.0.0In progress
2AStandard I/O and spec stragglers: print / println, .is_nan(), codepoint string APIs, .enumerate(), borrowed slices &[T], the iterator protocol, @derive(Debug, PartialEq)Complete
2BTensor core: Tensor<T, [...]>, literal coercion, move semantics, DLPack, slicing, shape generics, named dims, dynamic shapes, reductionsIn progress
2CMLIR lowering: tensor arithmetic to Linalg, broadcasting, matmul behind @, end-to-end HIR → MLIR → LLVMPlanned
2DPool allocator: pool blocks, PoolAware, LIFO release at scope exitPlanned
2EFunctional sugar: pipeline |>, composition >>, einstein notation, functional tensor opsPlanned
3Automatic differentiation: Enzyme MLIR pass, @grad(wrt: ...), .backward() / .zero_grad(), higher-order derivatives, SGDPlanned
4GPU acceleration: MLIR GPU dialects (nvgpu / rocdl / Triton), @gpu, KernelOut<T> aliasing model, device memory pool, CPU fallbackPlanned
5Neural network standard library: TrainableTensor, ParameterList, optimizers, @model, Dense / Conv2d / Attention, .nrm serializationPlanned
6Async runtime: async func, Future<T>, spawn, JoinHandle, join / race, executor for data-loader / I/O overlapPlanned
7Interop and advanced features: Python FFI via DLPack, spread operator, advanced pattern matching, custom attributes, deferPlanned
8Developer experience: Language Server Protocol, diagnostics polish, formatter, @test runnerPlanned
9Package manager and distribution: neurpm, cross-OS installer / uninstaller / self-updater, signed release binaries, optimization passes (loop unrolling, AD-aware inlining, LTO)Planned

Development

Set LLVM_SYS_201_PREFIX for your platform before running any Cargo command (see Installation for the correct path per OS).

# Build the full workspace
cargo build --workspace

# Run all tests
cargo test --workspace

# Lint
cargo clippy --workspace --all-targets -- -D warnings

# Format check
cargo fmt --all -- --check

# Apply formatting
cargo fmt --all

On Windows, use PowerShell or a Developer Command Prompt. The env var must be set in the current session; prefix it inline if needed:

$env:LLVM_SYS_201_PREFIX = "C:\LLVM"
cargo build --workspace

VSCode Extension

Syntax highlighting for .nr files is included in neuro-language-support/.

cd neuro-language-support
npm install -g @vscode/vsce      # once
vsce package                     # -> neuro-language-support-<version>.vsix
code --install-extension neuro-language-support-*.vsix --force

Reload the VS Code window afterwards (Developer: Reload Window). A grammar change does not apply to already-open editors. During grammar work, symlinking the folder into ~/.vscode/extensions/ avoids repackaging: a window reload then picks up every edit.


File Extensions

ExtensionPurpose
.nrNeuro source files
.nrlCompiled library modules
.nrmSerialized model/matrix data
.nrpPackage definitions

Contributing

See CONTRIBUTING.md for architecture guidelines, coding standards, and the pull request process. Confirmed open defects live in docs/BUGS.md. Fixing one is the best way to start.

The project is in early alpha, so breaking changes are expected. Contributions should focus on Phase 2 (Tensors and MLIR); the Quick Roadmap marks which phase is currently open.


Why Neuro?

AI development is stuck in a fragmented paradigm: developers iterate in an interpreted glue language (Python), while underlying libraries are written in unmanaged, safety-critical systems languages (C++/CUDA).

Neuro is built to unify this stack:

  1. True native performance. Compiled AOT via LLVM 20, with no heavy runtime interpreter and no global interpreter lock (GIL). Measured against C++ and Python on compute-bound programs.
  2. AI-First Type System: Native compile-time shape verification for tensors using MLIR (Phase 2), preventing runtime dimension mismatches before a single line of training executes.
  3. Immutability by Default: A modern val/mut paradigm to ensure highly parallelized tensor computations are thread-safe by design.

License

Licensed under the Neuro Shared Source License v2.1.

Why not MIT/Apache 2.0 right now? Neuro is in a critical pre-stabilization phase. The license protects against three specific risks: commercial re-packaging of the compiler before the language spec is stable, AI-assisted reproduction of the compiler for a competing product, and misleading forks that fragment the early ecosystem. None of these restrictions affect normal use.

What you can do freely:

  • Use, study, and modify the compiler for any personal or internal purpose
  • Write Neuro programs and distribute or sell the compiled output under any terms you choose. programs you compile are wholly exempt from this license
  • Build tools, plugins, and editor integrations that call into the compiler
  • Contribute code back to the project

What requires a commercial license:

  • Redistributing the Neuro compiler itself (or a fork of it) as part of a commercial product

See LICENSE for full terms.

Acknowledgments

Inspired by Rust (ownership, type system), Python (AI ecosystem simplicity), Swift (language ergonomics), and Mojo (AI-first design). Built with inkwell, logos, and the LLVM infrastructure.

Contributors

PanzerPeter

221 commits

Languages

Rust

98.1%