3 # Broadly compatible with tcllib ::struct::tree
7 # Create a tree named $procname
8 # This automatically creates a node named "root"
10 # tree destroy procname
12 # Destroy the tree and all it's nodes
14 # $pt set <nodename> <key> <value>
16 # Set the value for the given key
18 # $pt lappend <nodename> <key> <value> ...
20 # Append to the (list) value(s) for the given key, or set if not yet set
22 # $pt keyexists <nodename> <key>
24 # Returns 1 if the given key exists
26 # $pt get <nodename> <key>
28 # Returns the value associated with the given key
30 # $pt depth <nodename>
32 # Returns the depth of the given node. The depth of "root" is 0.
34 # $pt parent <nodename>
36 # Returns the name of the parent node, or "" for the root node.
38 # $pt numchildren <nodename>
40 # Returns the number of child nodes.
42 # $pt children <nodename>
44 # Returns a list of the child nodes.
48 # Returns the next sibling node, or "" if none.
50 # $pt insert <nodename> <index>
52 # Add a new child node to the given node.
53 # Currently the node is always added at the end (index=end)
54 # Returns the name of the newly added node
56 # $pt walk <nodename> dfs|bfs {actionvar nodevar} <code>
58 # Walks the tree starting from the given node, either breadth first (bfs)
60 # The value "enter" or "exit" is stored in variable $actionvar
61 # The name of each node is stored in $nodevar.
62 # The script $code is evaluated twice for each node, on entry and exit.
67 proc tree
{action handle
} {
68 # A tree is a dictionary of (name, noderef)
69 # The name for the root node is always "root",
70 # and other nodes are automatically named "node1", "node2", etc.
72 if {$action eq
"destroy"} {
76 } elseif
{$action eq
"create"} {
77 # Create the root node
78 lassign
[_tree_makenode
""] dummy rootref
80 # Create the tree containing one node
81 set tree
[dict create root
$rootref]
83 # And create a reference to a tree dictionary
84 set treeref
[ref
$tree tree
]
86 proc $handle {command args
} {treeref
} {
87 #puts "You invoked [list treehandle $command $args]"
88 tailcall tree_
$command $treeref {*}$args
91 error "Usage: tree destroy|create handle"
95 # treehandle insert node ?index?
97 proc tree_insert
{treeref node
{index end
}} {
99 set parentref
[_tree_getnoderef
$treeref $node]
102 lassign
[_tree_makenode
$parentref] childname childref
104 # Add it to the list of children in the parent node
105 _tree_update_node
$treeref $node parent
{
106 lappend parent
(.children
) $childref
110 _tree_update_tree
$treeref tree
{
111 set tree
($childname) $childref
117 # treehandle set node key value
119 proc tree_set
{treeref node key value
} {
120 _tree_update_node
$treeref $node n
{
126 # treehandle lappend node key value
128 proc tree_lappend
{treeref node key args
} {
129 _tree_update_node
$treeref $node n
{
130 lappend n
($key) {expand
}$args
136 # treehandle get node key
138 proc tree_get
{treeref node key
} {
139 set n
[_tree_getnode
$treeref $node]
144 # treehandle keyexists node key
146 proc tree_keyexists
{treeref node key
} {
147 set n
[_tree_getnode
$treeref $node]
151 # treehandle depth node
153 proc tree_depth
{treeref node
} {
154 set n
[_tree_getnode
$treeref $node]
158 # treehandle parent node
160 proc tree_parent
{treeref node
} {
161 set n
[_tree_getnode
$treeref $node]
165 # treehandle numchildren node
167 proc tree_numchildren
{treeref node
} {
168 set n
[_tree_getnode
$treeref $node]
169 llength $n(.children
)
172 # treehandle children node
174 proc tree_children
{treeref node
} {
175 set n
[_tree_getnode
$treeref $node]
177 foreach child
$n(.children
) {
178 set c
[getref
$child]
179 lappend result
$c(.name
)
184 # treehandle next node
186 proc tree_next
{treeref node
} {
187 set parent
[tree_parent
$treeref $node]
188 set siblings
[tree_children
$treeref $parent]
189 set i
[lsearch $siblings $node]
191 return [lindex $siblings $i]
194 # treehandle walk node bfs|dfs {action loopvar} <code>
196 proc tree_walk
{treeref node type vars code
} {
197 set n
[_tree_getnode
$treeref $node]
200 lassign
$vars actionvar namevar
202 if {$type ne
"child"} {
204 upvar $actionvar action
213 if {$type eq
"dfs"} {
214 # Depth-first so do the children
215 foreach childref
$n(.children
) {
216 set child
[getref
$childref]
217 uplevel 1 [list tree_walk
$treeref $child(.name
) $type $vars $code]
219 } elseif
{$type ne
"none"} {
220 # Breadth-first so do the children to one level only
221 foreach childref
$n(.children
) {
222 set child
[getref
$childref]
223 uplevel 1 [list tree_walk
$treeref $child(.name
) none
$vars $code]
226 # Now our grandchildren
227 foreach childref
$n(.children
) {
228 set child
[getref
$childref]
229 uplevel 1 [list tree_walk
$treeref $child(.name
) child
$vars $code]
233 if {$type ne
"child"} {
243 # INTERNAL procedures below this point
246 # Discards all the nodes
248 proc tree__destroy
{treeref
} {
249 set tree
[getref
$treeref]
250 foreach {nodename noderef
} $tree {
257 # Make a new child node of the parent
259 # Note that this does *not* add the node
260 # to the parent or to the tree
262 # Returns a list of {nodename noderef}
264 proc _tree_makenode
{parent
} {{nodeid
1}} {
271 set parentnode
[getref
$parent]
275 set depth
$parentnode(.depth
)
277 set parentname
$parentnode(.name
)
280 # Return a list of name, reference
281 list $name [ref
[list .name
$name .depth
$depth .parent
$parentname .children
{}] node
]
284 # Return the node (dictionary value) with the given name
286 proc _tree_getnode
{treeref node
} {
287 getref
[dict get
[getref
$treeref] $node]
290 # Return the noderef with the given name
292 proc _tree_getnoderef
{treeref node
} {
293 dict get
[getref
$treeref] $node
296 # Set a dictionary value named $varname in the parent context,
297 # evaluate $code, and then store any changes to
298 # the node (via $varname) back to the node
300 proc _tree_update_node
{treeref node varname code
} {
303 # Get a reference to the node
304 set ref
[_tree_getnoderef
$treeref $node]
306 # Get the node itself
311 # And update the reference
315 # Set a dictionary value named $varname in the parent context,
316 # evaluate $code, and then store any changes to
317 # the tree (via $varname) back to the tree
319 proc _tree_update_tree
{treeref varname code
} {
323 set t
[getref
$treeref]
328 # And update the reference