To define prepositional forms.


§1. Prepositions. These are words which introduce relative clauses, or mark the text following them as having some role; in more inflected languages, noun cases might be used for some of the same purposes. For example, in the sentence "Peter is in the Library", the word "in" is a preposition (as it is twice in this sentence, too). Prepositions often occur in combination with the copular verb "to be" like this, but not always. Inform's standard usage "X substitutes for Y" couples the preposition "for" to the verb "to substitute".

That all sounds linguistically sound, but we're on dodgier ground in how we analyse text like "X is contained by Y". We're going to regard this as the verb "to be" combined with the preposition "contained by", for implementation reasons.

Note that the following says nothing about the preposition's meaning, which will vary depending on the verb it's coupled with.

typedef struct preposition {
    struct word_assemblage prep_text;
    struct verb_conjugation *prep_lex_entry;  for use when indexing
    struct parse_node *where_prep_created;  for use if problem messages needed
    int allow_unexpected_upper_case;  for preps like "in Cahoots With"
    struct linguistic_stock_item *in_stock;
    CLASS_DEFINITION
} preposition;

§2. Prepositions are a grammatical category:

grammatical_category *prepositions_category = NULL;
void Prepositions::create_category(void) {
    prepositions_category = Stock::new_category(I"preposition");
    METHOD_ADD(prepositions_category, LOG_GRAMMATICAL_CATEGORY_MTID, Prepositions::log_item);
}

void Prepositions::log_item(grammatical_category *cat, general_pointer data) {
    preposition *P = RETRIEVE_POINTER_preposition(data);
    LOG("%A", &(P->prep_text));
}

§3. As with verbs, "prepositions" can be long, but are not unlimited.

define MAX_WORDS_IN_PREPOSITION (MAX_WORDS_IN_ASSEMBLAGE - 2)

§4. Preposition words are marked for efficiency of parsing:

define PREPOSITION_MC 0x00800000  a word which might introduce a relative clause

§5. Logging.

void Prepositions::log(OUTPUT_STREAM, void *vprep) {
    preposition *prep = (preposition *) vprep;
    if (prep == NULL) { WRITE("___"); }
    else { WRITE("%A", &(prep->prep_text)); }
}

§6. Creation. Prepositions are completely determined by their wording: the "for" attached to one verb is the same preposition as the "for" attached to another one.

preposition *Prepositions::make(word_assemblage wa, int unexpected_upper_casing_used,
    parse_node *where) {
    preposition *prep = NULL;
    LOOP_OVER(prep, preposition)
        if (WordAssemblages::eq(&(prep->prep_text), &wa))
            return prep;

    prep = CREATE(preposition);
    prep->prep_text = wa;
    prep->where_prep_created = where;
    prep->allow_unexpected_upper_case = unexpected_upper_casing_used;
    Prepositions::mark_as_preposition(WordAssemblages::first_word(&wa));
    prep->prep_lex_entry = current_main_verb;
    prep->in_stock = Stock::new(prepositions_category, STORE_POINTER_preposition(prep));
    LOGIF(VERB_FORMS, "New preposition: $p\n", prep);

    return prep;
}

§7. Two utility routines:

parse_node *Prepositions::get_where_pu_created(preposition *prep) {
    return prep->where_prep_created;
}

int Prepositions::length(preposition *prep) {
    if (prep == NULL) return 0;
    return WordAssemblages::length(&(prep->prep_text));
}

§8. Parsing source text against preposition usages. The following parses to see if the preposition occurs at the beginning of, perhaps entirely filling, the given wording. We return the word number after the preposition ends, which might therefore be just outside the range.

int Prepositions::parse_prep_against(wording W, preposition *prep) {
    return WordAssemblages::parse_as_weakly_initial_text(W, &(prep->prep_text), EMPTY_WORDING,
        prep->allow_unexpected_upper_case, TRUE);
}

§9. The following nonterminal is currently not used. In principle it spots any preposition, but note that it does so by testing in creation order.

<preposition> internal ? {
    if (Vocabulary::test_flags(Wordings::first_wn(W), PREPOSITION_MC) == FALSE) {
        ==> { fail nonterminal };
    }
    preposition *prep;
    LOOP_OVER(prep, preposition) {
        int i = Prepositions::parse_prep_against(W, prep);
        if ((i>Wordings::first_wn(W)) && (i<=Wordings::last_wn(W)+1)) {
            ==> { -, prep };
            return i-1;
        }
    }
    ==> { fail nonterminal };
}

§10. It's often useful to look for prepositions which can be combined with the copular verb "to be". These are tested in order of the list of possible verb forms for "to be', which is constructed with longer prepositions first. So it will find the longest match.

<copular-preposition> internal ? {
    if (copular_verb == NULL) { ==> { fail nonterminal }; }
    if (Vocabulary::test_flags(Wordings::first_wn(W), PREPOSITION_MC) == FALSE) { ==> { fail nonterminal }; }
    for (verb_form *vf = copular_verb->first_form; vf; vf=vf->next_form) {
        preposition *prep = vf->preposition;
        if ((prep) && (VerbMeanings::is_meaningless(&(vf->list_of_senses->vm)) == FALSE)) {
            int i = Prepositions::parse_prep_against(W, prep);
            if ((i>Wordings::first_wn(W)) && (i<=Wordings::last_wn(W)+1)) {
                ==> { -, prep };
                return i-1;
            }
        }
    }
    ==> { fail nonterminal };
}

§11. This is exactly similar, except that it looks for prepositions combined with a given "permitted verb".

<permitted-preposition> internal ? {
    if (Vocabulary::test_flags(Wordings::first_wn(W), PREPOSITION_MC) == FALSE) {
        ==> { fail nonterminal };
    }
    if (permitted_verb)
        for (verb_form *vf = permitted_verb->first_form; vf; vf=vf->next_form) {
            preposition *prep = vf->preposition;
            if ((prep) && (VerbMeanings::is_meaningless(&(vf->list_of_senses->vm)) == FALSE)) {
                int i = Prepositions::parse_prep_against(W, prep);
                if ((i>Wordings::first_wn(W)) && (i<=Wordings::last_wn(W)+1)) {
                    ==> { -, prep };
                    return i-1;
                }
            }
        }
    ==> { fail nonterminal };
}

§12.

void Prepositions::mark_for_preform(void) {
    Nonterminals::flag_words_with(<rc-marker>, PREPOSITION_MC);
}

void Prepositions::preform_optimiser(void) {
    NTI::one_word_in_match_must_have_my_NTI_bit(<preposition>);
    NTI::one_word_in_match_must_have_my_NTI_bit(<copular-preposition>);
    NTI::one_word_in_match_must_have_my_NTI_bit(<permitted-preposition>);
}

void Prepositions::mark_as_preposition(vocabulary_entry *ve) {
    Vocabulary::set_flags(ve, PREPOSITION_MC);
    NTI::mark_vocabulary(ve, <preposition>);
    NTI::mark_vocabulary(ve, <copular-preposition>);
    NTI::mark_vocabulary(ve, <permitted-preposition>);
}