diff --git a/Chapter_3/The Analyser.w b/Chapter_3/The_Analyser.nw similarity index 82% rename from Chapter_3/The Analyser.w rename to Chapter_3/The_Analyser.nw index 74e0b56..adbdf7d 100644 --- a/Chapter_3/The Analyser.w +++ b/Chapter_3/The_Analyser.nw @@ -3,11 +3,11 @@ Here we analyse the code in the web, enabling us to see how functions and data structures are used within the program. -@h Scanning webs. +@ \section{Scanning webs.} This scanner is intended for debugging Inweb, and simply shows the main result of reading in and parsing the web: -= +<<*>>= void Analyser::scan_line_categories(web *W, text_stream *range) { PRINT("Scan of source lines for '%S'\n", range); int count = 1; @@ -16,39 +16,40 @@ void Analyser::scan_line_categories(web *W, text_stream *range) { section *S; LOOP_OVER_LINKED_LIST(S, section, C->sections) for (source_line *L = S->first_line; L; L = L->next_line) - @; + <>; } else { section *S = Reader::get_section_for_range(W, range); if (S) { for (source_line *L = S->first_line; L; L = L->next_line) - @ + <> } else { LOOP_OVER_LINKED_LIST(C, chapter, W->chapters) LOOP_OVER_LINKED_LIST(S, section, C->sections) for (source_line *L = S->first_line; L; L = L->next_line) - @; + <>; } } } -@ = +<>= TEMPORARY_TEXT(C) WRITE_TO(C, "%s", Lines::category_name(L->category)); while (Str::len(C) < 20) PUT_TO(C, '.'); PRINT("%07d %S %S\n", count++, C, L->text); DISCARD_TEXT(C) -@h The section catalogue. +@ \section{The section catalogue.} This provides quite a useful overview of the sections. As we'll see frequently in Chapter 4, we call out to a general routine in Chapter 5 to provide annotations which are programming-language specific; the aim is to abstract so that Chapter 4 contains no assumptions about the language. -@enum BASIC_SECTIONCAT from 1 -@enum STRUCTURES_SECTIONCAT -@enum FUNCTIONS_SECTIONCAT +<<*>>= +enum BASIC_SECTIONCAT from 1 +enum STRUCTURES_SECTIONCAT +enum FUNCTIONS_SECTIONCAT -= +<<*>>= void Analyser::catalogue_the_sections(web *W, text_stream *range, int form) { int max_width = 0, max_range_width = 0; chapter *C; @@ -79,7 +80,7 @@ void Analyser::catalogue_the_sections(web *W, text_stream *range, int form) { } } -@h Analysing code. +@ \section{Analysing code.} We can't pretend to a full-scale static analysis of the code -- for one thing, that would mean knowing more about the syntax of the web's language than we actually do. So the following provides only a toolkit which other code can @@ -91,35 +92,36 @@ punctuation around them. Usage codes are used to define a set of allowed contexts in which to spot these identifiers. -@d ELEMENT_ACCESS_USAGE 0x00000001 /* C-like languages: access via |->| or |.| operators to structure element */ -@d FCALL_USAGE 0x00000002 /* C-like languages: function call made using brackets, |name(args)| */ -@d PREFORM_IN_CODE_USAGE 0x00000004 /* InC only: use of a Preform nonterminal as a C "constant" */ -@d PREFORM_IN_GRAMMAR_USAGE 0x00000008 /* InC only: ditto, but within Preform production rather than C code */ -@d MISC_USAGE 0x00000010 /* any other appearance as an identifier */ -@d ANY_USAGE 0x7fffffff /* any of the above */ +<<*>> +#define ELEMENT_ACCESS_USAGE 0x00000001 /* C-like languages: access via [[->| or |.]] operators to structure element */ +#define FCALL_USAGE 0x00000002 /* C-like languages: function call made using brackets, [[name(args)]] */ +#define PREFORM_IN_CODE_USAGE 0x00000004 /* InC only: use of a Preform nonterminal as a C "constant" */ +#define PREFORM_IN_GRAMMAR_USAGE 0x00000008 /* InC only: ditto, but within Preform production rather than C code */ +#define MISC_USAGE 0x00000010 /* any other appearance as an identifier */ +#define ANY_USAGE 0x7fffffff /* any of the above */ @ The main analysis routine goes through a web as follows. Note that we only perform the search here, we don't comment on the results; any action to be -taken must be handled by |LanguageMethods::late_preweave_analysis| when we're done. +taken must be handled by [[LanguageMethods::late_preweave_analysis]] when we're done. -= +<<*>>= void Analyser::analyse_code(web *W) { if (W->analysed) return; - @; + <>; chapter *C; section *S; LOOP_WITHIN_TANGLE(C, S, Tangler::primary_target(W)) switch (L->category) { case BEGIN_DEFINITION_LCAT: - @; + <>; break; case CODE_BODY_LCAT: - @; + <>; break; case PREFORM_GRAMMAR_LCAT: - @; + <>; break; } @@ -133,11 +135,11 @@ below) to look for names of particular functions it knows about. In Version 1 webs, this code is also expected to parse any Interface lines in a section which it recognises, marking those by setting their -|interface_line_identified| flags. Any that are left must be erroneous. +[[interface_line_identified]] flags. Any that are left must be erroneous. Version 2 removed Interface altogeter as being cumbersome for no real gain in practice. -@ = +<>= LanguageMethods::early_preweave_analysis(W->main_language, W); chapter *C; @@ -148,10 +150,10 @@ practice. (Regexp::string_is_white_space(L->text) == FALSE)) Main::error_in_web(I"unrecognised interface line", L); -@ = +<>= Analyser::analyse_as_code(W, L, L->text, 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)) { L = L->next_line; @@ -159,29 +161,29 @@ practice. } @ Lines in a Preform grammar generally take the form of some BNF grammar, where -we want only to identify any nonterminals mentioned, then a |==>| divider, +we want only to identify any nonterminals mentioned, then a [[==>]] divider, and then some C code to deal with a match. The code is subjected to analysis just as any other code would be. -@ = +<>= Analyser::analyse_as_code(W, L, L->text_operand2, ANY_USAGE, 0); Analyser::analyse_as_code(W, L, L->text_operand, PREFORM_IN_CODE_USAGE, PREFORM_IN_GRAMMAR_USAGE); -@h Identifier searching. -Here's what we actually do, then. We take the code fragment |text|, drawn -from part or all of source line |L| from web |W|, and look for any identifier -names used in one of the contexts in the bitmap |mask|. Any that we find are -passed to |Analyser::analyse_find|, along with the context they were found in (or, if -|transf| is nonzero, with |transf| as their context). +@ \section{Identifier searching.} +Here's what we actually do, then. We take the code fragment [[text]], drawn +from part or all of source line [[L]] from web [[W]], and look for any identifier +names used in one of the contexts in the bitmap [[mask]]. Any that we find are +passed to [[Analyser::analyse_find]], along with the context they were found in (or, if +[[transf]] is nonzero, with [[transf]] as their context). What we do is to look for instances of an identifier, defined as a maximal -string of |%i| characters or hyphens not followed by |>| characters. (Thus -|fish-or-chips| counts, but |fish-| is not an identifier when it occurs in -|fish->bone|.) +string of [[%i]] characters or hyphens not followed by [[>]] characters. (Thus +[[fish-or-chips]] counts, but [[fish-]] is not an identifier when it occurs in +[[fish->bone]].) -= +<<*>>= void Analyser::analyse_as_code(web *W, source_line *L, text_stream *text, int mask, int transf) { int start_at = -1, element_follows = FALSE; for (int i = 0; i < Str::len(text); i++) { @@ -189,7 +191,7 @@ void Analyser::analyse_as_code(web *W, source_line *L, text_stream *text, int ma ((Str::get_at(text, i) == '-') && (Str::get_at(text, i+1) != '>'))) { if (start_at == -1) start_at = i; } else { - if (start_at != -1) @; + if (start_at != -1) <>; if (Str::get_at(text, i) == '.') element_follows = TRUE; else if ((Str::get_at(text, i) == '-') && (Str::get_at(text, i+1) == '>')) { element_follows = TRUE; i++; @@ -198,11 +200,11 @@ void Analyser::analyse_as_code(web *W, source_line *L, text_stream *text, int ma } if (start_at != -1) { int i = Str::len(text); - @; + <>; } } -@ = +<>= int u = MISC_USAGE; if (element_follows) u = ELEMENT_ACCESS_USAGE; else if (Str::get_at(text, i) == '(') u = FCALL_USAGE; @@ -218,17 +220,18 @@ void Analyser::analyse_as_code(web *W, source_line *L, text_stream *text, int ma } start_at = -1; element_follows = FALSE; -@h The identifier hash table. +@ \section{The identifier hash table.} We clearly need rapid access to a large symbols table, and we store this as a hash. Identifiers are hash-coded with the following simple code, which is simplified from one used by Inform; it's the algorithm called "X 30011" in Aho, Sethi and Ullman, "Compilers: Principles, Techniques and Tools" (1986), adapted slightly to separate out literal numbers. -@d HASH_TAB_SIZE 1000 /* the possible hash codes are 0 up to this minus 1 */ -@d NUMBER_HASH 0 /* literal decimal integers, and no other words, have this hash code */ +<<*>>= +#define HASH_TAB_SIZE 1000 /* the possible hash codes are 0 up to this minus 1 */ +#define NUMBER_HASH 0 /* literal decimal integers, and no other words, have this hash code */ -= +<<*>>= int Analyser::hash_code_from_word(text_stream *text) { unsigned int hash_code = 0; string_position p = Str::start(text); @@ -251,11 +254,12 @@ int Analyser::hash_code_from_word(text_stream *text) { @ The actual table is stored here: -@d HASH_SAFETY_CODE 0x31415927 +<<*>>= +#define HASH_SAFETY_CODE 0x31415927 -= +<<*>>= typedef struct hash_table { - struct linked_list *analysis_hash[HASH_TAB_SIZE]; /* of |hash_table_entry| */ + struct linked_list *analysis_hash[HASH_TAB_SIZE]; /* of [[hash_table_entry]] */ int safety_code; /* when we start up, array's contents are undefined, so... */ } hash_table; @@ -266,11 +270,11 @@ void Analyser::initialise_hash_table(hash_table *HT) { @ Where we define: -= +<<*>>= typedef struct hash_table_entry { text_stream *hash_key; int language_reserved_word; /* in the language currently being woven, that is */ - struct linked_list *usages; /* of |hash_table_entry_usage| */ + struct linked_list *usages; /* of [[hash_table_entry_usage]] */ struct source_line *definition_line; /* or null, if it's not a constant, function or type name */ struct language_function *as_function; /* for function names only */ CLASS_DEFINITION @@ -281,7 +285,7 @@ in it, as usual with symbols tables. For example, the code to handle C-like languages prepares for code analysis by calling this routine on the name of each C function. -= +<<*>>= hash_table_entry *Analyser::find_hash_entry(hash_table *HT, text_stream *text, int create) { int h = Analyser::hash_code_from_word(text); if (h == NUMBER_HASH) return NULL; @@ -316,7 +320,7 @@ hash_table_entry *Analyser::find_hash_entry_for_section(section *S, text_stream @ Marking and testing these bits: -= +<<*>>= hash_table_entry *Analyser::mark_reserved_word(hash_table *HT, text_stream *p, int e) { hash_table_entry *hte = Analyser::find_hash_entry(HT, p, TRUE); hte->language_reserved_word |= (1 << (e % 32)); @@ -366,16 +370,16 @@ but that it can be used in multiple ways within that paragraph: for example, a function might be both called and used as a constant value within the same paragraph of code. -= +<<*>>= typedef struct hash_table_entry_usage { struct paragraph *usage_recorded_at; - int form_of_usage; /* bitmap of the |*_USAGE| constants defined above */ + int form_of_usage; /* bitmap of the [[*_USAGE]] constants defined above */ CLASS_DEFINITION } hash_table_entry_usage; @ And here's how we create these usages: -= +<<*>>= void Analyser::analyse_find(web *W, source_line *L, text_stream *identifier, int u) { hash_table_entry *hte = Analyser::find_hash_entry_for_section(L->owning_section, identifier, FALSE); @@ -394,11 +398,11 @@ void Analyser::analyse_find(web *W, source_line *L, text_stream *identifier, int hteu->form_of_usage |= u; } -@h Open-source project support. +@ \section{Open-source project support.} The work here is all delegated. In each case we look for a script in the web's folder: failing that, we fall back on a default script belonging to Inweb. -= +<<*>>= void Analyser::write_makefile(web *W, filename *F, module_search *I, text_stream *platform) { pathname *P = W->md->path_to_web; text_stream *short_name = Pathnames::directory_name(P); diff --git a/Chapter_3/The Collater.w b/Chapter_3/The_Collater.nw similarity index 84% rename from Chapter_3/The Collater.w rename to Chapter_3/The_Collater.nw index 7fb9be1..3b89b3a 100644 --- a/Chapter_3/The Collater.w +++ b/Chapter_3/The_Collater.nw @@ -2,7 +2,7 @@ To collate material generated by the weaver into finished, fully-woven files. -@h Collation. +@ \section{Collation.} This is the process of reading a template file, substituting material into placeholders in it, and writing the result. @@ -15,7 +15,7 @@ directly. For convenience, we provide three ways to call: -= +<<*>>= void Collater::for_web_and_pattern(text_stream *OUT, web *W, weave_pattern *pattern, filename *F, filename *into) { Collater::collate(OUT, W, I"", F, pattern, NULL, NULL, NULL, into); @@ -39,12 +39,13 @@ void Collater::collate(text_stream *OUT, web *W, text_stream *range, @ The current state of the processor is recorded in the following. -@d TRACE_COLLATER_EXECUTION FALSE /* set true for debugging */ +<<*>>= +#define TRACE_COLLATER_EXECUTION FALSE /* set true for debugging */ -@d MAX_TEMPLATE_LINES 8192 /* maximum number of lines in template */ -@d CI_STACK_CAPACITY 8 /* maximum recursion of chapter/section iteration */ +#define MAX_TEMPLATE_LINES 8192 /* maximum number of lines in template */ +#define CI_STACK_CAPACITY 8 /* maximum recursion of chapter/section iteration */ -= +<<*>>= typedef struct collater_state { struct web *for_web; struct text_stream *tlines[MAX_TEMPLATE_LINES]; @@ -62,14 +63,14 @@ typedef struct collater_state { struct filename *errors_at; struct weave_order *wv; struct filename *into_file; - struct linked_list *modules; /* of |module| */ + struct linked_list *modules; /* of [[module]] */ } collater_state; @ Note the unfortunate maximum size limit on the template file. It means that really humungous Javascript files in plugins might have trouble, though if so, they can always be subdivided. -= +<<*>>= collater_state Collater::initial_state(web *W, text_stream *range, filename *template_filename, weave_pattern *pattern, filename *nav_file, linked_list *crumbs, weave_order *wv, filename *into) { @@ -88,13 +89,13 @@ collater_state Collater::initial_state(web *W, text_stream *range, cls.modules = NEW_LINKED_LIST(module); if (W) { int c = LinkedLists::len(W->md->as_module->dependencies); - if (c > 0) @
; + if (c > 0) <>; } - @; + <>; return cls; } -@ = +<>= module **module_array = Memory::calloc(c, sizeof(module *), ARRAY_SORTING_MREASON); module *M; int d=0; @@ -105,7 +106,7 @@ collater_state Collater::initial_state(web *W, text_stream *range, for (int d=0; d = +<>= TextFiles::read(template_filename, FALSE, "can't find contents template", TRUE, Collater::temp_line, NULL, &cls); if (TRACE_COLLATER_EXECUTION) @@ -114,7 +115,7 @@ collater_state Collater::initial_state(web *W, text_stream *range, PRINT("Warning: template <%f> truncated after %d line(s)\n", template_filename, cls.no_tlines); -@ = +<<*>>= void Collater::temp_line(text_stream *line, text_file_position *tfp, void *v_ies) { collater_state *cls = (collater_state *) v_ies; if (cls->no_tlines < MAX_TEMPLATE_LINES) @@ -123,14 +124,14 @@ void Collater::temp_line(text_stream *line, text_file_position *tfp, void *v_ies @ Running the engine... -= +<<*>>= void Collater::process(text_stream *OUT, collater_state *cls) { int lpos = 0; /* This is our program counter: a line number in the template */ while (lpos < cls->no_tlines) { match_results mr = Regexp::create_mr(); TEMPORARY_TEXT(tl) Str::copy(tl, cls->tlines[lpos++]); /* Fetch the line at the program counter and advance */ - @; + <>; WRITE("%S\n", tl); /* Copy the now finished line to the output */ DISCARD_TEXT(tl) CYCLE: ; @@ -140,29 +141,29 @@ void Collater::process(text_stream *OUT, collater_state *cls) { cls->inside_navigation_submenu = FALSE; } -@ = +<>= if (Regexp::match(&mr, tl, L"(%c*?) ")) Str::copy(tl, mr.exp[0]); /* Strip trailing spaces */ if (TRACE_COLLATER_EXECUTION) - @; + <>; if ((Regexp::match(&mr, tl, L"%[%[(%c+)%]%]")) || (Regexp::match(&mr, tl, L" %[%[(%c+)%]%]"))) { TEMPORARY_TEXT(command) Str::copy(command, mr.exp[0]); - @; - @; - @; - @; - @; + <>; + <>; + <>; + <>; + <>; DISCARD_TEXT(command) } - @; - @; - @; + <>; + <>; + <>; -@h The repeat stack and loops. +@ \section{The repeat stack and loops.} This is used only for debugging: -@ = +<>= PRINT("%04d: %S\nStack:", lpos-1, tl); for (int j=0; jsp; j++) { if (cls->repeat_stack_level[j] == CHAPTER_LEVEL) @@ -184,7 +185,7 @@ This is used only for debugging: one-iteration loop in which the loop variable has the given section or chapter as its value during the sole iteration. -@ = +<>= match_results mr = Regexp::create_mr(); if (Regexp::match(&mr, command, L"Select (%c*)")) { chapter *C; @@ -210,7 +211,7 @@ chapter as its value during the sole iteration. @ Conditionals: -@ = +<>= if (Regexp::match(&mr, command, L"If (%c*)")) { text_stream *condition = mr.exp[0]; int level = IF_FALSE_LEVEL; @@ -253,7 +254,7 @@ chapter as its value during the sole iteration. goto CYCLE; } -@ = +<>= if (Regexp::match(&mr, command, L"Else")) { if (cls->sp <= 0) { Errors::at_position("Else without If", @@ -275,7 +276,7 @@ chapter as its value during the sole iteration. @ Next, a genuine loop beginning: -@ = +<>= int loop_level = 0; if (Regexp::match(&mr, command, L"Repeat Module")) loop_level = MODULE_LEVEL; if (Regexp::match(&mr, command, L"Repeat Chapter")) loop_level = CHAPTER_LEVEL; @@ -285,18 +286,18 @@ chapter as its value during the sole iteration. linked_list_item *CI = FIRST_ITEM_IN_LINKED_LIST(chapter, cls->for_web->chapters); while ((CI) && (CONTENT_IN_ITEM(CI, chapter)->md->imported)) CI = NEXT_ITEM_IN_LINKED_LIST(CI, chapter); - if (loop_level == MODULE_LEVEL) @; - if (loop_level == CHAPTER_LEVEL) @; - if (loop_level == SECTION_LEVEL) @; + if (loop_level == MODULE_LEVEL) <>; + if (loop_level == CHAPTER_LEVEL) <>; + if (loop_level == SECTION_LEVEL) <>; Collater::start_CI_loop(cls, loop_level, from, to, lpos); goto CYCLE; } -@ = +<>= from = FIRST_ITEM_IN_LINKED_LIST(module, cls->modules); to = LAST_ITEM_IN_LINKED_LIST(module, cls->modules); -@ = +<>= from = CI; to = LAST_ITEM_IN_LINKED_LIST(chapter, cls->for_web->chapters); if (Str::eq_wide_string(cls->restrict_to_range, L"0") == FALSE) { @@ -308,7 +309,7 @@ chapter as its value during the sole iteration. } } -@ = +<>= chapter *within_chapter = CONTENT_IN_ITEM(Collater::heading_topmost_on_stack(cls, CHAPTER_LEVEL), chapter); @@ -326,7 +327,7 @@ chapter as its value during the sole iteration. @ And at the other bookend: -@ = +<>= int end_form = -1; if (Regexp::match(&mr, command, L"End Repeat")) end_form = 1; if (Regexp::match(&mr, command, L"End Select")) end_form = 2; @@ -357,16 +358,16 @@ chapter as its value during the sole iteration. break; } switch (cls->repeat_stack_level[cls->sp-1]) { - case MODULE_LEVEL: @; break; - case CHAPTER_LEVEL: @; break; - case SECTION_LEVEL: @; break; - case IF_TRUE_LEVEL: @; break; - case IF_FALSE_LEVEL: @; break; + case MODULE_LEVEL: <>; break; + case CHAPTER_LEVEL: <>; break; + case SECTION_LEVEL: <>; break; + case IF_TRUE_LEVEL: <>; break; + case IF_FALSE_LEVEL: <>; break; } goto CYCLE; } -@ = +<>= linked_list_item *CI = cls->repeat_stack_variable[cls->sp-1]; if (CI == cls->repeat_stack_threshold[cls->sp-1]) Collater::end_CI_loop(cls); @@ -376,7 +377,7 @@ chapter as its value during the sole iteration. lpos = cls->repeat_stack_startpos[cls->sp-1]; /* Back round loop */ } -@ = +<>= linked_list_item *CI = cls->repeat_stack_variable[cls->sp-1]; if (CI == cls->repeat_stack_threshold[cls->sp-1]) Collater::end_CI_loop(cls); @@ -386,7 +387,7 @@ chapter as its value during the sole iteration. lpos = cls->repeat_stack_startpos[cls->sp-1]; /* Back round loop */ } -@ = +<>= linked_list_item *SI = cls->repeat_stack_variable[cls->sp-1]; if ((SI == cls->repeat_stack_threshold[cls->sp-1]) || (NEXT_ITEM_IN_LINKED_LIST(SI, section) == NULL)) @@ -397,12 +398,12 @@ chapter as its value during the sole iteration. lpos = cls->repeat_stack_startpos[cls->sp-1]; /* Back round loop */ } -@ = +<>= Collater::end_CI_loop(cls); @ It can happen that a section loop, at least, is empty: -@ = +<>= for (int rstl = cls->sp-1; rstl >= 0; rstl--) if (cls->repeat_stack_level[cls->sp-1] == SECTION_LEVEL) { linked_list_item *SI = cls->repeat_stack_threshold[cls->sp-1]; @@ -411,15 +412,15 @@ chapter as its value during the sole iteration. goto CYCLE; } -@ = +<>= for (int j=cls->sp-1; j>=0; j--) if (cls->repeat_stack_level[j] == IF_FALSE_LEVEL) goto CYCLE; @ If called with the non-conditional levels, the following function returns -the topmost item. It's never called for |IF_TRUE_LEVEL| or |IF_FALSE_LEVEL|. +the topmost item. It's never called for [[IF_TRUE_LEVEL|]]or [[IF_FALSE_LEVEL]]. -= +<<*>>= linked_list_item *Collater::heading_topmost_on_stack(collater_state *cls, int level) { for (int rstl = cls->sp-1; rstl >= 0; rstl--) if (cls->repeat_stack_level[rstl] == level) @@ -430,13 +431,14 @@ linked_list_item *Collater::heading_topmost_on_stack(collater_state *cls, int le @ This is the function for starting a loop or code block, which stacks up the details, and similarly for ending it by popping them again: -@d MODULE_LEVEL 1 -@d CHAPTER_LEVEL 2 -@d SECTION_LEVEL 3 -@d IF_TRUE_LEVEL 4 -@d IF_FALSE_LEVEL 5 +<<*>>= +#define MODULE_LEVEL 1 +#define CHAPTER_LEVEL 2 +#define SECTION_LEVEL 3 +#define IF_TRUE_LEVEL 4 +#define IF_FALSE_LEVEL 5 -= +<<*>>= void Collater::start_CI_loop(collater_state *cls, int level, linked_list_item *from, linked_list_item *to, int pos) { if (cls->sp < CI_STACK_CAPACITY) { @@ -451,17 +453,17 @@ void Collater::end_CI_loop(collater_state *cls) { cls->sp--; } -@h Variable substitutions. +@ \section{Variable substitutions.} We can now forget about this tiny stack machine: the one task left is to take a line from the template, and make substitutions of variables into its square-bracketed parts. -Note that we do not allow this to recurse, i.e., if |[[X]]| substitutes into -text which itself contains a |[[...]]| notation, then we do not expand that -inner one. If we did, then the value of the bibliographic variable |[[Code]]|, +Note that we do not allow this to recurse, i.e., if [[[[X]]]] substitutes into +text which itself contains a [[[[...]]]] notation, then we do not expand that +inner one. If we did, then the value of the bibliographic variable [[[[Code]]]], used by the HTML renderer, would cause a modest-sized explosion on some pages. -@ = +<>= TEMPORARY_TEXT(rewritten) int slen, spos; while ((spos = Regexp::find_expansion(tl, '[', '[', ']', ']', &slen)) >= 0) { @@ -474,50 +476,50 @@ used by the HTML renderer, would cause a modest-sized explosion on some pages. match_results mr = Regexp::create_mr(); if (Bibliographic::data_exists(cls->for_web->md, varname)) { - @; + <>; } else if (Regexp::match(&mr, varname, L"Navigation")) { - @; + <>; } else if (Regexp::match(&mr, varname, L"Breadcrumbs")) { - @; + <>; } else if (Str::eq_wide_string(varname, L"Plugins")) { - @; + <>; } else if (Regexp::match(&mr, varname, L"Complete (%c+)")) { text_stream *detail = mr.exp[0]; - @; + <>; } else if (Regexp::match(&mr, varname, L"Module (%c+)")) { text_stream *detail = mr.exp[0]; - @; + <>; } else if (Regexp::match(&mr, varname, L"Chapter (%c+)")) { text_stream *detail = mr.exp[0]; - @; + <>; } else if (Regexp::match(&mr, varname, L"Section (%c+)")) { text_stream *detail = mr.exp[0]; - @; + <>; } else if (Regexp::match(&mr, varname, L"Docs")) { - @; + <>; } else if (Regexp::match(&mr, varname, L"Assets")) { - @; + <>; } else if (Regexp::match(&mr, varname, L"URL \"(%c+)\"")) { text_stream *link_text = mr.exp[0]; - @; + <>; } else if (Regexp::match(&mr, varname, L"Link \"(%c+)\"")) { text_stream *link_text = mr.exp[0]; - @; + <>; } else if (Regexp::match(&mr, varname, L"Menu \"(%c+)\"")) { text_stream *menu_name = mr.exp[0]; - @; + <>; } else if (Regexp::match(&mr, varname, L"Item \"(%c+)\"")) { text_stream *item_name = mr.exp[0]; text_stream *icon_text = NULL; - @; + <>; text_stream *link_text = item_name; - @; + <>; } else if (Regexp::match(&mr, varname, L"Item \"(%c+)\" -> (%c+)")) { text_stream *item_name = mr.exp[0]; text_stream *link_text = mr.exp[1]; text_stream *icon_text = NULL; - @; - @; + <>; + <>; } else { WRITE_TO(substituted, "%S", varname); if (Regexp::match(&mr, varname, L"%i+%c*")) @@ -535,15 +537,15 @@ used by the HTML renderer, would cause a modest-sized explosion on some pages. Str::clear(tl); Str::copy(tl, rewritten); DISCARD_TEXT(rewritten) -@ This is why, for instance, |[[Author]]| is replaced by the author's name: +@ This is why, for instance, [[[[Author]]]] is replaced by the author's name: -@ = +<>= 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. -@ = +<>= if (cls->nav_file) { if (TextFiles::exists(cls->nav_file)) Collater::collate(substituted, cls->for_web, cls->restrict_to_range, @@ -556,17 +558,17 @@ this will recursively call The Collater, in fact. @ A trail of breadcrumbs, used for overhead navigation in web pages. -@ = +<>= Colonies::drop_initial_breadcrumbs(substituted, cls->into_file, cls->crumbs); -@ = +<>= Assets::include_relevant_plugins(OUT, cls->nav_pattern, cls->for_web, cls->wv, cls->into_file); @ We store little about the complete-web-in-one-file PDF: -@ = +<>= if (swarm_leader) if (Formats::substitute_post_processing_data(substituted, swarm_leader, detail, cls->nav_pattern) == FALSE) @@ -574,15 +576,15 @@ this will recursively call The Collater, in fact. @ And here for Modules: -@ = +<>= module *M = CONTENT_IN_ITEM( Collater::heading_topmost_on_stack(cls, MODULE_LEVEL), module); if (M == NULL) Errors::at_position("no module is currently selected", cls->errors_at, lpos); - else @; + else <>; -@ = +<>= if (Str::eq_wide_string(detail, L"Title")) { text_stream *owner = Collater::module_owner(M, cls->for_web); if (Str::len(owner) > 0) WRITE_TO(substituted, "%S/", owner); @@ -601,15 +603,15 @@ this will recursively call The Collater, in fact. @ And here for Chapters: -@ = +<>= chapter *C = CONTENT_IN_ITEM( Collater::heading_topmost_on_stack(cls, CHAPTER_LEVEL), chapter); if (C == NULL) Errors::at_position("no chapter is currently selected", cls->errors_at, lpos); - else @; + else <>; -@ = +<>= if (Str::eq_wide_string(detail, L"Title")) { Str::copy(substituted, C->md->ch_title); } else if (Str::eq_wide_string(detail, L"Code")) { @@ -625,15 +627,15 @@ this will recursively call The Collater, in fact. @ And this is a very similar construction for Sections. -@ = +<>= section *S = CONTENT_IN_ITEM( Collater::heading_topmost_on_stack(cls, SECTION_LEVEL), section); if (S == NULL) Errors::at_position("no section is currently selected", cls->errors_at, lpos); - else @; + else <>; -@ = +<>= if (Str::eq_wide_string(detail, L"Title")) { Str::copy(substituted, S->md->sect_title); } else if (Str::eq_wide_string(detail, L"Purpose")) { @@ -662,33 +664,33 @@ this will recursively call The Collater, in fact. @ These commands are all used in constructing relative URLs, especially for navigation purposes. -@ = +<>= Pathnames::relative_URL(substituted, Filenames::up(cls->into_file), Pathnames::from_text(Colonies::home())); -@ = +<>= pathname *P = Colonies::assets_path(); if (P == NULL) P = Filenames::up(cls->into_file); Pathnames::relative_URL(substituted, Filenames::up(cls->into_file), P); -@ = +<>= Pathnames::relative_URL(substituted, Filenames::up(cls->into_file), Pathnames::from_text(link_text)); -@ = +<>= WRITE_TO(substituted, "into_file); WRITE_TO(substituted, "\">"); -@ = +<>= if (cls->inside_navigation_submenu) WRITE_TO(substituted, ""); WRITE_TO(substituted, "

%S

    ", menu_name); cls->inside_navigation_submenu = TRUE; -@ = +<>= match_results mr = Regexp::create_mr(); if (Regexp::match(&mr, item_name, L"<(%i+.%i+)> *(%c*)")) { icon_text = Str::duplicate(mr.exp[0]); @@ -699,40 +701,40 @@ navigation purposes. } Regexp::dispose_of(&mr); -@ = +<>= TEMPORARY_TEXT(url) Colonies::reference_URL(url, link_text, cls->into_file); - @; + <>; DISCARD_TEXT(url) -@ = +<>= TEMPORARY_TEXT(url) Colonies::link_URL(url, link_text, cls->into_file); - @; + <>; DISCARD_TEXT(url) -@ = +<>= if (cls->inside_navigation_submenu == FALSE) WRITE_TO(substituted, "
      "); cls->inside_navigation_submenu = TRUE; WRITE_TO(substituted, "
    • "); if (Str::eq(url, Filenames::get_leafname(cls->into_file))) { WRITE_TO(substituted, ""); - @; + <>; WRITE_TO(substituted, ""); } else if (Str::eq(url, I"index.html")) { WRITE_TO(substituted, "", url); WRITE_TO(substituted, ""); - @; + <>; WRITE_TO(substituted, ""); WRITE_TO(substituted, ""); } else { WRITE_TO(substituted, "", url); - @; + <>; WRITE_TO(substituted, ""); } WRITE_TO(substituted, "
    • "); -@ = +<>= if (Str::len(icon_text) > 0) { WRITE_TO(substituted, ">= text_stream *Collater::module_owner(const module *M, web *W) { text_stream *owner = Pathnames::directory_name(Pathnames::up(M->module_location)); @@ -761,7 +763,7 @@ text_stream *Collater::module_owner(const module *M, web *W) { top, then all other owners, in alphabetical order, and then last of all Inweb, so that //foundation// will always be at the bottom. -= +<<*>>= web *sorting_web = NULL; void Collater::sort_web(web *W) { sorting_web = W; diff --git a/Chapter_3/The Tangler.w b/Chapter_3/The_Tangler.nw similarity index 84% rename from Chapter_3/The Tangler.w rename to Chapter_3/The_Tangler.nw index 5441b38..b12fc0b 100644 --- a/Chapter_3/The Tangler.w +++ b/Chapter_3/The_Tangler.nw @@ -3,14 +3,14 @@ To transcribe a version of the text in the web into a form which can be compiled as a program. -@h The Master Tangler. -Here's what has happened so far, on a |-tangle| run of Inweb: on any +@ \section{The Master Tangler.} +Here's what has happened so far, on a [[-tangle]] run of Inweb: on any other sort of run, of course, we would never be in this section of code. The web was read completely into memory, and then fully parsed, with all of the arrays and hashes populated. Program Control then sent us straight here for the tangling to begin... -= +<<*>>= void Tangler::tangle(web *W, tangle_target *target, filename *dest_file) { programming_language *lang = target->tangle_language; PRINT(" tangling <%/f> (written in %S)\n", dest_file, lang->language_name); @@ -19,24 +19,25 @@ void Tangler::tangle(web *W, tangle_target *target, filename *dest_file) { text_stream *OUT = &TO_struct; if (STREAM_OPEN_TO_FILE(OUT, dest_file, ISO_ENC) == FALSE) Errors::fatal_with_file("unable to write tangled file", dest_file); - @; + <>; STREAM_CLOSE(OUT); - @; - @; + <>; + <>; LanguageMethods::additional_tangling(lang, W, target); } @ All of the sections are tangled together into one big file, the structure of which can be seen below. -@d LOOP_OVER_PARAGRAPHS(C, S, T, P) +<<*>>= +#define LOOP_OVER_PARAGRAPHS(C, S, T, P) LOOP_OVER_LINKED_LIST(C, chapter, W->chapters) LOOP_OVER_LINKED_LIST(S, section, C->sections) if (S->sect_target == T) LOOP_OVER_LINKED_LIST(P, paragraph, S->paragraphs) -@ = +<>= /* (a) The shebang line, a header for scripting languages, and other heading matter */ LanguageMethods::shebang(OUT, lang, W, target); LanguageMethods::disclaimer(OUT, lang, W, target); @@ -46,8 +47,8 @@ of which can be seen below. if ((P->placed_very_early) && (P->defines_macro == NULL)) Tangler::tangle_paragraph(OUT, P); - /* (b) Results of |@d| declarations */ - @; + /* (b) Results of [[@d]] declarations */ + <>; /* (c) Miscellaneous automated C predeclarations */ LanguageMethods::additional_predeclarations(OUT, lang, W); @@ -65,26 +66,26 @@ of which can be seen below. /* (f) Opposite of the shebang: a footer */ LanguageMethods::gnabehs(OUT, lang, W); -@ This is the result of all those |@d| definitions; note that these sometimes +@ This is the result of all those [[@d]] definitions; note that these sometimes extend across multiple lines. -@ = +<>= chapter *C; section *S; LOOP_WITHIN_TANGLE(C, S, target) if (L->category == BEGIN_DEFINITION_LCAT) if (L->default_defn == FALSE) - @; + <>; LOOP_WITHIN_TANGLE(C, S, target) if (L->category == BEGIN_DEFINITION_LCAT) if (L->default_defn) { LanguageMethods::open_ifdef(OUT, lang, L->text_operand, FALSE); - @; + <>; LanguageMethods::close_ifdef(OUT, lang, L->text_operand, FALSE); } Enumerations::define_extents(OUT, target, lang); -@ = +<>= if (L->owning_paragraph == NULL) Main::error_in_web(I"misplaced definition", L); else Tags::open_ifdefs(OUT, L->owning_paragraph); LanguageMethods::start_definition(OUT, lang, @@ -97,7 +98,7 @@ extend across multiple lines. LanguageMethods::end_definition(OUT, lang, S, L); if (L->owning_paragraph) Tags::close_ifdefs(OUT, L->owning_paragraph); -@ = +<>= filename *F; LOOP_OVER_LINKED_LIST(F, filename, W->headers) Shell::copy(F, Reader::tangled_folder(W), ""); @@ -106,9 +107,10 @@ extend across multiple lines. code, or configuration gobbledegook) marked as "to ...", giving a leafname. We place files of those leafnames in the same directory as the tangle target. -@d MAX_EXTRACT_FILES 10 +<<*>>= +#define MAX_EXTRACT_FILES 10 -@ = +<>= text_stream *extract_names[MAX_EXTRACT_FILES]; text_stream extract_files[MAX_EXTRACT_FILES]; int no_extract_files = 0; @@ -134,23 +136,23 @@ We place files of those leafnames in the same directory as the tangle target. for (int i=0; i>= void Tangler::tangle_paragraph(OUTPUT_STREAM, paragraph *P) { Tags::open_ifdefs(OUT, P); int contiguous = FALSE; for (source_line *L = P->first_line_in_paragraph; ((L) && (L->owning_paragraph == P)); L = L->next_line) { if (LanguageMethods::will_insert_in_tangle(P->under_section->sect_language, L)) { - @; + <>; LanguageMethods::insert_in_tangle(OUT, P->under_section->sect_language, L); } if ((L->category != CODE_BODY_LCAT) || (L->suppress_tangling)) { contiguous = FALSE; } else { - @; + <>; Tangler::tangle_line(OUT, L->text, P->under_section, L); WRITE("\n"); } } @@ -162,49 +164,49 @@ from many different origins. Some programming languages (C, for instance) support a notation to tell the compiler that code has come from somewhere else; if so, here's where we use it. -@ = +<>= if (contiguous == FALSE) { contiguous = TRUE; LanguageMethods::insert_line_marker(OUT, P->under_section->sect_language, L); } -@h The Code Tangler. +@ \section{The Code Tangler.} All of the final tangled code passes through the following routine. -Almost all of the time, it simply prints |original| verbatim to the file |OUT|. +Almost all of the time, it simply prints [[original]] verbatim to the file [[OUT]]. -= +<<*>>= void Tangler::tangle_line(OUTPUT_STREAM, text_stream *original, section *S, source_line *L) { int mlen, slen; int mpos = Regexp::find_expansion(original, '@', '<', '@', '>', &mlen); int spos = Regexp::find_expansion(original, '[', '[', ']', ']', &slen); if ((mpos >= 0) && ((spos == -1) || (mpos <= spos)) && (LanguageMethods::allow_expansion(S->sect_language, original))) - @ + <> else if (spos >= 0) - @ + <> else LanguageMethods::tangle_line(OUT, S->sect_language, original); /* this is usually what happens */ } @ The first form of escape is a paragraph macro in the middle of code. For example, we handle -= (text) - if (banana_count == 0) @; -= -by calling the lower-level tangler on |if (banana_count == 0) | (a substring + + if (banana_count == 0) <>; + +by calling the lower-level tangler on [[if (banana_count == 0) ]] (a substring which we know can't involve any macros, since we are detecting macros from left to right, and this is to the left of the one we found); then by tangling the definition of "Yes, we have no bananas"; then by calling the upper-level -code tangler on |;|. (In this case, of course, there's nothing much there, +code tangler on [[;]]. (In this case, of course, there's nothing much there, but in principle it could contain further macros.) Note that when we've expanded "Yes, we have no bananas" we have certainly placed code into the tangled file from a different location; that will insert -a |#line| marker for the definition location; and we don't want the eventual +a [[#line]] marker for the definition location; and we don't want the eventual C compiler to think that the code which follows is also from that location. So we insert a fresh line marker. -@ = +<>= TEMPORARY_TEXT(temp) Str::copy(temp, original); Str::truncate(temp, mpos); LanguageMethods::tangle_line(OUT, S->sect_language, temp); @@ -230,21 +232,21 @@ So we insert a fresh line marker. DISCARD_TEXT(temp) @ This is a similar matter, except that it expands bibliographic data: -= (text) + printf("This is build [[Build Number]].\n"); -= + takes the bibliographic data for "Build Number" (as set on the web's contents page) and substitutes that, so that we end up with (say) -= (text as C) + printf("This is build 5Q47.\n"); -= + In some languages there are also special expansions (for example, in -InC |[[nonterminals]]| has a special meaning). +InC [[[[nonterminals]]]] has a special meaning). If the text in double-squares isn't recognised, that's not an error: it simply -passes straight through. So |[[water]]| becomes just |[[water]]|. +passes straight through. So [[[[water]]]] becomes just [[[[water]]]]. -@ = +<>= web *W = S->owning_web; TEMPORARY_TEXT(temp) @@ -266,10 +268,10 @@ passes straight through. So |[[water]]| becomes just |[[water]]|. DISCARD_TEXT(rest) DISCARD_TEXT(temp) -@h Prinary target. +@ \section{Prinary target.} The first target in a web is always the one for the main program. -= +<<*>>= tangle_target *Tangler::primary_target(web *W) { if (W == NULL) internal_error("no such web"); return FIRST_IN_LINKED_LIST(tangle_target, W->tangle_targets); diff --git a/Chapter_3/The Weaver.w b/Chapter_3/The_Weaver.nw similarity index 82% rename from Chapter_3/The Weaver.w rename to Chapter_3/The_Weaver.nw index c7071a9..7b08b1d 100644 --- a/Chapter_3/The Weaver.w +++ b/Chapter_3/The_Weaver.nw @@ -2,7 +2,7 @@ To weave a portion of the code into instructions for TeX. -@h The Master Weaver. +@ \section{The Master Weaver.} Here's what has happened so far, on a weave run of Inweb: on any other sort of run, of course, we would never be in this section of code. The web was read completely into memory and fully parsed. A request was then made either @@ -10,9 +10,9 @@ to swarm a mass of individual weaves, or to make just a single weave, with the target in each case being identified by its range. A further decoding layer then translated each range into rather more basic details of what to weave and where to put the result: and so we arrive at the front door of the routine -|Weaver::weave| below. +[[Weaver::weave]] below. -= +<<*>>= int Weaver::weave(weave_order *wv) { heterogeneous_tree *tree = WeaveTree::new_tree(wv); TEMPORARY_TEXT(banner) @@ -38,32 +38,32 @@ int Weaver::weave(weave_order *wv) { return lines; } -@ = +<<*>>= int Weaver::weave_inner(weave_order *wv, heterogeneous_tree *tree, tree_node *body) { web *W = wv->weave_web; int lines_woven = 0; weaver_state state_at; weaver_state *state = &state_at; - @; + <>; chapter *C, *last_heading = NULL; section *S; LOOP_OVER_LINKED_LIST(C, chapter, W->chapters) if (C->md->imported == FALSE) { LOOP_OVER_LINKED_LIST(S, section, C->sections) if (Reader::range_within(S->md->sect_range, wv->weave_range)) { - @; - @; + <>; + <>; LanguageMethods::begin_weave(S, wv); - @; - @; + <>; + <>; } } - @; + <>; return lines_woven; } -@ = +<>= if (last_heading != C) { - @; + <>; tree_node *CH = WeaveTree::chapter(tree, C); Trees::make_child(CH, state->body_node); state->chapter_node = CH; @@ -75,7 +75,7 @@ int Weaver::weave_inner(weave_order *wv, heterogeneous_tree *tree, tree_node *bo } } -@ = +<>= if (wv->theme_match == NULL) { if (last_heading != NULL) { tree_node *F = WeaveTree::chapter_footer(tree, last_heading); @@ -83,7 +83,7 @@ int Weaver::weave_inner(weave_order *wv, heterogeneous_tree *tree, tree_node *bo } } -@ = +<>= tree_node *SH = WeaveTree::section(tree, S); Trees::make_child(SH, state->chapter_node); state->section_node = SH; @@ -93,25 +93,26 @@ int Weaver::weave_inner(weave_order *wv, heterogeneous_tree *tree, tree_node *bo Trees::make_child(H, state->section_node); } -@ = +<>= if (wv->theme_match == NULL) { tree_node *F = WeaveTree::section_footer(tree, S); Trees::make_child(F, state->section_node); } -@h The state. +@ \section{The state.} We can now begin on a clean page, by initialising the state of the weaver: -@e COMMENTARY_MATERIAL from 1 -@e MACRO_MATERIAL /* when a macro is being defined... */ -@e DEFINITION_MATERIAL /* ...versus when an |@d| definition is being made */ -@e CODE_MATERIAL /* verbatim code */ -@e ENDNOTES_MATERIAL /* endnotes at the foot of a paragraph */ -@e FOOTNOTES_MATERIAL /* footnote texts for a paragraph */ +<<*>>= +enum COMMENTARY_MATERIAL from 1 +enum MACRO_MATERIAL /* when a macro is being defined... */ +enum DEFINITION_MATERIAL /* ...versus when an [[@d]] definition is being made */ +enum CODE_MATERIAL /* verbatim code */ +enum ENDNOTES_MATERIAL /* endnotes at the foot of a paragraph */ +enum FOOTNOTES_MATERIAL /* footnote texts for a paragraph */ -= +<<*>>= typedef struct weaver_state { - int kind_of_material; /* one of the enumerated |*_MATERIAL| constants above */ + int kind_of_material; /* one of the enumerated [[*_MATERIAL]] constants above */ int line_break_pending; /* insert a line break before the next woven line? */ int next_heading_without_vertical_skip; int horizontal_rule_just_drawn; @@ -125,7 +126,7 @@ typedef struct weaver_state { struct tree_node *ap; } weaver_state; -@ = +<>= state->kind_of_material = COMMENTARY_MATERIAL; state->line_break_pending = FALSE; state->next_heading_without_vertical_skip = FALSE; @@ -139,15 +140,15 @@ typedef struct weaver_state { state->material_node = NULL; state->ap = body; -@h Weaving a section. +@ \section{Weaving a section.} -@ = +<>= paragraph *current_P = NULL; int toc_made = FALSE; for (source_line *LLL = S->first_line; LLL; LLL = LLL->next_line) { wv->current_weave_line = LLL; if (LLL->owning_paragraph == NULL) - @ + <> else if (LLL->owning_paragraph != current_P) { if (toc_made == FALSE) { if (Str::len(S->sect_purpose) > 0) { @@ -159,11 +160,11 @@ typedef struct weaver_state { } current_P = LLL->owning_paragraph; if (Tags::tagged_with(current_P, wv->theme_match)) - @; + <>; } } -@ = +<>= if (LLL->category == INTERFACE_BODY_LCAT) { state->horizontal_rule_just_drawn = FALSE; continue; @@ -190,7 +191,7 @@ typedef struct weaver_state { (LLL->category == SECTION_HEADING_LCAT)) continue; -@ = +<>= if (current_P->starts_on_new_page) Trees::make_child(WeaveTree::pagebreak(tree), state->ap); source_line *L = LLL; @@ -198,27 +199,27 @@ typedef struct weaver_state { (L->category != PARAGRAPH_START_LCAT)) Main::error_in_web(I"bad start to paragraph", L); /* should never happen */ - @; + <>; - @; + <>; L = L->next_line; for (; ((L) && (L->owning_paragraph == current_P)); L = L->next_line) { wv->current_weave_line = L; if (LanguageMethods::skip_in_weaving(S->sect_language, wv, L) == FALSE) { lines_woven++; - @; - @; + <>; + <>; } } L = NULL; Weaver::change_material(tree, state, ENDNOTES_MATERIAL, FALSE, NULL, NULL); Weaver::show_endnotes_on_previous_paragraph(tree, wv, state->ap, current_P); -@h How paragraphs begin. +@ \section{How paragraphs begin.} -@ = +<>= LanguageMethods::reset_syntax_colouring(S->sect_language); - if (wv->theme_match) @; + if (wv->theme_match) <>; state->para_node = WeaveTree::paragraph_heading(tree, current_P, state->next_heading_without_vertical_skip); Trees::make_child(state->para_node, state->section_node); @@ -229,7 +230,7 @@ typedef struct weaver_state { @ If we are weaving a selection of extracted paragraphs, normal conventions about breaking pages at chapters and sections fail to work. So: -@ = +<>= text_stream *cap = Tags::retrieve_caption(L->owning_paragraph, wv->theme_match); if (Str::len(cap) > 0) { Weaver::weave_subheading(tree, wv, state->ap, C->md->ch_title); @@ -244,7 +245,7 @@ about breaking pages at chapters and sections fail to work. So: @ There's quite likely ordinary text on the line following the paragraph start indication, too, so we need to weave this out: -@ = +<>= if (Str::len(L->text_operand2) > 0) { TEMPORARY_TEXT(matter) WRITE_TO(matter, "%S\n", L->text_operand2); @@ -252,7 +253,7 @@ about breaking pages at chapters and sections fail to work. So: DISCARD_TEXT(matter) } -@ = +<>= if (L->category == BEGIN_CODE_LCAT) { state->line_break_pending = FALSE; LanguageMethods::reset_syntax_colouring(S->sect_language); @@ -265,89 +266,89 @@ about breaking pages at chapters and sections fail to work. So: } TEMPORARY_TEXT(matter) Str::copy(matter, L->text); - if (L->is_commentary) @ - else @; + if (L->is_commentary) <> + else <>; DISCARD_TEXT(matter) @ And lastly we ignore commands, or act on them if they happen to be aimed at us; but we don't weave them into the output, that's for sure. -@ = +<>= if (L->category == COMMAND_LCAT) { if (L->command_code == PAGEBREAK_CMD) Trees::make_child(WeaveTree::pagebreak(tree), state->ap); if (L->command_code == GRAMMAR_INDEX_CMD) Trees::make_child(WeaveTree::grammar_index(tree), state->ap); - if (L->command_code == FIGURE_CMD) @; - if (L->command_code == HTML_CMD) @; - if (L->command_code == AUDIO_CMD) @; - if (L->command_code == VIDEO_CMD) @; - if (L->command_code == DOWNLOAD_CMD) @; - if (L->command_code == EMBED_CMD) @; - if (L->command_code == CAROUSEL_CMD) @; - if (L->command_code == CAROUSEL_ABOVE_CMD) @; - if (L->command_code == CAROUSEL_BELOW_CMD) @; - if (L->command_code == CAROUSEL_UNCAPTIONED_CMD) @; - if (L->command_code == CAROUSEL_END_CMD) @; + if (L->command_code == FIGURE_CMD) <>; + if (L->command_code == HTML_CMD) <>; + if (L->command_code == AUDIO_CMD) <>; + if (L->command_code == VIDEO_CMD) <>; + if (L->command_code == DOWNLOAD_CMD) <>; + if (L->command_code == EMBED_CMD) <>; + if (L->command_code == CAROUSEL_CMD) <>; + if (L->command_code == CAROUSEL_ABOVE_CMD) <>; + if (L->command_code == CAROUSEL_BELOW_CMD) <>; + if (L->command_code == CAROUSEL_UNCAPTIONED_CMD) <>; + if (L->command_code == CAROUSEL_END_CMD) <>; /* Otherwise assume it was a tangler command, and ignore it here */ continue; } -@ = +<>= int w, h; text_stream *figname = Parser::dimensions(L->text_operand, &w, &h, L); Trees::make_child(WeaveTree::figure(tree, figname, w, h), state->ap); -@ = +<>= Trees::make_child(WeaveTree::raw_extract(tree, L->text_operand), state->ap); -@ = +<>= int w, h; text_stream *figname = Parser::dimensions(L->text_operand, &w, &h, L); Trees::make_child(WeaveTree::audio(tree, figname, w), state->ap); -@ = +<>= int w, h; text_stream *figname = Parser::dimensions(L->text_operand, &w, &h, L); Trees::make_child(WeaveTree::video(tree, figname, w, h), state->ap); -@ = +<>= Trees::make_child(WeaveTree::download(tree, L->text_operand, L->text_operand2), state->ap); -@ = +<>= int w, h; text_stream *ID = Parser::dimensions(L->text_operand2, &w, &h, L); Trees::make_child(WeaveTree::embed(tree, L->text_operand, ID, w, h), state->ap); -@ = +<>= tree_node *C = WeaveTree::carousel_slide(tree, L->text_operand, L->command_code); Trees::make_child(C, state->para_node); state->ap = C; state->carousel_node = C; -@ = +<>= state->ap = state->para_node; state->carousel_node = NULL; -@h Commentary matter. +@ \section{Commentary matter.} Typographically this is a fairly simple business: it's almost the case that we only have to transcribe it. But not quite! -@ = - @; - @; - @; - @; - @; +<>= + <>; + <>; + <>; + <>; + <>; WRITE_TO(matter, "\n"); Weaver::commentary_text(tree, wv, state->ap, matter); continue; -@ Displayed source is the material marked with |>>| arrows in column 1. +@ Displayed source is the material marked with [[>>]] arrows in column 1. -@ = +<>= if (L->category == SOURCE_DISPLAY_LCAT) { Trees::make_child(WeaveTree::display_line(tree, L->text_operand), state->ap); continue; @@ -356,12 +357,12 @@ we only have to transcribe it. But not quite! @ Our style is to use paragraphs without initial-line indentation, so we add a vertical skip between them to show the division more clearly. -@ = +<>= if (Regexp::string_is_white_space(matter)) { if ((L->next_line) && (L->next_line->category == COMMENT_BODY_LCAT)) { match_results mr = Regexp::create_mr(); if ((state->kind_of_material != CODE_MATERIAL) || - (Regexp::match(&mr, matter, L"\t|(%c*)|(%c*?)"))) + (Regexp::match(&mr, matter, L"\t[[(%c*)]](%c*?)"))) Trees::make_child(WeaveTree::vskip(tree, TRUE), state->ap); Regexp::dispose_of(&mr); } @@ -369,9 +370,9 @@ add a vertical skip between them to show the division more clearly. } @ Here our extension is simply to provide a tidier way to use TeX's standard -|\item| and |\itemitem| macros for indented list items. +[[\item]] and [[\itemitem]] macros for indented list items. -@ = +<>= match_results mr = Regexp::create_mr(); if (Regexp::match(&mr, matter, L"%(-...%) (%c*)")) { /* continue double */ Weaver::change_material(tree, state, COMMENTARY_MATERIAL, FALSE, NULL, NULL); @@ -395,9 +396,9 @@ add a vertical skip between them to show the division more clearly. @ Finally, matter encased in vertical strokes one tab stop in from column 1 in the source is set indented in code style. -@ = +<>= match_results mr = Regexp::create_mr(); - if (Regexp::match(&mr, matter, L"\t|(%c*)|(%c*?)")) { + if (Regexp::match(&mr, matter, L"\t[[(%c*)]](%c*?)")) { TEMPORARY_TEXT(original) Weaver::change_material(tree, state, CODE_MATERIAL, FALSE, NULL, NULL); Str::copy(original, mr.exp[0]); @@ -415,7 +416,7 @@ in the source is set indented in code style. } Regexp::dispose_of(&mr); -@ = +<>= if (L->category == FOOTNOTE_TEXT_LCAT) { Weaver::change_material(tree, state, FOOTNOTES_MATERIAL, FALSE, NULL, NULL); footnote *F = L->footnote_text; @@ -425,21 +426,21 @@ in the source is set indented in code style. state->ap = FN; } -@h Code-like matter. -Even though Inweb's approach, unlike |CWEB|'s, is to respect the layout +@ \section{Code-like matter.} +Even though Inweb's approach, unlike [[CWEB]]'s, is to respect the layout of the original, this is still quite typographically complex: commentary and macro usage is rendered differently. -@ = - @; - @; +<>= + <>; + <>; Str::rectify_indentation(matter, 4); TEMPORARY_TEXT(prefatory) TEMPORARY_TEXT(concluding_comment) - @; - @; + <>; + <>; tree_node *CL = WeaveTree::code_line(tree); Trees::make_child(CL, state->ap); @@ -447,9 +448,9 @@ and macro usage is rendered differently. Trees::make_child(WeaveTree::weave_defn_node(tree, prefatory), CL); Str::clear(prefatory); - @; + <>; - @; + <>; TEMPORARY_TEXT(colouring) LanguageMethods::syntax_colour(S->sect_language, wv, L, matter, colouring); TextWeaver::source_code(tree, CL, matter, colouring, L->enable_hyperlinks); @@ -464,7 +465,7 @@ and macro usage is rendered differently. @ We're not in Kansas any more, so: -@ = +<>= if (state->kind_of_material != CODE_MATERIAL) { int will_be = CODE_MATERIAL; if (L->category == MACRO_DEFINITION_LCAT) @@ -497,7 +498,7 @@ and macro usage is rendered differently. @ A blank line is implemented differently in different formats, so it gets a node of its own, a vskip: -@ = +<>= if (state->line_break_pending) { Trees::make_child(WeaveTree::vskip(tree, FALSE), state->ap); state->line_break_pending = FALSE; @@ -510,7 +511,7 @@ a node of its own, a vskip: @ Comments which run to the end of a line can be set in italic type, for example, or flush left. -@ = +<>= TEMPORARY_TEXT(part_before_comment) TEMPORARY_TEXT(part_within_comment) programming_language *pl = S->sect_language; @@ -523,9 +524,9 @@ example, or flush left. DISCARD_TEXT(part_before_comment) DISCARD_TEXT(part_within_comment) -@ Set the |@d| definition escape very slightly more fancily: +@ Set the [[@d]] definition escape very slightly more fancily: -@ = +<>= if (L->category == BEGIN_DEFINITION_LCAT) { match_results mr = Regexp::create_mr(); if ((Regexp::match(&mr, matter, L"@d (%c*)")) || @@ -543,7 +544,7 @@ example, or flush left. Regexp::dispose_of(&mr); } -@ = +<>= TEMPORARY_TEXT(OUT) int taken = LanguageMethods::weave_code_line(OUT, S->sect_language, wv, W, C, S, L, matter, concluding_comment); @@ -554,7 +555,7 @@ example, or flush left. DISCARD_TEXT(OUT) if (taken) goto ClumsyLabel; -@ = +<>= match_results mr = Regexp::create_mr(); while (Regexp::match(&mr, matter, L"(%c*?)%@%<(%c*?)%@%>(%c*)")) { para_macro *pmac = Macros::find_by_name(mr.exp[1], S); @@ -571,29 +572,29 @@ example, or flush left. } Regexp::dispose_of(&mr); -@h Endnotes. +@ \section{Endnotes.} The endnotes describe function calls from far away, or unexpected -structure usage, or how |CWEB|-style code substitutions were made. +structure usage, or how [[CWEB]]-style code substitutions were made. -= +<<*>>= void Weaver::show_endnotes_on_previous_paragraph(heterogeneous_tree *tree, weave_order *wv, tree_node *ap, paragraph *P) { tree_node *body = ap; theme_tag *T = Tags::find_by_name(I"Preform", FALSE); if ((T) && (Tags::tagged_with(P, T))) - @; + <>; Tags::show_endnote_on_ifdefs(tree, ap, P); if (P->defines_macro) - @; + <>; language_function *fn; LOOP_OVER_LINKED_LIST(fn, language_function, P->functions) - @; + <>; language_type *st; LOOP_OVER_LINKED_LIST(st, language_type, P->structures) - @; + <>; } -@ = +<>= tree_node *E = WeaveTree::endnote(tree); Trees::make_child(E, body); ap = E; TextWeaver::commentary_text(tree, ap, I"This is "); @@ -607,7 +608,7 @@ void Weaver::show_endnotes_on_previous_paragraph(heterogeneous_tree *tree, DISCARD_TEXT(url) TextWeaver::commentary_text(tree, ap, I", not regular C code."); -@ = +<>= tree_node *E = WeaveTree::endnote(tree); Trees::make_child(E, body); ap = E; TextWeaver::commentary_text(tree, ap, I"This code is "); @@ -646,11 +647,11 @@ void Weaver::show_endnotes_on_previous_paragraph(heterogeneous_tree *tree, } TextWeaver::commentary_text(tree, ap, I"."); -@ = +<>= if (fn->usage_described == FALSE) Weaver::show_function_usage(tree, wv, ap, P, fn, FALSE); -@ = +<>= tree_node *E = WeaveTree::endnote(tree); Trees::make_child(E, body); ap = E; TextWeaver::commentary_text(tree, ap, I"The structure "); @@ -690,7 +691,7 @@ void Weaver::show_endnotes_on_previous_paragraph(heterogeneous_tree *tree, } TextWeaver::commentary_text(tree, ap, I"."); -@ = +<<*>>= void Weaver::show_function_usage(heterogeneous_tree *tree, weave_order *wv, tree_node *ap, paragraph *P, language_function *fn, int as_list) { tree_node *body = ap; @@ -711,10 +712,10 @@ void Weaver::show_function_usage(heterogeneous_tree *tree, weave_order *wv, LOOP_OVER_LINKED_LIST(hteu, hash_table_entry_usage, hte->usages) if ((P != hteu->usage_recorded_at) && (P->under_section == hteu->usage_recorded_at->under_section)) - @; + <>; LOOP_OVER_LINKED_LIST(hteu, hash_table_entry_usage, hte->usages) if (P->under_section != hteu->usage_recorded_at->under_section) - @; + <>; if (used_flag == FALSE) { if (as_list == FALSE) { TextWeaver::commentary_text(tree, ap, I" appears nowhere else"); @@ -729,7 +730,7 @@ void Weaver::show_function_usage(heterogeneous_tree *tree, weave_order *wv, } } -@ = +<>= if (as_list == FALSE) { if (used_flag == FALSE) TextWeaver::commentary_text(tree, ap, I" is used in "); } @@ -753,9 +754,9 @@ void Weaver::show_function_usage(heterogeneous_tree *tree, weave_order *wv, Trees::make_child(WeaveTree::locale(tree, hteu->usage_recorded_at, NULL), ap); last_cited_in = hteu->usage_recorded_at->under_section; -@h Non-paragraph subheadings. +@ \section{Non-paragraph subheadings.} -= +<<*>>= void Weaver::weave_subheading(heterogeneous_tree *tree, weave_order *wv, tree_node *ap, text_stream *text) { tree_node *D = WeaveTree::subheading(tree, text); @@ -794,11 +795,11 @@ void Weaver::commentary_text(heterogeneous_tree *tree, weave_order *wv, TextWeaver::commentary_text(tree, ap, matter); } -@h Section tables of contents. +@ \section{Section tables of contents.} These appear at the top of each woven section, and give links to the paragraphs -marked as |@h| headings. +marked as [[@h]] headings. -= +<<*>>= int Weaver::weave_table_of_contents(heterogeneous_tree *tree, tree_node *ap, section *S) { int noteworthy = 0; diff --git a/Chapter_3/The Weaver of Text.w b/Chapter_3/The_Weaver_of_Text.nw similarity index 88% rename from Chapter_3/The Weaver of Text.w rename to Chapter_3/The_Weaver_of_Text.nw index a627727..6ea247d 100644 --- a/Chapter_3/The Weaver of Text.w +++ b/Chapter_3/The_Weaver_of_Text.nw @@ -2,13 +2,13 @@ To manage the weaving of commentary or source code text. -@h Commentary text. +@ \section{Commentary text.} The following takes text, divides it up at stroke-mark boundaries -- -that is, |this is inside|, this is outside -- and sends contiguous pieces -of it either to |TextWeaver::inline_code_fragment| or |TextWeaver::commentary_fragment| +that is, [[this is inside]], this is outside -- and sends contiguous pieces +of it either to [[TextWeaver::inline_code_fragment]] or [[TextWeaver::commentary_fragment]] as appropriate. -= +<<*>>= void TextWeaver::commentary_text(heterogeneous_tree *tree, tree_node *ap, text_stream *matter) { TextWeaver::commentary_r(tree, ap, matter, FALSE, FALSE); } @@ -23,31 +23,31 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre text_stream *code_in_comments_notation = Bibliographic::get_datum(wv->weave_web->md, (in_code)?(I"Code In Code Comments Notation"):(I"Code In Commentary Notation")); - if (Str::ne(code_in_comments_notation, I"Off")) @; + if (Str::ne(code_in_comments_notation, I"Off")) <>; int display_flag = TRUE; text_stream *tex_notation = Bibliographic::get_datum(wv->weave_web->md, I"TeX Mathematics Displayed Notation"); - if (Str::ne(tex_notation, I"Off")) @; + if (Str::ne(tex_notation, I"Off")) <>; display_flag = FALSE; tex_notation = Bibliographic::get_datum(wv->weave_web->md, I"TeX Mathematics Notation"); - if (Str::ne(tex_notation, I"Off")) @; + if (Str::ne(tex_notation, I"Off")) <>; text_stream *xref_notation = Bibliographic::get_datum(wv->weave_web->md, I"Cross-References Notation"); - if (Str::ne(xref_notation, I"Off")) @; + if (Str::ne(xref_notation, I"Off")) <>; if (within) { TextWeaver::inline_code_fragment(tree, ap, matter); } else { - @; - @; + <>; + <>; TextWeaver::commentary_fragment(tree, ap, matter, in_code); } } -@ = +<>= for (int i=0; i < Str::len(matter); i++) { if (Str::get_at(matter, i) == '\\') i += Str::len(code_in_comments_notation) - 1; else if (Str::includes_at(matter, i, code_in_comments_notation)) { @@ -64,7 +64,7 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre } } -@ = +<>= for (int i=0; i < Str::len(matter); i++) { if ((Str::includes_at(matter, i, I"http://")) || (Str::includes_at(matter, i, I"https://"))) { @@ -95,7 +95,7 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre } } -@ = +<>= int N = Str::len(tex_notation); for (int i=0; i < Str::len(matter); i++) { if ((within == FALSE) && (Str::includes_at(matter, i, tex_notation))) { @@ -123,7 +123,7 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre } } -@ = +<>= TEMPORARY_TEXT(before) TEMPORARY_TEXT(cue) TEMPORARY_TEXT(after) @@ -146,7 +146,7 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre DISCARD_TEXT(after) if (allow) return; -@ = +<>= int N = Str::len(xref_notation); for (int i=0; i < Str::len(matter); i++) { if ((within == FALSE) && (Str::includes_at(matter, i, xref_notation)) && @@ -164,7 +164,7 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre Str::substr(before, Str::start(matter), Str::at(matter, i)); Str::substr(reference, Str::at(matter, i + N), Str::at(matter, j)); Str::substr(after, Str::at(matter, j + N), Str::end(matter)); - @; + <>; DISCARD_TEXT(before) DISCARD_TEXT(reference) DISCARD_TEXT(after) @@ -175,7 +175,7 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre } } -@ = +<>= TEMPORARY_TEXT(url) TEMPORARY_TEXT(title) int ext = FALSE; @@ -192,21 +192,21 @@ void TextWeaver::commentary_r(heterogeneous_tree *tree, tree_node *ap, text_stre @ This tests whether a cross-reference is allowed to begin or end: it must begin after and finish before a "boundary character". -Note the one-sided treatment of |:|, which is a boundary after but not before, -so that |http://| won't trigger a cross-reference with the standard |//| +Note the one-sided treatment of [[:]], which is a boundary after but not before, +so that [[http://]] won't trigger a cross-reference with the standard [[//]] xref notation. -= +<<*>>= int TextWeaver::boundary_character(int before, wchar_t c) { if (c == 0) return TRUE; if (Characters::is_whitespace(c)) return TRUE; - if ((c == '.') || (c == ',') || (c == '!') || (c == '?') || (c == ';') || + if ((c == '.') [[| (c == ',') || (c == '!') || (c == '?') || (c == ';') |]] (c == '(')|| (c == ')')) return TRUE; if ((before == FALSE) && (c == ':')) return TRUE; return FALSE; } -@ = +<<*>>= void TextWeaver::commentary_fragment(heterogeneous_tree *tree, tree_node *ap, text_stream *fragment, int in_code) { if (Str::len(fragment) > 0) @@ -223,9 +223,9 @@ void TextWeaver::inline_code_fragment(heterogeneous_tree *tree, tree_node *ap, t Trees::make_child(SC, I); } -@h Code text. +@ \section{Code text.} -= +<<*>>= void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap, text_stream *matter, text_stream *colouring, int linked) { weave_document_node *C = RETRIEVE_POINTER_weave_document_node(tree->root->content); @@ -234,11 +234,11 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap, int from = 0; for (int i=0; i < Str::len(matter); i++) { if (linked) { - @; + <>; text_stream *xref_notation = Bibliographic::get_datum(wv->weave_web->md, I"Cross-References Notation"); if (Str::ne(xref_notation, I"Off")) - @; + <>; } if ((Str::get_at(colouring, i) == FUNCTION_COLOUR) && (wv->current_weave_line->category != TEXT_EXTRACT_LCAT)) { @@ -248,7 +248,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap, PUT_TO(fname, Str::get_at(matter, j++)); if (Analyser::is_reserved_word_for_section( wv->current_weave_line->owning_section, fname, FUNCTION_COLOUR)) - @; + <>; 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)); } -@ = +<>= if ((Str::includes_at(matter, i, I"http://")) || (Str::includes_at(matter, i, I"https://"))) { TEMPORARY_TEXT(after) @@ -273,7 +273,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap, DISCARD_TEXT(after) } -@ = +<>= int N = Str::len(xref_notation); if ((Str::includes_at(matter, i, xref_notation))) { int j = i + N+1; @@ -281,7 +281,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap, if (Str::includes_at(matter, j, xref_notation)) { TEMPORARY_TEXT(reference) Str::substr(reference, Str::at(matter, i + N), Str::at(matter, j)); - @; + <>; DISCARD_TEXT(reference) break; } @@ -289,7 +289,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap, } } -@ = +<>= TEMPORARY_TEXT(url) TEMPORARY_TEXT(title) int ext = FALSE; @@ -304,7 +304,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap, DISCARD_TEXT(url) DISCARD_TEXT(title) -@ = +<>= language_function *fn = Analyser::get_function( wv->current_weave_line->owning_section, fname, FUNCTION_COLOUR); if (fn) { @@ -330,7 +330,7 @@ void TextWeaver::source_code(heterogeneous_tree *tree, tree_node *ap, } } -@ = +<<*>>= void TextWeaver::source_code_piece(heterogeneous_tree *tree, tree_node *ap, text_stream *matter, text_stream *colouring, int from, int to) { if (to > from) {