Chapter 6: Nowebify.

This commit is contained in:
AwesomeAdam54321 2024-03-09 21:23:53 +08:00
parent a733b321a5
commit d167c16357
5 changed files with 165 additions and 164 deletions

View file

@ -2,7 +2,7 @@
Cross-referencing multiple webs gathered together. Cross-referencing multiple webs gathered together.
@h Colonies of webs. @ \section{Colonies of webs.}
Social spiders are said to form "colonies" when their webs are shared,[1] and Social spiders are said to form "colonies" when their webs are shared,[1] and
in that spirit, a colony to Inweb is a collection of coexisting webs -- which in that spirit, a colony to Inweb is a collection of coexisting webs -- which
share no code, and in that sense have no connection at run-time, but which share no code, and in that sense have no connection at run-time, but which
@ -17,49 +17,50 @@ Orb-Weaving Spiders With Communal Webbing in a Man-Made Structural Habitat
@ So, then, a colony is really just a membership list: @ So, then, a colony is really just a membership list:
= <<*>>=
typedef struct colony { typedef struct colony {
struct linked_list *members; /* of |colony_member| */ struct linked_list *members; /* of [[colony_member]] */
struct text_stream *home; /* path of home repository */ struct text_stream *home; /* path of home repository */
struct pathname *assets_path; /* where assets shared between weaves live */ struct pathname *assets_path; /* where assets shared between weaves live */
struct pathname *patterns_path; /* where additional patterns live */ struct pathname *patterns_path; /* where additional patterns live */
CLASS_DEFINITION CLASS_DEFINITION
} colony; } colony;
@ Each member is represented by an instance of the following. Note the |loaded| @ Each member is represented by an instance of the following. Note the [[loaded]]
field: this holds metadata on the web/module in question. (Recall that a module field: this holds metadata on the web/module in question. (Recall that a module
is really just a web that doesn't tangle to an independent program but to a is really just a web that doesn't tangle to an independent program but to a
library of code: for almost all purposes, it's a web.) But for efficiency's library of code: for almost all purposes, it's a web.) But for efficiency's
sake, we read this metadata only on demand. sake, we read this metadata only on demand.
Note that the |path| might be either the name of a single-file web, or of a Note that the [[path]] might be either the name of a single-file web, or of a
directory holding a multi-section web. directory holding a multi-section web.
= <<*>>=
typedef struct colony_member { typedef struct colony_member {
int web_rather_than_module; /* |TRUE| for a web, |FALSE| for a module */ int web_rather_than_module; /* [[TRUE| for a web, |FALSE]] for a module */
struct text_stream *name; /* the |N| in |N at P in W| */ struct text_stream *name; /* the [[N| in |N at P in W]] */
struct text_stream *path; /* the |P| in |N at P in W| */ struct text_stream *path; /* the [[P| in |N at P in W]] */
struct pathname *weave_path; /* the |W| in |N at P in W| */ struct pathname *weave_path; /* the [[W| in |N at P in W]] */
struct text_stream *home_leaf; /* usually |index.html|, but not for single-file webs */ struct text_stream *home_leaf; /* usually [[index.html]], but not for single-file webs */
struct text_stream *default_weave_pattern; /* for use when weaving */ struct text_stream *default_weave_pattern; /* for use when weaving */
struct web_md *loaded; /* metadata on its sections, lazily evaluated */ struct web_md *loaded; /* metadata on its sections, lazily evaluated */
struct filename *navigation; /* navigation sidebar HTML */ struct filename *navigation; /* navigation sidebar HTML */
struct linked_list *breadcrumb_tail; /* of |breadcrumb_request| */ struct linked_list *breadcrumb_tail; /* of [[breadcrumb_request]] */
CLASS_DEFINITION CLASS_DEFINITION
} colony_member; } colony_member;
@ And the following reads a colony file |F| and produces a suitable |colony| @ And the following reads a colony file [[F]] and produces a suitable [[colony]]
object from it. This, for example, is the colony file for the Inweb repository object from it. This, for example, is the colony file for the Inweb repository
at GitHub: at GitHub:
= (text from Figures/colony.txt)
= (text from Figures/colony.txt)
<<*>>=
typedef struct colony_reader_state { typedef struct colony_reader_state {
struct colony *province; struct colony *province;
struct filename *nav; struct filename *nav;
struct linked_list *crumbs; /* of |breadcrumb_request| */ struct linked_list *crumbs; /* of [[breadcrumb_request]] */
struct text_stream *pattern; struct text_stream *pattern;
} colony_reader_state; } colony_reader_state;
@ -80,14 +81,14 @@ void Colonies::load(filename *F) {
@ Lines from the colony file are fed, one by one, into: @ Lines from the colony file are fed, one by one, into:
= <<*>>=
void Colonies::read_line(text_stream *line, text_file_position *tfp, void *v_crs) { void Colonies::read_line(text_stream *line, text_file_position *tfp, void *v_crs) {
colony_reader_state *crs = (colony_reader_state *) v_crs; colony_reader_state *crs = (colony_reader_state *) v_crs;
colony *C = crs->province; colony *C = crs->province;
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 */
match_results mr = Regexp::create_mr(); match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, line, L"(%c*?): \"*(%C+)\" at \"(%c*)\" in \"(%c*)\"")) { if (Regexp::match(&mr, line, L"(%c*?): \"*(%C+)\" at \"(%c*)\" in \"(%c*)\"")) {
@ -147,7 +148,7 @@ void Colonies::read_line(text_stream *line, text_file_position *tfp, void *v_crs
@ "Breadcrumbs" are the chain of links in a horizontal list at the top of @ "Breadcrumbs" are the chain of links in a horizontal list at the top of
the page, and this requests one. the page, and this requests one.
= <<*>>=
void Colonies::add_crumb(linked_list *L, text_stream *spec, text_file_position *tfp) { void Colonies::add_crumb(linked_list *L, text_stream *spec, text_file_position *tfp) {
match_results mr = Regexp::create_mr(); match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, spec, L"\"(%c*?)\"") == FALSE) { if (Regexp::match(&mr, spec, L"\"(%c*?)\"") == FALSE) {
@ -207,11 +208,11 @@ void Colonies::write_breadcrumb(OUTPUT_STREAM, text_stream *text, text_stream *l
} }
} }
@h Searching. @ \section{Searching.}
Given a name |T|, we try to find a colony member of that name, returning the Given a name [[T]], we try to find a colony member of that name, returning the
first we find. first we find.
= <<*>>=
colony_member *Colonies::find(text_stream *T) { colony_member *Colonies::find(text_stream *T) {
colony *C; colony *C;
LOOP_OVER(C, colony) { LOOP_OVER(C, colony) {
@ -229,20 +230,20 @@ already in Inweb's memory (because it is the web being woven, or is a module
imported by that web even if not now being woven). If it is, we want to use imported by that web even if not now being woven). If it is, we want to use
the data we already have; but if not, we read it in. the data we already have; but if not, we read it in.
= <<*>>=
module *Colonies::as_module(colony_member *CM, source_line *L, web_md *Wm) { module *Colonies::as_module(colony_member *CM, source_line *L, web_md *Wm) {
if (CM->loaded == NULL) @<Perhaps the web being woven@>; if (CM->loaded == NULL) <<Perhaps the web being woven>>;
if (CM->loaded == NULL) @<Perhaps a module imported by the web being woven@>; if (CM->loaded == NULL) <<Perhaps a module imported by the web being woven>>;
if (CM->loaded == NULL) @<Perhaps a module not yet seen@>; if (CM->loaded == NULL) <<Perhaps a module not yet seen>>;
if (CM->loaded == NULL) @<Failing that, throw an error@>; if (CM->loaded == NULL) <<Failing that, throw an error>>;
return CM->loaded->as_module; return CM->loaded->as_module;
} }
@<Perhaps the web being woven@> = <<Perhaps the web being woven>>=
if ((Wm) && (Str::eq_insensitive(Wm->as_module->module_name, CM->name))) if ((Wm) && (Str::eq_insensitive(Wm->as_module->module_name, CM->name)))
CM->loaded = Wm; CM->loaded = Wm;
@<Perhaps a module imported by the web being woven@> = <<Perhaps a module imported by the web being woven>>=
if (Wm) { if (Wm) {
module *M; module *M;
LOOP_OVER_LINKED_LIST(M, module, Wm->as_module->dependencies) LOOP_OVER_LINKED_LIST(M, module, Wm->as_module->dependencies)
@ -250,7 +251,7 @@ module *Colonies::as_module(colony_member *CM, source_line *L, web_md *Wm) {
CM->loaded = Wm; CM->loaded = Wm;
} }
@<Perhaps a module not yet seen@> = <<Perhaps a module not yet seen>>=
filename *F = NULL; filename *F = NULL;
pathname *P = NULL; pathname *P = NULL;
if (Str::suffix_eq(CM->path, I".inweb", 6)) if (Str::suffix_eq(CM->path, I".inweb", 6))
@ -259,14 +260,14 @@ module *Colonies::as_module(colony_member *CM, source_line *L, web_md *Wm) {
P = Pathnames::from_text(CM->path); P = Pathnames::from_text(CM->path);
CM->loaded = WebMetadata::get_without_modules(P, F); CM->loaded = WebMetadata::get_without_modules(P, F);
@<Failing that, throw an error@> = <<Failing that, throw an error>>=
TEMPORARY_TEXT(err) TEMPORARY_TEXT(err)
WRITE_TO(err, "unable to load '%S'", CM->name); WRITE_TO(err, "unable to load '%S'", CM->name);
Main::error_in_web(err, L); Main::error_in_web(err, L);
@ Finally: @ Finally:
= <<*>>=
text_stream *Colonies::home(void) { text_stream *Colonies::home(void) {
colony *C; colony *C;
LOOP_OVER(C, colony) LOOP_OVER(C, colony)
@ -288,9 +289,9 @@ pathname *Colonies::patterns_path(void) {
return NULL; return NULL;
} }
@h Cross-references. @ \section{Cross-references.}
The following must decide what references like the following should refer to: The following must decide what references like the following should refer to:
= (text)
Chapter 3 Chapter 3
Manual Manual
Enumerated Constants Enumerated Constants
@ -298,15 +299,15 @@ The following must decide what references like the following should refer to:
weave_order weave_order
foundation: Text Streams foundation: Text Streams
goldbach goldbach
=
The reference text is in |text|; we return |TRUE| if we can make unambiguous The reference text is in [[text]]; we return [[TRUE]] if we can make unambiguous
sense of it, or throw an error and return |FALSE| if not. If all is well, we sense of it, or throw an error and return [[FALSE]] if not. If all is well, we
must write a title and URL for the link. must write a title and URL for the link.
The web metadata |Wm| is for the web currently being woven, and the line |L| The web metadata [[Wm]] is for the web currently being woven, and the line [[L]]
is where the reference is made from. is where the reference is made from.
= <<*>>=
int Colonies::resolve_reference_in_weave(text_stream *url, text_stream *title, int Colonies::resolve_reference_in_weave(text_stream *url, text_stream *title,
filename *for_HTML_file, text_stream *text, web_md *Wm, source_line *L, int *ext) { filename *for_HTML_file, text_stream *text, web_md *Wm, source_line *L, int *ext) {
int r = 0; int r = 0;
@ -331,9 +332,9 @@ int Colonies::resolve_reference_in_weave_inner(text_stream *url, text_stream *ti
colony_member *search_CM = NULL; colony_member *search_CM = NULL;
int external = FALSE; int external = FALSE;
@<Is it an explicit URL?@>; <<Is it an explicit URL?>>;
@<Is it the name of a member of our colony?@>; <<Is it the name of a member of our colony?>>;
@<If it contains a colon, does this indicate a section in a colony member?@>; <<If it contains a colon, does this indicate a section in a colony member?>>;
module *found_M = NULL; module *found_M = NULL;
section_md *found_Sm = NULL; section_md *found_Sm = NULL;
@ -355,8 +356,8 @@ int Colonies::resolve_reference_in_weave_inner(text_stream *url, text_stream *ti
if (N == 0) { if (N == 0) {
if ((L) && (external == FALSE)) { if ((L) && (external == FALSE)) {
@<Is it the name of a function in the current web?@>; <<Is it the name of a function in the current web?>>;
@<Is it the name of a type in the current web?@>; <<Is it the name of a type in the current web?>>;
} }
TEMPORARY_TEXT(err) TEMPORARY_TEXT(err)
WRITE_TO(err, "Can't find the cross-reference '%S'", text); WRITE_TO(err, "Can't find the cross-reference '%S'", text);
@ -370,11 +371,11 @@ int Colonies::resolve_reference_in_weave_inner(text_stream *url, text_stream *ti
title, search_M, text, TRUE, FALSE); title, search_M, text, TRUE, FALSE);
return FALSE; return FALSE;
} }
@<It refers unambiguously to a single section@>; <<It refers unambiguously to a single section>>;
return TRUE; return TRUE;
} }
@<Is it an explicit URL?@> = <<Is it an explicit URL?>>=
match_results mr = Regexp::create_mr(); match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, text, L"https*://%c*")) { if (Regexp::match(&mr, text, L"https*://%c*")) {
WRITE_TO(url, "%S", text); WRITE_TO(url, "%S", text);
@ -385,17 +386,17 @@ int Colonies::resolve_reference_in_weave_inner(text_stream *url, text_stream *ti
} }
Regexp::dispose_of(&mr); Regexp::dispose_of(&mr);
@<Is it the name of a member of our colony?@> = <<Is it the name of a member of our colony?>>=
search_CM = Colonies::find(text); search_CM = Colonies::find(text);
if (search_CM) { if (search_CM) {
module *found_M = Colonies::as_module(search_CM, L, Wm); module *found_M = Colonies::as_module(search_CM, L, Wm);
section_md *found_Sm = FIRST_IN_LINKED_LIST(section_md, found_M->sections_md); section_md *found_Sm = FIRST_IN_LINKED_LIST(section_md, found_M->sections_md);
int bare_module_name = TRUE; int bare_module_name = TRUE;
WRITE_TO(title, "%S", search_CM->name); WRITE_TO(title, "%S", search_CM->name);
@<It refers unambiguously to a single section@>; <<It refers unambiguously to a single section>>;
} }
@<If it contains a colon, does this indicate a section in a colony member?@> = <<If it contains a colon, does this indicate a section in a colony member?>>=
match_results mr = Regexp::create_mr(); match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, text, L"(%c*?): (%c*)")) { if (Regexp::match(&mr, text, L"(%c*?): (%c*)")) {
search_CM = Colonies::find(mr.exp[0]); search_CM = Colonies::find(mr.exp[0]);
@ -411,7 +412,7 @@ int Colonies::resolve_reference_in_weave_inner(text_stream *url, text_stream *ti
} }
Regexp::dispose_of(&mr); Regexp::dispose_of(&mr);
@<Is it the name of a function in the current web?@> = <<Is it the name of a function in the current web?>>=
language_function *fn; language_function *fn;
LOOP_OVER(fn, language_function) { LOOP_OVER(fn, language_function) {
if (Str::eq_insensitive(fn->function_name, text)) { if (Str::eq_insensitive(fn->function_name, text)) {
@ -422,7 +423,7 @@ int Colonies::resolve_reference_in_weave_inner(text_stream *url, text_stream *ti
} }
} }
@<Is it the name of a type in the current web?@> = <<Is it the name of a type in the current web?>>=
language_type *str; language_type *str;
LOOP_OVER(str, language_type) { LOOP_OVER(str, language_type) {
if (Str::eq_insensitive(str->structure_name, text)) { if (Str::eq_insensitive(str->structure_name, text)) {
@ -433,13 +434,13 @@ int Colonies::resolve_reference_in_weave_inner(text_stream *url, text_stream *ti
} }
} }
@<It refers unambiguously to a single section@> = <<It refers unambiguously to a single section>>=
if (found_M == NULL) internal_error("could not locate M"); if (found_M == NULL) internal_error("could not locate M");
if (search_CM) @<The section is a known colony member@> if (search_CM) <<The section is a known colony member>>
else @<The section is not in a known colony member@>; else <<The section is not in a known colony member>>;
return TRUE; return TRUE;
@<The section is a known colony member@> = <<The section is a known colony member>>=
pathname *from = Filenames::up(for_HTML_file); pathname *from = Filenames::up(for_HTML_file);
pathname *to = search_CM->weave_path; pathname *to = search_CM->weave_path;
Pathnames::relative_URL(url, from, to); Pathnames::relative_URL(url, from, to);
@ -452,7 +453,7 @@ int Colonies::resolve_reference_in_weave_inner(text_stream *url, text_stream *ti
guess it makes is that modules of the current web will be woven alongside guess it makes is that modules of the current web will be woven alongside
the main one. the main one.
@<The section is not in a known colony member@> = <<The section is not in a known colony member>>=
if (found_M == from_M) { if (found_M == from_M) {
Colonies::section_URL(url, found_Sm); Colonies::section_URL(url, found_Sm);
} else { } else {
@ -462,9 +463,9 @@ the main one.
WRITE_TO(title, " (in %S)", found_M->module_name); WRITE_TO(title, " (in %S)", found_M->module_name);
} }
@h URL management. @ \section{URL management.}
= <<*>>=
void Colonies::link_URL(OUTPUT_STREAM, text_stream *link_text, filename *F) { void Colonies::link_URL(OUTPUT_STREAM, text_stream *link_text, filename *F) {
match_results mr = Regexp::create_mr(); match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, link_text, L" *//(%c+)// *")) if (Regexp::match(&mr, link_text, L" *//(%c+)// *"))

View file

@ -10,15 +10,15 @@ editing the sections in the web.
A ctags file is essentially just a list of identifiers called "tagnames", A ctags file is essentially just a list of identifiers called "tagnames",
which are usually names of functions or data types, along with details of which are usually names of functions or data types, along with details of
where they are defined in a program. Ctags files are almost never written by where they are defined in a program. Ctags files are almost never written by
hand, but are instead generated by a tool such as the eponymous |ctags|. Here, hand, but are instead generated by a tool such as the eponymous [[ctags]]. Here,
though, Inweb is going to do the generation, because it can make sense of the though, Inweb is going to do the generation, because it can make sense of the
web structure of source code in a way which the |ctags| parser cannot. web structure of source code in a way which the [[ctags]] parser cannot.
The original |ctags| dates to 1992, and was devised by Ken Arnold. This was The original [[ctags]] dates to 1992, and was devised by Ken Arnold. This was
much extended as Exuberant Ctags, by Darren Hiebert, which was then forked and much extended as Exuberant Ctags, by Darren Hiebert, which was then forked and
re-maintained as Universal Ctags by Reza Jelveh and others. The result is re-maintained as Universal Ctags by Reza Jelveh and others. The result is
nearly standard now, though as with a lot of early Unix infrastructure (compare nearly standard now, though as with a lot of early Unix infrastructure (compare
|make|, for example), that standard design feels very antique: white space is [[make]], for example), that standard design feels very antique: white space is
significant, filename extensions are not standard practice, and so on. See significant, filename extensions are not standard practice, and so on. See
//Universal Ctags -> https://ctags.io// for more.[1] //Universal Ctags -> https://ctags.io// for more.[1]
@ -28,10 +28,10 @@ but it omits details of, e.g., exactly what characters must be escaped and how;
what characters can legally be part of a tagname; and so on. what characters can legally be part of a tagname; and so on.
@ As mentioned above, Ctags go back to an age before filenames necessarily had @ As mentioned above, Ctags go back to an age before filenames necessarily had
extensions, and just as the defaukt make file is |makefile| and not |makefile.mk|, extensions, and just as the defaukt make file is [[makefile]] and not [[makefile.mk]],
so the default Ctags file is called |tags| and not |tags.ctag|. so the default Ctags file is called [[tags]] and not [[tags.ctag]].
= <<*>>=
void Ctags::write(web *W, filename *F) { void Ctags::write(web *W, filename *F) {
text_stream ctags_file; text_stream ctags_file;
pathname *P = NULL; pathname *P = NULL;
@ -44,41 +44,41 @@ void Ctags::write(web *W, filename *F) {
text_stream *OUT = &ctags_file; text_stream *OUT = &ctags_file;
if (STREAM_OPEN_TO_FILE(OUT, F, UTF8_ENC) == FALSE) if (STREAM_OPEN_TO_FILE(OUT, F, UTF8_ENC) == FALSE)
Errors::fatal_with_file("unable to write ctags file", F); Errors::fatal_with_file("unable to write ctags file", F);
@<Write header@>; <<Write header>>;
@<List defined constants@>; <<List defined constants>>;
@<List structures@>; <<List structures>>;
@<List functions@>; <<List functions>>;
STREAM_CLOSE(OUT); STREAM_CLOSE(OUT);
} }
@ Unless you really want to monkey with identifiers or filenames containing @ Unless you really want to monkey with identifiers or filenames containing
line break characters or tabs, a ctags file has a simple format to read or line break characters or tabs, a ctags file has a simple format to read or
write: there's one tag on each line, and each line has three or more fields write: there's one tag on each line, and each line has three or more fields
divided by tab characters. If we write | -> | for a tab, a line looks like: divided by tab characters. If we write [[ -> ]] for a tab, a line looks like:
= (text)
tagname -> filename -> /find/;" -> more tagname -> filename -> /find/;" -> more
=
The stranded double-quote there is not a misprint. For example: The stranded double-quote there is not a misprint. For example:
= (text)
Frogs::spawn -> pond/Chapter 1/Amphibians.w -> /^void Frogs::spawn(species *S) {$/;" -> f Frogs::spawn -> pond/Chapter 1/Amphibians.w -> /^void Frogs::spawn(species *S) {$/;" -> f
=
Here the tagname is |Frogs::spawn|. The filename |pond/Chapter 1/Amphibians.w| Here the tagname is [[Frogs::spawn]]. The filename [[pond/Chapter 1/Amphibians.w]]
is the file defining this function. The |find| field is an EX-format command for is the file defining this function. The [[find]] field is an EX-format command for
finding the line in question: see below. Finally, the |more| field is actually finding the line in question: see below. Finally, the [[more]] field is actually
a run of optional extra information, presented in a free-form sort of way, but a run of optional extra information, presented in a free-form sort of way, but
we will use it only the simplest of ways. In this example it is just |f|, we will use it only the simplest of ways. In this example it is just [[f]],
meaning "I am a function declaration". meaning "I am a function declaration".
The opening lines of the file, however, are usually metadata, i.e., describing the The opening lines of the file, however, are usually metadata, i.e., describing the
file itself and where it came from. In those lines, tagnames begin with |!_| and are file itself and where it came from. In those lines, tagnames begin with [[!_]] and are
called "pseudotags". The |filename| field is instead a value, while the |find| called "pseudotags". The [[filename]] field is instead a value, while the [[find]]
field is instead an optional comment. field is instead an optional comment.
The first two keys here are essential: the other three seem just to be good practice. The first two keys here are essential: the other three seem just to be good practice.
These are the five keys which Universal |ctags| writes by default, so we'll follow These are the five keys which Universal [[ctags]] writes by default, so we'll follow
suit. suit.
@<Write header@> = <<Write header>>=
WRITE("!_TAG_FILE_FORMAT\t2\t/extended format; --format=1 will not append ;\" to lines/\n"); WRITE("!_TAG_FILE_FORMAT\t2\t/extended format; --format=1 will not append ;\" to lines/\n");
WRITE("!_TAG_FILE_SORTED\t0\t/0=unsorted, 1=sorted, 2=foldcase/\n"); WRITE("!_TAG_FILE_SORTED\t0\t/0=unsorted, 1=sorted, 2=foldcase/\n");
WRITE("!_TAG_PROGRAM_AUTHOR\tGraham Nelson\t/graham.nelson@mod-langs.ox.ac.uk/\n"); WRITE("!_TAG_PROGRAM_AUTHOR\tGraham Nelson\t/graham.nelson@mod-langs.ox.ac.uk/\n");
@ -88,9 +88,9 @@ suit.
@ Having prudently opted to give the tags in an unsorted way, we're free to list @ Having prudently opted to give the tags in an unsorted way, we're free to list
them in any order convenient to us, and here goes. them in any order convenient to us, and here goes.
The |more| field |d| says that a tagname is a defined constant: The [[more]] field [[d]] says that a tagname is a defined constant:
@<List defined constants@> = <<List defined constants>>=
defined_constant *str; defined_constant *str;
LOOP_OVER(str, defined_constant) LOOP_OVER(str, defined_constant)
if (str->at->owning_section->owning_web == W) { if (str->at->owning_section->owning_web == W) {
@ -101,11 +101,11 @@ The |more| field |d| says that a tagname is a defined constant:
WRITE("\n"); WRITE("\n");
} }
@ The |more| field |t| says that a tagname is a type, and we add a clarifying @ The [[more]] field [[t]] says that a tagname is a type, and we add a clarifying
detail to say that it results from a |typedef struct|. (Note that |typeref| detail to say that it results from a [[typedef struct]]. (Note that [[typeref]]
here, with an "r", is not a mistake. This is what Universal |ctags| calls it.) here, with an "r", is not a mistake. This is what Universal [[ctags]] calls it.)
@<List structures@> = <<List structures>>=
language_type *str; language_type *str;
LOOP_OVER(str, language_type) LOOP_OVER(str, language_type)
if (str->structure_header_at->owning_section->owning_web == W) { if (str->structure_header_at->owning_section->owning_web == W) {
@ -116,9 +116,9 @@ here, with an "r", is not a mistake. This is what Universal |ctags| calls it.)
WRITE("\n"); WRITE("\n");
} }
@ The |more| field |f| says that a tagname is a function: @ The [[more]] field [[f]] says that a tagname is a function:
@<List functions@> = <<List functions>>=
language_function *fn; language_function *fn;
LOOP_OVER(fn, language_function) LOOP_OVER(fn, language_function)
if (fn->function_header_at->owning_section->owning_web == W) { if (fn->function_header_at->owning_section->owning_web == W) {
@ -129,20 +129,20 @@ here, with an "r", is not a mistake. This is what Universal |ctags| calls it.)
WRITE("\n"); WRITE("\n");
} }
@ So, then, here we write the |filename| and |find| fields for a given @ So, then, here we write the [[filename]] and [[find]] fields for a given
source line |L| in our web. Note that: source line [[L]] in our web. Note that:
(a) The filename must be given relative to the directory containing the tags (a) The filename must be given relative to the directory containing the tags
file, so for us that will be the home directory of the web. file, so for us that will be the home directory of the web.
(b) The |find| field looks like a regular expression but is not one, despite (b) The [[find]] field looks like a regular expression but is not one, despite
the suggestive positional markers |^| and |$|. Note in particular that round the suggestive positional markers [[^]] and [[$]]. Note in particular that round
brackets and asterisk characters are not escaped, as they would be in a regex. brackets and asterisk characters are not escaped, as they would be in a regex.
The Ctags documentation is vague here but does note that |^| and |$| should The Ctags documentation is vague here but does note that [[^]] and [[$]] should
be escaped only where they occur in the first or last positions. Tabs do be escaped only where they occur in the first or last positions. Tabs do
not need to be escaped. not need to be escaped.
= <<*>>=
void Ctags::write_line_ref(OUTPUT_STREAM, source_line *L, pathname *P) { void Ctags::write_line_ref(OUTPUT_STREAM, source_line *L, pathname *P) {
TEMPORARY_TEXT(fn) TEMPORARY_TEXT(fn)
WRITE_TO(fn, "%f", L->owning_section->md->source_file_for_section); WRITE_TO(fn, "%f", L->owning_section->md->source_file_for_section);
@ -169,16 +169,16 @@ We could laboriously extract that from the hash table of reserved words
(see //The Analyser//), but this is one of those times when life is short and (see //The Analyser//), but this is one of those times when life is short and
memory is cheap. It's easier to keep a duplicate list. memory is cheap. It's easier to keep a duplicate list.
= <<*>>=
typedef struct defined_constant { typedef struct defined_constant {
struct text_stream *name; struct text_stream *name;
struct source_line *at; struct source_line *at;
CLASS_DEFINITION CLASS_DEFINITION
} defined_constant; } defined_constant;
@ This is called for any |@d| or |@e| constant name, then: @ This is called for any [[@d]] or [[@e]] constant name, then:
= <<*>>=
void Ctags::note_defined_constant(source_line *L, text_stream *name) { void Ctags::note_defined_constant(source_line *L, text_stream *name) {
defined_constant *dc = CREATE(defined_constant); defined_constant *dc = CREATE(defined_constant);
dc->name = Str::duplicate(name); dc->name = Str::duplicate(name);

View file

@ -4,7 +4,7 @@ Constructing a suitable gitignore file for a simple inweb project.
@ This is an extremely simple use of //foundation: Preprocessor//. @ This is an extremely simple use of //foundation: Preprocessor//.
= <<*>>=
void Git::write_gitignore(web *W, filename *prototype, filename *F) { void Git::write_gitignore(web *W, filename *prototype, filename *F) {
linked_list *L = NEW_LINKED_LIST(preprocessor_macro); linked_list *L = NEW_LINKED_LIST(preprocessor_macro);
Preprocessor::new_macro(L, I"basics", NULL, Git::basics_expander, NULL); Preprocessor::new_macro(L, I"basics", NULL, Git::basics_expander, NULL);
@ -18,7 +18,7 @@ void Git::write_gitignore(web *W, filename *prototype, filename *F) {
@ Our one non-standard macro simply includes a file of standing material which @ Our one non-standard macro simply includes a file of standing material which
is the same as the default .giscript file anyway: is the same as the default .giscript file anyway:
= <<*>>=
void Git::basics_expander(preprocessor_macro *mm, preprocessor_state *PPS, void Git::basics_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) { text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
filename *prototype = Filenames::in(path_to_inweb_materials, I"default.giscript"); filename *prototype = Filenames::in(path_to_inweb_materials, I"default.giscript");

View file

@ -2,13 +2,13 @@
Constructing a suitable makefile for a simple inweb project. Constructing a suitable makefile for a simple inweb project.
@h Preprocessing. @ \section{Preprocessing.}
We will use //foundation: Preprocessor// with four special macros and one We will use //foundation: Preprocessor// with four special macros and one
special loop construct. special loop construct.
For the syntax being worked through, see //Webs, Tangling and Weaving//. For the syntax being worked through, see //Webs, Tangling and Weaving//.
= <<*>>=
void Makefiles::write(web *W, filename *prototype, filename *F, module_search *I, void Makefiles::write(web *W, filename *prototype, filename *F, module_search *I,
text_stream *platform) { text_stream *platform) {
linked_list *L = NEW_LINKED_LIST(preprocessor_macro); linked_list *L = NEW_LINKED_LIST(preprocessor_macro);
@ -33,7 +33,7 @@ void Makefiles::write(web *W, filename *prototype, filename *F, module_search *I
Makefiles::components_expander, NULL); Makefiles::components_expander, NULL);
makefile_specifics *specifics = CREATE(makefile_specifics); makefile_specifics *specifics = CREATE(makefile_specifics);
@<Initialise the specific data for makefile-preprocessing@>; <<Initialise the specific data for makefile-preprocessing>>;
text_stream *header = Str::new(); text_stream *header = Str::new();
WRITE_TO(header, "# This makefile was automatically written by inweb -makefile\n"); WRITE_TO(header, "# This makefile was automatically written by inweb -makefile\n");
@ -47,18 +47,18 @@ void Makefiles::write(web *W, filename *prototype, filename *F, module_search *I
@ We will allow a makescript to declare "components" (webs, really), so we need @ We will allow a makescript to declare "components" (webs, really), so we need
a data structure to store those declarations in: a data structure to store those declarations in:
= <<*>>=
typedef struct makefile_specifics { typedef struct makefile_specifics {
struct web *for_web; /* if one has been set at the command line */ struct web *for_web; /* if one has been set at the command line */
struct dictionary *tools_dictionary; /* components with |type: tool| */ struct dictionary *tools_dictionary; /* components with [[type: tool]] */
struct dictionary *webs_dictionary; /* components with |type: web| */ struct dictionary *webs_dictionary; /* components with [[type: web]] */
struct dictionary *modules_dictionary; /* components with |type: module| */ struct dictionary *modules_dictionary; /* components with [[type: module]] */
struct module_search *search_path; struct module_search *search_path;
struct text_stream *which_platform; struct text_stream *which_platform;
CLASS_DEFINITION CLASS_DEFINITION
} makefile_specifics; } makefile_specifics;
@<Initialise the specific data for makefile-preprocessing@> = <<Initialise the specific data for makefile-preprocessing>>=
specifics->for_web = W; specifics->for_web = W;
specifics->tools_dictionary = Dictionaries::new(16, FALSE); specifics->tools_dictionary = Dictionaries::new(16, FALSE);
specifics->webs_dictionary = Dictionaries::new(16, FALSE); specifics->webs_dictionary = Dictionaries::new(16, FALSE);
@ -66,9 +66,9 @@ typedef struct makefile_specifics {
specifics->search_path = I; specifics->search_path = I;
specifics->which_platform = platform; specifics->which_platform = platform;
@h The identity-settings expander. @ \section{The identity-settings expander.}
= <<*>>=
void Makefiles::identity_settings_expander(preprocessor_macro *mm, preprocessor_state *PPS, void Makefiles::identity_settings_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) { text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
makefile_specifics *specifics = RETRIEVE_POINTER_makefile_specifics(PPS->specifics); makefile_specifics *specifics = RETRIEVE_POINTER_makefile_specifics(PPS->specifics);
@ -84,13 +84,13 @@ void Makefiles::identity_settings_expander(preprocessor_macro *mm, preprocessor_
} }
} }
@h The platform-settings expander. @ \section{The platform-settings expander.}
We first scan Inweb's platform settings file for a definition line in the We first scan Inweb's platform settings file for a definition line in the
shape INWEBPLATFORM = PLATFORM, in order to find out what PLATFORM the make file shape INWEBPLATFORM = PLATFORM, in order to find out what PLATFORM the make file
will be used on. Then we splice in the appropriate file of standard definitions will be used on. Then we splice in the appropriate file of standard definitions
for that platform. for that platform.
= <<*>>=
void Makefiles::platform_settings_expander(preprocessor_macro *mm, preprocessor_state *PPS, void Makefiles::platform_settings_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) { text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
makefile_specifics *specifics = RETRIEVE_POINTER_makefile_specifics(PPS->specifics); makefile_specifics *specifics = RETRIEVE_POINTER_makefile_specifics(PPS->specifics);
@ -123,9 +123,9 @@ void Makefiles::seek_INWEBPLATFORM(text_stream *line, text_file_position *tfp, v
Regexp::dispose_of(&mr); Regexp::dispose_of(&mr);
} }
@h The modify filename expander. @ \section{The modify filename expander.}
= <<*>>=
void Makefiles::modify_filenames_expander(preprocessor_macro *mm, preprocessor_state *PPS, void Makefiles::modify_filenames_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) { text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
text_stream *OUT = PPS->dest; text_stream *OUT = PPS->dest;
@ -142,17 +142,17 @@ void Makefiles::modify_filenames_expander(preprocessor_macro *mm, preprocessor_s
if (Characters::is_whitespace(c)) { if (Characters::is_whitespace(c)) {
if ((previous != '\\') && (quoted == FALSE)) boundary = TRUE; if ((previous != '\\') && (quoted == FALSE)) boundary = TRUE;
} else { } else {
if (boundary) @<Captured a name@>; if (boundary) <<Captured a name>>;
boundary = FALSE; boundary = FALSE;
} }
PUT_TO(captured, c); PUT_TO(captured, c);
previous = c; previous = c;
} }
@<Captured a name@> <<Captured a name>>
DISCARD_TEXT(captured) DISCARD_TEXT(captured)
} }
@<Captured a name@> = <<Captured a name>>=
Str::trim_white_space(captured); Str::trim_white_space(captured);
if (Str::len(captured) > 0) { if (Str::len(captured) > 0) {
int in_quotes = FALSE; int in_quotes = FALSE;
@ -179,9 +179,9 @@ void Makefiles::modify_filenames_expander(preprocessor_macro *mm, preprocessor_s
Str::clear(captured); Str::clear(captured);
} }
@h The component expander. @ \section{The component expander.}
= <<*>>=
void Makefiles::component_expander(preprocessor_macro *mm, preprocessor_state *PPS, void Makefiles::component_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) { text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
makefile_specifics *specifics = RETRIEVE_POINTER_makefile_specifics(PPS->specifics); makefile_specifics *specifics = RETRIEVE_POINTER_makefile_specifics(PPS->specifics);
@ -196,25 +196,25 @@ void Makefiles::component_expander(preprocessor_macro *mm, preprocessor_state *P
if (Str::eq(category, I"tool")) { if (Str::eq(category, I"tool")) {
int marker = MAKEFILE_TOOL_MOM; int marker = MAKEFILE_TOOL_MOM;
dictionary *D = specifics->tools_dictionary; dictionary *D = specifics->tools_dictionary;
@<Add to dictionary@>; <<Add to dictionary>>;
@<Derive some make symbols@>; <<Derive some make symbols>>;
} else if (Str::eq(category, I"web")) { } else if (Str::eq(category, I"web")) {
int marker = MAKEFILE_WEB_MOM; int marker = MAKEFILE_WEB_MOM;
dictionary *D = specifics->webs_dictionary; dictionary *D = specifics->webs_dictionary;
@<Add to dictionary@>; <<Add to dictionary>>;
@<Derive some make symbols@>; <<Derive some make symbols>>;
} else if (Str::eq(category, I"module")) { } else if (Str::eq(category, I"module")) {
int marker = MAKEFILE_MODULE_MOM; int marker = MAKEFILE_MODULE_MOM;
dictionary *D = specifics->modules_dictionary; dictionary *D = specifics->modules_dictionary;
@<Add to dictionary@>; <<Add to dictionary>>;
@<Derive some make symbols@>; <<Derive some make symbols>>;
} else { } else {
Errors::in_text_file("category should be 'tool', 'module' or 'web'", tfp); Errors::in_text_file("category should be 'tool', 'module' or 'web'", tfp);
} }
PPS->last_line_was_blank = FALSE; PPS->last_line_was_blank = FALSE;
} }
@<Add to dictionary@> = <<Add to dictionary>>=
web_md *Wm = Reader::load_web_md(Pathnames::from_text(path), NULL, web_md *Wm = Reader::load_web_md(Pathnames::from_text(path), NULL,
specifics->search_path, TRUE); specifics->search_path, TRUE);
Wm->as_module->module_name = Str::duplicate(symbol); Wm->as_module->module_name = Str::duplicate(symbol);
@ -223,15 +223,15 @@ void Makefiles::component_expander(preprocessor_macro *mm, preprocessor_state *P
Dictionaries::create(D, symbol); Dictionaries::create(D, symbol);
Dictionaries::write_value(D, symbol, Wm); Dictionaries::write_value(D, symbol, Wm);
@<Derive some make symbols@> = <<Derive some make symbols>>=
WRITE("%SLEAF = %S\n", symbol, webname); WRITE("%SLEAF = %S\n", symbol, webname);
WRITE("%SWEB = %S\n", symbol, path); WRITE("%SWEB = %S\n", symbol, path);
WRITE("%SMAKER = $(%SWEB)/%S.mk\n", symbol, symbol, webname); WRITE("%SMAKER = $(%SWEB)/%S.mk\n", symbol, symbol, webname);
WRITE("%SX = $(%SWEB)/Tangled/%S\n", symbol, symbol, webname); WRITE("%SX = $(%SWEB)/Tangled/%S\n", symbol, symbol, webname);
@h The components loop construct. @ \section{The components loop construct.}
= <<*>>=
void Makefiles::components_expander(preprocessor_macro *mm, preprocessor_state *PPS, void Makefiles::components_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) { text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
Preprocessor::set_loop_var_name(loop, I"SYMBOL"); Preprocessor::set_loop_var_name(loop, I"SYMBOL");
@ -240,19 +240,19 @@ void Makefiles::components_expander(preprocessor_macro *mm, preprocessor_state *
if (Str::len(set) == 0) set = I"all"; if (Str::len(set) == 0) set = I"all";
if (Str::eq(category, I"tool")) { if (Str::eq(category, I"tool")) {
int marker = MAKEFILE_TOOL_MOM; int marker = MAKEFILE_TOOL_MOM;
@<Make the web iterations@>; <<Make the web iterations>>;
} else if (Str::eq(category, I"web")) { } else if (Str::eq(category, I"web")) {
int marker = MAKEFILE_WEB_MOM; int marker = MAKEFILE_WEB_MOM;
@<Make the web iterations@>; <<Make the web iterations>>;
} else if (Str::eq(category, I"module")) { } else if (Str::eq(category, I"module")) {
int marker = MAKEFILE_MODULE_MOM; int marker = MAKEFILE_MODULE_MOM;
@<Make the web iterations@>; <<Make the web iterations>>;
} else { } else {
Errors::in_text_file("category should be 'tool', 'module' or 'web'", tfp); Errors::in_text_file("category should be 'tool', 'module' or 'web'", tfp);
} }
} }
@<Make the web iterations@> = <<Make the web iterations>>=
module *M; module *M;
LOOP_OVER(M, module) { LOOP_OVER(M, module) {
if ((M->origin_marker == marker) && if ((M->origin_marker == marker) &&
@ -262,9 +262,9 @@ void Makefiles::components_expander(preprocessor_macro *mm, preprocessor_state *
} }
} }
@h The dependent-files expander. @ \section{The dependent-files expander.}
= <<*>>=
void Makefiles::dependent_files_expander(preprocessor_macro *mm, preprocessor_state *PPS, void Makefiles::dependent_files_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) { text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
makefile_specifics *specifics = RETRIEVE_POINTER_makefile_specifics(PPS->specifics); makefile_specifics *specifics = RETRIEVE_POINTER_makefile_specifics(PPS->specifics);
@ -318,20 +318,20 @@ void Makefiles::dependent_files_expander(preprocessor_macro *mm, preprocessor_st
} }
@ This outputs a makefile pattern matching a bunch of web source code filenames: @ This outputs a makefile pattern matching a bunch of web source code filenames:
say, |inweb/Chapter\ %d/*.w|. say, [[inweb/Chapter\ %d/*.w]].
= <<*>>=
void Makefiles::pattern(OUTPUT_STREAM, linked_list *L, filename *F) { void Makefiles::pattern(OUTPUT_STREAM, linked_list *L, filename *F) {
dictionary *patterns_done = Dictionaries::new(16, TRUE); dictionary *patterns_done = Dictionaries::new(16, TRUE);
if (F) @<Add pattern for file F, if not already given@>; if (F) <<Add pattern for file F, if not already given>>;
section_md *Sm; section_md *Sm;
LOOP_OVER_LINKED_LIST(Sm, section_md, L) { LOOP_OVER_LINKED_LIST(Sm, section_md, L) {
filename *F = Sm->source_file_for_section; filename *F = Sm->source_file_for_section;
@<Add pattern for file F, if not already given@>; <<Add pattern for file F, if not already given>>;
} }
} }
@<Add pattern for file F, if not already given@> = <<Add pattern for file F, if not already given>>=
pathname *P = Filenames::up(F); pathname *P = Filenames::up(F);
TEMPORARY_TEXT(leaf_pattern) TEMPORARY_TEXT(leaf_pattern)
WRITE_TO(leaf_pattern, "%S", Pathnames::directory_name(P)); WRITE_TO(leaf_pattern, "%S", Pathnames::directory_name(P));
@ -361,7 +361,7 @@ void Makefiles::pattern(OUTPUT_STREAM, linked_list *L, filename *F) {
bald statement really doesn't begin to go into how awkward makefiles can be bald statement really doesn't begin to go into how awkward makefiles can be
when filenames have spaces in, but there we are.) when filenames have spaces in, but there we are.)
= <<*>>=
void Makefiles::pathname_slashed(OUTPUT_STREAM, pathname *P) { void Makefiles::pathname_slashed(OUTPUT_STREAM, pathname *P) {
TEMPORARY_TEXT(PT) TEMPORARY_TEXT(PT)
WRITE_TO(PT, "%p", P); WRITE_TO(PT, "%p", P);

View file

@ -3,10 +3,10 @@
To construct Readme and similar files. To construct Readme and similar files.
@ This is a simple use of //foundation: Preprocessor//. Note that we use a @ This is a simple use of //foundation: Preprocessor//. Note that we use a
non-standard comment syntax (i.e., |/| at start of line, not |#|) to avoid non-standard comment syntax (i.e., [[/]] at start of line, not [[#]]) to avoid
colliding with Markdown's heading syntax. colliding with Markdown's heading syntax.
= <<*>>=
void Readme::write(filename *prototype, filename *F) { void Readme::write(filename *prototype, filename *F) {
linked_list *L = NEW_LINKED_LIST(preprocessor_macro); linked_list *L = NEW_LINKED_LIST(preprocessor_macro);
preprocessor_macro *mm = Preprocessor::new_macro(L, preprocessor_macro *mm = Preprocessor::new_macro(L,
@ -19,7 +19,7 @@ void Readme::write(filename *prototype, filename *F) {
@ And this is the one domain-specific macro: @ And this is the one domain-specific macro:
= <<*>>=
void Readme::bibliographic_expander(preprocessor_macro *mm, preprocessor_state *PPS, void Readme::bibliographic_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) { text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
text_stream *datum = parameter_values[0]; text_stream *datum = parameter_values[0];
@ -37,7 +37,7 @@ also be a few other rather Inform-specific things; those have a more limited
range of bibliographic data, just the version and date (and we will not range of bibliographic data, just the version and date (and we will not
assume that the version complies with any format). assume that the version complies with any format).
= <<*>>=
typedef struct writeme_asset { typedef struct writeme_asset {
struct text_stream *name; struct text_stream *name;
struct web_md *if_web; struct web_md *if_web;
@ -56,7 +56,7 @@ void Readme::write_var(text_stream *OUT, text_stream *program, text_stream *datu
@ That just leaves the business of inspecting assets to obtain their metadata. @ That just leaves the business of inspecting assets to obtain their metadata.
= <<*>>=
writeme_asset *Readme::find_asset(text_stream *program) { writeme_asset *Readme::find_asset(text_stream *program) {
writeme_asset *A; writeme_asset *A;
LOOP_OVER(A, writeme_asset) if (Str::eq(program, A->name)) return A; LOOP_OVER(A, writeme_asset) if (Str::eq(program, A->name)) return A;
@ -66,48 +66,48 @@ writeme_asset *Readme::find_asset(text_stream *program) {
A->date = Str::new(); A->date = Str::new();
A->version = Str::new(); A->version = Str::new();
A->next_is_version = FALSE; A->next_is_version = FALSE;
@<Read in the asset@>; <<Read in the asset>>;
return A; return A;
} }
@<Read in the asset@> = <<Read in the asset>>=
if (Str::ends_with_wide_string(program, L".i7x")) { if (Str::ends_with_wide_string(program, L".i7x")) {
@<Read in the extension file@>; <<Read in the extension file>>;
} else { } else {
if (WebMetadata::directory_looks_like_a_web(Pathnames::from_text(program))) { if (WebMetadata::directory_looks_like_a_web(Pathnames::from_text(program))) {
A->if_web = WebMetadata::get_without_modules(Pathnames::from_text(program), NULL); A->if_web = WebMetadata::get_without_modules(Pathnames::from_text(program), NULL);
} else { } else {
filename *I6_vn = Filenames::in( filename *I6_vn = Filenames::in(
Pathnames::down(Pathnames::from_text(program), I"inform6"), I"header.h"); Pathnames::down(Pathnames::from_text(program), I"inform6"), I"header.h");
if (TextFiles::exists(I6_vn)) @<Read in I6 source header file@>; if (TextFiles::exists(I6_vn)) <<Read in I6 source header file>>;
filename *template_vn = Filenames::in(Pathnames::from_text(program), I"(manifest).txt"); filename *template_vn = Filenames::in(Pathnames::from_text(program), I"(manifest).txt");
if (TextFiles::exists(template_vn)) @<Read in template manifest file@>; if (TextFiles::exists(template_vn)) <<Read in template manifest file>>;
filename *rmt_vn = Filenames::in(Pathnames::from_text(program), I"README.txt"); filename *rmt_vn = Filenames::in(Pathnames::from_text(program), I"README.txt");
if (TextFiles::exists(rmt_vn)) @<Read in README file@>; if (TextFiles::exists(rmt_vn)) <<Read in README file>>;
rmt_vn = Filenames::in(Pathnames::from_text(program), I"README.md"); rmt_vn = Filenames::in(Pathnames::from_text(program), I"README.md");
if (TextFiles::exists(rmt_vn)) @<Read in README file@>; if (TextFiles::exists(rmt_vn)) <<Read in README file>>;
} }
} }
@<Read in the extension file@> = <<Read in the extension file>>=
TextFiles::read(Filenames::from_text(program), FALSE, "unable to read extension", TRUE, TextFiles::read(Filenames::from_text(program), FALSE, "unable to read extension", TRUE,
&Readme::extension_harvester, NULL, A); &Readme::extension_harvester, NULL, A);
@<Read in I6 source header file@> = <<Read in I6 source header file>>=
TextFiles::read(I6_vn, FALSE, "unable to read header file from I6 source", TRUE, TextFiles::read(I6_vn, FALSE, "unable to read header file from I6 source", TRUE,
&Readme::header_harvester, NULL, A); &Readme::header_harvester, NULL, A);
@<Read in template manifest file@> = <<Read in template manifest file>>=
TextFiles::read(template_vn, FALSE, "unable to read manifest file from website template", TRUE, TextFiles::read(template_vn, FALSE, "unable to read manifest file from website template", TRUE,
&Readme::template_harvester, NULL, A); &Readme::template_harvester, NULL, A);
@<Read in README file@> = <<Read in README file>>=
TextFiles::read(rmt_vn, FALSE, "unable to read README file from website template", TRUE, TextFiles::read(rmt_vn, FALSE, "unable to read README file from website template", TRUE,
&Readme::readme_harvester, NULL, A); &Readme::readme_harvester, NULL, A);
@ The format for the contents section of a web is documented in Inweb. @ The format for the contents section of a web is documented in Inweb.
= <<*>>=
void Readme::extension_harvester(text_stream *text, text_file_position *tfp, void *state) { void Readme::extension_harvester(text_stream *text, text_file_position *tfp, void *state) {
writeme_asset *A = (writeme_asset *) state; writeme_asset *A = (writeme_asset *) state;
match_results mr = Regexp::create_mr(); match_results mr = Regexp::create_mr();
@ -117,9 +117,9 @@ void Readme::extension_harvester(text_stream *text, text_file_position *tfp, voi
Regexp::dispose_of(&mr); Regexp::dispose_of(&mr);
} }
@ Explicit code to read from |header.h| in the Inform 6 repository. @ Explicit code to read from [[header.h]] in the Inform 6 repository.
= <<*>>=
void Readme::header_harvester(text_stream *text, text_file_position *tfp, void *state) { void Readme::header_harvester(text_stream *text, text_file_position *tfp, void *state) {
writeme_asset *A = (writeme_asset *) state; writeme_asset *A = (writeme_asset *) state;
match_results mr = Regexp::create_mr(); match_results mr = Regexp::create_mr();
@ -133,7 +133,7 @@ void Readme::header_harvester(text_stream *text, text_file_position *tfp, void *
@ Explicit code to read from the manifest file of a website template. @ Explicit code to read from the manifest file of a website template.
= <<*>>=
void Readme::template_harvester(text_stream *text, text_file_position *tfp, void *state) { void Readme::template_harvester(text_stream *text, text_file_position *tfp, void *state) {
writeme_asset *A = (writeme_asset *) state; writeme_asset *A = (writeme_asset *) state;
match_results mr = Regexp::create_mr(); match_results mr = Regexp::create_mr();
@ -147,9 +147,9 @@ void Readme::template_harvester(text_stream *text, text_file_position *tfp, void
Regexp::dispose_of(&mr); Regexp::dispose_of(&mr);
} }
@ And this is needed for |cheapglk| and |glulxe| in the Inform repository. @ And this is needed for [[cheapglk]] and [[glulxe]] in the Inform repository.
= <<*>>=
void Readme::readme_harvester(text_stream *text, text_file_position *tfp, void *state) { void Readme::readme_harvester(text_stream *text, text_file_position *tfp, void *state) {
writeme_asset *A = (writeme_asset *) state; writeme_asset *A = (writeme_asset *) state;
match_results mr = Regexp::create_mr(); match_results mr = Regexp::create_mr();