Control flow
while-loop

Add while. Save the position before the condition. Loop: re-parse the condition from the saved position each time, execute or skip the body.

    check(
        \\var s: i64 = 0; var i: i64 = 1;
        \\while (i < 6) {
        \\    s = s + i;
        \\    i = i + 1;
        \\}
        \\s
    , 15);
        if (streq(word, "while")) {
            const loop_pos: usize = pos;
            var last: i64 = 0;
            while (true) {
                pos = loop_pos;
                if (cur() == '(') { pos += 1; skip(); }
                const cond: i64 = expression();
                if (cur() == ')') { pos += 1; skip(); }
                if (cond == 0) { skip_block(); break; }
                last = exec_block();
                if (return_flag) { break; }
            }
            return last;
        }

Notice something weird: we're re-parsing the condition string from scratch every iteration. We jump pos back to loop_pos, re-read the characters, re-evaluate the expression. It works, but it means we're paying the parsing cost every loop iteration. This is one reason we'll eventually want a tree -- you parse once, then walk the tree as many times as you like.