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
|
Here we analyse the code in the web, enabling us to see how functions
|
||||||
and data structures are used within the program.
|
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
|
This scanner is intended for debugging Inweb, and simply shows the main
|
||||||
result of reading in and parsing the web:
|
result of reading in and parsing the web:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Analyser::scan_line_categories(web *W, text_stream *range) {
|
void Analyser::scan_line_categories(web *W, text_stream *range) {
|
||||||
PRINT("Scan of source lines for '%S'\n", range);
|
PRINT("Scan of source lines for '%S'\n", range);
|
||||||
int count = 1;
|
int count = 1;
|
||||||
|
@ -16,39 +16,40 @@ void Analyser::scan_line_categories(web *W, text_stream *range) {
|
||||||
section *S;
|
section *S;
|
||||||
LOOP_OVER_LINKED_LIST(S, section, C->sections)
|
LOOP_OVER_LINKED_LIST(S, section, C->sections)
|
||||||
for (source_line *L = S->first_line; L; L = L->next_line)
|
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 {
|
} else {
|
||||||
section *S = Reader::get_section_for_range(W, range);
|
section *S = Reader::get_section_for_range(W, range);
|
||||||
if (S) {
|
if (S) {
|
||||||
for (source_line *L = S->first_line; L; L = L->next_line)
|
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 {
|
} else {
|
||||||
LOOP_OVER_LINKED_LIST(C, chapter, W->chapters)
|
LOOP_OVER_LINKED_LIST(C, chapter, W->chapters)
|
||||||
LOOP_OVER_LINKED_LIST(S, section, C->sections)
|
LOOP_OVER_LINKED_LIST(S, section, C->sections)
|
||||||
for (source_line *L = S->first_line; L; L = L->next_line)
|
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)
|
TEMPORARY_TEXT(C)
|
||||||
WRITE_TO(C, "%s", Lines::category_name(L->category));
|
WRITE_TO(C, "%s", Lines::category_name(L->category));
|
||||||
while (Str::len(C) < 20) PUT_TO(C, '.');
|
while (Str::len(C) < 20) PUT_TO(C, '.');
|
||||||
PRINT("%07d %S %S\n", count++, C, L->text);
|
PRINT("%07d %S %S\n", count++, C, L->text);
|
||||||
DISCARD_TEXT(C)
|
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
|
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
|
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
|
annotations which are programming-language specific; the aim is to abstract
|
||||||
so that Chapter 4 contains no assumptions about the language.
|
so that Chapter 4 contains no assumptions about the language.
|
||||||
|
|
||||||
@enum BASIC_SECTIONCAT from 1
|
<<*>>=
|
||||||
@enum STRUCTURES_SECTIONCAT
|
enum BASIC_SECTIONCAT from 1
|
||||||
@enum FUNCTIONS_SECTIONCAT
|
enum STRUCTURES_SECTIONCAT
|
||||||
|
enum FUNCTIONS_SECTIONCAT
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Analyser::catalogue_the_sections(web *W, text_stream *range, int form) {
|
void Analyser::catalogue_the_sections(web *W, text_stream *range, int form) {
|
||||||
int max_width = 0, max_range_width = 0;
|
int max_width = 0, max_range_width = 0;
|
||||||
chapter *C;
|
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,
|
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
|
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
|
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
|
Usage codes are used to define a set of allowed contexts in which to spot
|
||||||
these identifiers.
|
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)| */
|
#define ELEMENT_ACCESS_USAGE 0x00000001 /* C-like languages: access via [[->| or |.]] operators to structure element */
|
||||||
@d PREFORM_IN_CODE_USAGE 0x00000004 /* InC only: use of a Preform nonterminal as a C "constant" */
|
#define FCALL_USAGE 0x00000002 /* C-like languages: function call made using brackets, [[name(args)]] */
|
||||||
@d PREFORM_IN_GRAMMAR_USAGE 0x00000008 /* InC only: ditto, but within Preform production rather than C code */
|
#define PREFORM_IN_CODE_USAGE 0x00000004 /* InC only: use of a Preform nonterminal as a C "constant" */
|
||||||
@d MISC_USAGE 0x00000010 /* any other appearance as an identifier */
|
#define PREFORM_IN_GRAMMAR_USAGE 0x00000008 /* InC only: ditto, but within Preform production rather than C code */
|
||||||
@d ANY_USAGE 0x7fffffff /* any of the above */
|
#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
|
@ 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
|
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) {
|
void Analyser::analyse_code(web *W) {
|
||||||
if (W->analysed) return;
|
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;
|
chapter *C;
|
||||||
section *S;
|
section *S;
|
||||||
LOOP_WITHIN_TANGLE(C, S, Tangler::primary_target(W))
|
LOOP_WITHIN_TANGLE(C, S, Tangler::primary_target(W))
|
||||||
switch (L->category) {
|
switch (L->category) {
|
||||||
case BEGIN_DEFINITION_LCAT:
|
case BEGIN_DEFINITION_LCAT:
|
||||||
@<Perform analysis on the body of the definition@>;
|
<<Perform analysis on the body of the definition>>;
|
||||||
break;
|
break;
|
||||||
case CODE_BODY_LCAT:
|
case CODE_BODY_LCAT:
|
||||||
@<Perform analysis on a typical line of code@>;
|
<<Perform analysis on a typical line of code>>;
|
||||||
break;
|
break;
|
||||||
case PREFORM_GRAMMAR_LCAT:
|
case PREFORM_GRAMMAR_LCAT:
|
||||||
@<Perform analysis on productions in a Preform grammar@>;
|
<<Perform analysis on productions in a Preform grammar>>;
|
||||||
break;
|
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
|
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
|
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
|
Version 2 removed Interface altogeter as being cumbersome for no real gain in
|
||||||
practice.
|
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);
|
LanguageMethods::early_preweave_analysis(W->main_language, W);
|
||||||
|
|
||||||
chapter *C;
|
chapter *C;
|
||||||
|
@ -148,10 +150,10 @@ practice.
|
||||||
(Regexp::string_is_white_space(L->text) == FALSE))
|
(Regexp::string_is_white_space(L->text) == FALSE))
|
||||||
Main::error_in_web(I"unrecognised interface line", L);
|
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);
|
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);
|
Analyser::analyse_as_code(W, L, L->text_operand2, ANY_USAGE, 0);
|
||||||
while ((L->next_line) && (L->next_line->category == CONT_DEFINITION_LCAT)) {
|
while ((L->next_line) && (L->next_line->category == CONT_DEFINITION_LCAT)) {
|
||||||
L = L->next_line;
|
L = L->next_line;
|
||||||
|
@ -159,29 +161,29 @@ practice.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ Lines in a Preform grammar generally take the form of some BNF grammar, where
|
@ 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
|
and then some C code to deal with a match. The code is subjected to analysis
|
||||||
just as any other code would be.
|
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,
|
Analyser::analyse_as_code(W, L, L->text_operand2,
|
||||||
ANY_USAGE, 0);
|
ANY_USAGE, 0);
|
||||||
Analyser::analyse_as_code(W, L, L->text_operand,
|
Analyser::analyse_as_code(W, L, L->text_operand,
|
||||||
PREFORM_IN_CODE_USAGE, PREFORM_IN_GRAMMAR_USAGE);
|
PREFORM_IN_CODE_USAGE, PREFORM_IN_GRAMMAR_USAGE);
|
||||||
|
|
||||||
@h Identifier searching.
|
@ \section{Identifier searching.}
|
||||||
Here's what we actually do, then. We take the code fragment |text|, drawn
|
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
|
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
|
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
|
passed to [[Analyser::analyse_find]], along with the context they were found in (or, if
|
||||||
|transf| is nonzero, with |transf| as their context).
|
[[transf]] is nonzero, with [[transf]] as their context).
|
||||||
|
|
||||||
What we do is to look for instances of an identifier, defined as a maximal
|
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
|
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-or-chips]] counts, but [[fish-]] is not an identifier when it occurs in
|
||||||
|fish->bone|.)
|
[[fish->bone]].)
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Analyser::analyse_as_code(web *W, source_line *L, text_stream *text, int mask, int transf) {
|
void Analyser::analyse_as_code(web *W, source_line *L, text_stream *text, int mask, int transf) {
|
||||||
int start_at = -1, element_follows = FALSE;
|
int start_at = -1, element_follows = FALSE;
|
||||||
for (int i = 0; i < Str::len(text); i++) {
|
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) != '>'))) {
|
((Str::get_at(text, i) == '-') && (Str::get_at(text, i+1) != '>'))) {
|
||||||
if (start_at == -1) start_at = i;
|
if (start_at == -1) start_at = i;
|
||||||
} else {
|
} else {
|
||||||
if (start_at != -1) @<Found an identifier@>;
|
if (start_at != -1) <<Found an identifier>>;
|
||||||
if (Str::get_at(text, i) == '.') element_follows = TRUE;
|
if (Str::get_at(text, i) == '.') element_follows = TRUE;
|
||||||
else if ((Str::get_at(text, i) == '-') && (Str::get_at(text, i+1) == '>')) {
|
else if ((Str::get_at(text, i) == '-') && (Str::get_at(text, i+1) == '>')) {
|
||||||
element_follows = TRUE; i++;
|
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) {
|
if (start_at != -1) {
|
||||||
int i = Str::len(text);
|
int i = Str::len(text);
|
||||||
@<Found an identifier@>;
|
<<Found an identifier>>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Found an identifier@> =
|
<<Found an identifier>>=
|
||||||
int u = MISC_USAGE;
|
int u = MISC_USAGE;
|
||||||
if (element_follows) u = ELEMENT_ACCESS_USAGE;
|
if (element_follows) u = ELEMENT_ACCESS_USAGE;
|
||||||
else if (Str::get_at(text, i) == '(') u = FCALL_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;
|
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
|
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
|
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"
|
simplified from one used by Inform; it's the algorithm called "X 30011"
|
||||||
in Aho, Sethi and Ullman, "Compilers: Principles, Techniques and Tools"
|
in Aho, Sethi and Ullman, "Compilers: Principles, Techniques and Tools"
|
||||||
(1986), adapted slightly to separate out literal numbers.
|
(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) {
|
int Analyser::hash_code_from_word(text_stream *text) {
|
||||||
unsigned int hash_code = 0;
|
unsigned int hash_code = 0;
|
||||||
string_position p = Str::start(text);
|
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:
|
@ The actual table is stored here:
|
||||||
|
|
||||||
@d HASH_SAFETY_CODE 0x31415927
|
<<*>>=
|
||||||
|
#define HASH_SAFETY_CODE 0x31415927
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct hash_table {
|
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... */
|
int safety_code; /* when we start up, array's contents are undefined, so... */
|
||||||
} hash_table;
|
} hash_table;
|
||||||
|
|
||||||
|
@ -266,11 +270,11 @@ void Analyser::initialise_hash_table(hash_table *HT) {
|
||||||
|
|
||||||
@ Where we define:
|
@ Where we define:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct hash_table_entry {
|
typedef struct hash_table_entry {
|
||||||
text_stream *hash_key;
|
text_stream *hash_key;
|
||||||
int language_reserved_word; /* in the language currently being woven, that is */
|
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 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 */
|
struct language_function *as_function; /* for function names only */
|
||||||
CLASS_DEFINITION
|
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
|
languages prepares for code analysis by calling this routine on the name
|
||||||
of each C function.
|
of each C function.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
hash_table_entry *Analyser::find_hash_entry(hash_table *HT, text_stream *text, int create) {
|
hash_table_entry *Analyser::find_hash_entry(hash_table *HT, text_stream *text, int create) {
|
||||||
int h = Analyser::hash_code_from_word(text);
|
int h = Analyser::hash_code_from_word(text);
|
||||||
if (h == NUMBER_HASH) return NULL;
|
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:
|
@ Marking and testing these bits:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
hash_table_entry *Analyser::mark_reserved_word(hash_table *HT, text_stream *p, int e) {
|
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);
|
hash_table_entry *hte = Analyser::find_hash_entry(HT, p, TRUE);
|
||||||
hte->language_reserved_word |= (1 << (e % 32));
|
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
|
a function might be both called and used as a constant value within the
|
||||||
same paragraph of code.
|
same paragraph of code.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct hash_table_entry_usage {
|
typedef struct hash_table_entry_usage {
|
||||||
struct paragraph *usage_recorded_at;
|
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
|
CLASS_DEFINITION
|
||||||
} hash_table_entry_usage;
|
} hash_table_entry_usage;
|
||||||
|
|
||||||
@ And here's how we create these usages:
|
@ And here's how we create these usages:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Analyser::analyse_find(web *W, source_line *L, text_stream *identifier, int u) {
|
void Analyser::analyse_find(web *W, source_line *L, text_stream *identifier, int u) {
|
||||||
hash_table_entry *hte =
|
hash_table_entry *hte =
|
||||||
Analyser::find_hash_entry_for_section(L->owning_section, identifier, FALSE);
|
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;
|
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
|
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.
|
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) {
|
void Analyser::write_makefile(web *W, filename *F, module_search *I, text_stream *platform) {
|
||||||
pathname *P = W->md->path_to_web;
|
pathname *P = W->md->path_to_web;
|
||||||
text_stream *short_name = Pathnames::directory_name(P);
|
text_stream *short_name = Pathnames::directory_name(P);
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
To collate material generated by the weaver into finished, fully-woven files.
|
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
|
This is the process of reading a template file, substituting material into
|
||||||
placeholders in it, and writing the result.
|
placeholders in it, and writing the result.
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ directly.
|
||||||
|
|
||||||
For convenience, we provide three ways to call:
|
For convenience, we provide three ways to call:
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Collater::for_web_and_pattern(text_stream *OUT, web *W,
|
void Collater::for_web_and_pattern(text_stream *OUT, web *W,
|
||||||
weave_pattern *pattern, filename *F, filename *into) {
|
weave_pattern *pattern, filename *F, filename *into) {
|
||||||
Collater::collate(OUT, W, I"", F, pattern, NULL, NULL, NULL, 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.
|
@ 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 */
|
#define MAX_TEMPLATE_LINES 8192 /* maximum number of lines in template */
|
||||||
@d CI_STACK_CAPACITY 8 /* maximum recursion of chapter/section iteration */
|
#define CI_STACK_CAPACITY 8 /* maximum recursion of chapter/section iteration */
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct collater_state {
|
typedef struct collater_state {
|
||||||
struct web *for_web;
|
struct web *for_web;
|
||||||
struct text_stream *tlines[MAX_TEMPLATE_LINES];
|
struct text_stream *tlines[MAX_TEMPLATE_LINES];
|
||||||
|
@ -62,14 +63,14 @@ typedef struct collater_state {
|
||||||
struct filename *errors_at;
|
struct filename *errors_at;
|
||||||
struct weave_order *wv;
|
struct weave_order *wv;
|
||||||
struct filename *into_file;
|
struct filename *into_file;
|
||||||
struct linked_list *modules; /* of |module| */
|
struct linked_list *modules; /* of [[module]] */
|
||||||
} collater_state;
|
} collater_state;
|
||||||
|
|
||||||
@ Note the unfortunate maximum size limit on the template file. It means
|
@ Note the unfortunate maximum size limit on the template file. It means
|
||||||
that really humungous Javascript files in plugins might have trouble, though
|
that really humungous Javascript files in plugins might have trouble, though
|
||||||
if so, they can always be subdivided.
|
if so, they can always be subdivided.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
collater_state Collater::initial_state(web *W, text_stream *range,
|
collater_state Collater::initial_state(web *W, text_stream *range,
|
||||||
filename *template_filename, weave_pattern *pattern, filename *nav_file,
|
filename *template_filename, weave_pattern *pattern, filename *nav_file,
|
||||||
linked_list *crumbs, weave_order *wv, filename *into) {
|
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);
|
cls.modules = NEW_LINKED_LIST(module);
|
||||||
if (W) {
|
if (W) {
|
||||||
int c = LinkedLists::len(W->md->as_module->dependencies);
|
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;
|
return cls;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Form the list of imported modules@> =
|
<<Form the list of imported modules>>=
|
||||||
module **module_array =
|
module **module_array =
|
||||||
Memory::calloc(c, sizeof(module *), ARRAY_SORTING_MREASON);
|
Memory::calloc(c, sizeof(module *), ARRAY_SORTING_MREASON);
|
||||||
module *M; int d=0;
|
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);
|
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 *)));
|
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,
|
TextFiles::read(template_filename, FALSE,
|
||||||
"can't find contents template", TRUE, Collater::temp_line, NULL, &cls);
|
"can't find contents template", TRUE, Collater::temp_line, NULL, &cls);
|
||||||
if (TRACE_COLLATER_EXECUTION)
|
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",
|
PRINT("Warning: template <%f> truncated after %d line(s)\n",
|
||||||
template_filename, cls.no_tlines);
|
template_filename, cls.no_tlines);
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
void Collater::temp_line(text_stream *line, text_file_position *tfp, void *v_ies) {
|
void Collater::temp_line(text_stream *line, text_file_position *tfp, void *v_ies) {
|
||||||
collater_state *cls = (collater_state *) v_ies;
|
collater_state *cls = (collater_state *) v_ies;
|
||||||
if (cls->no_tlines < MAX_TEMPLATE_LINES)
|
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...
|
@ Running the engine...
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Collater::process(text_stream *OUT, collater_state *cls) {
|
void Collater::process(text_stream *OUT, collater_state *cls) {
|
||||||
int lpos = 0; /* This is our program counter: a line number in the template */
|
int lpos = 0; /* This is our program counter: a line number in the template */
|
||||||
while (lpos < cls->no_tlines) {
|
while (lpos < cls->no_tlines) {
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
TEMPORARY_TEXT(tl)
|
TEMPORARY_TEXT(tl)
|
||||||
Str::copy(tl, cls->tlines[lpos++]); /* Fetch the line at the program counter and advance */
|
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 */
|
WRITE("%S\n", tl); /* Copy the now finished line to the output */
|
||||||
DISCARD_TEXT(tl)
|
DISCARD_TEXT(tl)
|
||||||
CYCLE: ;
|
CYCLE: ;
|
||||||
|
@ -140,29 +141,29 @@ void Collater::process(text_stream *OUT, collater_state *cls) {
|
||||||
cls->inside_navigation_submenu = FALSE;
|
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 (Regexp::match(&mr, tl, L"(%c*?) ")) Str::copy(tl, mr.exp[0]); /* Strip trailing spaces */
|
||||||
if (TRACE_COLLATER_EXECUTION)
|
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+)%]%]")) ||
|
if ((Regexp::match(&mr, tl, L"%[%[(%c+)%]%]")) ||
|
||||||
(Regexp::match(&mr, tl, L" %[%[(%c+)%]%]"))) {
|
(Regexp::match(&mr, tl, L" %[%[(%c+)%]%]"))) {
|
||||||
TEMPORARY_TEXT(command)
|
TEMPORARY_TEXT(command)
|
||||||
Str::copy(command, mr.exp[0]);
|
Str::copy(command, mr.exp[0]);
|
||||||
@<Deal with a Select command@>;
|
<<Deal with a Select command>>;
|
||||||
@<Deal with an If command@>;
|
<<Deal with an If command>>;
|
||||||
@<Deal with an Else command@>;
|
<<Deal with an Else command>>;
|
||||||
@<Deal with a Repeat command@>;
|
<<Deal with a Repeat command>>;
|
||||||
@<Deal with a Repeat End command@>;
|
<<Deal with a Repeat End command>>;
|
||||||
DISCARD_TEXT(command)
|
DISCARD_TEXT(command)
|
||||||
}
|
}
|
||||||
@<Skip line if inside a failed conditional@>;
|
<<Skip line if inside a failed conditional>>;
|
||||||
@<Skip line if inside an empty loop@>;
|
<<Skip line if inside an empty loop>>;
|
||||||
@<Make substitutions of square-bracketed variables in line@>;
|
<<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:
|
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);
|
PRINT("%04d: %S\nStack:", lpos-1, tl);
|
||||||
for (int j=0; j<cls->sp; j++) {
|
for (int j=0; j<cls->sp; j++) {
|
||||||
if (cls->repeat_stack_level[j] == CHAPTER_LEVEL)
|
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
|
one-iteration loop in which the loop variable has the given section or
|
||||||
chapter as its value during the sole iteration.
|
chapter as its value during the sole iteration.
|
||||||
|
|
||||||
@<Deal with a Select command@> =
|
<<Deal with a Select command>>=
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if (Regexp::match(&mr, command, L"Select (%c*)")) {
|
if (Regexp::match(&mr, command, L"Select (%c*)")) {
|
||||||
chapter *C;
|
chapter *C;
|
||||||
|
@ -210,7 +211,7 @@ chapter as its value during the sole iteration.
|
||||||
|
|
||||||
@ Conditionals:
|
@ Conditionals:
|
||||||
|
|
||||||
@<Deal with an If command@> =
|
<<Deal with an If command>>=
|
||||||
if (Regexp::match(&mr, command, L"If (%c*)")) {
|
if (Regexp::match(&mr, command, L"If (%c*)")) {
|
||||||
text_stream *condition = mr.exp[0];
|
text_stream *condition = mr.exp[0];
|
||||||
int level = IF_FALSE_LEVEL;
|
int level = IF_FALSE_LEVEL;
|
||||||
|
@ -253,7 +254,7 @@ chapter as its value during the sole iteration.
|
||||||
goto CYCLE;
|
goto CYCLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Deal with an Else command@> =
|
<<Deal with an Else command>>=
|
||||||
if (Regexp::match(&mr, command, L"Else")) {
|
if (Regexp::match(&mr, command, L"Else")) {
|
||||||
if (cls->sp <= 0) {
|
if (cls->sp <= 0) {
|
||||||
Errors::at_position("Else without If",
|
Errors::at_position("Else without If",
|
||||||
|
@ -275,7 +276,7 @@ chapter as its value during the sole iteration.
|
||||||
|
|
||||||
@ Next, a genuine loop beginning:
|
@ Next, a genuine loop beginning:
|
||||||
|
|
||||||
@<Deal with a Repeat command@> =
|
<<Deal with a Repeat command>>=
|
||||||
int loop_level = 0;
|
int loop_level = 0;
|
||||||
if (Regexp::match(&mr, command, L"Repeat Module")) loop_level = MODULE_LEVEL;
|
if (Regexp::match(&mr, command, L"Repeat Module")) loop_level = MODULE_LEVEL;
|
||||||
if (Regexp::match(&mr, command, L"Repeat Chapter")) loop_level = CHAPTER_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);
|
linked_list_item *CI = FIRST_ITEM_IN_LINKED_LIST(chapter, cls->for_web->chapters);
|
||||||
while ((CI) && (CONTENT_IN_ITEM(CI, chapter)->md->imported))
|
while ((CI) && (CONTENT_IN_ITEM(CI, chapter)->md->imported))
|
||||||
CI = NEXT_ITEM_IN_LINKED_LIST(CI, chapter);
|
CI = NEXT_ITEM_IN_LINKED_LIST(CI, chapter);
|
||||||
if (loop_level == MODULE_LEVEL) @<Begin a module repeat@>;
|
if (loop_level == MODULE_LEVEL) <<Begin a module repeat>>;
|
||||||
if (loop_level == CHAPTER_LEVEL) @<Begin a chapter repeat@>;
|
if (loop_level == CHAPTER_LEVEL) <<Begin a chapter repeat>>;
|
||||||
if (loop_level == SECTION_LEVEL) @<Begin a section repeat@>;
|
if (loop_level == SECTION_LEVEL) <<Begin a section repeat>>;
|
||||||
Collater::start_CI_loop(cls, loop_level, from, to, lpos);
|
Collater::start_CI_loop(cls, loop_level, from, to, lpos);
|
||||||
goto CYCLE;
|
goto CYCLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Begin a module repeat@> =
|
<<Begin a module repeat>>=
|
||||||
from = FIRST_ITEM_IN_LINKED_LIST(module, cls->modules);
|
from = FIRST_ITEM_IN_LINKED_LIST(module, cls->modules);
|
||||||
to = LAST_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;
|
from = CI;
|
||||||
to = LAST_ITEM_IN_LINKED_LIST(chapter, cls->for_web->chapters);
|
to = LAST_ITEM_IN_LINKED_LIST(chapter, cls->for_web->chapters);
|
||||||
if (Str::eq_wide_string(cls->restrict_to_range, L"0") == FALSE) {
|
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 =
|
chapter *within_chapter =
|
||||||
CONTENT_IN_ITEM(Collater::heading_topmost_on_stack(cls, CHAPTER_LEVEL),
|
CONTENT_IN_ITEM(Collater::heading_topmost_on_stack(cls, CHAPTER_LEVEL),
|
||||||
chapter);
|
chapter);
|
||||||
|
@ -326,7 +327,7 @@ chapter as its value during the sole iteration.
|
||||||
|
|
||||||
@ And at the other bookend:
|
@ And at the other bookend:
|
||||||
|
|
||||||
@<Deal with a Repeat End command@> =
|
<<Deal with a Repeat End command>>=
|
||||||
int end_form = -1;
|
int end_form = -1;
|
||||||
if (Regexp::match(&mr, command, L"End Repeat")) 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;
|
if (Regexp::match(&mr, command, L"End Select")) end_form = 2;
|
||||||
|
@ -357,16 +358,16 @@ chapter as its value during the sole iteration.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch (cls->repeat_stack_level[cls->sp-1]) {
|
switch (cls->repeat_stack_level[cls->sp-1]) {
|
||||||
case MODULE_LEVEL: @<End a module repeat@>; break;
|
case MODULE_LEVEL: <<End a module repeat>>; break;
|
||||||
case CHAPTER_LEVEL: @<End a chapter repeat@>; break;
|
case CHAPTER_LEVEL: <<End a chapter repeat>>; break;
|
||||||
case SECTION_LEVEL: @<End a section repeat@>; break;
|
case SECTION_LEVEL: <<End a section repeat>>; break;
|
||||||
case IF_TRUE_LEVEL: @<End an If@>; break;
|
case IF_TRUE_LEVEL: <<End an If>>; break;
|
||||||
case IF_FALSE_LEVEL: @<End an If@>; break;
|
case IF_FALSE_LEVEL: <<End an If>>; break;
|
||||||
}
|
}
|
||||||
goto CYCLE;
|
goto CYCLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<End a module repeat@> =
|
<<End a module repeat>>=
|
||||||
linked_list_item *CI = cls->repeat_stack_variable[cls->sp-1];
|
linked_list_item *CI = cls->repeat_stack_variable[cls->sp-1];
|
||||||
if (CI == cls->repeat_stack_threshold[cls->sp-1])
|
if (CI == cls->repeat_stack_threshold[cls->sp-1])
|
||||||
Collater::end_CI_loop(cls);
|
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 */
|
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];
|
linked_list_item *CI = cls->repeat_stack_variable[cls->sp-1];
|
||||||
if (CI == cls->repeat_stack_threshold[cls->sp-1])
|
if (CI == cls->repeat_stack_threshold[cls->sp-1])
|
||||||
Collater::end_CI_loop(cls);
|
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 */
|
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];
|
linked_list_item *SI = cls->repeat_stack_variable[cls->sp-1];
|
||||||
if ((SI == cls->repeat_stack_threshold[cls->sp-1]) ||
|
if ((SI == cls->repeat_stack_threshold[cls->sp-1]) ||
|
||||||
(NEXT_ITEM_IN_LINKED_LIST(SI, section) == NULL))
|
(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 */
|
lpos = cls->repeat_stack_startpos[cls->sp-1]; /* Back round loop */
|
||||||
}
|
}
|
||||||
|
|
||||||
@<End an If@> =
|
<<End an If>>=
|
||||||
Collater::end_CI_loop(cls);
|
Collater::end_CI_loop(cls);
|
||||||
|
|
||||||
@ It can happen that a section loop, at least, is empty:
|
@ 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--)
|
for (int rstl = cls->sp-1; rstl >= 0; rstl--)
|
||||||
if (cls->repeat_stack_level[cls->sp-1] == SECTION_LEVEL) {
|
if (cls->repeat_stack_level[cls->sp-1] == SECTION_LEVEL) {
|
||||||
linked_list_item *SI = cls->repeat_stack_threshold[cls->sp-1];
|
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;
|
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--)
|
for (int j=cls->sp-1; j>=0; j--)
|
||||||
if (cls->repeat_stack_level[j] == IF_FALSE_LEVEL)
|
if (cls->repeat_stack_level[j] == IF_FALSE_LEVEL)
|
||||||
goto CYCLE;
|
goto CYCLE;
|
||||||
|
|
||||||
@ If called with the non-conditional levels, the following function returns
|
@ 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) {
|
linked_list_item *Collater::heading_topmost_on_stack(collater_state *cls, int level) {
|
||||||
for (int rstl = cls->sp-1; rstl >= 0; rstl--)
|
for (int rstl = cls->sp-1; rstl >= 0; rstl--)
|
||||||
if (cls->repeat_stack_level[rstl] == level)
|
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
|
@ 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:
|
details, and similarly for ending it by popping them again:
|
||||||
|
|
||||||
@d MODULE_LEVEL 1
|
<<*>>=
|
||||||
@d CHAPTER_LEVEL 2
|
#define MODULE_LEVEL 1
|
||||||
@d SECTION_LEVEL 3
|
#define CHAPTER_LEVEL 2
|
||||||
@d IF_TRUE_LEVEL 4
|
#define SECTION_LEVEL 3
|
||||||
@d IF_FALSE_LEVEL 5
|
#define IF_TRUE_LEVEL 4
|
||||||
|
#define IF_FALSE_LEVEL 5
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Collater::start_CI_loop(collater_state *cls, int level,
|
void Collater::start_CI_loop(collater_state *cls, int level,
|
||||||
linked_list_item *from, linked_list_item *to, int pos) {
|
linked_list_item *from, linked_list_item *to, int pos) {
|
||||||
if (cls->sp < CI_STACK_CAPACITY) {
|
if (cls->sp < CI_STACK_CAPACITY) {
|
||||||
|
@ -451,17 +453,17 @@ void Collater::end_CI_loop(collater_state *cls) {
|
||||||
cls->sp--;
|
cls->sp--;
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Variable substitutions.
|
@ \section{Variable substitutions.}
|
||||||
We can now forget about this tiny stack machine: the one task left is to
|
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
|
take a line from the template, and make substitutions of variables into
|
||||||
its square-bracketed parts.
|
its square-bracketed parts.
|
||||||
|
|
||||||
Note that we do not allow this to recurse, i.e., if |[[X]]| substitutes into
|
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
|
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]]|,
|
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.
|
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)
|
TEMPORARY_TEXT(rewritten)
|
||||||
int slen, spos;
|
int slen, spos;
|
||||||
while ((spos = Regexp::find_expansion(tl, '[', '[', ']', ']', &slen)) >= 0) {
|
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();
|
match_results mr = Regexp::create_mr();
|
||||||
if (Bibliographic::data_exists(cls->for_web->md, varname)) {
|
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")) {
|
} else if (Regexp::match(&mr, varname, L"Navigation")) {
|
||||||
@<Substitute Navigation@>;
|
<<Substitute Navigation>>;
|
||||||
} else if (Regexp::match(&mr, varname, L"Breadcrumbs")) {
|
} else if (Regexp::match(&mr, varname, L"Breadcrumbs")) {
|
||||||
@<Substitute Breadcrumbs@>;
|
<<Substitute Breadcrumbs>>;
|
||||||
} else if (Str::eq_wide_string(varname, L"Plugins")) {
|
} else if (Str::eq_wide_string(varname, L"Plugins")) {
|
||||||
@<Substitute Plugins@>;
|
<<Substitute Plugins>>;
|
||||||
} else if (Regexp::match(&mr, varname, L"Complete (%c+)")) {
|
} else if (Regexp::match(&mr, varname, L"Complete (%c+)")) {
|
||||||
text_stream *detail = mr.exp[0];
|
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+)")) {
|
} else if (Regexp::match(&mr, varname, L"Module (%c+)")) {
|
||||||
text_stream *detail = mr.exp[0];
|
text_stream *detail = mr.exp[0];
|
||||||
@<Substitute a Module@>;
|
<<Substitute a Module>>;
|
||||||
} else if (Regexp::match(&mr, varname, L"Chapter (%c+)")) {
|
} else if (Regexp::match(&mr, varname, L"Chapter (%c+)")) {
|
||||||
text_stream *detail = mr.exp[0];
|
text_stream *detail = mr.exp[0];
|
||||||
@<Substitute a Chapter@>;
|
<<Substitute a Chapter>>;
|
||||||
} else if (Regexp::match(&mr, varname, L"Section (%c+)")) {
|
} else if (Regexp::match(&mr, varname, L"Section (%c+)")) {
|
||||||
text_stream *detail = mr.exp[0];
|
text_stream *detail = mr.exp[0];
|
||||||
@<Substitute a Section@>;
|
<<Substitute a Section>>;
|
||||||
} else if (Regexp::match(&mr, varname, L"Docs")) {
|
} else if (Regexp::match(&mr, varname, L"Docs")) {
|
||||||
@<Substitute a Docs@>;
|
<<Substitute a Docs>>;
|
||||||
} else if (Regexp::match(&mr, varname, L"Assets")) {
|
} else if (Regexp::match(&mr, varname, L"Assets")) {
|
||||||
@<Substitute an Assets@>;
|
<<Substitute an Assets>>;
|
||||||
} else if (Regexp::match(&mr, varname, L"URL \"(%c+)\"")) {
|
} else if (Regexp::match(&mr, varname, L"URL \"(%c+)\"")) {
|
||||||
text_stream *link_text = mr.exp[0];
|
text_stream *link_text = mr.exp[0];
|
||||||
@<Substitute a URL@>;
|
<<Substitute a URL>>;
|
||||||
} else if (Regexp::match(&mr, varname, L"Link \"(%c+)\"")) {
|
} else if (Regexp::match(&mr, varname, L"Link \"(%c+)\"")) {
|
||||||
text_stream *link_text = mr.exp[0];
|
text_stream *link_text = mr.exp[0];
|
||||||
@<Substitute a Link@>;
|
<<Substitute a Link>>;
|
||||||
} else if (Regexp::match(&mr, varname, L"Menu \"(%c+)\"")) {
|
} else if (Regexp::match(&mr, varname, L"Menu \"(%c+)\"")) {
|
||||||
text_stream *menu_name = mr.exp[0];
|
text_stream *menu_name = mr.exp[0];
|
||||||
@<Substitute a Menu@>;
|
<<Substitute a Menu>>;
|
||||||
} else if (Regexp::match(&mr, varname, L"Item \"(%c+)\"")) {
|
} else if (Regexp::match(&mr, varname, L"Item \"(%c+)\"")) {
|
||||||
text_stream *item_name = mr.exp[0];
|
text_stream *item_name = mr.exp[0];
|
||||||
text_stream *icon_text = NULL;
|
text_stream *icon_text = NULL;
|
||||||
@<Look for icon text@>;
|
<<Look for icon text>>;
|
||||||
text_stream *link_text = item_name;
|
text_stream *link_text = item_name;
|
||||||
@<Substitute a member Item@>;
|
<<Substitute a member Item>>;
|
||||||
} else if (Regexp::match(&mr, varname, L"Item \"(%c+)\" -> (%c+)")) {
|
} else if (Regexp::match(&mr, varname, L"Item \"(%c+)\" -> (%c+)")) {
|
||||||
text_stream *item_name = mr.exp[0];
|
text_stream *item_name = mr.exp[0];
|
||||||
text_stream *link_text = mr.exp[1];
|
text_stream *link_text = mr.exp[1];
|
||||||
text_stream *icon_text = NULL;
|
text_stream *icon_text = NULL;
|
||||||
@<Look for icon text@>;
|
<<Look for icon text>>;
|
||||||
@<Substitute a general Item@>;
|
<<Substitute a general Item>>;
|
||||||
} else {
|
} else {
|
||||||
WRITE_TO(substituted, "%S", varname);
|
WRITE_TO(substituted, "%S", varname);
|
||||||
if (Regexp::match(&mr, varname, L"%i+%c*"))
|
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);
|
Str::clear(tl); Str::copy(tl, rewritten);
|
||||||
DISCARD_TEXT(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));
|
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.
|
this will recursively call The Collater, in fact.
|
||||||
|
|
||||||
@<Substitute Navigation@> =
|
<<Substitute Navigation>>=
|
||||||
if (cls->nav_file) {
|
if (cls->nav_file) {
|
||||||
if (TextFiles::exists(cls->nav_file))
|
if (TextFiles::exists(cls->nav_file))
|
||||||
Collater::collate(substituted, cls->for_web, cls->restrict_to_range,
|
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.
|
@ A trail of breadcrumbs, used for overhead navigation in web pages.
|
||||||
|
|
||||||
@<Substitute Breadcrumbs@> =
|
<<Substitute Breadcrumbs>>=
|
||||||
Colonies::drop_initial_breadcrumbs(substituted, cls->into_file,
|
Colonies::drop_initial_breadcrumbs(substituted, cls->into_file,
|
||||||
cls->crumbs);
|
cls->crumbs);
|
||||||
|
|
||||||
@<Substitute Plugins@> =
|
<<Substitute Plugins>>=
|
||||||
Assets::include_relevant_plugins(OUT, cls->nav_pattern, cls->for_web,
|
Assets::include_relevant_plugins(OUT, cls->nav_pattern, cls->for_web,
|
||||||
cls->wv, cls->into_file);
|
cls->wv, cls->into_file);
|
||||||
|
|
||||||
@ We store little about the complete-web-in-one-file PDF:
|
@ 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 (swarm_leader)
|
||||||
if (Formats::substitute_post_processing_data(substituted,
|
if (Formats::substitute_post_processing_data(substituted,
|
||||||
swarm_leader, detail, cls->nav_pattern) == FALSE)
|
swarm_leader, detail, cls->nav_pattern) == FALSE)
|
||||||
|
@ -574,15 +576,15 @@ this will recursively call The Collater, in fact.
|
||||||
|
|
||||||
@ And here for Modules:
|
@ And here for Modules:
|
||||||
|
|
||||||
@<Substitute a Module@> =
|
<<Substitute a Module>>=
|
||||||
module *M = CONTENT_IN_ITEM(
|
module *M = CONTENT_IN_ITEM(
|
||||||
Collater::heading_topmost_on_stack(cls, MODULE_LEVEL), module);
|
Collater::heading_topmost_on_stack(cls, MODULE_LEVEL), module);
|
||||||
if (M == NULL)
|
if (M == NULL)
|
||||||
Errors::at_position("no module is currently selected",
|
Errors::at_position("no module is currently selected",
|
||||||
cls->errors_at, lpos);
|
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")) {
|
if (Str::eq_wide_string(detail, L"Title")) {
|
||||||
text_stream *owner = Collater::module_owner(M, cls->for_web);
|
text_stream *owner = Collater::module_owner(M, cls->for_web);
|
||||||
if (Str::len(owner) > 0) WRITE_TO(substituted, "%S/", owner);
|
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:
|
@ And here for Chapters:
|
||||||
|
|
||||||
@<Substitute a Chapter@> =
|
<<Substitute a Chapter>>=
|
||||||
chapter *C = CONTENT_IN_ITEM(
|
chapter *C = CONTENT_IN_ITEM(
|
||||||
Collater::heading_topmost_on_stack(cls, CHAPTER_LEVEL), chapter);
|
Collater::heading_topmost_on_stack(cls, CHAPTER_LEVEL), chapter);
|
||||||
if (C == NULL)
|
if (C == NULL)
|
||||||
Errors::at_position("no chapter is currently selected",
|
Errors::at_position("no chapter is currently selected",
|
||||||
cls->errors_at, lpos);
|
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")) {
|
if (Str::eq_wide_string(detail, L"Title")) {
|
||||||
Str::copy(substituted, C->md->ch_title);
|
Str::copy(substituted, C->md->ch_title);
|
||||||
} else if (Str::eq_wide_string(detail, L"Code")) {
|
} 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.
|
@ And this is a very similar construction for Sections.
|
||||||
|
|
||||||
@<Substitute a Section@> =
|
<<Substitute a Section>>=
|
||||||
section *S = CONTENT_IN_ITEM(
|
section *S = CONTENT_IN_ITEM(
|
||||||
Collater::heading_topmost_on_stack(cls, SECTION_LEVEL), section);
|
Collater::heading_topmost_on_stack(cls, SECTION_LEVEL), section);
|
||||||
if (S == NULL)
|
if (S == NULL)
|
||||||
Errors::at_position("no section is currently selected",
|
Errors::at_position("no section is currently selected",
|
||||||
cls->errors_at, lpos);
|
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")) {
|
if (Str::eq_wide_string(detail, L"Title")) {
|
||||||
Str::copy(substituted, S->md->sect_title);
|
Str::copy(substituted, S->md->sect_title);
|
||||||
} else if (Str::eq_wide_string(detail, L"Purpose")) {
|
} 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
|
@ These commands are all used in constructing relative URLs, especially for
|
||||||
navigation purposes.
|
navigation purposes.
|
||||||
|
|
||||||
@<Substitute a Docs@> =
|
<<Substitute a Docs>>=
|
||||||
Pathnames::relative_URL(substituted,
|
Pathnames::relative_URL(substituted,
|
||||||
Filenames::up(cls->into_file),
|
Filenames::up(cls->into_file),
|
||||||
Pathnames::from_text(Colonies::home()));
|
Pathnames::from_text(Colonies::home()));
|
||||||
|
|
||||||
@<Substitute an Assets@> =
|
<<Substitute an Assets>>=
|
||||||
pathname *P = Colonies::assets_path();
|
pathname *P = Colonies::assets_path();
|
||||||
if (P == NULL) P = Filenames::up(cls->into_file);
|
if (P == NULL) P = Filenames::up(cls->into_file);
|
||||||
Pathnames::relative_URL(substituted,
|
Pathnames::relative_URL(substituted,
|
||||||
Filenames::up(cls->into_file), P);
|
Filenames::up(cls->into_file), P);
|
||||||
|
|
||||||
@<Substitute a URL@> =
|
<<Substitute a URL>>=
|
||||||
Pathnames::relative_URL(substituted,
|
Pathnames::relative_URL(substituted,
|
||||||
Filenames::up(cls->into_file),
|
Filenames::up(cls->into_file),
|
||||||
Pathnames::from_text(link_text));
|
Pathnames::from_text(link_text));
|
||||||
|
|
||||||
@<Substitute a Link@> =
|
<<Substitute a Link>>=
|
||||||
WRITE_TO(substituted, "<a href=\"");
|
WRITE_TO(substituted, "<a href=\"");
|
||||||
Colonies::reference_URL(substituted, link_text, cls->into_file);
|
Colonies::reference_URL(substituted, link_text, cls->into_file);
|
||||||
WRITE_TO(substituted, "\">");
|
WRITE_TO(substituted, "\">");
|
||||||
|
|
||||||
@<Substitute a Menu@> =
|
<<Substitute a Menu>>=
|
||||||
if (cls->inside_navigation_submenu) WRITE_TO(substituted, "</ul>");
|
if (cls->inside_navigation_submenu) WRITE_TO(substituted, "</ul>");
|
||||||
WRITE_TO(substituted, "<h2>%S</h2><ul>", menu_name);
|
WRITE_TO(substituted, "<h2>%S</h2><ul>", menu_name);
|
||||||
cls->inside_navigation_submenu = TRUE;
|
cls->inside_navigation_submenu = TRUE;
|
||||||
|
|
||||||
@<Look for icon text@> =
|
<<Look for icon text>>=
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if (Regexp::match(&mr, item_name, L"<(%i+.%i+)> *(%c*)")) {
|
if (Regexp::match(&mr, item_name, L"<(%i+.%i+)> *(%c*)")) {
|
||||||
icon_text = Str::duplicate(mr.exp[0]);
|
icon_text = Str::duplicate(mr.exp[0]);
|
||||||
|
@ -699,40 +701,40 @@ navigation purposes.
|
||||||
}
|
}
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
|
|
||||||
@<Substitute a member Item@> =
|
<<Substitute a member Item>>=
|
||||||
TEMPORARY_TEXT(url)
|
TEMPORARY_TEXT(url)
|
||||||
Colonies::reference_URL(url, link_text, cls->into_file);
|
Colonies::reference_URL(url, link_text, cls->into_file);
|
||||||
@<Substitute an item at this URL@>;
|
<<Substitute an item at this URL>>;
|
||||||
DISCARD_TEXT(url)
|
DISCARD_TEXT(url)
|
||||||
|
|
||||||
@<Substitute a general Item@> =
|
<<Substitute a general Item>>=
|
||||||
TEMPORARY_TEXT(url)
|
TEMPORARY_TEXT(url)
|
||||||
Colonies::link_URL(url, link_text, cls->into_file);
|
Colonies::link_URL(url, link_text, cls->into_file);
|
||||||
@<Substitute an item at this URL@>;
|
<<Substitute an item at this URL>>;
|
||||||
DISCARD_TEXT(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>");
|
if (cls->inside_navigation_submenu == FALSE) WRITE_TO(substituted, "<ul>");
|
||||||
cls->inside_navigation_submenu = TRUE;
|
cls->inside_navigation_submenu = TRUE;
|
||||||
WRITE_TO(substituted, "<li>");
|
WRITE_TO(substituted, "<li>");
|
||||||
if (Str::eq(url, Filenames::get_leafname(cls->into_file))) {
|
if (Str::eq(url, Filenames::get_leafname(cls->into_file))) {
|
||||||
WRITE_TO(substituted, "<span class=\"unlink\">");
|
WRITE_TO(substituted, "<span class=\"unlink\">");
|
||||||
@<Substitute icon and name@>;
|
<<Substitute icon and name>>;
|
||||||
WRITE_TO(substituted, "</span>");
|
WRITE_TO(substituted, "</span>");
|
||||||
} else if (Str::eq(url, I"index.html")) {
|
} else if (Str::eq(url, I"index.html")) {
|
||||||
WRITE_TO(substituted, "<a href=\"%S\">", url);
|
WRITE_TO(substituted, "<a href=\"%S\">", url);
|
||||||
WRITE_TO(substituted, "<span class=\"selectedlink\">");
|
WRITE_TO(substituted, "<span class=\"selectedlink\">");
|
||||||
@<Substitute icon and name@>;
|
<<Substitute icon and name>>;
|
||||||
WRITE_TO(substituted, "</span>");
|
WRITE_TO(substituted, "</span>");
|
||||||
WRITE_TO(substituted, "</a>");
|
WRITE_TO(substituted, "</a>");
|
||||||
} else {
|
} else {
|
||||||
WRITE_TO(substituted, "<a href=\"%S\">", url);
|
WRITE_TO(substituted, "<a href=\"%S\">", url);
|
||||||
@<Substitute icon and name@>;
|
<<Substitute icon and name>>;
|
||||||
WRITE_TO(substituted, "</a>");
|
WRITE_TO(substituted, "</a>");
|
||||||
}
|
}
|
||||||
WRITE_TO(substituted, "</li>");
|
WRITE_TO(substituted, "</li>");
|
||||||
|
|
||||||
@<Substitute icon and name@> =
|
<<Substitute icon and name>>=
|
||||||
if (Str::len(icon_text) > 0) {
|
if (Str::len(icon_text) > 0) {
|
||||||
WRITE_TO(substituted, "<img src=\"");
|
WRITE_TO(substituted, "<img src=\"");
|
||||||
pathname *I = Colonies::assets_path();
|
pathname *I = Colonies::assets_path();
|
||||||
|
@ -743,10 +745,10 @@ navigation purposes.
|
||||||
}
|
}
|
||||||
WRITE_TO(substituted, "%S", item_name);
|
WRITE_TO(substituted, "%S", item_name);
|
||||||
|
|
||||||
@ This is a utility for finding the owner of a module, returning |NULL| (the
|
@ 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|.
|
empty text) if it appears to belong to the current web [[W]].
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
text_stream *Collater::module_owner(const module *M, web *W) {
|
text_stream *Collater::module_owner(const module *M, web *W) {
|
||||||
text_stream *owner =
|
text_stream *owner =
|
||||||
Pathnames::directory_name(Pathnames::up(M->module_location));
|
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,
|
top, then all other owners, in alphabetical order, and then last of all Inweb,
|
||||||
so that //foundation// will always be at the bottom.
|
so that //foundation// will always be at the bottom.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
web *sorting_web = NULL;
|
web *sorting_web = NULL;
|
||||||
void Collater::sort_web(web *W) {
|
void Collater::sort_web(web *W) {
|
||||||
sorting_web = W;
|
sorting_web = W;
|
|
@ -3,14 +3,14 @@
|
||||||
To transcribe a version of the text in the web into a form which
|
To transcribe a version of the text in the web into a form which
|
||||||
can be compiled as a program.
|
can be compiled as a program.
|
||||||
|
|
||||||
@h The Master Tangler.
|
@ \section{The Master Tangler.}
|
||||||
Here's what has happened so far, on a |-tangle| run of Inweb: on any
|
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.
|
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
|
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
|
of the arrays and hashes populated. Program Control then sent us straight
|
||||||
here for the tangling to begin...
|
here for the tangling to begin...
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Tangler::tangle(web *W, tangle_target *target, filename *dest_file) {
|
void Tangler::tangle(web *W, tangle_target *target, filename *dest_file) {
|
||||||
programming_language *lang = target->tangle_language;
|
programming_language *lang = target->tangle_language;
|
||||||
PRINT(" tangling <%/f> (written in %S)\n", dest_file, lang->language_name);
|
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;
|
text_stream *OUT = &TO_struct;
|
||||||
if (STREAM_OPEN_TO_FILE(OUT, dest_file, ISO_ENC) == FALSE)
|
if (STREAM_OPEN_TO_FILE(OUT, dest_file, ISO_ENC) == FALSE)
|
||||||
Errors::fatal_with_file("unable to write tangled file", dest_file);
|
Errors::fatal_with_file("unable to write tangled file", dest_file);
|
||||||
@<Perform the actual tangle@>;
|
<<Perform the actual tangle>>;
|
||||||
STREAM_CLOSE(OUT);
|
STREAM_CLOSE(OUT);
|
||||||
|
|
||||||
@<Tangle any imported headers@>;
|
<<Tangle any imported headers>>;
|
||||||
@<Tangle any extract files not part of the target itself@>;
|
<<Tangle any extract files not part of the target itself>>;
|
||||||
LanguageMethods::additional_tangling(lang, W, target);
|
LanguageMethods::additional_tangling(lang, W, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ All of the sections are tangled together into one big file, the structure
|
@ All of the sections are tangled together into one big file, the structure
|
||||||
of which can be seen below.
|
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(C, chapter, W->chapters)
|
||||||
LOOP_OVER_LINKED_LIST(S, section, C->sections)
|
LOOP_OVER_LINKED_LIST(S, section, C->sections)
|
||||||
if (S->sect_target == T)
|
if (S->sect_target == T)
|
||||||
LOOP_OVER_LINKED_LIST(P, paragraph, S->paragraphs)
|
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 */
|
/* (a) The shebang line, a header for scripting languages, and other heading matter */
|
||||||
LanguageMethods::shebang(OUT, lang, W, target);
|
LanguageMethods::shebang(OUT, lang, W, target);
|
||||||
LanguageMethods::disclaimer(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))
|
if ((P->placed_very_early) && (P->defines_macro == NULL))
|
||||||
Tangler::tangle_paragraph(OUT, P);
|
Tangler::tangle_paragraph(OUT, P);
|
||||||
|
|
||||||
/* (b) Results of |@d| declarations */
|
/* (b) Results of [[@d]] declarations */
|
||||||
@<Tangle all the constant definitions in section order@>;
|
<<Tangle all the constant definitions in section order>>;
|
||||||
|
|
||||||
/* (c) Miscellaneous automated C predeclarations */
|
/* (c) Miscellaneous automated C predeclarations */
|
||||||
LanguageMethods::additional_predeclarations(OUT, lang, W);
|
LanguageMethods::additional_predeclarations(OUT, lang, W);
|
||||||
|
@ -65,26 +66,26 @@ of which can be seen below.
|
||||||
/* (f) Opposite of the shebang: a footer */
|
/* (f) Opposite of the shebang: a footer */
|
||||||
LanguageMethods::gnabehs(OUT, lang, W);
|
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.
|
extend across multiple lines.
|
||||||
|
|
||||||
@<Tangle all the constant definitions in section order@> =
|
<<Tangle all the constant definitions in section order>>=
|
||||||
chapter *C;
|
chapter *C;
|
||||||
section *S;
|
section *S;
|
||||||
LOOP_WITHIN_TANGLE(C, S, target)
|
LOOP_WITHIN_TANGLE(C, S, target)
|
||||||
if (L->category == BEGIN_DEFINITION_LCAT)
|
if (L->category == BEGIN_DEFINITION_LCAT)
|
||||||
if (L->default_defn == FALSE)
|
if (L->default_defn == FALSE)
|
||||||
@<Define the constant@>;
|
<<Define the constant>>;
|
||||||
LOOP_WITHIN_TANGLE(C, S, target)
|
LOOP_WITHIN_TANGLE(C, S, target)
|
||||||
if (L->category == BEGIN_DEFINITION_LCAT)
|
if (L->category == BEGIN_DEFINITION_LCAT)
|
||||||
if (L->default_defn) {
|
if (L->default_defn) {
|
||||||
LanguageMethods::open_ifdef(OUT, lang, L->text_operand, FALSE);
|
LanguageMethods::open_ifdef(OUT, lang, L->text_operand, FALSE);
|
||||||
@<Define the constant@>;
|
<<Define the constant>>;
|
||||||
LanguageMethods::close_ifdef(OUT, lang, L->text_operand, FALSE);
|
LanguageMethods::close_ifdef(OUT, lang, L->text_operand, FALSE);
|
||||||
}
|
}
|
||||||
Enumerations::define_extents(OUT, target, lang);
|
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);
|
if (L->owning_paragraph == NULL) Main::error_in_web(I"misplaced definition", L);
|
||||||
else Tags::open_ifdefs(OUT, L->owning_paragraph);
|
else Tags::open_ifdefs(OUT, L->owning_paragraph);
|
||||||
LanguageMethods::start_definition(OUT, lang,
|
LanguageMethods::start_definition(OUT, lang,
|
||||||
|
@ -97,7 +98,7 @@ extend across multiple lines.
|
||||||
LanguageMethods::end_definition(OUT, lang, S, L);
|
LanguageMethods::end_definition(OUT, lang, S, L);
|
||||||
if (L->owning_paragraph) Tags::close_ifdefs(OUT, L->owning_paragraph);
|
if (L->owning_paragraph) Tags::close_ifdefs(OUT, L->owning_paragraph);
|
||||||
|
|
||||||
@<Tangle any imported headers@> =
|
<<Tangle any imported headers>>=
|
||||||
filename *F;
|
filename *F;
|
||||||
LOOP_OVER_LINKED_LIST(F, filename, W->headers)
|
LOOP_OVER_LINKED_LIST(F, filename, W->headers)
|
||||||
Shell::copy(F, Reader::tangled_folder(W), "");
|
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.
|
code, or configuration gobbledegook) marked as "to ...", giving a leafname.
|
||||||
We place files of those leafnames in the same directory as the tangle target.
|
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_names[MAX_EXTRACT_FILES];
|
||||||
text_stream extract_files[MAX_EXTRACT_FILES];
|
text_stream extract_files[MAX_EXTRACT_FILES];
|
||||||
int no_extract_files = 0;
|
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]));
|
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
|
@ 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.
|
something quirky has been done to support a language feature.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void Tangler::tangle_paragraph(OUTPUT_STREAM, paragraph *P) {
|
void Tangler::tangle_paragraph(OUTPUT_STREAM, paragraph *P) {
|
||||||
Tags::open_ifdefs(OUT, P);
|
Tags::open_ifdefs(OUT, P);
|
||||||
int contiguous = FALSE;
|
int contiguous = FALSE;
|
||||||
for (source_line *L = P->first_line_in_paragraph;
|
for (source_line *L = P->first_line_in_paragraph;
|
||||||
((L) && (L->owning_paragraph == P)); L = L->next_line) {
|
((L) && (L->owning_paragraph == P)); L = L->next_line) {
|
||||||
if (LanguageMethods::will_insert_in_tangle(P->under_section->sect_language, L)) {
|
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);
|
LanguageMethods::insert_in_tangle(OUT, P->under_section->sect_language, L);
|
||||||
}
|
}
|
||||||
if ((L->category != CODE_BODY_LCAT) || (L->suppress_tangling)) {
|
if ((L->category != CODE_BODY_LCAT) || (L->suppress_tangling)) {
|
||||||
contiguous = FALSE;
|
contiguous = FALSE;
|
||||||
} else {
|
} 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");
|
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
|
support a notation to tell the compiler that code has come from somewhere
|
||||||
else; if so, here's where we use it.
|
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) {
|
if (contiguous == FALSE) {
|
||||||
contiguous = TRUE;
|
contiguous = TRUE;
|
||||||
LanguageMethods::insert_line_marker(OUT, P->under_section->sect_language, L);
|
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.
|
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) {
|
void Tangler::tangle_line(OUTPUT_STREAM, text_stream *original, section *S, source_line *L) {
|
||||||
int mlen, slen;
|
int mlen, slen;
|
||||||
int mpos = Regexp::find_expansion(original, '@', '<', '@', '>', &mlen);
|
int mpos = Regexp::find_expansion(original, '@', '<', '@', '>', &mlen);
|
||||||
int spos = Regexp::find_expansion(original, '[', '[', ']', ']', &slen);
|
int spos = Regexp::find_expansion(original, '[', '[', ']', ']', &slen);
|
||||||
if ((mpos >= 0) && ((spos == -1) || (mpos <= spos)) &&
|
if ((mpos >= 0) && ((spos == -1) || (mpos <= spos)) &&
|
||||||
(LanguageMethods::allow_expansion(S->sect_language, original)))
|
(LanguageMethods::allow_expansion(S->sect_language, original)))
|
||||||
@<Expand a paragraph macro@>
|
<<Expand a paragraph macro>>
|
||||||
else if (spos >= 0)
|
else if (spos >= 0)
|
||||||
@<Expand a double-square command@>
|
<<Expand a double-square command>>
|
||||||
else
|
else
|
||||||
LanguageMethods::tangle_line(OUT, S->sect_language, original); /* this is usually what happens */
|
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
|
@ The first form of escape is a paragraph macro in the middle of code. For
|
||||||
example, we handle
|
example, we handle
|
||||||
= (text)
|
|
||||||
if (banana_count == 0) @<Yes, we have no bananas@>;
|
if (banana_count == 0) <<Yes, we have no bananas>>;
|
||||||
=
|
|
||||||
by calling the lower-level tangler on |if (banana_count == 0) | (a substring
|
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
|
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
|
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
|
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.)
|
but in principle it could contain further macros.)
|
||||||
|
|
||||||
Note that when we've expanded "Yes, we have no bananas" we have certainly
|
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
|
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.
|
C compiler to think that the code which follows is also from that location.
|
||||||
So we insert a fresh line marker.
|
So we insert a fresh line marker.
|
||||||
|
|
||||||
@<Expand a paragraph macro@> =
|
<<Expand a paragraph macro>>=
|
||||||
TEMPORARY_TEXT(temp)
|
TEMPORARY_TEXT(temp)
|
||||||
Str::copy(temp, original); Str::truncate(temp, mpos);
|
Str::copy(temp, original); Str::truncate(temp, mpos);
|
||||||
LanguageMethods::tangle_line(OUT, S->sect_language, temp);
|
LanguageMethods::tangle_line(OUT, S->sect_language, temp);
|
||||||
|
@ -230,21 +232,21 @@ So we insert a fresh line marker.
|
||||||
DISCARD_TEXT(temp)
|
DISCARD_TEXT(temp)
|
||||||
|
|
||||||
@ This is a similar matter, except that it expands bibliographic data:
|
@ This is a similar matter, except that it expands bibliographic data:
|
||||||
= (text)
|
|
||||||
printf("This is build [[Build Number]].\n");
|
printf("This is build [[Build Number]].\n");
|
||||||
=
|
|
||||||
takes the bibliographic data for "Build Number" (as set on the web's contents
|
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)
|
page) and substitutes that, so that we end up with (say)
|
||||||
= (text as C)
|
|
||||||
printf("This is build 5Q47.\n");
|
printf("This is build 5Q47.\n");
|
||||||
=
|
|
||||||
In some languages there are also special expansions (for example, in
|
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
|
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;
|
web *W = S->owning_web;
|
||||||
|
|
||||||
TEMPORARY_TEXT(temp)
|
TEMPORARY_TEXT(temp)
|
||||||
|
@ -266,10 +268,10 @@ passes straight through. So |[[water]]| becomes just |[[water]]|.
|
||||||
DISCARD_TEXT(rest)
|
DISCARD_TEXT(rest)
|
||||||
DISCARD_TEXT(temp)
|
DISCARD_TEXT(temp)
|
||||||
|
|
||||||
@h Prinary target.
|
@ \section{Prinary target.}
|
||||||
The first target in a web is always the one for the main program.
|
The first target in a web is always the one for the main program.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
tangle_target *Tangler::primary_target(web *W) {
|
tangle_target *Tangler::primary_target(web *W) {
|
||||||
if (W == NULL) internal_error("no such web");
|
if (W == NULL) internal_error("no such web");
|
||||||
return FIRST_IN_LINKED_LIST(tangle_target, W->tangle_targets);
|
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.
|
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
|
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
|
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
|
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
|
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
|
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
|
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) {
|
int Weaver::weave(weave_order *wv) {
|
||||||
heterogeneous_tree *tree = WeaveTree::new_tree(wv);
|
heterogeneous_tree *tree = WeaveTree::new_tree(wv);
|
||||||
TEMPORARY_TEXT(banner)
|
TEMPORARY_TEXT(banner)
|
||||||
|
@ -38,32 +38,32 @@ int Weaver::weave(weave_order *wv) {
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
int Weaver::weave_inner(weave_order *wv, heterogeneous_tree *tree, tree_node *body) {
|
int Weaver::weave_inner(weave_order *wv, heterogeneous_tree *tree, tree_node *body) {
|
||||||
web *W = wv->weave_web;
|
web *W = wv->weave_web;
|
||||||
int lines_woven = 0;
|
int lines_woven = 0;
|
||||||
weaver_state state_at; weaver_state *state = &state_at;
|
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;
|
chapter *C, *last_heading = NULL;
|
||||||
section *S;
|
section *S;
|
||||||
LOOP_OVER_LINKED_LIST(C, chapter, W->chapters)
|
LOOP_OVER_LINKED_LIST(C, chapter, W->chapters)
|
||||||
if (C->md->imported == FALSE) {
|
if (C->md->imported == FALSE) {
|
||||||
LOOP_OVER_LINKED_LIST(S, section, C->sections)
|
LOOP_OVER_LINKED_LIST(S, section, C->sections)
|
||||||
if (Reader::range_within(S->md->sect_range, wv->weave_range)) {
|
if (Reader::range_within(S->md->sect_range, wv->weave_range)) {
|
||||||
@<Weave any necessary chapter header@>;
|
<<Weave any necessary chapter header>>;
|
||||||
@<Weave any necessary section header@>;
|
<<Weave any necessary section header>>;
|
||||||
LanguageMethods::begin_weave(S, wv);
|
LanguageMethods::begin_weave(S, wv);
|
||||||
@<Weave this section@>;
|
<<Weave this section>>;
|
||||||
@<Weave any necessary section footer@>;
|
<<Weave any necessary section footer>>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@<Weave any necessary chapter footer@>;
|
<<Weave any necessary chapter footer>>;
|
||||||
return lines_woven;
|
return lines_woven;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Weave any necessary chapter header@> =
|
<<Weave any necessary chapter header>>=
|
||||||
if (last_heading != C) {
|
if (last_heading != C) {
|
||||||
@<Weave any necessary chapter footer@>;
|
<<Weave any necessary chapter footer>>;
|
||||||
tree_node *CH = WeaveTree::chapter(tree, C);
|
tree_node *CH = WeaveTree::chapter(tree, C);
|
||||||
Trees::make_child(CH, state->body_node);
|
Trees::make_child(CH, state->body_node);
|
||||||
state->chapter_node = CH;
|
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 (wv->theme_match == NULL) {
|
||||||
if (last_heading != NULL) {
|
if (last_heading != NULL) {
|
||||||
tree_node *F = WeaveTree::chapter_footer(tree, last_heading);
|
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);
|
tree_node *SH = WeaveTree::section(tree, S);
|
||||||
Trees::make_child(SH, state->chapter_node);
|
Trees::make_child(SH, state->chapter_node);
|
||||||
state->section_node = SH;
|
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);
|
Trees::make_child(H, state->section_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Weave any necessary section footer@> =
|
<<Weave any necessary section footer>>=
|
||||||
if (wv->theme_match == NULL) {
|
if (wv->theme_match == NULL) {
|
||||||
tree_node *F = WeaveTree::section_footer(tree, S);
|
tree_node *F = WeaveTree::section_footer(tree, S);
|
||||||
Trees::make_child(F, state->section_node);
|
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:
|
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... */
|
enum COMMENTARY_MATERIAL from 1
|
||||||
@e DEFINITION_MATERIAL /* ...versus when an |@d| definition is being made */
|
enum MACRO_MATERIAL /* when a macro is being defined... */
|
||||||
@e CODE_MATERIAL /* verbatim code */
|
enum DEFINITION_MATERIAL /* ...versus when an [[@d]] definition is being made */
|
||||||
@e ENDNOTES_MATERIAL /* endnotes at the foot of a paragraph */
|
enum CODE_MATERIAL /* verbatim code */
|
||||||
@e FOOTNOTES_MATERIAL /* footnote texts for a paragraph */
|
enum ENDNOTES_MATERIAL /* endnotes at the foot of a paragraph */
|
||||||
|
enum FOOTNOTES_MATERIAL /* footnote texts for a paragraph */
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
typedef struct weaver_state {
|
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 line_break_pending; /* insert a line break before the next woven line? */
|
||||||
int next_heading_without_vertical_skip;
|
int next_heading_without_vertical_skip;
|
||||||
int horizontal_rule_just_drawn;
|
int horizontal_rule_just_drawn;
|
||||||
|
@ -125,7 +126,7 @@ typedef struct weaver_state {
|
||||||
struct tree_node *ap;
|
struct tree_node *ap;
|
||||||
} weaver_state;
|
} weaver_state;
|
||||||
|
|
||||||
@<Start the weaver with a clean slate@> =
|
<<Start the weaver with a clean slate>>=
|
||||||
state->kind_of_material = COMMENTARY_MATERIAL;
|
state->kind_of_material = COMMENTARY_MATERIAL;
|
||||||
state->line_break_pending = FALSE;
|
state->line_break_pending = FALSE;
|
||||||
state->next_heading_without_vertical_skip = FALSE;
|
state->next_heading_without_vertical_skip = FALSE;
|
||||||
|
@ -139,15 +140,15 @@ typedef struct weaver_state {
|
||||||
state->material_node = NULL;
|
state->material_node = NULL;
|
||||||
state->ap = body;
|
state->ap = body;
|
||||||
|
|
||||||
@h Weaving a section.
|
@ \section{Weaving a section.}
|
||||||
|
|
||||||
@<Weave this section@> =
|
<<Weave this section>>=
|
||||||
paragraph *current_P = NULL;
|
paragraph *current_P = NULL;
|
||||||
int toc_made = FALSE;
|
int toc_made = FALSE;
|
||||||
for (source_line *LLL = S->first_line; LLL; LLL = LLL->next_line) {
|
for (source_line *LLL = S->first_line; LLL; LLL = LLL->next_line) {
|
||||||
wv->current_weave_line = LLL;
|
wv->current_weave_line = LLL;
|
||||||
if (LLL->owning_paragraph == NULL)
|
if (LLL->owning_paragraph == NULL)
|
||||||
@<Largely ignore this extra-mural line@>
|
<<Largely ignore this extra-mural line>>
|
||||||
else if (LLL->owning_paragraph != current_P) {
|
else if (LLL->owning_paragraph != current_P) {
|
||||||
if (toc_made == FALSE) {
|
if (toc_made == FALSE) {
|
||||||
if (Str::len(S->sect_purpose) > 0) {
|
if (Str::len(S->sect_purpose) > 0) {
|
||||||
|
@ -159,11 +160,11 @@ typedef struct weaver_state {
|
||||||
}
|
}
|
||||||
current_P = LLL->owning_paragraph;
|
current_P = LLL->owning_paragraph;
|
||||||
if (Tags::tagged_with(current_P, wv->theme_match))
|
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) {
|
if (LLL->category == INTERFACE_BODY_LCAT) {
|
||||||
state->horizontal_rule_just_drawn = FALSE;
|
state->horizontal_rule_just_drawn = FALSE;
|
||||||
continue;
|
continue;
|
||||||
|
@ -190,7 +191,7 @@ typedef struct weaver_state {
|
||||||
(LLL->category == SECTION_HEADING_LCAT))
|
(LLL->category == SECTION_HEADING_LCAT))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@<Weave this paragraph@> =
|
<<Weave this paragraph>>=
|
||||||
if (current_P->starts_on_new_page)
|
if (current_P->starts_on_new_page)
|
||||||
Trees::make_child(WeaveTree::pagebreak(tree), state->ap);
|
Trees::make_child(WeaveTree::pagebreak(tree), state->ap);
|
||||||
source_line *L = LLL;
|
source_line *L = LLL;
|
||||||
|
@ -198,27 +199,27 @@ typedef struct weaver_state {
|
||||||
(L->category != PARAGRAPH_START_LCAT))
|
(L->category != PARAGRAPH_START_LCAT))
|
||||||
Main::error_in_web(I"bad start to paragraph", L); /* should never happen */
|
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;
|
L = L->next_line;
|
||||||
for (; ((L) && (L->owning_paragraph == current_P)); L = L->next_line) {
|
for (; ((L) && (L->owning_paragraph == current_P)); L = L->next_line) {
|
||||||
wv->current_weave_line = L;
|
wv->current_weave_line = L;
|
||||||
if (LanguageMethods::skip_in_weaving(S->sect_language, wv, L) == FALSE) {
|
if (LanguageMethods::skip_in_weaving(S->sect_language, wv, L) == FALSE) {
|
||||||
lines_woven++;
|
lines_woven++;
|
||||||
@<Respond to any commands aimed at the weaver, and otherwise skip commands@>;
|
<<Respond to any commands aimed at the weaver, and otherwise skip commands>>;
|
||||||
@<Weave this line@>;
|
<<Weave this line>>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
L = NULL;
|
L = NULL;
|
||||||
Weaver::change_material(tree, state, ENDNOTES_MATERIAL, FALSE, NULL, NULL);
|
Weaver::change_material(tree, state, ENDNOTES_MATERIAL, FALSE, NULL, NULL);
|
||||||
Weaver::show_endnotes_on_previous_paragraph(tree, wv, state->ap, current_P);
|
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);
|
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->para_node = WeaveTree::paragraph_heading(tree, current_P,
|
||||||
state->next_heading_without_vertical_skip);
|
state->next_heading_without_vertical_skip);
|
||||||
Trees::make_child(state->para_node, state->section_node);
|
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
|
@ If we are weaving a selection of extracted paragraphs, normal conventions
|
||||||
about breaking pages at chapters and sections fail to work. So:
|
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);
|
text_stream *cap = Tags::retrieve_caption(L->owning_paragraph, wv->theme_match);
|
||||||
if (Str::len(cap) > 0) {
|
if (Str::len(cap) > 0) {
|
||||||
Weaver::weave_subheading(tree, wv, state->ap, C->md->ch_title);
|
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
|
@ There's quite likely ordinary text on the line following the paragraph
|
||||||
start indication, too, so we need to weave this out:
|
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) {
|
if (Str::len(L->text_operand2) > 0) {
|
||||||
TEMPORARY_TEXT(matter)
|
TEMPORARY_TEXT(matter)
|
||||||
WRITE_TO(matter, "%S\n", L->text_operand2);
|
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)
|
DISCARD_TEXT(matter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Weave this line@> =
|
<<Weave this line>>=
|
||||||
if (L->category == BEGIN_CODE_LCAT) {
|
if (L->category == BEGIN_CODE_LCAT) {
|
||||||
state->line_break_pending = FALSE;
|
state->line_break_pending = FALSE;
|
||||||
LanguageMethods::reset_syntax_colouring(S->sect_language);
|
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);
|
TEMPORARY_TEXT(matter) Str::copy(matter, L->text);
|
||||||
if (L->is_commentary) @<Weave verbatim matter in commentary style@>
|
if (L->is_commentary) <<Weave verbatim matter in commentary style>>
|
||||||
else @<Weave verbatim matter in code style@>;
|
else <<Weave verbatim matter in code style>>;
|
||||||
DISCARD_TEXT(matter)
|
DISCARD_TEXT(matter)
|
||||||
|
|
||||||
@ And lastly we ignore commands, or act on them if they happen to be aimed
|
@ 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.
|
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->category == COMMAND_LCAT) {
|
||||||
if (L->command_code == PAGEBREAK_CMD)
|
if (L->command_code == PAGEBREAK_CMD)
|
||||||
Trees::make_child(WeaveTree::pagebreak(tree), state->ap);
|
Trees::make_child(WeaveTree::pagebreak(tree), state->ap);
|
||||||
if (L->command_code == GRAMMAR_INDEX_CMD)
|
if (L->command_code == GRAMMAR_INDEX_CMD)
|
||||||
Trees::make_child(WeaveTree::grammar_index(tree), state->ap);
|
Trees::make_child(WeaveTree::grammar_index(tree), state->ap);
|
||||||
if (L->command_code == FIGURE_CMD) @<Weave a figure@>;
|
if (L->command_code == FIGURE_CMD) <<Weave a figure>>;
|
||||||
if (L->command_code == HTML_CMD) @<Weave a raw HTML extract@>;
|
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 == AUDIO_CMD) <<Weave an audio clip>>;
|
||||||
if (L->command_code == VIDEO_CMD) @<Weave a video clip@>;
|
if (L->command_code == VIDEO_CMD) <<Weave a video clip>>;
|
||||||
if (L->command_code == DOWNLOAD_CMD) @<Weave a download@>;
|
if (L->command_code == DOWNLOAD_CMD) <<Weave a download>>;
|
||||||
if (L->command_code == EMBED_CMD) @<Weave an embed@>;
|
if (L->command_code == EMBED_CMD) <<Weave an embed>>;
|
||||||
if (L->command_code == CAROUSEL_CMD) @<Weave a carousel@>;
|
if (L->command_code == CAROUSEL_CMD) <<Weave a carousel>>;
|
||||||
if (L->command_code == CAROUSEL_ABOVE_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_BELOW_CMD) <<Weave a carousel>>;
|
||||||
if (L->command_code == CAROUSEL_UNCAPTIONED_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 == CAROUSEL_END_CMD) <<Weave a carousel end>>;
|
||||||
/* Otherwise assume it was a tangler command, and ignore it here */
|
/* Otherwise assume it was a tangler command, and ignore it here */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Weave a figure@> =
|
<<Weave a figure>>=
|
||||||
int w, h;
|
int w, h;
|
||||||
text_stream *figname = Parser::dimensions(L->text_operand, &w, &h, L);
|
text_stream *figname = Parser::dimensions(L->text_operand, &w, &h, L);
|
||||||
Trees::make_child(WeaveTree::figure(tree, figname, w, h), state->ap);
|
Trees::make_child(WeaveTree::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),
|
Trees::make_child(WeaveTree::raw_extract(tree, L->text_operand),
|
||||||
state->ap);
|
state->ap);
|
||||||
|
|
||||||
@<Weave an audio clip@> =
|
<<Weave an audio clip>>=
|
||||||
int w, h;
|
int w, h;
|
||||||
text_stream *figname = Parser::dimensions(L->text_operand, &w, &h, L);
|
text_stream *figname = Parser::dimensions(L->text_operand, &w, &h, L);
|
||||||
Trees::make_child(WeaveTree::audio(tree, figname, w), state->ap);
|
Trees::make_child(WeaveTree::audio(tree, figname, w), state->ap);
|
||||||
|
|
||||||
@<Weave a video clip@> =
|
<<Weave a video clip>>=
|
||||||
int w, h;
|
int w, h;
|
||||||
text_stream *figname = Parser::dimensions(L->text_operand, &w, &h, L);
|
text_stream *figname = Parser::dimensions(L->text_operand, &w, &h, L);
|
||||||
Trees::make_child(WeaveTree::video(tree, figname, w, h), state->ap);
|
Trees::make_child(WeaveTree::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),
|
Trees::make_child(WeaveTree::download(tree, L->text_operand, L->text_operand2),
|
||||||
state->ap);
|
state->ap);
|
||||||
|
|
||||||
@<Weave an embed@> =
|
<<Weave an embed>>=
|
||||||
int w, h;
|
int w, h;
|
||||||
text_stream *ID = Parser::dimensions(L->text_operand2, &w, &h, L);
|
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);
|
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);
|
tree_node *C = WeaveTree::carousel_slide(tree, L->text_operand, L->command_code);
|
||||||
Trees::make_child(C, state->para_node);
|
Trees::make_child(C, state->para_node);
|
||||||
state->ap = C;
|
state->ap = C;
|
||||||
state->carousel_node = C;
|
state->carousel_node = C;
|
||||||
|
|
||||||
@<Weave a carousel end@> =
|
<<Weave a carousel end>>=
|
||||||
state->ap = state->para_node;
|
state->ap = state->para_node;
|
||||||
state->carousel_node = NULL;
|
state->carousel_node = NULL;
|
||||||
|
|
||||||
@h Commentary matter.
|
@ \section{Commentary matter.}
|
||||||
Typographically this is a fairly simple business: it's almost the case that
|
Typographically this is a fairly simple business: it's almost the case that
|
||||||
we only have to transcribe it. But not quite!
|
we only have to transcribe it. But not quite!
|
||||||
|
|
||||||
@<Weave verbatim matter in commentary style@> =
|
<<Weave verbatim matter in commentary style>>=
|
||||||
@<Weave displayed source in its own special style@>;
|
<<Weave displayed source in its own special style>>;
|
||||||
@<Weave a blank line as a thin vertical skip and paragraph break@>;
|
<<Weave a blank line as a thin vertical skip and paragraph break>>;
|
||||||
@<Weave bracketed list indications at start of line into items@>;
|
<<Weave bracketed list indications at start of line into items>>;
|
||||||
@<Weave tabbed code material as a new indented paragraph@>;
|
<<Weave tabbed code material as a new indented paragraph>>;
|
||||||
@<Weave footnotes@>;
|
<<Weave footnotes>>;
|
||||||
WRITE_TO(matter, "\n");
|
WRITE_TO(matter, "\n");
|
||||||
Weaver::commentary_text(tree, wv, state->ap, matter);
|
Weaver::commentary_text(tree, wv, state->ap, matter);
|
||||||
continue;
|
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) {
|
if (L->category == SOURCE_DISPLAY_LCAT) {
|
||||||
Trees::make_child(WeaveTree::display_line(tree, L->text_operand), state->ap);
|
Trees::make_child(WeaveTree::display_line(tree, L->text_operand), state->ap);
|
||||||
continue;
|
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
|
@ Our style is to use paragraphs without initial-line indentation, so we
|
||||||
add a vertical skip between them to show the division more clearly.
|
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 (Regexp::string_is_white_space(matter)) {
|
||||||
if ((L->next_line) && (L->next_line->category == COMMENT_BODY_LCAT)) {
|
if ((L->next_line) && (L->next_line->category == COMMENT_BODY_LCAT)) {
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if ((state->kind_of_material != CODE_MATERIAL) ||
|
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);
|
Trees::make_child(WeaveTree::vskip(tree, TRUE), state->ap);
|
||||||
Regexp::dispose_of(&mr);
|
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
|
@ 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();
|
match_results mr = Regexp::create_mr();
|
||||||
if (Regexp::match(&mr, matter, L"%(-...%) (%c*)")) { /* continue double */
|
if (Regexp::match(&mr, matter, L"%(-...%) (%c*)")) { /* continue double */
|
||||||
Weaver::change_material(tree, state, COMMENTARY_MATERIAL, FALSE, NULL, NULL);
|
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
|
@ Finally, matter encased in vertical strokes one tab stop in from column 1
|
||||||
in the source is set indented in code style.
|
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();
|
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)
|
TEMPORARY_TEXT(original)
|
||||||
Weaver::change_material(tree, state, CODE_MATERIAL, FALSE, NULL, NULL);
|
Weaver::change_material(tree, state, CODE_MATERIAL, FALSE, NULL, NULL);
|
||||||
Str::copy(original, mr.exp[0]);
|
Str::copy(original, mr.exp[0]);
|
||||||
|
@ -415,7 +416,7 @@ in the source is set indented in code style.
|
||||||
}
|
}
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
|
|
||||||
@<Weave footnotes@> =
|
<<Weave footnotes>>=
|
||||||
if (L->category == FOOTNOTE_TEXT_LCAT) {
|
if (L->category == FOOTNOTE_TEXT_LCAT) {
|
||||||
Weaver::change_material(tree, state, FOOTNOTES_MATERIAL, FALSE, NULL, NULL);
|
Weaver::change_material(tree, state, FOOTNOTES_MATERIAL, FALSE, NULL, NULL);
|
||||||
footnote *F = L->footnote_text;
|
footnote *F = L->footnote_text;
|
||||||
|
@ -425,21 +426,21 @@ in the source is set indented in code style.
|
||||||
state->ap = FN;
|
state->ap = FN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Code-like matter.
|
@ \section{Code-like matter.}
|
||||||
Even though Inweb's approach, unlike |CWEB|'s, is to respect the layout
|
Even though Inweb's approach, unlike [[CWEB]]'s, is to respect the layout
|
||||||
of the original, this is still quite typographically complex: commentary
|
of the original, this is still quite typographically complex: commentary
|
||||||
and macro usage is rendered differently.
|
and macro usage is rendered differently.
|
||||||
|
|
||||||
@<Weave verbatim matter in code style@> =
|
<<Weave verbatim matter in code style>>=
|
||||||
@<Change material if necessary@>;
|
<<Change material if necessary>>;
|
||||||
@<Weave a blank line as a thin vertical skip@>;
|
<<Weave a blank line as a thin vertical skip>>;
|
||||||
|
|
||||||
Str::rectify_indentation(matter, 4);
|
Str::rectify_indentation(matter, 4);
|
||||||
|
|
||||||
TEMPORARY_TEXT(prefatory)
|
TEMPORARY_TEXT(prefatory)
|
||||||
TEMPORARY_TEXT(concluding_comment)
|
TEMPORARY_TEXT(concluding_comment)
|
||||||
@<Extract any comment matter ending the line to be set in italic@>;
|
<<Extract any comment matter ending the line to be set in italic>>;
|
||||||
@<Give constant definition lines slightly fancier openings@>;
|
<<Give constant definition lines slightly fancier openings>>;
|
||||||
|
|
||||||
tree_node *CL = WeaveTree::code_line(tree);
|
tree_node *CL = WeaveTree::code_line(tree);
|
||||||
Trees::make_child(CL, state->ap);
|
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);
|
Trees::make_child(WeaveTree::weave_defn_node(tree, prefatory), CL);
|
||||||
Str::clear(prefatory);
|
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)
|
TEMPORARY_TEXT(colouring)
|
||||||
LanguageMethods::syntax_colour(S->sect_language, wv, L, matter, colouring);
|
LanguageMethods::syntax_colour(S->sect_language, wv, L, matter, colouring);
|
||||||
TextWeaver::source_code(tree, CL, matter, colouring, L->enable_hyperlinks);
|
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:
|
@ We're not in Kansas any more, so:
|
||||||
|
|
||||||
@<Change material if necessary@> =
|
<<Change material if necessary>>=
|
||||||
if (state->kind_of_material != CODE_MATERIAL) {
|
if (state->kind_of_material != CODE_MATERIAL) {
|
||||||
int will_be = CODE_MATERIAL;
|
int will_be = CODE_MATERIAL;
|
||||||
if (L->category == MACRO_DEFINITION_LCAT)
|
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 blank line is implemented differently in different formats, so it gets
|
||||||
a node of its own, a vskip:
|
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) {
|
if (state->line_break_pending) {
|
||||||
Trees::make_child(WeaveTree::vskip(tree, FALSE), state->ap);
|
Trees::make_child(WeaveTree::vskip(tree, FALSE), state->ap);
|
||||||
state->line_break_pending = FALSE;
|
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
|
@ Comments which run to the end of a line can be set in italic type, for
|
||||||
example, or flush left.
|
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_before_comment)
|
||||||
TEMPORARY_TEXT(part_within_comment)
|
TEMPORARY_TEXT(part_within_comment)
|
||||||
programming_language *pl = S->sect_language;
|
programming_language *pl = S->sect_language;
|
||||||
|
@ -523,9 +524,9 @@ example, or flush left.
|
||||||
DISCARD_TEXT(part_before_comment)
|
DISCARD_TEXT(part_before_comment)
|
||||||
DISCARD_TEXT(part_within_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) {
|
if (L->category == BEGIN_DEFINITION_LCAT) {
|
||||||
match_results mr = Regexp::create_mr();
|
match_results mr = Regexp::create_mr();
|
||||||
if ((Regexp::match(&mr, matter, L"@d (%c*)")) ||
|
if ((Regexp::match(&mr, matter, L"@d (%c*)")) ||
|
||||||
|
@ -543,7 +544,7 @@ example, or flush left.
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@<Offer the line to the language to weave@> =
|
<<Offer the line to the language to weave>>=
|
||||||
TEMPORARY_TEXT(OUT)
|
TEMPORARY_TEXT(OUT)
|
||||||
int taken = LanguageMethods::weave_code_line(OUT, S->sect_language, wv,
|
int taken = LanguageMethods::weave_code_line(OUT, S->sect_language, wv,
|
||||||
W, C, S, L, matter, concluding_comment);
|
W, C, S, L, matter, concluding_comment);
|
||||||
|
@ -554,7 +555,7 @@ example, or flush left.
|
||||||
DISCARD_TEXT(OUT)
|
DISCARD_TEXT(OUT)
|
||||||
if (taken) goto ClumsyLabel;
|
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();
|
match_results mr = Regexp::create_mr();
|
||||||
while (Regexp::match(&mr, matter, L"(%c*?)%@%<(%c*?)%@%>(%c*)")) {
|
while (Regexp::match(&mr, matter, L"(%c*?)%@%<(%c*?)%@%>(%c*)")) {
|
||||||
para_macro *pmac = Macros::find_by_name(mr.exp[1], S);
|
para_macro *pmac = Macros::find_by_name(mr.exp[1], S);
|
||||||
|
@ -571,29 +572,29 @@ example, or flush left.
|
||||||
}
|
}
|
||||||
Regexp::dispose_of(&mr);
|
Regexp::dispose_of(&mr);
|
||||||
|
|
||||||
@h Endnotes.
|
@ \section{Endnotes.}
|
||||||
The endnotes describe function calls from far away, or unexpected
|
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,
|
void Weaver::show_endnotes_on_previous_paragraph(heterogeneous_tree *tree,
|
||||||
weave_order *wv, tree_node *ap, paragraph *P) {
|
weave_order *wv, tree_node *ap, paragraph *P) {
|
||||||
tree_node *body = ap;
|
tree_node *body = ap;
|
||||||
theme_tag *T = Tags::find_by_name(I"Preform", FALSE);
|
theme_tag *T = Tags::find_by_name(I"Preform", FALSE);
|
||||||
if ((T) && (Tags::tagged_with(P, T)))
|
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);
|
Tags::show_endnote_on_ifdefs(tree, ap, P);
|
||||||
if (P->defines_macro)
|
if (P->defines_macro)
|
||||||
@<Show endnote on where paragraph macro is used@>;
|
<<Show endnote on where paragraph macro is used>>;
|
||||||
language_function *fn;
|
language_function *fn;
|
||||||
LOOP_OVER_LINKED_LIST(fn, language_function, P->functions)
|
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;
|
language_type *st;
|
||||||
LOOP_OVER_LINKED_LIST(st, language_type, P->structures)
|
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);
|
tree_node *E = WeaveTree::endnote(tree);
|
||||||
Trees::make_child(E, body); ap = E;
|
Trees::make_child(E, body); ap = E;
|
||||||
TextWeaver::commentary_text(tree, ap, I"This is ");
|
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)
|
DISCARD_TEXT(url)
|
||||||
TextWeaver::commentary_text(tree, ap, I", not regular C code.");
|
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);
|
tree_node *E = WeaveTree::endnote(tree);
|
||||||
Trees::make_child(E, body); ap = E;
|
Trees::make_child(E, body); ap = E;
|
||||||
TextWeaver::commentary_text(tree, ap, I"This code is ");
|
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".");
|
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)
|
if (fn->usage_described == FALSE)
|
||||||
Weaver::show_function_usage(tree, wv, ap, P, fn, 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);
|
tree_node *E = WeaveTree::endnote(tree);
|
||||||
Trees::make_child(E, body); ap = E;
|
Trees::make_child(E, body); ap = E;
|
||||||
TextWeaver::commentary_text(tree, ap, I"The structure ");
|
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".");
|
TextWeaver::commentary_text(tree, ap, I".");
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
void Weaver::show_function_usage(heterogeneous_tree *tree, weave_order *wv,
|
void Weaver::show_function_usage(heterogeneous_tree *tree, weave_order *wv,
|
||||||
tree_node *ap, paragraph *P, language_function *fn, int as_list) {
|
tree_node *ap, paragraph *P, language_function *fn, int as_list) {
|
||||||
tree_node *body = ap;
|
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)
|
LOOP_OVER_LINKED_LIST(hteu, hash_table_entry_usage, hte->usages)
|
||||||
if ((P != hteu->usage_recorded_at) &&
|
if ((P != hteu->usage_recorded_at) &&
|
||||||
(P->under_section == hteu->usage_recorded_at->under_section))
|
(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)
|
LOOP_OVER_LINKED_LIST(hteu, hash_table_entry_usage, hte->usages)
|
||||||
if (P->under_section != hteu->usage_recorded_at->under_section)
|
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 (used_flag == FALSE) {
|
||||||
if (as_list == FALSE) {
|
if (as_list == FALSE) {
|
||||||
TextWeaver::commentary_text(tree, ap, I" appears nowhere else");
|
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 (as_list == FALSE) {
|
||||||
if (used_flag == FALSE) TextWeaver::commentary_text(tree, ap, I" is used in ");
|
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);
|
Trees::make_child(WeaveTree::locale(tree, hteu->usage_recorded_at, NULL), ap);
|
||||||
last_cited_in = hteu->usage_recorded_at->under_section;
|
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,
|
void Weaver::weave_subheading(heterogeneous_tree *tree, weave_order *wv,
|
||||||
tree_node *ap, text_stream *text) {
|
tree_node *ap, text_stream *text) {
|
||||||
tree_node *D = WeaveTree::subheading(tree, 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);
|
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
|
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,
|
int Weaver::weave_table_of_contents(heterogeneous_tree *tree,
|
||||||
tree_node *ap, section *S) {
|
tree_node *ap, section *S) {
|
||||||
int noteworthy = 0;
|
int noteworthy = 0;
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
To manage the weaving of commentary or source code text.
|
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 --
|
The following takes text, divides it up at stroke-mark boundaries --
|
||||||
that is, |this is inside|, this is outside -- and sends contiguous pieces
|
that is, [[this is inside]], this is outside -- and sends contiguous pieces
|
||||||
of it either to |TextWeaver::inline_code_fragment| or |TextWeaver::commentary_fragment|
|
of it either to [[TextWeaver::inline_code_fragment]] or [[TextWeaver::commentary_fragment]]
|
||||||
as appropriate.
|
as appropriate.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void TextWeaver::commentary_text(heterogeneous_tree *tree, tree_node *ap, text_stream *matter) {
|
void TextWeaver::commentary_text(heterogeneous_tree *tree, tree_node *ap, text_stream *matter) {
|
||||||
TextWeaver::commentary_r(tree, ap, matter, FALSE, FALSE);
|
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 =
|
text_stream *code_in_comments_notation =
|
||||||
Bibliographic::get_datum(wv->weave_web->md,
|
Bibliographic::get_datum(wv->weave_web->md,
|
||||||
(in_code)?(I"Code In Code Comments Notation"):(I"Code In Commentary Notation"));
|
(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;
|
int display_flag = TRUE;
|
||||||
text_stream *tex_notation = Bibliographic::get_datum(wv->weave_web->md,
|
text_stream *tex_notation = Bibliographic::get_datum(wv->weave_web->md,
|
||||||
I"TeX Mathematics Displayed Notation");
|
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;
|
display_flag = FALSE;
|
||||||
tex_notation = Bibliographic::get_datum(wv->weave_web->md,
|
tex_notation = Bibliographic::get_datum(wv->weave_web->md,
|
||||||
I"TeX Mathematics Notation");
|
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,
|
text_stream *xref_notation = Bibliographic::get_datum(wv->weave_web->md,
|
||||||
I"Cross-References Notation");
|
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) {
|
if (within) {
|
||||||
TextWeaver::inline_code_fragment(tree, ap, matter);
|
TextWeaver::inline_code_fragment(tree, ap, matter);
|
||||||
} else {
|
} else {
|
||||||
@<Recognise hyperlinks@>;
|
<<Recognise hyperlinks>>;
|
||||||
@<Detect use of footnotes@>;
|
<<Detect use of footnotes>>;
|
||||||
TextWeaver::commentary_fragment(tree, ap, matter, in_code);
|
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++) {
|
for (int i=0; i < Str::len(matter); i++) {
|
||||||
if (Str::get_at(matter, i) == '\\') i += Str::len(code_in_comments_notation) - 1;
|
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)) {
|
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++) {
|
for (int i=0; i < Str::len(matter); i++) {
|
||||||
if ((Str::includes_at(matter, i, I"http://")) ||
|
if ((Str::includes_at(matter, i, I"http://")) ||
|
||||||
(Str::includes_at(matter, i, I"https://"))) {
|
(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);
|
int N = Str::len(tex_notation);
|
||||||
for (int i=0; i < Str::len(matter); i++) {
|
for (int i=0; i < Str::len(matter); i++) {
|
||||||
if ((within == FALSE) && (Str::includes_at(matter, i, tex_notation))) {
|
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(before)
|
||||||
TEMPORARY_TEXT(cue)
|
TEMPORARY_TEXT(cue)
|
||||||
TEMPORARY_TEXT(after)
|
TEMPORARY_TEXT(after)
|
||||||
|
@ -146,7 +146,7 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre
|
||||||
DISCARD_TEXT(after)
|
DISCARD_TEXT(after)
|
||||||
if (allow) return;
|
if (allow) return;
|
||||||
|
|
||||||
@<Recognise cross-references@> =
|
<<Recognise cross-references>>=
|
||||||
int N = Str::len(xref_notation);
|
int N = Str::len(xref_notation);
|
||||||
for (int i=0; i < Str::len(matter); i++) {
|
for (int i=0; i < Str::len(matter); i++) {
|
||||||
if ((within == FALSE) && (Str::includes_at(matter, i, xref_notation)) &&
|
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(before, Str::start(matter), Str::at(matter, i));
|
||||||
Str::substr(reference, Str::at(matter, i + N), Str::at(matter, j));
|
Str::substr(reference, Str::at(matter, i + N), Str::at(matter, j));
|
||||||
Str::substr(after, Str::at(matter, j + N), Str::end(matter));
|
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(before)
|
||||||
DISCARD_TEXT(reference)
|
DISCARD_TEXT(reference)
|
||||||
DISCARD_TEXT(after)
|
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(url)
|
||||||
TEMPORARY_TEXT(title)
|
TEMPORARY_TEXT(title)
|
||||||
int ext = FALSE;
|
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
|
@ This tests whether a cross-reference is allowed to begin or end: it must
|
||||||
begin after and finish before a "boundary character".
|
begin after and finish before a "boundary character".
|
||||||
|
|
||||||
Note the one-sided treatment of |:|, which is a boundary after but not before,
|
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 |//|
|
so that [[http://]] won't trigger a cross-reference with the standard [[//]]
|
||||||
xref notation.
|
xref notation.
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
int TextWeaver::boundary_character(int before, wchar_t c) {
|
int TextWeaver::boundary_character(int before, wchar_t c) {
|
||||||
if (c == 0) return TRUE;
|
if (c == 0) return TRUE;
|
||||||
if (Characters::is_whitespace(c)) 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;
|
(c == '(')|| (c == ')')) return TRUE;
|
||||||
if ((before == FALSE) && (c == ':')) return TRUE;
|
if ((before == FALSE) && (c == ':')) return TRUE;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ =
|
<<*>>=
|
||||||
void TextWeaver::commentary_fragment(heterogeneous_tree *tree, tree_node *ap,
|
void TextWeaver::commentary_fragment(heterogeneous_tree *tree, tree_node *ap,
|
||||||
text_stream *fragment, int in_code) {
|
text_stream *fragment, int in_code) {
|
||||||
if (Str::len(fragment) > 0)
|
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);
|
Trees::make_child(SC, I);
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Code text.
|
@ \section{Code text.}
|
||||||
|
|
||||||
=
|
<<*>>=
|
||||||
void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap,
|
void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap,
|
||||||
text_stream *matter, text_stream *colouring, int linked) {
|
text_stream *matter, text_stream *colouring, int linked) {
|
||||||
weave_document_node *C = RETRIEVE_POINTER_weave_document_node(tree->root->content);
|
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;
|
int from = 0;
|
||||||
for (int i=0; i < Str::len(matter); i++) {
|
for (int i=0; i < Str::len(matter); i++) {
|
||||||
if (linked) {
|
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,
|
text_stream *xref_notation = Bibliographic::get_datum(wv->weave_web->md,
|
||||||
I"Cross-References Notation");
|
I"Cross-References Notation");
|
||||||
if (Str::ne(xref_notation, I"Off"))
|
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) &&
|
if ((Str::get_at(colouring, i) == FUNCTION_COLOUR) &&
|
||||||
(wv->current_weave_line->category != TEXT_EXTRACT_LCAT)) {
|
(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++));
|
PUT_TO(fname, Str::get_at(matter, j++));
|
||||||
if (Analyser::is_reserved_word_for_section(
|
if (Analyser::is_reserved_word_for_section(
|
||||||
wv->current_weave_line->owning_section, fname, FUNCTION_COLOUR))
|
wv->current_weave_line->owning_section, fname, FUNCTION_COLOUR))
|
||||||
@<Spot the function@>;
|
<<Spot the function>>;
|
||||||
DISCARD_TEXT(fname)
|
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));
|
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://")) ||
|
if ((Str::includes_at(matter, i, I"http://")) ||
|
||||||
(Str::includes_at(matter, i, I"https://"))) {
|
(Str::includes_at(matter, i, I"https://"))) {
|
||||||
TEMPORARY_TEXT(after)
|
TEMPORARY_TEXT(after)
|
||||||
|
@ -273,7 +273,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap,
|
||||||
DISCARD_TEXT(after)
|
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);
|
int N = Str::len(xref_notation);
|
||||||
if ((Str::includes_at(matter, i, xref_notation))) {
|
if ((Str::includes_at(matter, i, xref_notation))) {
|
||||||
int j = i + N+1;
|
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)) {
|
if (Str::includes_at(matter, j, xref_notation)) {
|
||||||
TEMPORARY_TEXT(reference)
|
TEMPORARY_TEXT(reference)
|
||||||
Str::substr(reference, Str::at(matter, i + N), Str::at(matter, j));
|
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)
|
DISCARD_TEXT(reference)
|
||||||
break;
|
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(url)
|
||||||
TEMPORARY_TEXT(title)
|
TEMPORARY_TEXT(title)
|
||||||
int ext = FALSE;
|
int ext = FALSE;
|
||||||
|
@ -304,7 +304,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap,
|
||||||
DISCARD_TEXT(url)
|
DISCARD_TEXT(url)
|
||||||
DISCARD_TEXT(title)
|
DISCARD_TEXT(title)
|
||||||
|
|
||||||
@<Spot the function@> =
|
<<Spot the function>>=
|
||||||
language_function *fn = Analyser::get_function(
|
language_function *fn = Analyser::get_function(
|
||||||
wv->current_weave_line->owning_section, fname, FUNCTION_COLOUR);
|
wv->current_weave_line->owning_section, fname, FUNCTION_COLOUR);
|
||||||
if (fn) {
|
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,
|
void TextWeaver::source_code_piece(heterogeneous_tree *tree, tree_node *ap,
|
||||||
text_stream *matter, text_stream *colouring, int from, int to) {
|
text_stream *matter, text_stream *colouring, int from, int to) {
|
||||||
if (to > from) {
|
if (to > from) {
|
Loading…
Reference in a new issue