To work out paragraph numbers within each section.

§1. 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) {
            Scan this section to see where paragraph macros are used1.1;
            Work out paragraph numbers within this section1.2;
        }
    }
}

§1.1. Scan this section to see where paragraph macros are used1.1 =

    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) Add a record that the macro is used in this paragraph1.1.2;
            DISCARD_TEXT(found_macro)
        }
        DISCARD_TEXT(p)
    }

§1.1.1. 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
    CLASS_DEFINITION
} macro_usage;

§1.1.2. Add a record that the macro is used in this paragraph1.1.2 =

    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++;

§1.2. 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.

Work out paragraph numbers within this section1.2 =

    The parent of a macro definition is the place where it's first used1.2.1;
    Otherwise share the parent of a following paragraph, provided it precedes us1.2.2;
    Create paragraph number texts1.2.3;
    Number the still parent-less paragraphs consecutively from 11.2.4;
    Recursively derive the numbers of parented paragraphs from those of their parents1.2.5;

§1.2.1. The parent of a macro definition is the place where it's first used1.2.1 =

    paragraph *P;
    LOOP_OVER_LINKED_LIST(P, paragraph, S->paragraphs)
        if (P->defines_macro) {
            macro_usage *mu;
            LOOP_OVER_LINKED_LIST(mu, macro_usage, P->defines_macro->macro_usages)
                if (P != mu->used_in_paragraph) {
                    Numbering::set_parent(P, mu->used_in_paragraph);
                    break;
                }
        }

§1.2.2. Otherwise share the parent of a following paragraph, provided it precedes us1.2.2 =

    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)
                        Numbering::set_parent(P, P2->parent_paragraph);
                    break;
                }
            }

§1.2.3. Create paragraph number texts1.2.3 =

    paragraph *P;
    LOOP_OVER_LINKED_LIST(P, paragraph, S->paragraphs)
        P->paragraph_number = Str::new();

§1.2.4. 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.

Number the still parent-less paragraphs consecutively from 11.2.4 =

    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);

§1.2.5. Recursively derive the numbers of parented paragraphs from those of their parents1.2.5 =

    paragraph *P;
    LOOP_OVER_LINKED_LIST(P, paragraph, S->paragraphs)
        Numbering::settle_paragraph_number(P);

§2. 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);
    if (P == P->parent_paragraph) internal_error("paragraph is its own parent");
    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;
}

void Numbering::set_parent(paragraph *of, paragraph *to) {
    if (of == NULL) internal_error("no paragraph");
    if (to == of) internal_error("paragraph parent set to itself");
    of->parent_paragraph = to;
}