What a single sense of an adjective can apply to: perhaps a kind or an instance.


§1. Introduction. Each adjective_meaning structure contains one of these. The "domain" of an adjective meaning is the set of values to which it can validly apply. For example, the meaning of "odd" for numbers has the set of all numbers as its domain, whereas the sense here:

Mrs Elspeth Spong can be odd, eccentric or mildly dotty.

has only a single instance as domain — Mrs Spong herself.

We represent this as an inference subject, since that can represent either an instance or a base kind; but note also the domain_kind field. At first sight this is redundant, but in fact it isn't, since it enables us to define adjectives on non-base kinds such as "lists of scenes".

This part of Inform is plagued with timing difficulties, however, where the domain of an adjective is given to us as text which we cannot yet understand. For example, if the author defines "A boojum is odd if it is in a dark room", and "boojum" is a kind yet to be created, we're stuck.

We therefore store such text until later, when the domain can be "determined".

typedef struct adjective_domain_data {
    struct wording domain_text;  text given by author about the domain
    struct inference_subject *domain_infs;  what domain the defn applies to
    struct kind *domain_kind;  what kind of values
    int currently_determining;  are we currently working this out?
    int problems_thrown;  number of problems thrown about this domain
} adjective_domain_data;

void AdjectiveMeaningDomains::log(adjective_domain_data *domain) {
    if (domain->domain_infs == NULL) LOG("domain:'%W'?", domain->domain_text);
    else LOG("domain:($j, %u)", domain->domain_infs, domain->domain_kind);
}

§2. If all we have is text:

void AdjectiveMeaningDomains::set_from_text(adjective_meaning *am, wording W) {
    am->domain = AdjectiveMeaningDomains::new_from_text(W);
    AdjectiveMeaningDomains::determine_if_possible(am);
}
adjective_domain_data AdjectiveMeaningDomains::new_from_text(wording W) {
    adjective_domain_data domain;
    domain.domain_text = W;
    domain.domain_infs = NULL;
    domain.domain_kind = NULL;
    domain.currently_determining = FALSE;
    domain.problems_thrown = 0;
    return domain;
}

§3. If in fact we know exactly what domain we want, these functions can be used. Determination on these domains then does nothing, because they are already pre-determined.

Note that we round up the kind to "object" if it's more specialised than that — say, if it's "door" — because run-time rather than compile-time disambiguation is used when applying adjectives to objects.

void AdjectiveMeaningDomains::set_from_kind(adjective_meaning *am, kind *K) {
    am->domain = AdjectiveMeaningDomains::new_from_kind(K);
}
adjective_domain_data AdjectiveMeaningDomains::new_from_kind(kind *K) {
    adjective_domain_data domain = AdjectiveMeaningDomains::new_from_text(EMPTY_WORDING);
    if ((K == NULL) || (Kinds::Behaviour::is_object(K))) K = K_object;
    domain.domain_infs = KindSubjects::from_kind(K);
    domain.domain_kind = K;
    return domain;
}

void AdjectiveMeaningDomains::set_from_instance(adjective_meaning *am, instance *I) {
    am->domain = AdjectiveMeaningDomains::new_from_instance(I);
}
adjective_domain_data AdjectiveMeaningDomains::new_from_instance(instance *I) {
    adjective_domain_data domain = AdjectiveMeaningDomains::new_from_text(EMPTY_WORDING);
    if (I == NULL) return AdjectiveMeaningDomains::new_from_kind(K_object);
    domain.domain_infs = Instances::as_subject(I);
    domain.domain_kind = Kinds::weaken(Instances::to_kind(I), K_object);
    return domain;
}

§4. Determination. So this is where we determine the domain. Sometimes we allow this to fail for timing reasons, sometimes we require that it must not fail:

void AdjectiveMeaningDomains::determine_all(void) {
    adjective *adj;
    LOOP_OVER(adj, adjective) {
        adjective_meaning *am;
        LOOP_OVER_LINKED_LIST(am, adjective_meaning, adj->adjective_meanings.in_defn_order)
            AdjectiveMeaningDomains::determine(am);
        AdjectiveAmbiguity::sort(adj);
    }
}
void AdjectiveMeaningDomains::determine(adjective_meaning *am) {
    AdjectiveMeaningDomains::determine_inner(am, FALSE);
}
void AdjectiveMeaningDomains::determine_if_possible(adjective_meaning *am) {
    AdjectiveMeaningDomains::determine_inner(am, TRUE);
}

