2020-04-17 18:18:40 +00:00
|
|
|
[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;
|
2020-05-09 12:05:00 +00:00
|
|
|
CLASS_DEFINITION
|
2020-04-17 18:18:40 +00:00
|
|
|
} 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;
|
2020-05-09 12:05:00 +00:00
|
|
|
CLASS_DEFINITION
|
2020-04-17 18:18:40 +00:00
|
|
|
} 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");
|
2020-05-09 12:05:00 +00:00
|
|
|
if (type->required_CLASS >= 0)
|
|
|
|
if (wrapping.run_time_type_code != type->required_CLASS)
|
2020-04-17 18:18:40 +00:00
|
|
|
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 */
|
2020-05-09 12:05:00 +00:00
|
|
|
CLASS_DEFINITION
|
2020-04-17 18:18:40 +00:00
|
|
|
} 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"| */
|
2020-05-09 12:05:00 +00:00
|
|
|
int required_CLASS; /* if any; or negative for no restriction */
|
2020-04-17 18:18:40 +00:00
|
|
|
int (*verify_children)(struct tree_node *); /* function to vet the children */
|
2020-05-09 12:05:00 +00:00
|
|
|
CLASS_DEFINITION
|
2020-04-17 18:18:40 +00:00
|
|
|
} 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);
|
2020-05-09 12:05:00 +00:00
|
|
|
NT->required_CLASS = req;
|
2020-04-17 18:18:40 +00:00
|
|
|
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;
|
2020-04-20 22:26:08 +00:00
|
|
|
if (p) Trees::verify_children(p);
|
2020-04-17 18:18:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2020-04-18 23:51:19 +00:00
|
|
|
int (*visitor)(tree_node *, void *, int L), void *state) {
|
2020-04-17 18:18:40 +00:00
|
|
|
if (T == NULL) internal_error("no tree");
|
|
|
|
Trees::traverse_from(T->root, visitor, state, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Trees::traverse_from(tree_node *N,
|
2020-04-18 23:51:19 +00:00
|
|
|
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);
|
2020-04-17 18:18:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Trees::traverse(tree_node *N,
|
2020-04-18 23:51:19 +00:00
|
|
|
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);
|
2020-04-17 18:18:40 +00:00
|
|
|
}
|
2020-04-20 22:26:08 +00:00
|
|
|
|
|
|
|
@
|
|
|
|
|
|
|
|
=
|
|
|
|
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);
|
|
|
|
}
|