Filters are used to require nouns to have specific kinds or attributes, or to have specific scoping rules: they correspond to Inform 6's |noun=Routine| and |scope=Routine| tokens. Though these are quite different concepts in I6, their common handling seems natural in I7.


§1. Definitions.

typedef struct noun_filter_token {
    struct parse_node *the_filter;
    struct parse_node *nft_created_at;
    int global_scope_flag;
    int any_things_flag;
    int parse_using_gpr;
    int nft_compiled;
    struct inter_name *nft_iname;
    CLASS_DEFINITION
} noun_filter_token;

§2. There are only three things we can do with these: create them, compile their names (used as I6 tokens), and compile their routines.

noun_filter_token *UnderstandFilterTokens::nft_new(parse_node *spec, int global_scope, int any_things) {
    pcalc_prop *prop = Specifications::to_proposition(spec);
    if ((prop) && (Binding::number_free(prop) != 1)) {
        LOG("So $P and $D\n", spec, prop);
        StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_FilterQuantified),
            "the [any ...] doesn't clearly give a description in the '...' part",
            "where I was expecting something like '[any vehicle]'.");
        spec = Specifications::from_kind(K_object);
    }

    noun_filter_token *nft = CREATE(noun_filter_token);
    nft->the_filter = spec;
    nft->global_scope_flag = global_scope;
    nft->any_things_flag = any_things;
    nft->nft_created_at = current_sentence;
    nft->parse_using_gpr = FALSE;
    nft->nft_compiled = FALSE;

    if (global_scope) {
        package_request *PR = Hierarchy::local_package(SCOPE_FILTERS_HAP);
        nft->nft_iname = Hierarchy::make_iname_in(SCOPE_FILTER_FN_HL, PR);
    } else {
        package_request *PR = Hierarchy::local_package(NOUN_FILTERS_HAP);
        nft->nft_iname = Hierarchy::make_iname_in(NOUN_FILTER_FN_HL, PR);
    }
    return nft;
}

inter_name *UnderstandFilterTokens::nft_compile_routine_iname(noun_filter_token *nft) {
    return nft->nft_iname;
}

