Now make parseTerm do its job.
Move the * and / handling out of parseExpression into parseTerm. parseExpression loops only on +/-. parseTerm loops only on *// and asks parseFactor for each operand.
When parseExpression calls parseTerm for its right operand on 2+3*4, parseTerm sees 3, then *4, and returns 12. Then parseExpression adds: 2+12 = 14.
testCase("2+3*4", 14);
testCase("3*4+2", 14);
testCase("2*3+4*5", 26);
testCase("(2+3)*4", 20);
testCase("100 - 55 * 2", -10);
fn parseTerm(source: [*]const u8, pos: *usize) i64 {
var val: i64 = parseFactor(source, pos);
while (source[pos.*] == '*' or source[pos.*] == '/') {
const op: u8 = source[pos.*];
pos.* += 1;
skipSpaces(source, pos);
const right: i64 = parseFactor(source, pos);
val = switch (op) {
'*' => val * right,
'/' => @divTrunc(val, right),
else => val,
};
}
return val;
}
fn parseExpression(source: [*]const u8, pos: *usize) i64 {
var val: i64 = parseTerm(source, pos);
while (source[pos.*] == '+' or 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,
else => val,
};
}
return val;
}
Three functions, three precedence layers: parseExpression -> parseTerm -> parseFactor. Each layer loops on its own operators and asks the next-tighter layer for operands. Operands come back fully resolved. Recursion does the rest.
This is recursive descent. To add another precedence level (say, ^ for power between term and factor), you insert one more function in the chain. Comparison operators come next; they slot in as a layer above expression.