Chapter 3: Nowebify.

This commit is contained in:
AwesomeAdam54321 2024-03-09 14:17:52 +08:00
parent cf990fc08d
commit 82d8056b15
5 changed files with 374 additions and 365 deletions

View file

@ -3,11 +3,11 @@
Here we analyse the code in the web, enabling us to see how functions
and data structures are used within the program.
@h Scanning webs.
@ \section{Scanning webs.}
This scanner is intended for debugging Inweb, and simply shows the main
result of reading in and parsing the web:
=
<<*>>=
void Analyser::scan_line_categories(web *W, text_stream *range) {
PRINT("Scan of source lines for '%S'\n", range);
int count = 1;
@ -16,39 +16,40 @@ void Analyser::scan_line_categories(web *W, text_stream *range) {
section *S;
LOOP_OVER_LINKED_LIST(S, section, C->sections)
for (source_line *L = S->first_line; L; L = L->next_line)
@<Trace the content and category of this source line@>;
<<Trace the content and category of this source line>>;
} else {
section *S = Reader::get_section_for_range(W, range);
if (S) {
for (source_line *L = S->first_line; L; L = L->next_line)
@<Trace the content and category of this source line@>
<<Trace the content and category of this source line>>
} else {
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)
@<Trace the content and category of this source line@>;
<<Trace the content and category of this source line>>;
}
}
}
@<Trace the content and category of this source line@> =
<<Trace the content and category of this source line>>=
TEMPORARY_TEXT(C)
WRITE_TO(C, "%s", Lines::category_name(L->category));
while (Str::len(C) < 20) PUT_TO(C, '.');
PRINT("%07d %S %S\n", count++, C, L->text);
DISCARD_TEXT(C)
@h The section catalogue.
@ \section{The section catalogue.}
This provides quite a useful overview of the sections. As we'll see frequently
in Chapter 4, we call out to a general routine in Chapter 5 to provide
annotations which are programming-language specific; the aim is to abstract
so that Chapter 4 contains no assumptions about the language.
@enum BASIC_SECTIONCAT from 1
@enum STRUCTURES_SECTIONCAT
@enum FUNCTIONS_SECTIONCAT
<<*>>=
enum BASIC_SECTIONCAT from 1
enum STRUCTURES_SECTIONCAT
enum FUNCTIONS_SECTIONCAT
=
<<*>>=
void Analyser::catalogue_the_sections(web *W, text_stream *range, int form) {
int max_width = 0, max_range_width = 0;
chapter *C;
@ -79,7 +80,7 @@ void Analyser::catalogue_the_sections(web *W, text_stream *range, int form) {
}
}
@h Analysing code.
@ \section{Analysing code.}
We can't pretend to a full-scale static analysis of the code -- for one thing,
that would mean knowing more about the syntax of the web's language than we
actually do. So the following provides only a toolkit which other code can
@ -91,35 +92,36 @@ punctuation around them.
Usage codes are used to define a set of allowed contexts in which to spot
these identifiers.
@d ELEMENT_ACCESS_USAGE 0x00000001 /* C-like languages: access via |->| or |.| operators to structure element */
@d FCALL_USAGE 0x00000002 /* C-like languages: function call made using brackets, |name(args)| */
@d PREFORM_IN_CODE_USAGE 0x00000004 /* InC only: use of a Preform nonterminal as a C "constant" */
@d PREFORM_IN_GRAMMAR_USAGE 0x00000008 /* InC only: ditto, but within Preform production rather than C code */
@d MISC_USAGE 0x00000010 /* any other appearance as an identifier */
@d ANY_USAGE 0x7fffffff /* any of the above */
<<*>>
#define ELEMENT_ACCESS_USAGE 0x00000001 /* C-like languages: access via [[->| or |.]] operators to structure element */
#define FCALL_USAGE 0x00000002 /* C-like languages: function call made using brackets, [[name(args)]] */
#define PREFORM_IN_CODE_USAGE 0x00000004 /* InC only: use of a Preform nonterminal as a C "constant" */
#define PREFORM_IN_GRAMMAR_USAGE 0x00000008 /* InC only: ditto, but within Preform production rather than C code */
#define MISC_USAGE 0x00000010 /* any other appearance as an identifier */
#define ANY_USAGE 0x7fffffff /* any of the above */
@ The main analysis routine goes through a web as follows. Note that we only
perform the search here, we don't comment on the results; any action to be
taken must be handled by |LanguageMethods::late_preweave_analysis| when we're done.
taken must be handled by [[LanguageMethods::late_preweave_analysis]] when we're done.
=
<<*>>=
void Analyser::analyse_code(web *W) {
if (W->analysed) return;
@<Ask language-specific code to identify search targets, and parse the Interfaces@>;
<<Ask language-specific code to identify search targets, and parse the Interfaces>>;
chapter *C;
section *S;
LOOP_WITHIN_TANGLE(C, S, Tangler::primary_target(W))
switch (L->category) {
case BEGIN_DEFINITION_LCAT:
@<Perform analysis on the body of the definition@>;
<<Perform analysis on the body of the definition>>;
break;
case CODE_BODY_LCAT:
@<Perform analysis on a typical line of code@>;
<<Perform analysis on a typical line of code>>;
break;
case PREFORM_GRAMMAR_LCAT:
@<Perform analysis on productions in a Preform grammar@>;
<<Perform analysis on productions in a Preform grammar>>;
break;
}
@ -133,11 +135,11 @@ below) to look for names of particular functions it knows about.
In Version 1 webs, this code is also expected to parse any Interface lines in
a section which it recognises, marking those by setting their
|interface_line_identified| flags. Any that are left must be erroneous.
[[interface_line_identified]] flags. Any that are left must be erroneous.
Version 2 removed Interface altogeter as being cumbersome for no real gain in
practice.
@<Ask language-specific code to identify search targets, and parse the Interfaces@> =
<<Ask language-specific code to identify search targets, and parse the Interfaces>>=
LanguageMethods::early_preweave_analysis(W->main_language, W);
chapter *C;
@ -148,10 +150,10 @@ practice.
(Regexp::string_is_white_space(L->text) == FALSE))
Main::error_in_web(I"unrecognised interface line", L);
@<Perform analysis on a typical line of code@> =
<<Perform analysis on a typical line of code>>=
Analyser::analyse_as_code(W, L, L->text, ANY_USAGE, 0);
@<Perform analysis on the body of the definition@> =
<<Perform analysis on the body of the definition>>=
Analyser::analyse_as_code(W, L, L->text_operand2, ANY_USAGE, 0);
while ((L->next_line) && (L->next_line->category == CONT_DEFINITION_LCAT)) {
L = L->next_line;
@ -159,29 +161,29 @@ practice.
}
@ Lines in a Preform grammar generally take the form of some BNF grammar, where
we want only to identify any nonterminals mentioned, then a |==>| divider,
we want only to identify any nonterminals mentioned, then a [[==>]] divider,
and then some C code to deal with a match. The code is subjected to analysis
just as any other code would be.
@<Perform analysis on productions in a Preform grammar@> =
<<Perform analysis on productions in a Preform grammar>>=
Analyser::analyse_as_code(W, L, L->text_operand2,
ANY_USAGE, 0);
Analyser::analyse_as_code(W, L, L->text_operand,
PREFORM_IN_CODE_USAGE, PREFORM_IN_GRAMMAR_USAGE);
@h Identifier searching.
Here's what we actually do, then. We take the code fragment |text|, drawn
from part or all of source line |L| from web |W|, and look for any identifier
names used in one of the contexts in the bitmap |mask|. Any that we find are
passed to |Analyser::analyse_find|, along with the context they were found in (or, if
|transf| is nonzero, with |transf| as their context).
@ \section{Identifier searching.}
Here's what we actually do, then. We take the code fragment [[text]], drawn
from part or all of source line [[L]] from web [[W]], and look for any identifier
names used in one of the contexts in the bitmap [[mask]]. Any that we find are
passed to [[Analyser::analyse_find]], along with the context they were found in (or, if
[[transf]] is nonzero, with [[transf]] as their context).
What we do is to look for instances of an identifier, defined as a maximal
string of |%i| characters or hyphens not followed by |>| characters. (Thus
|fish-or-chips| counts, but |fish-| is not an identifier when it occurs in
|fish->bone|.)
string of [[%i]] characters or hyphens not followed by [[>]] characters. (Thus
[[fish-or-chips]] counts, but [[fish-]] is not an identifier when it occurs in
[[fish->bone]].)
=
<<*>>=
void Analyser::analyse_as_code(web *W, source_line *L, text_stream *text, int mask, int transf) {
int start_at = -1, element_follows = FALSE;
for (int i = 0; i < Str::len(text); i++) {
@ -189,7 +191,7 @@ void Analyser::analyse_as_code(web *W, source_line *L, text_stream *text, int ma
((Str::get_at(text, i) == '-') && (Str::get_at(text, i+1) != '>'))) {
if (start_at == -1) start_at = i;
} else {
if (start_at != -1) @<Found an identifier@>;
if (start_at != -1) <<Found an identifier>>;
if (Str::get_at(text, i) == '.') element_follows = TRUE;
else if ((Str::get_at(text, i) == '-') && (Str::get_at(text, i+1) == '>')) {
element_follows = TRUE; i++;
@ -198,11 +200,11 @@ void Analyser::analyse_as_code(web *W, source_line *L, text_stream *text, int ma
}
if (start_at != -1) {
int i = Str::len(text);
@<Found an identifier@>;
<<Found an identifier>>;
}
}
@<Found an identifier@> =
<<Found an identifier>>=
int u = MISC_USAGE;
if (element_follows) u = ELEMENT_ACCESS_USAGE;
else if (Str::get_at(text, i) == '(') u = FCALL_USAGE;
@ -218,17 +220,18 @@ void Analyser::analyse_as_code(web *W, source_line *L, text_stream *text, int ma
}
start_at = -1; element_follows = FALSE;
@h The identifier hash table.
@ \section{The identifier hash table.}
We clearly need rapid access to a large symbols table, and we store this as
a hash. Identifiers are hash-coded with the following simple code, which is
simplified from one used by Inform; it's the algorithm called "X 30011"
in Aho, Sethi and Ullman, "Compilers: Principles, Techniques and Tools"
(1986), adapted slightly to separate out literal numbers.
@d HASH_TAB_SIZE 1000 /* the possible hash codes are 0 up to this minus 1 */
@d NUMBER_HASH 0 /* literal decimal integers, and no other words, have this hash code */
<<*>>=
#define HASH_TAB_SIZE 1000 /* the possible hash codes are 0 up to this minus 1 */
#define NUMBER_HASH 0 /* literal decimal integers, and no other words, have this hash code */
=
<<*>>=
int Analyser::hash_code_from_word(text_stream *text) {
unsigned int hash_code = 0;
string_position p = Str::start(text);
@ -251,11 +254,12 @@ int Analyser::hash_code_from_word(text_stream *text) {
@ The actual table is stored here:
@d HASH_SAFETY_CODE 0x31415927
<<*>>=
#define HASH_SAFETY_CODE 0x31415927
=
<<*>>=
typedef struct hash_table {
struct linked_list *analysis_hash[HASH_TAB_SIZE]; /* of |hash_table_entry| */
struct linked_list *analysis_hash[HASH_TAB_SIZE]; /* of [[hash_table_entry]] */
int safety_code; /* when we start up, array's contents are undefined, so... */
} hash_table;
@ -266,11 +270,11 @@ void Analyser::initialise_hash_table(hash_table *HT) {
@ Where we define:
=
<<*>>=
typedef struct hash_table_entry {
text_stream *hash_key;
int language_reserved_word; /* in the language currently being woven, that is */
struct linked_list *usages; /* of |hash_table_entry_usage| */
struct linked_list *usages; /* of [[hash_table_entry_usage]] */
struct source_line *definition_line; /* or null, if it's not a constant, function or type name */
struct language_function *as_function; /* for function names only */
CLASS_DEFINITION
@ -281,7 +285,7 @@ in it, as usual with symbols tables. For example, the code to handle C-like
languages prepares for code analysis by calling this routine on the name
of each C function.
=
<<*>>=
hash_table_entry *Analyser::find_hash_entry(hash_table *HT, text_stream *text, int create) {
int h = Analyser::hash_code_from_word(text);
if (h == NUMBER_HASH) return NULL;
@ -316,7 +320,7 @@ hash_table_entry *Analyser::find_hash_entry_for_section(section *S, text_stream
@ Marking and testing these bits:
=
<<*>>=
hash_table_entry *Analyser::mark_reserved_word(hash_table *HT, text_stream *p, int e) {
hash_table_entry *hte = Analyser::find_hash_entry(HT, p, TRUE);
hte->language_reserved_word |= (1 << (e % 32));
@ -366,16 +370,16 @@ but that it can be used in multiple ways within that paragraph: for example,
a function might be both called and used as a constant value within the
same paragraph of code.
=
<<*>>=
typedef struct hash_table_entry_usage {
struct paragraph *usage_recorded_at;
int form_of_usage; /* bitmap of the |*_USAGE| constants defined above */
int form_of_usage; /* bitmap of the [[*_USAGE]] constants defined above */
CLASS_DEFINITION
} hash_table_entry_usage;
@ And here's how we create these usages:
=
<<*>>=
void Analyser::analyse_find(web *W, source_line *L, text_stream *identifier, int u) {
hash_table_entry *hte =
Analyser::find_hash_entry_for_section(L->owning_section, identifier, FALSE);
@ -394,11 +398,11 @@ void Analyser::analyse_find(web *W, source_line *L, text_stream *identifier, int
hteu->form_of_usage |= u;
}
@h Open-source project support.
@ \section{Open-source project support.}
The work here is all delegated. In each case we look for a script in the web's
folder: failing that, we fall back on a default script belonging to Inweb.
=
<<*>>=
void Analyser::write_makefile(web *W, filename *F, module_search *I, text_stream *platform) {
pathname *P = W->md->path_to_web;
text_stream *short_name = Pathnames::directory_name(P);

View file

@ -2,7 +2,7 @@
To collate material generated by the weaver into finished, fully-woven files.
@h Collation.
@ \section{Collation.}
This is the process of reading a template file, substituting material into
placeholders in it, and writing the result.
@ -15,7 +15,7 @@ directly.
For convenience, we provide three ways to call:
=
<<*>>=
void Collater::for_web_and_pattern(text_stream *OUT, web *W,
weave_pattern *pattern, filename *F, filename *into) {
Collater::collate(OUT, W, I"", F, pattern, NULL, NULL, NULL, into);
@ -39,12 +39,13 @@ void Collater::collate(text_stream *OUT, web *W, text_stream *range,
@ The current state of the processor is recorded in the following.
@d TRACE_COLLATER_EXECUTION FALSE /* set true for debugging */
<<*>>=
#define TRACE_COLLATER_EXECUTION FALSE /* set true for debugging */
@d MAX_TEMPLATE_LINES 8192 /* maximum number of lines in template */
@d CI_STACK_CAPACITY 8 /* maximum recursion of chapter/section iteration */
#define MAX_TEMPLATE_LINES 8192 /* maximum number of lines in template */
#define CI_STACK_CAPACITY 8 /* maximum recursion of chapter/section iteration */
=
<<*>>=
typedef struct collater_state {
struct web *for_web;
struct text_stream *tlines[MAX_TEMPLATE_LINES];
@ -62,14 +63,14 @@ typedef struct collater_state {
struct filename *errors_at;
struct weave_order *wv;
struct filename *into_file;
struct linked_list *modules; /* of |module| */
struct linked_list *modules; /* of [[module]] */
} collater_state;
@ Note the unfortunate maximum size limit on the template file. It means
that really humungous Javascript files in plugins might have trouble, though
if so, they can always be subdivided.
=
<<*>>=
collater_state Collater::initial_state(web *W, text_stream *range,
filename *template_filename, weave_pattern *pattern, filename *nav_file,
linked_list *crumbs, weave_order *wv, filename *into) {
@ -88,13 +89,13 @@ collater_state Collater::initial_state(web *W, text_stream *range,
cls.modules = NEW_LINKED_LIST(module);
if (W) {
int c = LinkedLists::len(W->md->as_module->dependencies);
if (c > 0) @<Form the list of imported modules@>;
if (c > 0) <<Form the list of imported modules>>;
}
@<Read in the source file containing the contents page template@>;
<<Read in the source file containing the contents page template>>;
return cls;
}
@<Form the list of imported modules@> =
<<Form the list of imported modules>>=
module **module_array =
Memory::calloc(c, sizeof(module *), ARRAY_SORTING_MREASON);
module *M; int d=0;
@ -105,7 +106,7 @@ collater_state Collater::initial_state(web *W, text_stream *range,
for (int d=0; d<c; d++) ADD_TO_LINKED_LIST(module_array[d], module, cls.modules);
Memory::I7_free(module_array, ARRAY_SORTING_MREASON, c*((int) sizeof(module *)));
@<Read in the source file containing the contents page template@> =
<<Read in the source file containing the contents page template>>=
TextFiles::read(template_filename, FALSE,
"can't find contents template", TRUE, Collater::temp_line, NULL, &cls);
if (TRACE_COLLATER_EXECUTION)
@ -114,7 +115,7 @@ collater_state Collater::initial_state(web *W, text_stream *range,
PRINT("Warning: template <%f> truncated after %d line(s)\n",
template_filename, cls.no_tlines);
@ =
<<*>>=
void Collater::temp_line(text_stream *line, text_file_position *tfp, void *v_ies) {
collater_state *cls = (collater_state *) v_ies;
if (cls->no_tlines < MAX_TEMPLATE_LINES)
@ -123,14 +124,14 @@ void Collater::temp_line(text_stream *line, text_file_position *tfp, void *v_ies
@ Running the engine...
=
<<*>>=
void Collater::process(text_stream *OUT, collater_state *cls) {
int lpos = 0; /* This is our program counter: a line number in the template */
while (lpos < cls->no_tlines) {
match_results mr = Regexp::create_mr();
TEMPORARY_TEXT(tl)
Str::copy(tl, cls->tlines[lpos++]); /* Fetch the line at the program counter and advance */
@<Make any necessary substitutions to turn tl into final output@>;
<<Make any necessary substitutions to turn tl into final output>>;
WRITE("%S\n", tl); /* Copy the now finished line to the output */
DISCARD_TEXT(tl)
CYCLE: ;
@ -140,29 +141,29 @@ void Collater::process(text_stream *OUT, collater_state *cls) {
cls->inside_navigation_submenu = FALSE;
}
@<Make any necessary substitutions to turn tl into final output@> =
<<Make any necessary substitutions to turn tl into final output>>=
if (Regexp::match(&mr, tl, L"(%c*?) ")) Str::copy(tl, mr.exp[0]); /* Strip trailing spaces */
if (TRACE_COLLATER_EXECUTION)
@<Print line and contents of repeat stack@>;
<<Print line and contents of repeat stack>>;
if ((Regexp::match(&mr, tl, L"%[%[(%c+)%]%]")) ||
(Regexp::match(&mr, tl, L" %[%[(%c+)%]%]"))) {
TEMPORARY_TEXT(command)
Str::copy(command, mr.exp[0]);
@<Deal with a Select command@>;
@<Deal with an If command@>;
@<Deal with an Else command@>;
@<Deal with a Repeat command@>;
@<Deal with a Repeat End command@>;
<<Deal with a Select command>>;
<<Deal with an If command>>;
<<Deal with an Else command>>;
<<Deal with a Repeat command>>;
<<Deal with a Repeat End command>>;
DISCARD_TEXT(command)
}
@<Skip line if inside a failed conditional@>;
@<Skip line if inside an empty loop@>;
@<Make substitutions of square-bracketed variables in line@>;
<<Skip line if inside a failed conditional>>;
<<Skip line if inside an empty loop>>;
<<Make substitutions of square-bracketed variables in line>>;
@h The repeat stack and loops.
@ \section{The repeat stack and loops.}
This is used only for debugging:
@<Print line and contents of repeat stack@> =
<<Print line and contents of repeat stack>>=
PRINT("%04d: %S\nStack:", lpos-1, tl);
for (int j=0; j<cls->sp; j++) {
if (cls->repeat_stack_level[j] == CHAPTER_LEVEL)
@ -184,7 +185,7 @@ This is used only for debugging:
one-iteration loop in which the loop variable has the given section or
chapter as its value during the sole iteration.
@<Deal with a Select command@> =
<<Deal with a Select command>>=
match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, command, L"Select (%c*)")) {
chapter *C;
@ -210,7 +211,7 @@ chapter as its value during the sole iteration.
@ Conditionals:
@<Deal with an If command@> =
<<Deal with an If command>>=
if (Regexp::match(&mr, command, L"If (%c*)")) {
text_stream *condition = mr.exp[0];
int level = IF_FALSE_LEVEL;
@ -253,7 +254,7 @@ chapter as its value during the sole iteration.
goto CYCLE;
}
@<Deal with an Else command@> =
<<Deal with an Else command>>=
if (Regexp::match(&mr, command, L"Else")) {
if (cls->sp <= 0) {
Errors::at_position("Else without If",
@ -275,7 +276,7 @@ chapter as its value during the sole iteration.
@ Next, a genuine loop beginning:
@<Deal with a Repeat command@> =
<<Deal with a Repeat command>>=
int loop_level = 0;
if (Regexp::match(&mr, command, L"Repeat Module")) loop_level = MODULE_LEVEL;
if (Regexp::match(&mr, command, L"Repeat Chapter")) loop_level = CHAPTER_LEVEL;
@ -285,18 +286,18 @@ chapter as its value during the sole iteration.
linked_list_item *CI = FIRST_ITEM_IN_LINKED_LIST(chapter, cls->for_web->chapters);
while ((CI) && (CONTENT_IN_ITEM(CI, chapter)->md->imported))
CI = NEXT_ITEM_IN_LINKED_LIST(CI, chapter);
if (loop_level == MODULE_LEVEL) @<Begin a module repeat@>;
if (loop_level == CHAPTER_LEVEL) @<Begin a chapter repeat@>;
if (loop_level == SECTION_LEVEL) @<Begin a section repeat@>;
if (loop_level == MODULE_LEVEL) <<Begin a module repeat>>;
if (loop_level == CHAPTER_LEVEL) <<Begin a chapter repeat>>;
if (loop_level == SECTION_LEVEL) <<Begin a section repeat>>;
Collater::start_CI_loop(cls, loop_level, from, to, lpos);
goto CYCLE;
}
@<Begin a module repeat@> =
<<Begin a module repeat>>=
from = FIRST_ITEM_IN_LINKED_LIST(module, cls->modules);
to = LAST_ITEM_IN_LINKED_LIST(module, cls->modules);
@<Begin a chapter repeat@> =
<<Begin a chapter repeat>>=
from = CI;
to = LAST_ITEM_IN_LINKED_LIST(chapter, cls->for_web->chapters);
if (Str::eq_wide_string(cls->restrict_to_range, L"0") == FALSE) {
@ -308,7 +309,7 @@ chapter as its value during the sole iteration.
}
}
@<Begin a section repeat@> =
<<Begin a section repeat>>=
chapter *within_chapter =
CONTENT_IN_ITEM(Collater::heading_topmost_on_stack(cls, CHAPTER_LEVEL),
chapter);
@ -326,7 +327,7 @@ chapter as its value during the sole iteration.
@ And at the other bookend:
@<Deal with a Repeat End command@> =
<<Deal with a Repeat End command>>=
int end_form = -1;
if (Regexp::match(&mr, command, L"End Repeat")) end_form = 1;
if (Regexp::match(&mr, command, L"End Select")) end_form = 2;
@ -357,16 +358,16 @@ chapter as its value during the sole iteration.
break;
}
switch (cls->repeat_stack_level[cls->sp-1]) {
case MODULE_LEVEL: @<End a module repeat@>; break;
case CHAPTER_LEVEL: @<End a chapter repeat@>; break;
case SECTION_LEVEL: @<End a section repeat@>; break;
case IF_TRUE_LEVEL: @<End an If@>; break;
case IF_FALSE_LEVEL: @<End an If@>; break;
case MODULE_LEVEL: <<End a module repeat>>; break;
case CHAPTER_LEVEL: <<End a chapter repeat>>; break;
case SECTION_LEVEL: <<End a section repeat>>; break;
case IF_TRUE_LEVEL: <<End an If>>; break;
case IF_FALSE_LEVEL: <<End an If>>; break;
}
goto CYCLE;
}
@<End a module repeat@> =
<<End a module repeat>>=
linked_list_item *CI = cls->repeat_stack_variable[cls->sp-1];
if (CI == cls->repeat_stack_threshold[cls->sp-1])
Collater::end_CI_loop(cls);
@ -376,7 +377,7 @@ chapter as its value during the sole iteration.
lpos = cls->repeat_stack_startpos[cls->sp-1]; /* Back round loop */
}
@<End a chapter repeat@> =
<<End a chapter repeat>>=
linked_list_item *CI = cls->repeat_stack_variable[cls->sp-1];
if (CI == cls->repeat_stack_threshold[cls->sp-1])
Collater::end_CI_loop(cls);
@ -386,7 +387,7 @@ chapter as its value during the sole iteration.
lpos = cls->repeat_stack_startpos[cls->sp-1]; /* Back round loop */
}
@<End a section repeat@> =
<<End a section repeat>>=
linked_list_item *SI = cls->repeat_stack_variable[cls->sp-1];
if ((SI == cls->repeat_stack_threshold[cls->sp-1]) ||
(NEXT_ITEM_IN_LINKED_LIST(SI, section) == NULL))
@ -397,12 +398,12 @@ chapter as its value during the sole iteration.
lpos = cls->repeat_stack_startpos[cls->sp-1]; /* Back round loop */
}
@<End an If@> =
<<End an If>>=
Collater::end_CI_loop(cls);
@ It can happen that a section loop, at least, is empty:
@<Skip line if inside an empty loop@> =
<<Skip line if inside an empty loop>>=
for (int rstl = cls->sp-1; rstl >= 0; rstl--)
if (cls->repeat_stack_level[cls->sp-1] == SECTION_LEVEL) {
linked_list_item *SI = cls->repeat_stack_threshold[cls->sp-1];
@ -411,15 +412,15 @@ chapter as its value during the sole iteration.
goto CYCLE;
}
@<Skip line if inside a failed conditional@> =
<<Skip line if inside a failed conditional>>=
for (int j=cls->sp-1; j>=0; j--)
if (cls->repeat_stack_level[j] == IF_FALSE_LEVEL)
goto CYCLE;
@ If called with the non-conditional levels, the following function returns
the topmost item. It's never called for |IF_TRUE_LEVEL| or |IF_FALSE_LEVEL|.
the topmost item. It's never called for [[IF_TRUE_LEVEL|]]or [[IF_FALSE_LEVEL]].
=
<<*>>=
linked_list_item *Collater::heading_topmost_on_stack(collater_state *cls, int level) {
for (int rstl = cls->sp-1; rstl >= 0; rstl--)
if (cls->repeat_stack_level[rstl] == level)
@ -430,13 +431,14 @@ linked_list_item *Collater::heading_topmost_on_stack(collater_state *cls, int le
@ This is the function for starting a loop or code block, which stacks up the
details, and similarly for ending it by popping them again:
@d MODULE_LEVEL 1
@d CHAPTER_LEVEL 2
@d SECTION_LEVEL 3
@d IF_TRUE_LEVEL 4
@d IF_FALSE_LEVEL 5
<<*>>=
#define MODULE_LEVEL 1
#define CHAPTER_LEVEL 2
#define SECTION_LEVEL 3
#define IF_TRUE_LEVEL 4
#define IF_FALSE_LEVEL 5
=
<<*>>=
void Collater::start_CI_loop(collater_state *cls, int level,
linked_list_item *from, linked_list_item *to, int pos) {
if (cls->sp < CI_STACK_CAPACITY) {
@ -451,17 +453,17 @@ void Collater::end_CI_loop(collater_state *cls) {
cls->sp--;
}
@h Variable substitutions.
@ \section{Variable substitutions.}
We can now forget about this tiny stack machine: the one task left is to
take a line from the template, and make substitutions of variables into
its square-bracketed parts.
Note that we do not allow this to recurse, i.e., if |[[X]]| substitutes into
text which itself contains a |[[...]]| notation, then we do not expand that
inner one. If we did, then the value of the bibliographic variable |[[Code]]|,
Note that we do not allow this to recurse, i.e., if [[[[X]]]] substitutes into
text which itself contains a [[[[...]]]] notation, then we do not expand that
inner one. If we did, then the value of the bibliographic variable [[[[Code]]]],
used by the HTML renderer, would cause a modest-sized explosion on some pages.
@<Make substitutions of square-bracketed variables in line@> =
<<Make substitutions of square-bracketed variables in line>>=
TEMPORARY_TEXT(rewritten)
int slen, spos;
while ((spos = Regexp::find_expansion(tl, '[', '[', ']', ']', &slen)) >= 0) {
@ -474,50 +476,50 @@ used by the HTML renderer, would cause a modest-sized explosion on some pages.
match_results mr = Regexp::create_mr();
if (Bibliographic::data_exists(cls->for_web->md, varname)) {
@<Substitute any bibliographic datum named@>;
<<Substitute any bibliographic datum named>>;
} else if (Regexp::match(&mr, varname, L"Navigation")) {
@<Substitute Navigation@>;
<<Substitute Navigation>>;
} else if (Regexp::match(&mr, varname, L"Breadcrumbs")) {
@<Substitute Breadcrumbs@>;
<<Substitute Breadcrumbs>>;
} else if (Str::eq_wide_string(varname, L"Plugins")) {
@<Substitute Plugins@>;
<<Substitute Plugins>>;
} else if (Regexp::match(&mr, varname, L"Complete (%c+)")) {
text_stream *detail = mr.exp[0];
@<Substitute a detail about the complete PDF@>;
<<Substitute a detail about the complete PDF>>;
} else if (Regexp::match(&mr, varname, L"Module (%c+)")) {
text_stream *detail = mr.exp[0];
@<Substitute a Module@>;
<<Substitute a Module>>;
} else if (Regexp::match(&mr, varname, L"Chapter (%c+)")) {
text_stream *detail = mr.exp[0];
@<Substitute a Chapter@>;
<<Substitute a Chapter>>;
} else if (Regexp::match(&mr, varname, L"Section (%c+)")) {
text_stream *detail = mr.exp[0];
@<Substitute a Section@>;
<<Substitute a Section>>;
} else if (Regexp::match(&mr, varname, L"Docs")) {
@<Substitute a Docs@>;
<<Substitute a Docs>>;
} else if (Regexp::match(&mr, varname, L"Assets")) {
@<Substitute an Assets@>;
<<Substitute an Assets>>;
} else if (Regexp::match(&mr, varname, L"URL \"(%c+)\"")) {
text_stream *link_text = mr.exp[0];
@<Substitute a URL@>;
<<Substitute a URL>>;
} else if (Regexp::match(&mr, varname, L"Link \"(%c+)\"")) {
text_stream *link_text = mr.exp[0];
@<Substitute a Link@>;
<<Substitute a Link>>;
} else if (Regexp::match(&mr, varname, L"Menu \"(%c+)\"")) {
text_stream *menu_name = mr.exp[0];
@<Substitute a Menu@>;
<<Substitute a Menu>>;
} else if (Regexp::match(&mr, varname, L"Item \"(%c+)\"")) {
text_stream *item_name = mr.exp[0];
text_stream *icon_text = NULL;
@<Look for icon text@>;
<<Look for icon text>>;
text_stream *link_text = item_name;
@<Substitute a member Item@>;
<<Substitute a member Item>>;
} else if (Regexp::match(&mr, varname, L"Item \"(%c+)\" -> (%c+)")) {
text_stream *item_name = mr.exp[0];
text_stream *link_text = mr.exp[1];
text_stream *icon_text = NULL;
@<Look for icon text@>;
@<Substitute a general Item@>;
<<Look for icon text>>;
<<Substitute a general Item>>;
} else {
WRITE_TO(substituted, "%S", varname);
if (Regexp::match(&mr, varname, L"%i+%c*"))
@ -535,15 +537,15 @@ used by the HTML renderer, would cause a modest-sized explosion on some pages.
Str::clear(tl); Str::copy(tl, rewritten);
DISCARD_TEXT(rewritten)
@ This is why, for instance, |[[Author]]| is replaced by the author's name:
@ This is why, for instance, [[[[Author]]]] is replaced by the author's name:
@<Substitute any bibliographic datum named@> =
<<Substitute any bibliographic datum named>>=
WRITE_TO(substituted, "%S", Bibliographic::get_datum(cls->for_web->md, varname));
@ |[[Navigation]]| substitutes to the content of the sidebar navigation file;
@ [[[[Navigation]]]] substitutes to the content of the sidebar navigation file;
this will recursively call The Collater, in fact.
@<Substitute Navigation@> =
<<Substitute Navigation>>=
if (cls->nav_file) {
if (TextFiles::exists(cls->nav_file))
Collater::collate(substituted, cls->for_web, cls->restrict_to_range,
@ -556,17 +558,17 @@ this will recursively call The Collater, in fact.
@ A trail of breadcrumbs, used for overhead navigation in web pages.
@<Substitute Breadcrumbs@> =
<<Substitute Breadcrumbs>>=
Colonies::drop_initial_breadcrumbs(substituted, cls->into_file,
cls->crumbs);
@<Substitute Plugins@> =
<<Substitute Plugins>>=
Assets::include_relevant_plugins(OUT, cls->nav_pattern, cls->for_web,
cls->wv, cls->into_file);
@ We store little about the complete-web-in-one-file PDF:
@<Substitute a detail about the complete PDF@> =
<<Substitute a detail about the complete PDF>>=
if (swarm_leader)
if (Formats::substitute_post_processing_data(substituted,
swarm_leader, detail, cls->nav_pattern) == FALSE)
@ -574,15 +576,15 @@ this will recursively call The Collater, in fact.
@ And here for Modules:
@<Substitute a Module@> =
<<Substitute a Module>>=
module *M = CONTENT_IN_ITEM(
Collater::heading_topmost_on_stack(cls, MODULE_LEVEL), module);
if (M == NULL)
Errors::at_position("no module is currently selected",
cls->errors_at, lpos);
else @<Substitute a detail about the currently selected Module@>;
else <<Substitute a detail about the currently selected Module>>;
@<Substitute a detail about the currently selected Module@> =
<<Substitute a detail about the currently selected Module>>=
if (Str::eq_wide_string(detail, L"Title")) {
text_stream *owner = Collater::module_owner(M, cls->for_web);
if (Str::len(owner) > 0) WRITE_TO(substituted, "%S/", owner);
@ -601,15 +603,15 @@ this will recursively call The Collater, in fact.
@ And here for Chapters:
@<Substitute a Chapter@> =
<<Substitute a Chapter>>=
chapter *C = CONTENT_IN_ITEM(
Collater::heading_topmost_on_stack(cls, CHAPTER_LEVEL), chapter);
if (C == NULL)
Errors::at_position("no chapter is currently selected",
cls->errors_at, lpos);
else @<Substitute a detail about the currently selected Chapter@>;
else <<Substitute a detail about the currently selected Chapter>>;
@<Substitute a detail about the currently selected Chapter@> =
<<Substitute a detail about the currently selected Chapter>>=
if (Str::eq_wide_string(detail, L"Title")) {
Str::copy(substituted, C->md->ch_title);
} else if (Str::eq_wide_string(detail, L"Code")) {
@ -625,15 +627,15 @@ this will recursively call The Collater, in fact.
@ And this is a very similar construction for Sections.
@<Substitute a Section@> =
<<Substitute a Section>>=
section *S = CONTENT_IN_ITEM(
Collater::heading_topmost_on_stack(cls, SECTION_LEVEL), section);
if (S == NULL)
Errors::at_position("no section is currently selected",
cls->errors_at, lpos);
else @<Substitute a detail about the currently selected Section@>;
else <<Substitute a detail about the currently selected Section>>;
@<Substitute a detail about the currently selected Section@> =
<<Substitute a detail about the currently selected Section>>=
if (Str::eq_wide_string(detail, L"Title")) {
Str::copy(substituted, S->md->sect_title);
} else if (Str::eq_wide_string(detail, L"Purpose")) {
@ -662,33 +664,33 @@ this will recursively call The Collater, in fact.
@ These commands are all used in constructing relative URLs, especially for
navigation purposes.
@<Substitute a Docs@> =
<<Substitute a Docs>>=
Pathnames::relative_URL(substituted,
Filenames::up(cls->into_file),
Pathnames::from_text(Colonies::home()));
@<Substitute an Assets@> =
<<Substitute an Assets>>=
pathname *P = Colonies::assets_path();
if (P == NULL) P = Filenames::up(cls->into_file);
Pathnames::relative_URL(substituted,
Filenames::up(cls->into_file), P);
@<Substitute a URL@> =
<<Substitute a URL>>=
Pathnames::relative_URL(substituted,
Filenames::up(cls->into_file),
Pathnames::from_text(link_text));
@<Substitute a Link@> =
<<Substitute a Link>>=
WRITE_TO(substituted, "<a href=\"");
Colonies::reference_URL(substituted, link_text, cls->into_file);
WRITE_TO(substituted, "\">");
@<Substitute a Menu@> =
<<Substitute a Menu>>=
if (cls->inside_navigation_submenu) WRITE_TO(substituted, "</ul>");
WRITE_TO(substituted, "<h2>%S</h2><ul>", menu_name);
cls->inside_navigation_submenu = TRUE;
@<Look for icon text@> =
<<Look for icon text>>=
match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, item_name, L"<(%i+.%i+)> *(%c*)")) {
icon_text = Str::duplicate(mr.exp[0]);
@ -699,40 +701,40 @@ navigation purposes.
}
Regexp::dispose_of(&mr);
@<Substitute a member Item@> =
<<Substitute a member Item>>=
TEMPORARY_TEXT(url)
Colonies::reference_URL(url, link_text, cls->into_file);
@<Substitute an item at this URL@>;
<<Substitute an item at this URL>>;
DISCARD_TEXT(url)
@<Substitute a general Item@> =
<<Substitute a general Item>>=
TEMPORARY_TEXT(url)
Colonies::link_URL(url, link_text, cls->into_file);
@<Substitute an item at this URL@>;
<<Substitute an item at this URL>>;
DISCARD_TEXT(url)
@<Substitute an item at this URL@> =
<<Substitute an item at this URL>>=
if (cls->inside_navigation_submenu == FALSE) WRITE_TO(substituted, "<ul>");
cls->inside_navigation_submenu = TRUE;
WRITE_TO(substituted, "<li>");
if (Str::eq(url, Filenames::get_leafname(cls->into_file))) {
WRITE_TO(substituted, "<span class=\"unlink\">");
@<Substitute icon and name@>;
<<Substitute icon and name>>;
WRITE_TO(substituted, "</span>");
} else if (Str::eq(url, I"index.html")) {
WRITE_TO(substituted, "<a href=\"%S\">", url);
WRITE_TO(substituted, "<span class=\"selectedlink\">");
@<Substitute icon and name@>;
<<Substitute icon and name>>;
WRITE_TO(substituted, "</span>");
WRITE_TO(substituted, "</a>");
} else {
WRITE_TO(substituted, "<a href=\"%S\">", url);
@<Substitute icon and name@>;
<<Substitute icon and name>>;
WRITE_TO(substituted, "</a>");
}
WRITE_TO(substituted, "</li>");
@<Substitute icon and name@> =
<<Substitute icon and name>>=
if (Str::len(icon_text) > 0) {
WRITE_TO(substituted, "<img src=\"");
pathname *I = Colonies::assets_path();
@ -743,10 +745,10 @@ navigation purposes.
}
WRITE_TO(substituted, "%S", item_name);
@ This is a utility for finding the owner of a module, returning |NULL| (the
empty text) if it appears to belong to the current web |W|.
@ This is a utility for finding the owner of a module, returning [[NULL]] (the
empty text) if it appears to belong to the current web [[W]].
=
<<*>>=
text_stream *Collater::module_owner(const module *M, web *W) {
text_stream *owner =
Pathnames::directory_name(Pathnames::up(M->module_location));
@ -761,7 +763,7 @@ text_stream *Collater::module_owner(const module *M, web *W) {
top, then all other owners, in alphabetical order, and then last of all Inweb,
so that //foundation// will always be at the bottom.
=
<<*>>=
web *sorting_web = NULL;
void Collater::sort_web(web *W) {
sorting_web = W;

View file

@ -3,14 +3,14 @@
To transcribe a version of the text in the web into a form which
can be compiled as a program.
@h The Master Tangler.
Here's what has happened so far, on a |-tangle| run of Inweb: on any
@ \section{The Master Tangler.}
Here's what has happened so far, on a [[-tangle]] 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 then fully parsed, with all
of the arrays and hashes populated. Program Control then sent us straight
here for the tangling to begin...
=
<<*>>=
void Tangler::tangle(web *W, tangle_target *target, filename *dest_file) {
programming_language *lang = target->tangle_language;
PRINT(" tangling <%/f> (written in %S)\n", dest_file, lang->language_name);
@ -19,24 +19,25 @@ void Tangler::tangle(web *W, tangle_target *target, filename *dest_file) {
text_stream *OUT = &TO_struct;
if (STREAM_OPEN_TO_FILE(OUT, dest_file, ISO_ENC) == FALSE)
Errors::fatal_with_file("unable to write tangled file", dest_file);
@<Perform the actual tangle@>;
<<Perform the actual tangle>>;
STREAM_CLOSE(OUT);
@<Tangle any imported headers@>;
@<Tangle any extract files not part of the target itself@>;
<<Tangle any imported headers>>;
<<Tangle any extract files not part of the target itself>>;
LanguageMethods::additional_tangling(lang, W, target);
}
@ All of the sections are tangled together into one big file, the structure
of which can be seen below.
@d LOOP_OVER_PARAGRAPHS(C, S, T, P)
<<*>>=
#define LOOP_OVER_PARAGRAPHS(C, S, T, P)
LOOP_OVER_LINKED_LIST(C, chapter, W->chapters)
LOOP_OVER_LINKED_LIST(S, section, C->sections)
if (S->sect_target == T)
LOOP_OVER_LINKED_LIST(P, paragraph, S->paragraphs)
@<Perform the actual tangle@> =
<<Perform the actual tangle>>=
/* (a) The shebang line, a header for scripting languages, and other heading matter */
LanguageMethods::shebang(OUT, lang, W, target);
LanguageMethods::disclaimer(OUT, lang, W, target);
@ -46,8 +47,8 @@ of which can be seen below.
if ((P->placed_very_early) && (P->defines_macro == NULL))
Tangler::tangle_paragraph(OUT, P);
/* (b) Results of |@d| declarations */
@<Tangle all the constant definitions in section order@>;
/* (b) Results of [[@d]] declarations */
<<Tangle all the constant definitions in section order>>;
/* (c) Miscellaneous automated C predeclarations */
LanguageMethods::additional_predeclarations(OUT, lang, W);
@ -65,26 +66,26 @@ of which can be seen below.
/* (f) Opposite of the shebang: a footer */
LanguageMethods::gnabehs(OUT, lang, W);
@ This is the result of all those |@d| definitions; note that these sometimes
@ This is the result of all those [[@d]] definitions; note that these sometimes
extend across multiple lines.
@<Tangle all the constant definitions in section order@> =
<<Tangle all the constant definitions in section order>>=
chapter *C;
section *S;
LOOP_WITHIN_TANGLE(C, S, target)
if (L->category == BEGIN_DEFINITION_LCAT)
if (L->default_defn == FALSE)
@<Define the constant@>;
<<Define the constant>>;
LOOP_WITHIN_TANGLE(C, S, target)
if (L->category == BEGIN_DEFINITION_LCAT)
if (L->default_defn) {
LanguageMethods::open_ifdef(OUT, lang, L->text_operand, FALSE);
@<Define the constant@>;
<<Define the constant>>;
LanguageMethods::close_ifdef(OUT, lang, L->text_operand, FALSE);
}
Enumerations::define_extents(OUT, target, lang);
@<Define the constant@> =
<<Define the constant>>=
if (L->owning_paragraph == NULL) Main::error_in_web(I"misplaced definition", L);
else Tags::open_ifdefs(OUT, L->owning_paragraph);
LanguageMethods::start_definition(OUT, lang,
@ -97,7 +98,7 @@ extend across multiple lines.
LanguageMethods::end_definition(OUT, lang, S, L);
if (L->owning_paragraph) Tags::close_ifdefs(OUT, L->owning_paragraph);
@<Tangle any imported headers@> =
<<Tangle any imported headers>>=
filename *F;
LOOP_OVER_LINKED_LIST(F, filename, W->headers)
Shell::copy(F, Reader::tangled_folder(W), "");
@ -106,9 +107,10 @@ extend across multiple lines.
code, or configuration gobbledegook) marked as "to ...", giving a leafname.
We place files of those leafnames in the same directory as the tangle target.
@d MAX_EXTRACT_FILES 10
<<*>>=
#define MAX_EXTRACT_FILES 10
@<Tangle any extract files not part of the target itself@> =
<<Tangle any extract files not part of the target itself>>=
text_stream *extract_names[MAX_EXTRACT_FILES];
text_stream extract_files[MAX_EXTRACT_FILES];
int no_extract_files = 0;
@ -134,23 +136,23 @@ We place files of those leafnames in the same directory as the tangle target.
for (int i=0; i<no_extract_files; i++) STREAM_CLOSE(&(extract_files[i]));
@ So here is the main tangler for a single paragraph. We basically expect to
act only on |CODE_BODY_LCAT| lines (those containing actual code), unless
act only on [[CODE_BODY_LCAT]] lines (those containing actual code), unless
something quirky has been done to support a language feature.
=
<<*>>=
void Tangler::tangle_paragraph(OUTPUT_STREAM, paragraph *P) {
Tags::open_ifdefs(OUT, P);
int contiguous = FALSE;
for (source_line *L = P->first_line_in_paragraph;
((L) && (L->owning_paragraph == P)); L = L->next_line) {
if (LanguageMethods::will_insert_in_tangle(P->under_section->sect_language, L)) {
@<Insert line marker if necessary to show the origin of this code@>;
<<Insert line marker if necessary to show the origin of this code>>;
LanguageMethods::insert_in_tangle(OUT, P->under_section->sect_language, L);
}
if ((L->category != CODE_BODY_LCAT) || (L->suppress_tangling)) {
contiguous = FALSE;
} else {
@<Insert line marker if necessary to show the origin of this code@>;
<<Insert line marker if necessary to show the origin of this code>>;
Tangler::tangle_line(OUT, L->text, P->under_section, L); WRITE("\n");
}
}
@ -162,49 +164,49 @@ from many different origins. Some programming languages (C, for instance)
support a notation to tell the compiler that code has come from somewhere
else; if so, here's where we use it.
@<Insert line marker if necessary to show the origin of this code@> =
<<Insert line marker if necessary to show the origin of this code>>=
if (contiguous == FALSE) {
contiguous = TRUE;
LanguageMethods::insert_line_marker(OUT, P->under_section->sect_language, L);
}
@h The Code Tangler.
@ \section{The Code Tangler.}
All of the final tangled code passes through the following routine.
Almost all of the time, it simply prints |original| verbatim to the file |OUT|.
Almost all of the time, it simply prints [[original]] verbatim to the file [[OUT]].
=
<<*>>=
void Tangler::tangle_line(OUTPUT_STREAM, text_stream *original, section *S, source_line *L) {
int mlen, slen;
int mpos = Regexp::find_expansion(original, '@', '<', '@', '>', &mlen);
int spos = Regexp::find_expansion(original, '[', '[', ']', ']', &slen);
if ((mpos >= 0) && ((spos == -1) || (mpos <= spos)) &&
(LanguageMethods::allow_expansion(S->sect_language, original)))
@<Expand a paragraph macro@>
<<Expand a paragraph macro>>
else if (spos >= 0)
@<Expand a double-square command@>
<<Expand a double-square command>>
else
LanguageMethods::tangle_line(OUT, S->sect_language, original); /* this is usually what happens */
}
@ The first form of escape is a paragraph macro in the middle of code. For
example, we handle
= (text)
if (banana_count == 0) @<Yes, we have no bananas@>;
=
by calling the lower-level tangler on |if (banana_count == 0) | (a substring
if (banana_count == 0) <<Yes, we have no bananas>>;
by calling the lower-level tangler on [[if (banana_count == 0) ]] (a substring
which we know can't involve any macros, since we are detecting macros from
left to right, and this is to the left of the one we found); then by tangling
the definition of "Yes, we have no bananas"; then by calling the upper-level
code tangler on |;|. (In this case, of course, there's nothing much there,
code tangler on [[;]]. (In this case, of course, there's nothing much there,
but in principle it could contain further macros.)
Note that when we've expanded "Yes, we have no bananas" we have certainly
placed code into the tangled file from a different location; that will insert
a |#line| marker for the definition location; and we don't want the eventual
a [[#line]] marker for the definition location; and we don't want the eventual
C compiler to think that the code which follows is also from that location.
So we insert a fresh line marker.
@<Expand a paragraph macro@> =
<<Expand a paragraph macro>>=
TEMPORARY_TEXT(temp)
Str::copy(temp, original); Str::truncate(temp, mpos);
LanguageMethods::tangle_line(OUT, S->sect_language, temp);
@ -230,21 +232,21 @@ So we insert a fresh line marker.
DISCARD_TEXT(temp)
@ This is a similar matter, except that it expands bibliographic data:
= (text)
printf("This is build [[Build Number]].\n");
=
takes the bibliographic data for "Build Number" (as set on the web's contents
page) and substitutes that, so that we end up with (say)
= (text as C)
printf("This is build 5Q47.\n");
=
In some languages there are also special expansions (for example, in
InC |[[nonterminals]]| has a special meaning).
InC [[[[nonterminals]]]] has a special meaning).
If the text in double-squares isn't recognised, that's not an error: it simply
passes straight through. So |[[water]]| becomes just |[[water]]|.
passes straight through. So [[[[water]]]] becomes just [[[[water]]]].
@<Expand a double-square command@> =
<<Expand a double-square command>>=
web *W = S->owning_web;
TEMPORARY_TEXT(temp)
@ -266,10 +268,10 @@ passes straight through. So |[[water]]| becomes just |[[water]]|.
DISCARD_TEXT(rest)
DISCARD_TEXT(temp)
@h Prinary target.
@ \section{Prinary target.}
The first target in a web is always the one for the main program.
=
<<*>>=
tangle_target *Tangler::primary_target(web *W) {
if (W == NULL) internal_error("no such web");
return FIRST_IN_LINKED_LIST(tangle_target, W->tangle_targets);

View file

@ -2,7 +2,7 @@
To weave a portion of the code into instructions for TeX.
@h The Master Weaver.
@ \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
@ -10,9 +10,9 @@ 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.
[[Weaver::weave]] below.
=
<<*>>=
int Weaver::weave(weave_order *wv) {
heterogeneous_tree *tree = WeaveTree::new_tree(wv);
TEMPORARY_TEXT(banner)
@ -38,32 +38,32 @@ int Weaver::weave(weave_order *wv) {
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@>;
<<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@>;
<<Weave any necessary chapter header>>;
<<Weave any necessary section header>>;
LanguageMethods::begin_weave(S, wv);
@<Weave this section@>;
@<Weave any necessary section footer@>;
<<Weave this section>>;
<<Weave any necessary section footer>>;
}
}
@<Weave any necessary chapter footer@>;
<<Weave any necessary chapter footer>>;
return lines_woven;
}
@<Weave any necessary chapter header@> =
<<Weave any necessary chapter header>>=
if (last_heading != C) {
@<Weave any necessary chapter footer@>;
<<Weave any necessary chapter footer>>;
tree_node *CH = WeaveTree::chapter(tree, C);
Trees::make_child(CH, state->body_node);
state->chapter_node = CH;
@ -75,7 +75,7 @@ int Weaver::weave_inner(weave_order *wv, heterogeneous_tree *tree, tree_node *bo
}
}
@<Weave any necessary chapter footer@> =
<<Weave any necessary chapter footer>>=
if (wv->theme_match == NULL) {
if (last_heading != NULL) {
tree_node *F = WeaveTree::chapter_footer(tree, last_heading);
@ -83,7 +83,7 @@ int Weaver::weave_inner(weave_order *wv, heterogeneous_tree *tree, tree_node *bo
}
}
@<Weave any necessary section header@> =
<<Weave any necessary section header>>=
tree_node *SH = WeaveTree::section(tree, S);
Trees::make_child(SH, state->chapter_node);
state->section_node = SH;
@ -93,25 +93,26 @@ int Weaver::weave_inner(weave_order *wv, heterogeneous_tree *tree, tree_node *bo
Trees::make_child(H, state->section_node);
}
@<Weave any necessary section footer@> =
<<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);
}
@h The state.
@ \section{The state.}
We can now begin on a clean page, by initialising the state of the weaver:
@e COMMENTARY_MATERIAL from 1
@e MACRO_MATERIAL /* when a macro is being defined... */
@e DEFINITION_MATERIAL /* ...versus when an |@d| definition is being made */
@e CODE_MATERIAL /* verbatim code */
@e ENDNOTES_MATERIAL /* endnotes at the foot of a paragraph */
@e FOOTNOTES_MATERIAL /* footnote texts for a paragraph */
<<*>>=
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 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;
@ -125,7 +126,7 @@ typedef struct weaver_state {
struct tree_node *ap;
} weaver_state;
@<Start the weaver with a clean slate@> =
<<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;
@ -139,15 +140,15 @@ typedef struct weaver_state {
state->material_node = NULL;
state->ap = body;
@h Weaving a section.
@ \section{Weaving a section.}
@<Weave this 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@>
<<Largely ignore this extra-mural line>>
else if (LLL->owning_paragraph != current_P) {
if (toc_made == FALSE) {
if (Str::len(S->sect_purpose) > 0) {
@ -159,11 +160,11 @@ typedef struct weaver_state {
}
current_P = LLL->owning_paragraph;
if (Tags::tagged_with(current_P, wv->theme_match))
@<Weave this paragraph@>;
<<Weave this paragraph>>;
}
}
@<Largely ignore this extra-mural line@> =
<<Largely ignore this extra-mural line>>=
if (LLL->category == INTERFACE_BODY_LCAT) {
state->horizontal_rule_just_drawn = FALSE;
continue;
@ -190,7 +191,7 @@ typedef struct weaver_state {
(LLL->category == SECTION_HEADING_LCAT))
continue;
@<Weave this paragraph@> =
<<Weave this paragraph>>=
if (current_P->starts_on_new_page)
Trees::make_child(WeaveTree::pagebreak(tree), state->ap);
source_line *L = LLL;
@ -198,27 +199,27 @@ typedef struct weaver_state {
(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@>;
<<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@>;
<<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@>;
<<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);
@h How paragraphs begin.
@ \section{How paragraphs begin.}
@<Deal with the marker for the start of a new paragraph, section or chapter@> =
<<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@>;
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);
@ -229,7 +230,7 @@ typedef struct weaver_state {
@ 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@> =
<<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);
@ -244,7 +245,7 @@ about breaking pages at chapters and sections fail to work. So:
@ 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@> =
<<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);
@ -252,7 +253,7 @@ about breaking pages at chapters and sections fail to work. So:
DISCARD_TEXT(matter)
}
@<Weave this line@> =
<<Weave this line>>=
if (L->category == BEGIN_CODE_LCAT) {
state->line_break_pending = FALSE;
LanguageMethods::reset_syntax_colouring(S->sect_language);
@ -265,89 +266,89 @@ about breaking pages at chapters and sections fail to work. So:
}
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@>;
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@> =
<<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@>;
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@> =
<<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@> =
<<Weave a raw HTML extract>>=
Trees::make_child(WeaveTree::raw_extract(tree, L->text_operand),
state->ap);
@<Weave an audio clip@> =
<<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@> =
<<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@> =
<<Weave a download>>=
Trees::make_child(WeaveTree::download(tree, L->text_operand, L->text_operand2),
state->ap);
@<Weave an embed@> =
<<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@> =
<<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@> =
<<Weave a carousel end>>=
state->ap = state->para_node;
state->carousel_node = NULL;
@h Commentary matter.
@ \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@>;
<<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.
@ Displayed source is the material marked with [[>>]] arrows in column 1.
@<Weave displayed source in its own special style@> =
<<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;
@ -356,12 +357,12 @@ we only have to transcribe it. But not quite!
@ 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@> =
<<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*?)")))
(Regexp::match(&mr, matter, L"\t[[(%c*)]](%c*?)")))
Trees::make_child(WeaveTree::vskip(tree, TRUE), state->ap);
Regexp::dispose_of(&mr);
}
@ -369,9 +370,9 @@ add a vertical skip between them to show the division more clearly.
}
@ Here our extension is simply to provide a tidier way to use TeX's standard
|\item| and |\itemitem| macros for indented list items.
[[\item]] and [[\itemitem]] macros for indented list items.
@<Weave bracketed list indications at start of line into 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);
@ -395,9 +396,9 @@ add a vertical skip between them to show the division more clearly.
@ 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@> =
<<Weave tabbed code material as a new indented paragraph>>=
match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, matter, L"\t|(%c*)|(%c*?)")) {
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]);
@ -415,7 +416,7 @@ in the source is set indented in code style.
}
Regexp::dispose_of(&mr);
@<Weave footnotes@> =
<<Weave footnotes>>=
if (L->category == FOOTNOTE_TEXT_LCAT) {
Weaver::change_material(tree, state, FOOTNOTES_MATERIAL, FALSE, NULL, NULL);
footnote *F = L->footnote_text;
@ -425,21 +426,21 @@ in the source is set indented in code style.
state->ap = FN;
}
@h Code-like matter.
Even though Inweb's approach, unlike |CWEB|'s, is to respect the layout
@ \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@>;
<<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@>;
<<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);
@ -447,9 +448,9 @@ and macro usage is rendered differently.
Trees::make_child(WeaveTree::weave_defn_node(tree, prefatory), CL);
Str::clear(prefatory);
@<Offer the line to the language to weave@>;
<<Offer the line to the language to weave>>;
@<Find macro usages and adjust syntax colouring accordingly@>;
<<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);
@ -464,7 +465,7 @@ and macro usage is rendered differently.
@ We're not in Kansas any more, so:
@<Change material if necessary@> =
<<Change material if necessary>>=
if (state->kind_of_material != CODE_MATERIAL) {
int will_be = CODE_MATERIAL;
if (L->category == MACRO_DEFINITION_LCAT)
@ -497,7 +498,7 @@ and macro usage is rendered differently.
@ 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@> =
<<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;
@ -510,7 +511,7 @@ a node of its own, a vskip:
@ 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@> =
<<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;
@ -523,9 +524,9 @@ example, or flush left.
DISCARD_TEXT(part_before_comment)
DISCARD_TEXT(part_within_comment)
@ Set the |@d| definition escape very slightly more fancily:
@ Set the [[@d]] definition escape very slightly more fancily:
@<Give constant definition lines slightly fancier openings@> =
<<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*)")) ||
@ -543,7 +544,7 @@ example, or flush left.
Regexp::dispose_of(&mr);
}
@<Offer the line to the language to weave@> =
<<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);
@ -554,7 +555,7 @@ example, or flush left.
DISCARD_TEXT(OUT)
if (taken) goto ClumsyLabel;
@<Find macro usages and adjust syntax colouring accordingly@> =
<<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);
@ -571,29 +572,29 @@ example, or flush left.
}
Regexp::dispose_of(&mr);
@h Endnotes.
@ \section{Endnotes.}
The endnotes describe function calls from far away, or unexpected
structure usage, or how |CWEB|-style code substitutions were made.
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@>;
<<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@>;
<<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@>;
<<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 where this language type is accessed>>;
}
@<Show endnote on use of Preform@> =
<<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 ");
@ -607,7 +608,7 @@ void Weaver::show_endnotes_on_previous_paragraph(heterogeneous_tree *tree,
DISCARD_TEXT(url)
TextWeaver::commentary_text(tree, ap, I", not regular C code.");
@<Show endnote on where paragraph macro is used@> =
<<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 ");
@ -646,11 +647,11 @@ void Weaver::show_endnotes_on_previous_paragraph(heterogeneous_tree *tree,
}
TextWeaver::commentary_text(tree, ap, I".");
@<Show endnote on where this function is used@> =
<<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@> =
<<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 ");
@ -690,7 +691,7 @@ void Weaver::show_endnotes_on_previous_paragraph(heterogeneous_tree *tree,
}
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;
@ -711,10 +712,10 @@ void Weaver::show_function_usage(heterogeneous_tree *tree, weave_order *wv,
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@>;
<<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@>;
<<Cite usage of function here>>;
if (used_flag == FALSE) {
if (as_list == FALSE) {
TextWeaver::commentary_text(tree, ap, I" appears nowhere else");
@ -729,7 +730,7 @@ void Weaver::show_function_usage(heterogeneous_tree *tree, weave_order *wv,
}
}
@<Cite usage of function here@> =
<<Cite usage of function here>>=
if (as_list == FALSE) {
if (used_flag == FALSE) TextWeaver::commentary_text(tree, ap, I" is used in ");
}
@ -753,9 +754,9 @@ void Weaver::show_function_usage(heterogeneous_tree *tree, weave_order *wv,
Trees::make_child(WeaveTree::locale(tree, hteu->usage_recorded_at, NULL), ap);
last_cited_in = hteu->usage_recorded_at->under_section;
@h Non-paragraph subheadings.
@ \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);
@ -794,11 +795,11 @@ void Weaver::commentary_text(heterogeneous_tree *tree, weave_order *wv,
TextWeaver::commentary_text(tree, ap, matter);
}
@h Section tables of contents.
@ \section{Section tables of contents.}
These appear at the top of each woven section, and give links to the paragraphs
marked as |@h| headings.
marked as [[@h]] headings.
=
<<*>>=
int Weaver::weave_table_of_contents(heterogeneous_tree *tree,
tree_node *ap, section *S) {
int noteworthy = 0;

View file

@ -2,13 +2,13 @@
To manage the weaving of commentary or source code text.
@h Commentary text.
@ \section{Commentary text.}
The following takes text, divides it up at stroke-mark boundaries --
that is, |this is inside|, this is outside -- and sends contiguous pieces
of it either to |TextWeaver::inline_code_fragment| or |TextWeaver::commentary_fragment|
that is, [[this is inside]], this is outside -- and sends contiguous pieces
of it either to [[TextWeaver::inline_code_fragment]] or [[TextWeaver::commentary_fragment]]
as appropriate.
=
<<*>>=
void TextWeaver::commentary_text(heterogeneous_tree *tree, tree_node *ap, text_stream *matter) {
TextWeaver::commentary_r(tree, ap, matter, FALSE, FALSE);
}
@ -23,31 +23,31 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre
text_stream *code_in_comments_notation =
Bibliographic::get_datum(wv->weave_web->md,
(in_code)?(I"Code In Code Comments Notation"):(I"Code In Commentary Notation"));
if (Str::ne(code_in_comments_notation, I"Off")) @<Split text and code extracts@>;
if (Str::ne(code_in_comments_notation, I"Off")) <<Split text and code extracts>>;
int display_flag = TRUE;
text_stream *tex_notation = Bibliographic::get_datum(wv->weave_web->md,
I"TeX Mathematics Displayed Notation");
if (Str::ne(tex_notation, I"Off")) @<Recognise mathematics@>;
if (Str::ne(tex_notation, I"Off")) <<Recognise mathematics>>;
display_flag = FALSE;
tex_notation = Bibliographic::get_datum(wv->weave_web->md,
I"TeX Mathematics Notation");
if (Str::ne(tex_notation, I"Off")) @<Recognise mathematics@>;
if (Str::ne(tex_notation, I"Off")) <<Recognise mathematics>>;
text_stream *xref_notation = Bibliographic::get_datum(wv->weave_web->md,
I"Cross-References Notation");
if (Str::ne(xref_notation, I"Off")) @<Recognise cross-references@>;
if (Str::ne(xref_notation, I"Off")) <<Recognise cross-references>>;
if (within) {
TextWeaver::inline_code_fragment(tree, ap, matter);
} else {
@<Recognise hyperlinks@>;
@<Detect use of footnotes@>;
<<Recognise hyperlinks>>;
<<Detect use of footnotes>>;
TextWeaver::commentary_fragment(tree, ap, matter, in_code);
}
}
@<Split text and code extracts@> =
<<Split text and code extracts>>=
for (int i=0; i < Str::len(matter); i++) {
if (Str::get_at(matter, i) == '\\') i += Str::len(code_in_comments_notation) - 1;
else if (Str::includes_at(matter, i, code_in_comments_notation)) {
@ -64,7 +64,7 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre
}
}
@<Recognise hyperlinks@> =
<<Recognise hyperlinks>>=
for (int i=0; i < Str::len(matter); i++) {
if ((Str::includes_at(matter, i, I"http://")) ||
(Str::includes_at(matter, i, I"https://"))) {
@ -95,7 +95,7 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre
}
}
@<Recognise mathematics@> =
<<Recognise mathematics>>=
int N = Str::len(tex_notation);
for (int i=0; i < Str::len(matter); i++) {
if ((within == FALSE) && (Str::includes_at(matter, i, tex_notation))) {
@ -123,7 +123,7 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre
}
}
@<Detect use of footnotes@> =
<<Detect use of footnotes>>=
TEMPORARY_TEXT(before)
TEMPORARY_TEXT(cue)
TEMPORARY_TEXT(after)
@ -146,7 +146,7 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre
DISCARD_TEXT(after)
if (allow) return;
@<Recognise cross-references@> =
<<Recognise cross-references>>=
int N = Str::len(xref_notation);
for (int i=0; i < Str::len(matter); i++) {
if ((within == FALSE) && (Str::includes_at(matter, i, xref_notation)) &&
@ -164,7 +164,7 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre
Str::substr(before, Str::start(matter), Str::at(matter, i));
Str::substr(reference, Str::at(matter, i + N), Str::at(matter, j));
Str::substr(after, Str::at(matter, j + N), Str::end(matter));
@<Attempt to resolve the cross-reference@>;
<<Attempt to resolve the cross-reference>>;
DISCARD_TEXT(before)
DISCARD_TEXT(reference)
DISCARD_TEXT(after)
@ -175,7 +175,7 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre
}
}
@<Attempt to resolve the cross-reference@> =
<<Attempt to resolve the cross-reference>>=
TEMPORARY_TEXT(url)
TEMPORARY_TEXT(title)
int ext = FALSE;
@ -192,21 +192,21 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre
@ This tests whether a cross-reference is allowed to begin or end: it must
begin after and finish before a "boundary character".
Note the one-sided treatment of |:|, which is a boundary after but not before,
so that |http://| won't trigger a cross-reference with the standard |//|
Note the one-sided treatment of [[:]], which is a boundary after but not before,
so that [[http://]] won't trigger a cross-reference with the standard [[//]]
xref notation.
=
<<*>>=
int TextWeaver::boundary_character(int before, wchar_t c) {
if (c == 0) return TRUE;
if (Characters::is_whitespace(c)) return TRUE;
if ((c == '.') || (c == ',') || (c == '!') || (c == '?') || (c == ';') ||
if ((c == '.') [[| (c == ',') || (c == '!') || (c == '?') || (c == ';') |]]
(c == '(')|| (c == ')')) return TRUE;
if ((before == FALSE) && (c == ':')) return TRUE;
return FALSE;
}
@ =
<<*>>=
void TextWeaver::commentary_fragment(heterogeneous_tree *tree, tree_node *ap,
text_stream *fragment, int in_code) {
if (Str::len(fragment) > 0)
@ -223,9 +223,9 @@ void TextWeaver::inline_code_fragment(heterogeneous_tree *tree, tree_node *ap, t
Trees::make_child(SC, I);
}
@h Code text.
@ \section{Code text.}
=
<<*>>=
void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap,
text_stream *matter, text_stream *colouring, int linked) {
weave_document_node *C = RETRIEVE_POINTER_weave_document_node(tree->root->content);
@ -234,11 +234,11 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap,
int from = 0;
for (int i=0; i < Str::len(matter); i++) {
if (linked) {
@<Pick up hyperlinking at the eleventh hour@>;
<<Pick up hyperlinking at the eleventh hour>>;
text_stream *xref_notation = Bibliographic::get_datum(wv->weave_web->md,
I"Cross-References Notation");
if (Str::ne(xref_notation, I"Off"))
@<Pick up cross-references at the eleventh hour@>;
<<Pick up cross-references at the eleventh hour>>;
}
if ((Str::get_at(colouring, i) == FUNCTION_COLOUR) &&
(wv->current_weave_line->category != TEXT_EXTRACT_LCAT)) {
@ -248,7 +248,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap,
PUT_TO(fname, Str::get_at(matter, j++));
if (Analyser::is_reserved_word_for_section(
wv->current_weave_line->owning_section, fname, FUNCTION_COLOUR))
@<Spot the function@>;
<<Spot the function>>;
DISCARD_TEXT(fname)
}
@ -257,7 +257,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap,
TextWeaver::source_code_piece(tree, ap, matter, colouring, from, Str::len(matter));
}
@<Pick up hyperlinking at the eleventh hour@> =
<<Pick up hyperlinking at the eleventh hour>>=
if ((Str::includes_at(matter, i, I"http://")) ||
(Str::includes_at(matter, i, I"https://"))) {
TEMPORARY_TEXT(after)
@ -273,7 +273,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap,
DISCARD_TEXT(after)
}
@<Pick up cross-references at the eleventh hour@> =
<<Pick up cross-references at the eleventh hour>>=
int N = Str::len(xref_notation);
if ((Str::includes_at(matter, i, xref_notation))) {
int j = i + N+1;
@ -281,7 +281,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap,
if (Str::includes_at(matter, j, xref_notation)) {
TEMPORARY_TEXT(reference)
Str::substr(reference, Str::at(matter, i + N), Str::at(matter, j));
@<Attempt to resolve the cross-reference at the eleventh hour@>;
<<Attempt to resolve the cross-reference at the eleventh hour>>;
DISCARD_TEXT(reference)
break;
}
@ -289,7 +289,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap,
}
}
@<Attempt to resolve the cross-reference at the eleventh hour@> =
<<Attempt to resolve the cross-reference at the eleventh hour>>=
TEMPORARY_TEXT(url)
TEMPORARY_TEXT(title)
int ext = FALSE;
@ -304,7 +304,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap,
DISCARD_TEXT(url)
DISCARD_TEXT(title)
@<Spot the function@> =
<<Spot the function>>=
language_function *fn = Analyser::get_function(
wv->current_weave_line->owning_section, fname, FUNCTION_COLOUR);
if (fn) {
@ -330,7 +330,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap,
}
}
@ =
<<*>>=
void TextWeaver::source_code_piece(heterogeneous_tree *tree, tree_node *ap,
text_stream *matter, text_stream *colouring, int from, int to) {
if (to > from) {