void UnderstandFilterTokens::nft_compile_routine(noun_filter_token *nft) {
    parse_node *noun_var = Lvalues::new_actual_NONLOCAL_VARIABLE(Inter_noun_VAR);
    kind *R = Specifications::to_kind(nft->the_filter);
    kind *K = NonlocalVariables::kind(Inter_noun_VAR);
    NonlocalVariables::set_kind(Inter_noun_VAR, R);
    if (Kinds::Behaviour::is_object(R) == FALSE) nft->parse_using_gpr = TRUE;

    packaging_state save = Routines::begin(nft->nft_iname);
    if (nft->parse_using_gpr) {
        inter_symbol *v_s = LocalVariables::add_internal_local_c_as_symbol(I"v", "value parsed");
        inter_symbol *n_s = LocalVariables::add_internal_local_c_as_symbol(I"n", "saved value of noun");

        Produce::inv_primitive(Emit::tree(), STORE_BIP);
        Produce::down(Emit::tree());
            Produce::ref_symbol(Emit::tree(), K_value, v_s);
            inter_name *gpr_to_ask = Kinds::Behaviour::get_explicit_I6_GPR_iname(R);
            if (gpr_to_ask == NULL) gpr_to_ask = RTKinds::get_kind_GPR_iname(R);
            Produce::inv_call_iname(Emit::tree(), gpr_to_ask);
        Produce::up(Emit::tree());

        Produce::inv_primitive(Emit::tree(), IF_BIP);
        Produce::down(Emit::tree());
            Produce::inv_primitive(Emit::tree(), EQ_BIP);
            Produce::down(Emit::tree());
                Produce::val_symbol(Emit::tree(), K_value, v_s);
                Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(GPR_NUMBER_HL));
            Produce::up(Emit::tree());
            Produce::code(Emit::tree());
            Produce::down(Emit::tree());
                Produce::inv_primitive(Emit::tree(), STORE_BIP);
                Produce::down(Emit::tree());
                    Produce::ref_symbol(Emit::tree(), K_value, n_s);
                    Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(NOUN_HL));
                Produce::up(Emit::tree());
                Produce::inv_primitive(Emit::tree(), STORE_BIP);
                Produce::down(Emit::tree());
                    Produce::ref_iname(Emit::tree(), K_object, Hierarchy::find(NOUN_HL));
                    Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(PARSED_NUMBER_HL));
                Produce::up(Emit::tree());

                Produce::inv_primitive(Emit::tree(), IF_BIP);
                Produce::down(Emit::tree());
                    Produce::inv_primitive(Emit::tree(), NOT_BIP);
                    Produce::down(Emit::tree());
                        Calculus::Deferrals::emit_test_if_var_matches_description(noun_var, nft->the_filter);
                    Produce::up(Emit::tree());
                    Produce::code(Emit::tree());
                    Produce::down(Emit::tree());
                        Produce::inv_primitive(Emit::tree(), STORE_BIP);
                        Produce::down(Emit::tree());
                            Produce::ref_symbol(Emit::tree(), K_value, v_s);
                            Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(GPR_FAIL_HL));
                        Produce::up(Emit::tree());
                    Produce::up(Emit::tree());
                Produce::up(Emit::tree());

                Produce::inv_primitive(Emit::tree(), STORE_BIP);
                Produce::down(Emit::tree());
                    Produce::ref_iname(Emit::tree(), K_object, Hierarchy::find(NOUN_HL));
                    Produce::val_symbol(Emit::tree(), K_value, n_s);
                Produce::up(Emit::tree());
            Produce::up(Emit::tree());
        Produce::up(Emit::tree());

        Produce::inv_primitive(Emit::tree(), RETURN_BIP);
        Produce::down(Emit::tree());
            Produce::val_symbol(Emit::tree(), K_value, v_s);
        Produce::up(Emit::tree());
    } else if (nft->global_scope_flag) {
        inter_symbol *obj_s = LocalVariables::add_internal_local_c_as_symbol(I"obj", "object loop variable");
        inter_symbol *o2_s = LocalVariables::add_internal_local_c_as_symbol(I"o2", "saved value of noun");

        Produce::inv_primitive(Emit::tree(), SWITCH_BIP);
        Produce::down(Emit::tree());
            Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(SCOPE_STAGE_HL));
            Produce::code(Emit::tree());
            Produce::down(Emit::tree());
                Produce::inv_primitive(Emit::tree(), CASE_BIP);
                Produce::down(Emit::tree());
                    Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 1);
                    Produce::code(Emit::tree());
                    Produce::down(Emit::tree());
                        if (nft->any_things_flag) Produce::rtrue(Emit::tree());
                        else Produce::rfalse(Emit::tree());
                    Produce::up(Emit::tree());
                Produce::up(Emit::tree());
                Produce::inv_primitive(Emit::tree(), CASE_BIP);
                Produce::down(Emit::tree());
                    Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 2);
                    Produce::code(Emit::tree());
                    Produce::down(Emit::tree());
                        Produce::inv_primitive(Emit::tree(), STORE_BIP);
                        Produce::down(Emit::tree());
                            Produce::ref_symbol(Emit::tree(), K_value, obj_s);
                            Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(NOUN_HL));
                        Produce::up(Emit::tree());

                        Produce::inv_primitive(Emit::tree(), OBJECTLOOP_BIP);
                        Produce::down(Emit::tree());
                            Produce::ref_iname(Emit::tree(), K_object, Hierarchy::find(NOUN_HL));
                            Produce::val_iname(Emit::tree(), K_value, RTKinds::I6_classname(K_object));
                            Calculus::Deferrals::emit_test_if_var_matches_description(noun_var, nft->the_filter);

                            Produce::code(Emit::tree());
                            Produce::down(Emit::tree());
                                Produce::inv_primitive(Emit::tree(), STORE_BIP);
                                Produce::down(Emit::tree());
                                    Produce::ref_symbol(Emit::tree(), K_value, o2_s);
                                    Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(NOUN_HL));
                                Produce::up(Emit::tree());
                                Produce::inv_primitive(Emit::tree(), STORE_BIP);
                                Produce::down(Emit::tree());
                                    Produce::ref_iname(Emit::tree(), K_object, Hierarchy::find(NOUN_HL));
                                    Produce::val_symbol(Emit::tree(), K_value, obj_s);
                                Produce::up(Emit::tree());

                                Produce::inv_primitive(Emit::tree(), STORE_BIP);
                                Produce::down(Emit::tree());
                                    Produce::ref_iname(Emit::tree(), K_object, Hierarchy::find(SUPPRESS_SCOPE_LOOPS_HL));
                                    Produce::val(Emit::tree(), K_truth_state, LITERAL_IVAL, 1);
                                Produce::up(Emit::tree());

                                Produce::inv_call_iname(Emit::tree(), Hierarchy::find(PLACEINSCOPE_HL));
                                Produce::down(Emit::tree());
                                    Produce::val_symbol(Emit::tree(), K_value, o2_s);
                                    Produce::val(Emit::tree(), K_truth_state, LITERAL_IVAL, 1);
                                Produce::up(Emit::tree());

                                Produce::inv_primitive(Emit::tree(), STORE_BIP);
                                Produce::down(Emit::tree());
                                    Produce::ref_iname(Emit::tree(), K_object, Hierarchy::find(SUPPRESS_SCOPE_LOOPS_HL));
                                    Produce::val(Emit::tree(), K_truth_state, LITERAL_IVAL, 0);
                                Produce::up(Emit::tree());

                                Produce::inv_primitive(Emit::tree(), STORE_BIP);
                                Produce::down(Emit::tree());
                                    Produce::ref_iname(Emit::tree(), K_object, Hierarchy::find(NOUN_HL));
                                    Produce::val_symbol(Emit::tree(), K_value, o2_s);
                                Produce::up(Emit::tree());
                            Produce::up(Emit::tree());
                        Produce::up(Emit::tree());

                        Produce::inv_primitive(Emit::tree(), STORE_BIP);
                        Produce::down(Emit::tree());
                            Produce::ref_iname(Emit::tree(), K_object, Hierarchy::find(NOUN_HL));
                            Produce::val_symbol(Emit::tree(), K_value, obj_s);
                        Produce::up(Emit::tree());

                    Produce::up(Emit::tree());
                Produce::up(Emit::tree());
                Produce::inv_primitive(Emit::tree(), CASE_BIP);
                Produce::down(Emit::tree());
                    Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 3);
                    Produce::code(Emit::tree());
                    Produce::down(Emit::tree());
                        Produce::inv_primitive(Emit::tree(), STORE_BIP);
                        Produce::down(Emit::tree());
                            Produce::ref_iname(Emit::tree(), K_object, Hierarchy::find(NEXTBEST_ETYPE_HL));
                            Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(NOTINCONTEXTPE_HL));
                        Produce::up(Emit::tree());
                        Produce::inv_primitive(Emit::tree(), RETURN_BIP);
                        Produce::down(Emit::tree());
                            Produce::val(Emit::tree(), K_number, LITERAL_IVAL, (inter_ti) (-1));
                        Produce::up(Emit::tree());
                    Produce::up(Emit::tree());
                Produce::up(Emit::tree());
            Produce::up(Emit::tree());
        Produce::up(Emit::tree());
    } else {
        inter_symbol *x_s = LocalVariables::add_internal_local_c_as_symbol(I"x", "saved value of noun");
        Produce::inv_primitive(Emit::tree(), STORE_BIP);
        Produce::down(Emit::tree());
            Produce::ref_symbol(Emit::tree(), K_value, x_s);
            Produce::val_iname(Emit::tree(), K_object, Hierarchy::find(NOUN_HL));
        Produce::up(Emit::tree());

        Produce::inv_primitive(Emit::tree(), RETURN_BIP);
        Produce::down(Emit::tree());
            if (Specifications::to_proposition(nft->the_filter)) {
                Propositions::Checker::type_check(Specifications::to_proposition(nft->the_filter), Propositions::Checker::tc_no_problem_reporting());
                Calculus::Deferrals::emit_test_of_proposition(
                    noun_var, Specifications::to_proposition(nft->the_filter));
            } else
                Calculus::Deferrals::emit_test_if_var_matches_description(noun_var, nft->the_filter);
        Produce::up(Emit::tree());
    }
    Routines::end(save);
    NonlocalVariables::set_kind(Inter_noun_VAR, K);
}

