The semantics of the
if/
while statement's logical test are not well
specified in the book, and the implementation is inconsistent in JackCompiler (v2.5).
I discovered that my compiler and JackCompiler were not generating equivalent
code for while statements when I compiled my Memory.jack with
JackCompiler and it failed. (I developed it using my compiler.)
The problem was my linked list traversal:
let block = freeList;
while (block) {
...
let block = block[0];
}
I've written this same code so many times, in so many languages, that it was
interesting that it failed. I did some investigating.
What's happening is that JackCompiler generates:
push block
not
if-goto break-label
If
block is a non-null pointer one expects the
while body to execute,
but it does not because
not is bitwise, not logical. For "
if
(test)", JackCompiler generates:
push test
if-goto true-label
goto false-label
label true-label
Which works as expected.
So my question is: What are the correct semantics for if/while
statements in Jack?
- 8.2.1 specifies for VM command if-goto: "if the value is not
zero" which implies the the Jack if/while should behave likewise,
- figure 9.8 specifies "Typical if statement" and
"Typical while statement",
- figure 11.5 shows emitted code for if and while as "VM
code for computing ~(cond)"
[Alas, I've been in the industry long enough to know that "The source code/tools
are the documentation." — I've rewritten my loop as
"
while (~(block=0))" so that it will work with JackCompiler.]
None of the test programs turn up this subtlety. Here's one that will:
/** Test if/while statements with integer conditions. */
class Main {
function void main() {
var int test;
var boolean fail;
let test = 1;
if (test) {
do Output.printString("if(1) tested True"); }
else {
do Output.printString("ERROR: if(1) tested False"); }
do Output.println();
let fail = true;
let test = 1;
while (test) {
do Output.printString("while(1) tested True");
let fail = false;
let test = 0;
}
if (fail) {
do Output.printString("ERROR: while(1) tested False"); }
return;
}
}
--Mark