Phrases defined with a list of invocations, rather than inline, have to be compiled to I6 routines, and this is where we organise that.


§1. Definitions.

§2. The nature of the phrase currently being compiled provides a sort of context for how we read the definition — for example, that it makes no sense to write "end the action" in a phrase which isn't an action-based rule. So we keep track of this. Note that one phrase definition cannot contain another, so there is never any need to recursively compile phrases.

    phrase *phrase_being_compiled = NULL;     phrase whose definition is being compiled

§3. This routine sits at the summit of a mountain of code: it compiles a non-line phrase definition into a routine. Note once again that a single phrase can be compiled multiple times, with different kinds. For example,

To grasp (V - a value): say "I see, [V]."

might be compiled once in a form where V was a text, and another time where it was a number. The form needed is included in the request req, which should always be supplied for "To..." phrases, but left null for rules.

    void Routines::Compile::routine(phrase *ph,
        stacked_variable_owner_list *legible, to_phrase_request *req,
        applicability_condition *acl) {

        if ((ph->declaration_node == NULL) ||
            (ParseTree::get_type(ph->declaration_node) != ROUTINE_NT) ||
            (Wordings::empty(ParseTree::get_text(ph->declaration_node))))
            internal_error("tried to compile phrase with bad ROUTINE node");
        LOGIF(PHRASE_COMPILATION, "Compiling phrase:\n$T", ph->declaration_node);

        Modules::set_current(ph->declaration_node);
        phrase_being_compiled = ph;
        <Set up the stack frame for this compilation request 3.2>;

        <Compile some commentary about the routine to follow 3.1>;

        packaging_state save = Routines::begin_framed(Routines::Compile::iname(ph, req), &(ph->stack_frame));

        <Compile the body of the routine 3.3>;

        Routines::end(save);

        phrase_being_compiled = NULL;
        current_sentence = NULL;
        Modules::set_current(NULL);
    }

The function Routines::Compile::routine is used in 22/ph (§12).

§3.1. <Compile some commentary about the routine to follow 3.1> =

        heading *definition_area =
            Sentences::Headings::of_wording(ParseTree::get_text(ph->declaration_node));
        extension_file *definition_extension =
            Sentences::Headings::get_extension_containing(definition_area);
        if (definition_extension)
            Extensions::Files::write_I6_comment_describing(definition_extension);
        Routines::ToPhrases::comment_on_request(req);
        Phrases::Usage::write_I6_comment_describing(&(ph->usage_data));

This code is used in §3.

§3.2. <Set up the stack frame for this compilation request 3.2> =

        ph_stack_frame *phsf = &(ph->stack_frame);
        ph_type_data *phtd = &(ph->type_data);
        Frames::make_current(phsf);

        kind *version_kind = NULL;
        if (req) version_kind = Routines::ToPhrases::kind_of_request(req);
        else version_kind = Phrases::TypeData::kind(phtd);
        Phrases::TypeData::into_stack_frame(phsf, phtd, version_kind, FALSE);

        if (req) Frames::set_kind_variables(phsf,
            Routines::ToPhrases::kind_variables_for_request(req));
        else Frames::set_kind_variables(phsf, NULL);

        Frames::set_stvol(phsf, legible);

        LocalVariables::deallocate_all(phsf);     in case any are left from an earlier compile
        ExParser::warn_expression_cache();     that local variables may have changed

This code is used in §3.

§3.3. <Compile the body of the routine 3.3> =

        current_sentence = ph->declaration_node;
        if (Phrases::Context::compile_test_head(ph, acl) == FALSE) {
            if (ph->declaration_node) {
                ParseTree::verify_structure(ph->declaration_node);
                Routines::Compile::code_block_outer(1, ph->declaration_node->down);
                ParseTree::verify_structure(ph->declaration_node);
            }
            current_sentence = ph->declaration_node;
            Phrases::Context::compile_test_tail(ph, acl);

            <Compile a terminal return statement 3.3.1>;
        }

This code is used in §3.

§3.3.1. In I6, all routines return a value, and if execution runs into the ] end marker without any return being made then the routine returns false if the routine is a property value, true otherwise. That convention is unhelpful to us, so we end our routine with code which certainly performs a return.

<Compile a terminal return statement 3.3.1> =

        Produce::inv_primitive(Emit::tree(), RETURN_BIP);
        Produce::down(Emit::tree());
        kind *K = Frames::get_kind_returned();
        if (K) {
            if (Kinds::RunTime::emit_default_value_as_val(K, EMPTY_WORDING,
                "value decided by this phrase") != TRUE) {
                Problems::Issue::sentence_problem(_p_(PM_DefaultDecideFails),
                    "it's not possible to decide such a value",
                    "so this can't be allowed.");
                Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 0);
            }
        } else {
            Produce::val(Emit::tree(), K_number, LITERAL_IVAL, 0);     that is, return "false"
        }
        Produce::up(Emit::tree());

This code is used in §3.3.

§4. The name of our I6 routine depends not only on the phrase but also on the request made for its compilation — this enables the text version of a phrase to be different from the number version, and so on.

    inter_name *Routines::Compile::iname(phrase *ph, to_phrase_request *req) {
        if (req) return req->req_iname;
        return Phrases::iname(ph);
    }

The function Routines::Compile::iname is used in §3, 22/ph (§13), 22/tp (§8), 25/ciac (§1).

