Exercise 1: Double Negation, -(-(5)) — Possible Solution ==================================================================== THE TREE ------------------------------ tree = Unary('-', Grouping(Unary('-', Literal(5.0, line=1), line=1), line=1), line=1) chunk = Compiler().compile(tree) result = run_vm(chunk) RESULT ------------------------------ -(-(5)) -> 5.0 (correct -- two negations cancel out) WHY THIS WORKS AS AN ANSWER ------------------------------ Trace visit_unary's own compile order for the OUTER Unary node: def visit_unary(self, node): self.visit(node.right) # compiles the INNER expression first self.chunk.write(OP_NEGATE, node.line) # THEN emits this node's own negate The outer Unary's `right` is the Grouping, which (per this chapter's own finding) emits nothing itself and just visits ITS OWN inner expression -- the inner Unary. That inner Unary, in turn, compiles its own `right` (Literal(5.0)) first, THEN emits its own OP_NEGATE. So the final instruction order is: push 5.0, negate (stack: -5.0), negate again (stack: 5.0), return. Two OP_NEGATE instructions run back to back on the VM's own stack -- the first flips 5.0 to -5.0, the second flips -5.0 back to 5.0. This isn't a special case the compiler had to be taught -- it falls straight out of "compile the child, then emit this node's own instruction," applied recursively, exactly the same way it was applied to every other test in this chapter. Two negations landing back-to-back in the instruction stream is just what "negate a negated value" mechanically looks like once translated into a sequence of stack operations -- nothing about the compiler needed to know it was looking at a double negation specifically.