Errors with the source text, either lexical issues or major syntactic ones, are found when Inbuild reads the text in: what this section does is to collect and issue those errors as tidy Inform problem messages.

§1. To trigger all of the problems listed below, test with the :inbuild group.

void SourceProblems::issue_problems_arising(inbuild_copy *C) {
    if (C == NULL) return;
    copy_error *CE;
    LOOP_OVER_LINKED_LIST(CE, copy_error, C->errors_reading_source_text) {
        switch (CE->error_category) {
            case OPEN_FAILED_CE:
                Problems::quote_stream(1, Filenames::get_leafname(CE->details_file));
                StandardProblems::handmade_problem(Task::syntax_tree(), _p_(Untestable));
                Problems::issue_problem_segment(
                    "I can't open the file '%1' of source text. %P"
                    "If you are using the 'Source' subfolder of Materials to "
                    "hold your source text, maybe your 'Contents.txt' has a "
                    "typo in it?");
                Problems::issue_problem_end();
                break;
            case EXT_MISWORDED_CE:
                Problems::quote_work(1, CE->copy->found_by->work);
                Problems::quote_stream(2, CE->details);
                StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_ExtMiswordedBeginsHere));
                Problems::issue_problem_segment(
                    "The extension %1, which your source text makes use of, seems to be "
                    "damaged or incorrect: its identifying opening line is wrong. "
                    "Specifically, %2.");
                Problems::issue_problem_end();
                break;
            case EXT_BAD_DIRNAME_CE:
                Problems::quote_work(1, CE->copy->found_by->work);
                Problems::quote_stream(2, CE->details);
                StandardProblems::handmade_problem(Task::syntax_tree(), _p_(Untestable));
                Problems::issue_problem_segment(
                    "The extension %1, which your source text makes use of, is stored "
                    "in a directory (which is fine), but does not follow the rules for "
                    "what that directory is called (which is not fine). Specifically, %2.");
                Problems::issue_problem_end();
                break;
            case EXT_BAD_FILENAME_CE:
                Problems::quote_work(1, CE->copy->found_by->work);
                Problems::quote_stream(2, CE->details);
                StandardProblems::handmade_problem(Task::syntax_tree(), _p_(Untestable));
                Problems::issue_problem_segment(
                    "The extension %1, which your source text makes use of, has the wrong "
                    "filename for its source text. Specifically, %2.");
                Problems::issue_problem_end();
                break;
            case EXT_RANEOUS_CE:
                Problems::quote_work(1, CE->copy->found_by->work);
                Problems::quote_stream(2, CE->details);
                StandardProblems::handmade_problem(Task::syntax_tree(), _p_(Untestable));
                Problems::issue_problem_segment(
                    "The extension %1, which your source text makes use of, is stored "
                    "in a directory (which is fine), but contains files or subdirectories "
                    "which I don't recognise (which is not fine). Specifically, %2.");
                Problems::issue_problem_end();
                break;
            case METADATA_MALFORMED_CE:
            case MALFORMED_LICENCE_CE:
                if (CE->copy->found_by) {
                    Problems::quote_work(1, CE->copy->found_by->work);
                    Problems::quote_stream(2, CE->details);
                    SourceProblems::quote_genre(3, CE);
                    StandardProblems::handmade_problem(Task::syntax_tree(), _p_(Untestable));
                    Problems::issue_problem_segment(
                        "The %3 %1, which your source text makes use of, seems to have "
                        "metadata problems. Specifically: %2.");
                    Problems::issue_problem_end();
                } else {
                    Problems::quote_work(1, CE->copy->edition->work);
                    Problems::quote_stream(2, CE->details);
                    SourceProblems::quote_genre(3, CE);
                    StandardProblems::handmade_problem(Task::syntax_tree(), _p_(Untestable));
                    Problems::issue_problem_segment(
                        "The %3 %1 seems to have metadata problems. Specifically: %2.");
                    Problems::issue_problem_end();
                }
                break;
            case LANGUAGE_UNAVAILABLE_CE:
                Problems::quote_work(1, CE->copy->edition->work);
                Problems::quote_stream(2, CE->details);
                SourceProblems::quote_genre(3, CE);
                StandardProblems::handmade_problem(Task::syntax_tree(), _p_(Untestable));
                Problems::issue_problem_segment(
                    "The %3 %1 seems to need me to know about a non-English language, '%2'. "
                    "I can't find any definition for this language.");
                Problems::issue_problem_end();
                break;
            case LANGUAGE_DEFICIENT_CE:
                Problems::quote_work(1, CE->copy->edition->work);
                Problems::quote_stream(2, CE->details);
                SourceProblems::quote_genre(3, CE);
                StandardProblems::handmade_problem(Task::syntax_tree(), _p_(Untestable));
                Problems::issue_problem_segment(
                    "The %3 %1 seems to need me to work with a non-English language, but '%2'.");
                Problems::issue_problem_end();
                break;
            case EXT_TITLE_TOO_LONG_CE: {
                int max = MAX_EXTENSION_TITLE_LENGTH;
                int overage = CE->details_N - MAX_EXTENSION_TITLE_LENGTH;
                Problems::quote_work(1, CE->copy->found_by->work);
                Problems::quote_number(2, &max);
                Problems::quote_number(3, &overage);
                StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_ExtTitleTooLong));
                Problems::issue_problem_segment(
                    "The extension %1, which your source text makes use of, has a "
                    "title which is too long, exceeding the maximum allowed "
                    "(%2 characters) by %3.");
                Problems::issue_problem_end();
                break;
            }
            case EXT_AUTHOR_TOO_LONG_CE: {
                int max = MAX_EXTENSION_AUTHOR_LENGTH;
                int overage = CE->details_N - MAX_EXTENSION_AUTHOR_LENGTH;
                Problems::quote_work(1, CE->copy->found_by->work);
                Problems::quote_number(2, &max);
                Problems::quote_number(3, &overage);
                StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_ExtAuthorTooLong));
                Problems::issue_problem_segment(
                    "The extension %1, which your source text makes use of, has an "
                    "author name which is too long, exceeding the maximum allowed "
                    "(%2 characters) by %3.");
                Problems::issue_problem_end();
                break;
            }
            case LEXER_CE:
                switch (CE->error_subcategory) {
                    case STRING_TOO_LONG_LEXERERROR:
                        StandardProblems::lexical_problem(Task::syntax_tree(), _p_(PM_TooMuchQuotedText),
                            "Too much text in quotation marks", CE->details_word,
                            "...\" The maximum length is very high, so this is more "
                            "likely to be because a close quotation mark was "
                            "forgotten.");
                        break;
                    case WORD_TOO_LONG_LEXERERROR:
                          StandardProblems::lexical_problem(Task::syntax_tree(), _p_(PM_WordTooLong),
                            "Word too long", CE->details_word,
                            "(Individual words of unquoted text can run up to "
                            "128 letters long, which ought to be plenty. The longest "
                            "recognised place name in the English speaking world is "
                            "a hill in New Zealand called Taumatawhakatang-"
                            "ihangakoauauot-amateaturipukaka-pikimaunga-"
                            "horonuku-pokaiwhenuak-itanatahu. (You say tomato, "
                            "I say taumatawhakatang-...) The longest word found in a "
                            "classic novel is bababadalgharaghtakamminarronnkonnbronntonn"
                            "erronntuonnthunntrovarrhounawnskawntoohoohoordenenthurnuk, "
                            "creation's thunderclap from Finnegan's Wake. And both of those "
                            "words are fine.)");
                        break;
                    case I6_TOO_LONG_LEXERERROR:
                        StandardProblems::lexical_problem(Task::syntax_tree(), _p_(Untestable),  well, not at all conveniently
                            "Verbatim Inform 6 extract too long", CE->details_word,
                            "... -). The maximum length is quite high, so this "
                            "may be because a '-)' was forgotten. Still, if "
                            "you do need to paste a huge I6 program in, try "
                            "using several verbatim inclusions in a row.");
                        break;
                    case STRING_NEVER_ENDS_LEXERERROR:
                        StandardProblems::lexical_problem_S(Task::syntax_tree(), _p_(PM_UnendingQuote),
                            "Some source text ended in the middle of quoted text",
                            CE->details,
                            "This probably means that a quotation mark is missing "
                            "somewhere. If you are using Inform with syntax colouring, "
                            "look for where the quoted-text colour starts. (Sometimes "
                            "this problem turns up because a piece of quoted text contains "
                            "a text substitution in square brackets which in turn contains "
                            "another piece of quoted text - this is not allowed, and causes "
                            "me to lose track.)");
                        break;
                    case COMMENT_NEVER_ENDS_LEXERERROR:
                        StandardProblems::lexical_problem_S(Task::syntax_tree(), _p_(PM_UnendingComment),
                            "Some source text ended in the middle of a comment",
                            CE->details,
                            "This probably means that a ']' is missing somewhere. "
                            "(If you are using Inform with syntax colouring, look for "
                            "where the comment colour starts.) Inform's convention on "
                            "'nested comments' is that each '[' in a comment must be "
                            "matched by a corresponding ']': so for instance '[This "
                            "[even nested like so] acts as a comment]' is a single "
                            "comment - the first ']' character matches the second '[' "
                            "and so doesn't end the comment: only the second ']' ends "
                            "the comment.");
                        break;
                    case I6_NEVER_ENDS_LEXERERROR:
                        StandardProblems::lexical_problem_S(Task::syntax_tree(), _p_(PM_UnendingI6),
                            "Some source text ended in the middle of a verbatim passage "
                            "of Inform 6 code",
                            CE->details,
                            "This probably means that a '-)' is missing.");
                        break;
                    default:
                        internal_error("unknown lexer error");
                }
                break;
            case SYNTAX_CE:
                switch (CE->error_subcategory) {
                    case UnexpectedSemicolon_SYNERROR:
                        Problems::quote_source(1, Diagrams::new_UNPARSED_NOUN(CE->details_W));
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_UnexpectedSemicolon));
                        Problems::issue_problem_segment(
                            "The text %1 is followed by a semicolon ';', which only makes "
                            "sense to me inside a rule or phrase (where there's a heading, "
                            "then a colon, then a list of instructions divided by semicolons). "
                            "Perhaps you want a full stop '.' instead?");
                        Problems::issue_problem_end();
                        break;
                    case ParaEndsInColon_SYNERROR:
                        Problems::quote_source(1, Diagrams::new_UNPARSED_NOUN(CE->details_W));
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_ParaEndsInColon));
                        Problems::issue_problem_segment(
                            "The text %1 seems to end a paragraph with a colon. (Rule declarations "
                            "can end a sentence with a colon, so maybe there's accidentally a "
                            "skipped line here?)");
                        Problems::issue_problem_end();
                        break;
                    case SentenceEndsInColon_SYNERROR:
                        Problems::quote_source(1, Diagrams::new_UNPARSED_NOUN(CE->details_W));
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_SentenceEndsInColon));
                        Problems::issue_problem_segment(
                            "The text %1 seems to have a colon followed by a full stop, which is "
                            "punctuation I don't understand.");
                        Problems::issue_problem_end();
                        break;
                    case SentenceEndsInSemicolon_SYNERROR:
                        Problems::quote_source(1, Diagrams::new_UNPARSED_NOUN(CE->details_W));
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_SentenceEndsInSemicolon));
                        Problems::issue_problem_segment(
                            "The text %1 seems to have a semicolon followed by a full stop, which is "
                            "punctuation I don't understand.");
                        Problems::issue_problem_end();
                        break;
                    case SemicolonAfterColon_SYNERROR:
                        Problems::quote_source(1, Diagrams::new_UNPARSED_NOUN(CE->details_W));
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_SemicolonAfterColon));
                        Problems::issue_problem_segment(
                            "The text %1 seems to have a semicolon following a colon, which is "
                            "punctuation I don't understand.");
                        Problems::issue_problem_end();
                        break;
                    case SemicolonAfterStop_SYNERROR:
                        Problems::quote_source(1, Diagrams::new_UNPARSED_NOUN(CE->details_W));
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_SemicolonAfterStop));
                        Problems::issue_problem_segment(
                            "The text %1 seems to have a semicolon following a full stop, which is "
                            "punctuation I don't understand.");
                        Problems::issue_problem_end();
                        break;
                    case HeadingOverLine_SYNERROR:
                        Problems::quote_source(1, Diagrams::new_UNPARSED_NOUN(CE->details_W));
                        Problems::quote_source(2, Diagrams::new_UNPARSED_NOUN(Wordings::up_to(CE->details_W, CE->details_N-1)));
                        Problems::quote_source(3, Diagrams::new_UNPARSED_NOUN(Wordings::from(CE->details_W, CE->details_N)));
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_HeadingOverLine));
                        Problems::issue_problem_segment(
                            "The text %1 seems to be a heading, but contains a "
                            "line break, which is not allowed: so I am reading it "
                            "as just %2 and ignoring the continuation %3. The rule "
                            "is that a heading must be a single line which is the "
                            "only sentence in its paragraph, so there must be a "
                            "skipped line above and below.");
                        Problems::issue_problem_end();
                        break;
                    case HeadingStopsBeforeEndOfLine_SYNERROR:
                        Problems::quote_source(1, Diagrams::new_UNPARSED_NOUN(CE->details_W));
                        Problems::quote_source(2,
                            Diagrams::new_UNPARSED_NOUN(Wordings::new(Wordings::last_wn(CE->details_W)+1, CE->details_N-1)));
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_HeadingStopsBeforeEndOfLine));
                        Problems::issue_problem_segment(
                            "The text %1 seems to be a heading, but does not occupy "
                            "the whole of its line of source text, which continues %2. "
                            "The rule is that a heading must occupy a whole single line "
                            "which is the only sentence in its paragraph, so there "
                            "must be a skipped line above and below. %P"
                            "A heading must not contain a colon ':' or any full stop "
                            "characters '.', even if they occur in an ellipsis '...' or a "
                            "number '2.3.13'. (I mention that because sometimes this problem "
                            "arises when a decimal point is misread as a full stop.)");
                        Problems::issue_problem_end();
                        break;

                    case ExtNoBeginsHere_SYNERROR:
                        StandardProblems::extension_problem(_p_(PM_ExtNoBeginsHere),
                            Extensions::from_copy(C),
                            "has no 'begins here' sentence");
                        break;
                    case ExtNoEndsHere_SYNERROR:
                        StandardProblems::extension_problem(_p_(PM_ExtNoEndsHere),
                            Extensions::from_copy(C),
                            "has no 'ends here' sentence");
                        break;
                    case ExtSpuriouslyContinues_SYNERROR:
                        StandardProblems::extension_problem(_p_(PM_ExtSpuriouslyContinues),
                            Extensions::from_copy(C),
                            "continues after the 'ends here' sentence");
                        break;
                    case ExtMultipleEndsHere_SYNERROR:
                        StandardProblems::extension_problem(_p_(PM_ExtMultipleEndsHere),
                            Extensions::from_copy(C),
                            "has more than one 'ends here' sentence");
                        break;
                    case ExtMultipleBeginsHere_SYNERROR:
                        StandardProblems::extension_problem(_p_(PM_ExtMultipleBeginsHere),
                            Extensions::from_copy(C),
                            "has more than one 'begins here' sentence");
                        break;
                    case ExtBeginsAfterEndsHere_SYNERROR:
                        StandardProblems::extension_problem(_p_(PM_ExtBeginsAfterEndsHere),
                            Extensions::from_copy(C),
                            "has a further 'begins here' after an 'ends here'");
                        break;
                    case ExtEndsWithoutBegins_SYNERROR:
                        StandardProblems::extension_problem(_p_(BelievedImpossible),
                            Extensions::from_copy(C),
                            "has an 'ends here' with nothing having begun");
                        break;
                    case BadTitleSentence_SYNERROR:
                        current_sentence = NULL;
                        StandardProblems::unlocated_problem(Task::syntax_tree(),
                            _p_(PM_BadTitleSentence),
                            "The opening bibliographic sentence can only be a title in "
                            "double-quotes, possibly followed with 'by' and the name of "
                            "the author.");
                        break;
                    case UnknownLanguageElement_SYNERROR:
                        current_sentence = CE->details_node;
                        StandardProblems::sentence_problem(
                            Task::syntax_tree(), _p_(PM_UnknownLanguageElement),
                            "this heading contains a stipulation about the current "
                            "Inform language definition which I can't understand",
                            "and should be something like '(for Glulx external files "
                            "language element only)'.");
                        break;
                    case UnknownVirtualMachine_SYNERROR:
                        current_sentence = CE->details_node;
                        StandardProblems::sentence_problem(
                            Task::syntax_tree(), _p_(PM_UnknownVirtualMachine),
                            "this heading contains a stipulation about the Setting "
                            "for story file format which I can't understand",
                            "and should be something like '(for Z-machine version 5 "
                            "or 8 only)' or '(for Glulx only)'.");
                        break;
                    case UseElementWithdrawn_SYNERROR:
                        current_sentence = CE->details_node;
                        StandardProblems::sentence_problem(
                            Task::syntax_tree(), _p_(PM_UseElementWithdrawn),
                            "the ability to activate or deactivate compiler elements "
                            "in source text has been withdrawn",
                            "in favour of a new system with Inform kits.");
                        break;
                    case IncludeExtQuoted_SYNERROR:
                        current_sentence = CE->details_node;
                        StandardProblems::sentence_problem(
                            Task::syntax_tree(), _p_(PM_IncludeExtQuoted),
                            "the name of an included extension should be given without double "
                            "quotes in an Include sentence",
                            "so for instance 'Include Oh My God by Janice Bing.' rather than "
                            "'Include \"Oh My God\" by Janice Bing.')");
                        break;
                    case BogusExtension_SYNERROR:
                        current_sentence = CE->details_node;
                        Problems::quote_source(1, current_sentence);
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_BogusExtension));
                        Problems::issue_problem_segment(
                            "I can't find the extension requested by: %1.");
                        Problems::issue_problem_end();
                        break;
                    case ExtVersionTooLow_SYNERROR:
                        current_sentence = CE->details_node;
                        Problems::quote_source(1, current_sentence);
                        Problems::quote_stream(2, CE->details);
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_ExtVersionTooLow));
                        Problems::issue_problem_segment(
                            "I can't find the right version of the extension requested by %1 - "
                            "I can only find %2.");
                        Problems::issue_problem_end();
                        break;
                    case ExtVersionMalformed_SYNERROR:
                        current_sentence = CE->details_node;
                        StandardProblems::sentence_problem(
                            Task::syntax_tree(), _p_(PM_ExtVersionMalformed),
                            "a version number must have the form N/DDDDDD",
                            "as in the example '2/040426' for release 2 made on 26 April 2004. "
                            "(The DDDDDD part is optional, so '3' is a legal version number too. "
                            "N must be between 1 and 999: in particular, there is no version 0.)");
                        break;
                    case ExtInadequateVM_SYNERROR:
                        current_sentence = CE->details_node;
                        Problems::quote_source(1, current_sentence);
                        Problems::quote_stream(2, CE->details);
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_ExtInadequateVM));
                        Problems::issue_problem_segment(
                            "You wrote %1: but my copy of that extension stipulates that it "
                            "is '%2'. That means it can only be used with certain of "
                            "the possible compiled story file formats, and at the "
                            "moment, we don't fit the requirements. (You can change "
                            "the format used for this project on the Settings panel.)");
                        Problems::issue_problem_end();
                        break;
                    case ExtMisidentifiedEnds_SYNERROR:
                        current_sentence = CE->details_node;
                        Problems::quote_extension(1, Extensions::from_copy(C));
                        Problems::quote_wording(2, CE->details_W);
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_ExtMisidentifiedEnds));
                        Problems::issue_problem_segment(
                            "The extension %1, which your source text makes use of, seems to be "
                            "malformed: its 'begins here' sentence correctly identifies it, but "
                            "then the 'ends here' sentence calls it '%2' instead. (They need "
                            "to be a matching pair except that the end does not name the "
                            "author: for instance, 'Hocus Pocus by Jan Ackerman begins here.' "
                            "would match with 'Hocus Pocus ends here.')");
                        Problems::issue_problem_end();
                        break;
                    case HeadingInPlaceOfUnincluded_SYNERROR:
                        current_sentence = CE->details_node;
                        Problems::quote_source(1, current_sentence);
                        Problems::quote_extension_id(2, CE->details_work);
                        StandardProblems::handmade_problem(
                            Task::syntax_tree(), _p_(PM_HeadingInPlaceOfUnincluded));
                        Problems::issue_problem_segment(
                            "In the sentence %1, it looks as if you intend to replace a section "
                            "of source text from the extension '%2', but no extension of that "
                            "name has been included - so it is not possible to replace any of its "
                            "headings.");
                        Problems::issue_problem_end();
                        break;
                    case UnequalHeadingInPlaceOf_SYNERROR:
                        current_sentence = CE->details_node;
                        StandardProblems::sentence_problem(
                            Task::syntax_tree(), _p_(PM_UnequalHeadingInPlaceOf),
                            "these headings are not of the same level",
                            "so it is not possible to make the replacement. (Level here means "
                            "being a Volume, Book, Part, Chapter or Section: for instance, "
                            "only a Chapter heading can be used 'in place of' a Chapter.)");
                        break;
                    case HeadingInPlaceOfSubordinate_SYNERROR:
                        current_sentence = CE->details_node;
                        Problems::quote_source(1, current_sentence);
                        Problems::quote_extension_id(2, CE->details_work);
                        Problems::quote_source(3, CE->details_node2);
                        Problems::quote_extension_id(4, CE->details_work2);
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_HeadingInPlaceOfSubordinate));
                        Problems::issue_problem_segment(
                            "In the sentence %1, it looks as if you intend to replace a section "
                            "of source text from the extension '%2', but that doesn't really make "
                            "sense because this new piece of source text is part of a superior "
                            "heading ('%3') which is already being replaced spliced into '%4'.");
                        Problems::issue_problem_end();
                        break;
                    case HeadingInPlaceOfUnknown_SYNERROR:
                        current_sentence = CE->details_node;
                        Problems::quote_source(1, current_sentence);
                        Problems::quote_extension_id(2, CE->details_work);
                        Problems::quote_wording(3, CE->details_W);
                        Problems::quote_stream(4, CE->details);
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_HeadingInPlaceOfUnknown));
                        Problems::issue_problem_segment(
                            "In the sentence %1, it looks as if you intend to replace a section "
                            "of source text from the extension '%2', but that extension does "
                            "not seem to have any heading called '%3'. (The version I loaded "
                            "was %4.)");
                        Problems::issue_problem_end();
                        break;
                    case UnavailableLOS_SYNERROR:
                        current_sentence = NULL;
                        Problems::quote_stream(1, CE->details);
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(...));
                        Problems::issue_problem_segment(
                            "The project says that its syntax is written in a language "
                            "other than English (specifically, %1), but the language bundle "
                            "for that language does not provide a file of Preform definitions.");
                        Problems::issue_problem_end();
                        break;
                    case DialogueOnSectionsOnly_SYNERROR:
                        current_sentence = CE->details_node;
                        Problems::quote_source(1, current_sentence);
                        StandardProblems::handmade_problem(Task::syntax_tree(),
                            _p_(PM_DialogueOnSectionsOnly));
                        Problems::issue_problem_segment(
                            "In the heading %1, you've marked for '(dialogue)', but only "
                            "Sections can be so marked - not Chapters, Books, and so on.");
                        Problems::issue_problem_end();
                        break;
                    case UnexpectedDialogue_SYNERROR:
                        Problems::quote_source(1, Diagrams::new_UNPARSED_NOUN(CE->details_W));
                        StandardProblems::handmade_problem(Task::syntax_tree(),
                            _p_(PM_UnexpectedDialogue));
                        Problems::issue_problem_segment(
                            "The text %1 appears under a section heading marked as dialogue, "
                            "so it needs to be either a cue in brackets '(like this.)', or "
                            "else a line of dialogue 'Speaker: \"Something to say!\"'. It "
                            "doesn't seem to be either of those.");
                        Problems::issue_problem_end();
                        break;
                    case UnquotedDialogue_SYNERROR:
                        Problems::quote_source(1, Diagrams::new_UNPARSED_NOUN(CE->details_W));
                        StandardProblems::handmade_problem(Task::syntax_tree(),
                             _p_(PM_UnquotedDialogue));
                        Problems::issue_problem_segment(
                            "The text %1 appears to be a line of dialogue, but after the "
                            "colon ':' there should only be a single double-quoted text.");
                        Problems::issue_problem_end();
                        break;
                    case EmptyDialogueClause_SYNERROR:
                        Problems::quote_source(1, Diagrams::new_UNPARSED_NOUN(CE->details_W));
                        StandardProblems::handmade_problem(Task::syntax_tree(),
                            _p_(PM_EmptyDialogueClause));
                        Problems::issue_problem_segment(
                            "The text %1 appears to be a bracketed clause to do with "
                            "dialogue, but the punctuation looks wrong because it includes "
                            "an empty part.");
                        Problems::issue_problem_end();
                        break;
                    case MisbracketedDialogueClause_SYNERROR:
                        Problems::quote_source(1, Diagrams::new_UNPARSED_NOUN(CE->details_W));
                        StandardProblems::handmade_problem(Task::syntax_tree(),
                            _p_(PM_MisbracketedDialogueClause));
                        Problems::issue_problem_segment(
                            "The text %1 appears to be a bracketed clause to do with "
                            "dialogue, but the punctuation looks wrong because it uses "
                            "brackets '(' and ')' in a way which doesn't match. There "
                            "should be just one outer pair of brackets, and inside they "
                            "can only be used to clarify clauses, if necessary.");
                        Problems::issue_problem_end();
                        break;
                    case MissingSourceFile_SYNERROR:
                        current_sentence = CE->details_node;
                        Problems::quote_source(1, current_sentence);
                        Problems::quote_stream(2, CE->details);
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(...));
                        Problems::issue_problem_segment(
                            "I can't find the source file holding the content of the heading %1 - "
                            "it should be '%2' in the 'Source' subdirectory of the materials "
                            "for this project.");
                        Problems::issue_problem_end();
                        break;
                    case HeadingWithFileNonempty_SYNERROR:
                        current_sentence = CE->details_node;
                        Problems::quote_source(1, current_sentence);
                        Problems::quote_stream(2, CE->details);
                        Problems::quote_source(3, current_sentence->down);
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(...));
                        Problems::issue_problem_segment(
                            "The heading %1 should refer only to the contents of the file "
                            "'%2' (in the 'Source' subdirectory of the materials for this "
                            "project) but in fact goes on to contain other material too. "
                            "That other material (see %3) needs to be put under a new "
                            "heading of equal or greater priority (or else moved to the file).");
                        Problems::issue_problem_end();
                        break;
                    case MisheadedSourceFile_SYNERROR:
                        current_sentence = CE->details_node;
                        Problems::quote_source(1, current_sentence);
                        Problems::quote_stream(2, CE->details);
                        heading *h = Node::get_embodying_heading(current_sentence);
                        if (h) Problems::quote_wording(3, h->heading_text);
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(...));
                        Problems::issue_problem_segment(
                            "The heading %1 says that its contents are in the file "
                            "'%2' (in the 'Source' subdirectory of the materials for this "
                            "project). If so, then that file needs to start with a matching "
                            "opening line, giving the same heading name '%3'; and it doesn't.");
                        Problems::issue_problem_end();
                        break;
                    case HeadingTooGreat_SYNERROR:
                        current_sentence = CE->details_node;
                        Problems::quote_source(1, current_sentence);
                        StandardProblems::handmade_problem(Task::syntax_tree(), _p_(...));
                        Problems::issue_problem_segment(
                            "The heading %1 is too high a level to appear in this source "
                            "file. For example, if a source file contains the contents of "
                            "a Chapter, then it cannot contain a Book heading - "
                            "a Chapter can be part of a Book, but not vice versa.");
                        Problems::issue_problem_end();
                        break;
                    default:
                        internal_error("unknown syntax error");
                }
                break;
            default: internal_error("an unknown error occurred");
        }
    }
    LOOP_OVER_LINKED_LIST(CE, copy_error, C->warnings) {
        switch (CE->error_category) {
            case PROJECT_EXTRANEOUS_CE:
                Problems::quote_stream(1, CE->details);
                StandardProblems::handmade_warning(Task::syntax_tree(), _p_(Untestable));
                Problems::issue_problem_segment(
                    "This project contains some unexpected files or directories. Specifically, %1.");
                Problems::issue_warning_end();
                break;
        }
    }
    C->warnings = NULL;
}