§5.

    int disallow_let_assignments = FALSE;
    int Routines::Compile::disallow_let(void) {
        return disallow_let_assignments;
    }

    void Routines::Compile::code_block_outer(int statement_count, parse_node *pn) {
        Routines::Compile::code_block(statement_count, pn, TRUE);
    }

    int Routines::Compile::code_block(int statement_count, parse_node *pn, int top_level) {
        if (pn) {
            int m = <s-value-uncached>->multiplicitous;
            <s-value-uncached>->multiplicitous = TRUE;
            if (ParseTree::get_type(pn) != CODE_BLOCK_NT) internal_error("not a code block");
            if ((top_level == FALSE) && (pn->down) && (pn->down->next == NULL) && (pn->down->down == NULL))
                disallow_let_assignments = TRUE;
            for (parse_node *p = pn->down; p; p = p->next) {
                statement_count = Routines::Compile::code_line(statement_count, p);
            }
            disallow_let_assignments = FALSE;
            <s-value-uncached>->multiplicitous = m;
        }
        return statement_count;
    }

    int Routines::Compile::code_line(int statement_count, parse_node *p) {
        control_structure_phrase *csp = ParseTree::get_control_structure_used(p);
        parse_node *to_compile = p;
        if (Sentences::RuleSubtrees::opens_block(csp)) {
            Frames::Blocks::beginning_block_phrase(csp);
            to_compile = p->down;
        }
        statement_count++;
        <Compile a comment about this line 5.1>;
        int L = Produce::level(Emit::tree());
        <Compile the head 5.2>;
        <Compile the midriff 5.3>;
        <Compile the tail 5.4>;
        return statement_count;
    }

The function Routines::Compile::disallow_let is used in 14/ds2 (§11.9.1.1.3.1).

The function Routines::Compile::code_block_outer is used in §3.3, 17/ts (§11.1).

The function Routines::Compile::code_block is used in §5.3.4, §5.3.5.1, §5.3.5.2, §5.3.5.3, §5.3.5.4, §5.4.3, §5.4.5.

The function Routines::Compile::code_line appears nowhere else.

§5.1. <Compile a comment about this line 5.1> =

        if (Wordings::nonempty(ParseTree::get_text(to_compile))) {
            TEMPORARY_TEXT(C);
            WRITE_TO(C, "[%d: ", statement_count);
            CompiledText::comment(C, ParseTree::get_text(to_compile));
            WRITE_TO(C, "]");
            Emit::code_comment(C);
            DISCARD_TEXT(C);
        }

This code is used in §5.

§5.2. <Compile the head 5.2> =

        if (csp == say_CSP) {
            current_sentence = to_compile;
            <Compile a say head 5.2.1>;
        }

This code is used in §5.

§5.2.1. <Compile a say head 5.2.1> =

        for (parse_node *say_node = p->down, *prev_sn = NULL; say_node; prev_sn = say_node, say_node = say_node->next) {
            ExParser::parse_say_term(say_node);
            parse_node *inv = Invocations::first_in_list(say_node->down);
            if (inv) {
                if (prev_sn) {
                    if ((ParseTree::get_say_verb(inv)) ||
                        (ParseTree::get_say_adjective(inv)) ||
                        ((Phrases::TypeData::is_a_say_phrase(ParseTree::get_phrase_invoked(inv))) &&
                            (ParseTree::get_phrase_invoked(inv)->type_data.as_say.say_phrase_running_on)))
                        ParseTree::annotate_int(prev_sn, suppress_newlines_ANNOT, TRUE);
                }
            }
        }
        Produce::inv_primitive(Emit::tree(), STORE_BIP);     warn the paragraph breaker: this will print
        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());
        Routines::Compile::verify_say_node_list(p->down);

This code is used in §5.2.

§5.3. <Compile the midriff 5.3> =

        if (ParseTree::get_type(to_compile) == INVOCATION_LIST_SAY_NT) <Compile a say term midriff 5.3.1>
        else if (csp == now_CSP) <Compile a now midriff 5.3.2>
        else if (csp == if_CSP) <Compile an if midriff 5.3.4>
        else if (csp == switch_CSP) <Compile a switch midriff 5.3.5>
        else if ((csp != say_CSP) && (csp != instead_CSP)) {
            if (<named-rulebook-outcome>(ParseTree::get_text(to_compile)))
                <Compile a named rulebook outline midriff 5.3.3>
            else <Compile a standard midriff 5.3.6>;
        }

This code is used in §5.

§5.3.1. <Compile a say term midriff 5.3.1> =

        BEGIN_COMPILATION_MODE;
        if (ParseTree::int_annotation(to_compile, suppress_newlines_ANNOT))
            COMPILATION_MODE_EXIT(IMPLY_NEWLINES_IN_SAY_CMODE);
        Routines::Compile::line(to_compile, TRUE, INTER_VOID_VHMODE);
        END_COMPILATION_MODE;

This code is used in §5.3.

§5.3.2. In fact, "now" propositions are never empty, but there's nothing in principle wrong with asserting that the universally true proposition is henceforth to be true, so we simply compile empty code in that case.

<Compile a now midriff 5.3.2> =

        current_sentence = to_compile;
        wording XW = ParseTree::get_text(p->down);
        parse_node *cs = NULL;
        if (<s-condition>(XW)) cs = <<rp>>; else cs = Specifications::new_UNKNOWN(XW);
        LOGIF(MATCHING, "Now cond is $T\n", cs);
        int rv = Dash::check_condition(cs);
        LOGIF(MATCHING, "After Dash, it's $T\n", cs);

        if (ParseTree::is(cs, TEST_PROPOSITION_NT)) {
            if (rv != NEVER_MATCH) {
                pcalc_prop *prop = Specifications::to_proposition(cs);
                if (prop) {
                    BEGIN_COMPILATION_MODE;
                    COMPILATION_MODE_ENTER(PERMIT_LOCALS_IN_TEXT_CMODE);
                    Calculus::Deferrals::emit_now_proposition(prop);
                    END_COMPILATION_MODE;
                }
            }
        } else if (ParseTreeUsage::is_condition(cs))
            <Issue a problem message for the wrong sort of condition in a "now" 5.3.2.1>
        else if (rv != NEVER_MATCH) <Issue a problem message for an unrecognised condition 5.3.2.2>;

