To load, optimise and throw problem messages related to Preform syntax.


§1. Reading Preform declarations from Syntax files. At present we do this only when L is English, but the infrastructure is general.

int CorePreform::load(inform_language *L) {
    if (L == NULL) internal_error("can't load preform from null language");
    filename *F = Filenames::in(Languages::path_to_bundle(L), I"Syntax.preform");
    int nonterminals_declared = LoadPreform::load(F, L);
    LOG("%d Preform nonterminals read from %f\n", nonterminals_declared, F);
    return nonterminals_declared;
}

§2. Converting Preform errors to problems. Errors in Preform syntax are generated in the words module, and are ordinarily issued in a low-level way, with terse lines printed to STDERR. Providing the following allows us to give the Inform user a fuller message:

define PREFORM_ERROR_WORDS_CALLBACK CorePreform::preform_error
void CorePreform::preform_error(word_assemblage base_text, nonterminal *nt,
    production *pr, char *message) {
    if (pr) {
        LOG("The production at fault is:\n");
        Instrumentation::log_production(pr, FALSE); LOG("\n");
    }
    if (nt == NULL)
        Problems::quote_text(1, "(no nonterminal)");
    else
        Problems::quote_wide_text(1, Vocabulary::get_exemplar(nt->nonterminal_id, FALSE));
    Problems::quote_text(2, message);
    StandardProblems::handmade_problem(Task::syntax_tree(), _p_(Untestable));
    if (WordAssemblages::nonempty(base_text)) {
        Problems::quote_wa(5, &base_text);
        Problems::issue_problem_segment(
            "I'm having difficulties conjugating the verb '%5'. ");
    }

    TEMPORARY_TEXT(TEMP)
    if (pr) {
        Problems::quote_number(3, &(pr->match_number));
        ptoken *pt;
        for (pt = pr->first_pt; pt; pt = pt->next_pt) {
            Instrumentation::write_ptoken(TEMP, pt);
            if (pt->next_pt) WRITE_TO(TEMP, " ");
        }
        Problems::quote_stream(4, TEMP);
        Problems::issue_problem_segment(
            "There's a problem in Inform's linguistic grammar, which is probably "
            "set by a translation extension. The problem occurs in line %3 of "
            "%1 ('%4'): %2.");
    } else {
        Problems::issue_problem_segment(
            "There's a problem in Inform's linguistic grammar, which is probably "
            "set by a translation extension. The problem occurs in the definition of "
            "%1: %2.");
    }
    Problems::issue_problem_end();
    DISCARD_TEXT(TEMP)
}

§3. Optimisation. The following is fine-tuning for speed: if it weren't here, the compiler would still function, but would be slower. With that said, it's possible to break things by making the wrong settings here, so be wary of making changes.

Setting up happens in two stages. Firstly, when this module (and therefore the compiler) starts up, certain internally-defined Preform nonterminals — those defined by functions in the code, not loaded from Syntax files — need to be marked with NT incidence bits. (See Nonterminal Incidences (in words).)

void CorePreform::set_core_internal_NTIs(void) {
    NTI::give_nt_reserved_incidence_bit(<s-adjective>, ADJECTIVE_RES_NT_BIT);
    NTI::give_nt_reserved_incidence_bit(<s-object-instance>, PROPER_NOUN_RES_NT_BIT);
}

§4. Later on, the words module calls the following function to mark that a match to the given nonterminal must contain only words with certain NTI bits: for example, a match to <k-kind> has to contain words with either the <article> bit or the <k-kind> bit set, which as we see above is COMMON_NOUN_RES_NT_BIT.

define MORE_PREFORM_OPTIMISER_WORDS_CALLBACK CorePreform::set_core_internal_requirements
void CorePreform::set_core_internal_requirements(void) {
    NTI::every_word_in_match_must_have_my_NTI_bit(<s-adjective>);
    CorePreform::mark_nt_as_requiring_itself_articled(<s-object-instance>);
    CorePreform::mark_nt_as_requiring_itself_articled(<k-kind-variable>);
    CorePreform::mark_nt_as_requiring_itself_articled(<k-formal-variable>);
    CorePreform::mark_nt_as_requiring_itself_articled(<k-base-kind>);
    CorePreform::mark_nt_as_requiring_itself_articled(<k-kind-construction>);
    CorePreform::mark_nt_as_requiring_itself_articled(<k-kind>);
    CorePreform::mark_nt_as_requiring_itself_articled(<k-kind-of-kind>);
}

void CorePreform::mark_nt_as_requiring_itself_articled(nonterminal *nt) {
    NTI::every_word_in_match_must_have_my_NTI_bit_or_this_one(nt,
        NTI::nt_incidence_bit(<article>));
}