From 1005fd668c3f0ca7a98bd60751a671c76423b89e Mon Sep 17 00:00:00 2001 From: Graham Nelson Date: Wed, 6 May 2020 18:16:52 +0100 Subject: [PATCH] Made heading tree subordinate to syntax tree --- .../supervisor-module/Chapter 6/Headings.w | 144 +++++++++--------- .../core-module/Chapter 1/How To Compile.w | 2 +- inform7/core-module/Chapter 7/Headings.w | 54 +++++-- shared/syntax-module/Chapter 2/Parse Tree.w | 8 + 4 files changed, 120 insertions(+), 88 deletions(-) diff --git a/inbuild/supervisor-module/Chapter 6/Headings.w b/inbuild/supervisor-module/Chapter 6/Headings.w index 5fa500617..934368daa 100644 --- a/inbuild/supervisor-module/Chapter 6/Headings.w +++ b/inbuild/supervisor-module/Chapter 6/Headings.w @@ -3,7 +3,8 @@ To keep track of the hierarchy of headings and subheadings found in the source text. -@ Headings in the source text correspond to |HEADING_NT| nodes in syntax +@h The hierarchy. +Headings in the source text correspond to |HEADING_NT| nodes in syntax trees, and mostly occur when the user has explicitly typed a heading such as: >> Part VII - The Ghost of the Aragon @@ -31,40 +32,6 @@ in a source text is 30 to 100. @d NO_HEADING_LEVELS 10 -@ Although it is implicit in the parse tree already, the heading structure -is not easy to deduce, and so in this section we build a much smaller tree -consisting just of the hierarchy of headings. The heading tree has nodes -made from the following structures: - -= -typedef struct heading { - struct parse_node *sentence_declaring; /* if any: file starts are undeclared */ - struct source_location start_location; /* first word under this heading is here */ - int level; /* 0 for Volume (highest) to 5 for Section (lowest) */ - int indentation; /* in a hierarchical listing */ - int index_definitions_made_under_this; /* for instance, global variables made here? */ - int for_release; /* include this material in a release version? */ - int omit_material; /* if set, simply ignore all of this */ - int use_with_or_without; /* if TRUE, use with the extension; if FALSE, without */ - 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 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; - MEMORY_MANAGEMENT -} heading; - -@ The headings and subheadings are formed into a tree in which each heading -contains its lesser-order headings. The pseudo-heading exists to be the root -of this tree; the entire text falls under it. It is not a real heading at all, -and has no "level" or "indentation" as such. - -= -heading pseudo_heading; /* The entire source falls under this top-level heading */ - @ As an example, a sequence in the primary source text of (Chapter I, Book Two, Section 5, Chapter I, Section 1, Chapter III) would be formed up into the heading tree: @@ -90,7 +57,7 @@ the same indentation. But when the new heading is lower level than its predecessor (i.e., more important) then the indentation decreases to match the last one equally important. -We can secure the last of those properties with a formal definition as +@ We can secure the last of those properties with a formal definition as follows. The level $\ell_n$ of a heading depends only on its wording (or source file origin), but the indentation of the $n$th heading, $i_n$, depends on $(\ell_1, \ell_2, ..., \ell_n)$, the sequence of all levels so @@ -116,7 +83,7 @@ $$ i_{m(K)},\qquad {\rm where}\qquad m(K) = {\rm max} \lbrace j \mid 0\leq j < n for each possible heading level $K=0, 1, ..., 9$. This requires much less storage: we call it the "last indentation above level $K$". -This leads to the following algorithm when looking at the headings in any +@ This leads to the following algorithm when looking at the headings in any individual file of source text: at the top of file, = (text as C) for (i=0; iowning_syntax_tree = T; h->parent_heading = NULL; h->child_heading = NULL; h->next_heading = NULL; h->list_of_contents = NULL; h->last_in_list_of_contents = NULL; h->for_release = NOT_APPLICABLE; h->omit_material = FALSE; @@ -228,7 +222,7 @@ heading *Headings::declare(parse_node_tree *T, parse_node *PN) { @; LOGIF(HEADINGS, "Created heading $H\n", h); - if (heading_tree_made_at_least_once) Headings::make_tree(); + if (heading_tree_made_at_least_once) Headings::make_tree(T); return h; } @@ -372,17 +366,18 @@ to make revisions if late news comes in of a new heading (see above), we begin by removing any existing relationships between the heading nodes. = -void Headings::make_tree(void) { +void Headings::make_tree(parse_node_tree *tree) { heading *h; @; - LOOP_OVER(h, heading) { - @; - @; - } + LOOP_OVER(h, heading) + if (h->owning_syntax_tree == tree) { + @; + @; + } heading_tree_made_at_least_once = TRUE; - Headings::verify_heading_tree(); + Headings::verify_heading_tree(tree); } @ Note that the loop over headings below loops through all those which were @@ -390,12 +385,14 @@ created by the memory manager: which is to say, all of them except for the pseudo-heading, which was explicitly placed in static memory above. @ = + tree->heading_root.child_heading = NULL; + tree->heading_root.parent_heading = NULL; + tree->heading_root.next_heading = NULL; heading *h; - pseudo_heading.child_heading = NULL; pseudo_heading.parent_heading = NULL; - pseudo_heading.next_heading = NULL; - LOOP_OVER(h, heading) { - h->parent_heading = NULL; h->child_heading = NULL; h->next_heading = NULL; - } + LOOP_OVER(h, heading) + if (h->owning_syntax_tree == tree) { + h->parent_heading = NULL; h->child_heading = NULL; h->next_heading = NULL; + } @ The idea of the heading loop is that when we place a heading, we also place subsequent headings of lesser or equal status until we cannot do so any longer. @@ -405,7 +402,7 @@ at the top of the tree. @ = if (h->parent_heading == NULL) - Headings::make_child_heading(h, &pseudo_heading); + Headings::make_child_heading(h, &(tree->heading_root)); @ 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". @@ -488,19 +485,19 @@ our working. = int heading_tree_damaged = FALSE; -void Headings::verify_heading_tree(void) { - Headings::verify_heading_tree_r(&pseudo_heading, -1); +void Headings::verify_heading_tree(parse_node_tree *tree) { + Headings::verify_heading_tree_r(&(tree->heading_root), &(tree->heading_root), -1); if (heading_tree_damaged) internal_error("heading tree failed to verify"); } -void Headings::verify_heading_tree_r(heading *h, int depth) { +void Headings::verify_heading_tree_r(heading *root, heading *h, int depth) { if (h == NULL) return; - if ((h != &pseudo_heading) && (depth != h->indentation)) { + if ((h != root) && (depth != h->indentation)) { heading_tree_damaged = TRUE; LOG("$H\n*** indentation should be %d ***\n", h, depth); } - Headings::verify_heading_tree_r(h->child_heading, depth+1); - Headings::verify_heading_tree_r(h->next_heading, depth); + Headings::verify_heading_tree_r(root, h->child_heading, depth+1); + Headings::verify_heading_tree_r(root, h->next_heading, depth); } @h Miscellaneous heading services. @@ -585,8 +582,9 @@ But when the following is called, we do know that. void Headings::satisfy_dependencies(parse_node_tree *T, inbuild_copy *C) { heading *h; LOOP_OVER(h, heading) - if (h->use_with_or_without != NOT_APPLICABLE) - Headings::satisfy_individual_heading_dependency(T, C, h); + if (h->owning_syntax_tree == T) + if (h->use_with_or_without != NOT_APPLICABLE) + Headings::satisfy_individual_heading_dependency(T, C, h); } @ And now the code to check an individual heading's usage. This whole @@ -612,21 +610,23 @@ void Headings::satisfy_individual_heading_dependency(parse_node_tree *T, inbuild wchar_t *text = Lexer::word_text(Wordings::first_wn(S)); S = Feeds::feed_text(text); } - heading *h2; int found = FALSE; if (loaded == FALSE) @ else { - LOOP_OVER(h2, heading) - if ((Wordings::nonempty(h2->heading_text)) && - (Wordings::match_perhaps_quoted(S, h2->heading_text)) && - (Works::match( - Headings::get_extension_containing(h2)->as_copy->edition->work, work))) { - found = TRUE; - if (h->level != h2->level) - @; - Headings::excise_material_under(T, C, h2, NULL); - Headings::excise_material_under(T, C, h, h2->sentence_declaring); - break; - } + heading *h2; + int found = FALSE; + LOOP_OVER(h2, heading) + if (h2->owning_syntax_tree == T) + if ((Wordings::nonempty(h2->heading_text)) && + (Wordings::match_perhaps_quoted(S, h2->heading_text)) && + (Works::match( + Headings::get_extension_containing(h2)->as_copy->edition->work, work))) { + found = TRUE; + if (h->level != h2->level) + @; + Headings::excise_material_under(T, C, h2, NULL); + Headings::excise_material_under(T, C, h, h2->sentence_declaring); + break; + } if (found == FALSE) @; } } diff --git a/inform7/core-module/Chapter 1/How To Compile.w b/inform7/core-module/Chapter 1/How To Compile.w index 1b094540f..833bb3816 100644 --- a/inform7/core-module/Chapter 1/How To Compile.w +++ b/inform7/core-module/Chapter 1/How To Compile.w @@ -117,7 +117,7 @@ most of these worker functions are in the |core| module, some are not. Task::advance_stage_to(SEMANTIC_II_CSEQ, I"Semantic analysis II", -1); BENCH(ParseTreeUsage::verify) - BENCH(Headings::make_tree) + BENCH(Sentences::Headings::make_the_tree) BENCH(Sentences::Headings::write_as_xml) BENCH(Modules::traverse_to_define) diff --git a/inform7/core-module/Chapter 7/Headings.w b/inform7/core-module/Chapter 7/Headings.w index de11ca131..d1d838dd5 100644 --- a/inform7/core-module/Chapter 7/Headings.w +++ b/inform7/core-module/Chapter 7/Headings.w @@ -45,9 +45,11 @@ void Sentences::Headings::verify_divisions(void) { int total = 0, disaster = FALSE; LOOP_OVER(nt, noun) Sentences::Headings::name_resolution_data(nt)->heading_count = 0; + parse_node_tree *T = Task::syntax_tree(); LOOP_OVER(h, heading) - LOOP_OVER_NOUNS_UNDER(nt, h) - Sentences::Headings::name_resolution_data(nt)->heading_count++, total++; + if (h->owning_syntax_tree == T) + LOOP_OVER_NOUNS_UNDER(nt, h) + Sentences::Headings::name_resolution_data(nt)->heading_count++, total++; LOOP_OVER(nt, noun) if (Sentences::Headings::name_resolution_data(nt)->heading_count > 1) { LOG("$z occurs under %d headings\n", @@ -141,6 +143,20 @@ void Sentences::Headings::disturb(void) { noun_search_list_valid_for_this_heading = NULL; } +@ The headings and subheadings are formed into a tree in which each heading +contains its lesser-order headings. The pseudo-heading exists to be the root +of this tree; the entire text falls under it. It is not a real heading at all, +and has no "level" or "indentation" as such. + += +void Sentences::Headings::make_the_tree(void) { + Headings::make_tree(Task::syntax_tree()); +} + +heading *Sentences::Headings::pseudo_heading(void) { + return &(Task::syntax_tree()->heading_root); +} + @ Leaving aside the cache, then, we build a list as initially empty, then all nametags of priority 1 as found by recursively searching headings, then all nametags of priority 2, and so on. @@ -182,7 +198,8 @@ ever get that far. @ = nt_search_start = NULL; nt_search_finish = NULL; - pseudo_heading.list_of_contents = NULL; /* should always be true, but just in case */ + heading *pseud = Sentences::Headings::pseudo_heading(); + pseud->list_of_contents = NULL; /* should always be true, but just in case */ @ The potential for disaster if this algorithm should be incorrect is high, so we perform a quick count to see if everything made it onto the list @@ -196,7 +213,7 @@ and produce an internal error if not. LOG("%d tags created, %d in ordering\n", no_tags_attached, c); Sentences::Headings::log_all_headings(); LOG("Making fresh tree:\n"); - Headings::make_tree(); + Sentences::Headings::make_the_tree(); Sentences::Headings::log_all_headings(); internal_error_tree_unsafe("reordering of nametags failed"); } @@ -301,7 +318,8 @@ to the index of the project, and to a freestanding XML file. = void Sentences::Headings::log(heading *h) { if (h==NULL) { LOG("\n"); return; } - if (h==&pseudo_heading) { LOG("\n"); return; } + heading *pseud = Sentences::Headings::pseudo_heading(); + if (h == pseud) { LOG("\n"); return; } LOG("H%d ", h->allocation_id); if (h->start_location.file_of_origin) LOG("<%f, line %d>", @@ -317,9 +335,12 @@ surreptitiously check that it is correctly formed at the same time. = void Sentences::Headings::log_all_headings(void) { heading *h; - LOOP_OVER(h, heading) LOG("$H\n", h); + parse_node_tree *T = Task::syntax_tree(); + LOOP_OVER(h, heading) + if (h->owning_syntax_tree == T) + LOG("$H\n", h); LOG("\n"); - Sentences::Headings::log_headings_recursively(&pseudo_heading, 0); + Sentences::Headings::log_headings_recursively(Sentences::Headings::pseudo_heading(), 0); } void Sentences::Headings::log_headings_recursively(heading *h, int depth) { @@ -345,7 +366,8 @@ void Sentences::Headings::index(OUTPUT_STREAM) { HTML_OPEN("p"); WRITE("CONTENTS"); HTML_CLOSE("p"); - Sentences::Headings::index_heading_recursively(OUT, pseudo_heading.child_heading); + Sentences::Headings::index_heading_recursively(OUT, + Sentences::Headings::pseudo_heading()->child_heading); contents_entry *ce; int min_positive_level = 10; LOOP_OVER(ce, contents_entry) @@ -485,13 +507,15 @@ void Sentences::Headings::write_headings_as_xml_inner(OUTPUT_STREAM) { WRITE("\n"); INDENT; WRITE("Application Version%B (build %B)\n", FALSE, TRUE); - LOOP_OVER(h, heading) { - WRITE("%d\n", h->allocation_id); - INDENT; - @; - OUTDENT; - WRITE("\n"); - } + parse_node_tree *T = Task::syntax_tree(); + LOOP_OVER(h, heading) + if (h->owning_syntax_tree == T) { + WRITE("%d\n", h->allocation_id); + INDENT; + @; + OUTDENT; + WRITE("\n"); + } OUTDENT; WRITE("\n"); } diff --git a/shared/syntax-module/Chapter 2/Parse Tree.w b/shared/syntax-module/Chapter 2/Parse Tree.w index 506717bfc..c1a7de14d 100644 --- a/shared/syntax-module/Chapter 2/Parse Tree.w +++ b/shared/syntax-module/Chapter 2/Parse Tree.w @@ -33,6 +33,9 @@ typedef struct parse_node_tree { int attachment_sp; struct parse_node *attachment_stack_parent[MAX_ATTACHMENT_STACK_SIZE]; struct parse_node *one_off_attachment_point; + #ifdef SUPERVISOR_MODULE + struct heading heading_root; + #endif MEMORY_MANAGEMENT } parse_node_tree; @@ -42,6 +45,11 @@ parse_node_tree *ParseTree::new_tree(void) { T->attachment_sp = 0; T->one_off_attachment_point = NULL; ParseTree::push_attachment_point(T, T->root_node); + #ifdef SUPERVISOR_MODULE + T->heading_root.parent_heading = NULL; + T->heading_root.child_heading = NULL; + T->heading_root.next_heading = NULL; + #endif return T; }