This code is used in §5.3.

§5.3.2.1. A deluxe problem message.

<Issue a problem message for the wrong sort of condition in a "now" 5.3.2.1> =

        Problems::quote_source(1, current_sentence);
        Problems::quote_wording(2, ParseTree::get_text(cs));
        if (ParseTree::is(cs, TEST_VALUE_NT)) {
            Problems::Issue::handmade_problem(_p_(PM_BadNow1));
            Problems::issue_problem_segment(
                "You wrote %1, but although '%2' is a condition which it is legal "
                "to test with 'if', 'when', and so forth, it is not something I "
                "can arrange to happen on request. Whether it is true or not "
                "depends on current circumstances: so to make it true, you will "
                "need to adjust those circumstances.");
            Problems::issue_problem_end();
        } else if (ParseTree::is(cs, LOGICAL_AND_NT)) {
            Problems::Issue::handmade_problem(_p_(PM_BadNow2));
            Problems::issue_problem_segment(
                "You wrote %1, but 'now' does not work with the condition '%2' "
                "because it can only make one wish come true at a time: so it "
                "doesn't like the 'and'. Try rewriting as two 'now's in a row?");
            Problems::issue_problem_end();
        } else {
            Problems::Issue::handmade_problem(_p_(PM_BadNow3));
            Problems::issue_problem_segment(
                "You wrote %1, but '%2'	isn't the sort of condition which can be "
                "made to be true, in the way that 'the ball is on the table' can be "
                "made true with a straightforward movement of one object (the ball).");
            Problems::issue_problem_end();
        }

This code is used in §5.3.2.

§5.3.2.2. <Issue a problem message for an unrecognised condition 5.3.2.2> =

        LOG("$T\n", current_sentence);
        Problems::quote_source(1, current_sentence);
        Problems::quote_wording(2, ParseTree::get_text(cs));
        Problems::Issue::handmade_problem(_p_(...));
        Problems::issue_problem_segment(
            "You wrote %1, but '%2'	isn't a condition, so I can't see how to "
            "make it true from here on.");
        Problems::issue_problem_end();

This code is used in §5.3.2.

§.1. <Issue a problem message for an unrecognised action .1> =

        Problems::quote_source(1, current_sentence);
        Problems::quote_wording(2, ParseTree::get_text(cs));
        Problems::Issue::handmade_problem(_p_(...));
        Problems::issue_problem_segment(
            "You wrote %1, but '%2'	isn't an action, so I can't see how to try it.");
        Problems::issue_problem_end();

This code is never used.

§5.3.3. <Compile a named rulebook outline midriff 5.3.3> =

        current_sentence = to_compile;
        named_rulebook_outcome *nrbo = <<rp>>;
        if (phrase_being_compiled) {
            int ram = Phrases::Usage::get_effect(&(phrase_being_compiled->usage_data));
            if ((ram != RULE_IN_RULEBOOK_EFF) &&
                (ram != RULE_NOT_IN_RULEBOOK_EFF)) {
                Problems::quote_source(1, current_sentence);
                Problems::quote_wording(2, ParseTree::get_text(to_compile));
                Problems::Issue::handmade_problem(_p_(PM_MisplacedRulebookOutcome2));
                Problems::issue_problem_segment(
                    "You wrote %1, but this is a rulebook outcome which can only be used "
                    "within rulebooks which recognise it. You've used it in a definition "
                    "which isn't for use in rulebooks at all, so it must be wrong here.");
                Problems::issue_problem_end();
            }
        }
        rulebook *rb = Rulebooks::Outcomes::allow_outcome(nrbo);
        if (rb) {
            Problems::quote_source(1, current_sentence);
            Problems::quote_wording(2, ParseTree::get_text(to_compile));
            Problems::quote_wording(3, rb->primary_name);
            Problems::Issue::handmade_problem(_p_(PM_MisplacedRulebookOutcome));
            Problems::issue_problem_segment(
                "You wrote %1, but this is a rulebook outcome which can only be used "
                "within rulebooks which recognise it. You've used it in a rule which "
                "has to be listed in the '%3' rulebook, where '%2' doesn't have a meaning.");
            Problems::issue_problem_end();
        }
        Rulebooks::Outcomes::compile_outcome(nrbo);

This code is used in §5.3.

§5.3.4. <Compile an if midriff 5.3.4> =

        if (p->down->next->next) Produce::inv_primitive(Emit::tree(), IFELSE_BIP);
        else Produce::inv_primitive(Emit::tree(), IF_BIP);
        Produce::down(Emit::tree());
            current_sentence = to_compile;
            Routines::Compile::line(to_compile, FALSE, INTER_VAL_VHMODE);

            Produce::code(Emit::tree());
            Produce::down(Emit::tree());
                Frames::Blocks::open_code_block();
                statement_count = Routines::Compile::code_block(statement_count, p->down->next, FALSE);
            if (p->down->next->next) {
            Produce::up(Emit::tree());
            Produce::code(Emit::tree());
            Produce::down(Emit::tree());
                Frames::Blocks::divide_code_block();
                statement_count = Routines::Compile::code_block(statement_count, p->down->next->next, FALSE);
            }
                Frames::Blocks::close_code_block();
            Produce::up(Emit::tree());
        Produce::up(Emit::tree());

