/*
   Explanation of how we handle conditions and booleans:

   of course, C has no booleans, and all conditionals are expected to be
   lazy evaluated, ie if we have "a || b", then if a is true, b is never
   evaluated.  Which is important if b is a function call with side effects,
   such as getchar().

   However C does convert the results of boolean operations and
   arithmetic comparisons to the canonical values 0 for false and
   1 for true, even akthough "if (x) ..." considers x to be true
   for any non-0 value.

   In this file, we identify those boolean expressions which are
   being used in a context that automatically assures that you
   can evaluate them as short-circuit conditionals, and jump to
   one of two destinations (corresponding to true or false for the
   whole condition) at any point in the evaluation of the condition.

   These conditions are the ones in "if (...) " statements etc; once
   they have been processed, and remaining boolean conditions are
   to be treated as evaluating to truth values 0 and 1, and stored
   somewhere or used appropriately - in other words, a statement
   like a = (x && y) is to be treated as a = (x && y ? TRUE : FALSE).

   Sometimes this equivalency is not necessary (eg if a and b are
   known to have already been canonicalised to 0 or 1), but when
   a and b are unknown, wrapping them in (... ? ... : ...) serves
   the dual purpose of both canonicaalisation _and_ wrapping the
   lazy evaluation conditional in a framework that causes the jumps
   to work.

   When we *are* able to treat conditions are pure booleans, we use
   BAND, BOR and BNOT to evaluate the expressions.  (BNOT == Eor #1)

   Also, in expressions like "a && b", a and b are implicitly to
   be treated as being compared against 0, ie (a != 0) && (b != 0)
   if using short-circuit evaluation, otherwise if treated as
   regular booleans, conditions can be evaluated with bitwise Ands
   and Ors.

   Coercion to 0/1 booleans should start at the leaves, and propogate
   up the tree so that parent nodes do not need to be coerced again.

   Finally, we take advantage of the jumps on true/false to implement
   negation by swapping those jumps, which is free.

   We don't yet simplify conditions using DeMorgan's law, which is
   also sometimes useful to save a negation when we don't have
   both true and false brtanches to follow.

   One final tweak - possible more sensible to implement in the general
   tree-rewrite optimiser module than here - is to catch all sequences
   of "||" where the operands are not canonical booleans, and handle
   them arithmetically, eg (a || b || c) -> ( a!=0 || b!=0 || c!=0)
   -> (a|b|c) != 0 

 */