Compiling APs.


§1. Compiling action tries.

void RTActionPatterns::emit_try(explicit_action *ea, int store_instead) {
    parse_node *spec0 = ea->first_noun;  the noun
    parse_node *spec1 = ea->second_noun;  the second noun
    parse_node *spec2 = ea->actor;  the actor

    if ((K_understanding) && (Rvalues::is_CONSTANT_of_kind(spec0, K_understanding)) &&
        (<subject-pronoun>(Node::get_text(spec0)) == FALSE))
        spec0 = Rvalues::from_wording(Node::get_text(spec0));
    if ((K_understanding) && (Rvalues::is_CONSTANT_of_kind(spec1, K_understanding)) &&
        (<subject-pronoun>(Node::get_text(spec1)) == FALSE))
        spec1 = Rvalues::from_wording(Node::get_text(spec1));

    action_name *an = ea->action;

    int flag_bits = 0;
    if (Kinds::eq(Specifications::to_kind(spec0), K_text)) flag_bits += 16;
    if (Kinds::eq(Specifications::to_kind(spec1), K_text)) flag_bits += 32;
    if (flag_bits > 0) RTKinds::ensure_basic_heap_present();

    if (ea->request) flag_bits += 1;

    Produce::inv_call_iname(Emit::tree(), Hierarchy::find(TRYACTION_HL));
    Produce::down(Emit::tree());
        Produce::val(Emit::tree(), K_number, LITERAL_IVAL, (inter_ti) flag_bits);
        if (spec2) RTActionPatterns::emit_try_action_parameter(spec2, K_object);
        else Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(PLAYER_HL));
        Produce::val_iname(Emit::tree(), K_action_name, RTActions::double_sharp(an));
        if (spec0) RTActionPatterns::emit_try_action_parameter(spec0, ActionSemantics::kind_of_noun(an));
        else Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 0);
        if (spec1) RTActionPatterns::emit_try_action_parameter(spec1, ActionSemantics::kind_of_second(an));
        else Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 0);
        if (store_instead) {
            Produce::inv_call_iname(Emit::tree(), Hierarchy::find(STORED_ACTION_TY_CURRENT_HL));
            Produce::down(Emit::tree());
                Frames::emit_allocation(K_stored_action);
            Produce::up(Emit::tree());
        }
    Produce::up(Emit::tree());
}

§2. Which requires the following. As ever, there have to be hacks to ensure that text as an action parameter is correctly read as parsing grammar rather than text when the action expects that.

void RTActionPatterns::emit_try_action_parameter(parse_node *spec, kind *required_kind) {
    if ((K_understanding) && (Kinds::eq(required_kind, K_understanding))) {
        kind *K = Specifications::to_kind(spec);
        if ((Kinds::compatible(K, K_understanding)) ||
            (Kinds::compatible(K, K_text))) {
            required_kind = NULL;
        }
    }

    if (Dash::check_value(spec, required_kind)) {
        BEGIN_COMPILATION_MODE;
        COMPILATION_MODE_EXIT(DEREFERENCE_POINTERS_CMODE);
        Specifications::Compiler::emit_as_val(K_object, spec);
        END_COMPILATION_MODE;
    }
}

