diff --git a/docs/assertions-module/6-aa.html b/docs/assertions-module/6-aa.html new file mode 100644 index 000000000..fb27caea2 --- /dev/null +++ b/docs/assertions-module/6-aa.html @@ -0,0 +1,403 @@ + + + + Adjective Ambiguity + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +

Managing the multiple contextual meanings which a single adjective can have.

+ +
+ +

§1. Adjectives can have multiple meanings. For example, it is legal to define +both of these in the same source text: +

+ +
+Definition: a text is empty rather than non-empty if it is "".
+
+Definition: a table name is empty rather than non-empty if the
+number of filled rows in it is 0.
+
+

This gives two different meanings to both "empty" and "non-empty". We can +only work out which meaning is intended by looking at the context, that is, +at the kind of whatever it is applied to. For a text, the first sense applies, +and for a table name, the second. +

+ +

So, then, every adjective has the following data attached to it: +

+ +
define ADJECTIVE_MEANING_LINGUISTICS_CALLBACK AdjectiveAmbiguity::new_set
+
+
+typedef struct adjective_meaning_data {
+    struct adjective_meaning *possible_meanings;  list in the order defined
+    struct adjective_meaning *sorted_meanings;  list in logical precedence order
+} adjective_meaning_data;
+
+void AdjectiveAmbiguity::new_set(adjective *adj) {
+    adj->adjective_meanings.possible_meanings = NULL;
+    adj->adjective_meanings.sorted_meanings = NULL;
+}
+
+ +

§2. The following assigns a new meaning to a given word range: we find the +appropriate APH (creating if necessary) and then add the new meaning to the +end of its unsorted meaning list. +

+ +

We eventually need to sort this list of definitions into logical priority +order — so that a definition applying to just Count Dracula precedes one +applying to men, which in turn precedes one applying to things. (Priority +order is irrelevant when two senses apply to domains with no overlap, as +in the case of texts and table names.) It's convenient and costs little +memory to keep the sorted list as a second linked list. +

+ +
+adjective *AdjectiveAmbiguity::add_meaning_to_adjective(adjective_meaning *am,
+    adjective *adj) {
+    adjective_meaning *aml = adj->adjective_meanings.possible_meanings;
+    if (aml == NULL) adj->adjective_meanings.possible_meanings = am;
+    else {
+        while (aml->next_meaning) aml = aml->next_meaning;
+        aml->next_meaning = am;
+    }
+    am->next_meaning = NULL;
+    am->owning_adjective = adj;
+    return adj;
+}
+
+

§3. And here we log the unsorted list. +

+ +
+void AdjectiveAmbiguity::log(adjective *adj) {
+    if (adj == NULL) { LOG("<null-APH>\n"); return; }
+    adjective_meaning *am;
+    int n;
+    for (n=1, am = adj->adjective_meanings.possible_meanings; am;
+        n++, am = am->next_meaning)
+        LOG("%d: %W (domain:$j) (dk:%u)\n", n, am->adjective_index_text,
+            am->domain_infs, am->domain_kind);
+}
+
+

§4. If the source tries to apply the word "open", say, to a given value or +object \(X\), when does that make sense? +

+ +

We can only find out by checking every possible meaning of "open" to see +if it can accommodate the kind of value of \(X\). But this time we use weak +checking, and make it weaker still since a null kind is taken to mean "any +object", either in the AM's definition — which can happen if we are very +early in Inform's run — or because the caller doesn't actually know the +kind of value of \(X\). (In other words, adjectives tend to assume they apply +to objects rather than other values.) This means we will accept some +logically impossible outcomes — we would say that it's acceptable to apply +"open" to an animal, say — but that is actually a good thing. It means +that "list of open things" or "something open" are allowed. Source text +such as: +

+ +
+

The labrador puppy is an open animal.

+
+ +

will successfully parse, but then result in higher-level problem messages. +The following does compile: +

+ +
+

now the labrador puppy is open;

+
+ +

but results in a run-time problem message when it executes. +

+ +

It makes no difference what order we check the AMs in, so we can use the +unsorted list, which is helpful since we may need to call this routine +early in the run when sorting cannot yet be done. +

+ +
+int AdjectiveAmbiguity::can_be_applied_to(adjective *adj, kind *K) {
+    if (adj) {
+        adjective_meaning *am;
+        for (am = adj->adjective_meanings.possible_meanings; am; am = am->next_meaning) {
+            if (am->domain_infs == NULL) {
+                if (am->setting_domain) Issue a problem for a circularity4.1;
+                am->setting_domain = TRUE;
+                AdjectiveMeanings::set_definition_domain(am, TRUE);
+                am->setting_domain = FALSE;
+            }
+            kind *am_kind = AdjectiveMeanings::get_domain(am);
+            if (Kinds::Behaviour::is_object(am_kind)) {
+                if (K == NULL) return TRUE;
+                if (Kinds::Behaviour::is_object(K)) return TRUE;
+            } else {
+                if ((K) && (Kinds::Behaviour::is_object(K) == FALSE) &&
+                    (Kinds::compatible(K, am_kind) == ALWAYS_MATCH))
+                    return TRUE;
+            }
+        }
+    }
+    return FALSE;
+}
+
+

§4.1. Issue a problem for a circularity4.1 = +

+ +
+    if (problem_count == 0) {
+        Problems::quote_source(1, current_sentence);
+        Problems::quote_wording(2, Clusters::get_form(adj->adjective_names, FALSE));
+        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_AdjectiveCircular));
+        Problems::issue_problem_segment(
+            "In the sentence %1, it looks as if the definition of the adjective "
+            "'%2' may be circular.");
+        Problems::issue_problem_end();
+    }
+    return FALSE;
+
+ +

§5. Does a given adjective have any interpretation as an enumerated property +value, or an either/or property? If so we return the earliest known. +

