A complete compiler and toolchain for Nova, a statically-typed programming language built from scratch in C++17. Features a full compilation pipeline with three execution backends, generics with monomorphization, traits with bounded generics, a step-through debugger, an LSP server for editor integration, a mark-and-sweep garbage collector, a module system, and rich error diagnostics.
- Static typing with type inference (
int,float,string,bool, arrays, structs, enums) - Generics with monomorphization (
fn identity<T>(x: T) -> T) - Traits with bounded generics (
trait Printable,impl Printable for Point,<T: Printable>) - Enums with variant access (
enum Direction { North, South },Direction::North) - Structs with methods via
implblocks - First-class functions and closures
- Pattern matching with
matchexpressions and block arms - Module system with
import/export - Mark-and-sweep garbage collector for heap objects
- Multi-pass optimizer (constant folding, dead code elimination, common subexpression elimination)
- Rich error diagnostics with source context and "did you mean?" suggestions
| Backend | Description | Use Case |
|---|---|---|
| VM | Stack-based bytecode interpreter | Default, portable, easy to debug |
| JIT | Bytecode → x86-64 machine code at runtime | Maximum performance |
| LLVM IR | Generates LLVM IR text output | Integration with LLVM toolchain |
| Tool | Description |
|---|---|
| Step-through Debugger | Breakpoints, step over/into/out, variable inspection — integrated into the WASM playground |
| LSP Server | Diagnostics, go-to-definition, hover, completion, document symbols for VS Code |
| WASM Playground | Browser-based editor with live error checking and all execution backends |
| REPL | Interactive read-eval-print loop |
make # Build everything
./bin/nova examples/01_syntax_and_types.nova # Run with VM
./bin/nova --jit examples/09_jit_benchmark.nova # Run with JIT
./bin/nova --target=llvm examples/01_syntax_and_types.nova # Generate LLVM IR
./bin/nova-repl # Interactive REPL
make test # Run 196 testslet name: string = "Nova";
let age = 25; // type inferred as int
const PI = 3.14159; // immutable
let active = true;
fn factorial(n: int) -> int {
if (n <= 1) { return 1; }
return n * factorial(n - 1);
}
print(factorial(10)); // 3628800
fn identity<T>(x: T) -> T {
return x;
}
let a = identity<int>(42);
let b = identity<string>("hello");
fn first<T, U>(x: T, y: U) -> T {
return x;
}
trait Describable {
fn describe(self) -> string;
}
struct Cat { name: string, age: int }
struct Dog { name: string, breed: string }
impl Describable for Cat {
fn describe(self) -> string { return self.name; }
}
impl Describable for Dog {
fn describe(self) -> string { return self.name; }
}
fn show<T: Describable>(item: T) -> string {
return item.describe();
}
let cat = Cat { name: "Whiskers", age: 3 };
print(show<Cat>(cat)); // Whiskers
struct Point { x: int, y: int }
impl Point {
fn manhattan(self, other: Point) -> int {
return abs(self.x - other.x) + abs(self.y - other.y);
}
}
let p = Point { x: 3, y: 4 };
let q = Point { x: 0, y: 0 };
print(p.manhattan(q)); // 7
enum Direction { North, South, East, West }
fn describe(d: Direction) -> string {
let result = match d {
Direction::North => { "Going north" },
Direction::South => { "Going south" },
_ => { "Other direction" }
};
return result;
}
print(describe(Direction::North)); // Going north
let nums = [10, 20, 30];
nums.push(40);
print(nums.length); // 4
print(nums[2]); // 30
fn make_adder(n: int) -> fn(int) -> int {
return |x: int| -> int { return n + x; };
}
let add5 = make_adder(5);
print(add5(10)); // 15
// lib/math.nova
export fn square(x: int) -> int { return x * x; }
// main.nova
import math;
print(square(5)); // 25
The compiler produces Rust-style error messages with source context:
error[E200]: undefined variable 'nme'
--> examples/test.nova:5:12
|
5 | let y = nme + 1;
| ^^^ undefined variable 'nme'
= help: did you mean 'name'?
The JIT compiler translates bytecode to native x86-64 machine code, achieving orders-of-magnitude speedup over the VM interpreter:
# VM: ~80 seconds
time ./bin/nova examples/9_jit_vs_vm_benchmark.nova
# JIT: ~0.01 seconds
time ./bin/nova --jit examples/9_jit_vs_vm_benchmark.novaBoth produce the same correct result: 200000010000000.
Try Nova in the browser with the interactive playground:
make wasm # Build WASM (requires Emscripten)
python3 -m http.server 8080 --directory playground # Serve locallyFeatures:
- Monaco editor with Nova syntax highlighting and autocomplete
- Live error checking as you type
- Run, AST view, bytecode disassembly, and LLVM IR output tabs
- Step-through debugger with breakpoints, variable inspection, and call stack
- Example programs dropdown
Full language server for VS Code with diagnostics, go-to-definition, hover, completion, and document symbols.
make lsp # Build the LSP serverA VS Code extension is included in editors/vscode/. It provides:
- Syntax highlighting for
.novafiles - Language server integration (auto-starts
nova-lsp) - Bracket matching and auto-closing pairs
NovaCompiler/
include/ C++ headers (AST, Bytecode, Type, GC, VM, JIT, ...)
src/ C++ implementations
tests/ Test suite (196 tests, 391 assertions)
examples/ Example Nova programs
lib/ Standard library modules (math, strings, collections)
playground/ WASM-powered browser playground
editors/vscode/ VS Code extension with LSP support
docs/ Documentation (BYTECODE.md, TYPE_SYSTEM.md)
.github/workflows/ CI configuration
Source (.nova) → Lexer → Parser → Semantic Analyzer → Optimizer → CodeGen → VM/JIT/LLVM
Each stage is implemented from scratch — no parser generators, no external dependencies.
| Stage | Description |
|---|---|
| Lexer | Tokenizes source into a stream of tokens with position tracking |
| Parser | Recursive descent parser, builds AST with operator precedence climbing |
| Semantic Analyzer | Type checking, scope resolution, generic monomorphization, trait validation, module imports |
| Optimizer | Multi-pass: constant folding, algebraic simplification, dead code elimination |
| CodeGen | Emits bytecode (64 opcodes) or LLVM IR |
| VM | Stack-based interpreter with GC-managed heap and debug execution mode |
| JIT | x86-64 native code generation via mmap + PROT_EXEC |
See ARCHITECTURE.md for the full architecture document.
Nova ships with built-in functions and importable standard library modules:
Built-in functions: print, abs, max, min, parseInt, toString, toUpper, toLower
Standard library modules (in lib/):
math—square,cube,is_evenstrings—repeat,starts_with,is_empty,char_atcollections—range,sum,contains
make # Build all binaries (nova, nova-repl, nova-bench, nova-jit)
make test # Run the test suite
make lsp # Build the LSP server
make wasm # Build WASM playground (requires Emscripten)
make all_examples # Run all example programs
make clean # Remove build artifacts
make help # Show all targetsRequires g++ with C++17 support and make. WASM build requires Emscripten.
- ARCHITECTURE.md — Full compiler architecture overview
- docs/BYTECODE.md — Complete opcode reference (64 opcodes with stack effects)
- docs/TYPE_SYSTEM.md — Type system, inference rules, generics