This code is used in §5.3.

§5.3.5. <Compile a switch midriff 5.3.5> =

        current_sentence = to_compile;
        Routines::Compile::line(to_compile, FALSE, INTER_VOID_VHMODE);

        Frames::Blocks::open_code_block();

        parse_node *val = Frames::Blocks::switch_value();
        if (val == NULL) internal_error("no switch value");
        kind *switch_kind = Specifications::to_kind(val);
        int pointery = FALSE;
        inter_symbol *sw_v = NULL;

        if (Kinds::Behaviour::uses_pointer_values(switch_kind)) pointery = TRUE;

        LOG("Switch val is $T for kind $u pointery %d\n", val, switch_kind, pointery);

        local_variable *lvar = NULL;
        int downs = 0;

        if (pointery) {
            lvar = LocalVariables::add_switch_value(K_value);
            sw_v = LocalVariables::declare_this(lvar, FALSE, 7);
            Produce::inv_primitive(Emit::tree(), STORE_BIP);
            Produce::down(Emit::tree());
                Produce::ref_symbol(Emit::tree(), K_value, sw_v);
                Specifications::Compiler::emit_as_val(switch_kind, val);
            Produce::up(Emit::tree());
        } else {
            Produce::inv_primitive(Emit::tree(), SWITCH_BIP);
            Produce::down(Emit::tree());
                Specifications::Compiler::emit_as_val(switch_kind, val);
                Produce::code(Emit::tree());
                Produce::down(Emit::tree());
        }

                int c = 0;
                for (parse_node *ow_node = p->down->next->next; ow_node; ow_node = ow_node->next, c++) {
                    current_sentence = ow_node;
                    Frames::Blocks::divide_code_block();

                    if (ParseTree::get_control_structure_used(ow_node) == default_case_CSP) {
                        if (pointery) <Handle a pointery default 5.3.5.4>
                        else <Handle a non-pointery default 5.3.5.2>;
                    } else {
                        if (<s-type-expression-or-value>(ParseTree::get_text(ow_node))) {
                            parse_node *case_spec = <<rp>>;
                            case_spec = NonlocalVariables::substitute_constants(case_spec);
                            ParseTree::set_evaluation(ow_node, case_spec);
                            if (Dash::check_value(case_spec, NULL) != NEVER_MATCH) {
                                kind *case_kind = Specifications::to_kind(case_spec);
                                instance *I = Rvalues::to_object_instance(case_spec);
                                if (I) case_kind = Instances::to_kind(I);
                                LOGIF(MATCHING, "(h.3) switch kind is $u, case kind is $u\n", switch_kind, case_kind);
                                if ((ParseTree::get_kind_of_value(case_spec) == NULL) && (I == NULL)) {
                                    Problems::quote_source(1, current_sentence);
                                    Problems::quote_kind(2, switch_kind);
                                    Problems::Issue::handmade_problem(_p_(PM_CaseValueNonConstant));
                                    Problems::issue_problem_segment(
                                        "The case %1 is required to be a constant value, rather than "
                                        "something which has different values at different times: "
                                        "specifically, it has to be %2.");
                                    Problems::issue_problem_end();
                                    case_spec = Rvalues::new_nothing_object_constant();
                                } else if (Kinds::Compare::compatible(case_kind, switch_kind) != ALWAYS_MATCH) {
                                    Problems::quote_source(1, current_sentence);
                                    Problems::quote_kind(2, case_kind);
                                    Problems::quote_kind(3, switch_kind);
                                    Problems::Issue::handmade_problem(_p_(PM_CaseValueMismatch));
                                    Problems::issue_problem_segment(
                                        "The case %1 has the wrong kind of value for the possibilities "
                                        "being chosen from: %2 instead of %3.");
                                    Problems::issue_problem_end();
                                    case_spec = Rvalues::new_nothing_object_constant();
                                } else {
                                    if (pointery) <Handle a pointery case 5.3.5.3>
                                    else <Handle a non-pointery case 5.3.5.1>;
                                }
                            } else <Issue problem message for unknown case value 5.3.5.5>
                        } else <Issue problem message for unknown case value 5.3.5.5>;
                    }
                }

        if (pointery) {
            while (downs-- > 0) Produce::up(Emit::tree());
            Frames::Blocks::close_code_block();
        } else {
            Produce::up(Emit::tree());
            Frames::Blocks::close_code_block();
        Produce::up(Emit::tree());
        }

        if (problem_count == 0)
            for (parse_node *A = p->down->next->next; A; A = A->next) {
                int dup = FALSE;
                for (parse_node *B = A->next; B; B = B->next)
                    if (Rvalues::compare_CONSTANT(
                        ParseTree::get_evaluation(A), ParseTree::get_evaluation(B)))
                            dup = TRUE;
                if (dup) {
                    current_sentence = A;
                    Problems::quote_source(1, A);
                    Problems::quote_spec(2, ParseTree::get_evaluation(A));
                    Problems::Issue::handmade_problem(_p_(PM_CaseValueDuplicated));
                    Problems::issue_problem_segment(
                        "The case %1 occurs more than once in this 'if' switch.");
                    Problems::issue_problem_end();
                }
            }

This code is used in §5.3.

§5.3.5.1. <Handle a non-pointery case 5.3.5.1> =

        Produce::inv_primitive(Emit::tree(), CASE_BIP);
        Produce::down(Emit::tree());
            Specifications::Compiler::emit_as_val(switch_kind, case_spec);
            Produce::code(Emit::tree());
            Produce::down(Emit::tree());
                statement_count = Routines::Compile::code_block(statement_count, ow_node, FALSE);
            Produce::up(Emit::tree());
        Produce::up(Emit::tree());

