inweb-bootstrap/foundation-module/Chapter 8/Web Modules.w
2020-06-27 23:03:14 +01:00

211 lines
7.3 KiB
OpenEdge ABL

[WebModules::] Web Modules.
To search for included modules, and track dependencies between them.
@h Creation.
Each web of source material discovered by Inweb is given one of the following.
Ordinarily these are found only when reading in a web for weaving, tangling
and so on: in the vast majority of Inweb runs, all modules will have the
"module origin marker" |READING_WEB_MOM|. But when Inweb is constructing a
makefile for a suite of tools, it can also discover multiple webs by other
means.
@e READING_WEB_MOM from 0
@e MAKEFILE_TOOL_MOM
@e MAKEFILE_WEB_MOM
@e MAKEFILE_MODULE_MOM
=
typedef struct module {
struct pathname *module_location;
struct text_stream *module_name;
struct linked_list *dependencies; /* of |module|: which other modules does this need? */
struct text_stream *module_tag;
int origin_marker; /* one of the |*_MOM| values above */
struct linked_list *chapters_md; /* of |chapter_md|: just the ones in this module */
struct linked_list *sections_md; /* of |section_md|: just the ones in this module */
CLASS_DEFINITION
} module;
@ =
module *WebModules::new(text_stream *name, pathname *at, int m) {
module *M = CREATE(module);
M->module_location = at;
M->module_name = Str::duplicate(name);
M->dependencies = NEW_LINKED_LIST(module);
M->origin_marker = m;
M->module_tag = I"miscellaneous";
M->chapters_md = NEW_LINKED_LIST(chapter_md);
M->sections_md = NEW_LINKED_LIST(section_md);
return M;
}
@ In the Inweb documentation, "module" is used to refer to a sidekick web which
contains a suite of utility routines, or a major component of a program, but
which is not a program in its own right.
Internally, though, every web produces a |module| structure. The one for the
main web -- which can be tangled, and results in an actual program -- is
internally named |"(main)"|, a name which the user will never see.
=
module *WebModules::create_main_module(web_md *WS) {
return WebModules::new(I"(main)", WS->path_to_web, READING_WEB_MOM);
}
@h Dependencies.
When web A imports module B, we will say that A is dependent on B. A web
can import multiple modules, so there can a list of dependencies. These are
needed when constructing makefiles, since the source code in B affects the
program generated by A.
=
void WebModules::dependency(module *A, module *B) {
if ((A == NULL) || (B == NULL)) internal_error("no module");
ADD_TO_LINKED_LIST(B, module, A->dependencies);
}
@h Searching.
The following abstracts the idea of a place where modules might be found.
(At one time there was going to be a more elaborate search hierarchy.)
=
typedef struct module_search {
struct pathname *path_to_search;
CLASS_DEFINITION
} module_search;
@ =
module_search *WebModules::make_search_path(pathname *ext_path) {
module_search *ms = CREATE(module_search);
ms->path_to_search = ext_path;
return ms;
}
@ When a web's contents page says to |import Blah|, how do we find the module
called |Blah| on disc? We try four possibilities in sequence:
=
module *WebModules::find(web_md *WS, module_search *ms, text_stream *name, pathname *X) {
TEMPORARY_TEXT(T)
WRITE_TO(T, "%S-module", name);
pathname *tries[4];
tries[0] = WS?(WS->path_to_web):NULL;
tries[1] = tries[0]?(Pathnames::up(tries[0])):NULL;
tries[2] = X;
tries[3] = ms->path_to_search;
int N = 4;
for (int i=0; i<N; i++) {
pathname *P = Pathnames::from_text_relative(tries[i], T);
if ((P) && (WebModules::exists(P))) @<Accept this directory as the module@>;
}
DISCARD_TEXT(T)
return NULL;
}
@ When the module is found (if it is), a suitable module structure is made,
and a dependency created from the web's |(main)| module to this one.
@<Accept this directory as the module@> =
pathname *Q = Pathnames::from_text(name);
module *M = WebModules::new(Pathnames::directory_name(Q), P, READING_WEB_MOM);
WebModules::dependency(WS->as_module, M);
return M;
@ We accept that a plausibly-named directory is indeed the module being
sought if it looks like a web.
=
int WebModules::exists(pathname *P) {
return WebMetadata::directory_looks_like_a_web(P);
}
@h Resolving cross-reference names.
Suppose we are in module |from_M| and want to understand which section of
a relevant web |text| might refer to. It could be the name of a module,
either this one or one dependent on it; or the name of a chapter in one
of those, or the shortened forms of those; or the name of a section. It
may match multiple possibilities: we return how many, and if this is
positive, we write the module in which the first find was made in |*return M|,
the section in |*return_Sm|, and set the flag |*named_as_module| according
to whether the reference was a bare module name (say, "foundation") or not.
Note that we consider first the possibilities within |from_M|: we only
look at other modules if there are none. Thus, an unambiguous result in
|from_M| is good enough, even if there are other possibilities elsewhere.
A reference in the form |module: reference| is taken to be in the module
of that name: for example, |"foundation: Web Modules"| would find the
section of code you are now reading.
=
int WebModules::named_reference(module **return_M, section_md **return_Sm,
int *named_as_module, text_stream *title, module *from_M, text_stream *text, int list) {
*return_M = NULL; *return_Sm = NULL; *named_as_module = FALSE;
module *M;
int finds = 0;
if (from_M == NULL) return 0;
match_results mr = Regexp::create_mr();
text_stream *seek = text;
if (Regexp::match(&mr, text, L"(%C+?): *(%c+?) *")) {
LOOP_OVER_LINKED_LIST(M, module, from_M->dependencies)
if (Str::eq_insensitive(M->module_name, mr.exp[0])) {
seek = mr.exp[1];
@<Look for references to chapters or sections in M@>;
}
}
Regexp::dispose_of(&mr);
seek = text;
for (int stage = 1; ((finds == 0) && (stage <= 2)); stage++) {
if (stage == 1) {
M = from_M;
@<Look for references to chapters or sections in M@>;
}
if (stage == 2) {
LOOP_OVER_LINKED_LIST(M, module, from_M->dependencies)
@<Look for references to chapters or sections in M@>;
}
}
return finds;
}
@<Look for references to chapters or sections in M@> =
if (M == NULL) internal_error("no module");
if (Str::eq_insensitive(M->module_name, seek))
@<Found first section in module@>;
chapter_md *Cm;
section_md *Sm;
LOOP_OVER_LINKED_LIST(Cm, chapter_md, M->chapters_md) {
if ((Str::eq_insensitive(Cm->ch_title, seek)) ||
(Str::eq_insensitive(Cm->ch_basic_title, seek)) ||
(Str::eq_insensitive(Cm->ch_decorated_title, seek)))
@<Found first section in chapter@>;
LOOP_OVER_LINKED_LIST(Sm, section_md, Cm->sections_md)
if (Str::eq_insensitive(Sm->sect_title, seek))
@<Found section by name@>;
}
@<Found first section in module@> =
finds++;
if (finds == 1) {
*return_M = M; *return_Sm = FIRST_IN_LINKED_LIST(section_md, M->sections_md);
*named_as_module = TRUE;
WRITE_TO(title, "the %S module", M->module_name);
}
if (list) WRITE_TO(STDERR, "(%d) Module '%S'\n", finds, M->module_name);
@<Found first section in chapter@> =
finds++;
if (finds == 1) {
*return_M = M; *return_Sm = FIRST_IN_LINKED_LIST(section_md, Cm->sections_md);
WRITE_TO(title, "%S", Cm->ch_title);
}
if (list) WRITE_TO(STDERR, "(%d) Chapter '%S'\n", finds, Cm->ch_title);
@<Found section by name@> =
finds++;
if (finds == 1) {
*return_M = M; *return_Sm = Sm;
WRITE_TO(title, "%S", Sm->sect_title);
}
if (list) WRITE_TO(STDERR, "(%d) Section '%S'\n", finds, Sm->sect_title);