§5. This variant is useful to catch circularities like "A big container is big if...", where the adjective's domain directly or indirectly involves itself:

int AdjectiveMeaningDomains::determine_avoiding_circularity(adjective_meaning *am) {
    if (am->domain.currently_determining) Issue a problem for a circularity5.1;
    am->domain.currently_determining = TRUE;
    AdjectiveMeaningDomains::determine_if_possible(am);
    am->domain.currently_determining = FALSE;
    return TRUE;
}

§5.1. Issue a problem for a circularity5.1 =

    if (problem_count == 0) {
        current_sentence = am->defined_at;
        StandardProblems::adjective_problem(Task::syntax_tree(), _p_(PM_AdjectiveCircular),
            am->indexing_text, am->domain.domain_text,
            "this doesn't really define an adjective",
            "because it seems to be circular - it is involved in its own definition.");
    }
    return FALSE;

§6.

void AdjectiveMeaningDomains::determine_inner(adjective_meaning *am,
    int suppress_problems) {
    if (am->domain.domain_infs) return;  already determined

    current_sentence = am->defined_at;
    if (Wordings::empty(am->domain.domain_text))
        internal_error("undeclared domain kind for AM");
    parse_node *supplied = NULL;
    if (<s-type-expression>(am->domain.domain_text)) supplied = <<rp>>;
    if (supplied == NULL) Reject domain of adjective6.1;
    Reject domain of adjective unless a kind of value or description of objects6.4;
    kind *K = NULL;
    if (Specifications::is_condition(supplied)) {
        if (Specifications::to_kind(supplied)) K = Specifications::to_kind(supplied);
        else K = K_object;
        Reject domain of adjective if it is a set of objects which may vary in play6.5;
    } else if (Rvalues::is_rvalue(supplied))
        K = Rvalues::to_kind(supplied);
    if (K == NULL) Reject domain of adjective6.1;
    if (Kinds::Behaviour::is_kind_of_kind(K)) Reject domain as vague6.2;
    if ((K_understanding) && (Kinds::eq(K, K_understanding))) Reject domain as topic6.3;
    instance *I = Rvalues::to_object_instance(supplied);
    if (I) supplied = Rvalues::from_instance(I);
    else if (Kinds::Behaviour::is_subkind_of_object(K))
        supplied = Specifications::from_kind(K);
    am->domain.domain_infs = InferenceSubjects::from_specification(supplied);
    am->domain.domain_kind = K;
}

§6.1. Note that we throw only one problem message per AM, as otherwise duplication can't be avoided.

Reject domain of adjective6.1 =

    if ((suppress_problems) || (am->domain.problems_thrown++ > 0)) return;
    current_sentence = am->defined_at;
    StandardProblems::adjective_problem(Task::syntax_tree(),
        _p_(PM_AdjDomainUnknown),
        am->indexing_text, am->domain.domain_text,
        "this isn't a thing, a kind of thing or a kind of value",
        "and indeed doesn't have any meaning I can make sense of.");
    return;

§6.2. Reject domain as vague6.2 =

    if ((suppress_problems) || (am->domain.problems_thrown++ > 0)) return;
    current_sentence = am->defined_at;
    StandardProblems::adjective_problem(Task::syntax_tree(),
        _p_(PM_AdjDomainVague),
        am->indexing_text, am->domain.domain_text,
        "this isn't allowed as the domain of a definition",
        "since it potentially describes many different kinds, not just one.");
    return;

§6.3. Reject domain as topic6.3 =

    if ((suppress_problems) || (am->domain.problems_thrown++ > 0)) return;
    current_sentence = am->defined_at;
    StandardProblems::adjective_problem(Task::syntax_tree(),
        _p_(PM_AdjDomainTopic),
        am->indexing_text, am->domain.domain_text,
        "this isn't allowed as the domain of a definition",
        "because 'topic' doesn't behave the way other kinds of value do when "
        "it comes to making comparisons.");
    return;

