[Weaver::] The Weaver. To weave a portion of the code into instructions for TeX. @ \section{The Master Weaver.} Here's what has happened so far, on a weave run of Inweb: on any other sort of run, of course, we would never be in this section of code. The web was read completely into memory and fully parsed. A request was then made either to swarm a mass of individual weaves, or to make just a single weave, with the target in each case being identified by its range. A further decoding layer then translated each range into rather more basic details of what to weave and where to put the result: and so we arrive at the front door of the routine [[Weaver::weave]] below. <<*>>= int Weaver::weave(weave_order *wv) { heterogeneous_tree *tree = WeaveTree::new_tree(wv); TEMPORARY_TEXT(banner) WRITE_TO(banner, "Weave of '%S' generated by Inweb", wv->booklet_title); tree_node *H = WeaveTree::head(tree, banner); DISCARD_TEXT(banner) tree_node *B = WeaveTree::body(tree); tree_node *T = WeaveTree::tail(tree, I"End of weave"); Trees::make_child(H, tree->root); Trees::make_child(B, tree->root); Trees::make_child(T, tree->root); int lines = Weaver::weave_inner(wv, tree, B); WeaveTree::prune(tree); text_stream TO_struct; text_stream *OUT = &TO_struct; if (STREAM_OPEN_TO_FILE(OUT, wv->weave_to, UTF8_ENC) == FALSE) Errors::fatal_with_file("unable to write woven file", wv->weave_to); Formats::render(OUT, tree, wv->weave_to); STREAM_CLOSE(OUT); return lines; } <<*>>= int Weaver::weave_inner(weave_order *wv, heterogeneous_tree *tree, tree_node *body) { web *W = wv->weave_web; int lines_woven = 0; weaver_state state_at; weaver_state *state = &state_at; <>; chapter *C, *last_heading = NULL; section *S; LOOP_OVER_LINKED_LIST(C, chapter, W->chapters) if (C->md->imported == FALSE) { LOOP_OVER_LINKED_LIST(S, section, C->sections) if (Reader::range_within(S->md->sect_range, wv->weave_range)) { <>; <>; LanguageMethods::begin_weave(S, wv); <>; <>; } } <>; return lines_woven; } <>= if (last_heading != C) { <>; tree_node *CH = WeaveTree::chapter(tree, C); Trees::make_child(CH, state->body_node); state->chapter_node = CH; state->ap = CH; last_heading = C; if (wv->theme_match == NULL) { tree_node *H = WeaveTree::chapter_header(tree, C); Trees::make_child(H, state->chapter_node); } } <>= if (wv->theme_match == NULL) { if (last_heading != NULL) { tree_node *F = WeaveTree::chapter_footer(tree, last_heading); Trees::make_child(F, state->chapter_node); } } <>= tree_node *SH = WeaveTree::section(tree, S); Trees::make_child(SH, state->chapter_node); state->section_node = SH; state->ap = SH; if (wv->theme_match == NULL) { tree_node *H = WeaveTree::section_header(tree, S); Trees::make_child(H, state->section_node); } <>= if (wv->theme_match == NULL) { tree_node *F = WeaveTree::section_footer(tree, S); Trees::make_child(F, state->section_node); } @ \section{The state.} We can now begin on a clean page, by initialising the state of the weaver: <<*>>= enum COMMENTARY_MATERIAL from 1 enum MACRO_MATERIAL /* when a macro is being defined... */ enum DEFINITION_MATERIAL /* ...versus when an [[@d]] definition is being made */ enum CODE_MATERIAL /* verbatim code */ enum ENDNOTES_MATERIAL /* endnotes at the foot of a paragraph */ enum FOOTNOTES_MATERIAL /* footnote texts for a paragraph */ <<*>>= typedef struct weaver_state { int kind_of_material; /* one of the enumerated [[*_MATERIAL]] constants above */ int line_break_pending; /* insert a line break before the next woven line? */ int next_heading_without_vertical_skip; int horizontal_rule_just_drawn; struct section *last_extract_from; struct tree_node *body_node; struct tree_node *chapter_node; struct tree_node *section_node; struct tree_node *para_node; struct tree_node *carousel_node; struct tree_node *material_node; struct tree_node *ap; } weaver_state; <>= state->kind_of_material = COMMENTARY_MATERIAL; state->line_break_pending = FALSE; state->next_heading_without_vertical_skip = FALSE; state->horizontal_rule_just_drawn = FALSE; state->last_extract_from = NULL; state->body_node = body; state->chapter_node = NULL; state->section_node = NULL; state->para_node = NULL; state->carousel_node = NULL; state->material_node = NULL; state->ap = body; @ \section{Weaving a section.} <>= paragraph *current_P = NULL; int toc_made = FALSE; for (source_line *LLL = S->first_line; LLL; LLL = LLL->next_line) { wv->current_weave_line = LLL; if (LLL->owning_paragraph == NULL) <> else if (LLL->owning_paragraph != current_P) { if (toc_made == FALSE) { if (Str::len(S->sect_purpose) > 0) { tree_node *F = WeaveTree::purpose(tree, S->sect_purpose); Trees::make_child(F, state->ap); } Weaver::weave_table_of_contents(tree, state->ap, S); toc_made = TRUE; } current_P = LLL->owning_paragraph; if (Tags::tagged_with(current_P, wv->theme_match)) <>; } } <>= if (LLL->category == INTERFACE_BODY_LCAT) { state->horizontal_rule_just_drawn = FALSE; continue; } if (LLL->category == PURPOSE_BODY_LCAT) { continue; } if (LLL->category == DEFINITIONS_LCAT) { Weaver::weave_subheading(tree, wv, state->ap, I"Definitions"); state->next_heading_without_vertical_skip = TRUE; state->horizontal_rule_just_drawn = FALSE; continue; } if (LLL->category == BAR_LCAT) { state->kind_of_material = COMMENTARY_MATERIAL; state->next_heading_without_vertical_skip = TRUE; if (state->horizontal_rule_just_drawn == FALSE) { tree_node *B = WeaveTree::bar(tree); Trees::make_child(B, state->ap); } continue; } if ((LLL->category == CHAPTER_HEADING_LCAT) || (LLL->category == SECTION_HEADING_LCAT)) continue; <>= if (current_P->starts_on_new_page) Trees::make_child(WeaveTree::pagebreak(tree), state->ap); source_line *L = LLL; if ((L->category != HEADING_START_LCAT) && (L->category != PARAGRAPH_START_LCAT)) Main::error_in_web(I"bad start to paragraph", L); /* should never happen */ <>; <>; L = L->next_line; for (; ((L) && (L->owning_paragraph == current_P)); L = L->next_line) { wv->current_weave_line = L; if (LanguageMethods::skip_in_weaving(S->sect_language, wv, L) == FALSE) { lines_woven++; <>; <>; } } L = NULL; Weaver::change_material(tree, state, ENDNOTES_MATERIAL, FALSE, NULL, NULL); Weaver::show_endnotes_on_previous_paragraph(tree, wv, state->ap, current_P); @ \section{How paragraphs begin.} <>= LanguageMethods::reset_syntax_colouring(S->sect_language); if (wv->theme_match) <>; state->para_node = WeaveTree::paragraph_heading(tree, current_P, state->next_heading_without_vertical_skip); Trees::make_child(state->para_node, state->section_node); Weaver::change_material_for_para(tree, state); state->kind_of_material = COMMENTARY_MATERIAL; state->next_heading_without_vertical_skip = FALSE; @ If we are weaving a selection of extracted paragraphs, normal conventions about breaking pages at chapters and sections fail to work. So: <>= text_stream *cap = Tags::retrieve_caption(L->owning_paragraph, wv->theme_match); if (Str::len(cap) > 0) { Weaver::weave_subheading(tree, wv, state->ap, C->md->ch_title); } else if (state->last_extract_from != S) { TEMPORARY_TEXT(extr) WRITE_TO(extr, "From %S: %S", C->md->ch_title, S->md->sect_title); Weaver::weave_subheading(tree, wv, state->ap, extr); DISCARD_TEXT(extr) } state->last_extract_from = S; @ There's quite likely ordinary text on the line following the paragraph start indication, too, so we need to weave this out: <>= if (Str::len(L->text_operand2) > 0) { TEMPORARY_TEXT(matter) WRITE_TO(matter, "%S\n", L->text_operand2); Weaver::commentary_text(tree, wv, state->ap, matter); DISCARD_TEXT(matter) } <>= if (L->category == BEGIN_CODE_LCAT) { state->line_break_pending = FALSE; LanguageMethods::reset_syntax_colouring(S->sect_language); continue; } if (L->category == END_EXTRACT_LCAT) { Weaver::change_material(tree, state, COMMENTARY_MATERIAL, FALSE, NULL, NULL); continue; } TEMPORARY_TEXT(matter) Str::copy(matter, L->text); if (L->is_commentary) <> else <>; DISCARD_TEXT(matter) @ And lastly we ignore commands, or act on them if they happen to be aimed at us; but we don't weave them into the output, that's for sure. <>= if (L->category == COMMAND_LCAT) { if (L->command_code == PAGEBREAK_CMD) Trees::make_child(WeaveTree::pagebreak(tree), state->ap); if (L->command_code == GRAMMAR_INDEX_CMD) Trees::make_child(WeaveTree::grammar_index(tree), state->ap); if (L->command_code == FIGURE_CMD) <>; if (L->command_code == HTML_CMD) <>; if (L->command_code == AUDIO_CMD) <>; if (L->command_code == VIDEO_CMD) <>; if (L->command_code == DOWNLOAD_CMD) <>; if (L->command_code == EMBED_CMD) <>; if (L->command_code == CAROUSEL_CMD) <>; if (L->command_code == CAROUSEL_ABOVE_CMD) <>; if (L->command_code == CAROUSEL_BELOW_CMD) <>; if (L->command_code == CAROUSEL_UNCAPTIONED_CMD) <>; if (L->command_code == CAROUSEL_END_CMD) <>; /* Otherwise assume it was a tangler command, and ignore it here */ continue; } <>= int w, h; text_stream *figname = Parser::dimensions(L->text_operand, &w, &h, L); Trees::make_child(WeaveTree::figure(tree, figname, w, h), state->ap); <>= Trees::make_child(WeaveTree::raw_extract(tree, L->text_operand), state->ap); <>= int w, h; text_stream *figname = Parser::dimensions(L->text_operand, &w, &h, L); Trees::make_child(WeaveTree::audio(tree, figname, w), state->ap); <>= int w, h; text_stream *figname = Parser::dimensions(L->text_operand, &w, &h, L); Trees::make_child(WeaveTree::video(tree, figname, w, h), state->ap); <>= Trees::make_child(WeaveTree::download(tree, L->text_operand, L->text_operand2), state->ap); <>= int w, h; text_stream *ID = Parser::dimensions(L->text_operand2, &w, &h, L); Trees::make_child(WeaveTree::embed(tree, L->text_operand, ID, w, h), state->ap); <>= tree_node *C = WeaveTree::carousel_slide(tree, L->text_operand, L->command_code); Trees::make_child(C, state->para_node); state->ap = C; state->carousel_node = C; <>= state->ap = state->para_node; state->carousel_node = NULL; @ \section{Commentary matter.} Typographically this is a fairly simple business: it's almost the case that we only have to transcribe it. But not quite! <>= <>; <>; <>; <>; <>; WRITE_TO(matter, "\n"); Weaver::commentary_text(tree, wv, state->ap, matter); continue; @ Displayed source is the material marked with [[>>]] arrows in column 1. <>= if (L->category == SOURCE_DISPLAY_LCAT) { Trees::make_child(WeaveTree::display_line(tree, L->text_operand), state->ap); continue; } @ Our style is to use paragraphs without initial-line indentation, so we add a vertical skip between them to show the division more clearly. <>= if (Regexp::string_is_white_space(matter)) { if ((L->next_line) && (L->next_line->category == COMMENT_BODY_LCAT)) { match_results mr = Regexp::create_mr(); if ((state->kind_of_material != CODE_MATERIAL) || (Regexp::match(&mr, matter, L"\t[[(%c*)]](%c*?)"))) Trees::make_child(WeaveTree::vskip(tree, TRUE), state->ap); Regexp::dispose_of(&mr); } continue; } @ Here our extension is simply to provide a tidier way to use TeX's standard [[\item]] and [[\itemitem]] macros for indented list items. <>= match_results mr = Regexp::create_mr(); if (Regexp::match(&mr, matter, L"%(-...%) (%c*)")) { /* continue double */ Weaver::change_material(tree, state, COMMENTARY_MATERIAL, FALSE, NULL, NULL); Trees::make_child(WeaveTree::weave_item_node(tree, 2, I""), state->ap); Str::copy(matter, mr.exp[0]); } else if (Regexp::match(&mr, matter, L"%(...%) (%c*)")) { /* continue single */ Weaver::change_material(tree, state, COMMENTARY_MATERIAL, FALSE, NULL, NULL); Trees::make_child(WeaveTree::weave_item_node(tree, 1, I""), state->ap); Str::copy(matter, mr.exp[0]); } else if (Regexp::match(&mr, matter, L"%(-([a-zA-Z0-9*]+)%) (%c*)")) { /* begin double */ Weaver::change_material(tree, state, COMMENTARY_MATERIAL, FALSE, NULL, NULL); Trees::make_child(WeaveTree::weave_item_node(tree, 2, mr.exp[0]), state->ap); Str::copy(matter, mr.exp[1]); } else if (Regexp::match(&mr, matter, L"%(([a-zA-Z0-9*]+)%) (%c*)")) { /* begin single */ Weaver::change_material(tree, state, COMMENTARY_MATERIAL, FALSE, NULL, NULL); Trees::make_child(WeaveTree::weave_item_node(tree, 1, mr.exp[0]), state->ap); Str::copy(matter, mr.exp[1]); } Regexp::dispose_of(&mr); @ Finally, matter encased in vertical strokes one tab stop in from column 1 in the source is set indented in code style. <>= match_results mr = Regexp::create_mr(); if (Regexp::match(&mr, matter, L"\t[[(%c*)]](%c*?)")) { TEMPORARY_TEXT(original) Weaver::change_material(tree, state, CODE_MATERIAL, FALSE, NULL, NULL); Str::copy(original, mr.exp[0]); Str::copy(matter, mr.exp[1]); TEMPORARY_TEXT(colouring) for (int i=0; iap); TextWeaver::source_code(tree, CL, original, colouring, L->enable_hyperlinks); DISCARD_TEXT(colouring) DISCARD_TEXT(original) Weaver::commentary_text(tree, wv, state->ap, matter); Regexp::dispose_of(&mr); continue; } Regexp::dispose_of(&mr); <>= if (L->category == FOOTNOTE_TEXT_LCAT) { Weaver::change_material(tree, state, FOOTNOTES_MATERIAL, FALSE, NULL, NULL); footnote *F = L->footnote_text; tree_node *FN = WeaveTree::footnote(tree, F->cue_text); Trees::make_child(FN, state->material_node); if (F->cued_already == FALSE) Main::error_in_web(I"footnote never cued", L); state->ap = FN; } @ \section{Code-like matter.} Even though Inweb's approach, unlike [[CWEB]]'s, is to respect the layout of the original, this is still quite typographically complex: commentary and macro usage is rendered differently. <>= <>; <>; Str::rectify_indentation(matter, 4); TEMPORARY_TEXT(prefatory) TEMPORARY_TEXT(concluding_comment) <>; <>; tree_node *CL = WeaveTree::code_line(tree); Trees::make_child(CL, state->ap); if (Str::len(prefatory) > 0) Trees::make_child(WeaveTree::weave_defn_node(tree, prefatory), CL); Str::clear(prefatory); <>; <>; TEMPORARY_TEXT(colouring) LanguageMethods::syntax_colour(S->sect_language, wv, L, matter, colouring); TextWeaver::source_code(tree, CL, matter, colouring, L->enable_hyperlinks); DISCARD_TEXT(colouring) if (Str::len(concluding_comment) > 0) TextWeaver::comment_text_in_code(tree, CL, concluding_comment); DISCARD_TEXT(concluding_comment) DISCARD_TEXT(prefatory) ClumsyLabel: ; @ We're not in Kansas any more, so: <>= if (state->kind_of_material != CODE_MATERIAL) { int will_be = CODE_MATERIAL; if (L->category == MACRO_DEFINITION_LCAT) will_be = MACRO_MATERIAL; else if ((L->category == BEGIN_DEFINITION_LCAT) || (L->category == CONT_DEFINITION_LCAT)) will_be = DEFINITION_MATERIAL; else if ((state->kind_of_material == DEFINITION_MATERIAL) && ((L->category == CODE_BODY_LCAT) || (L->category == COMMENT_BODY_LCAT)) && (Str::len(L->text) == 0)) will_be = DEFINITION_MATERIAL; programming_language *pl = L->colour_as; if (pl == NULL) pl = S->sect_language; if (will_be != CODE_MATERIAL) pl = NULL; theme_tag *T = Tags::find_by_name(I"Preform", FALSE); if ((T) && (Tags::tagged_with(L->owning_paragraph, T))) { programming_language *prepl = Languages::find_by_name(I"Preform", wv->weave_web, FALSE); if (prepl) pl = prepl; } text_stream *note = NULL; if (Str::len(L->extract_to) > 0) { note = Str::new(); WRITE_TO(note, "This is part of the extract file %S.", L->extract_to); } Weaver::change_material(tree, state, will_be, L->plainer, pl, note); state->line_break_pending = FALSE; } @ A blank line is implemented differently in different formats, so it gets a node of its own, a vskip: <>= if (state->line_break_pending) { Trees::make_child(WeaveTree::vskip(tree, FALSE), state->ap); state->line_break_pending = FALSE; } if (Regexp::string_is_white_space(matter)) { state->line_break_pending = TRUE; goto ClumsyLabel; } @ Comments which run to the end of a line can be set in italic type, for example, or flush left. <>= TEMPORARY_TEXT(part_before_comment) TEMPORARY_TEXT(part_within_comment) programming_language *pl = S->sect_language; if (L->category == TEXT_EXTRACT_LCAT) pl = L->colour_as; if ((pl) && (LanguageMethods::parse_comment(pl, matter, part_before_comment, part_within_comment))) { Str::copy(matter, part_before_comment); Str::copy(concluding_comment, part_within_comment); } DISCARD_TEXT(part_before_comment) DISCARD_TEXT(part_within_comment) @ Set the [[@d]] definition escape very slightly more fancily: <>= if (L->category == BEGIN_DEFINITION_LCAT) { match_results mr = Regexp::create_mr(); if ((Regexp::match(&mr, matter, L"@d (%c*)")) || (Regexp::match(&mr, matter, L"@define (%c*)"))) { Str::copy(prefatory, I"define"); Str::copy(matter, mr.exp[0]); } else if (Regexp::match(&mr, matter, L"@default (%c*)")) { Str::copy(prefatory, I"default"); Str::copy(matter, mr.exp[0]); } else if ((Regexp::match(&mr, matter, L"@e (%c*)")) || (Regexp::match(&mr, matter, L"@enum (%c*)"))) { Str::copy(prefatory, I"enum"); Str::copy(matter, mr.exp[0]); } Regexp::dispose_of(&mr); } <>= TEMPORARY_TEXT(OUT) int taken = LanguageMethods::weave_code_line(OUT, S->sect_language, wv, W, C, S, L, matter, concluding_comment); if (taken) { tree_node *V = WeaveTree::verbatim(tree, OUT); Trees::make_child(V, CL); } DISCARD_TEXT(OUT) if (taken) goto ClumsyLabel; <>= match_results mr = Regexp::create_mr(); while (Regexp::match(&mr, matter, L"(%c*?)%@%<(%c*?)%@%>(%c*)")) { para_macro *pmac = Macros::find_by_name(mr.exp[1], S); if (pmac) { TEMPORARY_TEXT(front_colouring) LanguageMethods::syntax_colour(S->sect_language, wv, L, mr.exp[0], front_colouring); TextWeaver::source_code(tree, CL, mr.exp[0], front_colouring, L->enable_hyperlinks); DISCARD_TEXT(front_colouring) Str::copy(matter, mr.exp[2]); int defn = (L->owning_paragraph == pmac->defining_paragraph)?TRUE:FALSE; if (defn) Str::clear(matter); Trees::make_child(WeaveTree::pmac(tree, pmac, defn), CL); } else break; } Regexp::dispose_of(&mr); @ \section{Endnotes.} The endnotes describe function calls from far away, or unexpected structure usage, or how [[CWEB]]-style code substitutions were made. <<*>>= void Weaver::show_endnotes_on_previous_paragraph(heterogeneous_tree *tree, weave_order *wv, tree_node *ap, paragraph *P) { tree_node *body = ap; theme_tag *T = Tags::find_by_name(I"Preform", FALSE); if ((T) && (Tags::tagged_with(P, T))) <>; Tags::show_endnote_on_ifdefs(tree, ap, P); if (P->defines_macro) <>; language_function *fn; LOOP_OVER_LINKED_LIST(fn, language_function, P->functions) <>; language_type *st; LOOP_OVER_LINKED_LIST(st, language_type, P->structures) <>; } <>= tree_node *E = WeaveTree::endnote(tree); Trees::make_child(E, body); ap = E; TextWeaver::commentary_text(tree, ap, I"This is "); TEMPORARY_TEXT(url) int ext = FALSE; if (Colonies::resolve_reference_in_weave(url, NULL, wv->weave_to, I"words: About Preform", wv->weave_web->md, NULL, &ext)) Trees::make_child(WeaveTree::url(tree, url, I"Preform grammar", ext), ap); else TextWeaver::commentary_text(tree, ap, I"Preform grammar"); DISCARD_TEXT(url) TextWeaver::commentary_text(tree, ap, I", not regular C code."); <>= tree_node *E = WeaveTree::endnote(tree); Trees::make_child(E, body); ap = E; TextWeaver::commentary_text(tree, ap, I"This code is "); int ct = 0; macro_usage *mu; LOOP_OVER_LINKED_LIST(mu, macro_usage, P->defines_macro->macro_usages) ct++; if (ct == 1) TextWeaver::commentary_text(tree, ap, I"never used"); else { int k = 0, used_flag = FALSE; LOOP_OVER_LINKED_LIST(mu, macro_usage, P->defines_macro->macro_usages) if (P != mu->used_in_paragraph) { if (used_flag) { if (k < ct-1) TextWeaver::commentary_text(tree, ap, I", "); else TextWeaver::commentary_text(tree, ap, I" and "); } else { TextWeaver::commentary_text(tree, ap, I"used in "); } Trees::make_child(WeaveTree::locale(tree, mu->used_in_paragraph, NULL), ap); used_flag = TRUE; k++; switch (mu->multiplicity) { case 1: break; case 2: TextWeaver::commentary_text(tree, ap, I" (twice)"); break; case 3: TextWeaver::commentary_text(tree, ap, I" (three times)"); break; case 4: TextWeaver::commentary_text(tree, ap, I" (four times)"); break; case 5: TextWeaver::commentary_text(tree, ap, I" (five times)"); break; default: { TEMPORARY_TEXT(mt) WRITE_TO(mt, " (%d times)", mu->multiplicity); TextWeaver::commentary_text(tree, ap, mt); DISCARD_TEXT(mt) break; } } } } TextWeaver::commentary_text(tree, ap, I"."); <>= if (fn->usage_described == FALSE) Weaver::show_function_usage(tree, wv, ap, P, fn, FALSE); <>= tree_node *E = WeaveTree::endnote(tree); Trees::make_child(E, body); ap = E; TextWeaver::commentary_text(tree, ap, I"The structure "); TextWeaver::commentary_text(tree, ap, st->structure_name); section *S; LOOP_OVER(S, section) S->scratch_flag = FALSE; structure_element *elt; LOOP_OVER_LINKED_LIST(elt, structure_element, st->elements) { hash_table_entry *hte = Analyser::find_hash_entry_for_section(elt->element_created_at->owning_section, elt->element_name, FALSE); if (hte) { hash_table_entry_usage *hteu; LOOP_OVER_LINKED_LIST(hteu, hash_table_entry_usage, hte->usages) if (hteu->form_of_usage & ELEMENT_ACCESS_USAGE) hteu->usage_recorded_at->under_section->scratch_flag = TRUE; } } int usage_count = 0, external = 0; LOOP_OVER(S, section) if (S->scratch_flag) { usage_count++; if (S != P->under_section) external++; } if (external == 0) TextWeaver::commentary_text(tree, ap, I" is private to this section"); else { TextWeaver::commentary_text(tree, ap, I" is accessed in "); int c = 0; LOOP_OVER(S, section) if ((S->scratch_flag) && (S != P->under_section)) { if (c++ > 0) TextWeaver::commentary_text(tree, ap, I", "); TextWeaver::commentary_text(tree, ap, S->md->sect_range); } if (P->under_section->scratch_flag) TextWeaver::commentary_text(tree, ap, I" and here"); } TextWeaver::commentary_text(tree, ap, I"."); <<*>>= void Weaver::show_function_usage(heterogeneous_tree *tree, weave_order *wv, tree_node *ap, paragraph *P, language_function *fn, int as_list) { tree_node *body = ap; fn->usage_described = TRUE; hash_table_entry *hte = Analyser::find_hash_entry_for_section(fn->function_header_at->owning_section, fn->function_name, FALSE); if (as_list == FALSE) { tree_node *E = WeaveTree::endnote(tree); Trees::make_child(E, body); ap = E; TextWeaver::commentary_text(tree, ap, I"The function "); TextWeaver::commentary_text(tree, ap, fn->function_name); } int used_flag = FALSE; hash_table_entry_usage *hteu = NULL; section *last_cited_in = NULL; int count_under = 0; LOOP_OVER_LINKED_LIST(hteu, hash_table_entry_usage, hte->usages) if ((P != hteu->usage_recorded_at) && (P->under_section == hteu->usage_recorded_at->under_section)) <>; LOOP_OVER_LINKED_LIST(hteu, hash_table_entry_usage, hte->usages) if (P->under_section != hteu->usage_recorded_at->under_section) <>; if (used_flag == FALSE) { if (as_list == FALSE) { TextWeaver::commentary_text(tree, ap, I" appears nowhere else"); } else { TextWeaver::commentary_text(tree, ap, I"none"); } } if (as_list == FALSE) { if ((last_cited_in != P->under_section) && (last_cited_in)) TextWeaver::commentary_text(tree, ap, I")"); TextWeaver::commentary_text(tree, ap, I"."); } } <>= if (as_list == FALSE) { if (used_flag == FALSE) TextWeaver::commentary_text(tree, ap, I" is used in "); } used_flag = TRUE; section *S = hteu->usage_recorded_at->under_section; if ((S != last_cited_in) && (S != P->under_section)) { count_under = 0; if (last_cited_in) { if (as_list == FALSE) { if (last_cited_in != P->under_section) TextWeaver::commentary_text(tree, ap, I"), "); else TextWeaver::commentary_text(tree, ap, I", "); } else { Trees::make_child(WeaveTree::linebreak(tree), ap); } } TextWeaver::commentary_text(tree, ap, hteu->usage_recorded_at->under_section->md->sect_title); if (as_list == FALSE) TextWeaver::commentary_text(tree, ap, I" ("); else TextWeaver::commentary_text(tree, ap, I" - "); } if (count_under++ > 0) TextWeaver::commentary_text(tree, ap, I", "); Trees::make_child(WeaveTree::locale(tree, hteu->usage_recorded_at, NULL), ap); last_cited_in = hteu->usage_recorded_at->under_section; @ \section{Non-paragraph subheadings.} <<*>>= void Weaver::weave_subheading(heterogeneous_tree *tree, weave_order *wv, tree_node *ap, text_stream *text) { tree_node *D = WeaveTree::subheading(tree, text); Trees::make_child(D, ap); } void Weaver::change_material(heterogeneous_tree *tree, weaver_state *state, int new_material, int plainly, programming_language *pl, text_stream *note) { if (state->kind_of_material != new_material) { tree_node *D = WeaveTree::material(tree, new_material, plainly, pl, note); if (state->carousel_node) Trees::make_child(D, state->carousel_node); else Trees::make_child(D, state->para_node); state->material_node = D; state->ap = D; state->kind_of_material = new_material; } } void Weaver::change_material_for_para(heterogeneous_tree *tree, weaver_state *state) { tree_node *D = WeaveTree::material(tree, COMMENTARY_MATERIAL, FALSE, NULL, NULL); Trees::make_child(D, state->para_node); state->material_node = D; state->ap = D; state->kind_of_material = COMMENTARY_MATERIAL; } void Weaver::figure(heterogeneous_tree *tree, weave_order *wv, tree_node *ap, text_stream *figname, int w, int h) { tree_node *F = WeaveTree::figure(tree, figname, w, h); Trees::make_child(F, ap); } void Weaver::commentary_text(heterogeneous_tree *tree, weave_order *wv, tree_node *ap, text_stream *matter) { TextWeaver::commentary_text(tree, ap, matter); } @ \section{Section tables of contents.} These appear at the top of each woven section, and give links to the paragraphs marked as [[@h]] headings. <<*>>= int Weaver::weave_table_of_contents(heterogeneous_tree *tree, tree_node *ap, section *S) { int noteworthy = 0; paragraph *P; LOOP_OVER_LINKED_LIST(P, paragraph, S->paragraphs) if ((P->weight > 0) && ((S->barred == FALSE) || (P->above_bar == FALSE))) noteworthy++; if (noteworthy == 0) return FALSE; tree_node *TOC = WeaveTree::table_of_contents(tree, S->md->sect_range); Trees::make_child(TOC, ap); LOOP_OVER_LINKED_LIST(P, paragraph, S->paragraphs) if ((P->weight > 0) && ((S->barred == FALSE) || (P->above_bar == FALSE))) { TEMPORARY_TEXT(loc) WRITE_TO(loc, "%S%S", P->ornament, P->paragraph_number); Trees::make_child( WeaveTree::contents_line(tree, loc, P->first_line_in_paragraph->text_operand, P), TOC); DISCARD_TEXT(loc) } return TRUE; }