§3. Compiling action patterns. In the following routines, we compile a single clause in what may be a complex condition which determines whether a rule should fire. The flag f indicates whether any condition has already been printed, and is updated as the return value of the routine. (Thus, it's permissible for the routines to compile nothing and return f unchanged.) The simple case first:

§4. The more complex clauses mostly act on a single I6 global variable. In almost all cases, this falls through to the standard method for testing a condition: we force it to propositional form, substituting the global in for the value of free variable 0. However, rule clauses are allowed a few syntaxes not permitted to ordinary conditions, and these are handled as exceptional cases first:

void RTActionPatterns::compile_pattern_match_clause(value_holster *VH,
    nonlocal_variable *I6_global_variable,
    parse_node *spec, kind *verify_as_kind, int adapt_region) {
    if (spec == NULL) return;

    parse_node *I6_var_TS = NULL;
    if (I6_global_variable)
        I6_var_TS = Lvalues::new_actual_NONLOCAL_VARIABLE(I6_global_variable);

    int is_parameter = FALSE;
    if (I6_global_variable == parameter_object_VAR) is_parameter = TRUE;

    RTActionPatterns::compile_pattern_match_clause_inner(VH,
        I6_var_TS, is_parameter, spec, verify_as_kind, adapt_region);
}

void RTActionPatterns::compile_pattern_match_clause_inner(value_holster *VH,
    parse_node *I6_var_TS, int is_parameter,
    parse_node *spec, kind *verify_as_kind, int adapt_region) {
    int force_proposition = FALSE;

    if (spec == NULL) return;

    LOGIF(ACTION_PATTERN_COMPILATION, "[MPE on $P: $P]\n", I6_var_TS, spec);
    kind *K = Specifications::to_kind(spec);
    if (Kinds::Behaviour::definite(K) == FALSE) {
        StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_APClauseIndefinite),
            "that action seems to involve a value which is unclear about "
            "its kind",
            "and that's not allowed. For example, you're not allowed to just "
            "say 'Instead of taking a value: ...' because the taking action "
            "applies to objects; the vaguest you're allowed to be is 'Instead "
            "of taking an object: ...'.");
        return;
    }

    wording C = Descriptions::get_calling(spec);
    if (Wordings::nonempty(C)) {
        local_variable *lvar =
            LocalVariables::ensure_called_local(C,
                Specifications::to_kind(spec));
        LocalVariables::add_calling_to_condition(lvar);
        Produce::inv_primitive(Emit::tree(), SEQUENTIAL_BIP);
        Produce::down(Emit::tree());
            Produce::inv_primitive(Emit::tree(), STORE_BIP);
            Produce::down(Emit::tree());
                inter_symbol *lvar_s = LocalVariables::declare_this(lvar, FALSE, 8);
                Produce::ref_symbol(Emit::tree(), K_value, lvar_s);
                Specifications::Compiler::emit_as_val(K_value, I6_var_TS);
            Produce::up(Emit::tree());
    }

    force_proposition = TRUE;

    if (Node::is(spec, UNKNOWN_NT)) {
        if (problem_count == 0) internal_error("MPE found unknown SP");
        force_proposition = FALSE;
    }
    else if (Lvalues::is_lvalue(spec)) {
        force_proposition = TRUE;
        if (Node::is(spec, TABLE_ENTRY_NT)) {
            if (Node::no_children(spec) != 2) internal_error("MPE with bad no of args");
            LocalVariables::add_table_lookup();

            local_variable *ct_0_lv = LocalVariables::by_name(I"ct_0");
            inter_symbol *ct_0_s = LocalVariables::declare_this(ct_0_lv, FALSE, 8);
            local_variable *ct_1_lv = LocalVariables::by_name(I"ct_1");
            inter_symbol *ct_1_s = LocalVariables::declare_this(ct_1_lv, FALSE, 8);
            Produce::inv_primitive(Emit::tree(), STORE_BIP);
            Produce::down(Emit::tree());
                Produce::ref_symbol(Emit::tree(), K_value, ct_1_s);
                Produce::inv_call_iname(Emit::tree(), Hierarchy::find(EXISTSTABLEROWCORR_HL));
                Produce::down(Emit::tree());
                    Produce::inv_primitive(Emit::tree(), STORE_BIP);
                    Produce::down(Emit::tree());
                        Produce::ref_symbol(Emit::tree(), K_value, ct_0_s);
                        Specifications::Compiler::emit_as_val(K_value, spec->down->next);
                    Produce::up(Emit::tree());
                    Specifications::Compiler::emit_as_val(K_value, spec->down);
                    Specifications::Compiler::emit_as_val(K_value, I6_var_TS);
                Produce::up(Emit::tree());
            Produce::up(Emit::tree());
            force_proposition = FALSE;
        }
    }
    else if ((Specifications::is_kind_like(spec)) &&
            (Kinds::Behaviour::is_object(Specifications::to_kind(spec)) == FALSE)) {
            force_proposition = FALSE;
        }
    else if (Rvalues::is_rvalue(spec)) {
        if ((K_understanding) && (Rvalues::is_CONSTANT_of_kind(spec, K_understanding))) {
            if ((<understanding-action-irregular-operand>(Node::get_text(spec))) &&
                (<<r>> == TRUE)) {
                Produce::val(Emit::tree(), K_truth_state, LITERAL_IVAL, 1);
            } else {
                Produce::inv_primitive(Emit::tree(), NE_BIP);
                Produce::down(Emit::tree());
                    Produce::inv_primitive(Emit::tree(), INDIRECT2_BIP);
                    Produce::down(Emit::tree());
                        Specifications::Compiler::emit_as_val(K_value, spec);
                        Produce::val_iname(Emit::tree(), K_number, Hierarchy::find(CONSULT_FROM_HL));
                        Produce::val_iname(Emit::tree(), K_number, Hierarchy::find(CONSULT_WORDS_HL));
                    Produce::up(Emit::tree());
                    Produce::val_iname(Emit::tree(), K_number, Hierarchy::find(GPR_FAIL_HL));
                Produce::up(Emit::tree());
            }
            force_proposition = FALSE;
        }
        if ((is_parameter == FALSE) &&
            (Rvalues::is_object(spec))) {
            instance *I = Specifications::object_exactly_described_if_any(spec);
            if ((I) && (Instances::of_kind(I, K_region))) {
                LOGIF(ACTION_PATTERN_PARSING,
                    "$P on %u : $T\n", spec, verify_as_kind, current_sentence);
                if (adapt_region) {
                    Produce::inv_call_iname(Emit::tree(), Hierarchy::find(TESTREGIONALCONTAINMENT_HL));
                    Produce::down(Emit::tree());
                        Specifications::Compiler::emit_as_val(K_value, I6_var_TS);
                        Specifications::Compiler::emit_as_val(K_value, spec);
                    Produce::up(Emit::tree());
                    force_proposition = FALSE;
                }
            }
        }
    }
    else if (Specifications::is_description(spec)) {
        if ((is_parameter == FALSE) &&
            ((Descriptions::to_instance(spec)) &&
            (adapt_region) &&
            (Instances::of_kind(Descriptions::to_instance(spec), K_region)))) {
            Produce::inv_call_iname(Emit::tree(), Hierarchy::find(TESTREGIONALCONTAINMENT_HL));
            Produce::down(Emit::tree());
                Specifications::Compiler::emit_as_val(K_value, I6_var_TS);
                Specifications::Compiler::emit_as_val(K_value, spec);
            Produce::up(Emit::tree());
        }
        force_proposition = FALSE;
    }

    pcalc_prop *prop = NULL;
    if (Specifications::is_description(spec))
        prop = Descriptions::to_proposition(spec);

    if (Lvalues::is_lvalue(spec))
        LOGIF(ACTION_PATTERN_COMPILATION, "Storage has $D\n", prop);

    if ((force_proposition) && (prop == NULL)) {
        prop = SentencePropositions::from_spec(spec);
        LOGIF(ACTION_PATTERN_COMPILATION, "[MPE forced proposition: $D]\n", prop);
        if (prop == NULL) internal_error("MPE unable to force proposition");
        if (verify_as_kind) {
            prop = Propositions::concatenate(prop,
                KindPredicates::new_atom(
                    verify_as_kind, Terms::new_variable(0)));
            Calculus::Deferrals::prop_verify_descriptive(prop,
                "an action or activity to apply to things matching a given "
                "description", spec);
        }
    }

    if (prop) {
        LOGIF(ACTION_PATTERN_COMPILATION, "[MPE faces proposition: $D]\n", prop);
        Propositions::Checker::type_check(prop, Propositions::Checker::tc_no_problem_reporting());
        Calculus::Deferrals::emit_test_of_proposition(I6_var_TS, prop);
    }

    if (Wordings::nonempty(C)) {
        Produce::up(Emit::tree());
    }
}