This code is used in §5.3.5.

§5.3.5.2. <Handle a non-pointery default 5.3.5.2> =

        Produce::inv_primitive(Emit::tree(), DEFAULT_BIP);
        Produce::down(Emit::tree());
            Produce::code(Emit::tree());
            Produce::down(Emit::tree());
                statement_count = Routines::Compile::code_block(statement_count, ow_node, FALSE);
            Produce::up(Emit::tree());
        Produce::up(Emit::tree());

This code is used in §5.3.5.

§5.3.5.3. <Handle a pointery case 5.3.5.3> =

        int final_flag = FALSE;
        if (ow_node->next == NULL) final_flag = TRUE;

        if (final_flag) Produce::inv_primitive(Emit::tree(), IF_BIP);
        else Produce::inv_primitive(Emit::tree(), IFELSE_BIP);
        Produce::down(Emit::tree());
            LocalVariables::set_kind(lvar, switch_kind);
            parse_node *sw_v = Lvalues::new_LOCAL_VARIABLE(EMPTY_WORDING, lvar);
            pcalc_prop *prop = Calculus::Propositions::Abstract::to_set_relation(
                R_equality, NULL, sw_v, NULL, case_spec);
            Calculus::Propositions::Checker::type_check(prop, Calculus::Propositions::Checker::tc_no_problem_reporting());
            Calculus::Deferrals::emit_test_of_proposition(NULL, prop);
            Produce::code(Emit::tree());
            Produce::down(Emit::tree());
                statement_count = Routines::Compile::code_block(statement_count, ow_node, FALSE);
            if (final_flag == FALSE) {
                Produce::up(Emit::tree());
                Produce::code(Emit::tree());
                Produce::down(Emit::tree());
            }
        downs += 2;

This code is used in §5.3.5.

§5.3.5.4. <Handle a pointery default 5.3.5.4> =

        statement_count = Routines::Compile::code_block(statement_count, ow_node, FALSE);

This code is used in §5.3.5.

§5.3.6. <Compile a standard midriff 5.3.6> =

        current_sentence = to_compile;
        Routines::Compile::line(to_compile, FALSE, INTER_VOID_VHMODE);

This code is used in §5.3.

§5.4. <Compile the tail 5.4> =

        if (csp == if_CSP) <Compile an if tail 5.4.1>
        else if (csp == switch_CSP) <Compile a switch tail 5.4.2>
        else if (csp == say_CSP) <Compile a say tail 5.4.3>
        else if (csp == instead_CSP) <Compile an instead tail 5.4.4>
        else if (Sentences::RuleSubtrees::opens_block(csp)) <Compile a loop tail 5.4.5>;

This code is used in §5.

§5.4.1. <Compile an if tail 5.4.1> =

        ;

This code is used in §5.4.

§5.4.2. Switch statements in I6 look much like those in C, but are written without the ceaseless repetition of the keyword "case". Thus, 15: does what case 15: would do in C. But default: is the same in both.

<Compile a switch tail 5.4.2> =

        ;

This code is used in §5.4.

§5.3.5.5. <Issue problem message for unknown case value 5.3.5.5> =

        Problems::Issue::sentence_problem(_p_(PM_CaseValueUnknown),
            "I don't recognise this case value",
            "that is, the value written after the '--'.");

This code is used in §5.3.5 (twice).

§.1. <If this is a say group, but not a say control structure, notify the paragraphing code .1> =



This code is never used.

§5.4.3. As will be seen, two sets of labels and counters are kept here: see the inline definitions for "say if" and similar.

<Compile a say tail 5.4.3> =

        statement_count = Routines::Compile::code_block(statement_count, p, FALSE);

        TEMPORARY_TEXT(SAYL);
        WRITE_TO(SAYL, ".");
        JumpLabels::write(SAYL, I"Say");
        Produce::place_label(Emit::tree(), Produce::reserve_label(Emit::tree(), SAYL));
        DISCARD_TEXT(SAYL);

        JumpLabels::read_counter(I"Say", TRUE);

        TEMPORARY_TEXT(SAYXL);
        WRITE_TO(SAYXL, ".");
        JumpLabels::write(SAYXL, I"SayX");
        Produce::place_label(Emit::tree(), Produce::reserve_label(Emit::tree(), SAYXL));
        DISCARD_TEXT(SAYXL);

        JumpLabels::read_counter(I"SayX", TRUE);

This code is used in §5.4.

§5.4.4. <Compile an instead tail 5.4.4> =

        Produce::rtrue(Emit::tree());

This code is used in §5.4.

§5.4.5. <Compile a loop tail 5.4.5> =

        Frames::Blocks::open_code_block();
        statement_count = Routines::Compile::code_block(statement_count, p->down->next, FALSE);
        while (Produce::level(Emit::tree()) > L) Produce::up(Emit::tree());
        Frames::Blocks::close_code_block();

This code is used in §5.4.

