Chapter 6: Nowebify.
This commit is contained in:
parent
a733b321a5
commit
d167c16357
5 changed files with 165 additions and 164 deletions
|
@ -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+)// *"))
|
|
@ -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);
|
|
@ -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");
|
|
@ -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);
|
|
@ -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();
|
Loading…
Reference in a new issue