§5.

void RTActionPatterns::as_stored_action(value_holster *VH, explicit_action *ea) {
    inter_name *N = RTKinds::new_block_constant_iname();
    packaging_state save = Emit::named_late_array_begin(N, K_value);

    RTKinds::emit_block_value_header(K_stored_action, FALSE, 6);
    action_name *an = ea->action;
    Emit::array_action_entry(an);

    int request_bits = (ea->request)?1:0;
    if (ea->first_noun) {
        if ((K_understanding) && (Rvalues::is_CONSTANT_of_kind(ea->first_noun, K_understanding))) {
            request_bits = request_bits | 16;
            TEMPORARY_TEXT(BC)
            literal_text *lt = TextLiterals::compile_literal(NULL, FALSE, Node::get_text(ea->first_noun));
            Emit::array_iname_entry(lt->lt_sba_iname);
            DISCARD_TEXT(BC)
        } else Specifications::Compiler::emit(ea->first_noun);
    } else {
        Emit::array_numeric_entry(0);
    }
    if (ea->second_noun) {
        if ((K_understanding) && (Rvalues::is_CONSTANT_of_kind(ea->second_noun, K_understanding))) {
            request_bits = request_bits | 32;
            literal_text *lt = TextLiterals::compile_literal(NULL, TRUE, Node::get_text(ea->second_noun));
            Emit::array_iname_entry(lt->lt_sba_iname);
        } else Specifications::Compiler::emit(ea->second_noun);
    } else {
        Emit::array_numeric_entry(0);
    }
    if (ea->actor) {
        Specifications::Compiler::emit(ea->actor);
    } else
        Emit::array_iname_entry(RTInstances::iname(I_yourself));
    Emit::array_numeric_entry((inter_ti) request_bits);
    Emit::array_numeric_entry(0);
    Emit::array_end(save);
    if (N) Emit::holster(VH, N);
}

void RTActionPatterns::emit_pattern_match(action_pattern *ap, int naming_mode) {
    value_holster VH = Holsters::new(INTER_VAL_VHMODE);
    RTActionPatterns::compile_pattern_match(&VH, ap, naming_mode);
}

§6.

