Appendix B: Floating Point
float-literal-parsing

Add float literal parsing to the interpreter's factor(). When number() reads digits and hits a . followed by more digits, it's a float.

We store floats as their IEEE 754 bit pattern inside an i64, using Zig's @bitCast:

fn parse_float(whole: i64) i64 {
    var f: f64 = @floatFromInt(whole);
    pos += 1; // skip the '.'
    var divisor: f64 = 10.0;
    while (cur() >= '0' and cur() <= '9') {
        const digit: f64 = @floatFromInt(cur() - '0');
        f += digit / divisor;
        divisor *= 10.0;
        pos += 1;
    }
    skip();
    return @bitCast(f);
}

In factor(), after calling number(), check if the next character is .:

    // In the number/expression path:
    const val: i64 = number();
    if (cur() == '.' and pos + 1 < source.len and source[pos + 1] >= '0' and source[pos + 1] <= '9') {
        return parse_float(val);
    }

The returned i64 holds the bit pattern of a float. 3.14 returns the 64 bits of IEEE 754 3.14. To the variable storage system, it's just a number. The interpretation depends on the variable's declared type.