diff --git a/README.md b/README.md index 97eff62b5..5e27d7fe5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Inform 7 -[Version](notes/versioning.md): 10.2.0-beta+6V90 'Krypton' (31 October 2022) +[Version](notes/versioning.md): 10.2.0-beta+6V91 'Krypton' (1 November 2022) ## About Inform diff --git a/build.txt b/build.txt index 721c2fa21..17e548b9d 100644 --- a/build.txt +++ b/build.txt @@ -1,3 +1,3 @@ Prerelease: beta -Build Date: 31 October 2022 -Build Number: 6V90 +Build Date: 1 November 2022 +Build Number: 6V91 diff --git a/docs/assertions-module/4-nr.html b/docs/assertions-module/4-nr.html index c65d18a0b..f47157c65 100644 --- a/docs/assertions-module/4-nr.html +++ b/docs/assertions-module/4-nr.html @@ -249,7 +249,7 @@ and has no "level" or "indentation" as such.
 void NameResolution::make_the_tree(void) {
-    Headings::assemble_tree(Task::syntax_tree());
+    Headings::assemble_tree(Task::syntax_tree(), Task::project()->as_copy);
 }
 
 heading *NameResolution::pseudo_heading(void) {
diff --git a/docs/core-module/2-pwst.html b/docs/core-module/2-pwst.html
index d138cd618..f1b7f0fb9 100644
--- a/docs/core-module/2-pwst.html
+++ b/docs/core-module/2-pwst.html
@@ -535,6 +535,56 @@ group.
                             "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");
                 }
diff --git a/docs/supervisor-module/1-ic.html b/docs/supervisor-module/1-ic.html
index 90e3d3069..d79548525 100644
--- a/docs/supervisor-module/1-ic.html
+++ b/docs/supervisor-module/1-ic.html
@@ -430,7 +430,7 @@ line, which is why we couldn't work this out earlier:
 
 
 target_vm *current_target_VM = NULL;
