To handle sentences with primary verb "to be" or "to have".


§1. Definitions.

§2. "To Be and To Have" ought to be the name of an incomprehensible book by Sartre which dismisses Heidegger's seminal "To Have and To Be", or something like that, but instead it is the name of a section which contains the most important sentence handler: the one for assertions.

This will turn out to be quite a lot of work, occupying four sections of code in all. For etymological reasons, the English verb "to be" is a mixture of several different verbs which have blurred together into one: consider "I am 5", "I am happy" and "I am Chloe". Even the definition occupies some 12 columns of the "Oxford English Dictionary" and they make interesting reading in clarifying the problem. Most computer programming languages implement only = and ==, which correspond to OED's meaning 10, "to exist as the thing known by a certain name; to be identical with". But Inform implements a much broader set of meanings. For example, its distinction between spatial and property knowledge reflects the OED's distinction between meanings 5a ("to have or occupy a place somewhere") and 9b ("to have a place among the things distinguished by a specified quality") respectively.

§3. Here, and in the sections which follow, we conventionally write px and py for the subtrees representing subject and object sides of the verb. Thus

The white marble is in the bamboo box.

will result in px representing "white marble" and py "in the bamboo box" (not just a leaf, since it will be a tree showing the containment relationship as well as the noun).

    sentence_handler ASSERT_SH_handler = { SENTENCE_NT, ASSERT_VB, 0, Assertions::Copular::assertion };

    void Assertions::Copular::assertion(parse_node *pv) {
        if (ParseTree::int_annotation(pv->down, possessive_verb_ANNOT))
            Assertions::Copular::to_have(pv);
        else
            Assertions::Copular::to_be(pv);
    }

    void Assertions::Copular::to_be(parse_node *pv) {
        parse_node *px = pv->down->next;
        parse_node *py = pv->down->next->next;
        if ((Wordings::length(ParseTree::get_text(px)) > 1)
            && (Vocabulary::test_flags(
                Wordings::first_wn(ParseTree::get_text(px)), TEXT_MC+TEXTWITHSUBS_MC))) {
            Problems::Issue::sentence_problem(Task::syntax_tree(), _p_(PM_TextNotClosing),
                "it looks as if perhaps you did not intend that to read as a "
                "single sentence",
                "and possibly the text in quotes was supposed to stand as "
                "as a sentence on its own? (The convention is that if text "
                "ends in a full stop, exclamation or question mark, perhaps "
                "with a close bracket or quotation mark involved as well, then "
                "that punctuation mark also closes the sentence to which the "
                "text belongs: but otherwise the words following the quoted "
                "text are considered part of the same sentence.)");
            return;
        }
        Assertions::Copular::make_assertion(px, py);
    }

The function Assertions::Copular::assertion appears nowhere else.

The function Assertions::Copular::to_be is used in §4.

§4. "To have" may seem as if it ought to be an entirely different verb from "to be", but in fact they have heavily overlapping meanings, and we will implement them with a great deal of common code. (English is unusual in the way that "to be" has taken over some of the functions which "to have" has in other languages — compare the French "j'ai fatigu\'e", literally "I have tired" rather than "I am tired", which is arguably more logical since it talks about the possession of a property.)

On traverse 1 we therefore alter the tree judiciously to convert any use of "to have" into a use of "to be"; it follows that Assertions::Copular::to_have can never be called in traverse 2, when there are no uses of "to have" left in the tree.

    void Assertions::Copular::to_have(parse_node *pv) {
        parse_node *px = pv->down->next;
        parse_node *py = pv->down->next->next;

        <Reject two ungrammatical forms of "to have" 4.1>;

        if (ParseTree::get_type(py) == CALLED_NT)
            <Handle "X has an A called B" 4.2>
        else if (<k-kind>(ParseTree::get_text(py)))
            <Handle "X has a V" where V is a kind of value which is also a property 4.3>
        else
            <Handle "X has P" where P is a list of properties 4.4>;

        ParseTree::annotate_int(pv->down, possessive_verb_ANNOT, FALSE);
        Assertions::Copular::to_be(pv); and start again as if it had never been possessive
    }

