Exercise 3: A Missing Closing Parenthesis — Possible Solution ==================================================================== RESULT ------------------------------ parse("(2 + 3") SyntaxError correctly raised: expected ')' after expression WHICH LINE CATCHES IT ------------------------------ def primary(self): ... if self.match('LPAREN'): expr = self.parse_expression() if not self.match('RPAREN'): raise SyntaxError("expected ')' after expression") # <-- here return ('group', expr) After consuming the opening '(' and successfully parsing the inner expression "2 + 3", the parser tries to consume a closing ')' via self.match('RPAREN'). Since the token stream has already reached EOF at that point (there is no ')' left in the input), match() returns None, the `if not self.match(...)` condition is true, and the explicit SyntaxError fires with a specific, useful message. WHY THIS WORKS AS AN ANSWER ------------------------------ This confirms the parser fails at the exact right moment - not earlier (it doesn't reject "(2 + 3" prematurely while there's still a valid expression to parse) and not later (it doesn't silently return a successfully-parsed group node missing its own closing paren, which would let a malformed program continue past error detection). This is the parser-level equivalent of Chapter 1's own unterminated-string check: both catch a genuinely unclosed construct at the specific point where "there's nothing left to consume, and something was still required" becomes knowable, rather than letting the problem surface confusingly somewhere else - or not at all.