inweb-bootstrap/foundation-module/Chapter 2/Trees.w
2020-05-09 13:05:00 +01:00

256 lines
6.9 KiB
OpenEdge ABL

[Trees::] Trees.
To provide heterogeneous tree structures, where a node can be any structure
known to the Foundation memory manager.
@h Trees and nodes.
The tree itself is really just a root node, which is initially null, so that
a tree can be empty.
=
typedef struct heterogeneous_tree {
struct tree_type *type;
struct tree_node *root;
CLASS_DEFINITION
} heterogeneous_tree;
@ =
heterogeneous_tree *Trees::new(tree_type *type) {
heterogeneous_tree *T = CREATE(heterogeneous_tree);
T->type = type;
T->root = NULL;
return T;
}
@ =
typedef struct tree_node {
struct heterogeneous_tree *owner;
struct tree_node_type *type;
struct general_pointer content;
struct tree_node *next;
struct tree_node *parent;
struct tree_node *child;
CLASS_DEFINITION
} tree_node;
@ A node is created in limbo, removed from its tree, but it is still somehow
owned by it.
=
tree_node *Trees::new_node(heterogeneous_tree *T, tree_node_type *type, general_pointer wrapping) {
if (T == NULL) internal_error("no tree");
if (wrapping.run_time_type_code == -1)
internal_error("no reference given");
if (type->required_CLASS >= 0)
if (wrapping.run_time_type_code != type->required_CLASS)
internal_error("wrong reference type");
tree_node *N = CREATE(tree_node);
N->content = wrapping;
N->owner = T;
N->type = type;
N->parent = NULL;
N->child = NULL;
N->next = NULL;
return N;
}
@ A convenient abbreviation for a common manoeuvre:
=
tree_node *Trees::new_child(tree_node *of, tree_node_type *type, general_pointer wrapping) {
tree_node *N = Trees::new_node(of->owner, type, wrapping);
Trees::make_child(N, of);
return N;
}
@h Types.
The above will provide for multiple different types of tree to be used for
different purposes. Heterogeneous trees allow the coder to make dangerously
type-unsafe structures, so we want to hedge them in with self-imposed
constraints:
=
typedef struct tree_type {
struct text_stream *name;
int (*verify_root)(struct tree_node *); /* function to vet the root node */
CLASS_DEFINITION
} tree_type;
@ =
tree_type *Trees::new_type(text_stream *name, int (*verifier)(tree_node *)) {
tree_type *T = CREATE(tree_type);
T->name = Str::duplicate(name);
T->verify_root = verifier;
return T;
}
@ Each node in a tree also has a type. Whenever the children of a node change,
they are re-verified by the |verify_children|.
=
typedef struct tree_node_type {
struct text_stream *node_type_name; /* text such as |I"INVOCATION"| */
int required_CLASS; /* if any; or negative for no restriction */
int (*verify_children)(struct tree_node *); /* function to vet the children */
CLASS_DEFINITION
} tree_node_type;
@ =
tree_node_type *Trees::new_node_type(text_stream *name, int req,
int (*verifier)(tree_node *)) {
tree_node_type *NT = CREATE(tree_node_type);
NT->node_type_name = Str::duplicate(name);
NT->required_CLASS = req;
NT->verify_children = verifier;
return NT;
}
@h Hierarchy.
A special function is needed to choose the root node; and note that this is
verified.
=
void Trees::make_root(heterogeneous_tree *T, tree_node *N) {
if (T == NULL) internal_error("no tree");
if (N == NULL) internal_error("no node");
N->owner = T;
T->root = N;
N->parent = NULL;
N->next = NULL;
if (T->type->verify_root)
if ((*(T->type->verify_root))(N) == FALSE)
internal_error("disallowed node as root");
}
void Trees::remove_root(heterogeneous_tree *T) {
if (T == NULL) internal_error("no tree");
T->root = NULL;
}
@ Otherwise, nodes are placed in a tree with respect to other nodes:
=
void Trees::make_child(tree_node *N, tree_node *of) {
if (N == NULL) internal_error("no node");
if (of == NULL) internal_error("no node");
if (N == N->owner->root) Trees::remove_root(N->owner);
N->owner = of->owner;
N->parent = of;
N->next = NULL;
if (of->child == NULL)
of->child = N;
else
for (tree_node *S = of->child; S; S = S->next)
if (S->next == NULL) {
S->next = N; break;
}
Trees::verify_children(of);
}
void Trees::make_eldest_child(tree_node *N, tree_node *of) {
if (N == NULL) internal_error("no node");
if (of == NULL) internal_error("no node");
if (N == N->owner->root) Trees::remove_root(N->owner);
N->owner = of->owner;
N->parent = of;
N->next = of->child;
of->child = N;
Trees::verify_children(of);
}
void Trees::make_sibling(tree_node *N, tree_node *of) {
if (N == NULL) internal_error("no node");
if (of == NULL) internal_error("no node");
if (N == N->owner->root) Trees::remove_root(N->owner);
if (of == of->owner->root)
internal_error("nodes cannot be siblings of the root");
N->owner = of->owner;
N->parent = of->parent;
N->next = of->next;
of->next = N;
if (of->parent) Trees::verify_children(of->parent);
}
@ Removing a node from a tree does not change its ownership -- it still belongs
to that tree.
=
void Trees::remove(tree_node *N) {
if (N == NULL) internal_error("no node");
if (N == N->owner->root) { Trees::remove_root(N->owner); return; }
tree_node *p = N->parent;
if (N->parent->child == N)
N->parent->child = N->next;
else
for (tree_node *S = N->parent->child; S; S = S->next)
if (S->next == N)
S->next = N->next;
N->parent = NULL;
N->next = NULL;
if (p) Trees::verify_children(p);
}
int Trees::verify_children(tree_node *N) {
if (N == NULL) internal_error("no node");
if (N->type->verify_children)
return (*(N->type->verify_children))(N);
return TRUE;
}
@h Traversals.
These two functions allow us to traverse the tree, visiting each node along
the way and carrying a state as we do. The distinction is that //Trees::traverse_from//
iterates from and then below the given node, but doesn't go through its siblings,
whereas //Trees::traverse// does.
Note that it is legal to traverse the empty node, and does nothing.
=
void Trees::traverse_tree(heterogeneous_tree *T,
int (*visitor)(tree_node *, void *, int L), void *state) {
if (T == NULL) internal_error("no tree");
Trees::traverse_from(T->root, visitor, state, 0);
}
void Trees::traverse_from(tree_node *N,
int (*visitor)(tree_node *, void *, int L), void *state, int L) {
if (N)
if ((*visitor)(N, state, L))
Trees::traverse(N->child, visitor, state, L+1);
}
void Trees::traverse(tree_node *N,
int (*visitor)(tree_node *, void *, int L), void *state, int L) {
for (tree_node *M = N; M; M = M->next)
if ((*visitor)(M, state, L))
Trees::traverse(M->child, visitor, state, L+1);
}
@
=
void Trees::prune_tree(heterogeneous_tree *T,
int (*visitor)(tree_node *, void *), void *state) {
if (T == NULL) internal_error("no tree");
Trees::prune_from(T->root, visitor, state);
}
void Trees::prune_from(tree_node *N,
int (*visitor)(tree_node *, void *), void *state) {
if (N) {
if ((*visitor)(N, state))
Trees::remove(N);
else
Trees::prune(N->child, visitor, state);
}
}
void Trees::prune(tree_node *N,
int (*visitor)(tree_node *, void *), void *state) {
for (tree_node *M = N, *next_M = N?(N->next):NULL; M; M = next_M, next_M = M?(M->next):NULL)
if ((*visitor)(M, state))
Trees::remove(M);
else
Trees::prune(M->child, visitor, state);
}