The function Assertions::Copular::to_have is used in §3.

§4.1. <Reject two ungrammatical forms of "to have" 4.1> =

        if (ParseTree::get_type(py) == X_OF_Y_NT) {
            Problems::Issue::sentence_problem(Task::syntax_tree(), _p_(PM_SuperfluousOf),
                "the 'of' here appears superfluous",
                "assuming the sentence aims to give a property value of something. "
                "(For instance, if we want to declare the carrying capacity of "
                "something, the normal Inform practice is to say 'The box has "
                "carrying capacity 10' rather than 'The box has a carrying capacity "
                "of 10'.)");
            return;
        }
        if (ParseTree::get_type(py) == WITH_NT) {
            Problems::Issue::sentence_problem(Task::syntax_tree(), _p_(PM_SuperfluousWith),
                "the 'has ... with' here appears to be a mixture of two ways to "
                "give something properties",
                "that is, 'The box is a container with capacity 10.' and 'The box "
                "has capacity 10.'");
            return;
        }

This code is used in §4.

§4.2. Here py is a CALLED_NT subtree for "an A called B", which we relabel as a PROPERTYCALLED_NT subtree and hang beneath an ALLOWED_NT node.

<Handle "X has an A called B" 4.2> =

        if (Wordings::match(ParseTree::get_text(py->down->next), ParseTree::get_text(py->down))) {
            Problems::Issue::sentence_problem(Task::syntax_tree(), _p_(PM_SuperfluousCalled),
                "'called' should be used only when the name is different from the kind",
                "so this sentence should be simplified. For example, 'A door has a "
                "colour called colour' should be written more simply as 'A door has "
                "a colour'; but 'called' can be used for something like 'A door has "
                "a number called the street number'.");
            return;
        } else {
            ParseTree::set_type(py, PROPERTYCALLED_NT);
            if (ParseTree::get_type(py->down) == AND_NT) {
                int L = ParseTree::left_edge_of(py->down),
                    R = ParseTree::right_edge_of(py->down);
                <nounphrase-articled>(Wordings::new(L, R));
                parse_node *pn = <<rp>>;
                pn->next = py->down->next;
                py->down = pn;
                LOG("Thus $T", py);
            }
            px->next = ParseTree::new(ALLOWED_NT);
            px->next->down = py;
            int prohibited = <prohibited-property-owners>(ParseTree::get_text(px));
            if (!prohibited) {
                <nounphrase-articled-list>(ParseTree::get_text(py->down->next));
                py->down->next = <<rp>>;
            }
        }
        py = px->next;

This code is used in §4.

§4.3. More directly, here we simply relegate py by hanging it from a new ALLOWED_NT node. This is for something like

A thing has a colour.

where "colour" is the name of both a kind of value and (soon) a property.

<Handle "X has a V" where V is a kind of value which is also a property 4.3> =

        px->next = ParseTree::new(ALLOWED_NT);
        px->next->down = py;
        py = px->next;

This code is used in §4.

§4.4. And here we just mark py as a property list. Typically the sentence would be "The player has carrying capacity 7."

<Handle "X has P" where P is a list of properties 4.4> =

        ParseTree::set_type(py, PROPERTY_LIST_NT);

This code is used in §4.

§5. In either case, then, we end up going through Assertions::Copular::to_be and then to the following routine, which asserts that subtree px "is" py.

During traverse 1, this takes place in a three-stage process:

