To create one |phrase| object for each phrase declaration in the source text.


§1. Definitions.

§2. As noted in the introduction to this chapter, a phrase structure is created for each "To..." definition and each rule in the source text. It is divided internally into five substructures, the PHTD, PHUD, PHRCD, PHSF and PHOD.

Two more abbreviations appear in this section. The first is the EFF, or the "effect" of a phrase, which categorises all phrases into four: "To..." phrases, phrases used to define adjective, rules explicitly naming a rulebook they belong to, and rules not doing so. (This is called the "effect" because it decides under what circumstances the phrase will be executed at run-time.)

    define TO_PHRASE_EFF 1 				    "To award (some - number) points: ..."
    define RULE_IN_RULEBOOK_EFF 2 		    "Before taking a container, ..."
    define RULE_NOT_IN_RULEBOOK_EFF 3 	    "At 9 PM: ...", "This is the zap rule: ..."
    define DEFINITIONAL_PHRASE_EFF 4 	    "Definition: a container is roomy if: ..."

§3. The second new abbreviation is MOR, the "manner of return", which is only of interest for "To..." phrases. Some of these decide a value, some decide a condition, some decide nothing (but exist in order to do something); the exceptional case is the last, DECIDES_NOTHING_AND_RETURNS_MOR, which marks out a phrase which exits the phrase it is invoked from — like the statement return in a C function. (There is no way to create such a phrase in source text without using an inline definition, and the intention is that only the Standard Rules will ever make phrases like it.)

    define DONT_KNOW_MOR 1						    but ask me later
    define DECIDES_NOTHING_MOR 2				    e.g., "award 4 points"
    define DECIDES_VALUE_MOR 3					    e.g., "square root of 16"
    define DECIDES_CONDITION_MOR 4				    e.g., "a random chance of 1 in 3 succeeds"
    define DECIDES_NOTHING_AND_RETURNS_MOR 5	    e.g., "continue the action"

§4. And here is the structure. Note that the MOR and EFF are stored inside the sub-structures, and aren't visible here; but they're relevant to the code below.

    typedef struct phrase {
        struct parse_node *declaration_node;     ROUTINE_NT node where declared
        int inline_wn;     word number of inline I6 definition, or -1 if not inline
        struct inter_schema *inter_head_defn;     inline definition translated to inter, if possible
        struct inter_schema *inter_tail_defn;     inline definition translated to inter, if possible
        int inter_defn_converted;     has this been tried yet?
        int inline_mor;     manner of return for inline I6 definition, or UNKNOWN_NT
        struct wording ph_documentation_symbol;     cross-reference with documentation
        struct compilation_module *owning_module;
        struct package_request *requests_package;
        struct package_request *rule_package;

        struct ph_type_data type_data;
        struct ph_usage_data usage_data;
        struct ph_runtime_context_data runtime_context_data;
        struct ph_stack_frame stack_frame;
        struct ph_options_data options_data;

        int at_least_one_compiled_form_needed;     do we still need to compile this?
        int compile_with_run_time_debugging;     in the RULES command
        struct inter_name *ph_iname;     or NULL for inline phrases
        int imported;

        struct phrase *next_in_logical_order;     for "to..." phrases only
        int sequence_count;     within the logical order list, from 0

        MEMORY_MANAGEMENT
    } phrase;

The structure phrase is accessed in 2/si, 10/cap, 14/rv, 14/ds2, 15/pr, 20/eq, 21/rl, 21/rb, 21/rl2, 21/ac, 22/cs, 22/pu, 22/prcd, 22/ptd, 22/dptd, 22/po, 22/pav, 22/tp, 22/tp2, 22/pi, 25/in, 25/pi, 25/ci, 25/cii, 25/cp and here.

§5. "To..." phrases, though no others, are listed in logical precedence order:

    struct phrase *first_in_logical_order = NULL;

§6. The life of a phrase structure begins when we look at the parse-tree representation of its declaration in the source text.

A phrase is inline if and only if its definition consists of a single invocation which is given as verbatim I6.

    void Phrases::create_from_preamble(parse_node *p) {
        if ((p == NULL) || (ParseTree::get_type(p) != ROUTINE_NT))
            internal_error("a phrase preamble should be at a ROUTINE_NT node");
        int inline_wn = -1; 		    the word number of an inline I6 definition if any
        int mor = DONT_KNOW_MOR;	    and its manner of return
        wording OW = EMPTY_WORDING;	    the text of the phrase options, if any
        wording documentation_W = EMPTY_WORDING;     the documentation reference, if any

        <Look for an inline definition 6.1>;

        ph_options_data phod;
        ph_type_data phtd;
        ph_usage_data phud;
        ph_stack_frame phsf;
        ph_runtime_context_data phrcd;

        <Parse for the PHUD in fine mode 6.2>;

        int effect = Phrases::Usage::get_effect(&phud);
        if ((inline_wn >= 0) && (effect != TO_PHRASE_EFF)) <Inline is for To... phrases only 6.10>;

        if ((effect != DEFINITIONAL_PHRASE_EFF) && (p->down == NULL))
            <There seems to be no definition 6.9>;

        <Construct the PHTD, find the phrase options, find the documentation reference 6.3>;
        <Construct the PHOD 6.4>;
        <Construct the PHSF, using the PHTD and PHOD 6.5>;
        <Construct the PHRCD 6.6>;

        phrase *new_ph;
        <Create the phrase structure 6.8>;
        <Tell other parts of Inform about this new phrase 6.7>;
    }

