To search for included modules, and track dependencies between them.


§1. Introduction.When web W1 includes webs W2 and W3 as well, there are three "modules" in play: the so-called "main" module representing the un-imported content of W1, and then two other modules, one for material imported from W2, one for W3. Thus, every web has a main module, and many only have that one.

In this example, the literate source which is tangled will be the union of all three webs, but the literate source which us woven will just be that of W1. So, for example, if a tool called inexample is written as a web which imports this foundation library, then what's tangled and compiled is all of the source code in both modules, whereas a woven form of inexample shows just the source code unique to it, and not this foundation library as well.

§2. Creation.Each module has a "module origin marker", which indicates why it was created: this is always READING_WEB_MOM, unless we're constructing makefiles.

enum READING_WEB_MOM from 0
enum MAKEFILE_TOOL_MOM
enum MAKEFILE_WEB_MOM
enum MAKEFILE_MODULE_MOM
typedef struct ls_module {
    struct pathname *module_location;
    struct text_stream *module_name;  e.g., "(main)", or "foundation"
    struct linked_list *dependencies;  of ls_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;  of ls_chapter: just the ones in this module
    CLASS_DEFINITION
} ls_module;

§3.

ls_module *WebModules::new(text_stream *name, pathname *at, int m) {
    ls_module *M = CREATE(ls_module);
    M->module_location = at;
    M->module_name = Str::duplicate(name);
    M->dependencies = NEW_LINKED_LIST(ls_module);
    M->origin_marker = m;
    M->module_tag = I"miscellaneous";
    M->chapters = NEW_LINKED_LIST(ls_chapter);
    return M;
}

§4. The main module might be a single-file web, in which case there's no dedicated directory for its contents: in that case, W->path_to_web will be the directory containing the file.

ls_module *WebModules::create_main_module(ls_web *W) {
    return WebModules::new(I"(main)", W->path_to_web, READING_WEB_MOM);
}

§5. After creating a module, chapters must be added to it, in sequence. All of their sections are then included.

void WebModules::add_chapter(ls_module *A, ls_chapter *C) {
    C->owning_module = A;
    ADD_TO_LINKED_LIST(C, ls_chapter, A->chapters);
}

filename *WebModules::contents_filename(ls_module *A) {
    return Filenames::in(A->module_location, I"Contents.w");
}

§6. 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(ls_module *A, ls_module *B) {
    if ((A == NULL) || (B == NULL)) internal_error("no module");
    ADD_TO_LINKED_LIST(B, ls_module, A->dependencies);
}

§7. 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;

§8.

module_search *WebModules::make_search_path(pathname *ext_path) {
    module_search *ms = CREATE(module_search);
    ms->path_to_search = ext_path;
    return ms;
}

§9. 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:

ls_module *WebModules::find(ls_web *WS, module_search *ms, text_stream *name) {
    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] = Pathnames::path_to_inweb();
    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 module9.1;
    }
    DISCARD_TEXT(T)
    return NULL;
}

§9.1. 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 module9.1 =

    pathname *Q = Pathnames::from_text(name);
    ls_module *M = WebModules::new(Pathnames::directory_name(Q), P, READING_WEB_MOM);
    WebModules::dependency(WS->main_module, M);
    return M;

§10. 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 WebStructure::directory_looks_like_a_web(P);
}

§11. 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(ls_module **return_M, ls_section **return_Sm,
    int *named_as_module, text_stream *title, ls_module *from_M, text_stream *text,
    int list, int sections_only) {
    *return_M = NULL; *return_Sm = NULL; *named_as_module = FALSE;
    ls_module *M;
    int finds = 0;
    if (from_M == NULL) return 0;
    match_results mr = Regexp::create_mr();
    text_stream *seek = text;
    text_stream *seek_module = NULL;
    if (Regexp::match(&mr, text, U"(%C+?): *(%c+?) *")) {
        seek_module = mr.exp[0]; seek = mr.exp[1];
    } else {
        seek_module = from_M->module_name; seek = text;
    }
    LOOP_OVER_LINKED_LIST(M, ls_module, from_M->dependencies) {
        if (Str::eq_insensitive(M->module_name, seek_module)) {
            Look for references to chapters or sections in M11.1;
        }
    }
    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 M11.1;
        }
        if (stage == 2) {
            LOOP_OVER_LINKED_LIST(M, ls_module, from_M->dependencies)
                Look for references to chapters or sections in M11.1;
        }
    }
    return finds;
}

§11.1. Look for references to chapters or sections in M11.1 =

    if (M == NULL) internal_error("no module");
    if (Str::eq_insensitive(M->module_name, seek))
        Found first section in module11.1.1;
    ls_chapter *Cm;
    ls_section *Sm;
    LOOP_OVER_LINKED_LIST(Cm, ls_chapter, M->chapters) {
        if ((sections_only == FALSE) &&
            ((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 chapter11.1.2;
        LOOP_OVER_LINKED_LIST(Sm, ls_section, Cm->sections)
            if (Str::eq_insensitive(Sm->sect_title, seek))
                Found section by name11.1.3;
    }

§11.1.1. Found first section in module11.1.1 =

    finds++;
    if (finds == 1) {
        ls_chapter *C = FIRST_IN_LINKED_LIST(ls_chapter, M->chapters);
        *return_M = M; *return_Sm = FIRST_IN_LINKED_LIST(ls_section, C->sections);
        *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);

§11.1.2. Found first section in chapter11.1.2 =

    finds++;
    if (finds == 1) {
        *return_M = M; *return_Sm = FIRST_IN_LINKED_LIST(ls_section, Cm->sections);
        WRITE_TO(title, "%S", Cm->ch_title);
    }
    if (list) WRITE_TO(STDERR, "(%d)  Chapter '%S' of module '%S'\n",
        finds, Cm->ch_title, M->module_name);

§11.1.3. Found section by name11.1.3 =

    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' in chapter '%S' of module '%S'\n",
        finds, Sm->sect_title, Cm->ch_title, M->module_name);