In traverse 2, only (c) takes place; (a) and (b) are one-time events.

    void Assertions::Copular::make_assertion(parse_node *px, parse_node *py) {
        if (traverse == 1) {
            int pc = problem_count;
            if (!(<s-existential-np>(ParseTree::get_text(px))))
                Assertions::Refiner::refine(px, ALLOW_CREATION);
            Assertions::Refiner::refine(py, ALLOW_CREATION);
            if (problem_count > pc) return;
            if (Assertions::Creator::consult_the_creator(px, py) == FALSE) return;
        }

        if (trace_sentences) LOG("$T", current_sentence);
        if (<s-existential-np>(ParseTree::get_text(px))) {
            if (traverse == 1) Assertions::Copular::make_existential_assertion(py);
            px = py;
        } else {
            Assertions::Maker::make_assertion_recursive(px, py);
        }
        <Change the discussion topic for subsequent sentences 5.1>;
    }

The function Assertions::Copular::make_assertion is used in §3, 19/tod (§6.3).

§5.1. The slight asymmetry in what follows is partly pragmatic, partly the result of subject-verb inversion ("in the bag is the ball" not "the ball is in the bag"). We extract a subject from a relationship node on the left, but not on the right, and we don't extract an object from one. Consider:

A billiards table is in the Gazebo. On it is a trophy cup.

What does "it" mean, and why? A human reader goes for the billiards table at once, because it seems more likely as a supporter than the Gazebo, but that's not how Inform gets the same answer. It all hangs on "billiards table" being the object of the first sentence, not the Gazebo; if we descended the RHS, which is RELATIONSHIP_NT -> PROPER_NOUN_NT pointing to the Gazebo, that's the conclusion we would have reached.

<Change the discussion topic for subsequent sentences 5.1> =

        inference_subject *infsx = NULL, *infsy = NULL, *infsy_full = NULL;
        infsx = Assertions::Copular::discussed_at_node(px);
        infsy_full = Assertions::Copular::discussed_at_node(py);
        if (ParseTree::get_type(py) != KIND_NT) infsy = ParseTree::get_subject(py);
        Assertions::Traverse::change_discussion_topic(infsx, infsy, infsy_full);
        if (ParseTree::get_type(px) == AND_NT) Assertions::Traverse::subject_of_discussion_a_list();
        if (ParseTree::int_annotation(current_sentence, clears_pronouns_ANNOT))
            Assertions::Traverse::new_discussion();

This code is used in §5.

§6.

    inference_subject *Assertions::Copular::discussed_at_node(parse_node *pn) {
        inference_subject *infs = NULL;
        if (ParseTree::get_type(pn) != KIND_NT) infs = ParseTree::get_subject(pn);
        if ((ParseTree::get_type(pn) == RELATIONSHIP_NT) && (pn->down) &&
            (ParseTree::get_type(pn->down) == PROPER_NOUN_NT))
            infs = ParseTree::get_subject(pn->down);
        if ((ParseTree::get_type(pn) == WITH_NT) && (pn->down) &&
            (ParseTree::get_type(pn->down) == PROPER_NOUN_NT))
            infs = ParseTree::get_subject(pn->down);
        return infs;
    }

The function Assertions::Copular::discussed_at_node is used in §5.1.

§7.

    void Assertions::Copular::make_existential_assertion(parse_node *py) {
        if (ParseTree::get_type(py) == WITH_NT) {
            Assertions::Copular::make_existential_assertion(py->down); return;
        }
        if (ParseTree::get_type(py) == AND_NT) {
            Assertions::Copular::make_existential_assertion(py->down);
            Assertions::Copular::make_existential_assertion(py->down->next);
            return;
        }
        if (ParseTree::get_type(py) == COMMON_NOUN_NT) {
            if ((InferenceSubjects::is_a_kind_of_object(ParseTree::get_subject(py))) ||
                (Kinds::Compare::eq(K_object, InferenceSubjects::as_kind(ParseTree::get_subject(py)))))
                Assertions::Creator::convert_instance_to_nounphrase(py, NULL);
            else
                Problems::Issue::sentence_problem(Task::syntax_tree(), _p_(PM_ThereIsVague),
                    "'there is...' can only be used to create objects",
                    "and not instances of other kinds.'");
        }
    }

The function Assertions::Copular::make_existential_assertion is used in §5.