+ +
+instance *AdjectiveAmbiguity::has_enumerative_meaning(adjective *adj) {
+    adjective_meaning *am;
+    for (am = adj->adjective_meanings.possible_meanings; am; am = am->next_meaning)
+        if (InstanceAdjectives::is_enumerative(am))
+            return RETRIEVE_POINTER_instance(am->detailed_meaning);
+    return NULL;
+}
+
+property *AdjectiveAmbiguity::has_either_or_property_meaning(adjective *adj, int *sense) {
+    if (adj)
+        for (adjective_meaning *am = adj->adjective_meanings.possible_meanings;
+            am; am = am->next_meaning)
+            if (Properties::EitherOr::is_either_or_adjective(am)) {
+                if (sense) *sense = am->meaning_parity;
+                return RETRIEVE_POINTER_property(am->detailed_meaning);
+            }
+    return NULL;
+}
+
+

§6. Occasionally we just want one meaning: +

+ +
+adjective_meaning *AdjectiveAmbiguity::first_meaning(adjective *adj) {
+    if (adj == NULL) return NULL;
+    return adj->adjective_meanings.possible_meanings;
+}
+
+

§7. Sorting lists of meanings. After meanings have been declared, a typical APH will have a disordered +"possible meaning" list and an empty "sorted meaning" list. The following +insertion-sorts1 the possibles list into the sorted list. +

+ + +
+void AdjectiveAmbiguity::sort(adjective *adj) {
+    if (adj == NULL) internal_error("tried to sort meanings for null adjective");
+    adjective_meaning *unsorted_head = adj->adjective_meanings.possible_meanings;
+    adjective_meaning *sorted_head = NULL;
+    adjective_meaning *am, *am2;
+    for (am = unsorted_head; am; am = am->next_meaning)
+        if (am->domain_infs == NULL)
+            AdjectiveMeanings::set_definition_domain(am, TRUE);
+    for (am = unsorted_head; am; am = am->next_meaning) {
+        if (sorted_head == NULL) {
+            sorted_head = am;
+            am->next_sorted = NULL;
+        } else {
+            adjective_meaning *lastdef = NULL;
+            for (am2 = sorted_head; am2; am2 = am2->next_sorted) {
+                if (AdjectiveMeanings::compare(am, am2) == 1) {
+                    if (lastdef == NULL) {
+                        sorted_head = am;
+                        am->next_sorted = am2;
+                    } else {
+                        lastdef->next_sorted = am;
+                        am->next_sorted = am2;
+                    }
+                    break;
+                }
+                if (am2->next_sorted == NULL) {
+                    am2->next_sorted = am;
+                    am->next_sorted = NULL;
+                    break;
+                }
+                lastdef = am2;
+            }
+        }
+    }
+    adj->adjective_meanings.sorted_meanings = sorted_head;
+}
+
+adjective_meaning *AdjectiveAmbiguity::get_sorted_definition_list(adjective *adj) {
+    return adj->adjective_meanings.sorted_meanings;
+}
+
+

§8. With that sorting done, we can begin to use an adjective. Suppose there has +been an assertion sentence like this: +

+ +
+

The ormolu clock is fixed in place.

+
+ +

"Fixed in place" is identified as an adjective, adj; the "ormulo clock" is +what it applies to, stored in either infs_to_assert_on or val_to_assert_on +depending on what it is. kind_domain is what kind we think this has. parity +is equal to TRUE. +

+ +

What happens is that the list of definitions for "fixed in place" is checked +in logical precedence order, and AdjectiveMeanings::assert_single called +on any kind which the "ormolu clock" matches. (That will probably be the +definition for the "fixed in place" either/or property for things, unless +someone has given the adjective some special meaning unique to the clock.) The +first adjective meaning to be assertable then wins. +

+ +

The following routine therefore acts as a junction-box, deciding which sense +of the adjective is to be applied. We return TRUE if we were able to find a +definition which could be asserted and which the clock matched, and FALSE if +there was no definition which applied, or if none of those which did could be +asserted for it. +

+ +
+int AdjectiveAmbiguity::assert(adjective *adj, kind *kind_domain,
+    inference_subject *infs_to_assert_on, parse_node *val_to_assert_on, int parity) {
+    AdjectiveAmbiguity::sort(adj);
+    for (adjective_meaning *am = adj->adjective_meanings.sorted_meanings;
+        am; am = am->next_sorted) {
+        if (AdjectiveMeanings::domain_weak_match(kind_domain,
+            AdjectiveMeanings::get_domain(am)) == FALSE) continue;
+        if (AdjectiveMeanings::domain_subj_compare(infs_to_assert_on, am) == FALSE)
+            continue;
+        if (AdjectiveMeanings::assert_single(am, infs_to_assert_on, val_to_assert_on, parity))
+            return TRUE;
+    }
+    return FALSE;
+}
+
+

§9. Similarly, the following produces an I6 schema to carry out a task for the +adjective. (See AdjectiveMeanings::set_i6_schema for tasks.) +

+ +
+i6_schema *AdjectiveAmbiguity::schema_for_task(adjective *adj, kind *kind_domain, int T) {
+    if (kind_domain == NULL) kind_domain = K_object;
+    AdjectiveAmbiguity::sort(adj);
+    for (adjective_meaning *am = adj->adjective_meanings.sorted_meanings; am; am = am->next_sorted) {
+        kind *am_kind = AdjectiveMeanings::get_domain(am);
+        if (am_kind == NULL) {
+            AdjectiveMeanings::set_definition_domain(am, FALSE);
+            am_kind = AdjectiveMeanings::get_domain(am);
+        }
+        if (AdjectiveMeanings::domain_weak_match(kind_domain, am_kind) == FALSE) continue;
+        i6_schema *i6s = AdjectiveMeanings::schema_for_task(am, T);
+        if (i6s) return i6s;
+    }
+    return NULL;
+}
+
+ + +
+ + + diff --git a/docs/runtime-module/4-adj.html b/docs/runtime-module/4-adj.html new file mode 100644 index 000000000..bc147213d --- /dev/null +++ b/docs/runtime-module/4-adj.html @@ -0,0 +1,418 @@ + + + + Adjectives + + + + + + + + + + + + + + + + + + + + + +
+ + +

To compile run-time support for adjective definitions.

+ +

§1. The following utility is used to loop through the sorted meaning list, +skipping over any which have been dealt with already. +

+ +
+adjective_meaning *RTAdjectives::list_next_domain_kind(adjective_meaning *am, kind **K, int T) {
+    while ((am) && ((am->defined_already) || (AdjectiveMeanings::compilation_possible(am, T) == FALSE)))
+        am = am->next_sorted;
+    if (am == NULL) return NULL;
+    *K = AdjectiveMeanings::get_domain(am);
+    return am->next_sorted;
+}
+
+