enum ACTOR_IS_PLAYER_CPMC from 1
enum ACTOR_ISNT_PLAYER_CPMC
enum REQUESTER_EXISTS_CPMC
enum REQUESTER_DOESNT_EXIST_CPMC
enum ACTOR_MATCHES_CPMC
enum ACTION_MATCHES_CPMC
enum SET_SELF_TO_ACTOR_CPMC
enum WHEN_CONDITION_HOLDS_CPMC
enum NOUN_EXISTS_CPMC
enum NOUN_IS_INP1_CPMC
enum SECOND_EXISTS_CPMC
enum SECOND_IS_INP1_CPMC
enum NOUN_MATCHES_AS_OBJECT_CPMC
enum NOUN_MATCHES_AS_VALUE_CPMC
enum SECOND_MATCHES_AS_OBJECT_CPMC
enum SECOND_MATCHES_AS_VALUE_CPMC
enum PLAYER_LOCATION_MATCHES_CPMC
enum ACTOR_IN_RIGHT_PLACE_CPMC
enum ACTOR_LOCATION_MATCHES_CPMC
enum PARAMETER_MATCHES_CPMC
enum OPTIONAL_CLAUSE_CPMC
enum PRESENCE_OF_MATCHES_CPMC
enum PRESENCE_OF_IN_SCOPE_CPMC
enum LOOP_OVER_SCOPE_WITH_CALLING_CPMC
enum LOOP_OVER_SCOPE_WITHOUT_CALLING_CPMC
define MAX_CPM_CLAUSES 256
define CPMC_NEEDED(C, A) {
    if (cpm_count >= MAX_CPM_CLAUSES) internal_error("action pattern grossly overcomplex");
    needed[cpm_count] = C;
    needed_apoc[cpm_count] = A;
    cpm_count++;
}
void RTActionPatterns::compile_pattern_match(value_holster *VH, action_pattern *ap, int naming_mode) {
    if (ap == NULL) return;

    int cpm_count = 0, needed[MAX_CPM_CLAUSES];
    ap_clause *needed_apoc[MAX_CPM_CLAUSES];
    LOGIF(ACTION_PATTERN_COMPILATION, "Compiling action pattern:\n  $A\n", ap);

    if (ap->duration) {
        LOGIF(ACTION_PATTERN_COMPILATION, "As past action\n");
        Chronology::compile_past_action_pattern(VH, ap->duration, *ap);
    } else {
        kind *kind_of_noun = K_object;
        kind *kind_of_second = K_object;

        if (naming_mode == FALSE) {
            if (APClauses::actor_is_anyone_except_player(ap) == FALSE) {
                int impose = FALSE;
                if (APClauses::spec(ap, ACTOR_AP_CLAUSE) != NULL) {
                    impose = TRUE;
                    nonlocal_variable *var = Lvalues::get_nonlocal_variable_if_any(APClauses::spec(ap, ACTOR_AP_CLAUSE));
                    if ((var) && (var == player_VAR)) impose = FALSE;
                    instance *I = Rvalues::to_object_instance(APClauses::spec(ap, ACTOR_AP_CLAUSE));
                    if ((I) && (I == I_yourself)) impose = FALSE;
                }
                if (impose) {
                    CPMC_NEEDED(ACTOR_ISNT_PLAYER_CPMC, NULL);
                    if (APClauses::is_request(ap)) {
                        CPMC_NEEDED(REQUESTER_EXISTS_CPMC, NULL);
                    } else {
                        CPMC_NEEDED(REQUESTER_DOESNT_EXIST_CPMC, NULL);
                    }
                    if (APClauses::spec(ap, ACTOR_AP_CLAUSE)) {
                        CPMC_NEEDED(ACTOR_MATCHES_CPMC, NULL);
                    }
                } else {
                    CPMC_NEEDED(ACTOR_IS_PLAYER_CPMC, NULL);
                }
            } else {
                if (APClauses::is_request(ap)) {
                    CPMC_NEEDED(REQUESTER_EXISTS_CPMC, NULL);
                } else {
                    CPMC_NEEDED(REQUESTER_DOESNT_EXIST_CPMC, NULL);
                }
            }
        }
        if (ActionNameLists::testing(ap->action_list)) {
            CPMC_NEEDED(ACTION_MATCHES_CPMC, NULL);
        }
        if ((ap->action_list == NULL) && (APClauses::spec(ap, NOUN_AP_CLAUSE))) {
            CPMC_NEEDED(NOUN_EXISTS_CPMC, NULL);
            CPMC_NEEDED(NOUN_IS_INP1_CPMC, NULL);
        }
        if ((ap->action_list == NULL) && (APClauses::spec(ap, SECOND_AP_CLAUSE))) {
            CPMC_NEEDED(SECOND_EXISTS_CPMC, NULL);
            CPMC_NEEDED(SECOND_IS_INP1_CPMC, NULL);
        }
        anl_item *item = ActionNameLists::first_item(ap->action_list);
        if ((item) && (item->action_listed)) {
            kind_of_noun = ActionSemantics::kind_of_noun(item->action_listed);
            if (kind_of_noun == NULL) kind_of_noun = K_object;
        }

        if (Kinds::Behaviour::is_object(kind_of_noun)) {
            if (APClauses::spec(ap, NOUN_AP_CLAUSE)) {
                CPMC_NEEDED(NOUN_MATCHES_AS_OBJECT_CPMC, NULL);
            }
        } else {
            if (APClauses::spec(ap, NOUN_AP_CLAUSE)) {
                CPMC_NEEDED(NOUN_MATCHES_AS_VALUE_CPMC, NULL);
            }
        }
        if ((item) && (item->action_listed)) {
            kind_of_second = ActionSemantics::kind_of_second(item->action_listed);
            if (kind_of_second == NULL) kind_of_second = K_object;
        }
        if (Kinds::Behaviour::is_object(kind_of_second)) {
            if (APClauses::spec(ap, SECOND_AP_CLAUSE)) {
                CPMC_NEEDED(SECOND_MATCHES_AS_OBJECT_CPMC, NULL);
            }
        } else {
            if (APClauses::spec(ap, SECOND_AP_CLAUSE)) {
                CPMC_NEEDED(SECOND_MATCHES_AS_VALUE_CPMC, NULL);
            }
        }

        if (APClauses::spec(ap, IN_AP_CLAUSE)) {
            if ((APClauses::actor_is_anyone_except_player(ap) == FALSE) && (naming_mode == FALSE) &&
                (APClauses::spec(ap, ACTOR_AP_CLAUSE) == NULL)) {
                CPMC_NEEDED(PLAYER_LOCATION_MATCHES_CPMC, NULL);
            } else {
                CPMC_NEEDED(ACTOR_IN_RIGHT_PLACE_CPMC, NULL);
                CPMC_NEEDED(ACTOR_LOCATION_MATCHES_CPMC, NULL);
            }
        }

        if (APClauses::spec(ap, PARAMETRIC_AP_CLAUSE)) {
            CPMC_NEEDED(PARAMETER_MATCHES_CPMC, NULL);
        }

        LOOP_OVER_AP_CLAUSES(apoc, ap)
            if ((apoc->stv_to_match) && (apoc->clause_spec)) {
                CPMC_NEEDED(OPTIONAL_CLAUSE_CPMC, apoc);
            }

        PluginCalls::set_pattern_match_requirements(ap, &cpm_count, needed, needed_apoc);

        if (APClauses::spec(ap, IN_THE_PRESENCE_OF_AP_CLAUSE) != NULL) {
            instance *to_be_present =
                Specifications::object_exactly_described_if_any(APClauses::spec(ap, IN_THE_PRESENCE_OF_AP_CLAUSE));
            if (to_be_present) {
                CPMC_NEEDED(PRESENCE_OF_MATCHES_CPMC, NULL);
                CPMC_NEEDED(PRESENCE_OF_IN_SCOPE_CPMC, NULL);
            } else {
                wording PC = Descriptions::get_calling(APClauses::spec(ap, IN_THE_PRESENCE_OF_AP_CLAUSE));
                if (Wordings::nonempty(PC)) {
                    CPMC_NEEDED(LOOP_OVER_SCOPE_WITH_CALLING_CPMC, NULL);
                } else {
                    CPMC_NEEDED(LOOP_OVER_SCOPE_WITHOUT_CALLING_CPMC, NULL);
                }
            }
        }
        if (APClauses::spec(ap, WHEN_AP_CLAUSE) != NULL) {
            CPMC_NEEDED(SET_SELF_TO_ACTOR_CPMC, NULL);
            CPMC_NEEDED(WHEN_CONDITION_HOLDS_CPMC, NULL);
        }

        Compile the condition from these instructions6.1;
    }
}

