Chapter 1: Nowebify.

This commit is contained in:
AwesomeAdam54321 2024-03-09 12:30:16 +08:00
parent eec9a49d8c
commit 5cdfe7a60b
6 changed files with 328 additions and 322 deletions

View file

@ -3,19 +3,19 @@
Mainly for HTML, to add the necessary JavaScript for unusual requirements Mainly for HTML, to add the necessary JavaScript for unusual requirements
such as equations or footnotes. such as equations or footnotes.
@h Creation. @ \section{Creation.}
At present, plugins are simply their names: Inweb knows as little as possible At present, plugins are simply their names: Inweb knows as little as possible
about what they do. The model is just that a file being woven either does or about what they do. The model is just that a file being woven either does or
does not need a plugin of a given name. does not need a plugin of a given name.
= <<*>>=
typedef struct weave_plugin { typedef struct weave_plugin {
struct text_stream *plugin_name; struct text_stream *plugin_name;
int last_included_in_round; int last_included_in_round;
CLASS_DEFINITION CLASS_DEFINITION
} weave_plugin; } weave_plugin;
@ = <<*>>=
weave_plugin *Assets::new(text_stream *name) { weave_plugin *Assets::new(text_stream *name) {
weave_plugin *wp; weave_plugin *wp;
LOOP_OVER(wp, weave_plugin) LOOP_OVER(wp, weave_plugin)
@ -30,7 +30,7 @@ weave_plugin *Assets::new(text_stream *name) {
@ And almost the same can be said about colour schemes, except that these we @ And almost the same can be said about colour schemes, except that these we
actually look for: they will be available to some patterns and not others. actually look for: they will be available to some patterns and not others.
= <<*>>=
typedef struct colour_scheme { typedef struct colour_scheme {
struct text_stream *scheme_name; struct text_stream *scheme_name;
struct text_stream *prefix; struct text_stream *prefix;
@ -39,7 +39,7 @@ typedef struct colour_scheme {
CLASS_DEFINITION CLASS_DEFINITION
} colour_scheme; } colour_scheme;
@ = <<*>>=
colour_scheme *Assets::find_colour_scheme(weave_pattern *pattern, colour_scheme *Assets::find_colour_scheme(weave_pattern *pattern,
text_stream *name, text_stream *pre) { text_stream *name, text_stream *pre) {
colour_scheme *cs; colour_scheme *cs;
@ -61,12 +61,12 @@ colour_scheme *Assets::find_colour_scheme(weave_pattern *pattern,
return cs; return cs;
} }
@h Plugin inclusion. @ \section{Plugin inclusion.}
Plugins are included both by the pattern, if they are needed for anything Plugins are included both by the pattern, if they are needed for anything
woven to that pattern, and by the individual weave order, if a particular woven to that pattern, and by the individual weave order, if a particular
need has arisen on a particular file. need has arisen on a particular file.
= <<*>>=
int current_inclusion_round = 0; int current_inclusion_round = 0;
void Assets::include_relevant_plugins(text_stream *OUT, weave_pattern *pattern, void Assets::include_relevant_plugins(text_stream *OUT, weave_pattern *pattern,
web *W, weave_order *wv, filename *from) { web *W, weave_order *wv, filename *from) {
@ -86,11 +86,11 @@ no matter how many times this is called.
To include a plugin is by definition to include its assets. These may be held To include a plugin is by definition to include its assets. These may be held
either in the current pattern, or in the one it is based on, or the one either in the current pattern, or in the one it is based on, or the one
that in turn is based on, and so forth. The first-discovered asset wins: that in turn is based on, and so forth. The first-discovered asset wins:
i.e., if the current pattern's copy of the asset contains |MyAsset.png| then i.e., if the current pattern's copy of the asset contains [[MyAsset.png]] then
this prevails over any |MyAsset.png| held by patterns further down. To do this prevails over any [[MyAsset.png]] held by patterns further down. To do
this, we store the leafnames in a dictionary. this, we store the leafnames in a dictionary.
= <<*>>=
void Assets::include_plugin(OUTPUT_STREAM, web *W, weave_plugin *wp, void Assets::include_plugin(OUTPUT_STREAM, web *W, weave_plugin *wp,
weave_pattern *pattern, filename *from) { weave_pattern *pattern, filename *from) {
if (wp->last_included_in_round == current_inclusion_round) return; if (wp->last_included_in_round == current_inclusion_round) return;
@ -125,13 +125,13 @@ void Assets::include_plugin(OUTPUT_STREAM, web *W, weave_plugin *wp,
} }
} }
@ Colour schemes are CSS files held slightly differently, in the |Colouring| @ Colour schemes are CSS files held slightly differently, in the [[Colouring]]
subdirectory of (presumably) an HTML-based pattern. subdirectory of (presumably) an HTML-based pattern.
A colour scheme can only be included once in each round, i.e., for each woven A colour scheme can only be included once in each round, i.e., for each woven
file, no matter how many times this is called. file, no matter how many times this is called.
= <<*>>=
void Assets::include_colour_scheme(OUTPUT_STREAM, web *W, colour_scheme *cs, void Assets::include_colour_scheme(OUTPUT_STREAM, web *W, colour_scheme *cs,
weave_pattern *pattern, filename *from) { weave_pattern *pattern, filename *from) {
if (cs->last_included_in_round == current_inclusion_round) return; if (cs->last_included_in_round == current_inclusion_round) return;
@ -153,21 +153,22 @@ void Assets::include_colour_scheme(OUTPUT_STREAM, web *W, colour_scheme *cs,
DISCARD_TEXT(css) DISCARD_TEXT(css)
} }
@h Asset rules lists. @ \section{Asset rules lists.}
The practical effect of the two function above, then, is to call The practical effect of the two function above, then, is to call
//Assets::include_asset// on each asset needed. What that function does //Assets::include_asset// on each asset needed. What that function does
is highly configurable by the pattern, so we now have to show how. Each is highly configurable by the pattern, so we now have to show how. Each
different filename extension, such as |.jpg|, has its own rule for what to do: different filename extension, such as [[.jpg]], has its own rule for what to do:
@e EMBED_ASSET_METHOD from 1 <<*>>=
@e COPY_ASSET_METHOD enum EMBED_ASSET_METHOD from 1
@e PRIVATE_COPY_ASSET_METHOD enum COPY_ASSET_METHOD
@e COLLATE_ASSET_METHOD enum PRIVATE_COPY_ASSET_METHOD
enum COLLATE_ASSET_METHOD
= <<*>>=
typedef struct asset_rule { typedef struct asset_rule {
struct text_stream *applies_to; struct text_stream *applies_to;
int method; /* one of the |*_ASSET_METHOD| values above */ int method; /* one of the [[*_ASSET_METHOD]] values above */
struct text_stream *pre; struct text_stream *pre;
struct text_stream *post; struct text_stream *post;
int transform_names; int transform_names;
@ -175,22 +176,22 @@ typedef struct asset_rule {
} asset_rule; } asset_rule;
@ A pattern has a list of such rules, as follows. In each list, exactly one @ A pattern has a list of such rules, as follows. In each list, exactly one
rule has the empty text as its |applies_to|: that one is the default, for any rule has the empty text as its [[applies_to]]: that one is the default, for any
file whose extension does not appear in the rules list. file whose extension does not appear in the rules list.
(The default rule is to copy the file as a binary object, doing nothing fancy.) (The default rule is to copy the file as a binary object, doing nothing fancy.)
= <<*>>=
linked_list *Assets::new_asset_rules_list(void) { linked_list *Assets::new_asset_rules_list(void) {
linked_list *L = NEW_LINKED_LIST(asset_rule); linked_list *L = NEW_LINKED_LIST(asset_rule);
Assets::add_asset_rule(L, I"", I"copy", NULL); Assets::add_asset_rule(L, I"", I"copy", NULL);
return L; return L;
} }
@ This is called by //Patterns// in response to |assets: EXT CMD| commands. The @ This is called by //Patterns// in response to [[assets: EXT CMD]] commands. The
|CMD| part is in |line|. [[CMD]] part is in [[line]].
= <<*>>=
void Assets::add_asset_rule(linked_list *L, text_stream *ext, text_stream *line, void Assets::add_asset_rule(linked_list *L, text_stream *ext, text_stream *line,
text_file_position *tfp) { text_file_position *tfp) {
asset_rule *R = Assets::new_rule(L, ext, line, tfp); asset_rule *R = Assets::new_rule(L, ext, line, tfp);
@ -203,23 +204,23 @@ asset_rule *Assets::new_rule(linked_list *L, text_stream *ext, text_stream *line
if (L) if (L)
LOOP_OVER_LINKED_LIST(R, asset_rule, L) LOOP_OVER_LINKED_LIST(R, asset_rule, L)
if (Str::eq_insensitive(R->applies_to, ext)) { if (Str::eq_insensitive(R->applies_to, ext)) {
@<Use this R@>; <<Use this R>>;
return R; return R;
} }
R = CREATE(asset_rule); R = CREATE(asset_rule);
R->applies_to = Str::duplicate(ext); R->applies_to = Str::duplicate(ext);
@<Set R to defaults@>; <<Set R to defaults>>;
@<Use this R@>; <<Use this R>>;
return R; return R;
} }
@<Set R to defaults@> = <<Set R to defaults>>=
R->method = COPY_ASSET_METHOD; R->method = COPY_ASSET_METHOD;
R->pre = Str::new(); R->pre = Str::new();
R->post = Str::new(); R->post = Str::new();
R->transform_names = FALSE; R->transform_names = FALSE;
@<Use this R@> = <<Use this R>>=
text_stream *cmd = line; text_stream *cmd = line;
text_stream *detail = NULL; text_stream *detail = NULL;
match_results mr = Regexp::create_mr(); match_results mr = Regexp::create_mr();
@ -228,13 +229,13 @@ asset_rule *Assets::new_rule(linked_list *L, text_stream *ext, text_stream *line
detail = mr.exp[1]; detail = mr.exp[1];
} }
if (Str::eq(cmd, I"copy")) { if (Str::eq(cmd, I"copy")) {
@<Set R to defaults@>; R->method = COPY_ASSET_METHOD; <<Set R to defaults>>; R->method = COPY_ASSET_METHOD;
} else if (Str::eq(cmd, I"private copy")) { } else if (Str::eq(cmd, I"private copy")) {
@<Set R to defaults@>; R->method = PRIVATE_COPY_ASSET_METHOD; <<Set R to defaults>>; R->method = PRIVATE_COPY_ASSET_METHOD;
} else if (Str::eq(cmd, I"embed")) { } else if (Str::eq(cmd, I"embed")) {
@<Set R to defaults@>; R->method = EMBED_ASSET_METHOD; <<Set R to defaults>>; R->method = EMBED_ASSET_METHOD;
} else if (Str::eq(cmd, I"collate")) { } else if (Str::eq(cmd, I"collate")) {
@<Set R to defaults@>; R->method = COLLATE_ASSET_METHOD; <<Set R to defaults>>; R->method = COLLATE_ASSET_METHOD;
} else if (Str::eq(cmd, I"prefix")) { } else if (Str::eq(cmd, I"prefix")) {
R->pre = Str::duplicate(detail); R->pre = Str::duplicate(detail);
} else if (Str::eq(cmd, I"suffix")) { } else if (Str::eq(cmd, I"suffix")) {
@ -244,11 +245,11 @@ asset_rule *Assets::new_rule(linked_list *L, text_stream *ext, text_stream *line
} else Errors::in_text_file("no such asset command", tfp); } else Errors::in_text_file("no such asset command", tfp);
Regexp::dispose_of(&mr); Regexp::dispose_of(&mr);
@ Given a filename |F| for some asset, which rule applies to it? The answer @ Given a filename [[F]] for some asset, which rule applies to it? The answer
is that if the current pattern, or any pattern it is based on, defines a rule, is that if the current pattern, or any pattern it is based on, defines a rule,
then the topmost one applies; and otherwise the default rule applies. then the topmost one applies; and otherwise the default rule applies.
= <<*>>=
asset_rule *Assets::applicable_rule(weave_pattern *pattern, filename *F) { asset_rule *Assets::applicable_rule(weave_pattern *pattern, filename *F) {
TEMPORARY_TEXT(ext) TEMPORARY_TEXT(ext)
Filenames::write_extension(ext, F); Filenames::write_extension(ext, F);
@ -266,11 +267,11 @@ asset_rule *Assets::applicable_rule(weave_pattern *pattern, filename *F) {
return NULL; return NULL;
} }
@h Inclusion of assets. @ \section{Inclusion of assets.}
Finally, then, we can include a single asset. This has already been located, Finally, then, we can include a single asset. This has already been located,
at filename |F|, and we now know how to find the applicable rule. at filename [[F]], and we now know how to find the applicable rule.
= <<*>>=
pathname *Assets::include_asset(OUTPUT_STREAM, asset_rule *R, web *W, filename *F, pathname *Assets::include_asset(OUTPUT_STREAM, asset_rule *R, web *W, filename *F,
text_stream *trans, weave_pattern *pattern, filename *from) { text_stream *trans, weave_pattern *pattern, filename *from) {
if (R == NULL) R = Assets::applicable_rule(pattern, F); if (R == NULL) R = Assets::applicable_rule(pattern, F);
@ -280,19 +281,19 @@ pathname *Assets::include_asset(OUTPUT_STREAM, asset_rule *R, web *W, filename *
WRITE_TO(url, "%S", Filenames::get_leafname(F)); WRITE_TO(url, "%S", Filenames::get_leafname(F));
if (R->transform_names == FALSE) trans = NULL; if (R->transform_names == FALSE) trans = NULL;
pathname *result = NULL; pathname *result = NULL;
if (Str::len(R->pre) > 0) @<Embed the prefix, if any@>; if (Str::len(R->pre) > 0) <<Embed the prefix, if any>>;
switch (R->method) { switch (R->method) {
case EMBED_ASSET_METHOD: @<Embed asset@>; break; case EMBED_ASSET_METHOD: <<Embed asset>>; break;
case COPY_ASSET_METHOD: @<Copy asset@>; break; case COPY_ASSET_METHOD: <<Copy asset>>; break;
case PRIVATE_COPY_ASSET_METHOD: @<Copy asset@>; break; case PRIVATE_COPY_ASSET_METHOD: <<Copy asset>>; break;
case COLLATE_ASSET_METHOD: @<Collate asset@>; break; case COLLATE_ASSET_METHOD: <<Collate asset>>; break;
} }
if (Str::len(R->post) > 0) @<Embed the suffix, if any@>; if (Str::len(R->post) > 0) <<Embed the suffix, if any>>;
DISCARD_TEXT(url) DISCARD_TEXT(url)
return result; return result;
} }
@<Embed the prefix, if any@> = <<Embed the prefix, if any>>=
for (int i=0; i<Str::len(R->pre); i++) { for (int i=0; i<Str::len(R->pre); i++) {
if (Str::includes_at(R->pre, i, I"URL")) { if (Str::includes_at(R->pre, i, I"URL")) {
WRITE("%S", url); WRITE("%S", url);
@ -301,11 +302,11 @@ pathname *Assets::include_asset(OUTPUT_STREAM, asset_rule *R, web *W, filename *
} }
WRITE("\n"); WRITE("\n");
@<Embed asset@> = <<Embed asset>>=
if (verbose_mode) PRINT("Embed asset %f\n", F); if (verbose_mode) PRINT("Embed asset %f\n", F);
Assets::transform(OUT, F, trans); Assets::transform(OUT, F, trans);
@<Copy asset@> = <<Copy asset>>=
pathname *H = W->redirect_weaves_to; pathname *H = W->redirect_weaves_to;
if (H == NULL) H = Reader::woven_folder(W); if (H == NULL) H = Reader::woven_folder(W);
if ((AP) && (R->method != PRIVATE_COPY_ASSET_METHOD)) H = AP; if ((AP) && (R->method != PRIVATE_COPY_ASSET_METHOD)) H = AP;
@ -326,11 +327,11 @@ pathname *Assets::include_asset(OUTPUT_STREAM, asset_rule *R, web *W, filename *
Epub::note_image(W->as_ebook, rel); Epub::note_image(W->as_ebook, rel);
} }
@<Collate asset@> = <<Collate asset>>=
if (verbose_mode) PRINT("Collate asset %f\n", F); if (verbose_mode) PRINT("Collate asset %f\n", F);
Collater::for_web_and_pattern(OUT, W, pattern, F, from); Collater::for_web_and_pattern(OUT, W, pattern, F, from);
@<Embed the suffix, if any@> = <<Embed the suffix, if any>>=
for (int i=0; i<Str::len(R->post); i++) { for (int i=0; i<Str::len(R->post); i++) {
if (Str::includes_at(R->post, i, I"URL")) { if (Str::includes_at(R->post, i, I"URL")) {
WRITE("%S", url); WRITE("%S", url);
@ -340,11 +341,11 @@ pathname *Assets::include_asset(OUTPUT_STREAM, asset_rule *R, web *W, filename *
WRITE("\n"); WRITE("\n");
@ "Transforming" is what happens to a CSS file to change the class names of @ "Transforming" is what happens to a CSS file to change the class names of
its |span| and |pre| styling rules, to add a prefix text. This is what changes its [[span]] and [[pre]] styling rules, to add a prefix text. This is what changes
the style names for colouring, say, COBOL source code from, e.g., the style names for colouring, say, COBOL source code from, e.g.,
|span.identifier-syntax| to |span.ConsoleText-identifier-syntax|. [[span.identifier-syntax]] to [[span.ConsoleText-identifier-syntax]].
= <<*>>=
typedef struct css_file_transformation { typedef struct css_file_transformation {
struct text_stream *OUT; struct text_stream *OUT;
struct text_stream *trans; struct text_stream *trans;

View file

@ -5,99 +5,101 @@ module.
@ Every program using //foundation// must define this: @ Every program using //foundation// must define this:
@d PROGRAM_NAME "inweb" <<*>>=
#define PROGRAM_NAME "inweb"
@ We need to itemise the structures we'll want to allocate. For explanations, @ We need to itemise the structures we'll want to allocate. For explanations,
see //foundation: A Brief Guide to Foundation//. see //foundation: A Brief Guide to Foundation//.
@e asset_rule_CLASS <<*>>=
@e breadcrumb_request_CLASS enum asset_rule_CLASS
@e chapter_CLASS enum breadcrumb_request_CLASS
@e colony_CLASS enum chapter_CLASS
@e colony_member_CLASS enum colony_CLASS
@e colour_scheme_CLASS enum colony_member_CLASS
@e colouring_language_block_CLASS enum colour_scheme_CLASS
@e colouring_rule_CLASS enum colouring_language_block_CLASS
@e defined_constant_CLASS enum colouring_rule_CLASS
@e enumeration_set_CLASS enum defined_constant_CLASS
@e footnote_CLASS enum enumeration_set_CLASS
@e hash_table_entry_CLASS enum footnote_CLASS
@e hash_table_entry_usage_CLASS enum hash_table_entry_CLASS
@e language_function_CLASS enum hash_table_entry_usage_CLASS
@e language_type_CLASS enum language_function_CLASS
@e macro_usage_CLASS enum language_type_CLASS
@e makefile_specifics_CLASS enum macro_usage_CLASS
@e nonterminal_variable_CLASS enum makefile_specifics_CLASS
@e para_macro_CLASS enum nonterminal_variable_CLASS
@e paragraph_CLASS enum para_macro_CLASS
@e paragraph_tagging_CLASS enum paragraph_CLASS
@e preform_nonterminal_CLASS enum paragraph_tagging_CLASS
@e programming_language_CLASS enum preform_nonterminal_CLASS
@e reserved_word_CLASS enum programming_language_CLASS
@e section_CLASS enum reserved_word_CLASS
@e source_line_CLASS enum section_CLASS
@e structure_element_CLASS enum source_line_CLASS
@e tangle_target_CLASS enum structure_element_CLASS
@e tex_results_CLASS enum tangle_target_CLASS
@e text_literal_CLASS enum tex_results_CLASS
@e theme_tag_CLASS enum text_literal_CLASS
@e weave_format_CLASS enum theme_tag_CLASS
@e weave_pattern_CLASS enum weave_format_CLASS
@e weave_plugin_CLASS enum weave_pattern_CLASS
@e weave_order_CLASS enum weave_plugin_CLASS
@e web_CLASS enum weave_order_CLASS
@e writeme_asset_CLASS enum web_CLASS
enum writeme_asset_CLASS
@e weave_document_node_CLASS enum weave_document_node_CLASS
@e weave_head_node_CLASS enum weave_head_node_CLASS
@e weave_body_node_CLASS enum weave_body_node_CLASS
@e weave_tail_node_CLASS enum weave_tail_node_CLASS
@e weave_section_header_node_CLASS enum weave_section_header_node_CLASS
@e weave_section_footer_node_CLASS enum weave_section_footer_node_CLASS
@e weave_chapter_header_node_CLASS enum weave_chapter_header_node_CLASS
@e weave_chapter_footer_node_CLASS enum weave_chapter_footer_node_CLASS
@e weave_verbatim_node_CLASS enum weave_verbatim_node_CLASS
@e weave_section_purpose_node_CLASS enum weave_section_purpose_node_CLASS
@e weave_subheading_node_CLASS enum weave_subheading_node_CLASS
@e weave_bar_node_CLASS enum weave_bar_node_CLASS
@e weave_linebreak_node_CLASS enum weave_linebreak_node_CLASS
@e weave_pagebreak_node_CLASS enum weave_pagebreak_node_CLASS
@e weave_paragraph_heading_node_CLASS enum weave_paragraph_heading_node_CLASS
@e weave_endnote_node_CLASS enum weave_endnote_node_CLASS
@e weave_material_node_CLASS enum weave_material_node_CLASS
@e weave_figure_node_CLASS enum weave_figure_node_CLASS
@e weave_extract_node_CLASS enum weave_extract_node_CLASS
@e weave_audio_node_CLASS enum weave_audio_node_CLASS
@e weave_download_node_CLASS enum weave_download_node_CLASS
@e weave_video_node_CLASS enum weave_video_node_CLASS
@e weave_embed_node_CLASS enum weave_embed_node_CLASS
@e weave_pmac_node_CLASS enum weave_pmac_node_CLASS
@e weave_vskip_node_CLASS enum weave_vskip_node_CLASS
@e weave_chapter_node_CLASS enum weave_chapter_node_CLASS
@e weave_section_node_CLASS enum weave_section_node_CLASS
@e weave_code_line_node_CLASS enum weave_code_line_node_CLASS
@e weave_function_usage_node_CLASS enum weave_function_usage_node_CLASS
@e weave_commentary_node_CLASS enum weave_commentary_node_CLASS
@e weave_carousel_slide_node_CLASS enum weave_carousel_slide_node_CLASS
@e weave_toc_node_CLASS enum weave_toc_node_CLASS
@e weave_toc_line_node_CLASS enum weave_toc_line_node_CLASS
@e weave_chapter_title_page_node_CLASS enum weave_chapter_title_page_node_CLASS
@e weave_defn_node_CLASS enum weave_defn_node_CLASS
@e weave_source_code_node_CLASS enum weave_source_code_node_CLASS
@e weave_url_node_CLASS enum weave_url_node_CLASS
@e weave_footnote_cue_node_CLASS enum weave_footnote_cue_node_CLASS
@e weave_begin_footnote_text_node_CLASS enum weave_begin_footnote_text_node_CLASS
@e weave_display_line_node_CLASS enum weave_display_line_node_CLASS
@e weave_function_defn_node_CLASS enum weave_function_defn_node_CLASS
@e weave_item_node_CLASS enum weave_item_node_CLASS
@e weave_grammar_index_node_CLASS enum weave_grammar_index_node_CLASS
@e weave_inline_node_CLASS enum weave_inline_node_CLASS
@e weave_locale_node_CLASS enum weave_locale_node_CLASS
@e weave_maths_node_CLASS enum weave_maths_node_CLASS
@ And then expand the following macros, all defined in //Memory//. @ And then expand the following macros, all defined in //Memory//.
= <<*>>=
DECLARE_CLASS_ALLOCATED_IN_ARRAYS(source_line, 1000) DECLARE_CLASS_ALLOCATED_IN_ARRAYS(source_line, 1000)
DECLARE_CLASS(asset_rule) DECLARE_CLASS(asset_rule)
DECLARE_CLASS(breadcrumb_request) DECLARE_CLASS(breadcrumb_request)

View file

@ -3,63 +3,63 @@
To parse the command line arguments with which inweb was called, To parse the command line arguments with which inweb was called,
and to handle any errors it needs to issue. and to handle any errors it needs to issue.
@h Instructions. @ \section{Instructions.}
The following structure exists just to hold what the user specified on the The following structure exists just to hold what the user specified on the
command line: there will only ever be one of these. command line: there will only ever be one of these.
= <<*>>=
typedef struct inweb_instructions { typedef struct inweb_instructions {
int inweb_mode; /* our main mode of operation: one of the |*_MODE| constants */ int inweb_mode; /* our main mode of operation: one of the [[*_MODE]] constants */
struct pathname *chosen_web; /* project folder relative to cwd */ struct pathname *chosen_web; /* project folder relative to cwd */
struct filename *chosen_file; /* or, single file relative to cwd */ struct filename *chosen_file; /* or, single file relative to cwd */
struct text_stream *chosen_range; /* which subset of this web we apply to (often, all of it) */ struct text_stream *chosen_range; /* which subset of this web we apply to (often, all of it) */
int chosen_range_actually_chosen; /* rather than being a default choice */ int chosen_range_actually_chosen; /* rather than being a default choice */
int swarm_mode; /* relevant to weaving only: one of the |*_SWARM| constants */ int swarm_mode; /* relevant to weaving only: one of the [[*_SWARM]] constants */
struct text_stream *tag_setting; /* |-weave-tag X|: weave, but only the material tagged X */ struct text_stream *tag_setting; /* [[-weave-tag X]]: weave, but only the material tagged X */
struct text_stream *weave_pattern; /* |-weave-as X|: for example, |-weave-to HTML| */ struct text_stream *weave_pattern; /* [[-weave-as X|: for example, |-weave-to HTML]] */
int show_languages_switch; /* |-show-languages|: print list of available PLs */ int show_languages_switch; /* [[-show-languages]]: print list of available PLs */
int catalogue_switch; /* |-catalogue|: print catalogue of sections */ int catalogue_switch; /* [[-catalogue]]: print catalogue of sections */
int functions_switch; /* |-functions|: print catalogue of functions within sections */ int functions_switch; /* [[-functions]]: print catalogue of functions within sections */
int structures_switch; /* |-structures|: print catalogue of structures within sections */ int structures_switch; /* [[-structures]]: print catalogue of structures within sections */
int advance_switch; /* |-advance-build|: advance build file for web */ int advance_switch; /* [[-advance-build]]: advance build file for web */
int scan_switch; /* |-scan|: simply show the syntactic scan of the source */ int scan_switch; /* [[-scan]]: simply show the syntactic scan of the source */
int ctags_switch; /* |-ctags|: generate a set of Universal Ctags on each tangle */ int ctags_switch; /* [[-ctags]]: generate a set of Universal Ctags on each tangle */
struct filename *weave_to_setting; /* |-weave-to X|: the pathname X, if supplied */ struct filename *weave_to_setting; /* [[-weave-to X]]: the pathname X, if supplied */
struct pathname *weave_into_setting; /* |-weave-into X|: the pathname X, if supplied */ struct pathname *weave_into_setting; /* [[-weave-into X]]: the pathname X, if supplied */
int sequential; /* give the sections sequential sigils */ int sequential; /* give the sections sequential sigils */
struct filename *tangle_setting; /* |-tangle-to X|: the pathname X, if supplied */ struct filename *tangle_setting; /* [[-tangle-to X]]: the pathname X, if supplied */
struct filename *ctags_setting; /* |-ctags-to X|: the pathname X, if supplied */ struct filename *ctags_setting; /* [[-ctags-to X]]: the pathname X, if supplied */
struct filename *makefile_setting; /* |-makefile X|: the filename X, if supplied */ struct filename *makefile_setting; /* [[-makefile X]]: the filename X, if supplied */
struct filename *gitignore_setting; /* |-gitignore X|: the filename X, if supplied */ struct filename *gitignore_setting; /* [[-gitignore X]]: the filename X, if supplied */
struct filename *advance_setting; /* |-advance-build-file X|: advance build file X */ struct filename *advance_setting; /* [[-advance-build-file X]]: advance build file X */
struct filename *writeme_setting; /* |-write-me X|: advance build file X */ struct filename *writeme_setting; /* [[-write-me X]]: advance build file X */
struct filename *prototype_setting; /* |-prototype X|: the pathname X, if supplied */ struct filename *prototype_setting; /* [[-prototype X]]: the pathname X, if supplied */
struct filename *navigation_setting; /* |-navigation X|: the filename X, if supplied */ struct filename *navigation_setting; /* [[-navigation X]]: the filename X, if supplied */
struct filename *colony_setting; /* |-colony X|: the filename X, if supplied */ struct filename *colony_setting; /* [[-colony X]]: the filename X, if supplied */
struct text_stream *member_setting; /* |-member X|: sets web to member X of colony */ struct text_stream *member_setting; /* [[-member X]]: sets web to member X of colony */
struct linked_list *breadcrumb_setting; /* of |breadcrumb_request| */ struct linked_list *breadcrumb_setting; /* of [[breadcrumb_request]] */
struct text_stream *platform_setting; /* |-platform X|: sets prevailing platform to X */ struct text_stream *platform_setting; /* [[-platform X]]: sets prevailing platform to X */
int verbose_switch; /* |-verbose|: print names of files read to stdout */ int verbose_switch; /* [[-verbose]]: print names of files read to stdout */
int targets; /* used only for parsing */ int targets; /* used only for parsing */
struct programming_language *test_language_setting; /* |-test-language X| */ struct programming_language *test_language_setting; /* [[-test-language X]] */
struct filename *test_language_on_setting; /* |-test-language-on X| */ struct filename *test_language_on_setting; /* [[-test-language-on X]] */
struct pathname *import_setting; /* |-import X|: where to find imported webs */ struct pathname *import_setting; /* [[-import X]]: where to find imported webs */
} inweb_instructions; } inweb_instructions;
@h Reading the command line. @ \section{Reading the command line.}
The dull work of this is done by the Foundation module: all we need to do is The dull work of this is done by the Foundation module: all we need to do is
to enumerate constants for the Inweb-specific command line switches, and to enumerate constants for the Inweb-specific command line switches, and
then declare them. then declare them.
= <<*>>=
inweb_instructions Configuration::read(int argc, char **argv) { inweb_instructions Configuration::read(int argc, char **argv) {
inweb_instructions args; inweb_instructions args;
@<Initialise the args@>; <<Initialise the args>>;
@<Declare the command-line switches specific to Inweb@>; <<Declare the command-line switches specific to Inweb>>;
CommandLine::read(argc, argv, &args, &Configuration::switch, &Configuration::bareword); CommandLine::read(argc, argv, &args, &Configuration::switch, &Configuration::bareword);
Configuration::member_and_colony(&args); Configuration::member_and_colony(&args);
if (Str::len(args.weave_pattern) == 0) WRITE_TO(args.weave_pattern, "HTML"); if (Str::len(args.weave_pattern) == 0) WRITE_TO(args.weave_pattern, "HTML");
@ -75,7 +75,7 @@ inweb_instructions Configuration::read(int argc, char **argv) {
return args; return args;
} }
@<Initialise the args@> = <<Initialise the args>>=
args.inweb_mode = NO_MODE; args.inweb_mode = NO_MODE;
args.swarm_mode = SWARM_OFF_SWM; args.swarm_mode = SWARM_OFF_SWM;
args.show_languages_switch = FALSE; args.show_languages_switch = FALSE;
@ -112,58 +112,59 @@ inweb_instructions Configuration::read(int argc, char **argv) {
args.test_language_on_setting = NULL; args.test_language_on_setting = NULL;
@ The CommandLine section of Foundation needs to be told what command-line @ The CommandLine section of Foundation needs to be told what command-line
switches we want, other than the standard set (such as |-help|) which it switches we want, other than the standard set (such as [[-help]]) which it
provides automatically. provides automatically.
@e VERBOSE_CLSW <<*>>=
@e IMPORT_FROM_CLSW enum VERBOSE_CLSW
enum IMPORT_FROM_CLSW
@e LANGUAGES_CLSG enum LANGUAGES_CLSG
@e LANGUAGE_CLSW enum LANGUAGE_CLSW
@e LANGUAGES_CLSW enum LANGUAGES_CLSW
@e SHOW_LANGUAGES_CLSW enum SHOW_LANGUAGES_CLSW
@e TEST_LANGUAGE_CLSW enum TEST_LANGUAGE_CLSW
@e TEST_LANGUAGE_ON_CLSW enum TEST_LANGUAGE_ON_CLSW
@e ANALYSIS_CLSG enum ANALYSIS_CLSG
@e CATALOGUE_CLSW enum CATALOGUE_CLSW
@e FUNCTIONS_CLSW enum FUNCTIONS_CLSW
@e STRUCTURES_CLSW enum STRUCTURES_CLSW
@e ADVANCE_CLSW enum ADVANCE_CLSW
@e GITIGNORE_CLSW enum GITIGNORE_CLSW
@e MAKEFILE_CLSW enum MAKEFILE_CLSW
@e WRITEME_CLSW enum WRITEME_CLSW
@e PLATFORM_CLSW enum PLATFORM_CLSW
@e ADVANCE_FILE_CLSW enum ADVANCE_FILE_CLSW
@e PROTOTYPE_CLSW enum PROTOTYPE_CLSW
@e SCAN_CLSW enum SCAN_CLSW
@e WEAVING_CLSG enum WEAVING_CLSG
@e WEAVE_CLSW enum WEAVE_CLSW
@e WEAVE_INTO_CLSW enum WEAVE_INTO_CLSW
@e WEAVE_TO_CLSW enum WEAVE_TO_CLSW
@e OPEN_CLSW enum OPEN_CLSW
@e WEAVE_AS_CLSW enum WEAVE_AS_CLSW
@e WEAVE_TAG_CLSW enum WEAVE_TAG_CLSW
@e BREADCRUMB_CLSW enum BREADCRUMB_CLSW
@e NAVIGATION_CLSW enum NAVIGATION_CLSW
@e TANGLING_CLSG enum TANGLING_CLSG
@e TANGLE_CLSW enum TANGLE_CLSW
@e TANGLE_TO_CLSW enum TANGLE_TO_CLSW
@e CTAGS_TO_CLSW enum CTAGS_TO_CLSW
@e CTAGS_CLSW enum CTAGS_CLSW
@e COLONIAL_CLSG enum COLONIAL_CLSG
@e COLONY_CLSW enum COLONY_CLSW
@e MEMBER_CLSW enum MEMBER_CLSW
@<Declare the command-line switches specific to Inweb@> = <<Declare the command-line switches specific to Inweb>>=
CommandLine::declare_heading(L"inweb: a tool for literate programming\n\n" CommandLine::declare_heading(L"inweb: a tool for literate programming\n\n"
L"Usage: inweb WEB OPTIONS RANGE\n\n" L"Usage: inweb WEB OPTIONS RANGE\n\n"
L"WEB must be a directory holding a literate program (a 'web')\n\n" L"WEB must be a directory holding a literate program (a 'web')\n\n"
@ -265,9 +266,9 @@ provides automatically.
CommandLine::declare_switch(IMPORT_FROM_CLSW, L"import-from", 2, CommandLine::declare_switch(IMPORT_FROM_CLSW, L"import-from", 2,
L"specify that imported modules are at pathname X"); L"specify that imported modules are at pathname X");
@ Foundation calls this on any |-switch| argument read: @ Foundation calls this on any [[-switch]] argument read:
= <<*>>=
void Configuration::switch(int id, int val, text_stream *arg, void *state) { void Configuration::switch(int id, int val, text_stream *arg, void *state) {
inweb_instructions *args = (inweb_instructions *) state; inweb_instructions *args = (inweb_instructions *) state;
switch (id) { switch (id) {
@ -379,7 +380,7 @@ void Configuration::switch(int id, int val, text_stream *arg, void *state) {
@ The colony file is, in one sense, a collection of presets for the web @ The colony file is, in one sense, a collection of presets for the web
location and its navigational aids. location and its navigational aids.
= <<*>>=
void Configuration::member_and_colony(inweb_instructions *args) { void Configuration::member_and_colony(inweb_instructions *args) {
if (args->colony_setting) Colonies::load(args->colony_setting); if (args->colony_setting) Colonies::load(args->colony_setting);
if (Str::len(args->member_setting) > 0) { if (Str::len(args->member_setting) > 0) {
@ -402,10 +403,10 @@ void Configuration::member_and_colony(inweb_instructions *args) {
} }
@ Foundation calls this routine on any command-line argument which is @ Foundation calls this routine on any command-line argument which is
neither a switch (like |-weave|), nor an argument for a switch (like neither a switch (like [[-weave]]), nor an argument for a switch (like
the |X| in |-weave-as X|). the [[X]] in [[-weave-as X]]).
= <<*>>=
void Configuration::bareword(int id, text_stream *opt, void *state) { void Configuration::bareword(int id, text_stream *opt, void *state) {
inweb_instructions *args = (inweb_instructions *) state; inweb_instructions *args = (inweb_instructions *) state;
if ((args->chosen_web == NULL) && (args->chosen_file == NULL)) { if ((args->chosen_web == NULL) && (args->chosen_file == NULL)) {
@ -416,11 +417,11 @@ void Configuration::bareword(int id, text_stream *opt, void *state) {
} else Configuration::set_range(args, opt); } else Configuration::set_range(args, opt);
} }
@ Here we read a range. The special ranges |index|, |chapters| and |sections| @ Here we read a range. The special ranges [[index]], [[chapters]] and [[sections]]
are converted into swarm settings instead. |all| is simply an alias for |0|. are converted into swarm settings instead. [[all]] is simply an alias for [[0]].
Otherwise, a range is a chapter number/letter, or a section range. Otherwise, a range is a chapter number/letter, or a section range.
= <<*>>=
void Configuration::set_range(inweb_instructions *args, text_stream *opt) { void Configuration::set_range(inweb_instructions *args, text_stream *opt) {
match_results mr = Regexp::create_mr(); match_results mr = Regexp::create_mr();
if (Str::eq_wide_string(opt, L"index")) { if (Str::eq_wide_string(opt, L"index")) {
@ -452,7 +453,7 @@ void Configuration::set_range(inweb_instructions *args, text_stream *opt) {
@ We can only be in a single mode at a time: @ We can only be in a single mode at a time:
= <<*>>=
void Configuration::set_fundamental_mode(inweb_instructions *args, int new_material) { void Configuration::set_fundamental_mode(inweb_instructions *args, int new_material) {
if ((args->inweb_mode != NO_MODE) && (args->inweb_mode != new_material)) if ((args->inweb_mode != NO_MODE) && (args->inweb_mode != new_material))
Errors::fatal("can only do one at a time - weaving, tangling or analysing"); Errors::fatal("can only do one at a time - weaving, tangling or analysing");

View file

@ -2,32 +2,32 @@
Managing weave patterns, which are bundled configuration settings for weaving. Managing weave patterns, which are bundled configuration settings for weaving.
@h Reading in. @ \section{Reading in.}
Patterns are stored as directories in the file system, and are identified by Patterns are stored as directories in the file system, and are identified by
names such as |HTML|. On request, we need to find the directory corresponding names such as [[HTML]]. On request, we need to find the directory corresponding
to such a name, and to read it in. This structure holds the result: to such a name, and to read it in. This structure holds the result:
= <<*>>=
typedef struct weave_pattern { typedef struct weave_pattern {
struct text_stream *pattern_name; /* such as |HTML| */ struct text_stream *pattern_name; /* such as [[HTML]] */
struct pathname *pattern_location; /* the directory */ struct pathname *pattern_location; /* the directory */
struct weave_pattern *based_on; /* inherit from which other pattern? */ struct weave_pattern *based_on; /* inherit from which other pattern? */
struct weave_format *pattern_format; /* such as |DVI|: the desired final format */ struct weave_format *pattern_format; /* such as [[DVI]]: the desired final format */
struct linked_list *plugins; /* of |weave_plugin|: any extras needed */ struct linked_list *plugins; /* of [[weave_plugin]]: any extras needed */
struct linked_list *colour_schemes; /* of |colour_scheme|: any extras needed */ struct linked_list *colour_schemes; /* of [[colour_scheme]]: any extras needed */
struct text_stream *mathematics_plugin; /* name only, not a |weave_pattern *| */ struct text_stream *mathematics_plugin; /* name only, not a [[weave_pattern *]] */
struct text_stream *footnotes_plugin; /* name only, not a |weave_pattern *| */ struct text_stream *footnotes_plugin; /* name only, not a [[weave_pattern *]] */
struct text_stream *initial_extension; /* filename extension, that is */ struct text_stream *initial_extension; /* filename extension, that is */
struct linked_list *post_commands; /* of |text_stream| */ struct linked_list *post_commands; /* of [[text_stream]] */
struct linked_list *blocked_templates; /* of |text_stream| */ struct linked_list *blocked_templates; /* of [[text_stream]] */
struct linked_list *asset_rules; /* of |asset_rule| */ struct linked_list *asset_rules; /* of [[asset_rule]] */
int show_abbrevs; /* show section range abbreviations in the weave? */ int show_abbrevs; /* show section range abbreviations in the weave? */
int number_sections; /* insert section numbers into the weave? */ int number_sections; /* insert section numbers into the weave? */
struct text_stream *default_range; /* for example, |sections| */ struct text_stream *default_range; /* for example, [[sections]] */
struct web *patterned_for; /* the web which caused this to be read in */ struct web *patterned_for; /* the web which caused this to be read in */
@ -38,17 +38,17 @@ typedef struct weave_pattern {
@ When a given web needs a pattern with a given name, this is where it comes. @ When a given web needs a pattern with a given name, this is where it comes.
= <<*>>=
weave_pattern *Patterns::find(web *W, text_stream *name) { weave_pattern *Patterns::find(web *W, text_stream *name) {
filename *pattern_file = NULL; filename *pattern_file = NULL;
weave_pattern *wp = CREATE(weave_pattern); weave_pattern *wp = CREATE(weave_pattern);
@<Initialise the pattern structure@>; <<Initialise the pattern structure>>;
@<Locate the pattern directory@>; <<Locate the pattern directory>>;
@<Read in the pattern.txt file@>; <<Read in the pattern.txt file>>;
return wp; return wp;
} }
@<Initialise the pattern structure@> = <<Initialise the pattern structure>>=
wp->pattern_name = Str::duplicate(name); wp->pattern_name = Str::duplicate(name);
wp->pattern_location = NULL; wp->pattern_location = NULL;
wp->plugins = NEW_LINKED_LIST(weave_plugin); wp->plugins = NEW_LINKED_LIST(weave_plugin);
@ -66,7 +66,7 @@ weave_pattern *Patterns::find(web *W, text_stream *name) {
wp->commands = 0; wp->commands = 0;
wp->name_command_given = FALSE; wp->name_command_given = FALSE;
@<Locate the pattern directory@> = <<Locate the pattern directory>>=
wp->pattern_location = NULL; wp->pattern_location = NULL;
pathname *CP = Colonies::patterns_path(); pathname *CP = Colonies::patterns_path();
if (CP) { if (CP) {
@ -89,7 +89,7 @@ weave_pattern *Patterns::find(web *W, text_stream *name) {
if (wp->pattern_location == NULL) if (wp->pattern_location == NULL)
Errors::fatal_with_text("no such weave pattern as '%S'", name); Errors::fatal_with_text("no such weave pattern as '%S'", name);
@<Read in the pattern.txt file@> = <<Read in the pattern.txt file>>=
if (pattern_file) if (pattern_file)
TextFiles::read(pattern_file, FALSE, "can't open pattern.txt file", TextFiles::read(pattern_file, FALSE, "can't open pattern.txt file",
TRUE, Patterns::scan_pattern_line, NULL, wp); TRUE, Patterns::scan_pattern_line, NULL, wp);
@ -99,16 +99,16 @@ weave_pattern *Patterns::find(web *W, text_stream *name) {
Errors::fatal_with_text("pattern did not name itself at the top", name); Errors::fatal_with_text("pattern did not name itself at the top", name);
@ The Foundation module provides a standard way to scan text files line by @ The Foundation module provides a standard way to scan text files line by
line, and this is used to send each line in the |pattern.txt| file to the line, and this is used to send each line in the [[pattern.txt]] file to the
following routine: following routine:
= <<*>>=
void Patterns::scan_pattern_line(text_stream *line, text_file_position *tfp, void *X) { void Patterns::scan_pattern_line(text_stream *line, text_file_position *tfp, void *X) {
weave_pattern *wp = (weave_pattern *) X; weave_pattern *wp = (weave_pattern *) X;
Str::trim_white_space(line); /* ignore trailing space */ Str::trim_white_space(line); /* ignore trailing space */
if (Str::len(line) == 0) return; /* ignore blank lines */ if (Str::len(line) == 0) return; /* ignore blank lines */
if (Str::get_first_char(line) == '#') return; /* lines opening with |#| are comments */ if (Str::get_first_char(line) == '#') return; /* lines opening with [[#]] are comments */
wp->commands++; wp->commands++;
match_results mr = Regexp::create_mr(); match_results mr = Regexp::create_mr();
@ -180,7 +180,7 @@ void Patterns::scan_pattern_line(text_stream *line, text_file_position *tfp, voi
Regexp::dispose_of(&mr); Regexp::dispose_of(&mr);
} }
@ = <<*>>=
int Patterns::yes_or_no(text_stream *arg, text_file_position *tfp) { int Patterns::yes_or_no(text_stream *arg, text_file_position *tfp) {
if (Str::eq(arg, I"yes")) return TRUE; if (Str::eq(arg, I"yes")) return TRUE;
if (Str::eq(arg, I"no")) return FALSE; if (Str::eq(arg, I"no")) return FALSE;
@ -200,11 +200,11 @@ text_stream *Patterns::plugin_name(text_stream *arg, text_file_position *tfp) {
return Str::duplicate(arg); return Str::duplicate(arg);
} }
@h Post-processing. @ \section{Post-processing.}
In effect, a pattern can hold a shell script to run after each weave (subset) In effect, a pattern can hold a shell script to run after each weave (subset)
completes. completes.
= <<*>>=
void Patterns::post_process(weave_pattern *pattern, weave_order *wv) { void Patterns::post_process(weave_pattern *pattern, weave_order *wv) {
text_stream *T; text_stream *T;
LOOP_OVER_LINKED_LIST(T, text_stream, pattern->post_commands) { LOOP_OVER_LINKED_LIST(T, text_stream, pattern->post_commands) {
@ -241,14 +241,14 @@ void Patterns::post_process(weave_pattern *pattern, weave_order *wv) {
} }
} }
@h Obtaining files. @ \section{Obtaining files.}
Patterns provide place template files, such as |template-body.html|, in Patterns provide place template files, such as [[template-body.html]], in
their root directories. their root directories.
Note that if you're rash enough to set up a cycle of patterns inheriting Note that if you're rash enough to set up a cycle of patterns inheriting
from each other then this routine will lock up into an infinite loop. from each other then this routine will lock up into an infinite loop.
= <<*>>=
filename *Patterns::find_template(weave_pattern *pattern, text_stream *leafname) { filename *Patterns::find_template(weave_pattern *pattern, text_stream *leafname) {
for (weave_pattern *wp = pattern; wp; wp = wp->based_on) { for (weave_pattern *wp = pattern; wp; wp = wp->based_on) {
text_stream *T; text_stream *T;
@ -263,7 +263,7 @@ filename *Patterns::find_template(weave_pattern *pattern, text_stream *leafname)
@ Similarly, but looking in an intermediate directory: @ Similarly, but looking in an intermediate directory:
= <<*>>=
filename *Patterns::find_file_in_subdirectory(weave_pattern *pattern, filename *Patterns::find_file_in_subdirectory(weave_pattern *pattern,
text_stream *dirname, text_stream *leafname) { text_stream *dirname, text_stream *leafname) {
for (weave_pattern *wp = pattern; wp; wp = wp->based_on) { for (weave_pattern *wp = pattern; wp; wp = wp->based_on) {
@ -274,7 +274,7 @@ filename *Patterns::find_file_in_subdirectory(weave_pattern *pattern,
return NULL; return NULL;
} }
@ = <<*>>=
void Patterns::include_plugins(OUTPUT_STREAM, web *W, weave_pattern *pattern, filename *from) { void Patterns::include_plugins(OUTPUT_STREAM, web *W, weave_pattern *pattern, filename *from) {
for (weave_pattern *p = pattern; p; p = p->based_on) { for (weave_pattern *p = pattern; p; p = p->based_on) {
weave_plugin *wp; weave_plugin *wp;

View file

@ -6,7 +6,7 @@ this plan out.
@ Inweb syntax has gradually shifted over the years, but there are two main @ Inweb syntax has gradually shifted over the years, but there are two main
versions: the second was cleaned up and simplified from the first in 2019. versions: the second was cleaned up and simplified from the first in 2019.
= <<*>>=
int default_inweb_syntax = V2_SYNTAX; int default_inweb_syntax = V2_SYNTAX;
@ Inweb has a single fundamental mode of operation: on any given run, it @ Inweb has a single fundamental mode of operation: on any given run, it
@ -14,21 +14,22 @@ is either tangling, weaving or analysing. These processes use the same input
and parsing code, but then do very different things to produce their output, and parsing code, but then do very different things to produce their output,
so the fork in the road is not met until halfway through Inweb's execution. so the fork in the road is not met until halfway through Inweb's execution.
@e NO_MODE from 0 /* a special mode for doing nothing except printing command-line syntax */ <<*>>=
@e ANALYSE_MODE /* for -scan, -catalogue, -functions and so on */ enum NO_MODE from 0 /* a special mode for doing nothing except printing command-line syntax */
@e TANGLE_MODE /* for any form of -tangle */ enum ANALYSE_MODE /* for -scan, -catalogue, -functions and so on */
@e WEAVE_MODE /* for any form of -weave */ enum TANGLE_MODE /* for any form of -tangle */
@e TRANSLATE_MODE /* a special mode for translating a multi-web makefile */ enum WEAVE_MODE /* for any form of -weave */
enum TRANSLATE_MODE /* a special mode for translating a multi-web makefile */
= <<*>>=
int fundamental_mode = NO_MODE; int fundamental_mode = NO_MODE;
@ This operation will be applied to a single web, and will apply to the whole @ This operation will be applied to a single web, and will apply to the whole
of that web unless we specify otherwise. Subsets of the web are represented by of that web unless we specify otherwise. Subsets of the web are represented by
short pieces of text called "ranges". This can be a section range like short pieces of text called "ranges". This can be a section range like
|2/pine|, a chapter number like |12|, an appendix letter |A| or the [[2/pine]], a chapter number like [[12]], an appendix letter [[A]] or the
preliminaries block |P|, the special chapter |S| for the "Sections" chapter preliminaries block [[P]], the special chapter [[S]] for the "Sections" chapter
of an unchaptered web, or the special value |0| to mean the entire web (which of an unchaptered web, or the special value [[0]] to mean the entire web (which
is the default). is the default).
When weaving in "swarm mode", however, the user chooses a multiplicity of When weaving in "swarm mode", however, the user chooses a multiplicity of
@ -36,33 +37,34 @@ operations rather than just one. Now it's no longer a matter of weaving a
particular section or chapter: we can weave all of the sections or chapters, particular section or chapter: we can weave all of the sections or chapters,
one after another. one after another.
@e SWARM_OFF_SWM from 0 <<*>>=
@e SWARM_INDEX_SWM /* make index(es) as if swarming, but don't actually swarm */ enum SWARM_OFF_SWM from 0
@e SWARM_CHAPTERS_SWM /* swarm the chapters */ enum SWARM_INDEX_SWM /* make index(es) as if swarming, but don't actually swarm */
@e SWARM_SECTIONS_SWM /* swarm the individual sections */ enum SWARM_CHAPTERS_SWM /* swarm the chapters */
enum SWARM_SECTIONS_SWM /* swarm the individual sections */
@ In order to run, Inweb needs to know where it is installed -- this @ In order to run, Inweb needs to know where it is installed -- this
enables it to find its configuration file, the macros file, and so on. enables it to find its configuration file, the macros file, and so on.
Unless told otherwise on the command line, we'll assume Inweb is present Unless told otherwise on the command line, we'll assume Inweb is present
in the current working directory. The "materials" will then be in a further in the current working directory. The "materials" will then be in a further
subfolder called |Materials|. subfolder called [[Materials]].
= <<*>>=
pathname *path_to_inweb = NULL; /* where we are installed */ pathname *path_to_inweb = NULL; /* where we are installed */
pathname *path_to_inweb_materials = NULL; /* the materials pathname */ pathname *path_to_inweb_materials = NULL; /* the materials pathname */
pathname *path_to_inweb_patterns = NULL; /* where built-in patterns are stored */ pathname *path_to_inweb_patterns = NULL; /* where built-in patterns are stored */
@ We count the errors in order to be able to exit with a suitable exit code. @ We count the errors in order to be able to exit with a suitable exit code.
= <<*>>=
int no_inweb_errors = 0; int no_inweb_errors = 0;
int verbose_mode = FALSE; int verbose_mode = FALSE;
@h Main routine. @ \section{Main routine.}
= <<*>>=
int main(int argc, char **argv) { int main(int argc, char **argv) {
@<Initialise inweb@>; <<Initialise inweb>>;
inweb_instructions args = Configuration::read(argc, argv); inweb_instructions args = Configuration::read(argc, argv);
verbose_mode = args.verbose_switch; verbose_mode = args.verbose_switch;
fundamental_mode = args.inweb_mode; fundamental_mode = args.inweb_mode;
@ -76,22 +78,22 @@ int main(int argc, char **argv) {
Main::follow_instructions(&args); Main::follow_instructions(&args);
@<Shut inweb down@>; <<Shut inweb down>>;
} }
@<Initialise inweb@> = <<Initialise inweb>>=
Foundation::start(argc, argv); Foundation::start(argc, argv);
Formats::create_weave_formats(); Formats::create_weave_formats();
@<Shut inweb down@> = <<Shut inweb down>>=
Foundation::end(); Foundation::end();
return (no_inweb_errors == 0)?0:1; return (no_inweb_errors == 0)?0:1;
@h Following instructions. @ \section{Following instructions.}
This is the whole program in a nutshell, and it's a pretty old-school This is the whole program in a nutshell, and it's a pretty old-school
program: some input, some thinking, a choice of three forms of output. program: some input, some thinking, a choice of three forms of output.
= <<*>>=
void Main::follow_instructions(inweb_instructions *ins) { void Main::follow_instructions(inweb_instructions *ins) {
web *W = NULL; web *W = NULL;
if ((ins->chosen_web) || (ins->chosen_file)) { if ((ins->chosen_web) || (ins->chosen_file)) {
@ -102,16 +104,16 @@ void Main::follow_instructions(inweb_instructions *ins) {
Parser::parse_web(W, ins->inweb_mode); Parser::parse_web(W, ins->inweb_mode);
} }
if (no_inweb_errors == 0) { if (no_inweb_errors == 0) {
if (ins->inweb_mode == TRANSLATE_MODE) @<Translate a makefile@> if (ins->inweb_mode == TRANSLATE_MODE) <<Translate a makefile>>
else if (ins->show_languages_switch) @<List available programming languages@> else if (ins->show_languages_switch) <<List available programming languages>>
else if ((ins->test_language_setting) || (ins->test_language_on_setting)) @<Test a language@> else if ((ins->test_language_setting) || (ins->test_language_on_setting)) <<Test a language>>
else if (ins->inweb_mode != NO_MODE) @<Analyse, tangle or weave an existing web@>; else if (ins->inweb_mode != NO_MODE) <<Analyse, tangle or weave an existing web>>;
} }
} }
@ This is a one-off featurette: @ This is a one-off featurette:
@<Translate a makefile@> = <<Translate a makefile>>=
if ((ins->makefile_setting) && (ins->prototype_setting == NULL)) if ((ins->makefile_setting) && (ins->prototype_setting == NULL))
ins->prototype_setting = Filenames::from_text(I"script.mkscript"); ins->prototype_setting = Filenames::from_text(I"script.mkscript");
if ((ins->gitignore_setting) && (ins->prototype_setting == NULL)) if ((ins->gitignore_setting) && (ins->prototype_setting == NULL))
@ -130,13 +132,13 @@ void Main::follow_instructions(inweb_instructions *ins) {
@ As is this: @ As is this:
@<List available programming languages@> = <<List available programming languages>>=
Languages::read_definitions(NULL); Languages::read_definitions(NULL);
Languages::show(STDOUT); Languages::show(STDOUT);
@ And this: @ And this:
@<Test a language@> = <<Test a language>>=
if ((ins->test_language_setting) && (ins->test_language_on_setting)) { if ((ins->test_language_setting) && (ins->test_language_on_setting)) {
TEMPORARY_TEXT(matter) TEMPORARY_TEXT(matter)
TEMPORARY_TEXT(coloured) TEMPORARY_TEXT(coloured)
@ -152,15 +154,15 @@ void Main::follow_instructions(inweb_instructions *ins) {
@ But otherwise we do something with the given web: @ But otherwise we do something with the given web:
@<Analyse, tangle or weave an existing web@> = <<Analyse, tangle or weave an existing web>>=
if (ins->inweb_mode != ANALYSE_MODE) Reader::print_web_statistics(W); if (ins->inweb_mode != ANALYSE_MODE) Reader::print_web_statistics(W);
if (ins->inweb_mode == ANALYSE_MODE) @<Analyse the web@>; if (ins->inweb_mode == ANALYSE_MODE) <<Analyse the web>>;
if (ins->inweb_mode == TANGLE_MODE) @<Tangle the web@>; if (ins->inweb_mode == TANGLE_MODE) <<Tangle the web>>;
if (ins->inweb_mode == WEAVE_MODE) @<Weave the web@>; if (ins->inweb_mode == WEAVE_MODE) <<Weave the web>>;
@ "Analysis" invokes any combination of the following diagnostic tools: @ "Analysis" invokes any combination of the following diagnostic tools:
@<Analyse the web@> = <<Analyse the web>>=
if (ins->swarm_mode != SWARM_OFF_SWM) if (ins->swarm_mode != SWARM_OFF_SWM)
Errors::fatal("only specific parts of the web can be analysed"); Errors::fatal("only specific parts of the web can be analysed");
if (ins->catalogue_switch) if (ins->catalogue_switch)
@ -189,17 +191,17 @@ quite different language from the rest of the web, and tangles to a different
output, but needs to be part of the web since it's essential to an understanding output, but needs to be part of the web since it's essential to an understanding
of the whole system. of the whole system.
In this section we determine |tn|, the target number wanted, and |tangle_to|, In this section we determine [[tn]], the target number wanted, and [[tangle_to]],
the filename of the tangled code to write. This may have been set at the command the filename of the tangled code to write. This may have been set at the command
line , but otherwise we impose a sensible choice based on the target. line , but otherwise we impose a sensible choice based on the target.
@<Tangle the web@> = <<Tangle the web>>=
TEMPORARY_TEXT(tangle_leaf) TEMPORARY_TEXT(tangle_leaf)
tangle_target *tn = NULL; tangle_target *tn = NULL;
if (Str::eq_wide_string(ins->chosen_range, L"0")) { if (Str::eq_wide_string(ins->chosen_range, L"0")) {
@<Work out main tangle destination@>; <<Work out main tangle destination>>;
} else if (Reader::get_section_for_range(W, ins->chosen_range)) { } else if (Reader::get_section_for_range(W, ins->chosen_range)) {
@<Work out an independent tangle destination, from one section of the web@>; <<Work out an independent tangle destination, from one section of the web>>;
} }
if (Str::len(tangle_leaf) == 0) { Errors::fatal("no tangle destination known"); } if (Str::len(tangle_leaf) == 0) { Errors::fatal("no tangle destination known"); }
@ -217,7 +219,7 @@ line , but otherwise we impose a sensible choice based on the target.
@ Here the target number is 0, and the tangle is of the main part of the web, @ Here the target number is 0, and the tangle is of the main part of the web,
which for many small webs will be the entire thing. which for many small webs will be the entire thing.
@<Work out main tangle destination@> = <<Work out main tangle destination>>=
tn = NULL; tn = NULL;
if (Bibliographic::data_exists(W->md, I"Short Title")) if (Bibliographic::data_exists(W->md, I"Short Title"))
Str::copy(tangle_leaf, Bibliographic::get_datum(W->md, I"Short Title")); Str::copy(tangle_leaf, Bibliographic::get_datum(W->md, I"Short Title"));
@ -225,9 +227,9 @@ which for many small webs will be the entire thing.
Str::copy(tangle_leaf, Bibliographic::get_datum(W->md, I"Title")); Str::copy(tangle_leaf, Bibliographic::get_datum(W->md, I"Title"));
Str::concatenate(tangle_leaf, W->main_language->file_extension); Str::concatenate(tangle_leaf, W->main_language->file_extension);
@ If someone tangles, say, |2/eg| then the default filename is "Example Section". @ If someone tangles, say, [[2/eg]] then the default filename is "Example Section".
@<Work out an independent tangle destination, from one section of the web@> = <<Work out an independent tangle destination, from one section of the web>>=
section *S = Reader::get_section_for_range(W, ins->chosen_range); section *S = Reader::get_section_for_range(W, ins->chosen_range);
tn = S->sect_target; tn = S->sect_target;
if (tn == NULL) Errors::fatal("section cannot be independently tangled"); if (tn == NULL) Errors::fatal("section cannot be independently tangled");
@ -235,7 +237,7 @@ which for many small webs will be the entire thing.
@ Weaving is not actually easier, it's just more thoroughly delegated: @ Weaving is not actually easier, it's just more thoroughly delegated:
@<Weave the web@> = <<Weave the web>>=
Numbering::number_web(W); Numbering::number_web(W);
theme_tag *tag = Tags::find_by_name(ins->tag_setting, FALSE); theme_tag *tag = Tags::find_by_name(ins->tag_setting, FALSE);
@ -248,7 +250,7 @@ which for many small webs will be the entire thing.
int r = Formats::begin_weaving(W, pattern); int r = Formats::begin_weaving(W, pattern);
if (r != SWARM_OFF_SWM) ins->swarm_mode = r; if (r != SWARM_OFF_SWM) ins->swarm_mode = r;
@<Assign section numbers for printing purposes@>; <<Assign section numbers for printing purposes>>;
if (ins->swarm_mode == SWARM_OFF_SWM) { if (ins->swarm_mode == SWARM_OFF_SWM) {
Swarm::weave_subset(W, ins->chosen_range, FALSE, tag, pattern, Swarm::weave_subset(W, ins->chosen_range, FALSE, tag, pattern,
ins->weave_to_setting, ins->weave_into_setting, ins->weave_to_setting, ins->weave_into_setting,
@ -260,18 +262,18 @@ which for many small webs will be the entire thing.
} }
Formats::end_weaving(W, pattern); Formats::end_weaving(W, pattern);
@<Assign section numbers for printing purposes@> = <<Assign section numbers for printing purposes>>=
section *S; int k = 1; section *S; int k = 1;
LOOP_OVER(S, section) LOOP_OVER(S, section)
if (Reader::range_within(S->md->sect_range, ins->chosen_range)) if (Reader::range_within(S->md->sect_range, ins->chosen_range))
S->printed_number = k++; S->printed_number = k++;
@h Error messages. @ \section{Error messages.}
The Foundation module provides convenient functions to issue error messages, The Foundation module provides convenient functions to issue error messages,
but we'll use the following wrapper when issuing an error at a line of web but we'll use the following wrapper when issuing an error at a line of web
source: source:
= <<*>>=
void Main::error_in_web(text_stream *message, source_line *sl) { void Main::error_in_web(text_stream *message, source_line *sl) {
if (sl) { if (sl) {
Errors::in_text_file_S(message, &(sl->source)); Errors::in_text_file_S(message, &(sl->source));

View file

@ -3,7 +3,7 @@
To feed multiple output requests to the weaver, and to present To feed multiple output requests to the weaver, and to present
weaver results, and update indexes or contents pages. weaver results, and update indexes or contents pages.
@h Swarming. @ \section{Swarming.}
A "weave" occurs when Inweb takes a portion of the web -- one section, one A "weave" occurs when Inweb takes a portion of the web -- one section, one
chapter, or the whole thing -- and writes it out in a human-readable form (or chapter, or the whole thing -- and writes it out in a human-readable form (or
in some intermediate state which can be made into one, like a TeX file). in some intermediate state which can be made into one, like a TeX file).
@ -11,10 +11,10 @@ There can be many weaves in a single run of Inweb, in which case we call the
resulting flurry a "swarm", like the glittering cloud of locusts in the title resulting flurry a "swarm", like the glittering cloud of locusts in the title
of Chapter 25 of "On the Banks of Plum Creek". of Chapter 25 of "On the Banks of Plum Creek".
This routine is called with mode |SWARM_SECTIONS_SWM|, |SWARM_CHAPTERS_SWM| or This routine is called with mode [[SWARM_SECTIONS_SWM]], [[SWARM_CHAPTERS_SWM]] or
|SWARM_INDEX_SWM|, so in a non-swarming run it isn't called at all. [[SWARM_INDEX_SWM]], so in a non-swarming run it isn't called at all.
= <<*>>=
weave_order *swarm_leader = NULL; /* the most inclusive one we weave */ weave_order *swarm_leader = NULL; /* the most inclusive one we weave */
void Swarm::weave(web *W, text_stream *range, int swarm_mode, theme_tag *tag, void Swarm::weave(web *W, text_stream *range, int swarm_mode, theme_tag *tag,
@ -47,26 +47,26 @@ void Swarm::weave(web *W, text_stream *range, int swarm_mode, theme_tag *tag,
from the swarm, or has been specified at the command line (in which case from the swarm, or has been specified at the command line (in which case
the call comes from Program Control). the call comes from Program Control).
= <<*>>=
weave_order *Swarm::weave_subset(web *W, text_stream *range, int open_afterwards, weave_order *Swarm::weave_subset(web *W, text_stream *range, int open_afterwards,
theme_tag *tag, weave_pattern *pattern, filename *to, pathname *into, theme_tag *tag, weave_pattern *pattern, filename *to, pathname *into,
linked_list *breadcrumbs, filename *navigation) { linked_list *breadcrumbs, filename *navigation) {
weave_order *wv = NULL; weave_order *wv = NULL;
if (no_inweb_errors == 0) { if (no_inweb_errors == 0) {
Analyser::analyse_code(W); Analyser::analyse_code(W);
@<Compile a set of instructions for the weaver@>; <<Compile a set of instructions for the weaver>>;
if (Weaver::weave(wv) == 0) /* i.e., the number of lines woven was zero */ if (Weaver::weave(wv) == 0) /* i.e., the number of lines woven was zero */
Errors::fatal("empty weave request"); Errors::fatal("empty weave request");
Patterns::post_process(wv->pattern, wv); Patterns::post_process(wv->pattern, wv);
Formats::post_process_weave(wv, open_afterwards); Formats::post_process_weave(wv, open_afterwards);
@<Report on the outcome of the weave to the console@>; <<Report on the outcome of the weave to the console>>;
} }
return wv; return wv;
} }
@ Each individual weave generates one of the following sets of instructions: @ Each individual weave generates one of the following sets of instructions:
= <<*>>=
typedef struct weave_order { typedef struct weave_order {
struct web *weave_web; /* which web we weave */ struct web *weave_web; /* which web we weave */
struct text_stream *weave_range; /* which parts of the web in this weave */ struct text_stream *weave_range; /* which parts of the web in this weave */
@ -78,16 +78,16 @@ typedef struct weave_order {
void *post_processing_results; /* optional typesetting diagnostics after running through */ void *post_processing_results; /* optional typesetting diagnostics after running through */
int self_contained; /* make a self-contained file if possible */ int self_contained; /* make a self-contained file if possible */
struct linked_list *breadcrumbs; /* non-standard breadcrumb trail, if any */ struct linked_list *breadcrumbs; /* non-standard breadcrumb trail, if any */
struct filename *navigation; /* navigation links, or |NULL| if not supplied */ struct filename *navigation; /* navigation links, or [[NULL]] if not supplied */
struct linked_list *plugins; /* of |weave_plugin|: these are for HTML extensions */ struct linked_list *plugins; /* of [[weave_plugin]]: these are for HTML extensions */
struct linked_list *colour_schemes; /* of |colour_scheme|: these are for HTML */ struct linked_list *colour_schemes; /* of [[colour_scheme]]: these are for HTML */
/* used for workspace during an actual weave: */ /* used for workspace during an actual weave: */
struct source_line *current_weave_line; struct source_line *current_weave_line;
CLASS_DEFINITION CLASS_DEFINITION
} weave_order; } weave_order;
@<Compile a set of instructions for the weaver@> = <<Compile a set of instructions for the weaver>>=
wv = CREATE(weave_order); wv = CREATE(weave_order);
wv->weave_web = W; wv->weave_web = W;
wv->weave_range = Str::duplicate(range); wv->weave_range = Str::duplicate(range);
@ -116,7 +116,7 @@ typedef struct weave_order {
Errors::fatal("no sections match that range"); Errors::fatal("no sections match that range");
TEMPORARY_TEXT(leafname) TEMPORARY_TEXT(leafname)
@<Translate the subweb range into details of what to weave@>; <<Translate the subweb range into details of what to weave>>;
pathname *H = W->redirect_weaves_to; pathname *H = W->redirect_weaves_to;
if (H == NULL) H = into; if (H == NULL) H = into;
if (H == NULL) { if (H == NULL) {
@ -136,7 +136,7 @@ typedef struct weave_order {
@ From the range and the theme, we work out the weave title, the leafname, @ From the range and the theme, we work out the weave title, the leafname,
and details of any cover-sheet to use. and details of any cover-sheet to use.
@<Translate the subweb range into details of what to weave@> = <<Translate the subweb range into details of what to weave>>=
match_results mr = Regexp::create_mr(); match_results mr = Regexp::create_mr();
if (Str::eq_wide_string(range, L"0")) { if (Str::eq_wide_string(range, L"0")) {
if (W->md->single_file) { if (W->md->single_file) {
@ -146,7 +146,7 @@ and details of any cover-sheet to use.
wv->booklet_title = Str::new_from_wide_string(L"Complete Program"); wv->booklet_title = Str::new_from_wide_string(L"Complete Program");
WRITE_TO(leafname, "Complete"); WRITE_TO(leafname, "Complete");
} }
if (wv->theme_match) @<Change the titling and leafname to match the tagged theme@>; if (wv->theme_match) <<Change the titling and leafname to match the tagged theme>>;
} else if (Regexp::match(&mr, range, L"%d+")) { } else if (Regexp::match(&mr, range, L"%d+")) {
Str::clear(wv->booklet_title); Str::clear(wv->booklet_title);
WRITE_TO(wv->booklet_title, "Chapter %S", range); WRITE_TO(wv->booklet_title, "Chapter %S", range);
@ -174,19 +174,19 @@ and details of any cover-sheet to use.
WRITE_TO(leafname, "%S", Formats::file_extension(wv->format)); WRITE_TO(leafname, "%S", Formats::file_extension(wv->format));
Regexp::dispose_of(&mr); Regexp::dispose_of(&mr);
@<Change the titling and leafname to match the tagged theme@> = <<Change the titling and leafname to match the tagged theme>>=
Str::clear(wv->booklet_title); Str::clear(wv->booklet_title);
WRITE_TO(wv->booklet_title, "Extracts: %S", wv->theme_match->tag_name); WRITE_TO(wv->booklet_title, "Extracts: %S", wv->theme_match->tag_name);
Str::copy(leafname, wv->theme_match->tag_name); Str::copy(leafname, wv->theme_match->tag_name);
@ Each weave results in a compressed one-line printed report: @ Each weave results in a compressed one-line printed report:
@<Report on the outcome of the weave to the console@> = <<Report on the outcome of the weave to the console>>=
PRINT("[%S: %S -> %f", wv->booklet_title, wv->format->format_name, wv->weave_to); PRINT("[%S: %S -> %f", wv->booklet_title, wv->format->format_name, wv->weave_to);
Formats::report_on_post_processing(wv); Formats::report_on_post_processing(wv);
PRINT("]\n"); PRINT("]\n");
@ = <<*>>=
void Swarm::ensure_plugin(weave_order *wv, text_stream *name) { void Swarm::ensure_plugin(weave_order *wv, text_stream *name) {
weave_plugin *existing; weave_plugin *existing;
LOOP_OVER_LINKED_LIST(existing, weave_plugin, wv->plugins) LOOP_OVER_LINKED_LIST(existing, weave_plugin, wv->plugins)
@ -227,7 +227,7 @@ void Swarm::include_plugins(OUTPUT_STREAM, web *W, weave_order *wv, filename *fr
@ After every swarm, we rebuild the index: @ After every swarm, we rebuild the index:
= <<*>>=
void Swarm::weave_index_templates(web *W, text_stream *range, weave_pattern *pattern, void Swarm::weave_index_templates(web *W, text_stream *range, weave_pattern *pattern,
pathname *into, filename *nav, linked_list *crumbs) { pathname *into, filename *nav, linked_list *crumbs) {
if (!(Bibliographic::data_exists(W->md, I"Version Number"))) if (!(Bibliographic::data_exists(W->md, I"Version Number")))