Skip whitespace. Add two helpers: isSpace tells us whether a byte is whitespace, and skipSpaces advances past a run of them.
Treat any of these four bytes as whitespace: space ' ', tab '\t', newline '\n', carriage return '\r'.
Strategy: the cursor should always sit on a non-whitespace character when we're about to read something. Call skipSpaces(source, &pos) once at the start of eval, and after every consume so the next reader doesn't have to think about spaces.
testCase(" 3 + 5 ", 8);
testCase("100 + 23", 123);
fn isSpace(c: u8) bool {
return c == ' ' or c == '\n' or c == '\t' or c == '\r';
}
fn skipSpaces(
source: []const u8,
pos: *usize,
) void {
var current_char: u8 = curChar(source, pos.*);
while (isSpace(current_char)) {
pos.* += 1;
current_char = curChar(source, pos.*);
}
}
curChar handles end-of-input -- past source.len it returns 0, and isSpace(0) is false, so the loop stops on its own even if the input ends with whitespace.
Three places to add skipSpaces(source, &pos);:
1. At the start of eval, before parsing begins.
2. At the end of readNumber, before returning.
3. After the pos += 1 that consumes the operator character. Easy to forget -- the operator is just one byte, so there's no readOperator helper that would skip for us.
Updated readNumber:
fn readNumber(
source: []const u8,
pos: *usize,
) i64 {
var value: i64 = 0;
var current_char: u8 = curChar(source, pos.*);
while (isDigit(current_char)) {
value = value * 10 + digitValue(current_char);
pos.* += 1;
current_char = curChar(source, pos.*);
}
skipSpaces(source, pos);
return value;
}
Updated eval:
fn eval(source: []const u8) i64 {
var pos: usize = 0;
skipSpaces(source, &pos);
var value: i64 = readNumber(source, &pos);
var current_char: u8 = curChar(source, pos);
while (current_char != 0) {
const op: u8 = current_char;
pos += 1;
skipSpaces(source, &pos);
const right: i64 = readNumber(source, &pos);
value = switch (op) {
'+' => value + right,
'-' => value - right,
'*' => value * right,
'/' => @divTrunc(value, right),
else => value,
};
current_char = curChar(source, pos);
}
return value;
}