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 the expression() 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:
- expression -- loops on +/-, asks for terms between them.
- term -- loops on *//, asks for factors between them.
- factor -- a number, or a parenthesized expression. (This is what we've been calling numberOrParens -- now rename it to factor. The grammar vocabulary clicks.)
For this step:
1. Rename numberOrParens() to factor(). Update the call sites in expression().
2. Introduce term() as a passthrough -- it just returns factor(), no loop yet.
3. Have expression() call term() instead of factor() in its operand reads.
fn factor() i64 {
if (cur() == '(') {
pos += 1; skip();
const val: i64 = expression();
if (cur() == ')') { pos += 1; skip(); }
return val;
}
return number();
}
fn term() i64 {
return factor();
}
fn expression() i64 {
var val: i64 = term();
while (cur() != 0 and cur() != ')') {
const op: u8 = cur();
pos += 1; skip();
const right: i64 = term();
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 term() in the next page.