§2. And this is where we do the iteration. The idea is that one adjective +definition routine is defined (for each task number) which covers all of +the weakly-domain-equal definitions for the same adjective. Thus one +routine might handle "detailed" for rulebooks, and another might handle +"detailed" for all of its meanings associated with objects — possibly +many AMs. +

+ +
+void RTAdjectives::compile_support_code(void) {
+    Ensure, just in case, that domains exist and are sorted on2.1;
+    int T;
+    for (T=1; T<=NO_ADJECTIVE_TASKS; T++) {
+        adjective *aph;
+        LOOP_OVER(aph, adjective) {
+            adjective_meaning *am;
+            for (am = aph->adjective_meanings.possible_meanings; am; am = am->next_meaning)
+                am->defined_already = FALSE;
+            for (am = aph->adjective_meanings.sorted_meanings; am; ) {
+                kind *K = NULL;
+                am = RTAdjectives::list_next_domain_kind(am, &K, T);
+                if (K)
+                    Compile adjective definition for this atomic kind of value2.2;
+            }
+        }
+    }
+}
+
+

§2.1. It's unlikely that we have got this far without the domains for the AMs +having been established, but certainly possible. We need the domains to be +known in order to sort. +

+ +

Ensure, just in case, that domains exist and are sorted on2.1 = +

+ +
+    adjective *aph;
+    LOOP_OVER(aph, adjective) {
+        adjective_meaning *am;
+        for (am = aph->adjective_meanings.possible_meanings; am; am = am->next_meaning) {
+            AdjectiveMeanings::set_definition_domain(am, FALSE);
+            am->defined_already = FALSE;
+        }
+        AdjectiveAmbiguity::sort(aph);
+    }
+
+ +

§2.2. The following is a standard way to compile a one-off routine. +

+ +

Compile adjective definition for this atomic kind of value2.2 = +

+ +
+    wording W = Adjectives::get_nominative_singular(aph);
+    LOGIF(VARIABLE_CREATIONS, "Compiling support code for %W applying to %u, task %d\n",
+        W, K, T);
+
+    inter_name *iname = AdjectiveMeanings::iname(aph, T, RTKinds::weak_id(K));
+    packaging_state save = Routines::begin(iname);
+    Add an it-variable to represent the value or object in the domain2.2.1;
+
+    TEMPORARY_TEXT(C)
+    WRITE_TO(C, "meaning of \"");
+    if (Wordings::nonempty(W)) WRITE_TO(C, "%~W", W);
+    else WRITE_TO(C, "<nameless>");
+    WRITE_TO(C, "\"");
+    Emit::code_comment(C);
+    DISCARD_TEXT(C)
+
+    if (problem_count == 0) {
+        local_variable *it_lv = LocalVariables::it_variable();
+        inter_symbol *it_s = LocalVariables::declare_this(it_lv, FALSE, 8);
+        RTAdjectives::list_compile(aph->adjective_meanings.sorted_meanings, Frames::current_stack_frame(), K, T, it_s);
+    }
+    Produce::rfalse(Emit::tree());
+
+    Routines::end(save);
+
+ +

§2.2.1. The stack frame has just one call parameter: the value \(x\) which might, or +might not, be such that adjective(\(x\)) is true. We allow this to be called +"it", though it can also have a calling name in some cases (see below). +

+ +

Clearly it ought to have the kind which defines the domain — so it's a rulebook +if the domain is all rulebooks, and so on — but it doesn't always do so. The +exception is that it is bogusly given the kind "number" if the adjective is +being defined only by I6 routines. This is done to avoid compiling very +inefficient code from the Standard Rules. For instance, the SR reads, in +slightly simplified form: +

+ +
+

Definition: a text is empty if I6 routine |"TEXT\_TY\_Empty"| says so.

+
+ +

rather than the more obvious: +

+ +
+

Definition: a text is empty if it is not |""|.

+
+ +

Both of these definitions work. But if the routine defining "empty" for text +is allowed to act on a text variable, Inform needs to compile code which acts +on block values held on the memory heap at run-time. That means it needs to +compile a memory heap; and that costs 8K or so of storage, making large +Z-machine games which don't need text alteration or lists impossible to fit into +the 64K array space limit. (There's also a benefit even if we do need a heap; +the adjective can act on a direct pointer to the structure, and no time is +wasted allocating memory and copying the block value first.) +

+ +

Add an it-variable to represent the value or object in the domain2.2.1 = +

+ +
+    kind *add_K = K_number;
+    adjective_meaning *am;
+    for (am = aph->adjective_meanings.sorted_meanings; am; am = am->next_sorted)
+        if ((Phrases::RawPhrasal::is_by_Inter_function(am) == FALSE) &&
+            (AdjectiveMeanings::domain_weak_match(K, AdjectiveMeanings::get_domain(am))))
+            add_K = K;
+
+    LocalVariables::add_pronoun(Frames::current_stack_frame(), EMPTY_WORDING, add_K);
+    LocalVariables::enable_possessive_form_of_it();
+
+ +

§3. We run through possible meanings of the APH which share the current weak +domain, and compile code which performs the stronger part of the domain +test at run-time. In practice, at present the only weak domain which might +have multiple definitions is "object", but that may change. +

+ +
+void RTAdjectives::list_compile(adjective_meaning *list_head,
+    ph_stack_frame *phsf, kind *K, int T, inter_symbol *t0_s) {
+    adjective_meaning *am;
+    for (am = list_head; am; am = am->next_sorted)
+        if ((AdjectiveMeanings::compilation_possible(am, T)) &&
+            (AdjectiveMeanings::domain_weak_match(K, AdjectiveMeanings::get_domain(am)))) {
+            current_sentence = am->defined_at;
+            Produce::inv_primitive(Emit::tree(), IF_BIP);
+            Produce::down(Emit::tree());
+                InferenceSubjects::emit_element_of_condition(am->domain_infs, t0_s);
+                Produce::code(Emit::tree());
+                Produce::down(Emit::tree());
+                    Produce::inv_primitive(Emit::tree(), RETURN_BIP);
+                    Produce::down(Emit::tree());
+                        if ((am->meaning_parity == FALSE) && (T == TEST_ADJECTIVE_TASK)) {
+                            Produce::inv_primitive(Emit::tree(), NOT_BIP);
+                            Produce::down(Emit::tree());
+                        }
+                        AdjectiveMeanings::emit_meaning(am, T, phsf);
+                        am->defined_already = TRUE;
+                        if ((am->meaning_parity == FALSE) && (T == TEST_ADJECTIVE_TASK)) {
+                            Produce::up(Emit::tree());
+                        }
+                    Produce::up(Emit::tree());
+                Produce::up(Emit::tree());
+            Produce::up(Emit::tree());
+        }
+}
+
+