void SourceProblems::quote_genre(int N, copy_error *CE) {
    text_stream *name = CE->copy->edition->work->genre->genre_name;
    if (Str::eq(name, I"projectbundle")) name = I"project";
    if (Str::eq(name, I"projectfile")) name = I"project";
    if (Str::eq(name, I"extensionbundle")) name = I"extension";
    Problems::quote_stream(N, name);
}

§2. These are errors generated by the building module, but which we want to tidy up and present in the usual Inform 7 way.

void SourceProblems::inter_schema_errors(inter_schema *sch) {
    Problems::quote_source(1, current_sentence);
    Problems::quote_stream(2, sch->converted_from);
    StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_InterSchemaErrors));
    Problems::issue_problem_segment(
        "In the sentence %1, you use a fragment of code written in Inform 6 "
        "syntax which seems to be malformed in some way. I delegate all that "
        "work to a lesser compiler: I gave it '%2' to compile, and it came "
        "back with this: ");
    schema_parsing_error *err;
    int c = 1;
    LOOP_OVER_LINKED_LIST(err, schema_parsing_error, sch->parsing_errors) {
        Problems::quote_stream(1, err->message);
        Problems::quote_number(2, &c);
        Problems::issue_problem_segment("%P%2. %1 ");
        c++;
    }
    Problems::issue_problem_end();
}

