inweb-bootstrap/Chapter_3/The_Weaver.nw

825 lines
30 KiB
Text

[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;
<<Start the weaver with a clean slate>>;
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)) {
<<Weave any necessary chapter header>>;
<<Weave any necessary section header>>;
LanguageMethods::begin_weave(S, wv);
<<Weave this section>>;
<<Weave any necessary section footer>>;
}
}
<<Weave any necessary chapter footer>>;
return lines_woven;
}
<<Weave any necessary chapter header>>=
if (last_heading != C) {
<<Weave any necessary chapter footer>>;
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);
}
}
<<Weave any necessary chapter footer>>=
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);
}
}
<<Weave any necessary section header>>=
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);
}
<<Weave any necessary section footer>>=
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;
<<Start the weaver with a clean slate>>=
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.}
<<Weave this 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)
<<Largely ignore this extra-mural line>>
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))
<<Weave this paragraph>>;
}
}
<<Largely ignore this extra-mural line>>=
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;
<<Weave this paragraph>>=
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 */
<<Deal with the marker for the start of a new paragraph, section or chapter>>;
<<Weave any regular commentary text after the heading on the same line>>;
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++;
<<Respond to any commands aimed at the weaver, and otherwise skip commands>>;
<<Weave this line>>;
}
}
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.}
<<Deal with the marker for the start of a new paragraph, section or chapter>>=
LanguageMethods::reset_syntax_colouring(S->sect_language);
if (wv->theme_match) <<Apply special rules for thematic extracts>>;
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:
<<Apply special rules for thematic extracts>>=
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:
<<Weave any regular commentary text after the heading on the same line>>=
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)
}
<<Weave this line>>=
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) <<Weave verbatim matter in commentary style>>
else <<Weave verbatim matter in code style>>;
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.
<<Respond to any commands aimed at the weaver, and otherwise skip commands>>=
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) <<Weave a figure>>;
if (L->command_code == HTML_CMD) <<Weave a raw HTML extract>>;
if (L->command_code == AUDIO_CMD) <<Weave an audio clip>>;
if (L->command_code == VIDEO_CMD) <<Weave a video clip>>;
if (L->command_code == DOWNLOAD_CMD) <<Weave a download>>;
if (L->command_code == EMBED_CMD) <<Weave an embed>>;
if (L->command_code == CAROUSEL_CMD) <<Weave a carousel>>;
if (L->command_code == CAROUSEL_ABOVE_CMD) <<Weave a carousel>>;
if (L->command_code == CAROUSEL_BELOW_CMD) <<Weave a carousel>>;
if (L->command_code == CAROUSEL_UNCAPTIONED_CMD) <<Weave a carousel>>;
if (L->command_code == CAROUSEL_END_CMD) <<Weave a carousel end>>;
/* Otherwise assume it was a tangler command, and ignore it here */
continue;
}
<<Weave a figure>>=
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);
<<Weave a raw HTML extract>>=
Trees::make_child(WeaveTree::raw_extract(tree, L->text_operand),
state->ap);
<<Weave an audio clip>>=
int w, h;
text_stream *figname = Parser::dimensions(L->text_operand, &w, &h, L);
Trees::make_child(WeaveTree::audio(tree, figname, w), state->ap);
<<Weave a video clip>>=
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);
<<Weave a download>>=
Trees::make_child(WeaveTree::download(tree, L->text_operand, L->text_operand2),
state->ap);
<<Weave an embed>>=
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);
<<Weave a carousel>>=
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;
<<Weave a carousel end>>=
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!
<<Weave verbatim matter in commentary style>>=
<<Weave displayed source in its own special style>>;
<<Weave a blank line as a thin vertical skip and paragraph break>>;
<<Weave bracketed list indications at start of line into items>>;
<<Weave tabbed code material as a new indented paragraph>>;
<<Weave footnotes>>;
WRITE_TO(matter, "\n");
Weaver::commentary_text(tree, wv, state->ap, matter);
continue;
@ Displayed source is the material marked with [[>>]] arrows in column 1.
<<Weave displayed source in its own special style>>=
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.
<<Weave a blank line as a thin vertical skip and paragraph break>>=
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.
<<Weave bracketed list indications at start of line into 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.
<<Weave tabbed code material as a new indented paragraph>>=
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; i<Str::len(original); i++) PUT_TO(colouring, PLAIN_COLOUR);
tree_node *CL = WeaveTree::code_line(tree);
Trees::make_child(CL, state->ap);
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);
<<Weave footnotes>>=
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.
<<Weave verbatim matter in code style>>=
<<Change material if necessary>>;
<<Weave a blank line as a thin vertical skip>>;
Str::rectify_indentation(matter, 4);
TEMPORARY_TEXT(prefatory)
TEMPORARY_TEXT(concluding_comment)
<<Extract any comment matter ending the line to be set in italic>>;
<<Give constant definition lines slightly fancier openings>>;
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);
<<Offer the line to the language to weave>>;
<<Find macro usages and adjust syntax colouring accordingly>>;
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:
<<Change material if necessary>>=
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:
<<Weave a blank line as a thin vertical skip>>=
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.
<<Extract any comment matter ending the line to be set in italic>>=
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:
<<Give constant definition lines slightly fancier openings>>=
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);
}
<<Offer the line to the language to weave>>=
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;
<<Find macro usages and adjust syntax colouring accordingly>>=
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)))
<<Show endnote on use of Preform>>;
Tags::show_endnote_on_ifdefs(tree, ap, P);
if (P->defines_macro)
<<Show endnote on where paragraph macro is used>>;
language_function *fn;
LOOP_OVER_LINKED_LIST(fn, language_function, P->functions)
<<Show endnote on where this function is used>>;
language_type *st;
LOOP_OVER_LINKED_LIST(st, language_type, P->structures)
<<Show endnote on where this language type is accessed>>;
}
<<Show endnote on use of Preform>>=
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.");
<<Show endnote on where paragraph macro is used>>=
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".");
<<Show endnote on where this function is used>>=
if (fn->usage_described == FALSE)
Weaver::show_function_usage(tree, wv, ap, P, fn, FALSE);
<<Show endnote on where this language type is accessed>>=
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))
<<Cite usage of function here>>;
LOOP_OVER_LINKED_LIST(hteu, hash_table_entry_usage, hte->usages)
if (P->under_section != hteu->usage_recorded_at->under_section)
<<Cite usage of function here>>;
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".");
}
}
<<Cite usage of function here>>=
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;
}