[InterTree::] Inter Trees. To manage tree structures of inter code, and manage the movement of nodes within these trees. @ An //inter_tree// expresses a single program: see //What This Module Does// for more. At first sight, it's a very small object, but |root_node| leads to a massive tree structure, and the |inter_warehouse| and |building_site| compoments can also be huge. Note that the latter is managed entirely by the //building// module, but that everything else here is ours. = typedef struct inter_tree { struct inter_tree_node *root_node; struct inter_package *root_package; struct inter_warehouse *housed; unsigned int history_bits; struct building_site site; CLASS_DEFINITION } inter_tree; @ = inter_tree *InterTree::new(void) { inter_tree *I = CREATE(inter_tree); @; @; I->history_bits = 0; InterTree::set_history(I, CREATED_ITHBIT); BuildingModule::clear_data(I); return I; } @ This must be done first, since we can't make symbols tables without it: @ = I->housed = Inter::Warehouse::new(); @ Now a delicate little dance. The entire content of the tree is contained inside a special "root package". Packages are visible from the outside but not the inside, so the root package is effectively invisible: nothing is outside it. This is why it has no name, and is never referred to by Inter code written out in textual form. In any case, special restrictions apply to it, and calling //Inter::Packages::make_rootlike// causes those to be enforced. Every package has a "head node": the content of the package will be the children and descendants of that node. The root node for the tree is by definition the head node for the root package of the tree. |N| here is the warehouse ID number for the global symbols table of the tree, which is by definition the symbols table for the root package. @ = inter_ti N = Inter::Warehouse::create_symbols_table(I->housed); inter_symbols_table *globals = Inter::Warehouse::get_symbols_table(I->housed, N); inter_ti root_package_ID = Inter::Warehouse::create_package(I->housed, I); I->root_package = Inter::Warehouse::get_package(I->housed, root_package_ID); I->root_node = Inode::new_root_node(I->housed, I); I->root_package->package_head = I->root_node; Inter::Packages::make_rootlike(I->root_package); Inter::Packages::set_scope(I->root_package, globals); I->root_node->package = I->root_package; Inter::Warehouse::attribute_resource(I->housed, N, I->root_package); @ = inter_package *InterTree::root_package(inter_tree *I) { if (I) return I->root_package; return NULL; } inter_symbols_table *InterTree::global_scope(inter_tree *I) { return Inter::Packages::scope(I->root_package); } inter_warehouse *InterTree::warehouse(inter_tree *I) { return I->housed; } @h Walking along branches of the tree. For operations on individual nodes, including how to create them, see //Inter Nodes//. Here, we provide functions for walking through the nodes, making use of the tree structure between them. = inter_tree_node *InterTree::previous(inter_tree_node *F) { if (F == NULL) return NULL; return F->previous_itn; } inter_tree_node *InterTree::next(inter_tree_node *F) { if (F == NULL) return NULL; return F->next_itn; } inter_tree_node *InterTree::first_child(inter_tree_node *F) { if (F == NULL) return NULL; return F->first_child_itn; } inter_tree_node *InterTree::second_child(inter_tree_node *P) { if (P == NULL) return NULL; P = P->first_child_itn; if (P == NULL) return NULL; return P->next_itn; } inter_tree_node *InterTree::third_child(inter_tree_node *P) { if (P == NULL) return NULL; P = P->first_child_itn; if (P == NULL) return NULL; P = P->next_itn; if (P == NULL) return NULL; return P->next_itn; } inter_tree_node *InterTree::fourth_child(inter_tree_node *P) { if (P == NULL) return NULL; P = P->first_child_itn; if (P == NULL) return NULL; P = P->next_itn; if (P == NULL) return NULL; P = P->next_itn; if (P == NULL) return NULL; return P->next_itn; } inter_tree_node *InterTree::fifth_child(inter_tree_node *P) { if (P == NULL) return NULL; P = P->first_child_itn; if (P == NULL) return NULL; P = P->next_itn; if (P == NULL) return NULL; P = P->next_itn; if (P == NULL) return NULL; P = P->next_itn; if (P == NULL) return NULL; return P->next_itn; } inter_tree_node *InterTree::sixth_child(inter_tree_node *P) { if (P == NULL) return NULL; P = P->first_child_itn; if (P == NULL) return NULL; P = P->next_itn; if (P == NULL) return NULL; P = P->next_itn; if (P == NULL) return NULL; P = P->next_itn; if (P == NULL) return NULL; P = P->next_itn; if (P == NULL) return NULL; return P->next_itn; } inter_tree_node *InterTree::last_child(inter_tree_node *F) { if (F == NULL) return NULL; return F->last_child_itn; } inter_tree_node *InterTree::parent(inter_tree_node *F) { if (F == NULL) return NULL; return F->parent_itn; } @ Accessing child nodes one by one -- //InterTree::third_child//, etc. -- can only take you so far. Here's a convenient fast way to loop through: @d LOOP_THROUGH_INTER_CHILDREN(F, P) for (inter_tree_node *F = InterTree::first_child(P); F; F = InterTree::next(F)) @ If we want to do this more slowly, making sure that severing the current node won't cause the loop to terminate early: @d PROTECTED_LOOP_THROUGH_INTER_CHILDREN(F, P) for (inter_tree_node *F = InterTree::first_child(P), *FN = F?(InterTree::next(F)):NULL; F; F = FN, FN = FN?(InterTree::next(FN)):NULL) @h Traversing an entire tree. The following traverses through all of the root nodes of a tree, calling the |visitor| function on each node matching the given type filter. If |filter| is 0, that's every node; if it is something like |PACKAGE_IST|, then it visits only nodes of type |PACKAGE_IST|; if it is |-PACKAGE_IST|, it visits only nodes of types other than |PACKAGE_IST|. |state| is opaque to us, and is a way for the caller to have persistent state across visits to different nodes. = void InterTree::traverse_root_only(inter_tree *from, void (*visitor)(inter_tree *, inter_tree_node *, void *), void *state, int filter) { PROTECTED_LOOP_THROUGH_INTER_CHILDREN(P, from->root_node) { if ((filter == 0) || ((filter > 0) && (P->W.data[ID_IFLD] == (inter_ti) filter)) || ((filter < 0) && (P->W.data[ID_IFLD] != (inter_ti) -filter))) (*visitor)(from, P, state); } } @ This is similar, but begins at the root of the package |mp|, and recurses downwards through it and all its subpackages. If |mp| is null, recursion is from the tree's |/main| package. Note that this does not visit nodes at the root level, for which see above. The same filter conventions apply. = void InterTree::traverse(inter_tree *from, void (*visitor)(inter_tree *, inter_tree_node *, void *), void *state, inter_package *mp, int filter) { if (mp == NULL) mp = LargeScale::main_package_if_it_exists(from); if (mp) { inter_tree_node *D = Inter::Packages::definition(mp); if ((filter == 0) || ((filter > 0) && (D->W.data[ID_IFLD] == (inter_ti) filter)) || ((filter < 0) && (D->W.data[ID_IFLD] != (inter_ti) -filter))) (*visitor)(from, D, state); InterTree::traverse_r(from, D, visitor, state, filter); } } void InterTree::traverse_r(inter_tree *from, inter_tree_node *P, void (*visitor)(inter_tree *, inter_tree_node *, void *), void *state, int filter) { PROTECTED_LOOP_THROUGH_INTER_CHILDREN(C, P) { if ((filter == 0) || ((filter > 0) && (C->W.data[ID_IFLD] == (inter_ti) filter)) || ((filter < 0) && (C->W.data[ID_IFLD] != (inter_ti) -filter))) (*visitor)(from, C, state); InterTree::traverse_r(from, C, visitor, state, filter); } } @ It is also convenient to provide a way to loop through the subpackages of a package. @d LOOP_THROUGH_SUBPACKAGES(entry, pack, ptype) inter_symbol *pack##wanted = (pack)?(LargeScale::package_type(pack->package_head->tree, ptype)):NULL; if (pack) LOOP_THROUGH_INTER_CHILDREN(C, Inter::Packages::definition(pack)) if ((C->W.data[ID_IFLD] == PACKAGE_IST) && (entry = Inter::Package::defined_by_frame(C)) && (Inter::Packages::type(entry) == pack##wanted)) @ As a demonstration of this in action: = int InterTree::no_subpackages(inter_package *pack, text_stream *ptype) { int N = 0; if (pack) { inter_package *entry; LOOP_THROUGH_SUBPACKAGES(entry, pack, ptype) N++; } return N; } @h History of a tree. In 1964, a Nevada geologist felled a bristlecone pine and was dismayed to find that it contained 4862 rings, and had therefore germinated in around 2900 BC. Inter trees also record their history, though can safely accommodate only 32 different events, identified as flag bits 0 to 31. Calling |InterTree::set_history(I, B)| sets flag |B|; |InterTree::test_history| then tests it. There is purposely no way to clear these flags once set. They should only be used to record that irrevocable, one-time-only, things have been done. @e CREATED_ITHBIT from 0 = void InterTree::set_history(inter_tree *I, int bit) { I->history_bits |= (1 << bit); } int InterTree::test_history(inter_tree *I, int bit) { if (I->history_bits & (1 << bit)) return TRUE; return FALSE; }