§6.1.

define CPMC_RANGE(ix, F, T) {
    ranges_from[ix] = F; ranges_to[ix] = T; ranges_count[ix] = 0;
    for (int i=0; i<cpm_count; i++)
        if ((needed[i] >= F) && (needed[i] <= T))
            ranges_count[ix]++;
}

Compile the condition from these instructions6.1 =

    int ranges_from[4], ranges_to[4], ranges_count[4];
    CPMC_RANGE(0, ACTOR_IS_PLAYER_CPMC, ACTOR_MATCHES_CPMC);
    CPMC_RANGE(1, ACTION_MATCHES_CPMC, ACTION_MATCHES_CPMC);
    CPMC_RANGE(2, NOUN_EXISTS_CPMC, NO_DEFINED_CPMC_VALUES);
    CPMC_RANGE(3, SET_SELF_TO_ACTOR_CPMC, WHEN_CONDITION_HOLDS_CPMC);

    int range_to_compile = 0;
    LocalVariables::begin_condition_emit();

    if (ActionNameLists::listwise_negated(ap->action_list)) {
        if (ranges_count[0] > 0) {
            Produce::inv_primitive(Emit::tree(), AND_BIP);
            Produce::down(Emit::tree());
                range_to_compile = 0;
                Emit CPM range6.1.1;
        }
        if (ranges_count[3] > 0) {
            Produce::inv_primitive(Emit::tree(), AND_BIP);
            Produce::down(Emit::tree());
        }
        Produce::inv_primitive(Emit::tree(), NOT_BIP);
        Produce::down(Emit::tree());
        if ((ranges_count[1] == 0) && (ranges_count[2] == 0))
            Produce::val(Emit::tree(), K_truth_state, LITERAL_IVAL, 0);
        else {
            if ((ranges_count[1] > 0) && (ranges_count[2] > 0)) {
                Produce::inv_primitive(Emit::tree(), AND_BIP);
                Produce::down(Emit::tree());
            }
            if (ranges_count[1] > 0) {
                range_to_compile = 1;
                Emit CPM range6.1.1;
            }
            if (ranges_count[2] > 0) {
                range_to_compile = 2;
                Emit CPM range6.1.1;
            }
            if ((ranges_count[1] > 0) && (ranges_count[2] > 0)) Produce::up(Emit::tree());
        }
        Produce::up(Emit::tree());
        if (ranges_count[3] > 0) {
            range_to_compile = 3;
            Emit CPM range6.1.1;
        }
        if (ranges_count[3] > 0) Produce::up(Emit::tree());
        if (ranges_count[0] > 0) Produce::up(Emit::tree());
    } else {
        int downs = 0;
        if (ranges_count[1] > 0) {
            if (ranges_count[0]+ranges_count[2]+ranges_count[3] > 0) {
                Produce::inv_primitive(Emit::tree(), AND_BIP);
                Produce::down(Emit::tree()); downs++;
            }
            range_to_compile = 1;
            Emit CPM range6.1.1;
        }
        if (ranges_count[0] > 0) {
            if (ranges_count[2]+ranges_count[3] > 0) {
                Produce::inv_primitive(Emit::tree(), AND_BIP);
                Produce::down(Emit::tree()); downs++;
            }
            range_to_compile = 0;
            Emit CPM range6.1.1;
        }
        if (ranges_count[2] > 0) {
            if (ranges_count[3] > 0) {
                Produce::inv_primitive(Emit::tree(), AND_BIP);
                Produce::down(Emit::tree()); downs++;
            }
            range_to_compile = 2;
            Emit CPM range6.1.1;
        }
        if (ranges_count[3] > 0) {
            range_to_compile = 3;
            Emit CPM range6.1.1;
        }
        while (downs > 0) { Produce::up(Emit::tree()); downs--; }
    }

    if ((ranges_count[0] + ranges_count[1] + ranges_count[2] + ranges_count[3] == 0) &&
        (ActionNameLists::listwise_negated(ap->action_list) == FALSE)) {
        Produce::val(Emit::tree(), K_truth_state, LITERAL_IVAL, 1);
    }
    LocalVariables::end_condition_emit();

