Temporary chapter name for all chapters
term-multiplies

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.