§6. This routine takes the text of a line from a phrase definition, parses it, type-checks it, and finally, all being well, compiles it.

    parse_node *void_phrase_please = NULL;     instructions for the typechecker

    void Routines::Compile::line(parse_node *p, int already_parsed, int vhm) {
        int initial_problem_count = problem_count;

        LOGIF(EXPRESSIONS, "\n-- -- Evaluating <%W> -- --\n", ParseTree::get_text(p));

        LOGIF(EXPRESSIONS, "(a) Parsing:\n");
        if (already_parsed) {
            parse_node *inv = Invocations::first_in_list(p->down);
            if ((inv) &&
                (ParseTree::get_phrase_invoked(inv)) &&
                (Phrases::TypeData::is_a_say_phrase(ParseTree::get_phrase_invoked(inv))) &&
                (ParseTree::get_phrase_invoked(inv)->type_data.as_say.say_control_structure == NO_SAY_CS)) {
                Produce::inv_call_iname(Emit::tree(), Hierarchy::find(PARACONTENT_HL));
            }
        } else {
            ExParser::parse_void_phrase(p);
        }

        if (initial_problem_count == problem_count) {
            LOGIF(EXPRESSIONS, "(b) Type checking:\n$E", p->down);
            Dash::check_invl(p);
        }

        if (initial_problem_count == problem_count) {
            LOGIF(EXPRESSIONS, "(c) Compilation:\n$E", p->down);
            value_holster VH = Holsters::new(vhm);
            Invocations::Compiler::compile_invocation_list(&VH,
                p->down, ParseTree::get_text(p));
        }

        if (initial_problem_count == problem_count) {
            LOGIF(EXPRESSIONS, "-- -- Completed -- --\n");
        } else {
            LOGIF(EXPRESSIONS, "-- -- Failed -- --\n");
        }
    }

The function Routines::Compile::line is used in §5.3.1, §5.3.4, §5.3.5, §5.3.6.

§7. And this is where we are:

    parse_node *Routines::Compile::line_being_compiled(void) {
        if (phrase_being_compiled) return current_sentence;
        return NULL;
    }

The function Routines::Compile::line_being_compiled is used in 14/rv (§24.3).

§8. Validation of invocations. Recall that a complex text such as:

"Platinum is shinier than [if a Colony is in the Supply Pile]gold[otherwise]silver."

is complied into a specification holding a list of invocations; in this case there are five, invoking the phrases —

In the following routine we check this list to see that two sorts of control structure are correctly used. The first is "say if"; here, for instance, it would be an error to use "say otherwise" without "say if", or to have them the wrong way round.

The other is the SSP, the "segmented say phrase". For example:

"The best defence is [one of]Lighthouse[or]Moat[or]having no money[at random]."

Here there are nine invocations, and the interesting ones have to come in the sequence "[one of]" (a start), then any number of "[or]" segments (middles), and lastly "[at random]" (an end). SSPs can even be nested, within limits:

    define MAX_COMPLEX_SAY_DEPTH 32     and it would be terrible coding style to approach this
    int Routines::Compile::verify_say_node_list(parse_node *say_node_list) {
        int problem_issued = FALSE;
        int say_invocations_found = 0;
        <Check that say control structures have been used in a correct sequence 8.1>;
        return say_invocations_found;
    }

The function Routines::Compile::verify_say_node_list is used in §5.2.1.

§8.1. Given correct code, the following does very little. It checks that structural say phrases (SSPs), such as the substitutions here:

"Platinum is shinier than [if a Colony is in the Supply Pile]gold[otherwise]silver."

...are used correctly; for instance, that there isn't an "[otherwise]" before the "[if...]".

It doesn't quite do nothing, though, because it also counts the say phrases found.

<Check that say control structures have been used in a correct sequence 8.1> =

        int it_was_not_worth_adding = it_is_not_worth_adding;
        it_is_not_worth_adding = TRUE;

        int SSP_sp = 0;
        int SSP_stack[MAX_COMPLEX_SAY_DEPTH];
        int SSP_stack_otherwised[MAX_COMPLEX_SAY_DEPTH];
        parse_node *SSP_invocations[MAX_COMPLEX_SAY_DEPTH];
        int say_if_nesting = 0;

        for (parse_node *say_node = say_node_list; say_node; say_node = say_node->next) {
            parse_node *invl = say_node->down;
            if (invl) {
                parse_node *inv;
                LOOP_THROUGH_INVOCATION_LIST(inv, invl) {
                    phrase *ph = ParseTree::get_phrase_invoked(inv);
                    if ((ParseTree::get_phrase_invoked(inv)) &&
                        (Phrases::TypeData::is_a_say_phrase(ph))) {
                        int say_cs, ssp_tok, ssp_ctok, ssp_pos;
                        Phrases::TypeData::get_say_data(&(ph->type_data.as_say),
                            &say_cs, &ssp_tok, &ssp_ctok, &ssp_pos);

                        if (ssp_pos == SSP_START) <This starts a complex SSP 8.1.1>;
                        if (ssp_pos == SSP_MIDDLE) <This is a middle term in a complex SSP 8.1.2>;
                        if (ssp_pos == SSP_END) <This ends a complex SSP 8.1.3>;

                        if (say_cs == IF_SAY_CS) <This is a say if 8.1.4>;
                        if ((say_cs == OTHERWISE_SAY_CS) || (say_cs == OTHERWISE_IF_SAY_CS))
                            <This is a say otherwise 8.1.5>;
                        if (say_cs == END_IF_SAY_CS) <This is a say end if 8.1.6>;

                        say_invocations_found++;
                    }
                }
            }
        }
        if (SSP_sp > 0) {
            if ((SSP_sp == 1) && (SSP_stack[0] == -1)) {
                an if without an end if, which uniquely is legal
            } else {
                <Issue a problem message for an SSP without end 8.1.7>;
            }
        }
        it_is_not_worth_adding = it_was_not_worth_adding;

This code is used in §8.

