Variables
is-letter-read-name

Write is_letter(c) and read_name(). A name is letters, digits, and underscores. read_name returns a slice into the source string.

fn is_letter(c: u8) bool {
    return (c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z') or c == '_';
}

fn read_name() []const u8 {
    const start: usize = pos;
    while (pos < source.len and (is_letter(source[pos]) or
        (source[pos] >= '0' and source[pos] <= '9')))
    {
        pos += 1;
    }
    const name: []const u8 = source[start..pos];
    skip();
    return name;
}

source[start..pos] is a slice -- a view into the string. No copying, no allocation. Slices are one of Zig's best features, and we'll use them constantly.