5: Source Files
Contents
5.1: .leaf Files — Structured Source
.leaf files are the standard source file. They may contain declarations only at the top level:
- Struct definitions
- Enum definitions
- Interface definitions
- Function definitions
- Type aliases
- Constant bindings (immutable, with compile-time constant expression initializers)
Leaf has no const keyword. Module-level constants are written as regular immutable bindings: PI = 3.14159. The term "constant" refers to immutable bindings initialized with compile-time constant expressions.
Top-level executable statements — function calls, mutable bindings, control flow, expressions with side effects — are not allowed in .leaf files. All executable code must live inside function bodies.
# valid.leaf — declarations only at top level
PI = 3.14159
MAX_SIZE = 100
struct Point
pub x: int
pub y: int
end
fn greet(name: str)
println("Hello {name}")
end
fn main()
greet("world")
println("PI is {PI}")
end
Constant initializers must be constant expressions — expressions that can be fully evaluated at compile time without any runtime context. A constant expression is one of:
- A primitive literal: integer (
42,0xFF,100u,42b), float (3.14159), string ("hello"), or boolean (true,false) - A unary
-applied to a numeric constant expression - A binary
+,-,*, or/applied to two numeric constant expressions of the same type
Constants cannot be initialized with:
- Function calls (
NOW = getTimestamp()is invalid) - References to other constants (
DOUBLE_MAX = MAX_SIZE * 2is invalid) - Struct, list, map, or tuple literals
- Enum variant constructors
- Any other operator or expression form
This allows common patterns like negative sentinels and simple scale factors:
TIMEOUT = -1
NEG_INFINITY_APPROX = -3.14159 * 1000.0
HALF_CAPACITY = 512 / 2
Constants are always immutable — there is no mut form for top-level bindings.
The file src/main.leaf may define a fn main() function. This is the program's entry point. Only src/main.leaf may define fn main() — declaring it in any other file is a compile error. The main function must take no parameters and return unit (()): fn main() is the only valid signature. Programs that need command-line arguments should use library functions; programs that need top-level error handling should use panic() or handle errors explicitly within main. Files without main are libraries: they export declarations for other files to import.