To manage packages of inter code.


§1. Introduction. As noted in What This Module Does, the content in a tree is structured by being placed in a nested hierarchy of boxes called "packages".

A package has a location in the tree defined by its package_head node: this will be a PACKAGE_IST instruction. Every package has a name and a type. For example, suppose we have:

    A
        B
    package gadgets _paraphernalia          <-- package_head node
        symbol private misc ^is_electrical
        C
            D
        E
    F
    G

Here the Inter instructions C, D and E are the content of the package, which is called "gadgets" and has the type _paraphernalia. Instructions A, B, F, G , along with the package instruction itself, belong to the wider context. The symbol name ^is_electrical is visible to C, D and E, but not to A, B, F, and G: it belongs to the "scope" of the gadgets package, and is recorded in its private symbols table.

Note that the package head node is outside the package. So although the name gadgets is also a symbol, it belongs to the wider scope (the A, B, ... scope): it does not appear in the package's own symbols table, and in that sense a package cannot see its own name.

§2. Clearly a package involves more data than can be recorded in the PACKAGE_IST instruction alone, so each package has a corresponding inter_package structure. That structure is a resource belonging to the tree, so it's included in the resource list of the tree's warehouse, and has a resource ID within it. See The Warehouse.

typedef struct inter_package {
    struct inter_tree_node *package_head;
    struct inter_symbols_table *package_scope;
    int package_flags;  a bitmap of the *_PACKAGE_FLAG bits
    inter_ti resource_ID;  within the warehouse for the tree holding the package
    CLASS_DEFINITION
} inter_package;

§3. Do not call this directly to make a new package: it needs the resource ID n to exist already, and that has to be allocated. So instead you could call InterWarehouse::create_package, which calls this. But in fact what you should really do is just to generate a PACKAGE_IST instruction, because the package needs its head node too: everything will then automatically work. See PackageInstruction::new for how to do that.

inter_package *InterPackage::new(inter_tree *I, inter_ti n) {
    inter_package *pack = CREATE(inter_package);
    pack->package_head = NULL;
    pack->package_scope = NULL;
    pack->package_flags = 0;
    pack->resource_ID = n;
    return pack;
}

inter_ti InterPackage::warehouse_ID(inter_package *pack) {
    if (pack == NULL) return 0;
    return pack->resource_ID;
}

§4. inter_package structures and PACKAGE_IST instruction nodes correspond to each other in a way which exactly matches, except for the root package. For all other packages, these two operations are inverse to each other:

The root package is a very special one-off case — see Inter Trees: it does not originate from any package instruction because it represents the outermost box, that is, the top level of the hierarchy.

inter_tree_node *InterPackage::head(inter_package *pack) {
    if (pack == NULL) return NULL;
    if (InterPackage::is_a_root_package(pack)) return NULL;
    return pack->package_head;
}

inter_tree *InterPackage::tree(inter_package *pack) {
    if (pack == NULL) return NULL;
    return pack->package_head->tree;
}

§5. The following function relies on an important rule of the road: that the parent node of a package head node must be another package head node (except of course at the very top of the tree).

It follows that we can get from a package to its next outermost package (its "parent") by taking its head node, taking the node-parent of that, and then finding the package with that head.

inter_package *InterPackage::parent(inter_package *pack) {
    if (pack) {
        if (InterPackage::is_a_root_package(pack)) return NULL;
        inter_tree_node *D = InterPackage::head(pack);
        inter_tree_node *P = InterTree::parent(D);
        if (P == NULL) return NULL;
        return PackageInstruction::at_this_head(P);
    }
    return NULL;
}

§6. The baseline level for a package is the level in the hierarchy of its root node, or is 0 for the root package.

int InterPackage::baseline(inter_package *P) {
    if (P == NULL) return 0;
    if (InterPackage::is_a_root_package(P)) return 0;
    return Inode::get_level(InterPackage::head(P));
}

§7. Naming. The name of a package is by definition the name of its symbol, which can be extracted from the bytecode of its package instruction, stored at the head-node. (And the root package, which has no head-node, has the empty name.)

text_stream *InterPackage::name(inter_package *pack) {
    if (pack) {
        inter_symbol *S = PackageInstruction::name_symbol(pack);
        if (S) return InterSymbol::identifier(S);
    }
    return NULL;
}

