[Numbering::] Paragraph Numbering. To work out paragraph numbers within each section. @ Traditional LP tools have numbered paragraphs in the obvious way, starting from 1 and working up to what may be an enormous number. (The web for Knuth's Metafont runs from 1 to 1215, for example.) Inweb expects to be working on rather larger programs and therefore numbers independently from 1 within each section. It also tries to make the numbering more structurally relevant: thus paragraph 1.1 will be used within paragraph 1, and so on. It's a little ambiguous how to do this for the best, as we'll see. We can certainly only do it if we know exactly where macros are used. This is something we scan for on a weave, but not on a tangle; that's fine, though, because tangled code doesn't need to know its own paragraph numbers. = void Numbering::number_web(web *W) { chapter *C; section *S; LOOP_OVER_LINKED_LIST(C, chapter, W->chapters) { LOOP_OVER_LINKED_LIST(S, section, C->sections) { @; @; } } } @ = for (source_line *L = S->first_line; L; L = L->next_line) { TEMPORARY_TEXT(p); Str::copy(p, L->text); int mlen, mpos; while ((mpos = Regexp::find_expansion(p, '@', '<', '@', '>', &mlen)) != -1) { TEMPORARY_TEXT(found_macro); Str::substr(found_macro, Str::at(p, mpos+2), Str::at(p, mpos+mlen-2)); TEMPORARY_TEXT(original_p); Str::copy(original_p, p); Str::clear(p); Str::substr(p, Str::at(original_p, mpos + mlen), Str::end(original_p)); DISCARD_TEXT(original_p); para_macro *pmac = Macros::find_by_name(found_macro, S); if (pmac) @; DISCARD_TEXT(found_macro); } DISCARD_TEXT(p); } @ Each macro comes with a linked list of notes about which paragraphs use it; necessarily paragraphs within the same section. This paragraph you're looking at now shows the difficulty involved in paragraph numbering. It's not a macro, so it's not obviously used by any other paragraph. Should it be bumped up to paragraph 2? But if we do that, we end up with numbers out of order, since the one after it would have to be 1.1.1. Instead this one will be 1.1.1, to place it into the natural lexicographic sequence. = typedef struct macro_usage { struct paragraph *used_in_paragraph; int multiplicity; /* for example, 2 if it's used twice in this paragraph */ MEMORY_MANAGEMENT } macro_usage; @ = macro_usage *mu, *last = NULL; LOOP_OVER_LINKED_LIST(mu, macro_usage, pmac->macro_usages) { last = mu; if (mu->used_in_paragraph == L->owning_paragraph) break; } if (mu == NULL) { mu = CREATE(macro_usage); mu->used_in_paragraph = L->owning_paragraph; mu->multiplicity = 0; ADD_TO_LINKED_LIST(mu, macro_usage, pmac->macro_usages); } mu->multiplicity++; @ Basically we'll form the paragraphs into a tree, or in fact a forest. If a paragraph defines a macro then we want it to be a child node of the paragraph where the macro is first used; it's then a matter of filling in other nodes a bit speculatively. @ = @; @; @; @; @; @ = paragraph *P; LOOP_OVER_LINKED_LIST(P, paragraph, S->paragraphs) if (P->defines_macro) { macro_usage *mu = FIRST_IN_LINKED_LIST(macro_usage, P->defines_macro->macro_usages); if (mu) P->parent_paragraph = mu->used_in_paragraph; } @ = paragraph *P; LOOP_OVER_LINKED_LIST(P, paragraph, S->paragraphs) if (P->parent_paragraph == NULL) for (linked_list_item *P2_item = P_item; P2_item; P2_item = NEXT_ITEM_IN_LINKED_LIST(P2_item, paragraph)) { paragraph *P2 = CONTENT_IN_ITEM(P2_item, paragraph); if (P2->parent_paragraph) { if (P2->parent_paragraph->allocation_id < P->allocation_id) P->parent_paragraph = P2->parent_paragraph; break; } } @ = paragraph *P; LOOP_OVER_LINKED_LIST(P, paragraph, S->paragraphs) P->paragraph_number = Str::new(); @ Now we have our tree, and we number paragraphs accordingly: root notes are numbered 1, 2, 3, ..., and then children are numbered with suffixes .1, .2, .3, ..., under their parents. @ = int top_level = 1; paragraph *P; LOOP_OVER_LINKED_LIST(P, paragraph, S->paragraphs) if (P->parent_paragraph == NULL) { WRITE_TO(P->paragraph_number, "%d", top_level++); P->next_child_number = 1; } else Str::clear(P->paragraph_number); @ = paragraph *P; LOOP_OVER_LINKED_LIST(P, paragraph, S->paragraphs) Numbering::settle_paragraph_number(P); @ The following paragraph shows the deficiencies of the algorithm: it's going to end up numbered 2, because it isn't used anywhere and doesn't seem to be in the middle of a wider description. But better to keep it in the sequence chosen by the author, so 2 it is. = void Numbering::settle_paragraph_number(paragraph *P) { if (Str::len(P->paragraph_number) > 0) return; WRITE_TO(P->paragraph_number, "X"); /* to prevent malformed sections hanging this */ if (P->parent_paragraph) Numbering::settle_paragraph_number(P->parent_paragraph); Str::clear(P->paragraph_number); WRITE_TO(P->paragraph_number, "%S.%d", P->parent_paragraph->paragraph_number, P->parent_paragraph->next_child_number++); P->next_child_number = 1; }