M++ Language Reference
Complete documentation for M++ — a statically-typed, LLVM-compiled systems language with no external runtime. From hello world to low-level memory management.
Getting Started
M++ source files use the .mpp extension. Every program needs a main function as its entry point.
Hello World
fn main() {
print("Hello, world!");
}Compiling & Running
The mpp CLI compiles source to native binaries via LLVM.
mpp build # build from mpp.toml mpp main.mpp # compile single file mpp build --run # build and run
Project Structure
For multi-file projects, create a mpp.toml in your project root:
name = "hello" version = "0.1.0" main = "src/main.mpp"
Then run mpp build to compile. The output binary is placed in the project root by default.
Variables & Constants
Variables are declared with let. They are immutable by default. Use let mut for mutable bindings.
let name = "R2ND"; // immutable, type inferred let mut counter = 0; // mutable let x: int = 42; // explicit type const MAX_BUFFER: int = 4096; // compile-time constant
Type Inference
The compiler infers types from the right-hand side. Explicit annotations are optional but can improve clarity.
let a = 3.14; // inferred as float let b = true; // inferred as bool let c = "hello"; // inferred as string let d: i64 = 100; // explicit sized integer
Constants
const values are evaluated at compile time. They must have an explicit type annotation and cannot be reassigned.
const PI: float = 3.14159265; const APP_NAME: string = "MyApp"; const MAX_RETRIES: int = 3;
const is a compile-time error.Primitive Types
M++ has a small set of built-in primitive types. Sized integer variants are available for low-level control.
| Type | Description | Example |
|---|---|---|
| int | Platform-sized signed integer (64-bit on x86_64) | 42 |
| float | 64-bit floating point (IEEE 754) | 3.14 |
| bool | Boolean true or false | true |
| string | UTF-8 string | "hello" |
| ptr | Raw pointer | null |
| void | No value / unit type | — |
Sized Integer Types
| Type | Bits | Range |
|---|---|---|
| u8 | 8 | 0 to 255 |
| i8 | 8 | −128 to 127 |
| u16 | 16 | 0 to 65,535 |
| i16 | 16 | −32,768 to 32,767 |
| u32 | 32 | 0 to 4,294,967,295 |
| i32 | 32 | −2³¹ to 2³¹−1 |
| u64 | 64 | 0 to 2⁶⁴−1 |
| i64 | 64 | −2⁶³ to 2⁶³−1 |
Hex & Binary Literals
let a: u8 = 255; let b: u32 = 0xFF; // hex literal let c: i64 = 0b10101010; // binary literal let d: u64 = 1_000_000; // underscore separators allowed
int and float for general-purpose code.Operators
Arithmetic
| Operator | Description | Example |
|---|---|---|
| + | Addition / string concatenation | a + b |
| - | Subtraction | a - b |
| * | Multiplication | a * b |
| / | Division | a / b |
| % | Modulo (remainder) | a % b |
Comparison
| Operator | Description |
|---|---|
| == | Equal to |
| != | Not equal to |
| < | Less than |
| > | Greater than |
| <= | Less than or equal |
| >= | Greater than or equal |
Logical
| Operator | Description | Example |
|---|---|---|
| && | Logical AND (short-circuits) | a && b |
| || | Logical OR (short-circuits) | a || b |
| ! | Logical NOT | !flag |
Bitwise
| Operator | Description |
|---|---|
| & | Bitwise AND |
| | | Bitwise OR |
| ^ | Bitwise XOR |
| ~ | Bitwise NOT |
| << | Left shift |
| >> | Right shift |
Assignment & Compound Assignment
| Operator | Equivalent |
|---|---|
| = | Assign |
| += | a = a + b |
| -= | a = a - b |
| *= | a = a * b |
| /= | a = a / b |
| %= | a = a % b |
| &= | a = a & b |
| |= | a = a | b |
| ^= | a = a ^ b |
| <<= | a = a << b |
| >>= | a = a >> b |
Other Operators
| Operator | Description | Example |
|---|---|---|
| as | Type cast | x as float |
| sizeof | Size of type in bytes | sizeof(int) |
| &x | Address-of (get pointer) | let p = &x; |
| *p | Dereference pointer | let val = *p; |
| .. | Range (exclusive end) | 0..10 |
let mut x = 10; x += 5; // x is now 15 let y = x as float; // cast to float: 15.0 let mask: u8 = 0b1111_0000; let low = mask & 0x0F; // bitwise AND: 0 let high = mask >> 4; // right shift: 15
Control Flow
If / Else
let score = 85;
if score >= 90 {
print("A");
} else if score >= 80 {
print("B");
} else {
print("C");
}While Loop
let mut i = 0;
while i < 10 {
print(to_string(i));
i += 1;
}For..in (Range)
Ranges use .. syntax with an exclusive upper bound.
for i in 0..5 {
print(to_string(i)); // prints 0, 1, 2, 3, 4
}For-each (Arrays)
let names = ["Alice", "Bob", "Charlie"];
for name in names {
print("Hello, " + name);
}For-each (Maps)
let ages = { "Alice": 30, "Bob": 25 };
for key, value in ages {
print(key + " is " + to_string(value));
}Break & Continue
for i in 0..100 {
if i % 2 == 0 {
continue; // skip even numbers
}
if i > 20 {
break; // stop at 20
}
print(to_string(i));
}Match / Pattern Matching
match expressions support literal patterns, enum destructuring, and a wildcard _ default case.
match status_code {
200 => { print("OK"); },
404 => { print("Not Found"); },
500 => { print("Server Error"); },
_ => { print("Unknown status"); }
}Pattern matching is especially powerful with enums (see the Enums section):
match shape {
Shape.Circle(r) => {
return 3.14159 * r * r;
},
Shape.Rect(w, h) => {
return w * h;
},
_ => { return 0.0; }
}Functions
Declaration
Functions are declared with fn. Return types come after ->.
fn add(a: int, b: int) -> int {
return a + b;
}
fn greet(name: string) {
print("Hello, " + name + "!");
}Return Types
Functions without an explicit return type return void. Multiple returns are handled through structs or tuples.
fn divide(a: float, b: float) -> Result[float] {
if b == 0.0 {
return err("division by zero");
}
return ok(a / b);
}First-Class Functions
Functions can be stored in variables, passed as arguments, and returned from other functions.
fn apply(f: fn(int) -> int, x: int) -> int {
return f(x);
}
fn double(n: int) -> int {
return n * 2;
}
let result = apply(double, 5); // 10Function Types
let op: fn(int, int) -> int = add; let result = op(3, 4); // 7
Methods (Receiver Syntax)
Methods are functions with a type prefix before the name. The first parameter self refers to the instance.
struct Counter {
value: int,
}
fn Counter.increment(self) {
self.value += 1;
}
fn Counter.get(self) -> int {
return self.value;
}
let mut c = Counter { value: 0 };
c.increment();
print(to_string(c.get())); // 1Strings
String Literals
let greeting = "Hello, world!"; let multiline = "Line one Line two Line three";
Escape Sequences
| Sequence | Meaning |
|---|---|
| Newline | |
| Tab | |
| Carriage return | |
| \ | Backslash |
| " | Double quote |