Peeper

Variables and conditions

let, mut, values set later, blocks, and if with && and ||.

Variables

let names a value, and mut lets it change. Without a type, a variable takes the type of its value.

let fixed = 5;                  // `i32`, from the value
let mut total: i64 = 0;

Changing a variable declared without mut is an error:

peeper check
[T0007]: cannot assign to immutable `count`  --> src/main.peep:3:5  | 1 | fn main() {2 |     let count = 1;  |         ----- declared without `mut` here3 |     count = 2;  |     ^^^^^  |   = help: write `let mut count`Compilation failed with 1 error(s)

A let without a value is set by its first assignment. It must be set once on every path before it is read:

let result: i32;                // set below, once on each path
if value > 0 {
    result = 1;
} else if value < 0 {
    result = -1;
} else {
    result = 0;
}

If one path can skip the assignment, the compiler shows that path:

peeper check
[T0039]: `value` is used before it is set  --> src/main.peep:6:12  | 1 | fn pick(flag: bool) -> i32 {2 |     let value: i32;  |         ----- declared here without a value...5 |     }6 |     return value;  |            ^^^^^ `value` is not set on every path to here  |   = help: set `value` on every path before this use, or give it a value where it is declaredCompilation failed with 1 error(s)

A block can declare names of its own, which hide outer names until the block ends. A parameter can be mut too.

examples/04_variables.peep
// Variables: `let` names a value and `mut` lets it change. A `let` without
// a value is set by its first assignment, once on every path before it is
// read. A block may declare names of its own.
// Run: peeper run examples/04_variables.peep
fn sign(value: i32) -> i32 {
    let result: i32;                // set below, once on each path
    if value > 0 {
        result = 1;
    } else if value < 0 {
        result = -1;
    } else {
        result = 0;
    }
    return result;
}

fn next(mut count: i32) -> i32 {    // a `mut` parameter
    count = count + 1;
    return count;
}

fn main() {
    let fixed = 5;                  // `i32`, from the value
    let mut total: i64 = 0;
    total = total + fixed;
    total = total * 2;
    {
        let fixed = 100;            // hides the outer `fixed` in this block
        total = total + fixed;
    }
    println("total:", total, "fixed:", fixed);
    println("signs:", sign(-8), sign(0), sign(3));
    println("next:", next(fixed));
}
Output
total: 110 fixed: 5
signs: -1 0 1
next: 6

Conditions

if, else if and else take a bool. && and || skip their right side when the left side already decides the answer, and ! negates.

examples/05_conditions.peep
// Conditions: `if`, `else if` and `else` take a `bool`. `&&` and `||` skip
// their right side when the left decides, and `!` negates.
// Run: peeper run examples/05_conditions.peep
fn describe(n: i32) {
    if n < 0 {
        println(n, "is negative");
    } else if n == 0 {
        println(n, "is zero");
    } else {
        println(n, "is positive");
    }
}

fn noisy(value: bool) -> bool {
    println("  noisy runs");
    return value;
}

fn main() {
    describe(-3);
    describe(0);
    describe(8);
    println("and:", false && noisy(true));  // `noisy` does not run
    println("or:", true || noisy(false));   // nor here
    println("both:", true && noisy(true));  // here it does
    println("not:", !(1 > 2));
}
Output
-3 is negative
0 is zero
8 is positive
and: false
or: true
  noisy runs
both: true
not: true

On this page