Evaluating conditions.
§1. This section implements the primitives which evaluate conditions. !propertyvalue might seem a surprising inclusion in the list: as the name suggests, this finds a property value. But although it is often used in a value context, it's also used as a condition. For example, if kit code (written in Inform 6 notation) does this:
if (obj has concealed) ...
then the condition amounts to an inv !propertyvalue. Now, since any value can be used as a condition, this may still not seem to mean that !propertyvalue belongs here; but consider that it is also legal to write —
if (obj has concealed or scenery) ...
Here the inv !propertyvalue involves an inv !alternative in its children, and handling that requires the mechanism below.
int CConditions::invoke_primitive(code_generation *gen, inter_ti bip, inter_tree_node *P) { text_stream *OUT = CodeGen::current(gen); switch (bip) { case NOT_BIP: WRITE("(!("); VNODE_1C; WRITE("))"); break; case AND_BIP: WRITE("(("); VNODE_1C; WRITE(") && ("); VNODE_2C; WRITE("))"); break; case OR_BIP: WRITE("(("); VNODE_1C; WRITE(") || ("); VNODE_2C; WRITE("))"); break; case PROPERTYEXISTS_BIP: C_GEN_DATA(objdata.value_ranges_needed) = TRUE; C_GEN_DATA(objdata.value_property_holders_needed) = TRUE; WRITE("(i7_provides_gprop(proc, "); VNODE_1C; WRITE(", "); VNODE_2C; WRITE(", "); VNODE_3C; WRITE("))"); break; case EQ_BIP: case NE_BIP: case GT_BIP: case GE_BIP: case LT_BIP: case LE_BIP: case OFCLASS_BIP: case IN_BIP: case NOTIN_BIP: CConditions::comparison_r(gen, bip, NULL, InterTree::first_child(P), InterTree::second_child(P), 0); break; case PROPERTYVALUE_BIP: CConditions::comparison_r(gen, bip, InterTree::first_child(P), InterTree::second_child(P), InterTree::third_child(P), 0); break; case ALTERNATIVE_BIP: internal_error("misplaced !alternative in Inter tree"); break; default: return NOT_APPLICABLE; } return FALSE; }
§2. The following recursive mechanism exists because of the need to support alternative choices in Inter conditions, as here:
inv !if inv !eq val K_number x inv !alternative val K_number 4 val K_number 8 ...
This is the equivalent of writing if (x == 4 or 8) ... in Inform 6, but C does not have an or operator like that. We could with care sometimes compile this as if ((x == 4) (x == 8)), but if evaluating x has side-effects, or is slow, this will cause problems. Instead we compile if (t = x, ((t == 4) (t == 8))) where t is temporary storage.
Note that !ne and !notin interpret !alternative in a de Morgan-like way, so that we compile if ((x != 4) && (x != 8)) rather than if ((x != 4) (x != 8)). The former is equivalent to negating !eq on the same choices, which is what we want; the latter would be universally true, which is useless.
void CConditions::comparison_r(code_generation *gen, inter_ti bip, inter_tree_node *K, inter_tree_node *X, inter_tree_node *Y, int depth) { if (Inode::is(Y, INV_IST)) { if (InvInstruction::method(Y) == PRIMITIVE_INVMETH) { inter_symbol *prim = InvInstruction::primitive(Y); inter_ti ybip = Primitives::to_BIP(gen->from, prim); if (ybip == ALTERNATIVE_BIP) { text_stream *OUT = CodeGen::current(gen); if (depth == 0) { WRITE("(proc->state.tmp[0] = "); Vanilla::node(gen, X); WRITE(", ("); } CConditions::comparison_r(gen, bip, K, NULL, InterTree::first_child(Y), depth+1); if ((bip == NE_BIP) || (bip == NOTIN_BIP)) WRITE(" && "); else WRITE(" || "); CConditions::comparison_r(gen, bip, K, NULL, InterTree::second_child(Y), depth+1); if (depth == 0) { WRITE("))"); } return; } } } text_stream *OUT = CodeGen::current(gen); int positive = TRUE; text_stream *test_fn = NULL, *test_operator = NULL; switch (bip) { case OFCLASS_BIP: positive = TRUE; test_fn = I"i7_ofclass"; break; case IN_BIP: positive = TRUE; test_fn = I"i7_in"; break; case NOTIN_BIP: positive = FALSE; test_fn = I"i7_in"; break; case EQ_BIP: test_operator = I"=="; break; case NE_BIP: test_operator = I"!="; break; case GT_BIP: test_operator = I">"; break; case GE_BIP: test_operator = I">="; break; case LT_BIP: test_operator = I"<"; break; case LE_BIP: test_operator = I"<="; break; case PROPERTYVALUE_BIP: break; default: internal_error("unsupported condition"); break; } if (bip == PROPERTYVALUE_BIP) { WRITE("(i7_read_gprop_value(proc, ", test_fn); Vanilla::node(gen, K); WRITE(", "); Compile first comparand2.1; WRITE(", "); Compile second comparand2.2; WRITE("))"); } else if (Str::len(test_fn) > 0) { WRITE("(%S(proc, ", test_fn); Compile first comparand2.1; WRITE(", "); Compile second comparand2.2; WRITE(")"); if (positive == FALSE) WRITE(" == 0"); WRITE(")"); } else { WRITE("("); Compile first comparand2.1; WRITE(" %S ", test_operator); Compile second comparand2.2; WRITE(")"); } }
§2.1. Compile first comparand2.1 =
if (X) Vanilla::node(gen, X); else WRITE("proc->state.tmp[0]");
- This code is used in §2 (three times).
§2.2. Compile second comparand2.2 =
Vanilla::node(gen, Y);
- This code is used in §2 (three times).