§3. Access via ID. For now, though, these are perhaps strangely accessed by ID number. (Because the parse_node structure can't conveniently be annotated with pointers, that's why.)

int too_late_for_further_NFTs = FALSE;

int UnderstandFilterTokens::new_id(parse_node *spec, int global_scope, int any_things) {
    if (too_late_for_further_NFTs)
        StandardProblems::sentence_problem(Task::syntax_tree(), _p_(BelievedImpossible),
            "complicated instructions on understanding the player's command "
            "are not allowed in the past tense",
            "for instance by being applied to several previous turns in a row.");

    kind *K = Specifications::to_kind(spec);
    if ((Kinds::Behaviour::is_object(K) == FALSE) && (Kinds::Behaviour::request_I6_GPR(K) == FALSE) && (global_scope))
        StandardProblems::sentence_problem(Task::syntax_tree(), _p_(BelievedImpossible),
            "this is a kind of value I can't understand in command grammar",
            "so the '[any ...]' part will have to go.");

    return UnderstandFilterTokens::nft_new(spec, global_scope, any_things)->allocation_id;
}

void UnderstandFilterTokens::compile_id(int id) {
    noun_filter_token *nft;
    LOOP_OVER(nft, noun_filter_token)
        if (nft->allocation_id == id) {
            if (nft->parse_using_gpr) Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(GPR_TT_HL));
            else if (nft->global_scope_flag) Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(SCOPE_TT_HL));
            else Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(ROUTINEFILTER_TT_HL));
            Produce::val_iname(Emit::tree(), K_value, nft->nft_iname);
        }
}

void UnderstandFilterTokens::emit_id(int id) {
    noun_filter_token *nft;
    LOOP_OVER(nft, noun_filter_token)
        if (nft->allocation_id == id) {
            inter_ti annot = 0;
            if (nft->parse_using_gpr == FALSE) {
                if (nft->global_scope_flag) annot = SCOPE_FILTER_IANN;
                else annot = NOUN_FILTER_IANN;
            }
            inter_name *iname = UnderstandFilterTokens::nft_compile_routine_iname(nft);
            if (annot != 0)
                if (Produce::read_annotation(iname, annot) != 1)
                    Produce::annotate_i(iname, annot, 1);
            Emit::array_iname_entry(iname);
        }
}

§4. Compiling everything. Having referred to these filter routines, we need to compile them.

void UnderstandFilterTokens::compile(void) {
    noun_filter_token *nft;
    LOOP_OVER(nft, noun_filter_token)
        if (nft->nft_compiled == FALSE) {
            current_sentence = nft->nft_created_at;
            UnderstandFilterTokens::nft_compile_routine(nft);
            nft->nft_compiled = TRUE;
        }
     too_late_for_further_NFTs = TRUE;
}