aio recvfrom was not null terminating the result
[jimtcl.git] / tree.tcl
blob969469a960429cafe79712dfd2f1f43e0acb1af6
1 package provide tree
3 # Broadly compatible with tcllib ::struct::tree
5 # tree create procname
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.
46 # $pt next <nodename>
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)
59 # depth first (dfs).
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.
64 # tree create handle
65 # tree destroy handle
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"} {
73 $handle _destroy
74 rename $handle ""
75 return
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
90 } else {
91 error "Usage: tree destroy|create handle"
95 # treehandle insert node ?index?
97 proc tree_insert {treeref node {index end}} {
98 # Get the parent node
99 set parentref [_tree_getnoderef $treeref $node]
101 # Make a new 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
109 # Add it to the tree
110 _tree_update_tree $treeref tree {
111 set tree($childname) $childref
114 return $childname
117 # treehandle set node key value
119 proc tree_set {treeref node key value} {
120 _tree_update_node $treeref $node n {
121 set n($key) $value
123 return $value
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
131 set result $n($key)
133 return $result
136 # treehandle get node key
138 proc tree_get {treeref node key} {
139 set n [_tree_getnode $treeref $node]
141 return $n($key)
144 # treehandle keyexists node key
146 proc tree_keyexists {treeref node key} {
147 set n [_tree_getnode $treeref $node]
148 info exists n($key)
151 # treehandle depth node
153 proc tree_depth {treeref node} {
154 set n [_tree_getnode $treeref $node]
155 return $n(.depth)
158 # treehandle parent node
160 proc tree_parent {treeref node} {
161 set n [_tree_getnode $treeref $node]
162 return $n(.parent)
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]
176 set result {}
177 foreach child $n(.children) {
178 set c [getref $child]
179 lappend result $c(.name)
181 return $result
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]
190 incr i
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]
199 # set up vars
200 lassign $vars actionvar namevar
202 if {$type ne "child"} {
203 upvar $namevar name
204 upvar $actionvar action
206 # Enter this node
207 set name $node
208 set action enter
210 uplevel 1 $code
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"} {
234 # Exit this node
235 set name $node
236 set action exit
238 uplevel 1 $code
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 {
251 setref $noderef {}
253 setref $treeref {}
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}} {
265 if {$parent eq ""} {
266 # The root node
267 set name root
268 set depth 0
269 set parentname ""
270 } else {
271 set parentnode [getref $parent]
273 set name node$nodeid
274 incr nodeid
275 set depth $parentnode(.depth)
276 incr 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} {
301 upvar $varname n
303 # Get a reference to the node
304 set ref [_tree_getnoderef $treeref $node]
306 # Get the node itself
307 set n [getref $ref]
309 uplevel 1 $code
311 # And update the reference
312 setref $ref $n
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} {
320 upvar $varname t
322 # Get the tree value
323 set t [getref $treeref]
325 # Possibly modify it
326 uplevel 1 $code
328 # And update the reference
329 setref $treeref $t