Exercise 1: A Type-Checked Modulo Operator — Possible Solution ==================================================================== LEXER CHANGE ------------------------------ TOKEN_RE = re.compile( r'\s*(?:(?P\d+\.\d+|\d+)|"(?P[^"]*)"' r'|(?P==|!=|<=|>=|[+\-*/%()<>!;])' # <- '%' added here r'|(?P[A-Za-z_][A-Za-z0-9_]*)|(?P\S))' ) PARSER CHANGE — same precedence level as * and / ------------------------------ def factor(self): expr = self.unary() while self.match('*', '/', '%'): op = self.tokens[self.pos - 1][0] right = self.unary() expr = Binary(expr, op, right) return expr INTERPRETER CHANGE — same type-checking shape as -, *, / ------------------------------ def visit_binary(self, node): if node.operator == '%': left = node.left.accept(self) right = node.right.accept(self) if not (isinstance(left, float) and isinstance(right, float)): raise WispRuntimeError( "operands of '%' must be numbers, got " + type(left).__name__ + " and " + type(right).__name__ ) return float(left % right) # ...existing +, -, *, /, comparisons, equality unchanged RESULTS ------------------------------ 'print 10 % 3;' -> 1 '"x" % 2;' -> WispRuntimeError: operands of '%' must be numbers, got str and float WHY THIS WORKS AS AN ANSWER ------------------------------ Modulo belongs at the exact same precedence tier as multiplication and division -- all three are equally "tight" and left-associative, so adding it to factor()'s own while-loop condition (rather than creating a new precedence level, the way Chapter 2's own exercise 1 did for exponentiation) is the correct structural choice. The type check follows the identical pattern already used for '-', '*', and '/' in this chapter's own visit_binary -- new operators don't get a pass on the "check types before trusting Python's own operator" discipline just because they're new; if anything, a newly-added operator is exactly where it's easiest to forget the check, since there's no existing branch to copy the pattern from by habit.