The VM
vm-parse-int-name

Write a helper to parse a number from a string at a given offset:

fn parse_int(s: []const u8, from: usize) i64 {
    var i: usize = from;
    var neg: bool = false;
    if (i < s.len and s[i] == '-') {
        neg = true;
        i += 1;
    }
    var val: i64 = 0;
    while (i < s.len and s[i] >= '0' and s[i] <= '9') {
        val = val * 10 + digit(s[i]);
        i += 1;
    }
    return if (neg) -val else val;
}

And one to extract a name after $:

fn parse_name(s: []const u8, from: usize) []const u8 {
    var i: usize = from;
    // find the $
    while (i < s.len and s[i] != '$') {
        i += 1;
    }
    if (i < s.len) {
        i += 1; // skip $
    }
    const start: usize = i;
    while (i < s.len and s[i] != ' ' and s[i] != ')' and s[i] != '\n') {
        i += 1;
    }
    return s[start..i];
}

These are boring but necessary. The VM spends most of its time extracting numbers and names from text lines. In a real implementation, you'd use binary bytecode and skip all this parsing. We use text because we can read it and debug it.