The function Phrases::create_from_preamble is used in 22/cs (§4).

§6.1. <Look for an inline definition 6.1> =

        if ((p->down) && (p->down->down) && (p->down->down->next == NULL))
            Phrases::parse_possible_inline_defn(
                ParseTree::get_text(p->down->down), &inline_wn, &mor);
        if (inline_wn >= 0) {
            wchar_t *inline_defn = Lexer::word_text(inline_wn);
            if (Wide::len(inline_defn) >= MAX_INLINE_DEFN_LENGTH)
                <Forbid overly long inline definitions 6.1.1>;
        }

This code is used in §6.

§6.2. <Parse for the PHUD in fine mode 6.2> =

        phud = Phrases::Usage::new(ParseTree::get_text(p), FALSE);

This code is used in §6.

§6.3. <Construct the PHTD, find the phrase options, find the documentation reference 6.3> =

        wording XW = Phrases::Usage::get_preamble_text(&phud);
        phtd = Phrases::TypeData::new();
        if (inline_wn >= 0) Phrases::TypeData::make_inline(&phtd);
        switch (effect) {
            case TO_PHRASE_EFF:
                documentation_W = Index::DocReferences::position_of_symbol(&XW);
                Phrases::TypeData::Textual::parse(&phtd, XW, &OW);
                break;
            case DEFINITIONAL_PHRASE_EFF:
                Phrases::TypeData::set_mor(&phtd, DECIDES_CONDITION_MOR, NULL);
                break;
            default:
                Phrases::TypeData::set_mor(&phtd, DECIDES_NOTHING_AND_RETURNS_MOR, NULL);
                break;
        }

This code is used in §6.

§6.4. <Construct the PHOD 6.4> =

        phod = Phrases::Options::parse_declared_options(OW);

This code is used in §6.

§6.5. The stack frame needs to know the kind of this phrase — something like

        phrase number -> text

— in order to work out what happens when values are decided by it later on. We also tell the stack frame if there are phrase options, because then a special parameter called {phrase options} is available when expanding inline definitions.

<Construct the PHSF, using the PHTD and PHOD 6.5> =

        phsf = Frames::new();
        Phrases::TypeData::into_stack_frame(&phsf, &phtd,
            Phrases::TypeData::kind(&phtd), TRUE);
        if (Phrases::Options::allows_options(&phod))
            LocalVariables::options_parameter_is_needed(&phsf);

This code is used in §6.

§6.6. <Construct the PHRCD 6.6> =

        phrcd = Phrases::Context::new();

This code is used in §6.

§6.7. <Tell other parts of Inform about this new phrase 6.7> =

        switch (effect) {
            case TO_PHRASE_EFF:
                Routines::ToPhrases::new(new_ph);
                break;
            case DEFINITIONAL_PHRASE_EFF:
                <Give this phrase a local variable for the subject of the definition 6.7.1>;
                break;
            case RULE_IN_RULEBOOK_EFF:
                Rules::request_automatic_placement(
                    Phrases::Usage::to_rule(&(new_ph->usage_data), new_ph));
                new_ph->compile_with_run_time_debugging = TRUE;
                break;
            case RULE_NOT_IN_RULEBOOK_EFF:
                Phrases::Usage::to_rule(&(new_ph->usage_data), new_ph);
                new_ph->compile_with_run_time_debugging = TRUE;
                break;
        }

This code is used in §6.

§6.7.1. If a phrase defines an adjective, like so:

Definition: A container is capacious if: ...

we need to make the pronoun "it" a local variable of kind "container" in the stack frame used to compile the "..." part. If it uses a calling, like so:

Definition: A container (called the sack) is capacious if: ...

then we also want the name "sack" to refer to this. Here's where we take care of it:

<Give this phrase a local variable for the subject of the definition 6.7.1> =

        wording CW = EMPTY_WORDING;
        kind *K = NULL;
        Phrases::Phrasal::define_adjective_by_phrase(p, new_ph, &CW, &K);
        LocalVariables::add_pronoun(&(new_ph->stack_frame), CW, K);

