To recognise certain kind names as familiar built-in ones.


§1. In the Inform source code, we're clearly going to need to refer to some kinds explicitly, and we need a way to do that. We adopt two naming conventions:

We will now define all of the K_... and CON_... used by the core of Inform. The kinds module does not need all of these to be created: for example, in a Basic Inform compilation, stored action never will be. The variable K_stored_action will then remain NULL, but will also never be used for anything, so no harm is done.

§2. We begin with the protocol-like "kinds of kinds", the superheroes of the kinds world:

kind *K_value = NULL;
kind *K_stored_value = NULL;
kind *K_pointer_value = NULL;
kind *K_sayable_value = NULL;
kind *K_understandable_value = NULL;
kind *K_arithmetic_value = NULL;
kind *K_real_arithmetic_value = NULL;
kind *K_enumerated_value = NULL;

§3. Next, some awkward punctuation-like special base kinds needed in order to construct things. These are used in combination to make tuples, that is, collections \((K_1, K_2, ..., K_n)\) of kinds of value.

kind_constructor *CON_TUPLE_ENTRY = NULL;
kind *K_void = NULL;
kind_constructor *CON_VOID = NULL;

§4. Thus we store \(()\) (the empty tuple) as CON_VOID, we store \((A)\) as:

    CON_TUPLE_ENTRY
        A
        CON_VOID

so that \(A\) and \((A)\) are distinguishable, and continue. For example, \((A, B, C)\) is:

    CON_TUPLE_ENTRY
        A
        CON_TUPLE_ENTRY
            B
            CON_TUPLE_ENTRY
                C
                CON_VOID

This traditional LISP-like device enables us to store tuples of arbitrary size without need for any constructor of arity greater than 2.

§5. K_unknown plays no role in the type system — as noted in What This Module Does, the way to express "unknown kind" is to use the value NULL. The purpose of K_unknown is purely so that run-time code generated by Inform has a way to mark out empty lists as having entries of no known kind.

kind *K_nil = NULL;
kind_constructor *CON_NIL = NULL;
kind *K_unknown = NULL;
kind_constructor *CON_UNKNOWN = NULL;

§6. CON_INTERMEDIATE is used to represent kinds which are brought into being through uncompleted arithmetic operations: see Dimensions for a full discussion.

kind_constructor *CON_INTERMEDIATE = NULL;

§7. CON_KIND_VARIABLE is used to formally represent kind variables, such as the letter K:

kind_constructor *CON_KIND_VARIABLE = NULL;

§8. So much for the exotica: back onto familiar ground for anyone who uses Inform. Some standard kinds follow. Some belong only to features; if the feature in question is inactive, they will remain NULL and do nothing.

kind *K_equation = NULL;
kind *K_grammatical_gender = NULL;
kind *K_natural_language = NULL;
kind *K_number = NULL;
kind *K_object = NULL;
kind *K_real_number = NULL;
kind *K_response = NULL;
kind *K_snippet = NULL;
kind *K_table = NULL;
kind *K_text = NULL;
kind *K_truth_state = NULL;
kind *K_unicode_character = NULL;
kind *K_use_option = NULL;
kind *K_verb = NULL;
kind *K_version_number = NULL;

§9. And here are two more standard kinds, but which most Inform users don't realise are there, because they are omitted from the Kinds index:

kind *K_rulebook_outcome = NULL;
kind *K_understanding = NULL;

§10. Finally, the standard set of constructors:

kind_constructor *CON_list_of = NULL;
kind_constructor *CON_description = NULL;
kind_constructor *CON_relation = NULL;
kind_constructor *CON_rule = NULL;
kind_constructor *CON_rulebook = NULL;
kind_constructor *CON_activity = NULL;
kind_constructor *CON_phrase = NULL;
kind_constructor *CON_property = NULL;
kind_constructor *CON_table_column = NULL;
kind_constructor *CON_combination = NULL;
kind_constructor *CON_variable = NULL;

§11. Kind names in Inter code. We defined some "constant" kinds and constructors above, to provide values like K_number for use in this C source code. We will also want to refer to these kinds in the Inter code generated by Inform, where they will have identifiers such as NUMBER_TY.

So we need a way of pairing up names in these two source codes, and here it is. There is no need for speed here.

define IDENTIFIERS_CORRESPOND(text_of_I6_name, what_have_you)
    if ((sn) && (Str::eq_narrow_string(sn, text_of_I6_name))) return what_have_you;