§6.4. Reject domain of adjective unless a kind of value or description of objects6.4 =

    if ((Node::is(supplied, CONSTANT_NT)) &&
        (Specifications::is_description_like(supplied) == FALSE) &&
        (Rvalues::to_instance(supplied) == NULL)) {
        if ((suppress_problems) || (am->domain.problems_thrown++ > 0)) return;
        current_sentence = am->defined_at;
        StandardProblems::adjective_problem(Task::syntax_tree(),
            _p_(PM_AdjDomainSurreal),
            am->indexing_text, am->domain.domain_text,
            "this isn't allowed as the domain of a definition",
            "since adjectives like this can be applied only to specific things, "
            "kinds of things or kinds of values: so 'Definition: a door is ajar "
            "if...' is fine, because a door is a kind of thing, and 'Definition: "
            "a number is prime if ...' is fine too, but 'Definition: 5 is prime "
            "if ...' is not allowed.");
        return;
    }

§6.5. Reject domain of adjective if it is a set of objects which may vary in play6.5 =

    if (Descriptions::is_qualified(supplied)) {
        if (am->domain.problems_thrown++ > 0) return;
        current_sentence = am->defined_at;
        StandardProblems::adjective_problem(Task::syntax_tree(),
            _p_(PM_AdjDomainSlippery),
            am->indexing_text, am->domain.domain_text,
            "this is slippery",
            "because it can change during play. Definitions can only be "
            "made in cases where it's clear for any given value or object "
            "what definition will apply. For instance, 'Definition: a "
            "door is shiny if ...' is fine, but 'Definition: an open "
            "door is shiny if ...' is not allowed - Inform wouldn't know "
            "whether or not to apply it to the Big Blue Door (say), since "
            "it would only apply some of the time.");
        return;
    }

§7. Finally, then, we can read what we currently believe the kind of the domain is with the following. Note that:

kind *AdjectiveMeaningDomains::get_kind(adjective_meaning *am) {
    if (am == NULL) return NULL;
    if (am->domain.domain_infs == NULL) return NULL;
    return am->domain.domain_kind;
}

§8. And similarly the subject:

inference_subject *AdjectiveMeaningDomains::get_subject(adjective_meaning *am) {
    if (am == NULL) return NULL;
    return am->domain.domain_infs;
}

§9. Matching and sorting. "Matching" is used to tell when a meaning can be applied to a term of a given kind, or inference subject. It comes in two flavours: weak and strong.

int AdjectiveMeaningDomains::weak_match(kind *K1, adjective_meaning *am) {
    kind *K2 = AdjectiveMeaningDomains::get_kind(am);
    if (RTKindIDs::weak_iname(K1) == RTKindIDs::weak_iname(K2)) return TRUE;
    return FALSE;
}

int AdjectiveMeaningDomains::strong_match(kind *K1, inference_subject *infs,
    adjective_meaning *am) {
    if (AdjectiveMeaningDomains::weak_match(K1, am)) {
        instance *I = InstanceSubjects::to_object_instance(infs);
        if (I == NULL) return TRUE;
        while (infs) {
            if (am->domain.domain_infs == infs) return TRUE;
            infs = InferenceSubjects::narrowest_broader_subject(infs);
        }
    }
    return FALSE;
}

§10. The following sorting function is used in the process of sorting the meanings of an adjective into precedence order — see AdjectiveAmbiguity::sort. It takes two domains \(D_1\) and \(D_2\), and returns

int AdjectiveMeaningDomains::cmp(adjective_domain_data *domain1,
    adjective_domain_data *domain2) {
    if ((domain1->domain_infs) && (domain2->domain_infs == NULL)) return 1;
    if ((domain1->domain_infs == NULL) && (domain2->domain_infs)) return -1;

    if (InferenceSubjects::is_strictly_within(
        domain1->domain_infs, domain2->domain_infs)) return 1;
    if (InferenceSubjects::is_strictly_within(
        domain2->domain_infs, domain1->domain_infs)) return -1;

    kind *K1 = KindSubjects::to_nonobject_kind(domain1->domain_infs);
    kind *K2 = KindSubjects::to_nonobject_kind(domain2->domain_infs);
    if ((K1) && (K2)) {
        int c1 = Kinds::compatible(K1, K2);
        int c2 = Kinds::compatible(K2, K1);
        if ((c1 == ALWAYS_MATCH) && (c2 != ALWAYS_MATCH)) return 1;
        if ((c1 != ALWAYS_MATCH) && (c2 == ALWAYS_MATCH)) return -1;
    }
    return 0;
}