§6.1.1. Emit CPM range6.1.1 =

    TEMPORARY_TEXT(C)
    WRITE_TO(C, "Range %d from %d to %d", range_to_compile, ranges_from[range_to_compile], ranges_to[range_to_compile]);
    Emit::code_comment(C);
    DISCARD_TEXT(C)
    int downs = 0;
    for (int i=0, done=0; i<cpm_count; i++) {
        int cpmc = needed[i];
        if ((cpmc >= ranges_from[range_to_compile]) && (cpmc <= ranges_to[range_to_compile])) {
            done++;
            if (done < ranges_count[range_to_compile]) {
                Produce::inv_primitive(Emit::tree(), AND_BIP);
                Produce::down(Emit::tree()); downs++;
            }
            ap_clause *apoc = needed_apoc[i];
            Emit CPM condition piece6.1.1.1;
        }
    }
    while (downs > 0) { Produce::up(Emit::tree()); downs--; }

§6.1.1.1. Emit CPM condition piece6.1.1.1 =

    TEMPORARY_TEXT(C)
    WRITE_TO(C, "So %d", cpmc);
    Emit::code_comment(C);
    DISCARD_TEXT(C)
    if (PluginCalls::compile_pattern_match_clause(VH, ap, cpmc) == FALSE)
    switch (cpmc) {
        case ACTOR_IS_PLAYER_CPMC:
            Produce::inv_primitive(Emit::tree(), EQ_BIP);
            Produce::down(Emit::tree());
                Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(ACTOR_HL));
                Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(PLAYER_HL));
            Produce::up(Emit::tree());
            break;
        case ACTOR_ISNT_PLAYER_CPMC:
            Produce::inv_primitive(Emit::tree(), NE_BIP);
            Produce::down(Emit::tree());
                Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(ACTOR_HL));
                Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(PLAYER_HL));
            Produce::up(Emit::tree());
            break;
        case REQUESTER_EXISTS_CPMC:
            Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(ACT_REQUESTER_HL));
            break;
        case REQUESTER_DOESNT_EXIST_CPMC:
            Produce::inv_primitive(Emit::tree(), EQ_BIP);
            Produce::down(Emit::tree());
                Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(ACT_REQUESTER_HL));
                Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 0);
            Produce::up(Emit::tree());
            break;
        case ACTOR_MATCHES_CPMC:
            RTActionPatterns::compile_pattern_match_clause(VH, Inter_actor_VAR, APClauses::spec(ap, ACTOR_AP_CLAUSE), K_object, FALSE);
            break;
        case ACTION_MATCHES_CPMC:
            RTActions::emit_anl(ap->action_list);
            break;
        case NOUN_EXISTS_CPMC:
            Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(NOUN_HL));
            break;
        case NOUN_IS_INP1_CPMC:
            Produce::inv_primitive(Emit::tree(), EQ_BIP);
            Produce::down(Emit::tree());
                Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(NOUN_HL));
                Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(INP1_HL));
            Produce::up(Emit::tree());
            break;
        case SECOND_EXISTS_CPMC:
            Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(SECOND_HL));
            break;
        case SECOND_IS_INP1_CPMC:
            Produce::inv_primitive(Emit::tree(), EQ_BIP);
            Produce::down(Emit::tree());
                Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(SECOND_HL));
                Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(INP2_HL));
            Produce::up(Emit::tree());
            break;
        case NOUN_MATCHES_AS_OBJECT_CPMC:
            RTActionPatterns::compile_pattern_match_clause(VH, Inter_noun_VAR, APClauses::spec(ap, NOUN_AP_CLAUSE),
                kind_of_noun, FALSE);
            break;
        case NOUN_MATCHES_AS_VALUE_CPMC:
            RTActionPatterns::compile_pattern_match_clause(VH,
                RTTemporaryVariables::from_iname(Hierarchy::find(PARSED_NUMBER_HL), kind_of_noun),
                APClauses::spec(ap, NOUN_AP_CLAUSE), kind_of_noun, FALSE);
            break;
        case SECOND_MATCHES_AS_OBJECT_CPMC:
            RTActionPatterns::compile_pattern_match_clause(VH, Inter_second_noun_VAR, APClauses::spec(ap, SECOND_AP_CLAUSE),
                kind_of_second, FALSE);
            break;
        case SECOND_MATCHES_AS_VALUE_CPMC:
            RTActionPatterns::compile_pattern_match_clause(VH,
                RTTemporaryVariables::from_iname(Hierarchy::find(PARSED_NUMBER_HL), kind_of_second),
                APClauses::spec(ap, SECOND_AP_CLAUSE), kind_of_second, FALSE);
            break;
        case PLAYER_LOCATION_MATCHES_CPMC:
            RTActionPatterns::compile_pattern_match_clause(VH, real_location_VAR, APClauses::spec(ap, IN_AP_CLAUSE), K_object, TRUE);
            break;
        case ACTOR_IN_RIGHT_PLACE_CPMC:
            Produce::inv_primitive(Emit::tree(), STORE_BIP);
            Produce::down(Emit::tree());
                Produce::ref_iname(Emit::tree(), K_object, Hierarchy::find(ACTOR_LOCATION_HL));
                Produce::inv_call_iname(Emit::tree(), Hierarchy::find(LOCATIONOF_HL));
                Produce::down(Emit::tree());
                    Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(ACTOR_HL));
                Produce::up(Emit::tree());
            Produce::up(Emit::tree());
            break;
        case ACTOR_LOCATION_MATCHES_CPMC:
            RTActionPatterns::compile_pattern_match_clause(VH, actor_location_VAR,
                APClauses::spec(ap, IN_AP_CLAUSE), K_object, TRUE);
            break;
        case PARAMETER_MATCHES_CPMC: {
            kind *saved_kind = NonlocalVariables::kind(parameter_object_VAR);
            NonlocalVariables::set_kind(parameter_object_VAR, ap->parameter_kind);
            RTActionPatterns::compile_pattern_match_clause(VH,
                parameter_object_VAR, APClauses::spec(ap, PARAMETRIC_AP_CLAUSE), ap->parameter_kind, FALSE);
            NonlocalVariables::set_kind(parameter_object_VAR, saved_kind);
            break;
        }
        case OPTIONAL_CLAUSE_CPMC: {
            kind *K = StackedVariables::get_kind(apoc->stv_to_match);
            RTActionPatterns::compile_pattern_match_clause(VH,
                RTTemporaryVariables::from_existing_variable(apoc->stv_to_match->underlying_var, K),
                apoc->clause_spec, K, APClauses::opt(apoc, ALLOW_REGION_AS_ROOM_APCOPT));
            break;
        }
        case PRESENCE_OF_MATCHES_CPMC: {
            instance *to_be_present =
                Specifications::object_exactly_described_if_any(APClauses::spec(ap, IN_THE_PRESENCE_OF_AP_CLAUSE));
            RTActionPatterns::compile_pattern_match_clause(VH,
                RTTemporaryVariables::from_iname(RTInstances::iname(to_be_present), K_object),
                APClauses::spec(ap, IN_THE_PRESENCE_OF_AP_CLAUSE), K_object, FALSE);
            break;
        }
        case PRESENCE_OF_IN_SCOPE_CPMC: {
            instance *to_be_present =
                Specifications::object_exactly_described_if_any(APClauses::spec(ap, IN_THE_PRESENCE_OF_AP_CLAUSE));
            Produce::inv_call_iname(Emit::tree(), Hierarchy::find(TESTSCOPE_HL));
            Produce::down(Emit::tree());
                Produce::val_iname(Emit::tree(), K_value, RTInstances::iname(to_be_present));
                Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(ACTOR_HL));
            Produce::up(Emit::tree());
            break;
        }
        case LOOP_OVER_SCOPE_WITH_CALLING_CPMC: {
            loop_over_scope *los = LoopingOverScope::new(APClauses::spec(ap, IN_THE_PRESENCE_OF_AP_CLAUSE));
            wording PC = Descriptions::get_calling(APClauses::spec(ap, IN_THE_PRESENCE_OF_AP_CLAUSE));
            local_variable *lvar = LocalVariables::ensure_called_local(PC,
                Specifications::to_kind(APClauses::spec(ap, IN_THE_PRESENCE_OF_AP_CLAUSE)));
            inter_symbol *lvar_s = LocalVariables::declare_this(lvar, FALSE, 8);
            Produce::inv_primitive(Emit::tree(), SEQUENTIAL_BIP);
            Produce::down(Emit::tree());
                Produce::inv_primitive(Emit::tree(), STORE_BIP);
                Produce::down(Emit::tree());
                    Produce::ref_iname(Emit::tree(), K_value, Hierarchy::find(LOS_RV_HL));
                    Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 0);
                Produce::up(Emit::tree());
                Produce::inv_primitive(Emit::tree(), SEQUENTIAL_BIP);
                Produce::down(Emit::tree());
                    Produce::inv_call_iname(Emit::tree(), Hierarchy::find(LOOPOVERSCOPE_HL));
                    Produce::down(Emit::tree());
                        Produce::val_iname(Emit::tree(), K_value, los->los_iname);
                        Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(ACTOR_HL));
                    Produce::up(Emit::tree());
                    Produce::inv_primitive(Emit::tree(), STORE_BIP);
                    Produce::down(Emit::tree());
                        Produce::ref_symbol(Emit::tree(), K_value, lvar_s);
                        Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(LOS_RV_HL));
                    Produce::up(Emit::tree());
                Produce::up(Emit::tree());
            Produce::up(Emit::tree());
            break;
        }
        case LOOP_OVER_SCOPE_WITHOUT_CALLING_CPMC: {
            loop_over_scope *los = LoopingOverScope::new(APClauses::spec(ap, IN_THE_PRESENCE_OF_AP_CLAUSE));
            Produce::inv_primitive(Emit::tree(), SEQUENTIAL_BIP);
            Produce::down(Emit::tree());
                Produce::inv_primitive(Emit::tree(), STORE_BIP);
                Produce::down(Emit::tree());
                    Produce::ref_iname(Emit::tree(), K_value, Hierarchy::find(LOS_RV_HL));
                    Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 0);
                Produce::up(Emit::tree());
                Produce::inv_primitive(Emit::tree(), SEQUENTIAL_BIP);
                Produce::down(Emit::tree());
                    Produce::inv_call_iname(Emit::tree(), Hierarchy::find(LOOPOVERSCOPE_HL));
                    Produce::down(Emit::tree());
                        Produce::val_iname(Emit::tree(), K_value, los->los_iname);
                        Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(ACTOR_HL));
                    Produce::up(Emit::tree());
                    Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(LOS_RV_HL));
                Produce::up(Emit::tree());
            Produce::up(Emit::tree());
            break;
        }
        case SET_SELF_TO_ACTOR_CPMC:
            Produce::inv_primitive(Emit::tree(), SEQUENTIAL_BIP);
            Produce::down(Emit::tree());
                Produce::inv_primitive(Emit::tree(), STORE_BIP);
                Produce::down(Emit::tree());
                    Produce::ref_iname(Emit::tree(), K_value, Hierarchy::find(SELF_HL));
                    Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(ACTOR_HL));
                Produce::up(Emit::tree());
                Produce::val(Emit::tree(), K_truth_state, LITERAL_IVAL, 1);
            Produce::up(Emit::tree());
            break;
        case WHEN_CONDITION_HOLDS_CPMC:
            Specifications::Compiler::emit_as_val(K_value, APClauses::spec(ap, WHEN_AP_CLAUSE));
            break;
    }