kind_constructor **FamiliarKinds::known_con(text_stream *sn) {
    IDENTIFIERS_CORRESPOND("ACTIVITY_TY", &CON_activity);
    IDENTIFIERS_CORRESPOND("COMBINATION_TY", &CON_combination);
    IDENTIFIERS_CORRESPOND("DESCRIPTION_OF_TY", &CON_description);
    IDENTIFIERS_CORRESPOND("INTERMEDIATE_TY", &CON_INTERMEDIATE);
    IDENTIFIERS_CORRESPOND("KIND_VARIABLE_TY", &CON_KIND_VARIABLE);
    IDENTIFIERS_CORRESPOND("LIST_OF_TY", &CON_list_of);
    IDENTIFIERS_CORRESPOND("PHRASE_TY", &CON_phrase);
    IDENTIFIERS_CORRESPOND("NIL_TY", &CON_NIL);
    IDENTIFIERS_CORRESPOND("VOID_TY", &CON_VOID);
    IDENTIFIERS_CORRESPOND("PROPERTY_TY", &CON_property);
    IDENTIFIERS_CORRESPOND("RELATION_TY", &CON_relation);
    IDENTIFIERS_CORRESPOND("RULE_TY", &CON_rule);
    IDENTIFIERS_CORRESPOND("RULEBOOK_TY", &CON_rulebook);
    IDENTIFIERS_CORRESPOND("TABLE_COLUMN_TY", &CON_table_column);
    IDENTIFIERS_CORRESPOND("TUPLE_ENTRY_TY", &CON_TUPLE_ENTRY);
    IDENTIFIERS_CORRESPOND("UNKNOWN_TY", &CON_UNKNOWN);
    IDENTIFIERS_CORRESPOND("VARIABLE_TY", &CON_variable);
    return NULL;
}

kind **FamiliarKinds::known_kind(text_stream *sn) {
    IDENTIFIERS_CORRESPOND("ARITHMETIC_VALUE_TY", &K_arithmetic_value);
    IDENTIFIERS_CORRESPOND("ENUMERATED_VALUE_TY", &K_enumerated_value);
    IDENTIFIERS_CORRESPOND("EQUATION_TY", &K_equation);
    IDENTIFIERS_CORRESPOND("TEXT_TY", &K_text);
    IDENTIFIERS_CORRESPOND("NUMBER_TY", &K_number);
    IDENTIFIERS_CORRESPOND("OBJECT_TY", &K_object);
    IDENTIFIERS_CORRESPOND("POINTER_VALUE_TY", &K_pointer_value);
    IDENTIFIERS_CORRESPOND("STORED_VALUE_TY", &K_stored_value);
    IDENTIFIERS_CORRESPOND("REAL_ARITHMETIC_VALUE_TY", &K_real_arithmetic_value);
    IDENTIFIERS_CORRESPOND("REAL_NUMBER_TY", &K_real_number);
    IDENTIFIERS_CORRESPOND("RESPONSE_TY", &K_response);
    IDENTIFIERS_CORRESPOND("RULEBOOK_OUTCOME_TY", &K_rulebook_outcome);
    IDENTIFIERS_CORRESPOND("SAYABLE_VALUE_TY", &K_sayable_value);
    IDENTIFIERS_CORRESPOND("UNDERSTANDABLE_VALUE_TY", &K_understandable_value);
    IDENTIFIERS_CORRESPOND("SNIPPET_TY", &K_snippet);
    IDENTIFIERS_CORRESPOND("TABLE_TY", &K_table);
    IDENTIFIERS_CORRESPOND("TRUTH_STATE_TY", &K_truth_state);
    IDENTIFIERS_CORRESPOND("UNDERSTANDING_TY", &K_understanding);
    IDENTIFIERS_CORRESPOND("UNKNOWN_TY", &K_unknown);
    IDENTIFIERS_CORRESPOND("UNICODE_CHARACTER_TY", &K_unicode_character);
    IDENTIFIERS_CORRESPOND("USE_OPTION_TY", &K_use_option);
    IDENTIFIERS_CORRESPOND("VALUE_TY", &K_value);
    IDENTIFIERS_CORRESPOND("VERB_TY", &K_verb);
    IDENTIFIERS_CORRESPOND("VERSION_NUMBER_TY", &K_version_number);
    IDENTIFIERS_CORRESPOND("NIL_TY", &K_nil);
    IDENTIFIERS_CORRESPOND("VOID_TY", &K_void);
    return NULL;
}

int FamiliarKinds::is_known(text_stream *sn) {
    if (FamiliarKinds::known_con(sn)) return TRUE;
    if (FamiliarKinds::known_kind(sn)) return TRUE;
    return FALSE;
}

§12. Kind names in source text. Inform creates the "natural language" kind in source text, not by loading it from a file, but we still need to refer to it in the compiler. Similarly for "grammatical gender". The others here are not referred to in the compiler, but are indexed together.

<notable-linguistic-kinds> ::=
    natural language |
    grammatical gender |
    grammatical tense |
    narrative viewpoint |
    grammatical case

§13.

void FamiliarKinds::notice_new_kind(kind *K, wording W) {
    if (<notable-linguistic-kinds>(W)) {
        KindConstructors::mark_as_linguistic(K->construct);
        switch (<<r>>) {
            case 0: K_natural_language = K;
                #ifdef NOTIFY_NATURAL_LANGUAGE_KINDS_CALLBACK
                NOTIFY_NATURAL_LANGUAGE_KINDS_CALLBACK(K);
                #endif
                break;
            case 1: K_grammatical_gender = K; break;
        }
    }
}