The parser is at 2+3*4. It just consumed the 2 and the +. It's sitting on the 3. Why can't it just add 2 + 3 right now?
Because we're in a sum, and the right operand of a sum isn't a single value -- it's a term. A term is one or more factors multiplied (or divided) together. A factor is a number, or something inside parentheses (the numberOrParens we already wrote). Even a lone 3 is a term -- a term made of one factor.
So when parseExpression reads its right operand, it should ask for a term, not just a factor. A term function reads factors and grinds through any *// chain it finds, then hands back one number. The + only happens after that.
The structure we're heading toward:
- parseExpression -- loops on +/-, asks for terms between them.
- parseTerm -- loops on *//, asks for factors between them.
- parseFactor -- a number, or a parenthesized expression. (This is what we've been calling numberOrParens -- now rename it to parseFactor. The grammar vocabulary clicks.)
For this step:
1. Rename numberOrParens to parseFactor. Update the call sites.
2. Introduce parseTerm as a passthrough -- it just returns parseFactor, no loop yet.
3. Have parseExpression call parseTerm instead of parseFactor in its operand reads.
fn parseFactor(source: [*]const u8, pos: *usize) i64 {
if (source[pos.*] == '(') {
pos.* += 1;
skipSpaces(source, pos);
const val: i64 = parseExpression(source, pos);
if (source[pos.*] == ')') {
pos.* += 1;
skipSpaces(source, pos);
}
return val;
}
return readNumber(source, pos);
}
fn parseTerm(source: [*]const u8, pos: *usize) i64 {
return parseFactor(source, pos);
}
fn parseExpression(source: [*]const u8, pos: *usize) i64 {
var val: i64 = parseTerm(source, pos);
while (source[pos.*] != 0 and source[pos.*] != ')') {
const op: u8 = source[pos.*];
pos.* += 1;
skipSpaces(source, pos);
const right: i64 = parseTerm(source, pos);
val = switch (op) {
'+' => val + right,
'-' => val - right,
'*' => val * right,
'/' => @divTrunc(val, right),
else => val,
};
}
return val;
}
The bug isn't fixed yet. Stare at this for one minute and guess what you will put in parseTerm on the next page.