§8. Type.

inter_symbol *InterPackage::type(inter_package *pack) {
    if (pack == NULL) return NULL;
    inter_tree_node *D = pack->package_head;
    return PackageInstruction::get_type_of(Inode::tree(D), D);
}

§9. Scope. The symbols table of local names within scope for the package.

void InterPackage::set_scope(inter_package *P, inter_symbols_table *T) {
    if (P == NULL) internal_error("null package");
    P->package_scope = T;
    if (T) T->owning_package = P;
}

§10. This function is the inverse of InterSymbolsTable::package:

inter_symbols_table *InterPackage::scope(inter_package *pack) {
    if (pack == NULL) return NULL;
    return pack->package_scope;
}

§11. The following searches recursively: i.e., not just the package's scope, but also the scope of all its subpackages. This is a slow operation, but there is no need for it to be fast: it is used only very sparingly.

inter_symbol *InterPackage::find_symbol_slowly(inter_package *P, text_stream *S) {
    inter_symbol *found = InterSymbolsTable::symbol_from_name(InterPackage::scope(P), S);
    if (found) return found;
    inter_tree_node *D = InterPackage::head(P);
    LOOP_THROUGH_INTER_CHILDREN(C, D) {
        if (Inode::is(C, PACKAGE_IST)) {
            inter_package *Q = PackageInstruction::at_this_head(C);
            found = InterPackage::find_symbol_slowly(Q, S);
            if (found) return found;
        }
    }
    return NULL;
}

§12. Packages as containers. For any node, the innermost package containing that node is called its "container"; but this is null at the root of the tree, i.e., it is never equal to the special root package.

inter_package *InterPackage::container(inter_tree_node *P) {
    if (P == NULL) return NULL;
    inter_package *pack = Inode::get_package(P);
    if (InterPackage::is_a_root_package(pack)) return NULL;
    return pack;
}

inter_symbols_table *InterPackage::scope_of(inter_tree_node *P) {
    inter_package *pack = InterPackage::container(P);
    if (pack) return pack->package_scope;
    return Inode::globals(P);
}

§13. Flags. Packages with special behaviour are marked with flags. (Flags can also be used as temporary markers when fooling with Inter code during pipeline processing.)

define ROOT_PACKAGE_FLAG          1
define FUNCTION_BODY_PACKAGE_FLAG 2
define LINKAGE_PACKAGE_FLAG       4
define PERSISTENT_PACKAGE_FLAGS   255
define USED_PACKAGE_FLAG          256
define MARK_PACKAGE_FLAG          512
int InterPackage::get_flag(inter_package *P, int f) {
    if (P == NULL) internal_error("no package");
    return (P->package_flags & f)?TRUE:FALSE;
}

void InterPackage::set_flag(inter_package *P, int f) {
    if (P == NULL) internal_error("no package");
    P->package_flags = P->package_flags | f;
}

void InterPackage::clear_flag(inter_package *P, int f) {
    if (P == NULL) internal_error("no package");
    if (P->package_flags & f) P->package_flags = P->package_flags - f;
}

§14. These are used when reading and writing binary Inter files: because of course the data in the flags must persist when files are written out and read back again.

int InterPackage::get_persistent_flags(inter_package *P) {
    if (P == NULL) internal_error("no package");
    return P->package_flags & PERSISTENT_PACKAGE_FLAGS;
}

void InterPackage::set_persistent_flags(inter_package *P, int x) {
    if (P == NULL) internal_error("no package");
    P->package_flags =
        (P->package_flags & (~PERSISTENT_PACKAGE_FLAGS)) | (x & PERSISTENT_PACKAGE_FLAGS);
}

§15. The ROOT_PACKAGE_FLAG is given only to the root package of a tree, so there will only ever be one of these in any given tree.

int InterPackage::is_a_root_package(inter_package *pack) {
    if ((pack) && (pack->package_flags & ROOT_PACKAGE_FLAG)) return TRUE;
    return FALSE;
}

void InterPackage::mark_as_a_root_package(inter_package *pack) {
    if (pack) pack->package_flags |= ROOT_PACKAGE_FLAG;
}

