Your problem here is that the expression does not end with a ';'. It ends with a ')'.
while ( ~(key = 0) ) {
while body
}
Instead of the while loop breaking on ';' it needs break if the current token (after calling compileTerm) is
not an operator.
Since expression always starts with a term and is followed by 0 or more (operator term) pairs, I structure this loop as
compileTerm()
while tokenizer.token is an operator {
handle operator
compileTerm()
}
When compiling while (~(key = 0)) the while loop in the above code does not run because ~(key = 0) is a term and is handled by the compileTerm() call before the while loop.
(compileTerm does recursively call compileExpression, and that inner execution does enter the while loop while handling key = 0.)
FWIW, in tokenizing code I write comments that include the syntax that I'm compiling and where the tokenizer is expected to be on entry and exit. It helps me remember what's supposed to be happening when I'm writing the code. (If my coding wasn't always bug free, they might help in debugging it too 8^)
def _CompileExpression(self):
"""
Compiles <expression> :=
<term> (<op> <term>)*
ENTRY: Tokenizer positioned on the expression.
EXIT: Tokenizer positioned after the expression.
"""
self._WriteXmlTag('<expression>\n')
--Mark