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.
@h Colonies of webs.
@ \section{Colonies of webs.}
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
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:
=
<<*>>=
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 pathname *assets_path; /* where assets shared between weaves live */
struct pathname *patterns_path; /* where additional patterns live */
CLASS_DEFINITION
} 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
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
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.
=
<<*>>=
typedef struct colony_member {
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 *path; /* the |P| 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 */
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 *path; /* the [[P| 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 *default_weave_pattern; /* for use when weaving */
struct web_md *loaded; /* metadata on its sections, lazily evaluated */
struct filename *navigation; /* navigation sidebar HTML */
struct linked_list *breadcrumb_tail; /* of |breadcrumb_request| */
struct linked_list *breadcrumb_tail; /* of [[breadcrumb_request]] */
CLASS_DEFINITION
} 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
at GitHub:
= (text from Figures/colony.txt)
=
(text from Figures/colony.txt)
<<*>>=
typedef struct colony_reader_state {
struct colony *province;
struct filename *nav;
struct linked_list *crumbs; /* of |breadcrumb_request| */
struct linked_list *crumbs; /* of [[breadcrumb_request]] */
struct text_stream *pattern;
} colony_reader_state;
@ -80,14 +81,14 @@ void Colonies::load(filename *F) {
@ 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) {
colony_reader_state *crs = (colony_reader_state *) v_crs;
colony *C = crs->province;
Str::trim_white_space(line); /* ignore trailing space */
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();
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
the page, and this requests one.
=
<<*>>=
void Colonies::add_crumb(linked_list *L, text_stream *spec, text_file_position *tfp) {
match_results mr = Regexp::create_mr();
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.
Given a name |T|, we try to find a colony member of that name, returning the
@ \section{Searching.}
Given a name [[T]], we try to find a colony member of that name, returning the
first we find.
=
<<*>>=
colony_member *Colonies::find(text_stream *T) {
colony *C;
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
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) {
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 not yet seen@>;
if (CM->loaded == NULL) @<Failing that, throw an error@>;
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 not yet seen>>;
if (CM->loaded == NULL) <<Failing that, throw an error>>;
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)))
CM->loaded = Wm;
@<Perhaps a module imported by the web being woven@> =
<<Perhaps a module imported by the web being woven>>=
if (Wm) {
module *M;
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;
}
@<Perhaps a module not yet seen@> =
<<Perhaps a module not yet seen>>=
filename *F = NULL;
pathname *P = NULL;
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);
CM->loaded = WebMetadata::get_without_modules(P, F);
@<Failing that, throw an error@> =
<<Failing that, throw an error>>=
TEMPORARY_TEXT(err)
WRITE_TO(err, "unable to load '%S'", CM->name);
Main::error_in_web(err, L);
@ Finally:
=
<<*>>=
text_stream *Colonies::home(void) {
colony *C;
LOOP_OVER(C, colony)
@ -288,9 +289,9 @@ pathname *Colonies::patterns_path(void) {
return NULL;
}
@h Cross-references.
@ \section{Cross-references.}
The following must decide what references like the following should refer to:
= (text)
Chapter 3
Manual
Enumerated Constants
@ -298,15 +299,15 @@ The following must decide what references like the following should refer to:
weave_order
foundation: Text Streams
goldbach
=
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
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
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.
=
<<*>>=
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) {
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;
int external = FALSE;
@<Is it an explicit URL?@>;
@<Is it the name of a member of our colony?@>;
@<If it contains a colon, does this indicate a section in a colony member?@>;
<<Is it an explicit URL?>>;
<<Is it the name of a member of our colony?>>;
<<If it contains a colon, does this indicate a section in a colony member?>>;
module *found_M = 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 ((L) && (external == FALSE)) {
@<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 function in the current web?>>;
<<Is it the name of a type in the current web?>>;
}
TEMPORARY_TEXT(err)
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);
return FALSE;
}
@<It refers unambiguously to a single section@>;
<<It refers unambiguously to a single section>>;
return TRUE;
}
@<Is it an explicit URL?@> =
<<Is it an explicit URL?>>=
match_results mr = Regexp::create_mr();
if (Regexp::match(&mr, text, L"https*://%c*")) {
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);
@<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);
if (search_CM) {
module *found_M = Colonies::as_module(search_CM, L, Wm);
section_md *found_Sm = FIRST_IN_LINKED_LIST(section_md, found_M->sections_md);
int bare_module_name = TRUE;
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();
if (Regexp::match(&mr, text, L"(%c*?): (%c*)")) {
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);
@<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;
LOOP_OVER(fn, language_function) {
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;
LOOP_OVER(str, language_type) {
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 (search_CM) @<The section is a known colony member@>
else @<The section is not in a known colony member@>;
if (search_CM) <<The section is a known colony member>>
else <<The section is not in a known colony member>>;
return TRUE;
@<The section is a known colony member@> =
<<The section is a known colony member>>=
pathname *from = Filenames::up(for_HTML_file);
pathname *to = search_CM->weave_path;
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
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) {
Colonies::section_URL(url, found_Sm);
} else {
@ -462,9 +463,9 @@ the main one.
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) {
match_results mr = Regexp::create_mr();
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",
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
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
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
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
|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
//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.
@ 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|,
so the default Ctags file is called |tags| and not |tags.ctag|.
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]].
=
<<*>>=
void Ctags::write(web *W, filename *F) {
text_stream ctags_file;
pathname *P = NULL;
@ -44,41 +44,41 @@ void Ctags::write(web *W, filename *F) {
text_stream *OUT = &ctags_file;
if (STREAM_OPEN_TO_FILE(OUT, F, UTF8_ENC) == FALSE)
Errors::fatal_with_file("unable to write ctags file", F);
@<Write header@>;
@<List defined constants@>;
@<List structures@>;
@<List functions@>;
<<Write header>>;
<<List defined constants>>;
<<List structures>>;
<<List functions>>;
STREAM_CLOSE(OUT);
}
@ 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
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:
= (text)
divided by tab characters. If we write [[ -> ]] for a tab, a line looks like:
tagname -> filename -> /find/;" -> more
=
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
=
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
finding the line in question: see below. Finally, the |more| field is actually
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
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
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".
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
called "pseudotags". The |filename| field is instead a value, while the |find|
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]]
field is instead an optional comment.
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.
@<Write header@> =
<<Write header>>=
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_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
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;
LOOP_OVER(str, defined_constant)
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");
}
@ 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|
here, with an "r", is not a mistake. This is what Universal |ctags| calls it.)
@ 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]]
here, with an "r", is not a mistake. This is what Universal [[ctags]] calls it.)
@<List structures@> =
<<List structures>>=
language_type *str;
LOOP_OVER(str, language_type)
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");
}
@ 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;
LOOP_OVER(fn, language_function)
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");
}
@ So, then, here we write the |filename| and |find| fields for a given
source line |L| in our web. Note that:
@ So, then, here we write the [[filename]] and [[find]] fields for a given
source line [[L]] in our web. Note that:
(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.
(b) The |find| field looks like a regular expression but is not one, despite
the suggestive positional markers |^| and |$|. Note in particular that round
(b) The [[find]] field looks like a regular expression but is not one, despite
the suggestive positional markers [[^]] and [[$]]. Note in particular that round
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
not need to be escaped.
=
<<*>>=
void Ctags::write_line_ref(OUTPUT_STREAM, source_line *L, pathname *P) {
TEMPORARY_TEXT(fn)
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
memory is cheap. It's easier to keep a duplicate list.
=
<<*>>=
typedef struct defined_constant {
struct text_stream *name;
struct source_line *at;
CLASS_DEFINITION
} 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) {
defined_constant *dc = CREATE(defined_constant);
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//.
=
<<*>>=
void Git::write_gitignore(web *W, filename *prototype, filename *F) {
linked_list *L = NEW_LINKED_LIST(preprocessor_macro);
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
is the same as the default .giscript file anyway:
=
<<*>>=
void Git::basics_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
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.
@h Preprocessing.
@ \section{Preprocessing.}
We will use //foundation: Preprocessor// with four special macros and one
special loop construct.
For the syntax being worked through, see //Webs, Tangling and Weaving//.
=
<<*>>=
void Makefiles::write(web *W, filename *prototype, filename *F, module_search *I,
text_stream *platform) {
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);
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();
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
a data structure to store those declarations in:
=
<<*>>=
typedef struct makefile_specifics {
struct web *for_web; /* if one has been set at the command line */
struct dictionary *tools_dictionary; /* components with |type: tool| */
struct dictionary *webs_dictionary; /* components with |type: web| */
struct dictionary *modules_dictionary; /* components with |type: module| */
struct dictionary *tools_dictionary; /* components with [[type: tool]] */
struct dictionary *webs_dictionary; /* components with [[type: web]] */
struct dictionary *modules_dictionary; /* components with [[type: module]] */
struct module_search *search_path;
struct text_stream *which_platform;
CLASS_DEFINITION
} makefile_specifics;
@<Initialise the specific data for makefile-preprocessing@> =
<<Initialise the specific data for makefile-preprocessing>>=
specifics->for_web = W;
specifics->tools_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->which_platform = platform;
@h The identity-settings expander.
@ \section{The identity-settings expander.}
=
<<*>>=
void Makefiles::identity_settings_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
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
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
for that platform.
=
<<*>>=
void Makefiles::platform_settings_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
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);
}
@h The modify filename expander.
@ \section{The modify filename expander.}
=
<<*>>=
void Makefiles::modify_filenames_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
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 ((previous != '\\') && (quoted == FALSE)) boundary = TRUE;
} else {
if (boundary) @<Captured a name@>;
if (boundary) <<Captured a name>>;
boundary = FALSE;
}
PUT_TO(captured, c);
previous = c;
}
@<Captured a name@>
<<Captured a name>>
DISCARD_TEXT(captured)
}
@<Captured a name@> =
<<Captured a name>>=
Str::trim_white_space(captured);
if (Str::len(captured) > 0) {
int in_quotes = FALSE;
@ -179,9 +179,9 @@ void Makefiles::modify_filenames_expander(preprocessor_macro *mm, preprocessor_s
Str::clear(captured);
}
@h The component expander.
@ \section{The component expander.}
=
<<*>>=
void Makefiles::component_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
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")) {
int marker = MAKEFILE_TOOL_MOM;
dictionary *D = specifics->tools_dictionary;
@<Add to dictionary@>;
@<Derive some make symbols@>;
<<Add to dictionary>>;
<<Derive some make symbols>>;
} else if (Str::eq(category, I"web")) {
int marker = MAKEFILE_WEB_MOM;
dictionary *D = specifics->webs_dictionary;
@<Add to dictionary@>;
@<Derive some make symbols@>;
<<Add to dictionary>>;
<<Derive some make symbols>>;
} else if (Str::eq(category, I"module")) {
int marker = MAKEFILE_MODULE_MOM;
dictionary *D = specifics->modules_dictionary;
@<Add to dictionary@>;
@<Derive some make symbols@>;
<<Add to dictionary>>;
<<Derive some make symbols>>;
} else {
Errors::in_text_file("category should be 'tool', 'module' or 'web'", tfp);
}
PPS->last_line_was_blank = FALSE;
}
@<Add to dictionary@> =
<<Add to dictionary>>=
web_md *Wm = Reader::load_web_md(Pathnames::from_text(path), NULL,
specifics->search_path, TRUE);
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::write_value(D, symbol, Wm);
@<Derive some make symbols@> =
<<Derive some make symbols>>=
WRITE("%SLEAF = %S\n", symbol, webname);
WRITE("%SWEB = %S\n", symbol, path);
WRITE("%SMAKER = $(%SWEB)/%S.mk\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,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
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::eq(category, I"tool")) {
int marker = MAKEFILE_TOOL_MOM;
@<Make the web iterations@>;
<<Make the web iterations>>;
} else if (Str::eq(category, I"web")) {
int marker = MAKEFILE_WEB_MOM;
@<Make the web iterations@>;
<<Make the web iterations>>;
} else if (Str::eq(category, I"module")) {
int marker = MAKEFILE_MODULE_MOM;
@<Make the web iterations@>;
<<Make the web iterations>>;
} else {
Errors::in_text_file("category should be 'tool', 'module' or 'web'", tfp);
}
}
@<Make the web iterations@> =
<<Make the web iterations>>=
module *M;
LOOP_OVER(M, module) {
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,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
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:
say, |inweb/Chapter\ %d/*.w|.
say, [[inweb/Chapter\ %d/*.w]].
=
<<*>>=
void Makefiles::pattern(OUTPUT_STREAM, linked_list *L, filename *F) {
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;
LOOP_OVER_LINKED_LIST(Sm, section_md, L) {
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);
TEMPORARY_TEXT(leaf_pattern)
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
when filenames have spaces in, but there we are.)
=
<<*>>=
void Makefiles::pathname_slashed(OUTPUT_STREAM, pathname *P) {
TEMPORARY_TEXT(PT)
WRITE_TO(PT, "%p", P);

View file

@ -3,10 +3,10 @@
To construct Readme and similar files.
@ 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.
=
<<*>>=
void Readme::write(filename *prototype, filename *F) {
linked_list *L = NEW_LINKED_LIST(preprocessor_macro);
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:
=
<<*>>=
void Readme::bibliographic_expander(preprocessor_macro *mm, preprocessor_state *PPS,
text_stream **parameter_values, preprocessor_loop *loop, text_file_position *tfp) {
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
assume that the version complies with any format).
=
<<*>>=
typedef struct writeme_asset {
struct text_stream *name;
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.
=
<<*>>=
writeme_asset *Readme::find_asset(text_stream *program) {
writeme_asset *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->version = Str::new();
A->next_is_version = FALSE;
@<Read in the asset@>;
<<Read in the asset>>;
return A;
}
@<Read in the asset@> =
<<Read in the asset>>=
if (Str::ends_with_wide_string(program, L".i7x")) {
@<Read in the extension file@>;
<<Read in the extension file>>;
} else {
if (WebMetadata::directory_looks_like_a_web(Pathnames::from_text(program))) {
A->if_web = WebMetadata::get_without_modules(Pathnames::from_text(program), NULL);
} else {
filename *I6_vn = Filenames::in(
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");
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");
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");
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,
&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,
&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,
&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,
&Readme::readme_harvester, NULL, A);
@ 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) {
writeme_asset *A = (writeme_asset *) state;
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);
}
@ 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) {
writeme_asset *A = (writeme_asset *) state;
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.
=
<<*>>=
void Readme::template_harvester(text_stream *text, text_file_position *tfp, void *state) {
writeme_asset *A = (writeme_asset *) state;
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);
}
@ 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) {
writeme_asset *A = (writeme_asset *) state;
match_results mr = Regexp::create_mr();