Modules and Manifests
Sun uses a manifest-based module system where programs declare their dependencies in a manifest block. Multiple source files are merged into a single AST before compilation, enabling efficient cross-file optimization.
Manifests
Every Sun program has an entrypoint file containing a manifest block that declares all dependencies:
using sun;
function main() void {
println("Hello!");
}
manifest {
suns: ["utils.sun", "math.sun"] // Source files to compile together
moons: ["stdlib.moon"] // Precompiled libraries to link
}Manifest Syntax
manifest {
suns: ["file1.sun", "file2.sun"] // Optional: additional source files
moons: ["lib1.moon", "lib2.moon"] // Optional: precompiled libraries
protos: ["schemas/msgs.proto"] // Optional: protobuf schemas to import
}- suns — Source files (
.sun) to parse and merge with the entrypoint - moons — Precompiled library bundles (
.moon) to link - protos — Protobuf schemas (
.proto) compiled into message classes; see Protobuf
All fields are optional (entries are newline-separated; a trailing ; is
tolerated). A minimal program needs only a main() function.
Path Resolution
Paths in the manifest are resolved in order:
- Relative to the entrypoint file — First, check if the path exists relative to the directory containing the entrypoint
- Via SUN_PATH — If not found, search in the
SUN_PATHenvironment variable
export SUN_PATH=/path/to/sun/build
sun myprogram.sun # Can find stdlib.moon in SUN_PATHCompilation Pipeline
When you run sun main.sun:
- Parse entrypoint — Parse
main.sunand extract the manifest - Parse dependencies — Parse all files listed in
suns; synthesize a module of message classes for each schema inprotos - Merge ASTs — Combine all parsed ASTs into a single merged AST
- Load moons — Load type stubs and compiled code from
moons - Semantic analysis — Analyze the merged AST with moon type information
- Code generation — Generate LLVM IR for the merged AST
- Link — Link with moon compiled code
- Execute or output — JIT execute or emit native binary
Precompiled Libraries (.moon)
Moon files (.moon) are precompiled library bundles containing compiled code and type information. They enable fast compilation and reliable distribution.
Sun is designed to work with Moon, a safety-critical package manager (coming soon) that pins dependencies to the exact hash of the binary and compiler version. Every change is a breaking change — Moon prioritizes reproducibility over convenience.
Using Moon Files
Declare moon dependencies in your manifest:
using sun;
function main() i32 {
var allocator = make_heap_allocator();
var m = Matrix<i32>(allocator, [3, 3]);
m[0, 0] = 42;
return m[0, 0];
}
manifest {
moons: ["stdlib.moon"]
}Creating Moon Files
Create a moon from an entrypoint file with a manifest:
mylib.sun (entrypoint)
public module mylib {
public function helper() i32 { return 42; }
}
manifest {
suns: ["vec2.sun", "utils.sun"]
}sun --emit-moon -o mylib.moon mylib.sunThe compiler:
- Parses the entrypoint and all files listed in
suns - Merges them into a single AST
- Compiles and serializes the code and type information
- Packages everything into
mylib.moon
The Standard Library
Sun's standard library is built from stdlib/stdlib.sun:
manifest {
suns: [
"allocator.sun", "unique.sun", "errors.sun", "sys.sun", "option.sun",
"math.sun", "iterator.sun", "slice.sun", "contiguous_buffer.sun",
"vec.sun", "matrix.sun", "string.sun", "print.sun", "map.sun",
"linked_list.sun", "mutex.sun", "io.sun", "env.sun", "time.sun",
"process.sun", "networking.sun", "http.sun", "proto_wire.sun",
"json.sun", "bigint.sun"
]
}| Module | Contents |
|---|---|
allocator.sun | Memory allocators (HeapAllocator, IAllocator interface) |
bigint.sun | Arbitrary-precision BigUint |
env.sun | sun.env — environment variables, working directory, args |
errors.sun | Standard error types (EmptyError, NotFoundError, etc.) |
http.sun | HTTP client and server |
io.sun | sun.io — files, directories, Poller |
iterator.sun | Iteration protocol (IIterator<T, Container>, IIterable<T, Self>) |
json.sun | Json parsing and serialization |
linked_list.sun | Doubly-linked list LinkedList<T> |
map.sun | Generic hash map Map<K, V> |
matrix.sun | Matrix<T> and MatrixView<T> N-dimensional arrays |
mutex.sun | Mutex for thread synchronization |
networking.sun | TCP sockets (TcpStream, TcpListener) |
option.sun | Option<T> and Result<T, E> payload enums |
print.sun | Overloaded print/println/eprint/eprintln |
process.sun | sun.process — Command, signals, raw fork/exec |
proto_wire.sun | Protobuf wire format |
string.sun | String and StringView classes |
sys.sun | The stdlib's private extern "C" declarations |
time.sun | sun.time — Duration, Instant, DateTime |
unique.sun | Unique<T> smart pointer with automatic cleanup |
vec.sun | Generic growable array Vec<T> |
When building from source, the stdlib is automatically compiled:
cmake -B build && cmake --build build
# Creates build/stdlib.moonExample: Building a Custom Moon
Step 1: Create library source files
mylib/vec2.sun
class Vec2 {
var x: f64;
var y: f64;
function init(x_: f64, y_: f64) {
this.x = x_;
this.y = y_;
}
function add(other: ref Vec2) Vec2 {
return Vec2(this.x + other.x, this.y + other.y);
}
function length() f64 {
return _sqrt(this.x * this.x + this.y * this.y);
}
}mylib/utils.sun
function clamp(val: f64, min: f64, max: f64) f64 {
if (val < min) { return min; }
if (val > max) { return max; }
return val;
}Step 2: Create entrypoint with manifest
mylib/mylib.sun
manifest {
suns: ["vec2.sun", "utils.sun"]
}Step 3: Build the moon
sun --emit-moon -o mylib.moon mylib/mylib.sunStep 4: Use the library
function main() i32 {
var a = Vec2(3.0, 4.0);
var len = a.length(); // 5.0
var t = clamp(1.5, 0.0, 1.0); // 1.0
return 0;
}
manifest {
moons: ["mylib.moon"]
}Installing Moon Files
Per-project:
cp mylib.moon myproject/libs/manifest {
moons: ["libs/mylib.moon"]
}System-wide:
mkdir -p ~/.sun/lib
cp mylib.moon ~/.sun/lib/
export SUN_PATH=~/.sun/libmanifest {
moons: ["mylib.moon"] // Found via SUN_PATH
}Namespaces (module blocks)
Use module blocks to group related types and functions:
public module math {
public function square(x: i32) i32 {
return x * x;
}
public function cube(x: i32) i32 {
return x * x * x;
}
}Symbols inside a module are mangled with the module name prefix (e.g., math_square).
Using Statements
Use using to import all symbols from a module:
using sun; // Import all symbols from the sun module
function main() i32 {
var allocator = make_heap_allocator(); // No qualification needed
var v = Vec<i32>(allocator, 8);
v.push(42);
return v.get(0);
}
manifest {
moons: ["stdlib.moon"]
}Without using, you would need to reference symbols by their mangled names.
Module Scope
Inside a module, symbols can reference each other directly:
public module math {
public function square(x: i32) i32 {
return x * x;
}
public function sum_of_squares(a: i32, b: i32) i32 {
// Can call square() directly - same module
return square(a) + square(b);
}
}Visibility
Everything is private by default. public is the only modifier; there is
no private keyword — an item is private simply by not being marked public.
Privacy is scoped to the module: a private item is reachable from anywhere
inside the module that declares it, including nested (child) modules, and from
nowhere else. Files are not a boundary — every opening of module sun { ... }
across the stdlib's files is one module.
public module geometry {
// Private helper: callable anywhere inside `geometry` (and its children)
function square(x: f64) f64 { return x * x; }
public class Point {
var x: f64; // private field: `geometry` only
var y: f64;
public function init(x: f64, y: f64) { this.x = x; this.y = y; }
public function norm2() f64 { return square(this.x) + square(this.y); }
}
module internal { // private nested module
public function scratch() i32 { return 0; }
}
public module shapes { // public nested module
public function unit() Point { return Point(1.0, 0.0); }
}
}
using geometry;
function main() i32 {
var p = shapes.unit();
var n = p.norm2(); // OK: public method
// p.x // error: 'x' is private to class 'Point' in module 'geometry'
// square(2.0) // error: function 'square' is private to module 'geometry'
// internal.scratch() // error: module 'internal' is private to module 'geometry'
return 0;
}The modifier applies to every kind of module-level item — functions, classes,
interfaces, enums, globals, extern and declare declarations, and nested
modules — and to class and interface members. public module a.b { ... }
makes both a and b public. All openings of a module must agree on its
visibility. using M; never exposes M's private items.
Items declared outside any module belong to the program's root scope and are
reachable everywhere in that program (a single-file program needs no public
at all).
Visibility and .moon bundles
A bundle keeps every item — private ones included, since generic method
bodies shipped in the bundle may use them — but importers can only reach the
public API. --emit-moon requires each top-level module of the bundle to be
public; a bundle whose root module is private is rejected.