§8.1.1. <This starts a complex SSP 8.1.1> =

        if (SSP_sp >= MAX_COMPLEX_SAY_DEPTH) {
            <Issue a problem message for an overcomplex SSP 8.1.1.1>;
        } else {
            SSP_invocations[SSP_sp] = inv;
            SSP_stack_otherwised[SSP_sp] = FALSE;
            SSP_stack[SSP_sp++] = ssp_tok;
        }

This code is used in §8.1.

§8.1.2. <This is a middle term in a complex SSP 8.1.2> =

        if ((SSP_sp > 0) && (SSP_stack[SSP_sp-1] != -1) &&
            (compare_words(SSP_stack[SSP_sp-1], ssp_tok))) {
            ParseTree::annotate_int(SSP_invocations[SSP_sp-1], ssp_segment_count_ANNOT,
                ParseTree::int_annotation(SSP_invocations[SSP_sp-1], ssp_segment_count_ANNOT)+1);
            ParseTree::annotate_int(inv, ssp_segment_count_ANNOT,
                ParseTree::int_annotation(SSP_invocations[SSP_sp-1], ssp_segment_count_ANNOT));
        } else <Issue a problem message for middle without start 8.1.2.1>;

This code is used in §8.1.

§8.1.3. <This ends a complex SSP 8.1.3> =

        if ((SSP_sp > 0) && (SSP_stack[SSP_sp-1] != -1) &&
            (compare_words(SSP_stack[SSP_sp-1], ssp_tok))) {
            ParseTree::annotate_int(SSP_invocations[SSP_sp-1], ssp_segment_count_ANNOT,
                ParseTree::int_annotation(SSP_invocations[SSP_sp-1], ssp_segment_count_ANNOT)+1);
            ParseTree::annotate_int(SSP_invocations[SSP_sp-1], ssp_closing_segment_wn_ANNOT, ssp_ctok);
            ParseTree::annotate_int(inv, ssp_segment_count_ANNOT,
                ParseTree::int_annotation(SSP_invocations[SSP_sp-1], ssp_segment_count_ANNOT));
            SSP_sp--;
        } else <Issue a problem message for end without start 8.1.3.1>;

This code is used in §8.1.

§8.1.4. <This is a say if 8.1.4> =

        if (say_if_nesting == 0) {
            say_if_nesting++;
            SSP_invocations[SSP_sp] = NULL;
            SSP_stack_otherwised[SSP_sp] = FALSE;
            SSP_stack[SSP_sp++] = -1;
        } else <Issue a problem message for nested say if 8.1.4.1>;

This code is used in §8.1.

§8.1.5. <This is a say otherwise 8.1.5> =

        if (say_if_nesting == 0)
            <Issue a problem message for say otherwise without say if 8.1.5.1>
        else if (SSP_sp > 0) {
            if (SSP_stack[SSP_sp-1] != -1)
                <Issue a problem message for say otherwise interleaved with another construction 8.1.5.2>;
            if (SSP_stack_otherwised[SSP_sp-1])
                <Issue a problem message for two say otherwises 8.1.5.3>
            if (say_cs == OTHERWISE_SAY_CS) SSP_stack_otherwised[SSP_sp-1] = TRUE;
        }

This code is used in §8.1.

§8.1.6. <This is a say end if 8.1.6> =

        if (say_if_nesting == 0) <Issue a problem message for say end if without say if 8.1.6.1>
        else if ((SSP_sp > 0) && (SSP_stack[SSP_sp-1] != -1))
            <Issue a problem message for say end if interleaved with another construction 8.1.6.2>
        else {
            say_if_nesting--;
            SSP_sp--;
        }

This code is used in §8.1.

§8.1.2.1. <Issue a problem message for middle without start 8.1.2.1> =

        if (problem_issued == FALSE) {
            Problems::quote_source(1, current_sentence);
            Problems::quote_wording(2, ParseTree::get_text(inv));
            Problems::Issue::handmade_problem(_p_(PM_ComplicatedSayStructure));
            Problems::issue_problem_segment(
                "In the text at %1, the text substitution '[%2]' ought to occur as the "
                "middle part of its construction, but it appears to be on its own.");
            Routines::Compile::add_say_construction_to_error(ssp_tok);
            Problems::issue_problem_end();
            problem_issued = TRUE;
        }

This code is used in §8.1.2.

§8.1.3.1. <Issue a problem message for end without start 8.1.3.1> =

        if (problem_issued == FALSE) {
            Problems::quote_source(1, current_sentence);
            Problems::quote_wording(2, ParseTree::get_text(inv));
            Problems::Issue::handmade_problem(_p_(PM_ComplicatedSayStructure2));
            Problems::issue_problem_segment(
                "In the text at %1, the text substitution '[%2]' ought to occur as the "
                "ending part of its construction, but it appears to be on its own.");
            Routines::Compile::add_say_construction_to_error(ssp_tok);
            Problems::issue_problem_end();
            problem_issued = TRUE;
        }

This code is used in §8.1.3.

§8.1.4.1. <Issue a problem message for nested say if 8.1.4.1> =

        if (problem_issued == FALSE) {
            Problems::Issue::sentence_problem(_p_(PM_SayIfNested),
                "a second '[if ...]' text substitution occurs inside an existing one",
                "which makes this text too complicated. While a single text can contain "
                "more than one '[if ...]', this can only happen if the old if is finished "
                "with an '[end if]' or the new one is written '[otherwise if]'. If you "
                "need more complicated variety than this allows, the best approach is "
                "to define a new text substitution of your own ('To say fiddly details: "
                "...') and then use it in this text by including the '[fiddly details]'.");
            problem_issued = TRUE;
        }

This code is used in §8.1.4.

