Gallium is expression-based, meaning that almost every construct in the language is an expression and can be used as such.

Blocks

Blocks are created with {} and implicitly evaluate to the last expression that was evaluated inside of them.

let a = {
    print("Hello!")

    1 + 1
}

// a == 2

Control Flow

if

If takes two forms, if-then and if-else.

let a = if condition then a else b  

The result of an if-else expression can only be used if there is an else clause.

let a = if thing {
    b
} else {
    c
}

Loops

loop

An infinite loop. These are able to evaluate to something when break is evaluated.

let a = loop {
    if condition() {
        break 5
    }
}

// a = 5

while

A loop that goes until a condition is not true.

while !thing {
    use(&mut thing)
}

for

A for loop that goes from x to y. It can go backwards or forwards.

for i := 0 to 10 {
    // evaluated for [0, 9]
}
for i := 10 downto 0 {
    // evaluated for [10, 1]
}

Variables

Variables can be created with mut or let.

let is immutable, mut is mutable.

mut x = 5
x := 6 // fine

let y = 5
y := 6 // compiler error

Assertions

Assertions can be done with the assert statement, which holds a boolean condition and an optional message.

If the condition is false, the program aborts instantly.

assert 1 == 1, "did math break?"
assert true and not false

Literals

nil

nil can be used as a literal for a pointer of any type.

let x: *mut byte = nil

Numbers

Any number can be a literal for a numeric type, by default they will coerce to any type they fit in. If not coerced, they become i64.

let x = 5 // x: i64

Floating-point literals need a . somewhere. They are f64.

let x = 3.141592

Characters & Strings

Character literals must fit inside a single byte.

let c = '!'

String literals become a magic compiler-generated [char] that point to a magic compiler-generated string that is always accessible for the lifetime of the program.

let s = "Hello!"

Arrays

Arrays can be created with [] as expected.

let arr = [1, 2, 3, 4, 5]

Slices

Slices can be created through range notation (explained in #1) or from raw storage + a length.

let raw: *mut i64 = malloc(512 * sizeof i64) as! *mut i64
let slice: [mut i64] = [raw len 512]

Structures

Structures can be initialized with literals.

struct Point2D {
    x: f64
    y: f64
}

let origin = Point2D { x: 0.0, y: 0.0 }