Chapter 3: Nowebify.
This commit is contained in:
parent
cf990fc08d
commit
82d8056b15
5 changed files with 374 additions and 365 deletions
|
@ -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);
|
|
@ -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;
|
|
@ -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);
|
|
@ -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;
|
|
@ -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) {
|
Loading…
Reference in a new issue