A real parser
trace-precedence

Walk through the execution of expression() -> add_sub() -> term() -> factor() for the input "2 + 3 * 4". Write down each function call, which characters it consumes, and what it returns.

1. expression() -> add_sub()
2. add_sub() -> term() -> factor() -> number() reads 2, returns 2. term() sees + (not *), returns 2.
3. add_sub() sees +, advances. Calls term().
4. term() -> factor() -> number() reads 3, returns 3.
5. term() sees *, advances. Calls factor() -> number() reads 4, returns 4.
6. term() computes 3 * 4 = 12, returns 12.
7. add_sub() computes 2 + 12 = 14, returns 14.

The key insight: term() "grabs" the 3 * 4 before add_sub() can see it. That's how precedence works -- lower-precedence functions call higher-precedence ones, which consume their operands first.