§16. The FUNCTION_BODY_PACKAGE_FLAG is given to function bodies. Note that the code of each function always occupies a single package, which contains nothing else. Subsidiary parts of the function — what are called "code blocks" in C, like loop bodies — are not subpackages of this: a code package has no subpackages.

int InterPackage::is_a_function_body(inter_package *pack) {
    if ((pack) && (pack->package_flags & FUNCTION_BODY_PACKAGE_FLAG)) return TRUE;
    return FALSE;
}

void InterPackage::mark_as_a_function_body(inter_package *pack) {
    if (pack) pack->package_flags |= FUNCTION_BODY_PACKAGE_FLAG;
}

§17. The LINKAGE_PACKAGE_FLAG is given only to a few top-level packages which behave differently during the transmigration process used in linking trees together. This is not the place to explain: see Large-Scale Structure (in building).

int InterPackage::is_a_linkage_package(inter_package *pack) {
    if ((pack) && (pack->package_flags & LINKAGE_PACKAGE_FLAG)) return TRUE;
    return FALSE;
}

void InterPackage::mark_as_a_linkage_package(inter_package *pack) {
    if (pack) pack->package_flags |= LINKAGE_PACKAGE_FLAG;
}

§18. MARK_PACKAGE_FLAG is ephemeral and typically used to mark that something has already been done on a given package, so that it won't be done twice. At the start of such a process, call this.

void InterPackage::unmark_all(void) {
    inter_package *pack;
    LOOP_OVER(pack, inter_package)
        InterPackage::clear_flag(pack, MARK_PACKAGE_FLAG);
}

§19. Subpackages and URLs. A package is uniquely identifiable (within its tree) by its textual URL, in the form /main/whatever/example1/this. The following goes from an inter_package to its URL, which is particularly handy for the debugging log:

void InterPackage::write_URL(OUTPUT_STREAM, inter_package *P) {
    if (P == NULL) { WRITE("<none>"); return; }
    inter_package *chain[MAX_URL_SYMBOL_NAME_DEPTH];
    int chain_length = 0;
    while (P) {
        if (chain_length >= MAX_URL_SYMBOL_NAME_DEPTH)
            internal_error("package nesting too deep");
        chain[chain_length++] = P;
        P = InterPackage::parent(P);
    }
    for (int i=chain_length-1; i>=0; i--) WRITE("/%S", InterPackage::name(chain[i]));
}

void InterPackage::log(OUTPUT_STREAM, void *vp) {
    inter_package *pack = (inter_package *) vp;
    InterPackage::write_URL(OUT, pack);
}

§20. The other direction, parsing a URL into its corresponding inter_package, is necessarily slower, and we perform it as little as possible. The following looks for a subpackage called name within the parent package P:

inter_package *InterPackage::from_name(inter_package *P, text_stream *name) {
    if (P == NULL) return NULL;
    if (P == P->package_head->tree->root_package) {
        if (Str::eq(name, I"main"))
            return LargeScale::main_package_if_it_exists(P->package_head->tree);
    } else {
        inter_symbol *S = InterSymbolsTable::symbol_from_name_not_following(
            P->package_scope, name);
        if (S) return PackageInstruction::at_this_head(S->definition);
    }
    return NULL;
}

§21. And that is the key tool needed for the following. Note that if there is an initial slash, the URL is absolute, with respect to the top of the tree; and otherwise it is construed as a single name. (So searching for this/that could never succeed: without the initial slash, this would have to be the name of a single package, and slashes can't be part of package names.)

inter_package *InterPackage::from_URL(inter_tree *I, text_stream *S) {
    if (Str::get_first_char(S) == '/') {
        inter_package *at_P = I->root_package;
        TEMPORARY_TEXT(C)
        LOOP_THROUGH_TEXT(P, S) {
            inchar32_t c = Str::get(P);
            if (c == '/') {
                if (Str::len(C) > 0) {
                    at_P = InterPackage::from_name(at_P, C);
                    if (at_P == NULL) return NULL;
                }
                Str::clear(C);
            } else {
                PUT_TO(C, c);
            }
        }
        inter_package *pack = InterPackage::from_name(at_P, C);
        DISCARD_TEXT(C)
        return pack;
    }
    return InterPackage::from_name(I->root_package, S);
}