Version 1.2 - August 2026
Source files are UTF-8 encoded. Identifiers may contain Unicode characters following UAX #31 (XID_Start followed by XID_Continue*).
Whitespace characters (space, tab, newline, carriage return) separate tokens but are otherwise insignificant.
// Line comment (to end of line)
/* Block comment (may nest) */
/// Outer doc comment
//! Inner doc comment
Block comments nest: /* /* inner */ outer */ is valid.
fn let mut const static struct enum trait impl
if else match for while loop break continue return
true false self Self super
use mod pub
async await move unsafe extern
type where as in
effect handle perform resume
Integer literals:
42 // decimal (inferred integer type)
42i32 // explicitly i32
0xFF // hexadecimal
0o77 // octal
0b1010 // binary
1_000_000 // underscores for readability
Integer types: i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize.
Unsuffixed integer literals have type {integer} - a type variable constrained to resolve to a concrete integer type through inference.
Float literals:
3.14 // inferred float type
3.14f32 // explicitly f32
2.5e10 // scientific notation
Float types: f32, f64. Unsuffixed float literals have type {float}.
String literals:
"hello" // str (owned string)
b"bytes" // &'static [u8; 5]
'c' // char
b'A' // u8
r#"raw string"# // raw string (no escape processing)
Boolean literals: true, false.
Arithmetic: +, -, *, /, %
Comparison: ==, !=, <, >, <=, >=
Logical: &&, ||, !
Bitwise: &, |, ^, ~, <<, >>
Assignment: =, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
Reference: &, &mut, * (deref)
Other: ->, =>, .., ..=, ?, |> (pipe)
Delimiters: (, ), [, ], {, }, ,, ;, :, .
From lowest to highest binding power:
| Level | Operators | Associativity |
|---|---|---|
| 1 | =, +=, -=, etc. |
Right |
| 2 | .., ..= |
None |
| 3 | || |
Left |
| 4 | && |
Left |
| 5 | ==, !=, <, >, <=, >= |
Left |
| 6 | | |
Left |
| 7 | ^ |
Left |
| 8 | & |
Left |
| 9 | <<, >> |
Left |
| 10 | |> (pipe) |
Left |
| 11 | +, - |
Left |
| 12 | *, /, % |
Left |
| 13 | as (cast) |
Left |
| 14 | - (unary), !, ~, &, &mut, * |
Prefix |
| 15 | ., (), [], ? |
Postfix |
bool- boolean (true/false)char- Unicode scalar valuei8,i16,i32,i64,i128,isize- signed integersu8,u16,u32,u64,u128,usize- unsigned integersf32,f64- IEEE 754 floating pointstr- owned string()- unit type (zero-size)!- never type (no values)
(T, U, V)- tuple[T; N]- fixed-size array[T]- slice&T- shared reference&mut T- mutable reference*const T,*mut T- raw pointersfn(T) -> U- function pointer
Structs:
struct Point { x: f64, y: f64 } // named fields
struct Wrapper(i32) // tuple struct
struct Unit // unit struct
Enums:
enum Option<T> { Some(T), None }
enum Color { Red, Green, Blue }
Type aliases:
type Result<T> = std::result::Result<T, Error>;
fn identity<T>(x: T) -> T { x }
struct Pair<A, B> { first: A, second: B }
Generic type parameters are monomorphized at each call site. Each distinct instantiation generates a separate function.
fn print_it<T: Display>(x: T) { ... }
fn complex<T>(x: T) where T: Clone + Debug { ... }
BuildLang uses bidirectional type inference:
- Synthesis (bottom-up): the type of an expression is computed from its subexpressions
- Checking (top-down): an expected type flows down to constrain subexpressions
Unsuffixed integer literals resolve through inference: in let x: u64 = 42, the 42 is inferred as u64. Without context, integer literals default to i32 and float literals to f64.
Types may carry string annotations for domain-specific checking:
type LinearRGB = @[ColorSpace:Linear] Vec3;
The unifier checks annotation compatibility: if both operands carry annotations in the same category, they must match.
let r: &i32 = &x; // shared reference
let mr: &mut i32 = &mut x; // mutable reference
let v: i32 = *r; // dereference
- A value may have either one mutable reference or any number of shared references at any point in time (no aliasing).
- A mutable reference cannot be created while any other reference (shared or mutable) to the same value is active.
- References cannot outlive the value they refer to (no returning references to local variables).
Borrows expire at last use, not at scope end:
let r = &x;
println!("{}", *r); // last use of r
let mr = &mut x; // OK: r's borrow has expired
Borrows created in an inner scope expire when that scope ends:
{
let r = &mut x;
} // r's borrow expires here
let r2 = &x; // OK
fn name(param: Type, ...) -> ReturnType {
body
}
The last expression in a function body is the implicit return value.
struct Name {
field: Type,
}
impl Name {
fn method(&self) -> Type { ... }
fn method_mut(&mut self) { ... }
}
enum Name {
Variant,
Variant(Type),
Variant { field: Type },
}
Match expressions over enum types must be exhaustive:
match color {
Color::Red => ...,
Color::Green => ...,
Color::Blue => ..., // all variants covered
}
Missing variants produce a compile-time error.
trait Name {
fn method(&self) -> Type;
}
impl Name for ConcreteType {
fn method(&self) -> Type { ... }
}
mod name { // inline module
pub fn foo() {}
}
mod name; // external module (loads name.bld from disk)
use module::item; // import single item
use module::*; // glob import
use module::{a, b}; // nested import
if condition { ... } else { ... }
if let pattern = expr { ... }
match expr { pattern => expr, ... }
while condition { ... }
for pattern in iterator { ... }
loop { ... }
break;
break value;
continue;
return value;
let f = |x, y| x + y;
let g = |x: i32| -> i32 { x * 2 };
let captured = move || use_captured_var;
match value {
42 => ..., // literal
x => ..., // binding
_ => ..., // wildcard
(a, b) => ..., // tuple
Point { x, y } => ..., // struct
Some(v) => ..., // enum variant
A | B => ..., // or-pattern
x if x > 0 => ..., // guard
}
effect Error {
fn raise(msg: str) -> !;
}
fn might_fail() {
perform Error.raise("something went wrong");
}
handle Error {
might_fail()
} with {
raise(msg) => {
println!("caught: {}", msg);
resume(default_value)
}
}
Effects use one-shot continuations (setjmp/longjmp). resume may be called at most once per perform.
Constants: pi(), e(), tau()
Integer: i32_min, i32_max, i32_abs, i32_clamp
Float: f64_min, f64_max, f64_abs, f64_clamp
Predicates: is_even, is_odd, is_positive, is_negative, is_zero
Number theory: gcd, lcm, factorial, fibonacci, power
Angle conversion: deg_to_rad, rad_to_deg
Interpolation: lerp, smoothstep, inverse_lerp
I/O: println!, print!, eprintln!
Collections: vec_new, vec_push, vec_get, vec_len, vec_pop
HashMap: map_new, map_insert, map_get, map_contains, map_len
String: to_string_i32, to_string_f64
File I/O: read_file, write_file, file_exists
Environment: getenv, clock_ms, args_count, args_get
Math: sqrt, sin, cos, tan, log, exp, pow, abs, floor, ceil
Assert: assert, assert_eq
- C99 (primary): Portable, debuggable, uses system C compiler for native output
- HLSL: DirectX shaders and ReShade effects
- GLSL: OpenGL and Vulkan shaders
- SPIR-V: GPU compute and graphics (binary format)
- LLVM IR: Textual LLVM intermediate representation
- WebAssembly: Web and edge deployment (text format)
- x86-64: Experimental native machine code
- ARM64: Experimental native machine code
References compile to pointers. Structs compile to C structs. Enums compile to tagged unions. The runtime library (~190 C functions) is embedded in every output.
module = item* ;
item = function | struct_def | enum_def | trait_def | impl_def
| type_alias | const_def | static_def | mod_def | use_def ;
function = "fn" IDENT generics? "(" params? ")" ("->" type)? block ;
struct_def = "struct" IDENT generics? ( "{" fields "}" | "(" types ")" | ";" ) ;
enum_def = "enum" IDENT generics? "{" variants "}" ;
block = "{" stmt* expr? "}" ;
stmt = local | expr_stmt | item ;
local = "let" "mut"? pattern (":" type)? ("=" expr)? ";" ;
expr = literal | ident | path | unary | binary | call | method_call
| field | index | if | match | for | while | loop | block
| closure | ref | deref | return | break | continue ;
pattern = "_" | ident | literal | tuple | struct | enum | or | slice ;
type = path | "&" lifetime? "mut"? type | "[" type ";" expr "]"
| "(" types ")" | "fn" "(" types ")" "->" type ;
A conforming implementation must:
- Accept all programs that conform to this specification
- Reject programs that violate the borrowing rules (Section 3.2)
- Reject non-exhaustive match expressions over enum types (Section 4.3)
- Produce correct output for all built-in operations on primitive types
- Implement the precedence table in Section 1.6