2022-01-31 01:49:12 +02:00
|
|
|
[InterPackage::] Packages.
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
To manage packages of inter code.
|
|
|
|
|
2022-01-31 01:49:12 +02:00
|
|
|
@h 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:
|
|
|
|
= (text as Inter)
|
|
|
|
A
|
2022-02-08 01:30:40 +02:00
|
|
|
B
|
2022-01-31 01:49:12 +02:00
|
|
|
package gadgets _paraphernalia <-- package_head node
|
|
|
|
symbol private misc ^is_electrical
|
|
|
|
C
|
2022-02-08 01:30:40 +02:00
|
|
|
D
|
2022-01-31 01:49:12 +02:00
|
|
|
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,
|
2022-02-03 01:35:38 +02:00
|
|
|
G , along with the |package| instruction itself, belong to the wider context. The
|
2022-01-31 01:49:12 +02:00
|
|
|
symbiol 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.
|
|
|
|
|
2022-02-03 01:35:38 +02:00
|
|
|
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
|
2022-01-31 01:49:12 +02:00
|
|
|
sense a package cannot see its own name.
|
|
|
|
|
|
|
|
@ 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//.
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
=
|
|
|
|
typedef struct inter_package {
|
2019-07-26 12:14:17 +03:00
|
|
|
struct inter_tree_node *package_head;
|
2019-02-05 02:44:07 +02:00
|
|
|
struct inter_symbols_table *package_scope;
|
2022-01-31 01:49:12 +02:00
|
|
|
int package_flags; /* a bitmap of the |*_PACKAGE_FLAG| bits */
|
2022-02-03 17:51:44 +02:00
|
|
|
inter_ti resource_ID; /* within the warehouse for the tree holding the package */
|
2020-05-09 15:07:39 +03:00
|
|
|
CLASS_DEFINITION
|
2019-02-05 02:44:07 +02:00
|
|
|
} inter_package;
|
|
|
|
|
2022-01-31 01:49:12 +02:00
|
|
|
@ 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.
|
2022-03-01 02:41:22 +02:00
|
|
|
See //PackageInstruction::new_package// for how to do that.
|
2019-06-10 10:30:20 +03:00
|
|
|
|
2022-01-31 01:49:12 +02:00
|
|
|
=
|
|
|
|
inter_package *InterPackage::new(inter_tree *I, inter_ti n) {
|
2019-02-05 02:44:07 +02:00
|
|
|
inter_package *pack = CREATE(inter_package);
|
2019-07-26 12:14:17 +03:00
|
|
|
pack->package_head = NULL;
|
2019-02-05 02:44:07 +02:00
|
|
|
pack->package_scope = NULL;
|
2019-06-10 10:30:20 +03:00
|
|
|
pack->package_flags = 0;
|
2022-01-31 01:49:12 +02:00
|
|
|
pack->resource_ID = n;
|
2019-02-05 02:44:07 +02:00
|
|
|
return pack;
|
|
|
|
}
|
|
|
|
|
2022-03-02 02:04:54 +02:00
|
|
|
inter_ti InterPackage::warehouse_ID(inter_package *pack) {
|
|
|
|
if (pack == NULL) return 0;
|
|
|
|
return pack->resource_ID;
|
|
|
|
}
|
|
|
|
|
2022-01-31 01:49:12 +02:00
|
|
|
@ //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:
|
|
|
|
|
2022-03-01 02:41:22 +02:00
|
|
|
(*) To get from a head node to its package, call //PackageInstruction::at_this_head//.
|
2022-02-03 01:35:38 +02:00
|
|
|
(*) To get from a package to its head node, call //InterPackage::head//.
|
2022-01-31 01:49:12 +02:00
|
|
|
|
|
|
|
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) {
|
2019-07-26 21:20:27 +03:00
|
|
|
if (pack == NULL) return NULL;
|
2022-01-31 01:49:12 +02:00
|
|
|
if (InterPackage::is_a_root_package(pack)) return NULL;
|
2019-07-27 13:16:22 +03:00
|
|
|
return pack->package_head;
|
2019-07-26 21:20:27 +03:00
|
|
|
}
|
|
|
|
|
2022-02-03 01:35:38 +02:00
|
|
|
inter_tree *InterPackage::tree(inter_package *pack) {
|
|
|
|
if (pack == NULL) return NULL;
|
|
|
|
return pack->package_head->tree;
|
|
|
|
}
|
|
|
|
|
|
|
|
@ 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;
|
2022-03-01 02:41:22 +02:00
|
|
|
return PackageInstruction::at_this_head(P);
|
2022-02-03 01:35:38 +02:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
@ 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;
|
2022-02-09 12:33:49 +02:00
|
|
|
return Inode::get_level(InterPackage::head(P));
|
2022-02-03 01:35:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@h Naming.
|
2022-02-07 00:33:07 +02:00
|
|
|
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.)
|
2022-02-03 01:35:38 +02:00
|
|
|
|
|
|
|
=
|
|
|
|
text_stream *InterPackage::name(inter_package *pack) {
|
2022-02-07 00:33:07 +02:00
|
|
|
if (pack) {
|
2022-03-01 02:41:22 +02:00
|
|
|
inter_symbol *S = PackageInstruction::name_symbol(pack);
|
2022-02-14 01:44:50 +02:00
|
|
|
if (S) return InterSymbol::identifier(S);
|
2022-02-03 01:35:38 +02:00
|
|
|
}
|
2022-02-07 00:33:07 +02:00
|
|
|
return NULL;
|
2022-02-03 01:35:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@h 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;
|
|
|
|
}
|
|
|
|
|
2022-02-03 17:51:44 +02:00
|
|
|
@ This function is the inverse of //InterSymbolsTable::package//:
|
|
|
|
|
|
|
|
=
|
2022-02-03 01:35:38 +02:00
|
|
|
inter_symbols_table *InterPackage::scope(inter_package *pack) {
|
|
|
|
if (pack == NULL) return NULL;
|
|
|
|
return pack->package_scope;
|
|
|
|
}
|
|
|
|
|
|
|
|
@ 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) {
|
2022-02-03 17:51:44 +02:00
|
|
|
inter_symbol *found = InterSymbolsTable::symbol_from_name(InterPackage::scope(P), S);
|
2022-02-03 01:35:38 +02:00
|
|
|
if (found) return found;
|
|
|
|
inter_tree_node *D = InterPackage::head(P);
|
|
|
|
LOOP_THROUGH_INTER_CHILDREN(C, D) {
|
|
|
|
if (C->W.instruction[ID_IFLD] == PACKAGE_IST) {
|
2022-03-01 02:41:22 +02:00
|
|
|
inter_package *Q = PackageInstruction::at_this_head(C);
|
2022-02-03 01:35:38 +02:00
|
|
|
found = InterPackage::find_symbol_slowly(Q, S);
|
|
|
|
if (found) return found;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
@h 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);
|
|
|
|
}
|
|
|
|
|
2022-01-31 01:49:12 +02:00
|
|
|
@h 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.)
|
|
|
|
|
|
|
|
@d ROOT_PACKAGE_FLAG 1
|
|
|
|
@d FUNCTION_BODY_PACKAGE_FLAG 2
|
|
|
|
@d LINKAGE_PACKAGE_FLAG 4
|
|
|
|
|
2022-02-14 01:44:50 +02:00
|
|
|
@d PERSISTENT_PACKAGE_FLAGS 255
|
|
|
|
|
2022-01-31 01:49:12 +02:00
|
|
|
@d USED_PACKAGE_FLAG 256
|
|
|
|
@d MARK_PACKAGE_FLAG 512
|
|
|
|
|
2022-02-03 01:35:38 +02:00
|
|
|
=
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-02-14 01:44:50 +02:00
|
|
|
@ 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);
|
|
|
|
}
|
|
|
|
|
2022-01-31 01:49:12 +02:00
|
|
|
@ 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;
|
2019-07-26 12:14:17 +03:00
|
|
|
}
|
|
|
|
|
2022-01-31 01:49:12 +02:00
|
|
|
void InterPackage::mark_as_a_root_package(inter_package *pack) {
|
|
|
|
if (pack) pack->package_flags |= ROOT_PACKAGE_FLAG;
|
2019-07-26 12:14:17 +03:00
|
|
|
}
|
|
|
|
|
2022-01-31 01:49:12 +02:00
|
|
|
@ 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;
|
2019-07-13 16:48:27 +03:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2022-01-31 01:49:12 +02:00
|
|
|
void InterPackage::mark_as_a_function_body(inter_package *pack) {
|
|
|
|
if (pack) pack->package_flags |= FUNCTION_BODY_PACKAGE_FLAG;
|
2019-07-13 16:48:27 +03:00
|
|
|
}
|
|
|
|
|
2022-01-31 01:49:12 +02:00
|
|
|
@ 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 //building: Large-Scale Structure//.
|
|
|
|
|
|
|
|
=
|
|
|
|
int InterPackage::is_a_linkage_package(inter_package *pack) {
|
2019-07-20 09:18:40 +03:00
|
|
|
if ((pack) && (pack->package_flags & LINKAGE_PACKAGE_FLAG)) return TRUE;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2022-01-31 01:49:12 +02:00
|
|
|
void InterPackage::mark_as_a_linkage_package(inter_package *pack) {
|
|
|
|
if (pack) pack->package_flags |= LINKAGE_PACKAGE_FLAG;
|
2019-07-20 09:18:40 +03:00
|
|
|
}
|
|
|
|
|
2022-01-31 01:49:12 +02:00
|
|
|
@ |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);
|
2019-07-22 12:05:02 +03:00
|
|
|
}
|
|
|
|
|
2022-02-03 01:35:38 +02:00
|
|
|
@h 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:
|
2022-01-31 01:49:12 +02:00
|
|
|
|
2022-02-03 01:35:38 +02:00
|
|
|
=
|
2022-02-03 17:51:44 +02:00
|
|
|
void InterPackage::write_URL(OUTPUT_STREAM, inter_package *P) {
|
2022-02-03 01:35:38 +02:00
|
|
|
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);
|
2019-07-13 16:48:27 +03:00
|
|
|
}
|
2022-02-03 01:35:38 +02:00
|
|
|
for (int i=chain_length-1; i>=0; i--) WRITE("/%S", InterPackage::name(chain[i]));
|
2019-07-13 16:48:27 +03:00
|
|
|
}
|
|
|
|
|
2022-02-03 01:35:38 +02:00
|
|
|
void InterPackage::log(OUTPUT_STREAM, void *vp) {
|
|
|
|
inter_package *pack = (inter_package *) vp;
|
2022-02-03 17:51:44 +02:00
|
|
|
InterPackage::write_URL(OUT, pack);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
2022-02-03 01:35:38 +02:00
|
|
|
@ The other direction, parsing a URL into its corresponding //inter_package//, is
|
2022-02-07 00:33:07 +02:00
|
|
|
necessarily slower, and we perform it as little as possible. The following looks
|
|
|
|
for a subpackage called |name| within the parent package |P|:
|
2019-07-27 13:16:22 +03:00
|
|
|
|
2022-02-03 01:35:38 +02:00
|
|
|
=
|
2022-02-03 17:51:44 +02:00
|
|
|
inter_package *InterPackage::from_name(inter_package *P, text_stream *name) {
|
2019-07-27 13:16:22 +03:00
|
|
|
if (P == NULL) return NULL;
|
2022-02-07 00:33:07 +02:00
|
|
|
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);
|
2022-03-01 02:41:22 +02:00
|
|
|
if (S) return PackageInstruction::at_this_head(S->definition);
|
2022-02-07 00:33:07 +02:00
|
|
|
}
|
2019-07-27 17:01:50 +03:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-02-03 01:35:38 +02:00
|
|
|
@ 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.)
|
|
|
|
|
|
|
|
=
|
2022-02-03 17:51:44 +02:00
|
|
|
inter_package *InterPackage::from_URL(inter_tree *I, text_stream *S) {
|
2019-07-27 17:01:50 +03:00
|
|
|
if (Str::get_first_char(S) == '/') {
|
|
|
|
inter_package *at_P = I->root_package;
|
2020-06-28 01:18:54 +03:00
|
|
|
TEMPORARY_TEXT(C)
|
2019-07-27 17:01:50 +03:00
|
|
|
LOOP_THROUGH_TEXT(P, S) {
|
|
|
|
wchar_t c = Str::get(P);
|
|
|
|
if (c == '/') {
|
|
|
|
if (Str::len(C) > 0) {
|
2022-02-03 17:51:44 +02:00
|
|
|
at_P = InterPackage::from_name(at_P, C);
|
2019-07-27 17:01:50 +03:00
|
|
|
if (at_P == NULL) return NULL;
|
|
|
|
}
|
|
|
|
Str::clear(C);
|
|
|
|
} else {
|
|
|
|
PUT_TO(C, c);
|
|
|
|
}
|
|
|
|
}
|
2022-02-03 17:51:44 +02:00
|
|
|
inter_package *pack = InterPackage::from_name(at_P, C);
|
2020-06-28 01:18:54 +03:00
|
|
|
DISCARD_TEXT(C)
|
2019-09-22 18:43:06 +03:00
|
|
|
return pack;
|
2019-07-27 17:01:50 +03:00
|
|
|
}
|
2022-02-03 17:51:44 +02:00
|
|
|
return InterPackage::from_name(I->root_package, S);
|
2019-07-27 13:16:22 +03:00
|
|
|
}
|