§7.

void RTActionPatterns::emit_past_tense(action_pattern *ap) {
    int bad_form = FALSE;
    Produce::inv_call_iname(Emit::tree(), Hierarchy::find(TESTACTIONBITMAP_HL));
    Produce::down(Emit::tree());
    if (APClauses::spec(ap, NOUN_AP_CLAUSE) == NULL)
        Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 0);
    else
        Specifications::Compiler::emit_as_val(K_value, APClauses::spec(ap, NOUN_AP_CLAUSE));
    int L = ActionNameLists::length(ap->action_list);
    if (L == 0)
        Produce::val(Emit::tree(), K_number, LITERAL_IVAL, (inter_ti) -1);
    else {
        anl_item *item = ActionNameLists::first_item(ap->action_list);
        if (L >= 2) bad_form = TRUE;
        if (ActionSemantics::can_be_compiled_in_past_tense(item->action_listed) == FALSE)
            bad_form = TRUE;
        Produce::val_iname(Emit::tree(), K_value, RTActions::double_sharp(item->action_listed));
    }
    Produce::up(Emit::tree());
    if (APClauses::viable_in_past_tense(ap) == FALSE) bad_form = TRUE;
    if (bad_form)
        Issue too complex PT problem7.1;
}

§7.1. Issue too complex PT problem7.1 =

    StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_PTAPTooComplex),
        "that is too complex a past tense action",
        "at least for this version of Inform to handle: we may improve "
        "matters in later releases. The restriction is that the "
        "actions used in the past tense may take at most one "
        "object, and that this must be a physical thing (not a "
        "value, in other words). And no details of where or what "
        "else was then happening can be specified.");