Walk through the execution of parseExpression -> parseAddSub -> parseTerm -> parseFactor for the input "2 + 3 * 4". Write down each function call, which characters it consumes, and what it returns.
1. parseExpression -> parseAddSub
2. parseAddSub -> parseTerm -> parseFactor -> readNumber reads 2, returns 2. parseTerm sees + (not *), returns 2.
3. parseAddSub sees +, advances. Calls parseTerm.
4. parseTerm -> parseFactor -> readNumber reads 3, returns 3.
5. parseTerm sees *, advances. Calls parseFactor -> readNumber reads 4, returns 4.
6. parseTerm computes 3 * 4 = 12, returns 12.
7. parseAddSub computes 2 + 12 = 14, returns 14.
8. parseExpression has left = 14, sees end-of-input (no comparison operator), returns 14.
The key insight: parseTerm "grabs" the 3 * 4 before parseAddSub can see it. That's how precedence works -- lower-precedence functions call higher-precedence ones, which consume their operands first.