This code is used in §6.7.

§6.8. <Create the phrase structure 6.8> =

        wording XW = Phrases::Usage::get_preamble_text(&phud);
        LOGIF(PHRASE_CREATIONS, "Creating phrase: <%W>\n$U", XW, &phud);

        new_ph = CREATE(phrase);
        new_ph->declaration_node = p;

        new_ph->options_data = phod;
        new_ph->runtime_context_data = phrcd;
        new_ph->stack_frame = phsf;
        new_ph->type_data = phtd;
        new_ph->usage_data = phud;

        new_ph->inline_wn = inline_wn;
        new_ph->inter_head_defn = NULL;
        new_ph->inter_tail_defn = NULL;
        new_ph->inter_defn_converted = FALSE;
        new_ph->inline_mor = mor;
        new_ph->ph_iname = NULL;
        new_ph->imported = FALSE;
        new_ph->owning_module = Modules::find(current_sentence);
        new_ph->requests_package = NULL;
        if (inline_wn >= 0) {
            new_ph->at_least_one_compiled_form_needed = FALSE;
        } else {
            new_ph->at_least_one_compiled_form_needed = TRUE;
        }
        new_ph->compile_with_run_time_debugging = FALSE;

        new_ph->next_in_logical_order = NULL;
        new_ph->sequence_count = -1;

        new_ph->ph_documentation_symbol = documentation_W;

This code is used in §6.

§6.9. <There seems to be no definition 6.9> =

        Problems::Issue::sentence_problem(_p_(PM_Undefined),
            "there doesn't seem to be any definition here",
            "so I can't see what this rule or phrase would do.");

This code is used in §6.

§6.1.1. That just leaves two problem messages about inline definitions:

<Forbid overly long inline definitions 6.1.1> =

        LOG("Inline definition: <%s>\n", inline_defn);
        Problems::Issue::sentence_problem(_p_(PM_InlineTooLong),
            "the inline definition of this 'to...' phrase is too long",
            "using a quantity of Inform 6 code which exceeds the fairly small limit "
            "allowed. You will need either to write the phrase definition in Inform 7, "
            "or to call an I6 routine which you define elsewhere with an 'Include ...'.");
        inline_defn[MAX_INLINE_DEFN_LENGTH-1] = 0;

This code is used in §6.1.

§6.10. <Inline is for To... phrases only 6.10> =

        Problems::Issue::sentence_problem(_p_(PM_InlineRule),
            "only 'to...' phrases can be given inline Inform 6 definitions",
            "and in particular rules and adjective definitions can't.");

This code is used in §6.