§8.1.1.1. <Issue a problem message for an overcomplex SSP 8.1.1.1> =

        if (problem_issued == FALSE) {
            Problems::Issue::sentence_problem(_p_(PM_SayOverComplex),
                "this is too complex a text substitution",
                "and needs to be simplified. You might find it helful to define "
                "a new text substitution of your own ('To say fiddly details: "
                "...') and then use it in this text by including the '[fiddly details]'.");
            problem_issued = TRUE;
        }

This code is used in §8.1.1.

§8.1.5.1. <Issue a problem message for say otherwise without say if 8.1.5.1> =

        if (problem_issued == FALSE) {
            Problems::Issue::sentence_problem(_p_(PM_SayOtherwiseWithoutIf),
                "an '[otherwise]' text substitution occurs where there appears to be no "
                "[if ...]",
                "which doesn't make sense - there is nothing for it to be otherwise to.");
            problem_issued = TRUE;
        }

This code is used in §8.1.5.

§8.1.5.2. <Issue a problem message for say otherwise interleaved with another construction 8.1.5.2> =

        if (problem_issued == FALSE) {
            Problems::quote_source(1, current_sentence);
            Problems::quote_wording(2, ParseTree::get_text(inv));
            Problems::Issue::handmade_problem(_p_(PM_ComplicatedSayStructure5));
            Problems::issue_problem_segment(
                "In the text at %1, the '[%2]' ought to occur inside an [if ...], but "
                "is cut off because it has been interleaved with a complicated say "
                "construction.");
            Routines::Compile::add_say_construction_to_error(SSP_stack[SSP_sp-1]);
            Problems::issue_problem_end();
            problem_issued = TRUE;
        }

This code is used in §8.1.5.

§8.1.5.3. <Issue a problem message for two say otherwises 8.1.5.3> =

        if (problem_issued == FALSE) {
            Problems::Issue::sentence_problem(_p_(PM_TwoSayOtherwises),
                "there's already an (unconditional) \"[otherwise]\" or \"[else]\" "
                "in this text substitution",
                "so it doesn't make sense to follow that with a further one.");
            problem_issued = TRUE;
        }

This code is used in §8.1.5.

§8.1.6.1. <Issue a problem message for say end if without say if 8.1.6.1> =

        if (problem_issued == FALSE) {
            Problems::Issue::sentence_problem(_p_(PM_SayEndIfWithoutSayIf),
                "an '[end if]' text substitution occurs where there appears to be no "
                "[if ...]",
                "which doesn't make sense - there is nothing for it to end.");
            problem_issued = TRUE;
        }

This code is used in §8.1.6.

§8.1.6.2. <Issue a problem message for say end if interleaved with another construction 8.1.6.2> =

        if (problem_issued == FALSE) {
            Problems::quote_source(1, current_sentence);
            Problems::quote_wording(2, ParseTree::get_text(inv));
            Problems::Issue::handmade_problem(_p_(PM_ComplicatedSayStructure4));
            Problems::issue_problem_segment(
                "In the text at %1, the '[%2]' is cut off from its [if ...], because "
                "it has been interleaved with a complicated say construction.");
            Routines::Compile::add_say_construction_to_error(SSP_stack[SSP_sp-1]);
            Problems::issue_problem_end();
            problem_issued = TRUE;
        }

This code is used in §8.1.6.

§8.1.7. <Issue a problem message for an SSP without end 8.1.7> =

        if (problem_issued == FALSE) {
            parse_node *stinv = NULL;
            int i, ssp_tok = -1;
            for (i=0; i<SSP_sp; i++)
                if (SSP_invocations[i]) {
                    stinv = SSP_invocations[i];
                    ssp_tok = SSP_stack[i];
                }
            if (stinv) {
                Problems::quote_source(1, current_sentence);
                Problems::quote_wording(2, ParseTree::get_text(stinv));
                Problems::Issue::handmade_problem(_p_(PM_ComplicatedSayStructure3));
                Problems::issue_problem_segment(
                    "In the text at %1, the text substitution '[%2]' seems to start a "
                    "complicated say construction, but it doesn't have a matching end.");
                if (ssp_tok >= 0) Routines::Compile::add_say_construction_to_error(ssp_tok);
                Problems::issue_problem_end();
                problem_issued = TRUE;
            }
        }

This code is used in §8.1.

§9. Problem messages for complex say constructions.

    void Routines::Compile::add_say_construction_to_error(int ssp_tok) {
        Problems::issue_problem_segment(" %P(The construction I'm thinking of is '");
        Routines::Compile::add_scte_list(ssp_tok, SSP_START);
        Problems::issue_problem_segment(" ... ");
        Routines::Compile::add_scte_list(ssp_tok, SSP_MIDDLE);
        Problems::issue_problem_segment(" ... ");
        Routines::Compile::add_scte_list(ssp_tok, SSP_END);
        Problems::issue_problem_segment("'.)");
    }

    void Routines::Compile::add_scte_list(int ssp_tok, int list_pos) {
        phrase *ph; int ct = 0;
        LOOP_OVER(ph, phrase) {
            wording W;
            if (Phrases::TypeData::ssp_matches(&(ph->type_data), ssp_tok, list_pos, &W)) {
                Problems::quote_wording(3, W);
                if (ct++ == 0) Problems::issue_problem_segment("[%3]");
                else Problems::issue_problem_segment("/[%3]");
            }
        }
    }

The function Routines::Compile::add_say_construction_to_error is used in §8.1.2.1, §8.1.3.1, §8.1.5.2, §8.1.6.2, §8.1.7.

The function Routines::Compile::add_scte_list appears nowhere else.