To record the identity and different structural forms of verbs.
§1. Verb Identities. What is a verb? Are the verbs in "Peter is hungry" and "Jane will be in the Dining Room" the same? How about in "Donald Trump lies on the television" and "My cat Donald lies on the television"? This isn't so easy to answer.
For our purposes two usages of a verbs are "the same verb" if they ultimately come from the same instance of the following structure; this essentially defines a verb as a combination of its inflected forms with its possible usages in a sentence.
typedef struct verb { struct verb_conjugation *conjugation; struct verb_form *first_form; struct verb_form *base_form; struct linguistic_stock_item *in_stock; #ifdef VERB_COMPILATION_LINGUISTICS_CALLBACK struct verb_compilation_data compilation_data; #endif CLASS_DEFINITION } verb;
- The structure verb is accessed in 2/adj, 2/art, 2/daq, 2/nns, 2/prn, 3/vu, 3/prp, 4/vp and here.
§2. Verbs are a grammatical category:
grammatical_category *verbs_category = NULL; void Verbs::create_category(void) { verbs_category = Stock::new_category(I"verb"); METHOD_ADD(verbs_category, LOG_GRAMMATICAL_CATEGORY_MTID, Verbs::log_item); } void Verbs::log_item(grammatical_category *cat, general_pointer data) { verb *V = RETRIEVE_POINTER_verb(data); Verbs::log_verb(DL, V); } verb *Verbs::from_lcon(lcon_ti lcon) { linguistic_stock_item *item = Stock::from_lcon(lcon); if (item == NULL) return NULL; return RETRIEVE_POINTER_verb(item->data); } lcon_ti Verbs::to_lcon(verb *v) { return Stock::to_lcon(v->in_stock); }
§3. Note also that every verb always has a bare form, where no prepositions are combined with it. This is (initially) meaningless, but it always exists.
Finally, note that the conjugation can be null. This is used only for what we'll call "operator verbs", where a mathematical operator is used instead of a word: for example, the <= sign is such a verb in Inform.
verb *copular_verb = NULL; verb *Verbs::new_verb(verb_conjugation *vc, int cop) { verb *V = CREATE(verb); V->conjugation = vc; V->first_form = NULL; V->base_form = NULL; #ifdef VERB_COMPILATION_LINGUISTICS_CALLBACK VERB_COMPILATION_LINGUISTICS_CALLBACK(V); #endif Give the new verb a single meaningless form3.1; If this is the first copular verb, remember that3.2; V->in_stock = Stock::new(verbs_category, STORE_POINTER_verb(V)); LOGIF(VERB_FORMS, "New verb: $w\n", V); return V; }
§3.1. Give the new verb a single meaningless form3.1 =
Verbs::add_form(V, NULL, NULL, VerbMeanings::meaninglessness(), SVO_FS_BIT);
- This code is used in §3.
§3.2. Note that the first verb submitted with the copular flag set is considered to be the definitive copular verb.
If this is the first copular verb, remember that3.2 =
if ((cop) && (copular_verb == NULL) && (vc) && (WordAssemblages::nonempty(vc->infinitive))) copular_verb = V;
- This code is used in §3.
void Verbs::log_verb(OUTPUT_STREAM, void *vvi) { verb *V = (verb *) vvi; if (V == NULL) { WRITE("<no-V>"); } else { if (V->conjugation) WRITE("%A", &(V->conjugation->infinitive)); else WRITE("(unconjugated)"); WRITE("(%d)", V->allocation_id); } }
§5. Operator Verbs. As noted above, these are tenseless verbs with no conjugation, represented only by symbols such as <=. As infix operators, they mimic SVO sentence structures.
verb *Verbs::new_operator_verb(verb_meaning vm) { verb *V = Verbs::new_verb(NULL, FALSE); Verbs::add_form(V, NULL, NULL, vm, SVO_FS_BIT); return V; }
§6. Verb Forms. A "verb form" is a way to use a given verb in a sentence. This may require up to two different prepositions to be used. For example, "Peter is hungry" and "Jane will be in the Dining Room" exhibit two different verb forms: "to be" with no preposition (meaning that the object phrase, "hungry", is not introduced by a preposition), and "to be" with the preposition "in".
It's not always the case that the preposition follows the verb, even when it directly introduces the object, because of a quirk of English called inversion: for example "In the Dining Room is Jane" locates "in" far from "is". But the first preposition, if given, always introduces the object phrase. If it's not given, the object phrase has no introduction, as in the sentence "Peter is hungry".
The second clause preposition is again optional. This marks that the verb takes two object phrases, and provides a preposition to introduce the second of these. For example, in "X translates into Y as Z", the verb is "translate", the first preposition is "into", and the second is "as". In "X ends Y when Z", the first preposition is null, and the second is "when".
§7. A "form structure" is a possible way in which a verb form can be expressed. SVO means "subject, verb, object", as in "Peter likes Jane"; VOO means "verb, object, object", as in "Test me with ...", and so on. Since multiple form usages can be legal for the same form, this is a bitmap:
define SVO_FS_BIT 1 define VO_FS_BIT 2 define SVOO_FS_BIT 4 define VOO_FS_BIT 8
typedef struct verb_form { struct verb *underlying_verb; struct preposition *preposition; struct preposition *second_clause_preposition; int form_structures; bitmap of *_FS_BIT values struct word_assemblage infinitive_reference_text; e.g. "translate into" struct word_assemblage pos_reference_text; e.g. "translate into" struct word_assemblage neg_reference_text; e.g. "do not translate into" struct verb_sense *list_of_senses; struct verb_meaning *first_unspecial_meaning; struct verb_form *next_form; within the linked list for the verb struct linguistic_stock_item *in_stock; #ifdef VERB_FORM_COMPILATION_LINGUISTICS_CALLBACK struct verb_form_compilation_data verb_form_compilation; #endif CLASS_DEFINITION } verb_form;
- The structure verb_form is accessed in 2/adj, 2/art, 2/daq, 2/nns, 2/prn, 3/vu, 3/prp, 4/vp and here.
§9. Verb forms are also a grammatical category:
grammatical_category *verb_forms_category = NULL; void Verbs::create_forms_category(void) { verb_forms_category = Stock::new_category(I"verb_form"); METHOD_ADD(verb_forms_category, LOG_GRAMMATICAL_CATEGORY_MTID, Verbs::log_form_item); } void Verbs::log_form_item(grammatical_category *cat, general_pointer data) { verb_form *vf = RETRIEVE_POINTER_verb_form(data); Verbs::log_form(vf); } void Verbs::log_form(verb_form *vf) { LOG("$w + $p + $p", vf->underlying_verb, vf->preposition, vf->second_clause_preposition); if (vf->form_structures & SVO_FS_BIT) LOG(" SVO"); if (vf->form_structures & VO_FS_BIT) LOG(" VO"); if (vf->form_structures & SVOO_FS_BIT) LOG(" SVOO"); if (vf->form_structures & VOO_FS_BIT) LOG(" VOO"); }
§10. Verb senses. In this model, a verb can have multiple senses. Inform makes little use of that in verbs created by the user, but for example "X is Y" has more than a dozen different senses. (Inform distinguishes them on a case by case basis, by looking at X and Y.)
The following structure is just a holder for a "verb meaning", so that it can be joined into a linked list. Verb meanings are described elsewhere.
typedef struct verb_sense { struct verb_meaning vm; struct verb_sense *next_sense; within the linked list for the verb form CLASS_DEFINITION } verb_sense;
- The structure verb_sense is accessed in 3/vu, 3/prp, 4/vp and here.
§11. Creating forms and senses. Forms are stored in a linked list, and are uniquely identified by the triplet of verb and two prepositions.
The base form is by definition the one where no prepositions are used. We could therefore find that base form by calling Verbs::find_form(V, NULL, NULL), but instead we cache this result in V->base_form for speed: profiling shows that Inform otherwise spends nearly 1% of its entire running time making that innocent-looking call.
verb_form *Verbs::base_form(verb *V) { if (V) return V->base_form; return NULL; } verb_form *Verbs::find_form(verb *V, preposition *prep, preposition *second_prep) { if (V) for (verb_form *vf = V->first_form; vf; vf = vf->next_form) if ((vf->preposition == prep) && (vf->second_clause_preposition == second_prep)) return vf; return NULL; }
§12. And here's how we add them.
void Verbs::add_form(verb *V, preposition *prep, preposition *second_prep, verb_meaning vm, int form_structs) { if (VerbMeanings::is_meaningless(&vm) == FALSE) LOGIF(VERB_FORMS, " Adding form: $w + $p + $p = $y\n", V, prep, second_prep, &vm); VerbMeanings::set_where_assigned(&vm, current_sentence); if (V == NULL) internal_error("added form to null verb"); verb_form *vf = NULL; Find or create the verb form structure for this combination12.1; vf->form_structures = ((vf->form_structures) | form_structs); if ((VerbMeanings::is_meaningless(&vm) == FALSE) || (vf->list_of_senses == NULL)) Add this meaning as a new sense of the verb form12.2; }
§12.1. Find or create the verb form structure for this combination12.1 =
vf = Verbs::find_form(V, prep, second_prep); if (vf == NULL) { vf = CREATE(verb_form); vf->underlying_verb = V; vf->preposition = prep; vf->second_clause_preposition = second_prep; vf->form_structures = form_structs; vf->list_of_senses = NULL; vf->next_form = NULL; vf->first_unspecial_meaning = NULL; #ifdef VERB_FORM_COMPILATION_LINGUISTICS_CALLBACK VERB_FORM_COMPILATION_LINGUISTICS_CALLBACK(vf); #endif Compose the reference texts for the new form12.1.1; Insert the new form into the list of forms for this verb12.1.2; vf->in_stock = Stock::new(verb_forms_category, STORE_POINTER_verb_form(vf)); }
- This code is used in §12.
§12.1.1. The reference texts are just for convenience, really: they express the form in a canonical verbal form. For example, "translate into + as".
Compose the reference texts for the new form12.1.1 =
verb_conjugation *vc = V->conjugation; if (vc) { int p = VerbUsages::adaptive_person(vc->defined_in); int n = VerbUsages::adaptive_number(vc->defined_in); word_assemblage we_form = (vc->tabulations[ACTIVE_VOICE].vc_text[IS_TENSE][POSITIVE_SENSE][p][n]); word_assemblage we_dont_form = (vc->tabulations[ACTIVE_VOICE].vc_text[IS_TENSE][NEGATIVE_SENSE][p][n]); vf->infinitive_reference_text = vc->infinitive; vf->pos_reference_text = we_form; vf->neg_reference_text = we_dont_form; if (prep) { vf->infinitive_reference_text = WordAssemblages::join(vf->infinitive_reference_text, prep->prep_text); vf->pos_reference_text = WordAssemblages::join(vf->pos_reference_text, prep->prep_text); vf->neg_reference_text = WordAssemblages::join(vf->neg_reference_text, prep->prep_text); } if (second_prep) { word_assemblage plus = WordAssemblages::lit_1(PLUS_V); vf->infinitive_reference_text = WordAssemblages::join(vf->infinitive_reference_text, plus); vf->pos_reference_text = WordAssemblages::join(vf->pos_reference_text, plus); vf->neg_reference_text = WordAssemblages::join(vf->neg_reference_text, plus); vf->infinitive_reference_text = WordAssemblages::join(vf->infinitive_reference_text, second_prep->prep_text); vf->pos_reference_text = WordAssemblages::join(vf->pos_reference_text, second_prep->prep_text); vf->neg_reference_text = WordAssemblages::join(vf->neg_reference_text, second_prep->prep_text); } } else { vf->infinitive_reference_text = WordAssemblages::new_assemblage(); vf->pos_reference_text = WordAssemblages::new_assemblage(); vf->neg_reference_text = WordAssemblages::new_assemblage(); }
- This code is used in §12.1.
§12.1.2. The list of forms for a verb is in order of preposition length.
Insert the new form into the list of forms for this verb12.1.2 =
verb_form *prev = NULL, *evf = V->first_form; for (; evf; prev = evf, evf = evf->next_form) { if (Prepositions::length(prep) > Prepositions::length(evf->preposition)) break; } if (prev == NULL) { vf->next_form = V->first_form; V->first_form = vf; } else { vf->next_form = prev->next_form; prev->next_form = vf; } if ((prep == NULL) && (second_prep == NULL)) V->base_form = vf;
- This code is used in §12.1.
§12.2. A new sense is normally just added to the end of the list of senses for the given form, except that if there's a meaningless sense present already, we overwrite that with the new (presumably meaningful) one.
Add this meaning as a new sense of the verb form12.2 =
verb_sense *vs = vf->list_of_senses, *prev = NULL; while (vs) { if (VerbMeanings::is_meaningless(&(vs->vm))) { vs->vm = vm; break; } prev = vs; vs = vs->next_sense; } if (vs == NULL) { vs = CREATE(verb_sense); vs->vm = vm; vs->next_sense = NULL; if (prev == NULL) vf->list_of_senses = vs; else prev->next_sense = vs; } if (VerbMeanings::get_special_meaning(&(vs->vm)) == NULL) { vf->first_unspecial_meaning = &(vs->vm); } else { if (vf->first_unspecial_meaning == &(vs->vm)) vf->first_unspecial_meaning = NULL; }
- This code is used in §12.
§13. The following function may seem curious — what's so great about the first regular sense of a verb? The answer is that Inform generally gives a verb at most one regular sense.
We cache the result in vf->first_unspecial_meaning for speed, because profiling of inform7 suggests this is worth it, but retain the uncached algorithm as well in case we suspect bugs in future.
define CACHE_FIRST_UNSPECIAL_MEANING
verb_meaning *Verbs::first_unspecial_meaning_of_verb_form(verb_form *vf) { if (vf) { #ifdef CACHE_FIRST_UNSPECIAL_MEANING return vf->first_unspecial_meaning; #endif #ifndef CACHE_FIRST_UNSPECIAL_MEANING for (verb_sense *vs = vf->list_of_senses; vs; vs = vs->next_sense) if (VerbMeanings::get_special_meaning(&(vs->vm)) == NULL) return &(vs->vm); #endif } return NULL; }
§14. This is useful for indexing:
int Verbs::has_special_meanings(verb_form *vf) { if (vf) for (verb_sense *vs = vf->list_of_senses; vs; vs = vs->next_sense) if (VerbMeanings::get_special_meaning(&(vs->vm))) return TRUE; return FALSE; }