§7. Inline definitions open with a raw Inform 6 inclusion. The lexer processes those as two words: first (-, which serves as a marker, and then the raw text of the inclusion treated as a single "word".

Some inline definitions also mark themselves to be included only in To phrases of the right sort: it makes no sense to respond "yes" to a phrase "To decide what number is...", for instance.

    <inline-phrase-definition> ::=
        (- ### - in to only | 			==> DECIDES_NOTHING_MOR; <<inlinecode>> = Wordings::first_wn(WR[1])
        (- ### - in to decide if only |	==> DECIDES_CONDITION_MOR; <<inlinecode>> = Wordings::first_wn(WR[1])
        (- ### - in to decide only |	==> DECIDES_VALUE_MOR; <<inlinecode>> = Wordings::first_wn(WR[1])
        (- ### |						==> DONT_KNOW_MOR; <<inlinecode>> = Wordings::first_wn(WR[1])
        (- ### ...						==> DONT_KNOW_MOR; <<inlinecode>> = Wordings::first_wn(WR[1]); <Issue PM_TailAfterInline problem 7.1>

§7.1. <Issue PM_TailAfterInline problem 7.1> =

        *X = DONT_KNOW_MOR;
        Problems::Issue::sentence_problem(_p_(PM_TailAfterInline),
            "some unexpected text appears after the tail of an inline definition",
            "placed within '(-' and '-)' markers to indicate that it is written in "
            "Inform 6. Here, there seems to be something extra after the '-)'.");

This code is used in §7.

§8. And this is used when the preamble is first looked at:

    void Phrases::parse_possible_inline_defn(wording W, int *wn, int *mor) {
        LOGIF(MATCHING, "form of inline: %W\n", W);
        *wn = -1;
        if (<inline-phrase-definition>(W)) { *wn = <<inlinecode>>; *mor = <<r>>; }
    }

The function Phrases::parse_possible_inline_defn is used in §6.1.

§9. Miscellaneous. That completes the process of creation. Here's how we log them:

    void Phrases::log(phrase *ph) {
        if (ph == NULL) { LOG("RULE:NULL"); return; }
        LOG("%n", Phrases::iname(ph));
        Phrases::Usage::log_rule_name(&(ph->usage_data));
    }

    void Phrases::log_briefly(phrase *ph) {
        Phrases::TypeData::Textual::log_briefly(&(ph->type_data));
    }

The function Phrases::log is used in 1/cm (§5, §6.6), 22/tp (§6).

The function Phrases::log_briefly is used in 25/in (§10).

§10. Relatedly, for indexing purposes:

    void Phrases::write_HTML_representation(OUTPUT_STREAM, phrase *ph, int format) {
        Phrases::TypeData::Textual::write_HTML_representation(OUT, &(ph->type_data), format, NULL);
    }

The function Phrases::write_HTML_representation is used in 2/sq (§1), 22/dptd (§5, §6.1), 22/tp (§4).

§11. Some access functions:

    int Phrases::compiled_inline(phrase *ph) {
        if (ph->inline_wn < 0) return FALSE;
        return TRUE;
    }

    wchar_t *Phrases::get_inline_definition(phrase *ph) {
        if (ph->inline_wn < 0)
            internal_error("tried to access inline definition of non-inline phrase");
        return Lexer::word_text(ph->inline_wn);
    }

    inter_schema *Phrases::get_inter_head(phrase *ph) {
        if (ph->inter_defn_converted == FALSE) {
            if (ph->inline_wn >= 0) {
                InterSchemas::from_inline_phrase_definition(Phrases::get_inline_definition(ph), &(ph->inter_head_defn), &(ph->inter_tail_defn));
            }
            ph->inter_defn_converted = TRUE;
        }
        return ph->inter_head_defn;
    }

    inter_schema *Phrases::get_inter_tail(phrase *ph) {
        if (ph->inter_defn_converted == FALSE) {
            if (ph->inline_wn >= 0) {
                InterSchemas::from_inline_phrase_definition(Phrases::get_inline_definition(ph), &(ph->inter_head_defn), &(ph->inter_tail_defn));
            }
            ph->inter_defn_converted = TRUE;
        }
        return ph->inter_tail_defn;
    }

    inter_name *Phrases::iname(phrase *ph) {
        if (ph->ph_iname == NULL) {
            package_request *PR = Hierarchy::package(ph->owning_module, ADJECTIVE_PHRASES_HAP);
            ph->ph_iname = Hierarchy::make_iname_in(DEFINITION_FN_HL, PR);
        }
        return ph->ph_iname;
    }

    parse_node *Phrases::declaration_node(phrase *ph) {
        return ph->declaration_node;
    }

The function Phrases::compiled_inline is used in 22/pav (§7).

The function Phrases::get_inline_definition is used in 22/tp (§8).

The function Phrases::get_inter_head is used in 25/cii (§1.3).

The function Phrases::get_inter_tail is used in 25/cii (§1.3).

The function Phrases::iname is used in §9, 21/rl (§19), 22/prcd (§10, §10.1), 22/tp2 (§4), 23/abp (§1), 25/cp (§4).

The function Phrases::declaration_node is used in 22/cs (§10.6, §10.7), 22/pu (§10.2.1), 22/tp (§4, §7.1, §8), 22/pi (§1).

§12. Compilation. The following is called to give us an opportunity to compile a routine defining a phrase. As was mentioned in the introduction, "To..." phrases are sometimes compiled multiple times, for different kinds of tokens, and are compiled in response to "requests". All other phrases are compiled just once.

    void Phrases::import(phrase *ph) {
        ph->imported = TRUE;
    }

    void Phrases::compile(phrase *ph, int *i, int max_i,
        stacked_variable_owner_list *legible, to_phrase_request *req, applicability_condition *acl) {
        if (ph->imported) return;
        int effect = Phrases::Usage::get_effect(&(ph->usage_data));
        if (effect == RULE_NOT_IN_RULEBOOK_EFF) effect = RULE_IN_RULEBOOK_EFF;
        if (effect == TO_PHRASE_EFF) {
            Routines::Compile::routine(ph, legible, req, acl);
            <Move along the progress bar if it's this phrase's first compilation 12.1>;
        } else {
            if (ph->at_least_one_compiled_form_needed) {
                Routines::Compile::routine(ph, legible, NULL, acl);
                <Move along the progress bar if it's this phrase's first compilation 12.1>;
            }
        }
    }

The function Phrases::import is used in 21/rl (§22).

The function Phrases::compile is used in 21/rl (§22), 22/cs (§10.4), 22/tp (§9).

§12.1. <Move along the progress bar if it's this phrase's first compilation 12.1> =

        if (ph->at_least_one_compiled_form_needed) {
            ph->at_least_one_compiled_form_needed = FALSE;
            (*i)++;
            ProgressBar::update_progress_bar(4, ((float) (*i))/((float) max_i));
        }

This code is used in §12 (twice).