§3. And these are errors (mostly) from parsing the Inform 6-syntax content in Include (- ... -) insertions of low-level code:

text_stream *notified_kit_name = NULL;
text_stream *notified_architecture_name = NULL;
int general_kit_notice_issued = FALSE;
int trigger_kit_notice = FALSE;

void SourceProblems::kit_notification(text_stream *kit_name, text_stream *architecture_name) {
    if (Str::len(kit_name) > 0) trigger_kit_notice = TRUE;
    else trigger_kit_notice = FALSE;
    notified_kit_name = Str::duplicate(kit_name);
    notified_architecture_name = Str::duplicate(architecture_name);
}

void SourceProblems::I6_level_error(char *message, text_stream *quote,
    text_provenance at) {
    filename *F = Provenance::get_filename(at);
    int line = Provenance::get_line(at);
    TEMPORARY_TEXT(file)
    if (F) WRITE_TO(file, "%f", F);
    TEMPORARY_TEXT(kit)
    TEMPORARY_TEXT(M)
    WRITE_TO(M, message, quote);
    if (Provenance::is_somewhere(at)) {
        TEMPORARY_TEXT(EX)
        Filenames::write_extension(EX, F);
        if (Str::eq_insensitive(EX, I".i6t")) {
            pathname *P = Filenames::up(F);
            if (Str::eq_insensitive(Pathnames::directory_name(P), I"Sections"))
                P = Pathnames::up(P);
            WRITE_TO(kit, "%S", Pathnames::directory_name(P));
            Str::clear(file);
            WRITE_TO(file, "%S", Filenames::get_leafname(F));
        }
        DISCARD_TEXT(EX)
    }
    if (trigger_kit_notice) {
        if (general_kit_notice_issued == FALSE) {
            StandardProblems::handmade_problem(Task::syntax_tree(), _p_(...));
            Problems::issue_problem_segment(
                "Before the project could be translated, one of the 'kits' of low-level "
                "Inter code which it uses needed to be built first. This is seldom "
                "necessary and normally happens silently when it is, but this time errors "
                "occurred and therefore translation had to be abandoned. If you are "
                "currently tinkering with a kit, you'll often see errors like this, "
                "but otherwise it probably means that a new extension you're using "
                "(and which contains a kit) isn't properly working.");
            general_kit_notice_issued = TRUE;
            Problems::issue_problem_end();
        }
        WRITE_TO(problems_file, "<h3>Building %S for architecture %S</h3>\n",
            notified_kit_name, notified_architecture_name);
        trigger_kit_notice = FALSE;
    }
    StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_I6SyntaxError));
    Problems::quote_stream(1, M);
    if (Str::len(kit) > 0) {
        Problems::quote_stream(2, file);
        Problems::quote_number(3, &line);
        Problems::quote_stream(4, kit);
        if (general_kit_notice_issued) Problems::issue_problem_segment("%2, near line %3: %1.");
        else Problems::issue_problem_segment("Near line %3 of file %2 in %4: %1.");
    } else if (Provenance::is_somewhere(at)) {
        LOG("%S, line %d:\n", file, line);
        Problems::problem_quote_file(2, file, line);
        Problems::issue_problem_segment(
            "Inform 6 syntax error near here %2: %1.");
    } else {
        Problems::issue_problem_segment(
            "My low-level reader of source code reported a mistake - \"%1\". "
            "%PLow-level material written in Inform 6 syntax occurs either in kits or "
            "in matter written inside 'Include (- ... -)' in source text, either in "
            "the main source or in an extension used by it.");
    }
    Problems::issue_problem_end();
    if ((Str::len(kit) > 0) && (general_kit_notice_issued)) {
        WRITE_TO(problems_file, "<p>Path: ");
        pathname *P = NULL, *Q = NULL, *MAT = NULL, *EXT = NULL, *INT = NULL;
        for (Q = Filenames::up(F); Q; Q = Pathnames::up(Q)) {
            text_stream *name = Pathnames::directory_name(Q);
            if (Str::eq_insensitive(name, I"Extensions")) EXT = Q;
            if (Str::eq_insensitive(name, I"Inter")) INT = Q;
            if (Str::suffix_eq(name, I".materials", 10)) MAT = Q;
        }
        if (MAT) P = MAT; else if (EXT) P = EXT; else if (INT) P = INT;
        if (P) Filenames::to_text_relative(problems_file, F, Pathnames::up(P));
        else WRITE_TO(problems_file, "%f", F);
        WRITE_TO(problems_file, "</p>");
    }
    DISCARD_TEXT(M)
    DISCARD_TEXT(kit)
}