Step 1 of 3 · Reading · ~2 min
Getting Precedence Right
Parsing Expressions
Verifying Precedence End to End
With the full ladder wired up (equality → comparison → term → factor → unary → primary), this lesson is about proving it actually behaves correctly on the cases that matter, plus adding primary — the base case that includes literals, true/false/nil, and parenthesized groups.
primary: The Base Case
The key idea: a parenthesized expression calls back into expression() — the top of the precedence ladder — not into any single intermediate rule. That's what lets (1 + 2) * 3 work: inside the parens, the full ladder is available again, so + is legal there even though factor (which is parsing the outer * 3) wouldn't itself accept a bare +.
Associativity, Made Concrete
- Left-associative (all your binary operators):
1 + 2 + 3→(+ (+ 1.0 2.0) 3.0). Each new operator wraps the previous result on the left. - Verify this explicitly: if your parser instead produces
(+ 1.0 (+ 2.0 3.0)), the loop is building the tree backwards — a bug that won't matter for+(associative and commutative) but will silently corrupt results for-and/, where1 - 2 - 3and1 - (2 - 3)differ.
Precedence, Made Concrete
1 + 2 * 3→(+ 1.0 (* 2.0 3.0))— multiplication is nested inside the addition becauseterm's call tofactor()fully consumes2 * 3as one indivisible operand beforeterm's loop ever sees the+.-1 + 2→(+ (- 1.0) 2.0)— unary minus binds only to1, not to the whole1 + 2, becauseunaryis called beforeterm's loop starts, so it only ever sees onefactor-level operand at a time.
Syntax Errors: Reporting Without Crashing
When primary() finds a token that can't start any expression — a stray ), a dangling operator, end of input where a value was expected — that's a syntax error, and it should be reported in a specific, parseable format:
[line N] Error at '<token>': Expect expression.
Rather than letting an exception crash the whole program, catch it at the statement or top level, print the message, and — in a real compiler — enter panic-mode recovery: skip tokens until you reach a likely statement boundary (like a ; or a keyword such as class/fun/var) and resume parsing from there, so one typo doesn't hide every other error in the file. For this exercise, focus on producing the exact error message and location; the discipline of "report and recover" is the same one you saw in the scanner, and you'll lean on it again when statements are introduced.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…