-target_vm *Supervisor::current_vm(void) {
+target_vm *Supervisor::current_vm(void) {
     RUN_ONLY_FROM_PHASE(TINKERING_INBUILD_PHASE)
     return current_target_VM;
 }
diff --git a/docs/supervisor-module/2-ce.html b/docs/supervisor-module/2-ce.html
index 1dbf5f7da..095ed26f6 100644
--- a/docs/supervisor-module/2-ce.html
+++ b/docs/supervisor-module/2-ce.html
@@ -104,7 +104,7 @@ fields are blank.
 

-copy_error *CopyErrors::new(int cat, int subcat) {
+copy_error *CopyErrors::new(int cat, int subcat) {
     copy_error *CE = CREATE(copy_error);
     CE->copy = NULL;
     CE->error_category = cat;
@@ -121,7 +121,7 @@ fields are blank.
     return CE;
 }
 
-copy_error *CopyErrors::new_T(int cat, int subcat, text_stream *NB) {
+copy_error *CopyErrors::new_T(int cat, int subcat, text_stream *NB) {
     copy_error *CE = CopyErrors::new(cat, subcat);
     CE->details = Str::duplicate(NB);
     return CE;
@@ -151,7 +151,7 @@ we also offer these functions to tack extra details on:
 

-void CopyErrors::supply_wording(copy_error *CE, wording details_W) {
+void CopyErrors::supply_wording(copy_error *CE, wording details_W) {
     CE->details_W = details_W;
 }
 
@@ -164,7 +164,7 @@ we also offer these functions to tack extra details on:
     CE->details_work2 = w2;
 }
 
-void CopyErrors::supply_node(copy_error *CE, parse_node *n) {
+void CopyErrors::supply_node(copy_error *CE, parse_node *n) {
     CE->details_node = n;
 }
 
@@ -300,6 +300,16 @@ output.
             WRITE("empty clause in brackets in dialogue"); break;
         case MisbracketedDialogueClause_SYNERROR:
             WRITE("brackets '(' and ')' used in an unmatched way in dialogue"); break;
+        case MissingSourceFile_SYNERROR:
+            WRITE("cannot find source file: '%S'", CE->details); break;
+        case HeadingWithFileNonempty_SYNERROR:
+            WRITE("heading should refer only to source file '%S' but also contains material",
+                CE->details); break;
+        case MisheadedSourceFile_SYNERROR:
+            WRITE("source file '%S' does not begin with a heading matching 'see ...' line",
+                CE->details); break;
+        case HeadingTooGreat_SYNERROR:
+            WRITE("source file contains a heading of too high a level"); break;
         default:
             WRITE("syntax error"); break;
     }
diff --git a/docs/supervisor-module/2-cps.html b/docs/supervisor-module/2-cps.html
index 74ab70092..31c4f1c37 100644
--- a/docs/supervisor-module/2-cps.html
+++ b/docs/supervisor-module/2-cps.html
@@ -147,13 +147,13 @@ for later reporting. These are stored in a list.
 

-void Copies::attach_error(inbuild_copy *C, copy_error *CE) {
+void Copies::attach_error(inbuild_copy *C, copy_error *CE) {
     if (C == NULL) internal_error("no copy to attach to");
     CopyErrors::supply_attached_copy(CE, C);
     ADD_TO_LINKED_LIST(CE, copy_error, C->errors_reading_source_text);
 }
 
-void Copies::list_attached_errors(OUTPUT_STREAM, inbuild_copy *C) {
+void Copies::list_attached_errors(OUTPUT_STREAM, inbuild_copy *C) {
     if (C == NULL) return;
     copy_error *CE;
     int c = 1;
@@ -172,12 +172,12 @@ for later reporting. These are stored in a list.
 

§7. Reading source text.

-int Copies::source_text_has_been_read(inbuild_copy *C) {
+int Copies::source_text_has_been_read(inbuild_copy *C) {
     if (C == NULL) internal_error("no copy");
     return C->source_text_read;
 }
 
-wording Copies::get_source_text(inbuild_copy *C) {
+wording Copies::get_source_text(inbuild_copy *C) {
     if (C->source_text_read == FALSE) {
         C->source_text_read = TRUE;
         if (LinkedLists::len(C->errors_reading_source_text) > 0) {
diff --git a/docs/supervisor-module/2-nst.html b/docs/supervisor-module/2-nst.html
index 7d5cac514..d8eb14da0 100644
--- a/docs/supervisor-module/2-nst.html
+++ b/docs/supervisor-module/2-nst.html
@@ -97,7 +97,7 @@ see below for why. Lower-tag-numbered origins are better than later ones.
 enum INTERNAL_NEST_TAG
 
-int Nests::get_tag(inbuild_nest *N) {
+int Nests::get_tag(inbuild_nest *N) {
     if (N == NULL) return -1;
     return N->tag_value;
 }
@@ -176,7 +176,7 @@ genre's manager to look for copies of that genre:
 

-void Nests::search_for(inbuild_requirement *req,
+void Nests::search_for(inbuild_requirement *req,
     linked_list *search_list, linked_list *results) {
     inbuild_nest *N;
     LOOP_OVER_LINKED_LIST(N, inbuild_nest, search_list) {
@@ -191,7 +191,7 @@ others:
 

-inbuild_search_result *Nests::search_for_best(inbuild_requirement *req,
+inbuild_search_result *Nests::search_for_best(inbuild_requirement *req,
     linked_list *search_list) {
     linked_list *L = NEW_LINKED_LIST(inbuild_search_result);
     Nests::search_for(req, search_list, L);
diff --git a/docs/supervisor-module/2-rqr.html b/docs/supervisor-module/2-rqr.html
index 4cec65150..214c2329b 100644
--- a/docs/supervisor-module/2-rqr.html
+++ b/docs/supervisor-module/2-rqr.html
@@ -79,14 +79,14 @@ we can give a semantic version number range:
 

-inbuild_requirement *Requirements::new(inbuild_work *work, semver_range *R) {
+inbuild_requirement *Requirements::new(inbuild_work *work, semver_range *R) {
     inbuild_requirement *req = CREATE(inbuild_requirement);
     req->work = work;
     req->version_range = R;
     return req;
 }
 
-inbuild_requirement *Requirements::any_version_of(inbuild_work *work) {
+inbuild_requirement *Requirements::any_version_of(inbuild_work *work) {
     return Requirements::new(work, VersionNumberRanges::any_range());
 }
 
@@ -243,7 +243,7 @@ a requirement, then so will all other copies of it.
 

-int Requirements::meets(inbuild_edition *edition, inbuild_requirement *req) {
+int Requirements::meets(inbuild_edition *edition, inbuild_requirement *req) {
     if (req == NULL) return TRUE;
     if (req->work) {
         if (req->work->genre)
diff --git a/docs/supervisor-module/2-wrk.html b/docs/supervisor-module/2-wrk.html
index 209918ec4..d397c1975 100644
--- a/docs/supervisor-module/2-wrk.html
+++ b/docs/supervisor-module/2-wrk.html
@@ -92,7 +92,7 @@ not subsequently altered.
 

-inbuild_work *Works::new(inbuild_genre *genre, text_stream *ti, text_stream *an) {
+inbuild_work *Works::new(inbuild_genre *genre, text_stream *ti, text_stream *an) {
     return Works::new_inner(genre, ti, an, TRUE);
 }
 inbuild_work *Works::new_raw(inbuild_genre *genre, text_stream *ti, text_stream *an) {
diff --git a/docs/supervisor-module/3-bg.html b/docs/supervisor-module/3-bg.html
index c872cd3d9..94ed6b2a4 100644
--- a/docs/supervisor-module/3-bg.html
+++ b/docs/supervisor-module/3-bg.html
@@ -140,7 +140,7 @@ compiled, is a file vertex.
     return V;
 }
 
-build_vertex *Graphs::req_vertex(inbuild_requirement *R) {
+build_vertex *Graphs::req_vertex(inbuild_requirement *R) {
     if (R == NULL) internal_error("no requirement");
     build_vertex *V = Graphs::file_vertex(NULL);
     V->type = REQUIREMENT_VERTEX;
@@ -167,7 +167,7 @@ at most one edge of each colour.
 

-void Graphs::need_this_to_build(build_vertex *from, build_vertex *to) {
+void Graphs::need_this_to_build(build_vertex *from, build_vertex *to) {
     if (from == NULL) internal_error("no from");
     if (to == NULL) internal_error("no to");
     if (from == to) internal_error("graph node depends on itself");
@@ -177,7 +177,7 @@ at most one edge of each colour.
     ADD_TO_LINKED_LIST(to, build_vertex, from->build_edges);
 }
 
-void Graphs::need_this_to_use(build_vertex *from, build_vertex *to) {
+void Graphs::need_this_to_use(build_vertex *from, build_vertex *to) {
     if (from == NULL) internal_error("no from");
     if (to == NULL) internal_error("no to");
     if (from == to) internal_error("graph node depends on itself");
diff --git a/docs/supervisor-module/4-em.html b/docs/supervisor-module/4-em.html
index c6e88e81c..da82043b8 100644
--- a/docs/supervisor-module/4-em.html
+++ b/docs/supervisor-module/4-em.html
@@ -106,7 +106,7 @@ which stores data about extensions used by the Inform compiler.
 

-inform_extension *ExtensionManager::from_copy(inbuild_copy *C) {
+inform_extension *ExtensionManager::from_copy(inbuild_copy *C) {
     if ((C) && (C->edition->work->genre == extension_genre)) {
         return RETRIEVE_POINTER_inform_extension(C->metadata);
     }
diff --git a/docs/supervisor-module/4-pbm.html b/docs/supervisor-module/4-pbm.html
index baada8a0f..93cf3308a 100644
--- a/docs/supervisor-module/4-pbm.html
+++ b/docs/supervisor-module/4-pbm.html
@@ -96,7 +96,7 @@ which stores data about extensions used by the Inform compiler.
 

-inform_project *ProjectBundleManager::from_copy(inbuild_copy *C) {
+inform_project *ProjectBundleManager::from_copy(inbuild_copy *C) {
     if ((C) && (C->edition->work->genre == project_bundle_genre)) {
         return RETRIEVE_POINTER_inform_project(C->metadata);
     }
diff --git a/docs/supervisor-module/5-es.html b/docs/supervisor-module/5-es.html
index 520cb75dc..265a12517 100644
--- a/docs/supervisor-module/5-es.html
+++ b/docs/supervisor-module/5-es.html
@@ -421,7 +421,7 @@ project, then...
 

-void Extensions::set_associated_project(inform_extension *E, inform_project *P) {
+void Extensions::set_associated_project(inform_extension *E, inform_project *P) {
     E->read_into_project = P;
 }
 
@@ -646,10 +646,10 @@ problem messages and the index.

-void Extensions::set_inclusion_sentence(inform_extension *E, parse_node *N) {
+void Extensions::set_inclusion_sentence(inform_extension *E, parse_node *N) {
     E->inclusion_sentence = N;
 }
-parse_node *Extensions::get_inclusion_sentence(inform_extension *E) {
+parse_node *Extensions::get_inclusion_sentence(inform_extension *E) {
     if (E == NULL) return NULL;
     return E->inclusion_sentence;
 }
@@ -670,7 +670,7 @@ becomes empty and stays that way.
 

-void Extensions::must_satisfy(inform_extension *E, inbuild_requirement *req) {
+void Extensions::must_satisfy(inform_extension *E, inbuild_requirement *req) {
     if (E->must_satisfy == NULL) E->must_satisfy = req;
     else VersionNumberRanges::intersect_range(E->must_satisfy->version_range, req->version_range);
 }
diff --git a/docs/supervisor-module/5-ps2.html b/docs/supervisor-module/5-ps2.html
index e9f956250..298db1128 100644
--- a/docs/supervisor-module/5-ps2.html
+++ b/docs/supervisor-module/5-ps2.html
@@ -292,7 +292,7 @@ is small, but one likes to minimise the effect of the CWD.)
     return proj->materials_nest;
 }
 
-pathname *Projects::materials_path(inform_project *proj) {
+pathname *Projects::materials_path(inform_project *proj) {
     if (proj == NULL) return NULL;
     return proj->materials_nest->location;
 }
@@ -303,7 +303,7 @@ reasons, this list is created on demand.
 

-linked_list *Projects::nest_list(inform_project *proj) {
+linked_list *Projects::nest_list(inform_project *proj) {
     if (proj == NULL) return Supervisor::shared_nest_list();
     RUN_ONLY_FROM_PHASE(NESTED_INBUILD_PHASE)
     if (LinkedLists::len(proj->search_list) == 0) {
@@ -1149,7 +1149,7 @@ like Basic Inform or Standard Rules; and also any sentences in the
     Node::set_text(inclusions_heading,
         Feeds::feed_C_string_expanding_strings(L"Implied inclusions"));
     SyntaxTree::graft_sentence(proj->syntax_tree, inclusions_heading);
-    Headings::place_implied_level_0(proj->syntax_tree, inclusions_heading);
+    Headings::place_implied_level_0(proj->syntax_tree, inclusions_heading, proj->as_copy);
 
     int wc = lexer_wordcount;
     TEMPORARY_TEXT(early)
@@ -1215,7 +1215,7 @@ ready for those inventions (if in fact there are any).
     Node::set_text(implicit_heading,
         Feeds::feed_C_string_expanding_strings(L"Invented sentences"));
     SyntaxTree::graft_sentence(proj->syntax_tree, implicit_heading);
-    Headings::place_implied_level_0(proj->syntax_tree, implicit_heading);
+    Headings::place_implied_level_0(proj->syntax_tree, implicit_heading, proj->as_copy);
     SyntaxTree::pop_bud(proj->syntax_tree, l);
     SyntaxTree::push_bud(proj->syntax_tree, implicit_heading);  never popped
 
diff --git a/docs/supervisor-module/6-hdn.html b/docs/supervisor-module/6-hdn.html index 96c696d6a..3d4e36f20 100644 --- a/docs/supervisor-module/6-hdn.html +++ b/docs/supervisor-module/6-hdn.html @@ -242,18 +242,22 @@ on each heading in sequence: struct inbuild_work *for_use_with; e.g. "for use with ... by ..." struct wording in_place_of_text; e.g. "in place of ... in ... by ..." struct wording heading_text; once provisos have been stripped away + struct wording external_file; e.g. "see ..." + struct source_file *external_file_read; + int external_file_loaded; struct noun *list_of_contents; tagged names defined under this struct noun *last_in_list_of_contents; struct heading *parent_heading; struct heading *child_heading; struct heading *next_heading; + int omit_from_tree; #ifdef CORE_MODULE struct heading_compilation_data compilation_data; #endif CLASS_DEFINITION } heading;
-
  • The structure heading is accessed in 1/sm, 3/bs2 and here.
+
  • The structure heading is accessed in 1/sm, 3/bs2, 6/inc and here.

§7. It is guaranteed that this will be called once for each heading (except the pseudo-heading, which doesn't count) in sequence order:

@@ -263,11 +267,15 @@ pseudo-heading, which doesn't count) in sequence order: heading *h = CREATE(heading); h->owning_tree = T->headings; h->parent_heading = NULL; h->child_heading = NULL; h->next_heading = NULL; + h->omit_from_tree = FALSE; h->list_of_contents = NULL; h->last_in_list_of_contents = NULL; h->for_release = NOT_APPLICABLE; h->omit_material = FALSE; h->index_definitions_made_under_this = TRUE; h->use_with_or_without = NOT_APPLICABLE; h->in_place_of_text = EMPTY_WORDING; + h->external_file = EMPTY_WORDING; + h->external_file_read = NULL; + h->external_file_loaded = FALSE; h->for_use_with = NULL; h->sentence_declaring = pn; h->holds_dialogue = FALSE; @@ -294,7 +302,7 @@ included if the target virtual machine on this run of Inform is the Z-machine.)
 int Headings::place(parse_node_tree *T, parse_node *pn, inform_project *proj) {
-    heading *h = Headings::attach(T, pn);
+    heading *h = Headings::attach(T, pn, proj->as_copy);
     int are_we_releasing = Projects::currently_releasing(proj);
     if ((h->for_release == TRUE) && (are_we_releasing == FALSE)) return FALSE;
     if ((h->for_release == FALSE) && (are_we_releasing == TRUE)) return FALSE;
@@ -308,8 +316,8 @@ different way in. (These are never skipped.)
 

-void Headings::place_implied_level_0(parse_node_tree *T, parse_node *pn) {
-    Headings::attach(T, pn);
+void Headings::place_implied_level_0(parse_node_tree *T, parse_node *pn, inbuild_copy *for_copy) {
+    Headings::attach(T, pn, for_copy);
     Annotations::write_int(pn, heading_level_ANNOT, 0);
     Annotations::write_int(pn, implied_heading_ANNOT, TRUE);
 }
@@ -330,7 +338,7 @@ with the result of parsing any caveats in its wording.
 
 inbuild_work *work_identified = NULL;  temporary variable during parsing below
 
-heading *Headings::attach(parse_node_tree *T, parse_node *pn) {
+heading *Headings::attach(parse_node_tree *T, parse_node *pn, inbuild_copy *for_copy) {
     if ((pn == NULL) || (Wordings::empty(Node::get_text(pn))))
         internal_error("heading at textless node");
     if (Node::get_type(pn) != HEADING_NT)
@@ -347,7 +355,7 @@ with the result of parsing any caveats in its wording.
         Node::get_text(pn), h->level, h->indentation);
 
     if (T->headings->assembled_at_least_once)
-        Headings::assemble_tree(T);  to include new heading: unlikely but possible
+        Headings::assemble_tree(T, for_copy);  to include new heading: unlikely but possible
     return h;
 }
 
@@ -363,6 +371,7 @@ with the result of parsing any caveats in its wording. define USE_WITHOUT_HQ 6 define IN_PLACE_OF_HQ 7 define DIALOGUE_HQ 8 +define EXTERNAL_HQ 9

Parse heading text for release or other stipulations11.1 =

@@ -391,6 +400,10 @@ with the result of parsing any caveats in its wording. h->use_with_or_without = TRUE; h->in_place_of_text = GET_RW(<extension-qualifier>, 1); break; + case EXTERNAL_HQ: + h->external_file = GET_RW(<bracketed-heading-qualifier>, 1); + Word::dequote(Wordings::first_wn(h->external_file)); + break; } W = GET_RW(<heading-qualifier>, 1); } @@ -433,6 +446,7 @@ is determined. unindexed | ==> { UNINDEXED_HQ, - } dialogue | ==> { DIALOGUE_HQ, - } dialog | ==> { DIALOGUE_HQ, - } + see { <quoted-text> } | ==> { EXTERNAL_HQ, - } <platform-qualifier> | ==> { pass 1 } <extension-qualifier> ==> { pass 1 } @@ -515,15 +529,24 @@ to the heading tree are not in fact formed up into a tree structure.

-void Headings::assemble_tree(parse_node_tree *T) {
+void Headings::assemble_tree(parse_node_tree *T, inbuild_copy *for_copy) {
     heading *h;
     Disassemble the whole heading tree to a pile of twigs14.1;
     LOOP_OVER_LINKED_LIST(h, heading, T->headings->subordinates) {
-        If h is outside the tree, make it a child of the pseudo-heading14.2;
-        Run through subsequent equal or subordinate headings to move them downward14.3;
+        if (h->omit_from_tree == FALSE) {
+            If h is outside the tree, make it a child of the pseudo-heading14.2;
+            If h is held in an external file, move those headings to it14.3;
+            Run through subsequent equal or subordinate headings to move them downward14.4;
+        }
     }
     T->headings->assembled_at_least_once = TRUE;
     Headings::verify_heading_tree(T);
+    #ifndef CORE_MODULE
+    Copies::list_attached_errors(STDERR, for_copy);
+    #endif
+    #ifdef CORE_MODULE
+    SourceProblems::issue_problems_arising(for_copy);
+    #endif
 }
 

§14.1. It's possible to call Headings::assemble_tree more than once, to allow @@ -545,6 +568,7 @@ of subordinates. Everything else is. heading *h; LOOP_OVER_LINKED_LIST(h, heading, T->headings->subordinates) { h->parent_heading = NULL; h->child_heading = NULL; h->next_heading = NULL; + h->omit_from_tree = FALSE; }

  • This code is used in §14.
@@ -559,11 +583,53 @@ at the top of the tree.

-    if (h->parent_heading == NULL)
+    if ((h->parent_heading == NULL) && (h->omit_from_tree == FALSE))
         Headings::move_below(h, &(T->headings->heading_root));
 
  • This code is used in §14.
-

§14.3. Note that the following could be summed up as "move subsequent headings as +

§14.3. A complication is that the source text is read out of conceptual sequence +when headings referring to external files are run into. Because of that, if +we have a heading: +

+ +
+    Chapter 7 - Into the Woods (see "woods.i7")
+
+

and if we then have further headings inside the file woods.i7, those +further headings h2 won't be adjacent to the original heading h in +the list. So we fix this up here. +

+ +

There is a nameless level zero heading marking the change of source file: +we remove that. We also have to throw problems if we find, say, a Book +heading inside a file which is supposed to contain a Chapter. +

+ +
enum HeadingTooGreat_SYNERROR
+
+
+
+If h is held in an external file, move those headings to it14.3 =
+    if (h->external_file_read) {
+        heading *h2;
+        LOOP_OVER_LINKED_LIST(h2, heading, T->headings->subordinates)
+            if (h2->start_location.file_of_origin == h->external_file_read) {
+                if (h2->level == 0) {
+                    h2->omit_from_tree = TRUE;
+                } else if (h2->level <= h->level) {
+                    copy_error *CE = CopyErrors::new(SYNTAX_CE, HeadingTooGreat_SYNERROR);
+                    CopyErrors::supply_node(CE, h2->sentence_declaring);
+                    Copies::attach_error(for_copy, CE);
+                    h2->omit_from_tree = TRUE;
+                } else {
+                    Headings::move_below(h2, h);
+                    h2->indentation++;
+                }
+            }
+    }
+
+
  • This code is used in §14.
+

§14.4. Note that the following could be summed up as "move subsequent headings as deep in the tree as we can see they need to be from h's perspective alone". This isn't always the final position. For instance, given the sequence Volume 1, Chapter I, Section A, Chapter II, the tree is adjusted twice: @@ -583,7 +649,7 @@ demote every one of its successors — but since the depth is at most 9, it runs in linear time.)

-

Run through subsequent equal or subordinate headings to move them downward14.3 = +

Run through subsequent equal or subordinate headings to move them downward14.4 =

@@ -603,7 +669,8 @@ to the tree as a child of a given parent:
 

-void Headings::move_below(heading *ch, heading *pa) {
+void Headings::move_below(heading *ch, heading *pa) {
+LOG("Move %W below %W\n", ch->heading_text, pa->heading_text);
     heading *former_pa = ch->parent_heading;
     if (former_pa == pa) return;
     Detach ch from the heading tree if it is already there15.1;
diff --git a/docs/supervisor-module/6-inc.html b/docs/supervisor-module/6-inc.html
index 19ff40049..d6ec1e0e9 100644
--- a/docs/supervisor-module/6-inc.html
+++ b/docs/supervisor-module/6-inc.html
@@ -60,7 +60,7 @@ function togglePopup(material_id) {
     
 

To fulfill requests to include extensions, adding their material to the parse tree as needed, and removing INCLUDE nodes.

-
+

§1. Our main task here is to look through the syntax tree for INCLUDE_NT nodes, which are requests to include an extension, and replace them with @@ -87,12 +87,12 @@ read twice. do { includes_cleared = TRUE; if (LinkedLists::len(C->errors_reading_source_text) > no_copy_errors) break; - SyntaxTree::traverse_headingwise(T, Inclusions::visit, &includes_cleared); + SyntaxTree::traverse_headingwise(T, Inclusions::visit, &includes_cleared); } while (includes_cleared == FALSE); inclusions_errors_to = NULL; } -build_vertex *Inclusions::spawned_from_vertex(parse_node *H0) { +build_vertex *Inclusions::spawned_from_vertex(parse_node *H0) { if (H0) { inform_extension *ext = Node::get_inclusion_of_extension(H0); if (ext) return ext->as_copy->vertex; @@ -100,19 +100,34 @@ read twice. if (inclusions_errors_to == NULL) internal_error("no H0 ext or inclusion"); return inclusions_errors_to->vertex; } +

+

§2.

-void Inclusions::visit(parse_node_tree *T, parse_node *pn, parse_node *last_H0, +
enum MissingSourceFile_SYNERROR
+enum HeadingWithFileNonempty_SYNERROR
+enum MisheadedSourceFile_SYNERROR
+
+
+void Inclusions::visit(parse_node_tree *T, parse_node *pn, parse_node *last_H0,
     int *includes_cleared) {
     if (Node::get_type(pn) == INCLUDE_NT) {
-        Replace INCLUDE node with sentence nodes for any extensions required1.1;
+        Replace INCLUDE node with sentence nodes for any extensions required2.1;
         *includes_cleared = FALSE;
     }
+    if (Node::get_type(pn) == HEADING_NT) {
+        heading *h = Node::get_embodying_heading(pn);
+        if ((h) && (Wordings::nonempty(h->external_file)) && (h->external_file_loaded == FALSE)) {
+            inform_project *proj = ProjectBundleManager::from_copy(inclusions_errors_to);
+            if ((proj) && (proj->as_copy->location_if_path))
+                Include the contents of the external file under the heading2.2;
+        }
+    }
 }
 
-

§1.1. The INCLUDE node becomes an INCLUSION, which in turn contains the extension's code. +

§2.1. The INCLUDE node becomes an INCLUSION, which in turn contains the extension's code.

-

Replace INCLUDE node with sentence nodes for any extensions required1.1 = +

Replace INCLUDE node with sentence nodes for any extensions required2.1 =

@@ -122,7 +137,7 @@ read twice.
     wording author = GET_RW(<structural-sentence>, 2);
     Node::set_type(pn, INCLUSION_NT); pn->down = NULL;
     int l = SyntaxTree::push_bud(T, pn);
-    inform_extension *E = Inclusions::fulfill_request_to_include_extension(last_H0,
+    inform_extension *E = Inclusions::fulfill_request_to_include_extension(last_H0,
         title, author, inclusions_for_project);
     SyntaxTree::pop_bud(T, l);
     if (E) {
@@ -140,8 +155,70 @@ read twice.
         }
     }
 
-
  • This code is used in §1.
-

§2. Here we parse the content of an Include sentence: i.e., what comes after the +

  • This code is used in §2.
+

§2.2. Here we're at a HEADING node which is annotated (see "source-file"), and +we need to read that file in: it will be considered as the contents of the +HEADING. +

+ +

Include the contents of the external file under the heading2.2 = +

+ +
+    pathname *P = Pathnames::down(Projects::materials_path(proj), I"Source");
+    text_stream *leaf = Str::new();
+    WRITE_TO(leaf, "%W", h->external_file);
+    h->external_file_loaded = TRUE;
+    if (pn->down) {
+        copy_error *CE = CopyErrors::new_T(SYNTAX_CE, HeadingWithFileNonempty_SYNERROR, leaf);
+        CopyErrors::supply_node(CE, pn);
+        Copies::attach_error(inclusions_errors_to, CE);
+    } else {
+        filename *F = Filenames::in(P, leaf);
+        if (TextFiles::exists(F) == FALSE) {
+            copy_error *CE = CopyErrors::new_T(SYNTAX_CE, MissingSourceFile_SYNERROR, leaf);
+            CopyErrors::supply_node(CE, pn);
+            Copies::attach_error(inclusions_errors_to, CE);
+        } else {
+            int l = SyntaxTree::push_bud(T, pn);
+            source_file *S = SourceText::read_file(inclusions_errors_to, F, leaf, FALSE, FALSE);
+            if (S) Sentence-break the contents of S under the heading node2.2.1;
+            SyntaxTree::pop_bud(T, l);
+            *includes_cleared = FALSE;
+        }
+    }
+
+
  • This code is used in §2.
+

§2.2.1. Note that the opening words of the external file, here TOPW, must match +exactly the heading which called for the file, except for the annotation in +brackets. (It's to prevent that that we look for a round bracket at the +start of RESTW as a sign that the user has accidentally included this.) +

+ +

Provided a match is made, the TOPW words are then thrown away. Their purpose +was just to identify the file sensibly. +

+ +

Sentence-break the contents of S under the heading node2.2.1 = +

+ +
+    h->external_file_read = S;
+    wording SW = S->text_read;
+    int N = Wordings::length(h->heading_text);
+    wording TOPW = Wordings::up_to(SW, Wordings::first_wn(SW) + N - 1);
+    wording RESTW = Wordings::from(SW, Wordings::first_wn(SW) + N);
+    if ((Wordings::match(h->heading_text, TOPW) == FALSE) ||
+        (compare_word(Wordings::first_wn(RESTW), OPENBRACKET_V))) {
+        copy_error *CE = CopyErrors::new_T(SYNTAX_CE, MisheadedSourceFile_SYNERROR, leaf);
+        CopyErrors::supply_node(CE, pn);
+        Copies::attach_error(inclusions_errors_to, CE);
+    } else if (Wordings::nonempty(RESTW)) {
+        Sentences::break_into_project_copy(T, RESTW, inclusions_errors_to, proj);
+    }
+
+
  • This code is used in §2.2.
+

§3. Here we parse the content of an Include sentence: i.e., what comes after the word "Include", which might e.g. be "Locksmith by Emily Short".

@@ -157,14 +234,14 @@ word "Include", which might e.g. be "Locksmith by Emily Short". <extension-unversioned-inner> ==> { 0, - } <extension-unversioned-inner> ::= - <quoted-text> *** | ==> Issue PM_IncludeExtQuoted problem2.1 + <quoted-text> *** | ==> Issue PM_IncludeExtQuoted problem3.1 ... ==> { 0, -, <<t1>> = Wordings::first_wn(W), <<t2>> = Wordings::last_wn(W) }
-

§2.1. Quite a popular mistake, this: +

§3.1. Quite a popular mistake, this:

-

Issue PM_IncludeExtQuoted problem2.1 = +

Issue PM_IncludeExtQuoted problem3.1 =

@@ -173,8 +250,8 @@ word "Include", which might e.g. be "Locksmith by Emily Short".
     CopyErrors::supply_node(CE, current_sentence);
     Copies::attach_error(inclusions_errors_to, CE);
 
-
  • This code is used in §2.
-

§3. This nonterminal parses text which will probably be something like "3.14" or +

  • This code is used in §3.
+

§4. This nonterminal parses text which will probably be something like "3.14" or "12/110410", but at this stage it accepts anything: actual parsing comes later.

@@ -185,10 +262,10 @@ word "Include", which might e.g. be "Locksmith by Emily Short". }
-

§4.

+

§5.

-inform_extension *Inclusions::fulfill_request_to_include_extension(parse_node *last_H0,
+inform_extension *Inclusions::fulfill_request_to_include_extension(parse_node *last_H0,
     wording TW, wording AW, inform_project *for_project) {
     inform_extension *E = NULL;
     <<t1>> = -1; <<t2>> = -1;
@@ -196,11 +273,11 @@ word "Include", which might e.g. be "Locksmith by Emily Short".
     wording W = Wordings::new(<<t1>>, <<t2>>);
     int version_word = <<r>>;
 
-    if (Wordings::nonempty(W)) Fulfill request to include a single extension4.1;
+    if (Wordings::nonempty(W)) Fulfill request to include a single extension5.1;
     return E;
 }
 
-

§4.1. A request consists of author, name and version, the latter being optional. +

§5.1. A request consists of author, name and version, the latter being optional. We obtain the extension file structure corresponding to this: it may have no text at all (for instance if Inform could not open the file), or it may be one we have seen before, thanks to an earlier inclusion. Only when it @@ -209,7 +286,7 @@ and then we call the sentence-breaker to graft the new material on to the parse tree.

-

Fulfill request to include a single extension4.1 = +

Fulfill request to include a single extension5.1 =

@@ -219,16 +296,16 @@ parse tree.
     WRITE_TO(exfa, "%+W", AW);
     inbuild_work *work = Works::new(extension_genre, exft, exfa);
     semantic_version_number V = VersionNumbers::null();
-    if (version_word >= 0) V = Inclusions::parse_version(version_word);
+    if (version_word >= 0) V = Inclusions::parse_version(version_word);
     semver_range *R = VersionNumberRanges::compatibility_range(V);
     inbuild_requirement *req = Requirements::new(work, R);
     DISCARD_TEXT(exft)
     DISCARD_TEXT(exfa)
 
-    E = Inclusions::load(last_H0, current_sentence, req, for_project);
+    E = Inclusions::load(last_H0, current_sentence, req, for_project);
 
-
  • This code is used in §4.
-

§5. Extension loading. Note that we ignore a request for an extension which has already been +

  • This code is used in §5.
+

§6. Extension loading. Note that we ignore a request for an extension which has already been loaded, except if the new request ups the ante in terms of the minimum version permitted: in which case we need to record that the requirement has been tightened. That is, if we previously wanted version 2 of Pantomime @@ -243,7 +320,7 @@ Sausages by Mr Punch, and loaded it, but then read the sentence

-inform_extension *Inclusions::load(parse_node *last_H0, parse_node *at,
+inform_extension *Inclusions::load(parse_node *last_H0, parse_node *at,
     inbuild_requirement *req, inform_project *for_project) {
     inform_extension *E = NULL;
     LOOP_OVER(E, inform_extension)
@@ -252,13 +329,13 @@ Sausages by Mr Punch, and loaded it, but then read the sentence
             Extensions::must_satisfy(E, req);
             return E;
         }
-    Read the extension file into the lexer, and break it into body and documentation5.1;
+    Read the extension file into the lexer, and break it into body and documentation6.1;
     if ((for_project) && (E))
         ADD_TO_LINKED_LIST(E, inform_extension, for_project->extensions_included);
     return E;
 }
 
-

§5.1. Read the extension file into the lexer, and break it into body and documentation5.1 = +

§6.1. Read the extension file into the lexer, and break it into body and documentation6.1 =

@@ -272,7 +349,7 @@ Sausages by Mr Punch, and loaded it, but then read the sentence
             E->loaded_from_built_in_area = TRUE;
         compatibility_specification *C = E->as_copy->edition->compatibility;
         if (Compatibility::test(C, Supervisor::current_vm()) == FALSE)
-            Issue a problem message saying that the VM does not meet requirements5.1.1;
+            Issue a problem message saying that the VM does not meet requirements6.1.1;
 
         if (LinkedLists::len(search_result->copy->errors_reading_source_text) == 0) {
             Copies::get_source_text(search_result->copy);
@@ -282,21 +359,21 @@ Sausages by Mr Punch, and loaded it, but then read the sentence
         #endif
     } else {
         #ifdef CORE_MODULE
-        Issue a cannot-find problem5.1.2;
+        Issue a cannot-find problem6.1.2;
         #endif
         build_vertex *RV = Graphs::req_vertex(req);
         build_vertex *V = Inclusions::spawned_from_vertex(last_H0);
         Graphs::need_this_to_build(V, RV);
     }
 
-
  • This code is used in §5.
-

§5.1.1. Here the problem is not that the extension is broken in some way: it's +

  • This code is used in §6.
+

§6.1.1. Here the problem is not that the extension is broken in some way: it's just not what we can currently use. Therefore the correction should be a matter of removing the inclusion, not of altering the extension, so we report this problem at the inclusion line.

-

Issue a problem message saying that the VM does not meet requirements5.1.1 = +

Issue a problem message saying that the VM does not meet requirements6.1.1 =

@@ -304,8 +381,8 @@ report this problem at the inclusion line.
     CopyErrors::supply_node(CE, Extensions::get_inclusion_sentence(E));
     Copies::attach_error(inclusions_errors_to, CE);
 
-
  • This code is used in §5.1.
-

§5.1.2. Issue a cannot-find problem5.1.2 = +

  • This code is used in §6.1.
+

§6.1.2. Issue a cannot-find problem6.1.2 =

@@ -331,30 +408,30 @@ report this problem at the inclusion line.
         DISCARD_TEXT(versions)
     }
 
-
  • This code is used in §5.1.
-

§6.

+
  • This code is used in §6.1.
+

§7.

 int last_PM_ExtVersionMalformed_at = -1;
-semantic_version_number Inclusions::parse_version(int vwn) {
+semantic_version_number Inclusions::parse_version(int vwn) {
     semantic_version_number V = VersionNumbers::null();
     wording W = Wordings::one_word(vwn);
     if (<version-number>(W)) {
         semantic_version_number_holder *H = (semantic_version_number_holder *) <<rp>>;
         V = H->version;
     } else {
-        Issue a problem message for a malformed version number6.1;
+        Issue a problem message for a malformed version number7.1;
     }
     return V;
 }
 
-

§6.1. Because we tend to call Inclusions::parse_version repeatedly on +

§7.1. Because we tend to call Inclusions::parse_version repeatedly on the same word, we want to recover tidily from this problem, and not report it over and over. We do this by altering the text to 1, the lowest well-formed version number text.

-

Issue a problem message for a malformed version number6.1 = +

Issue a problem message for a malformed version number7.1 =

@@ -366,8 +443,8 @@ version number text.
         Copies::attach_error(inclusions_errors_to, CE);
     }
 
-
  • This code is used in §6.
-

§7. Checking the begins here and ends here sentences. When a newly loaded extension is being sentence-broken, problem messages +

  • This code is used in §7.
+

§8. Checking the begins here and ends here sentences. When a newly loaded extension is being sentence-broken, problem messages will be turned up unless it contains the matching pair of "begins here" and "ends here" sentences. Assuming it does, the sentence breaker has no objection, but it also calls the two routines below to verify that these @@ -383,7 +460,7 @@ version number is given (if it is), and check that we are not trying to use an extension which is marked as not working on the current VM.

-

§8. This parses the subject noun-phrase in the sentence +

§9. This parses the subject noun-phrase in the sentence

@@ -393,10 +470,10 @@ use an extension which is marked as not working on the current VM.
 <begins-here-sentence-subject> ::=
     <extension-title-and-version> by ... |
-    ...                                     ==> Issue problem8.1
+    ...                                     ==> Issue problem9.1
 
-

§8.1. Issue problem8.1 = +

§9.1. Issue problem9.1 =

@@ -404,18 +481,18 @@ use an extension which is marked as not working on the current VM.
     CopyErrors::supply_node(CE, current_sentence);
     Copies::attach_error(inclusions_errors_to, CE);
 
-
  • This code is used in §8.
-

§9.

+
  • This code is used in §9.
+

§10.

-void Inclusions::check_begins_here(parse_node *PN, inform_extension *E) {
+void Inclusions::check_begins_here(parse_node *PN, inform_extension *E) {
     inbuild_copy *S = inclusions_errors_to;
     inclusions_errors_to = E->as_copy;
     <begins-here-sentence-subject>(Node::get_text(PN));
     inclusions_errors_to = S;
 }
 
-

§10. Similarly, we check the "ends here" sentence. Here there are no +

§11. Similarly, we check the "ends here" sentence. Here there are no side-effects: we merely verify that the name matches the one quoted in the "begins here".

@@ -424,7 +501,7 @@ the "begins here". <the-prefix-for-extensions> ::= the ... -void Inclusions::check_ends_here(parse_node *PN, inform_extension *E) { +void Inclusions::check_ends_here(parse_node *PN, inform_extension *E) { inbuild_copy *S = inclusions_errors_to; inclusions_errors_to = E->as_copy; wording W = Node::get_text(PN); diff --git a/docs/supervisor-module/6-st.html b/docs/supervisor-module/6-st.html index 31cf2384a..2212a4e07 100644 --- a/docs/supervisor-module/6-st.html +++ b/docs/supervisor-module/6-st.html @@ -98,7 +98,7 @@ perhaps combining our feed with that of others.

-source_file *SourceText::read_file(inbuild_copy *C, filename *F, text_stream *synopsis,
+source_file *SourceText::read_file(inbuild_copy *C, filename *F, text_stream *synopsis,
     int documentation_only, int primary) {
     currently_lexing_into = C;
     general_pointer ref = STORE_POINTER_inbuild_copy(NULL);
@@ -394,8 +394,8 @@ of an extension.
 
 void SourceText::new_beginend(parse_node *pn, inbuild_copy *C) {
     inform_extension *E = ExtensionManager::from_copy(C);
-    if (Node::get_type(pn) == BEGINHERE_NT) Inclusions::check_begins_here(pn, E);
-    if (Node::get_type(pn) == ENDHERE_NT) Inclusions::check_ends_here(pn, E);
+    if (Node::get_type(pn) == BEGINHERE_NT) Inclusions::check_begins_here(pn, E);
+    if (Node::get_type(pn) == ENDHERE_NT) Inclusions::check_ends_here(pn, E);
 }
 

§14. This callback is called by syntax when it first reaches a dialogue line diff --git a/docs/supervisor-module/P-wtmd.html b/docs/supervisor-module/P-wtmd.html index bad1d94bf..9058bc787 100644 --- a/docs/supervisor-module/P-wtmd.html +++ b/docs/supervisor-module/P-wtmd.html @@ -428,15 +428,15 @@ which of those states actually happens.

§13. At any rate, when Inclusions::traverse finds an Include sentence which -it decides is valid, it calls Inclusions::fulfill_request_to_include_extension. +it decides is valid, it calls Inclusions::fulfill_request_to_include_extension. This performs a search for the best compatible copy of the extension named — -see above — and, once such a copy is found, calls Inclusions::load to +see above — and, once such a copy is found, calls Inclusions::load to merge its text into the current syntax tree. (Note: it doesn't form an isolated syntax tree of its own.) This is why Inform reads the text of an extension as if it appeared at the same position as the Include sentence.

-

When a valid Include is found, Inclusions::fulfill_request_to_include_extension +

When a valid Include is found, Inclusions::fulfill_request_to_include_extension also puts a dependency edge in between the vertex for our copy and the vertex for the new extension's copy. That will be a use edge if our copy is also an extension — i.e., you can't use Existing Extension unless you also have diff --git a/inbuild/supervisor-module/Chapter 2/Copy Errors.w b/inbuild/supervisor-module/Chapter 2/Copy Errors.w index bf49fc67e..1762e17b9 100644 --- a/inbuild/supervisor-module/Chapter 2/Copy Errors.w +++ b/inbuild/supervisor-module/Chapter 2/Copy Errors.w @@ -237,6 +237,8 @@ void CopyErrors::write(OUTPUT_STREAM, copy_error *CE) { case MisheadedSourceFile_SYNERROR: WRITE("source file '%S' does not begin with a heading matching 'see ...' line", CE->details); break; + case HeadingTooGreat_SYNERROR: + WRITE("source file contains a heading of too high a level"); break; default: WRITE("syntax error"); break; } diff --git a/inbuild/supervisor-module/Chapter 5/Project Services.w b/inbuild/supervisor-module/Chapter 5/Project Services.w index 9b533ef5b..80f7d8922 100644 --- a/inbuild/supervisor-module/Chapter 5/Project Services.w +++ b/inbuild/supervisor-module/Chapter 5/Project Services.w @@ -974,7 +974,7 @@ like Basic Inform or Standard Rules; and also any sentences in the Node::set_text(inclusions_heading, Feeds::feed_C_string_expanding_strings(L"Implied inclusions")); SyntaxTree::graft_sentence(proj->syntax_tree, inclusions_heading); - Headings::place_implied_level_0(proj->syntax_tree, inclusions_heading); + Headings::place_implied_level_0(proj->syntax_tree, inclusions_heading, proj->as_copy); int wc = lexer_wordcount; TEMPORARY_TEXT(early) @@ -1029,7 +1029,7 @@ ready for those inventions (if in fact there are any). Node::set_text(implicit_heading, Feeds::feed_C_string_expanding_strings(L"Invented sentences")); SyntaxTree::graft_sentence(proj->syntax_tree, implicit_heading); - Headings::place_implied_level_0(proj->syntax_tree, implicit_heading); + Headings::place_implied_level_0(proj->syntax_tree, implicit_heading, proj->as_copy); SyntaxTree::pop_bud(proj->syntax_tree, l); SyntaxTree::push_bud(proj->syntax_tree, implicit_heading); /* never popped */ diff --git a/inbuild/supervisor-module/Chapter 6/Headings.w b/inbuild/supervisor-module/Chapter 6/Headings.w index d6b879af0..64a3bd319 100644 --- a/inbuild/supervisor-module/Chapter 6/Headings.w +++ b/inbuild/supervisor-module/Chapter 6/Headings.w @@ -158,6 +158,7 @@ typedef struct heading { struct heading *parent_heading; struct heading *child_heading; struct heading *next_heading; + int omit_from_tree; #ifdef CORE_MODULE struct heading_compilation_data compilation_data; #endif @@ -172,6 +173,7 @@ heading *Headings::new(parse_node_tree *T, parse_node *pn, int level, source_loc heading *h = CREATE(heading); h->owning_tree = T->headings; h->parent_heading = NULL; h->child_heading = NULL; h->next_heading = NULL; + h->omit_from_tree = FALSE; h->list_of_contents = NULL; h->last_in_list_of_contents = NULL; h->for_release = NOT_APPLICABLE; h->omit_material = FALSE; h->index_definitions_made_under_this = TRUE; @@ -206,7 +208,7 @@ included if the target virtual machine on this run of Inform is the Z-machine.) = int Headings::place(parse_node_tree *T, parse_node *pn, inform_project *proj) { - heading *h = Headings::attach(T, pn); + heading *h = Headings::attach(T, pn, proj->as_copy); int are_we_releasing = Projects::currently_releasing(proj); if ((h->for_release == TRUE) && (are_we_releasing == FALSE)) return FALSE; if ((h->for_release == FALSE) && (are_we_releasing == TRUE)) return FALSE; @@ -219,8 +221,8 @@ which do not originate in the sentence-breaker, and which therefore need a different way in. (These are never skipped.) = -void Headings::place_implied_level_0(parse_node_tree *T, parse_node *pn) { - Headings::attach(T, pn); +void Headings::place_implied_level_0(parse_node_tree *T, parse_node *pn, inbuild_copy *for_copy) { + Headings::attach(T, pn, for_copy); Annotations::write_int(pn, heading_level_ANNOT, 0); Annotations::write_int(pn, implied_heading_ANNOT, TRUE); } @@ -239,7 +241,7 @@ with the result of parsing any caveats in its wording. = inbuild_work *work_identified = NULL; /* temporary variable during parsing below */ -heading *Headings::attach(parse_node_tree *T, parse_node *pn) { +heading *Headings::attach(parse_node_tree *T, parse_node *pn, inbuild_copy *for_copy) { if ((pn == NULL) || (Wordings::empty(Node::get_text(pn)))) internal_error("heading at textless node"); if (Node::get_type(pn) != HEADING_NT) @@ -256,7 +258,7 @@ heading *Headings::attach(parse_node_tree *T, parse_node *pn) { Node::get_text(pn), h->level, h->indentation); if (T->headings->assembled_at_least_once) - Headings::assemble_tree(T); /* to include new heading: unlikely but possible */ + Headings::assemble_tree(T, for_copy); /* to include new heading: unlikely but possible */ return h; } @@ -404,16 +406,24 @@ Until //Headings::assemble_tree// runs, the //heading// nodes listed as belongin to the heading tree are not in fact formed up into a tree structure. = -void Headings::assemble_tree(parse_node_tree *T) { +void Headings::assemble_tree(parse_node_tree *T, inbuild_copy *for_copy) { heading *h; @; LOOP_OVER_LINKED_LIST(h, heading, T->headings->subordinates) { -// LOG("H = %W, level %d\n", h->heading_text, h->level); - @; - @; + if (h->omit_from_tree == FALSE) { + @; + @; + @; + } } T->headings->assembled_at_least_once = TRUE; Headings::verify_heading_tree(T); + #ifndef CORE_MODULE + Copies::list_attached_errors(STDERR, for_copy); + #endif + #ifdef CORE_MODULE + SourceProblems::issue_problems_arising(for_copy); + #endif } @ It's possible to call //Headings::assemble_tree// more than once, to allow @@ -430,6 +440,7 @@ of subordinates. Everything else is. heading *h; LOOP_OVER_LINKED_LIST(h, heading, T->headings->subordinates) { h->parent_heading = NULL; h->child_heading = NULL; h->next_heading = NULL; + h->omit_from_tree = FALSE; } @ The idea of the heading loop is that when we place a heading, we also place @@ -439,9 +450,44 @@ subordinate to no earlier heading: thus, it must be attached to the pseudo-headi at the top of the tree. @ = - if (h->parent_heading == NULL) + if ((h->parent_heading == NULL) && (h->omit_from_tree == FALSE)) Headings::move_below(h, &(T->headings->heading_root)); +@ A complication is that the source text is read out of conceptual sequence +when headings referring to external files are run into. Because of that, if +we have a heading: += (text as Inform 7) + Chapter 7 - Into the Woods (see "woods.i7") += +and if we then have further headings inside the file |woods.i7|, those +further headings |h2| won't be adjacent to the original heading |h| in +the list. So we fix this up here. + +There is a nameless level zero heading marking the change of source file: +we remove that. We also have to throw problems if we find, say, a Book +heading inside a file which is supposed to contain a Chapter. + +@e HeadingTooGreat_SYNERROR + +@ = + if (h->external_file_read) { + heading *h2; + LOOP_OVER_LINKED_LIST(h2, heading, T->headings->subordinates) + if (h2->start_location.file_of_origin == h->external_file_read) { + if (h2->level == 0) { + h2->omit_from_tree = TRUE; + } else if (h2->level <= h->level) { + copy_error *CE = CopyErrors::new(SYNTAX_CE, HeadingTooGreat_SYNERROR); + CopyErrors::supply_node(CE, h2->sentence_declaring); + Copies::attach_error(for_copy, CE); + h2->omit_from_tree = TRUE; + } else { + Headings::move_below(h2, h); + h2->indentation++; + } + } + } + @ Note that the following could be summed up as "move subsequent headings as deep in the tree as we can see they need to be from h's perspective alone". This isn't always the final position. For instance, given the sequence @@ -475,6 +521,7 @@ to the tree as a child of a given parent: = void Headings::move_below(heading *ch, heading *pa) { +LOG("Move %W below %W\n", ch->heading_text, pa->heading_text); heading *former_pa = ch->parent_heading; if (former_pa == pa) return; @; diff --git a/inbuild/supervisor-module/Chapter 6/Inclusions.w b/inbuild/supervisor-module/Chapter 6/Inclusions.w index d2acd23f9..b9e48bf66 100644 --- a/inbuild/supervisor-module/Chapter 6/Inclusions.w +++ b/inbuild/supervisor-module/Chapter 6/Inclusions.w @@ -57,44 +57,8 @@ void Inclusions::visit(parse_node_tree *T, parse_node *pn, parse_node *last_H0, heading *h = Node::get_embodying_heading(pn); if ((h) && (Wordings::nonempty(h->external_file)) && (h->external_file_loaded == FALSE)) { inform_project *proj = ProjectBundleManager::from_copy(inclusions_errors_to); - if ((proj) && (proj->as_copy->location_if_path)) { - pathname *P = Pathnames::down(Projects::materials_path(proj), I"Source"); - text_stream *leaf = Str::new(); - WRITE_TO(leaf, "%W", h->external_file); - h->external_file_loaded = TRUE; - if (pn->down) { - copy_error *CE = CopyErrors::new_T(SYNTAX_CE, HeadingWithFileNonempty_SYNERROR, leaf); - CopyErrors::supply_node(CE, pn); - Copies::attach_error(inclusions_errors_to, CE); - } else { - filename *F = Filenames::in(P, leaf); - if (TextFiles::exists(F) == FALSE) { - copy_error *CE = CopyErrors::new_T(SYNTAX_CE, MissingSourceFile_SYNERROR, leaf); - CopyErrors::supply_node(CE, pn); - Copies::attach_error(inclusions_errors_to, CE); - } else { - int l = SyntaxTree::push_bud(T, pn); - source_file *S = SourceText::read_file(inclusions_errors_to, F, leaf, FALSE, FALSE); - if (S) { - h->external_file_read = S; - wording SW = S->text_read; - int N = Wordings::length(h->heading_text); - wording TOPW = Wordings::up_to(SW, Wordings::first_wn(SW) + N - 1); - wording RESTW = Wordings::from(SW, Wordings::first_wn(SW) + N); - if ((Wordings::match(h->heading_text, TOPW) == FALSE) || - (compare_word(Wordings::first_wn(RESTW), OPENBRACKET_V))) { - copy_error *CE = CopyErrors::new_T(SYNTAX_CE, MisheadedSourceFile_SYNERROR, leaf); - CopyErrors::supply_node(CE, pn); - Copies::attach_error(inclusions_errors_to, CE); - } else if (Wordings::nonempty(RESTW)) - Sentences::break_into_project_copy(T, - RESTW, inclusions_errors_to, proj); - } - SyntaxTree::pop_bud(T, l); - *includes_cleared = FALSE; - } - } - } + if ((proj) && (proj->as_copy->location_if_path)) + @; } } } @@ -126,6 +90,57 @@ void Inclusions::visit(parse_node_tree *T, parse_node *pn, parse_node *last_H0, } } +@ Here we're at a HEADING node which is annotated |(see "source-file")|, and +we need to read that file in: it will be considered as the contents of the +HEADING. + +@ = + pathname *P = Pathnames::down(Projects::materials_path(proj), I"Source"); + text_stream *leaf = Str::new(); + WRITE_TO(leaf, "%W", h->external_file); + h->external_file_loaded = TRUE; + if (pn->down) { + copy_error *CE = CopyErrors::new_T(SYNTAX_CE, HeadingWithFileNonempty_SYNERROR, leaf); + CopyErrors::supply_node(CE, pn); + Copies::attach_error(inclusions_errors_to, CE); + } else { + filename *F = Filenames::in(P, leaf); + if (TextFiles::exists(F) == FALSE) { + copy_error *CE = CopyErrors::new_T(SYNTAX_CE, MissingSourceFile_SYNERROR, leaf); + CopyErrors::supply_node(CE, pn); + Copies::attach_error(inclusions_errors_to, CE); + } else { + int l = SyntaxTree::push_bud(T, pn); + source_file *S = SourceText::read_file(inclusions_errors_to, F, leaf, FALSE, FALSE); + if (S) @; + SyntaxTree::pop_bud(T, l); + *includes_cleared = FALSE; + } + } + +@ Note that the opening words of the external file, here |TOPW|, must match +exactly the heading which called for the file, except for the annotation in +brackets. (It's to prevent that that we look for a round bracket at the +start of |RESTW| as a sign that the user has accidentally included this.) + +Provided a match is made, the |TOPW| words are then thrown away. Their purpose +was just to identify the file sensibly. + +@ = + h->external_file_read = S; + wording SW = S->text_read; + int N = Wordings::length(h->heading_text); + wording TOPW = Wordings::up_to(SW, Wordings::first_wn(SW) + N - 1); + wording RESTW = Wordings::from(SW, Wordings::first_wn(SW) + N); + if ((Wordings::match(h->heading_text, TOPW) == FALSE) || + (compare_word(Wordings::first_wn(RESTW), OPENBRACKET_V))) { + copy_error *CE = CopyErrors::new_T(SYNTAX_CE, MisheadedSourceFile_SYNERROR, leaf); + CopyErrors::supply_node(CE, pn); + Copies::attach_error(inclusions_errors_to, CE); + } else if (Wordings::nonempty(RESTW)) { + Sentences::break_into_project_copy(T, RESTW, inclusions_errors_to, proj); + } + @ Here we parse the content of an Include sentence: i.e., what comes after the word "Include", which might e.g. be "Locksmith by Emily Short". diff --git a/inform7/Downloads/preform-diagnostics.txt b/inform7/Downloads/preform-diagnostics.txt index 08215e2f9..f6f03d8b7 100644 --- a/inform7/Downloads/preform-diagnostics.txt +++ b/inform7/Downloads/preform-diagnostics.txt @@ -5593,6 +5593,8 @@ constraint CS = {27} extremes [1, 1] dialog constraint CS = {27} extremes [1, 1] + see {} + constraint DS = {27} extremes [2, 2] (hits 19/19) (matched: 'not for interactive fiction language element') constraint DS = {24} extremes [3, infinity) @@ -7842,7 +7844,7 @@ hits 1442/27604 nti 27 constraint (none) extremes [1, infinity) English: not - (hits 0/2987) constraint DS = {27} extremes [3, infinity) + (hits 0/2988) constraint DS = {27} extremes [3, infinity) (hits 0/6955) constraint (none) extremes [2, infinity) diff --git a/inform7/Figures/memory-diagnostics.txt b/inform7/Figures/memory-diagnostics.txt index 9ad01268a..98e3e79f4 100644 --- a/inform7/Figures/memory-diagnostics.txt +++ b/inform7/Figures/memory-diagnostics.txt @@ -1,6 +1,6 @@ Total memory consumption was 123341K = 120 MB - ---- was used for 2053890 objects, in 366129 frames in 0 x 800K = 0K = 0 MB: + ---- was used for 2053893 objects, in 366132 frames in 0 x 800K = 0K = 0 MB: 33.1% inter_tree_node_array 58 x 8192 = 475136 objects, 41813824 bytes 20.6% text_stream_array 4634 x 100 = 463400 objects, 26098688 bytes @@ -27,8 +27,8 @@ Total memory consumption was 123341K = 120 MB 0.8% id_body 946 objects, 1082224 bytes 0.7% adjective_meaning 202 objects, 1000304 bytes 0.7% excerpt_meaning 3118 objects, 972816 bytes - 0.7% production 3953 objects, 917096 bytes - 0.7% ptoken 8578 objects, 892112 bytes + 0.7% production 3954 objects, 917328 bytes + 0.7% ptoken 8580 objects, 892320 bytes 0.6% grammatical_usage 3638 objects, 873120 bytes 0.6% inter_schema_node 9056 objects, 869376 bytes 0.6% individual_form 2568 objects, 862848 bytes @@ -66,8 +66,8 @@ Total memory consumption was 123341K = 120 MB ---- linked_list_item_array 4 x 1000 = 4000 objects, 64128 bytes ---- kind_macro_definition 9 objects, 62280 bytes ---- booking 861 objects, 61992 bytes - ---- actions_rcd_data 1892 objects, 60544 bytes ---- scenes_rcd_data 1892 objects, 60544 bytes + ---- actions_rcd_data 1892 objects, 60544 bytes ---- kind_constructor 77 objects, 58520 bytes ---- command_grammar 130 objects, 58240 bytes ---- table 7 objects, 56672 bytes @@ -78,11 +78,11 @@ Total memory consumption was 123341K = 120 MB ---- response_message 408 objects, 52224 bytes ---- ap_clause_array 2 x 400 = 800 objects, 51264 bytes ---- HTML_tag_array 1 x 1000 objects, 48032 bytes + ---- heading 204 objects, 45696 bytes ---- text_substitution 437 objects, 41952 bytes ---- to_family_data 501 objects, 40080 bytes ---- anl_clause_array 1 x 1000 objects, 40032 bytes ---- activity_list_array 1 x 1000 objects, 40032 bytes - ---- heading 204 objects, 39168 bytes ---- shared_variable_access_list_array 12 x 100 = 1200 objects, 38784 bytes ---- parsing_data 675 objects, 37800 bytes ---- production_list 630 objects, 35280 bytes @@ -128,11 +128,11 @@ Total memory consumption was 123341K = 120 MB ---- text_literal_holder 163 objects, 6520 bytes ---- inbuild_copy 53 objects, 6360 bytes ---- inbuild_work 98 objects, 6272 bytes + ---- heading_tree 17 objects, 5304 bytes ---- inbuild_edition 69 objects, 4968 bytes ---- explicit_action_array 1 x 100 objects, 4832 bytes ---- section_md 50 objects, 4800 bytes ---- activity 35 objects, 4760 bytes - ---- heading_tree 17 objects, 4760 bytes ---- value_property_data 84 objects, 4704 bytes ---- parsing_pp_data 96 objects, 4608 bytes ---- build_script 138 objects, 4416 bytes @@ -247,7 +247,7 @@ Total memory consumption was 123341K = 120 MB 100.0% was used for memory not allocated for objects: - 57.6% text stream storage 72763156 bytes in 480864 claims + 57.6% text stream storage 72763676 bytes in 480865 claims 4.2% dictionary storage 5308416 bytes in 7613 claims ---- sorting 1560 bytes in 159 claims 5.7% source text 7200000 bytes in 3 claims @@ -265,5 +265,5 @@ Total memory consumption was 123341K = 120 MB ---- code generation workspace for objects 3480 bytes in 19 claims 0.2% emitter array storage 280544 bytes in 2001 claims --147.-7% was overhead - -186645920 bytes = -182271K = -177 MB +-147.-7% was overhead - -186653432 bytes = -182278K = -178 MB diff --git a/inform7/Figures/timings-diagnostics.txt b/inform7/Figures/timings-diagnostics.txt index afec14638..3632b9fbc 100644 --- a/inform7/Figures/timings-diagnostics.txt +++ b/inform7/Figures/timings-diagnostics.txt @@ -1,12 +1,12 @@ 100.0% in inform7 run - 70.4% in compilation to Inter - 49.7% in //Sequence::undertake_queued_tasks// - 4.7% in //MajorNodes::pre_pass// - 3.2% in //MajorNodes::pass_1// + 70.5% in compilation to Inter + 49.6% in //Sequence::undertake_queued_tasks// + 4.6% in //MajorNodes::pre_pass// + 3.3% in //MajorNodes::pass_1// 1.9% in //RTPhrasebook::compile_entries// 1.7% in //ImperativeDefinitions::assess_all// 1.5% in //RTKindConstructors::compile// - 0.9% in //Sequence::lint_inter// + 1.1% in //Sequence::lint_inter// 0.5% in //MajorNodes::pass_2// 0.5% in //Sequence::undertake_queued_tasks// 0.5% in //Sequence::undertake_queued_tasks// @@ -17,17 +17,17 @@ 0.1% in //RTKindConstructors::compile_permissions// 0.1% in //Task::make_built_in_kind_constructors// 0.1% in //World::stages_II_and_III// - 2.9% not specifically accounted for - 26.6% in running Inter pipeline - 10.5% in step 14/15: generate inform6 -> auto.inf - 5.5% in step 5/15: load-binary-kits - 5.5% in step 6/15: make-synoptic-module - 1.9% in step 9/15: make-identifiers-unique + 2.8% not specifically accounted for + 26.5% in running Inter pipeline + 10.1% in step 14/15: generate inform6 -> auto.inf + 5.8% in step 5/15: load-binary-kits + 5.4% in step 6/15: make-synoptic-module + 1.7% in step 9/15: make-identifiers-unique 0.3% in step 12/15: eliminate-redundant-operations 0.3% in step 4/15: compile-splats 0.3% in step 7/15: shorten-wiring 0.3% in step 8/15: detect-indirect-calls 0.1% in step 11/15: eliminate-redundant-labels - 1.3% not specifically accounted for - 2.4% in supervisor + 1.5% not specifically accounted for + 2.5% in supervisor 0.4% not specifically accounted for diff --git a/inform7/Internal/Inter/BasicInformExtrasKit/kit_metadata.json b/inform7/Internal/Inter/BasicInformExtrasKit/kit_metadata.json index 8e73ae0c9..1d0f622a9 100644 --- a/inform7/Internal/Inter/BasicInformExtrasKit/kit_metadata.json +++ b/inform7/Internal/Inter/BasicInformExtrasKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "BasicInformExtrasKit", - "version": "10.2.0-beta+6V90" + "version": "10.2.0-beta+6V91" }, "kit-details": { "has-priority": 1 diff --git a/inform7/Internal/Inter/BasicInformKit/kit_metadata.json b/inform7/Internal/Inter/BasicInformKit/kit_metadata.json index ff1a03b5c..65774079f 100644 --- a/inform7/Internal/Inter/BasicInformKit/kit_metadata.json +++ b/inform7/Internal/Inter/BasicInformKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "BasicInformKit", - "version": "10.2.0-beta+6V90" + "version": "10.2.0-beta+6V91" }, "needs": [ { "unless": { diff --git a/inform7/Internal/Inter/CommandParserKit/kit_metadata.json b/inform7/Internal/Inter/CommandParserKit/kit_metadata.json index 791060daa..95c142109 100644 --- a/inform7/Internal/Inter/CommandParserKit/kit_metadata.json +++ b/inform7/Internal/Inter/CommandParserKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "CommandParserKit", - "version": "10.2.0-beta+6V90" + "version": "10.2.0-beta+6V91" }, "needs": [ { "need": { diff --git a/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json b/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json index 77b891acd..af4e93425 100644 --- a/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json +++ b/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "EnglishLanguageKit", - "version": "10.2.0-beta+6V90" + "version": "10.2.0-beta+6V91" }, "needs": [ { "need": { diff --git a/inform7/Internal/Inter/WorldModelKit/kit_metadata.json b/inform7/Internal/Inter/WorldModelKit/kit_metadata.json index 820b087b9..01cc57b8c 100644 --- a/inform7/Internal/Inter/WorldModelKit/kit_metadata.json +++ b/inform7/Internal/Inter/WorldModelKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "WorldModelKit", - "version": "10.2.0-beta+6V90" + "version": "10.2.0-beta+6V91" }, "needs": [ { "need": { diff --git a/inform7/assertions-module/Chapter 4/Name Resolution.w b/inform7/assertions-module/Chapter 4/Name Resolution.w index 3fe8170cb..11b38597e 100644 --- a/inform7/assertions-module/Chapter 4/Name Resolution.w +++ b/inform7/assertions-module/Chapter 4/Name Resolution.w @@ -162,7 +162,7 @@ and has no "level" or "indentation" as such. = void NameResolution::make_the_tree(void) { - Headings::assemble_tree(Task::syntax_tree()); + Headings::assemble_tree(Task::syntax_tree(), Task::project()->as_copy); } heading *NameResolution::pseudo_heading(void) { diff --git a/inform7/core-module/Chapter 2/Problems With Source Text.w b/inform7/core-module/Chapter 2/Problems With Source Text.w index 1a58e8da9..8eae632c4 100644 --- a/inform7/core-module/Chapter 2/Problems With Source Text.w +++ b/inform7/core-module/Chapter 2/Problems With Source Text.w @@ -518,6 +518,17 @@ void SourceProblems::issue_problems_arising(inbuild_copy *C) { "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"); }