§4. Adaptive text: +

+ +
+void RTAdjectives::agreements(void) {
+    if (Projects::get_language_of_play(Task::project()) == DefaultLanguage::get(NULL)) return;
+    adjective *aph;
+    LOOP_OVER(aph, adjective) {
+        wording PW = Clusters::get_form_general(aph->adjective_names, Projects::get_language_of_play(Task::project()), 1, -1);
+        if (Wordings::empty(PW)) continue;
+
+        packaging_state save = Routines::begin(aph->adjective_compilation.aph_iname);
+        inter_symbol *o_s = LocalVariables::add_named_call_as_symbol(I"o");
+        inter_symbol *force_plural_s = LocalVariables::add_named_call_as_symbol(I"force_plural");
+        inter_symbol *gna_s = LocalVariables::add_internal_local_as_symbol(I"gna");
+
+        Produce::inv_primitive(Emit::tree(), IFELSE_BIP);
+        Produce::down(Emit::tree());
+            Produce::inv_primitive(Emit::tree(), EQ_BIP);
+            Produce::down(Emit::tree());
+                Produce::val_symbol(Emit::tree(), K_value, o_s);
+                Produce::val_nothing(Emit::tree());
+            Produce::up(Emit::tree());
+            Produce::code(Emit::tree());
+            Produce::down(Emit::tree());
+                Produce::inv_primitive(Emit::tree(), STORE_BIP);
+                Produce::down(Emit::tree());
+                    Produce::ref_symbol(Emit::tree(), K_value, gna_s);
+                    Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 6);
+                Produce::up(Emit::tree());
+            Produce::up(Emit::tree());
+            Produce::code(Emit::tree());
+            Produce::down(Emit::tree());
+                Produce::inv_primitive(Emit::tree(), STORE_BIP);
+                Produce::down(Emit::tree());
+                    Produce::ref_symbol(Emit::tree(), K_value, gna_s);
+                    inter_name *iname = Hierarchy::find(GETGNAOFOBJECT_HL);
+                    Produce::inv_call_iname(Emit::tree(), iname);
+                    Produce::down(Emit::tree());
+                        Produce::val_symbol(Emit::tree(), K_value, o_s);
+                    Produce::up(Emit::tree());
+                Produce::up(Emit::tree());
+            Produce::up(Emit::tree());
+        Produce::up(Emit::tree());
+
+        Produce::inv_primitive(Emit::tree(), IF_BIP);
+        Produce::down(Emit::tree());
+            Produce::ref_symbol(Emit::tree(), K_value, force_plural_s);
+            Produce::code(Emit::tree());
+            Produce::down(Emit::tree());
+                Produce::inv_primitive(Emit::tree(), IFELSE_BIP);
+                Produce::down(Emit::tree());
+                    Produce::inv_primitive(Emit::tree(), NE_BIP);
+                    Produce::down(Emit::tree());
+                        Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(PRIOR_NAMED_LIST_GENDER_HL));
+                        Produce::val(Emit::tree(), K_number, LITERAL_IVAL, (inter_ti) -1);
+                    Produce::up(Emit::tree());
+                    Produce::code(Emit::tree());
+                    Produce::down(Emit::tree());
+                        Produce::inv_primitive(Emit::tree(), STORE_BIP);
+                        Produce::down(Emit::tree());
+                            Produce::ref_symbol(Emit::tree(), K_value, gna_s);
+                            Produce::inv_primitive(Emit::tree(), PLUS_BIP);
+                            Produce::down(Emit::tree());
+                                Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 3);
+                                Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(PRIOR_NAMED_LIST_GENDER_HL));
+                            Produce::up(Emit::tree());
+                        Produce::up(Emit::tree());
+                    Produce::up(Emit::tree());
+                    Produce::code(Emit::tree());
+                    Produce::down(Emit::tree());
+                        Produce::inv_primitive(Emit::tree(), STORE_BIP);
+                        Produce::down(Emit::tree());
+                            Produce::ref_symbol(Emit::tree(), K_value, gna_s);
+                            Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 3);
+                        Produce::up(Emit::tree());
+                    Produce::up(Emit::tree());
+                Produce::up(Emit::tree());
+            Produce::up(Emit::tree());
+        Produce::up(Emit::tree());
+
+        Produce::inv_primitive(Emit::tree(), STORE_BIP);
+        Produce::down(Emit::tree());
+            Produce::ref_symbol(Emit::tree(), K_value, gna_s);
+            Produce::inv_primitive(Emit::tree(), MODULO_BIP);
+            Produce::down(Emit::tree());
+                Produce::val_symbol(Emit::tree(), K_value, gna_s);
+                Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 6);
+            Produce::up(Emit::tree());
+        Produce::up(Emit::tree());
+
+        Produce::inv_primitive(Emit::tree(), SWITCH_BIP);
+        Produce::down(Emit::tree());
+            Produce::val_symbol(Emit::tree(), K_value, gna_s);
+            Produce::code(Emit::tree());
+            Produce::down(Emit::tree());
+                for (int gna=0; gna<6; gna++) {
+                    Produce::inv_primitive(Emit::tree(), CASE_BIP);
+                    Produce::down(Emit::tree());
+                        Produce::val(Emit::tree(), K_number, LITERAL_IVAL, (inter_ti) gna);
+                        Produce::code(Emit::tree());
+                        Produce::down(Emit::tree());
+                            Produce::inv_primitive(Emit::tree(), PRINT_BIP);
+                            Produce::down(Emit::tree());
+                                TEMPORARY_TEXT(T)
+                                int number_sought = 1, gender_sought = NEUTER_GENDER;
+                                if (gna%3 == 0) gender_sought = MASCULINE_GENDER;
+                                if (gna%3 == 1) gender_sought = FEMININE_GENDER;
+                                if (gna >= 3) number_sought = 2;
+                                wording AW = Clusters::get_form_general(aph->adjective_names,
+                                    Projects::get_language_of_play(Task::project()), number_sought, gender_sought);
+                                if (Wordings::nonempty(AW)) WRITE_TO(T, "%W", AW);
+                                else WRITE_TO(T, "%W", PW);
+                                Produce::val_text(Emit::tree(), T);
+                                DISCARD_TEXT(T)
+                            Produce::up(Emit::tree());
+                        Produce::up(Emit::tree());
+                    Produce::up(Emit::tree());
+                }
+            Produce::up(Emit::tree());
+        Produce::up(Emit::tree());
+
+        Routines::end(save);
+    }
+}
+
+void RTAdjectives::emit(adjective *aph) {
+    Produce::inv_call_iname(Emit::tree(), aph->adjective_compilation.aph_iname);
+    Produce::down(Emit::tree());
+        Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(PRIOR_NAMED_NOUN_HL));
+        Produce::inv_primitive(Emit::tree(), GE_BIP);
+        Produce::down(Emit::tree());
+            Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(PRIOR_NAMED_LIST_HL));
+            Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 2);
+        Produce::up(Emit::tree());
+    Produce::up(Emit::tree());
+    Produce::inv_primitive(Emit::tree(), STORE_BIP);
+    Produce::down(Emit::tree());
+        Produce::ref_iname(Emit::tree(), K_number, Hierarchy::find(SAY__P_HL));
+        Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 1);
+    Produce::up(Emit::tree());
+}
+
+ + +
+ + + diff --git a/inform7/assertions-module/Chapter 6/Adjective Ambiguity.w b/inform7/assertions-module/Chapter 6/Adjective Ambiguity.w new file mode 100644 index 000000000..b1fd96b0e --- /dev/null +++ b/inform7/assertions-module/Chapter 6/Adjective Ambiguity.w @@ -0,0 +1,273 @@ +[AdjectiveAmbiguity::] Adjective Ambiguity. + +Managing the multiple contextual meanings which a single adjective can have. + +@ Adjectives can have multiple meanings. For example, it is legal to define +both of these in the same source text: += (text as Inform 7) +Definition: a text is empty rather than non-empty if it is "". + +Definition: a table name is empty rather than non-empty if the +number of filled rows in it is 0. += +This gives two different meanings to both "empty" and "non-empty". We can +only work out which meaning is intended by looking at the context, that is, +at the kind of whatever it is applied to. For a text, the first sense applies, +and for a table name, the second. + +So, then, every adjective has the following data attached to it: + +@d ADJECTIVE_MEANING_LINGUISTICS_CALLBACK AdjectiveAmbiguity::new_set + += +typedef struct adjective_meaning_data { + struct adjective_meaning *possible_meanings; /* list in the order defined */ + struct adjective_meaning *sorted_meanings; /* list in logical precedence order */ +} adjective_meaning_data; + +void AdjectiveAmbiguity::new_set(adjective *adj) { + adj->adjective_meanings.possible_meanings = NULL; + adj->adjective_meanings.sorted_meanings = NULL; +} + +@ The following assigns a new meaning to a given word range: we find the +appropriate APH (creating if necessary) and then add the new meaning to the +end of its unsorted meaning list. + +We eventually need to sort this list of definitions into logical priority +order -- so that a definition applying to just Count Dracula precedes one +applying to men, which in turn precedes one applying to things. (Priority +order is irrelevant when two senses apply to domains with no overlap, as +in the case of texts and table names.) It's convenient and costs little +memory to keep the sorted list as a second linked list. + += +adjective *AdjectiveAmbiguity::add_meaning_to_adjective(adjective_meaning *am, + adjective *adj) { + adjective_meaning *aml = adj->adjective_meanings.possible_meanings; + if (aml == NULL) adj->adjective_meanings.possible_meanings = am; + else { + while (aml->next_meaning) aml = aml->next_meaning; + aml->next_meaning = am; + } + am->next_meaning = NULL; + am->owning_adjective = adj; + return adj; +} + +@ And here we log the unsorted list. + += +void AdjectiveAmbiguity::log(adjective *adj) { + if (adj == NULL) { LOG("\n"); return; } + adjective_meaning *am; + int n; + for (n=1, am = adj->adjective_meanings.possible_meanings; am; + n++, am = am->next_meaning) + LOG("%d: %W (domain:$j) (dk:%u)\n", n, am->adjective_index_text, + am->domain_infs, am->domain_kind); +} + +@ If the source tries to apply the word "open", say, to a given value or +object $X$, when does that make sense? + +We can only find out by checking every possible meaning of "open" to see +if it can accommodate the kind of value of $X$. But this time we use weak +checking, and make it weaker still since a null kind is taken to mean "any +object", either in the AM's definition -- which can happen if we are very +early in Inform's run -- or because the caller doesn't actually know the +kind of value of $X$. (In other words, adjectives tend to assume they apply +to objects rather than other values.) This means we will accept some +logically impossible outcomes -- we would say that it's acceptable to apply +"open" to an animal, say -- but that is actually a good thing. It means +that "list of open things" or "something open" are allowed. Source text +such as: + +>> The labrador puppy is an open animal. + +will successfully parse, but then result in higher-level problem messages. +The following does compile: + +>> now the labrador puppy is open; + +but results in a run-time problem message when it executes. + +It makes no difference what order we check the AMs in, so we can use the +unsorted list, which is helpful since we may need to call this routine +early in the run when sorting cannot yet be done. + += +int AdjectiveAmbiguity::can_be_applied_to(adjective *adj, kind *K) { + if (adj) { + adjective_meaning *am; + for (am = adj->adjective_meanings.possible_meanings; am; am = am->next_meaning) { + if (am->domain_infs == NULL) { + if (am->setting_domain) @; + am->setting_domain = TRUE; + AdjectiveMeanings::set_definition_domain(am, TRUE); + am->setting_domain = FALSE; + } + kind *am_kind = AdjectiveMeanings::get_domain(am); + if (Kinds::Behaviour::is_object(am_kind)) { + if (K == NULL) return TRUE; + if (Kinds::Behaviour::is_object(K)) return TRUE; + } else { + if ((K) && (Kinds::Behaviour::is_object(K) == FALSE) && + (Kinds::compatible(K, am_kind) == ALWAYS_MATCH)) + return TRUE; + } + } + } + return FALSE; +} + +@ = + if (problem_count == 0) { + Problems::quote_source(1, current_sentence); + Problems::quote_wording(2, Clusters::get_form(adj->adjective_names, FALSE)); + StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_AdjectiveCircular)); + Problems::issue_problem_segment( + "In the sentence %1, it looks as if the definition of the adjective " + "'%2' may be circular."); + Problems::issue_problem_end(); + } + return FALSE; + +@ Does a given adjective have any interpretation as an enumerated property +value, or an either/or property? If so we return the earliest known. + += +instance *AdjectiveAmbiguity::has_enumerative_meaning(adjective *adj) { + adjective_meaning *am; + for (am = adj->adjective_meanings.possible_meanings; am; am = am->next_meaning) + if (InstanceAdjectives::is_enumerative(am)) + return RETRIEVE_POINTER_instance(am->detailed_meaning); + return NULL; +} + +property *AdjectiveAmbiguity::has_either_or_property_meaning(adjective *adj, int *sense) { + if (adj) + for (adjective_meaning *am = adj->adjective_meanings.possible_meanings; + am; am = am->next_meaning) + if (Properties::EitherOr::is_either_or_adjective(am)) { + if (sense) *sense = am->meaning_parity; + return RETRIEVE_POINTER_property(am->detailed_meaning); + } + return NULL; +} + +@ Occasionally we just want one meaning: + += +adjective_meaning *AdjectiveAmbiguity::first_meaning(adjective *adj) { + if (adj == NULL) return NULL; + return adj->adjective_meanings.possible_meanings; +} + +@h Sorting lists of meanings. +After meanings have been declared, a typical APH will have a disordered +"possible meaning" list and an empty "sorted meaning" list. The following +insertion-sorts[1] the possibles list into the sorted list. + +[1] Well, yes, but these are very short lists, typically 5 items or fewer. + += +void AdjectiveAmbiguity::sort(adjective *adj) { + if (adj == NULL) internal_error("tried to sort meanings for null adjective"); + adjective_meaning *unsorted_head = adj->adjective_meanings.possible_meanings; + adjective_meaning *sorted_head = NULL; + adjective_meaning *am, *am2; + for (am = unsorted_head; am; am = am->next_meaning) + if (am->domain_infs == NULL) + AdjectiveMeanings::set_definition_domain(am, TRUE); + for (am = unsorted_head; am; am = am->next_meaning) { + if (sorted_head == NULL) { + sorted_head = am; + am->next_sorted = NULL; + } else { + adjective_meaning *lastdef = NULL; + for (am2 = sorted_head; am2; am2 = am2->next_sorted) { + if (AdjectiveMeanings::compare(am, am2) == 1) { + if (lastdef == NULL) { + sorted_head = am; + am->next_sorted = am2; + } else { + lastdef->next_sorted = am; + am->next_sorted = am2; + } + break; + } + if (am2->next_sorted == NULL) { + am2->next_sorted = am; + am->next_sorted = NULL; + break; + } + lastdef = am2; + } + } + } + adj->adjective_meanings.sorted_meanings = sorted_head; +} + +adjective_meaning *AdjectiveAmbiguity::get_sorted_definition_list(adjective *adj) { + return adj->adjective_meanings.sorted_meanings; +} + +@ With that sorting done, we can begin to use an adjective. Suppose there has +been an assertion sentence like this: + +>> The ormolu clock is fixed in place. + +"Fixed in place" is identified as an adjective, |adj|; the "ormulo clock" is +what it applies to, stored in either |infs_to_assert_on| or |val_to_assert_on| +depending on what it is. |kind_domain| is what kind we think this has. |parity| +is equal to |TRUE|. + +What happens is that the list of definitions for "fixed in place" is checked +in logical precedence order, and //AdjectiveMeanings::assert_single// called +on any kind which the "ormolu clock" matches. (That will probably be the +definition for the "fixed in place" either/or property for things, unless +someone has given the adjective some special meaning unique to the clock.) The +first adjective meaning to be assertable then wins. + +The following routine therefore acts as a junction-box, deciding which sense +of the adjective is to be applied. We return |TRUE| if we were able to find a +definition which could be asserted and which the clock matched, and |FALSE| if +there was no definition which applied, or if none of those which did could be +asserted for it. + += +int AdjectiveAmbiguity::assert(adjective *adj, kind *kind_domain, + inference_subject *infs_to_assert_on, parse_node *val_to_assert_on, int parity) { + AdjectiveAmbiguity::sort(adj); + for (adjective_meaning *am = adj->adjective_meanings.sorted_meanings; + am; am = am->next_sorted) { + if (AdjectiveMeanings::domain_weak_match(kind_domain, + AdjectiveMeanings::get_domain(am)) == FALSE) continue; + if (AdjectiveMeanings::domain_subj_compare(infs_to_assert_on, am) == FALSE) + continue; + if (AdjectiveMeanings::assert_single(am, infs_to_assert_on, val_to_assert_on, parity)) + return TRUE; + } + return FALSE; +} + +@ Similarly, the following produces an I6 schema to carry out a task for the +adjective. (See //AdjectiveMeanings::set_i6_schema// for tasks.) + += +i6_schema *AdjectiveAmbiguity::schema_for_task(adjective *adj, kind *kind_domain, int T) { + if (kind_domain == NULL) kind_domain = K_object; + AdjectiveAmbiguity::sort(adj); + for (adjective_meaning *am = adj->adjective_meanings.sorted_meanings; am; am = am->next_sorted) { + kind *am_kind = AdjectiveMeanings::get_domain(am); + if (am_kind == NULL) { + AdjectiveMeanings::set_definition_domain(am, FALSE); + am_kind = AdjectiveMeanings::get_domain(am); + } + if (AdjectiveMeanings::domain_weak_match(kind_domain, am_kind) == FALSE) continue; + i6_schema *i6s = AdjectiveMeanings::schema_for_task(am, T); + if (i6s) return i6s; + } + return NULL; +} diff --git a/inform7/runtime-module/Chapter 4/Adjectives.w b/inform7/runtime-module/Chapter 4/Adjectives.w new file mode 100644 index 000000000..8c617e52b --- /dev/null +++ b/inform7/runtime-module/Chapter 4/Adjectives.w @@ -0,0 +1,299 @@ +[RTAdjectives::] Adjectives. + +To compile run-time support for adjective definitions. + +@ The following utility is used to loop through the sorted meaning list, +skipping over any which have been dealt with already. + += +adjective_meaning *RTAdjectives::list_next_domain_kind(adjective_meaning *am, kind **K, int T) { + while ((am) && ((am->defined_already) || (AdjectiveMeanings::compilation_possible(am, T) == FALSE))) + am = am->next_sorted; + if (am == NULL) return NULL; + *K = AdjectiveMeanings::get_domain(am); + return am->next_sorted; +} + +@ And this is where we do the iteration. The idea is that one adjective +definition routine is defined (for each task number) which covers all of +the weakly-domain-equal definitions for the same adjective. Thus one +routine might handle "detailed" for rulebooks, and another might handle +"detailed" for all of its meanings associated with objects -- possibly +many AMs. + += +void RTAdjectives::compile_support_code(void) { + @; + int T; + for (T=1; T<=NO_ADJECTIVE_TASKS; T++) { + adjective *aph; + LOOP_OVER(aph, adjective) { + adjective_meaning *am; + for (am = aph->adjective_meanings.possible_meanings; am; am = am->next_meaning) + am->defined_already = FALSE; + for (am = aph->adjective_meanings.sorted_meanings; am; ) { + kind *K = NULL; + am = RTAdjectives::list_next_domain_kind(am, &K, T); + if (K) + @; + } + } + } +} + +@ It's unlikely that we have got this far without the domains for the AMs +having been established, but certainly possible. We need the domains to be +known in order to sort. + +@ = + adjective *aph; + LOOP_OVER(aph, adjective) { + adjective_meaning *am; + for (am = aph->adjective_meanings.possible_meanings; am; am = am->next_meaning) { + AdjectiveMeanings::set_definition_domain(am, FALSE); + am->defined_already = FALSE; + } + AdjectiveAmbiguity::sort(aph); + } + +@ The following is a standard way to compile a one-off routine. + +@ = + wording W = Adjectives::get_nominative_singular(aph); + LOGIF(VARIABLE_CREATIONS, "Compiling support code for %W applying to %u, task %d\n", + W, K, T); + + inter_name *iname = AdjectiveMeanings::iname(aph, T, RTKinds::weak_id(K)); + packaging_state save = Routines::begin(iname); + @; + + TEMPORARY_TEXT(C) + WRITE_TO(C, "meaning of \""); + if (Wordings::nonempty(W)) WRITE_TO(C, "%~W", W); + else WRITE_TO(C, ""); + WRITE_TO(C, "\""); + Emit::code_comment(C); + DISCARD_TEXT(C) + + if (problem_count == 0) { + local_variable *it_lv = LocalVariables::it_variable(); + inter_symbol *it_s = LocalVariables::declare_this(it_lv, FALSE, 8); + RTAdjectives::list_compile(aph->adjective_meanings.sorted_meanings, Frames::current_stack_frame(), K, T, it_s); + } + Produce::rfalse(Emit::tree()); + + Routines::end(save); + +@ The stack frame has just one call parameter: the value $x$ which might, or +might not, be such that adjective($x$) is true. We allow this to be called +"it", though it can also have a calling name in some cases (see below). + +Clearly it ought to have the kind which defines the domain -- so it's a rulebook +if the domain is all rulebooks, and so on -- but it doesn't always do so. The +exception is that it is bogusly given the kind "number" if the adjective is +being defined only by I6 routines. This is done to avoid compiling very +inefficient code from the Standard Rules. For instance, the SR reads, in +slightly simplified form: + +>> Definition: a text is empty if I6 routine |"TEXT\_TY\_Empty"| says so. + +rather than the more obvious: + +>> Definition: a text is empty if it is not |""|. + +Both of these definitions work. But if the routine defining "empty" for text +is allowed to act on a text variable, Inform needs to compile code which acts +on block values held on the memory heap at run-time. That means it needs to +compile a memory heap; and that costs 8K or so of storage, making large +Z-machine games which don't need text alteration or lists impossible to fit into +the 64K array space limit. (There's also a benefit even if we do need a heap; +the adjective can act on a direct pointer to the structure, and no time is +wasted allocating memory and copying the block value first.) + +@ = + kind *add_K = K_number; + adjective_meaning *am; + for (am = aph->adjective_meanings.sorted_meanings; am; am = am->next_sorted) + if ((Phrases::RawPhrasal::is_by_Inter_function(am) == FALSE) && + (AdjectiveMeanings::domain_weak_match(K, AdjectiveMeanings::get_domain(am)))) + add_K = K; + + LocalVariables::add_pronoun(Frames::current_stack_frame(), EMPTY_WORDING, add_K); + LocalVariables::enable_possessive_form_of_it(); + +@ We run through possible meanings of the APH which share the current weak +domain, and compile code which performs the stronger part of the domain +test at run-time. In practice, at present the only weak domain which might +have multiple definitions is "object", but that may change. + += +void RTAdjectives::list_compile(adjective_meaning *list_head, + ph_stack_frame *phsf, kind *K, int T, inter_symbol *t0_s) { + adjective_meaning *am; + for (am = list_head; am; am = am->next_sorted) + if ((AdjectiveMeanings::compilation_possible(am, T)) && + (AdjectiveMeanings::domain_weak_match(K, AdjectiveMeanings::get_domain(am)))) { + current_sentence = am->defined_at; + Produce::inv_primitive(Emit::tree(), IF_BIP); + Produce::down(Emit::tree()); + InferenceSubjects::emit_element_of_condition(am->domain_infs, t0_s); + Produce::code(Emit::tree()); + Produce::down(Emit::tree()); + Produce::inv_primitive(Emit::tree(), RETURN_BIP); + Produce::down(Emit::tree()); + if ((am->meaning_parity == FALSE) && (T == TEST_ADJECTIVE_TASK)) { + Produce::inv_primitive(Emit::tree(), NOT_BIP); + Produce::down(Emit::tree()); + } + AdjectiveMeanings::emit_meaning(am, T, phsf); + am->defined_already = TRUE; + if ((am->meaning_parity == FALSE) && (T == TEST_ADJECTIVE_TASK)) { + Produce::up(Emit::tree()); + } + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + } +} + +@ Adaptive text: + += +void RTAdjectives::agreements(void) { + if (Projects::get_language_of_play(Task::project()) == DefaultLanguage::get(NULL)) return; + adjective *aph; + LOOP_OVER(aph, adjective) { + wording PW = Clusters::get_form_general(aph->adjective_names, Projects::get_language_of_play(Task::project()), 1, -1); + if (Wordings::empty(PW)) continue; + + packaging_state save = Routines::begin(aph->adjective_compilation.aph_iname); + inter_symbol *o_s = LocalVariables::add_named_call_as_symbol(I"o"); + inter_symbol *force_plural_s = LocalVariables::add_named_call_as_symbol(I"force_plural"); + inter_symbol *gna_s = LocalVariables::add_internal_local_as_symbol(I"gna"); + + Produce::inv_primitive(Emit::tree(), IFELSE_BIP); + Produce::down(Emit::tree()); + Produce::inv_primitive(Emit::tree(), EQ_BIP); + Produce::down(Emit::tree()); + Produce::val_symbol(Emit::tree(), K_value, o_s); + Produce::val_nothing(Emit::tree()); + Produce::up(Emit::tree()); + Produce::code(Emit::tree()); + Produce::down(Emit::tree()); + Produce::inv_primitive(Emit::tree(), STORE_BIP); + Produce::down(Emit::tree()); + Produce::ref_symbol(Emit::tree(), K_value, gna_s); + Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 6); + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + Produce::code(Emit::tree()); + Produce::down(Emit::tree()); + Produce::inv_primitive(Emit::tree(), STORE_BIP); + Produce::down(Emit::tree()); + Produce::ref_symbol(Emit::tree(), K_value, gna_s); + inter_name *iname = Hierarchy::find(GETGNAOFOBJECT_HL); + Produce::inv_call_iname(Emit::tree(), iname); + Produce::down(Emit::tree()); + Produce::val_symbol(Emit::tree(), K_value, o_s); + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + + Produce::inv_primitive(Emit::tree(), IF_BIP); + Produce::down(Emit::tree()); + Produce::ref_symbol(Emit::tree(), K_value, force_plural_s); + Produce::code(Emit::tree()); + Produce::down(Emit::tree()); + Produce::inv_primitive(Emit::tree(), IFELSE_BIP); + Produce::down(Emit::tree()); + Produce::inv_primitive(Emit::tree(), NE_BIP); + Produce::down(Emit::tree()); + Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(PRIOR_NAMED_LIST_GENDER_HL)); + Produce::val(Emit::tree(), K_number, LITERAL_IVAL, (inter_ti) -1); + Produce::up(Emit::tree()); + Produce::code(Emit::tree()); + Produce::down(Emit::tree()); + Produce::inv_primitive(Emit::tree(), STORE_BIP); + Produce::down(Emit::tree()); + Produce::ref_symbol(Emit::tree(), K_value, gna_s); + Produce::inv_primitive(Emit::tree(), PLUS_BIP); + Produce::down(Emit::tree()); + Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 3); + Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(PRIOR_NAMED_LIST_GENDER_HL)); + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + Produce::code(Emit::tree()); + Produce::down(Emit::tree()); + Produce::inv_primitive(Emit::tree(), STORE_BIP); + Produce::down(Emit::tree()); + Produce::ref_symbol(Emit::tree(), K_value, gna_s); + Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 3); + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + + Produce::inv_primitive(Emit::tree(), STORE_BIP); + Produce::down(Emit::tree()); + Produce::ref_symbol(Emit::tree(), K_value, gna_s); + Produce::inv_primitive(Emit::tree(), MODULO_BIP); + Produce::down(Emit::tree()); + Produce::val_symbol(Emit::tree(), K_value, gna_s); + Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 6); + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + + Produce::inv_primitive(Emit::tree(), SWITCH_BIP); + Produce::down(Emit::tree()); + Produce::val_symbol(Emit::tree(), K_value, gna_s); + Produce::code(Emit::tree()); + Produce::down(Emit::tree()); + for (int gna=0; gna<6; gna++) { + Produce::inv_primitive(Emit::tree(), CASE_BIP); + Produce::down(Emit::tree()); + Produce::val(Emit::tree(), K_number, LITERAL_IVAL, (inter_ti) gna); + Produce::code(Emit::tree()); + Produce::down(Emit::tree()); + Produce::inv_primitive(Emit::tree(), PRINT_BIP); + Produce::down(Emit::tree()); + TEMPORARY_TEXT(T) + int number_sought = 1, gender_sought = NEUTER_GENDER; + if (gna%3 == 0) gender_sought = MASCULINE_GENDER; + if (gna%3 == 1) gender_sought = FEMININE_GENDER; + if (gna >= 3) number_sought = 2; + wording AW = Clusters::get_form_general(aph->adjective_names, + Projects::get_language_of_play(Task::project()), number_sought, gender_sought); + if (Wordings::nonempty(AW)) WRITE_TO(T, "%W", AW); + else WRITE_TO(T, "%W", PW); + Produce::val_text(Emit::tree(), T); + DISCARD_TEXT(T) + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + } + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + + Routines::end(save); + } +} + +void RTAdjectives::emit(adjective *aph) { + Produce::inv_call_iname(Emit::tree(), aph->adjective_compilation.aph_iname); + Produce::down(Emit::tree()); + Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(PRIOR_NAMED_NOUN_HL)); + Produce::inv_primitive(Emit::tree(), GE_BIP); + Produce::down(Emit::tree()); + Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(PRIOR_NAMED_LIST_HL)); + Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 2); + Produce::up(Emit::tree()); + Produce::up(Emit::tree()); + Produce::inv_primitive(Emit::tree(), STORE_BIP); + Produce::down(Emit::tree()); + Produce::ref_iname(Emit::tree(), K_number, Hierarchy::find(SAY__P_HL)); + Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 1); + Produce::up(Emit::tree()); +}