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;
        MEMORY_MANAGEMENT
    } noun_filter_token;

The structure noun_filter_token is private to this section.

§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 *PL::Parsing::Tokens::Filters::nft_new(parse_node *spec, int global_scope, int any_things) {
        pcalc_prop *prop = Specifications::to_proposition(spec);
        if ((prop) && (Calculus::Variables::number_free(prop) != 1)) {
            LOG("So $P and $D\n", spec, prop);
            Problems::Issue::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 *PL::Parsing::Tokens::Filters::nft_compile_routine_iname(noun_filter_token *nft) {
        return nft->nft_iname;
    }

    void PL::Parsing::Tokens::Filters::nft_compile_routine(noun_filter_token *nft) {
        parse_node *noun_var = Lvalues::new_actual_NONLOCAL_VARIABLE(I6_noun_VAR);
        kind *R = Specifications::to_kind(nft->the_filter);
        kind *K = NonlocalVariables::kind(I6_noun_VAR);
        NonlocalVariables::set_kind(I6_noun_VAR, R);
        if (Kinds::Compare::le(R, K_object) == 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 = Kinds::RunTime::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, Kinds::RunTime::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_t) (-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)) {
                    Calculus::Propositions::Checker::type_check(Specifications::to_proposition(nft->the_filter), Calculus::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(I6_noun_VAR, K);
    }

The function PL::Parsing::Tokens::Filters::nft_new is used in §3.

The function PL::Parsing::Tokens::Filters::nft_compile_routine_iname is used in §3.

The function PL::Parsing::Tokens::Filters::nft_compile_routine is used in §4.

§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 PL::Parsing::Tokens::Filters::new_id(parse_node *spec, int global_scope, int any_things) {
        if (too_late_for_further_NFTs)
            Problems::Issue::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::Compare::le(K, K_object) == FALSE) && (Kinds::Behaviour::request_I6_GPR(K) == FALSE) && (global_scope))
            Problems::Issue::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 PL::Parsing::Tokens::Filters::nft_new(spec, global_scope, any_things)->allocation_id;
    }

    void PL::Parsing::Tokens::Filters::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 PL::Parsing::Tokens::Filters::emit_id(int id) {
        noun_filter_token *nft;
        LOOP_OVER(nft, noun_filter_token)
            if (nft->allocation_id == id) {
                inter_t 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 = PL::Parsing::Tokens::Filters::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);
            }
    }

The function PL::Parsing::Tokens::Filters::new_id is used in 5/gt2 (§11.2, §11.4).

The function PL::Parsing::Tokens::Filters::compile_id is used in 5/gt2 (§12).

The function PL::Parsing::Tokens::Filters::emit_id is used in 5/gt2 (§12).

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

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

The function PL::Parsing::Tokens::Filters::compile appears nowhere else.