00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 package require Tcl 8.2
00013 package require struct::list
00014
00015 namespace ::struct::tree {
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 variable counter 0
00033
00034
00035 namespace export tree_tcl
00036 }
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049 ret ::struct::tree::tree_tcl (type args) {
00050 variable counter
00051
00052 set src {}
00053 set srctype {}
00054
00055 switch -exact -- [llength [info level 0]] {
00056 1 {
00057 # Missing name, generate one.
00058 incr counter
00059 set name "tree${counter}"
00060 }
00061 2 {
00062 # Standard call. New empty tree.
00063 set name [lindex $args 0]
00064 }
00065 4 {
00066 # Copy construction.
00067 foreach {name as src} $args break
00068 switch -exact -- $as {
00069 = - := - as {
00070 set srctype tree
00071 }
00072 deserialize {
00073 set srctype serial
00074 }
00075 default {
00076 return -code error \
00077 "wrong # args: should be \"tree ?name ?=|:=|as|deserialize source??\""
00078 }
00079 }
00080 }
00081 default {
00082 # Error.
00083 return -code error \
00084 "wrong # args: should be \"tree ?name ?=|:=|as|deserialize source??\""
00085 }
00086 }
00087
00088 # FIRST, qualify the name.
00089 if {![string match "::*" $name]} {
00090 # Get caller's namespace; append :: if not global namespace.
00091 set ns [uplevel 1 [list namespace current]]
00092 if {"::" != $ns} {
00093 append ns "::"
00094 }
00095
00096 set name "$ns$name"
00097 }
00098 if {[llength [info commands $name]]} {
00099 return -code error \
00100 "command \"$name\" already exists, unable to create tree"
00101 }
00102
00103 # Set up the namespace for the object,
00104 # identical to the object command.
00105 namespace eval $name {
00106 variable rootname
00107 set rootname root
00108
00109 # Set up root node's child list
00110 variable children
00111 set children(root) [list]
00112
00113 # Set root node's parent
00114 variable parent
00115 set parent(root) [list]
00116
00117 # Set up the node attribute mapping
00118 variable attribute
00119 array set attribute {}
00120
00121 # Set up a counter for use in creating unique node names
00122 variable nextUnusedNode
00123 set nextUnusedNode 1
00124
00125 # Set up a counter for use in creating node attribute arrays.
00126 variable nextAttr
00127 set nextAttr 0
00128 }
00129
00130 # Create the command to manipulate the tree
00131 interp alias {} $name {} ::struct::tree::TreeProc $name
00132
00133 # Automatic execution of assignment if a source
00134 # is present.
00135 if {$src != {}} {
00136 switch -exact -- $srctype {
00137 tree {
00138 set code [catch {_= $name $src} msg]
00139 if {$code} {
00140 namespace delete $name
00141 interp alias {} $name {}
00142 return -code $code -errorinfo $::errorInfo -errorcode $::errorCode $msg
00143 }
00144 }
00145 serial {
00146 set code [catch {_deserialize $name $src} msg]
00147 if {$code} {
00148 namespace delete $name
00149 interp alias {} $name {}
00150 return -code $code -errorinfo $::errorInfo -errorcode $::errorCode $msg
00151 }
00152 }
00153 default {
00154 return -code error \
00155 "Internal error, illegal srctype \"$srctype\""
00156 }
00157 }
00158 }
00159
00160 # Give object to caller for use.
00161 return $name
00162 }
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181 ret ::struct::tree::prune_tcl () {
00182 return -code 5
00183 }
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200 ret ::struct::tree::TreeProc (type name , optional cmd ="" , type args) {
00201 # Do minimal args checks here
00202 if { [llength [info level 0]] == 2 } {
00203 return -code error "wrong # args: should be \"$name option ?arg arg ...?\""
00204 }
00205
00206 # Split the args into command and args components
00207 set sub _$cmd
00208 if { [llength [info commands ::struct::tree::$sub]] == 0 } {
00209 set optlist [lsort [info commands ::struct::tree::_*]]
00210 set xlist {}
00211 foreach p $optlist {
00212 set p [namespace tail $p]
00213 lappend xlist [string range $p 1 end]
00214 }
00215 set optlist [linsert [join $xlist ", "] "end-1" "or"]
00216 return -code error \
00217 "bad option \"$cmd\": must be $optlist"
00218 }
00219
00220 set code [catch {uplevel 1 [linsert $args 0 ::struct::tree::$sub $name]} result]
00221
00222 if {$code == 1} {
00223 return -errorinfo [ErrorInfoAsCaller uplevel $sub] \
00224 -errorcode $::errorCode -code error $result
00225 } elseif {$code == 2} {
00226 return -code $code $result
00227 }
00228 return $result
00229 }
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244 ret ::struct::tree::_= (type name , type source) {
00245 _deserialize $name [$source serialize]
00246 return
00247 }
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261 ret ::struct::tree::_--> (type name , type dest) {
00262 $dest deserialize [_serialize $name]
00263 return
00264 }
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279 ret ::struct::tree::_ancestors (type name , type node) {
00280 if { ![_exists $name $node] } {
00281 return -code error "node \"$node\" does not exist in tree \"$name\""
00282 }
00283
00284 variable ${name}::parent
00285 set a {}
00286 while {[info exists parent($node)]} {
00287 set node $parent($node)
00288 if {$node == {}} break
00289 lappend a $node
00290 }
00291 return $a
00292 }
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305 ret ::struct::tree::_attr (type name , type key , type args) {
00306 # Syntax:
00307 #
00308 # t attr key
00309 # t attr key -nodes {nodelist}
00310 # t attr key -glob nodepattern
00311 # t attr key -regexp nodepattern
00312
00313 variable ${name}::attribute
00314
00315 set usage "wrong # args: should be \"[list $name] attr key ?-nodes list|-glob pattern|-regexp pattern?\""
00316 if {([llength $args] != 0) && ([llength $args] != 2)} {
00317 return -code error $usage
00318 } elseif {[llength $args] == 0} {
00319 # This automatically restricts the list
00320 # to nodes which can have the attribute
00321 # in question.
00322
00323 set nodes [array names attribute]
00324 } else {
00325 # Determine a list of nodes to look at
00326 # based on the chosen restriction.
00327
00328 foreach {mode value} $args break
00329 switch -exact -- $mode {
00330 -nodes {
00331 # This is the only branch where we have to
00332 # perform an explicit restriction to the
00333 # nodes which have attributes.
00334 set nodes {}
00335 foreach n $value {
00336 if {![info exists attribute($n)]} continue
00337 lappend nodes $n
00338 }
00339 }
00340 -glob {
00341 set nodes [array names attribute $value]
00342 }
00343 -regexp {
00344 set nodes {}
00345 foreach n [array names attribute] {
00346 if {![regexp -- $value $n]} continue
00347 lappend nodes $n
00348 }
00349 }
00350 default {
00351 return -code error $usage
00352 }
00353 }
00354 }
00355
00356 # Without possibly matching nodes
00357 # the result has to be empty.
00358
00359 if {![llength $nodes]} {
00360 return {}
00361 }
00362
00363 # Now locate matching keys and their values.
00364
00365 set result {}
00366 foreach n $nodes {
00367 upvar ${name}::$attribute($n) data
00368 if {[info exists data($key)]} {
00369 lappend result $n $data($key)
00370 }
00371 }
00372
00373 return $result
00374 }
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388 ret ::struct::tree::_deserialize (type name , type serial) {
00389 # As we destroy the original tree as part of
00390 # the copying process we don't have to deal
00391 # with issues like node names from the new tree
00392 # interfering with the old ...
00393
00394 # I. Get the serialization of the source tree
00395 # and check it for validity.
00396
00397 CheckSerialization $serial attr p c rn
00398
00399 # Get all the relevant data into the scope
00400
00401 variable ${name}::rootname
00402 variable ${name}::children
00403 variable ${name}::parent
00404 variable ${name}::attribute
00405 variable ${name}::nextAttr
00406
00407 # Kill the existing parent/children information and insert the new
00408 # data in their place.
00409
00410 foreach n [array names parent] {
00411 unset parent($n) children($n)
00412 }
00413 array set parent [array get p]
00414 array set children [array get c]
00415 unset p c
00416
00417 set nextAttr 0
00418 foreach a [array names attribute] {
00419 unset ${name}::$attribute($a)
00420 }
00421 foreach n [array names attr] {
00422 GenAttributeStorage $name $n
00423 array set ${name}::$attribute($n) $attr($n)
00424 }
00425
00426 set rootname $rn
00427
00428 ## Debug ## Dump internals ...
00429 if {0} {
00430 puts "___________________________________ $name"
00431 puts $rootname
00432 parray children
00433 parray parent
00434 parray attribute
00435 puts ___________________________________
00436 }
00437 return
00438 }
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451 ret ::struct::tree::_children (type name , type args) {
00452 # args := ?-all? node ?filter cmdprefix?
00453
00454 # '-all' implies that not only the direct children of the
00455 # node, but all their children, and so on, are returned.
00456 #
00457 # 'filter cmd' implies that only those nodes in the result list
00458 # which pass the test 'cmd' are placed into the final result.
00459
00460 set usage "wrong # args: should be \"[list $name] children ?-all? node ?filter cmd?\""
00461
00462 if {([llength $args] < 1) || ([llength $args] > 4)} {
00463 return -code error $usage
00464 }
00465 if {[string equal [lindex $args 0] -all]} {
00466 set all 1
00467 set args [lrange $args 1 end]
00468 } else {
00469 set all 0
00470 }
00471
00472 # args := node ?filter cmdprefix?
00473
00474 if {([llength $args] != 1) && ([llength $args] != 3)} {
00475 return -code error $usage
00476 }
00477 if {[llength $args] == 3} {
00478 foreach {node _const_ cmd} $args break
00479 if {![string equal $_const_ filter] || ![llength $cmd]} {
00480 return -code error $usage
00481 }
00482 } else {
00483 set node [lindex $args 0]
00484 set cmd {}
00485 }
00486
00487 if { ![_exists $name $node] } {
00488 return -code error "node \"$node\" does not exist in tree \"$name\""
00489 }
00490
00491 if {$all} {
00492 set result [DescendantsCore $name $node]
00493 } else {
00494 variable ${name}::children
00495 set result $children($node)
00496 }
00497
00498 if {[llength $cmd]} {
00499 lappend cmd $name
00500 set result [uplevel 1 [list ::struct::list filter $result $cmd]]
00501 }
00502
00503 return $result
00504 }
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519 ret ::struct::tree::_cut (type name , type node) {
00520 variable ${name}::rootname
00521
00522 if { [string equal $node $rootname] } {
00523 # Can't delete the special root node
00524 return -code error "cannot cut root node"
00525 }
00526
00527 if { ![_exists $name $node] } {
00528 return -code error "node \"$node\" does not exist in tree \"$name\""
00529 }
00530
00531 variable ${name}::parent
00532 variable ${name}::children
00533
00534 # Locate our parent, children and our location in the parent
00535 set parentNode $parent($node)
00536 set childNodes $children($node)
00537
00538 set index [lsearch -exact $children($parentNode) $node]
00539
00540 # Excise this node from the parent list,
00541 set newChildren [lreplace $children($parentNode) $index $index]
00542
00543 # Put each of the children of $node into the parent's children list,
00544 # in the place of $node, and update the parent pointer of those nodes.
00545 foreach child $childNodes {
00546 set newChildren [linsert $newChildren $index $child]
00547 set parent($child) $parentNode
00548 incr index
00549 }
00550 set children($parentNode) $newChildren
00551
00552 KillNode $name $node
00553 return
00554 }
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568 ret ::struct::tree::_delete (type name , type node) {
00569 variable ${name}::rootname
00570 if { [string equal $node $rootname] } {
00571 # Can't delete the special root node
00572 return -code error "cannot delete root node"
00573 }
00574 if {![_exists $name $node]} {
00575 return -code error "node \"$node\" does not exist in tree \"$name\""
00576 }
00577
00578 variable ${name}::children
00579 variable ${name}::parent
00580
00581 # Remove this node from its parent's children list
00582 set parentNode $parent($node)
00583 set index [lsearch -exact $children($parentNode) $node]
00584 ldelete children($parentNode) $index
00585
00586 # Yes, we could use the stack structure implemented in ::struct::stack,
00587 # but it's slower than inlining it. Since we don't need a sophisticated
00588 # stack, don't bother.
00589 set st [list]
00590 foreach child $children($node) {
00591 lappend st $child
00592 }
00593
00594 KillNode $name $node
00595
00596 while {[llength $st] > 0} {
00597 set node [lindex $st end]
00598 ldelete st end
00599 foreach child $children($node) {
00600 lappend st $child
00601 }
00602
00603 KillNode $name $node
00604 }
00605 return
00606 }
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619 ret ::struct::tree::_depth (type name , type node) {
00620 if { ![_exists $name $node] } {
00621 return -code error "node \"$node\" does not exist in tree \"$name\""
00622 }
00623 variable ${name}::parent
00624 variable ${name}::rootname
00625 set depth 0
00626 while { ![string equal $node $rootname] } {
00627 incr depth
00628 set node $parent($node)
00629 }
00630 return $depth
00631 }
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644 ret ::struct::tree::_descendants (type name , type node , type args) {
00645 # children -all sucessor, allows filtering.
00646
00647 set usage "wrong # args: should be \"[list $name] descendants node ?filter cmd?\""
00648
00649 if {[llength $args] > 2} {
00650 return -code error $usage
00651 } elseif {[llength $args] == 2} {
00652 foreach {_const_ cmd} $args break
00653 if {![string equal $_const_ filter] || ![llength $cmd]} {
00654 return -code error $usage
00655 }
00656 } else {
00657 set cmd {}
00658 }
00659
00660 if { ![_exists $name $node] } {
00661 return -code error "node \"$node\" does not exist in tree \"$name\""
00662 }
00663
00664 set result [DescendantsCore $name $node]
00665
00666 if {[llength $cmd]} {
00667 lappend cmd $name
00668 set result [uplevel 1 [list ::struct::list filter $result $cmd]]
00669 }
00670
00671 return $result
00672 }
00673
00674 ret ::struct::tree::DescendantsCore (type name , type node) {
00675 # CORE for listing of node descendants.
00676 # No checks ...
00677 # No filtering ...
00678
00679 variable ${name}::children
00680
00681 # New implementation. Instead of keeping a second, and explicit,
00682 # list of pending nodes to shift through (= copying of array data
00683 # around), we reuse the result list for that, using a counter and
00684 # direct access to list elements to keep track of what nodes have
00685 # not been handled yet. This eliminates a whole lot of array
00686 # copying within the list implementation in the Tcl core. The
00687 # result is unchanged, i.e. the nodes are in the same order as
00688 # before.
00689
00690 set result $children($node)
00691 set at 0
00692
00693 while {$at < [llength $result]} {
00694 set n [lindex $result $at]
00695 incr at
00696 foreach c $children($n) {
00697 lappend result $c
00698 }
00699 }
00700
00701 return $result
00702 }
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714 ret ::struct::tree::_destroy (type name) {
00715 namespace delete $name
00716 interp alias {} $name {}
00717 }
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730 ret ::struct::tree::_exists (type name , type node) {
00731 return [info exists ${name}::parent($node)]
00732 }
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746 ret ::struct::tree::_get (type name , type node , type key) {
00747 if {![_exists $name $node]} {
00748 return -code error "node \"$node\" does not exist in tree \"$name\""
00749 }
00750
00751 variable ${name}::attribute
00752 if {![info exists attribute($node)]} {
00753 # No attribute data for this node, key has to be invalid.
00754 return -code error "invalid key \"$key\" for node \"$node\""
00755 }
00756
00757 upvar ${name}::$attribute($node) data
00758 if {![info exists data($key)]} {
00759 return -code error "invalid key \"$key\" for node \"$node\""
00760 }
00761 return $data($key)
00762 }
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775 ret ::struct::tree::_getall (type name , type node , optional pattern =*) {
00776 if {![_exists $name $node]} {
00777 return -code error "node \"$node\" does not exist in tree \"$name\""
00778 }
00779
00780 variable ${name}::attribute
00781 if {![info exists attribute($node)]} {
00782 # No attributes ...
00783 return {}
00784 }
00785
00786 upvar ${name}::$attribute($node) data
00787 return [array get data $pattern]
00788 }
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801 ret ::struct::tree::_height (type name , type node) {
00802 if { ![_exists $name $node] } {
00803 return -code error "node \"$node\" does not exist in tree \"$name\""
00804 }
00805
00806 variable ${name}::children
00807 variable ${name}::parent
00808
00809 if {[llength $children($node)] == 0} {
00810 # No children, is a leaf, height is 0.
00811 return 0
00812 }
00813
00814 # New implementation. We iteratively compute the height for each
00815 # node under the specified one, from the bottom up. The previous
00816 # implementation, using recursion will fail if the encountered
00817 # subtree has a height greater than the currently set recursion
00818 # limit.
00819
00820 array set h {}
00821
00822 # NOTE: Check out if a for loop doing direct access, i.e. without
00823 # list reversal, is faster.
00824
00825 foreach n [struct::list reverse [DescendantsCore $name $node]] {
00826 # Height of leafs
00827 if {![llength $children($n)]} {set h($n) 0}
00828
00829 # Height of our parent is max of our and previous height.
00830 set p $parent($n)
00831 if {![info exists h($p)] || ($h($n) >= $h($p))} {
00832 set h($p) [expr {$h($n) + 1}]
00833 }
00834 }
00835
00836 # NOTE: Check out how much we gain by caching the result.
00837 # For all nodes we have this computed. Use cache here
00838 # as well to cut the inspection of descendants down.
00839 # This may degenerate into a recursive solution again
00840 # however.
00841
00842 return $h($node)
00843 }
00844
00845
00846
00847
00848
00849
00850
00851
00852
00853
00854
00855
00856 ret ::struct::tree::_keys (type name , type node , optional pattern =*) {
00857 if {![_exists $name $node]} {
00858 return -code error "node \"$node\" does not exist in tree \"$name\""
00859 }
00860
00861 variable ${name}::attribute
00862 if {![info exists attribute($node)]} {
00863 # No attribute data for this node.
00864 return {}
00865 }
00866
00867 upvar ${name}::$attribute($node) data
00868 return [array names data $pattern]
00869 }
00870
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882
00883 ret ::struct::tree::_keyexists (type name , type node , type key) {
00884 if {![_exists $name $node]} {
00885 return -code error "node \"$node\" does not exist in tree \"$name\""
00886 }
00887
00888 variable ${name}::attribute
00889 if {![info exists attribute($node)]} {
00890 # No attribute data for this node, key cannot exist
00891 return 0
00892 }
00893
00894 upvar ${name}::$attribute($node) data
00895 return [info exists data($key)]
00896 }
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909 ret ::struct::tree::_index (type name , type node) {
00910 variable ${name}::rootname
00911 if { [string equal $node $rootname] } {
00912 # The special root node has no parent, thus no index in it either.
00913 return -code error "cannot determine index of root node"
00914 }
00915
00916 if { ![_exists $name $node] } {
00917 return -code error "node \"$node\" does not exist in tree \"$name\""
00918 }
00919
00920 variable ${name}::children
00921 variable ${name}::parent
00922
00923 # Locate the parent and ourself in its list of children
00924 set parentNode $parent($node)
00925
00926 return [lsearch -exact $children($parentNode) $node]
00927 }
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942
00943
00944 ret ::struct::tree::_insert (type name , type parentNode , type index , type args) {
00945 if { [llength $args] == 0 } {
00946 # No node name was given; generate a unique one
00947 set args [list [GenerateUniqueNodeName $name]]
00948 }
00949 if { ![_exists $name $parentNode] } {
00950 return -code error "parent node \"$parentNode\" does not exist in tree \"$name\""
00951 }
00952
00953 variable ${name}::parent
00954 variable ${name}::children
00955 variable ${name}::rootname
00956
00957 # Make sure the index is numeric
00958
00959 if {[string equal $index "end"]} {
00960 set index [llength $children($parentNode)]
00961 } elseif {[regexp {^end-([0-9]+)$} $index -> n]} {
00962 set index [expr {[llength $children($parentNode)] - $n}]
00963 }
00964
00965 foreach node $args {
00966 if {[_exists $name $node] } {
00967 # Move the node to its new home
00968 if { [string equal $node $rootname] } {
00969 return -code error "cannot move root node"
00970 }
00971
00972 # Cannot make a node its own descendant (I'm my own grandpa...)
00973 set ancestor $parentNode
00974 while { ![string equal $ancestor $rootname] } {
00975 if { [string equal $ancestor $node] } {
00976 return -code error "node \"$node\" cannot be its own descendant"
00977 }
00978 set ancestor $parent($ancestor)
00979 }
00980 # Remove this node from its parent's children list
00981 set oldParent $parent($node)
00982 set ind [lsearch -exact $children($oldParent) $node]
00983 ldelete children($oldParent) $ind
00984
00985 # If the node is moving within its parent, and its old location
00986 # was before the new location, decrement the new location, so that
00987 # it gets put in the right spot
00988 if { [string equal $oldParent $parentNode] && $ind < $index } {
00989 incr index -1
00990 }
00991 } else {
00992 # Set up the new node
00993 set children($node) [list]
00994 }
00995
00996 # Add this node to its parent's children list
00997 set children($parentNode) [linsert $children($parentNode) $index $node]
00998
00999 # Update the parent pointer for this node
01000 set parent($node) $parentNode
01001 incr index
01002 }
01003
01004 return $args
01005 }
01006
01007
01008
01009
01010
01011
01012
01013
01014
01015
01016
01017
01018 ret ::struct::tree::_isleaf (type name , type node) {
01019 if { ![_exists $name $node] } {
01020 return -code error "node \"$node\" does not exist in tree \"$name\""
01021 }
01022
01023 variable ${name}::children
01024 return [expr {[llength $children($node)] == 0}]
01025 }
01026
01027
01028
01029
01030
01031
01032
01033
01034
01035
01036
01037
01038
01039
01040
01041
01042
01043 ret ::struct::tree::_move (type name , type parentNode , type index , type node , type args) {
01044 set args [linsert $args 0 $node]
01045
01046 # Can only move a node to a real location in the tree
01047 if { ![_exists $name $parentNode] } {
01048 return -code error "parent node \"$parentNode\" does not exist in tree \"$name\""
01049 }
01050
01051 variable ${name}::parent
01052 variable ${name}::children
01053 variable ${name}::rootname
01054
01055 # Make sure the index is numeric
01056
01057 if {[string equal $index "end"]} {
01058 set index [llength $children($parentNode)]
01059 } elseif {[regexp {^end-([0-9]+)$} $index -> n]} {
01060 set index [expr {[llength $children($parentNode)] - $n}]
01061 }
01062
01063 # Validate all nodes to move before trying to move any.
01064 foreach node $args {
01065 if { [string equal $node $rootname] } {
01066 return -code error "cannot move root node"
01067 }
01068
01069 # Can only move real nodes
01070 if { ![_exists $name $node] } {
01071 return -code error "node \"$node\" does not exist in tree \"$name\""
01072 }
01073
01074 # Cannot move a node to be a descendant of itself
01075 set ancestor $parentNode
01076 while { ![string equal $ancestor $rootname] } {
01077 if { [string equal $ancestor $node] } {
01078 return -code error "node \"$node\" cannot be its own descendant"
01079 }
01080 set ancestor $parent($ancestor)
01081 }
01082 }
01083
01084 # Remove all nodes from their current parent's children list
01085 foreach node $args {
01086 set oldParent $parent($node)
01087 set ind [lsearch -exact $children($oldParent) $node]
01088
01089 ldelete children($oldParent) $ind
01090
01091 # Update the nodes parent value
01092 set parent($node) $parentNode
01093 }
01094
01095 # Add all nodes to their new parent's children list
01096 set children($parentNode) \
01097 [eval [list linsert $children($parentNode) $index] $args]
01098
01099 return
01100 }
01101
01102
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114 ret ::struct::tree::_next (type name , type node) {
01115 # The 'root' has no siblings.
01116 variable ${name}::rootname
01117 if { [string equal $node $rootname] } {
01118 return {}
01119 }
01120
01121 if { ![_exists $name $node] } {
01122 return -code error "node \"$node\" does not exist in tree \"$name\""
01123 }
01124
01125 # Locate the parent and our place in its list of children.
01126 variable ${name}::parent
01127 variable ${name}::children
01128
01129 set parentNode $parent($node)
01130 set index [lsearch -exact $children($parentNode) $node]
01131
01132 # Go to the node to the right and return its name.
01133 return [lindex $children($parentNode) [incr index]]
01134 }
01135
01136
01137
01138
01139
01140
01141
01142
01143
01144
01145
01146
01147 ret ::struct::tree::_numchildren (type name , type node) {
01148 if { ![_exists $name $node] } {
01149 return -code error "node \"$node\" does not exist in tree \"$name\""
01150 }
01151
01152 variable ${name}::children
01153 return [llength $children($node)]
01154 }
01155
01156
01157
01158
01159
01160
01161
01162
01163
01164
01165
01166 ret ::struct::tree::_nodes (type name) {
01167 variable ${name}::children
01168 return [array names children]
01169 }
01170
01171
01172
01173
01174
01175
01176
01177
01178
01179
01180
01181
01182 ret ::struct::tree::_parent (type name , type node) {
01183 if { ![_exists $name $node] } {
01184 return -code error "node \"$node\" does not exist in tree \"$name\""
01185 }
01186 # FRINK: nocheck
01187 return [set ${name}::parent($node)]
01188 }
01189
01190
01191
01192
01193
01194
01195
01196
01197
01198
01199
01200
01201
01202 ret ::struct::tree::_previous (type name , type node) {
01203 # The 'root' has no siblings.
01204 variable ${name}::rootname
01205 if { [string equal $node $rootname] } {
01206 return {}
01207 }
01208
01209 if { ![_exists $name $node] } {
01210 return -code error "node \"$node\" does not exist in tree \"$name\""
01211 }
01212
01213 # Locate the parent and our place in its list of children.
01214 variable ${name}::parent
01215 variable ${name}::children
01216
01217 set parentNode $parent($node)
01218 set index [lsearch -exact $children($parentNode) $node]
01219
01220 # Go to the node to the right and return its name.
01221 return [lindex $children($parentNode) [incr index -1]]
01222 }
01223
01224
01225
01226
01227
01228
01229
01230
01231
01232
01233
01234 ret ::struct::tree::_rootname (type name) {
01235 variable ${name}::rootname
01236 return $rootname
01237 }
01238
01239
01240
01241
01242
01243
01244
01245
01246
01247
01248
01249
01250
01251 ret ::struct::tree::_rename (type name , type node , type newname) {
01252 if { ![_exists $name $node] } {
01253 return -code error "node \"$node\" does not exist in tree \"$name\""
01254 }
01255 if {[_exists $name $newname]} {
01256 return -code error "unable to rename node to \"$newname\",\
01257 node of that name already present in the tree \"$name\""
01258 }
01259
01260 set oldname $node
01261
01262 # Perform the rename in the internal
01263 # data structures.
01264
01265 variable ${name}::rootname
01266 variable ${name}::children
01267 variable ${name}::parent
01268 variable ${name}::attribute
01269
01270 set children($newname) $children($oldname)
01271 unset children($oldname)
01272 set parent($newname) $parent($oldname)
01273 unset parent($oldname)
01274
01275 foreach c $children($newname) {
01276 set parent($c) $newname
01277 }
01278
01279 if {[string equal $oldname $rootname]} {
01280 set rootname $newname
01281 } else {
01282 set p $parent($newname)
01283 set pos [lsearch -exact $children($p) $oldname]
01284 lset children($p) $pos $newname
01285 }
01286
01287 if {[info exists attribute($oldname)]} {
01288 set attribute($newname) $attribute($oldname)
01289 unset attribute($oldname)
01290 }
01291
01292 return $newname
01293 }
01294
01295
01296
01297
01298
01299
01300
01301
01302
01303
01304
01305
01306 ret ::struct::tree::_serialize (type name , type args) {
01307 if {[llength $args] > 1} {
01308 return -code error \
01309 "wrong # args: should be \"[list $name] serialize ?node?\""
01310 } elseif {[llength $args] == 1} {
01311 set node [lindex $args 0]
01312
01313 if {![_exists $name $node]} {
01314 return -code error "node \"$node\" does not exist in tree \"$name\""
01315 }
01316 } else {
01317 variable ${name}::rootname
01318 set node $rootname
01319 }
01320
01321 set tree [list]
01322 Serialize $name $node tree
01323 return $tree
01324 }
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337
01338 ret ::struct::tree::_set (type name , type node , type key , type args) {
01339 if {[llength $args] > 1} {
01340 return -code error "wrong # args: should be \"$name set node key\
01341 ?value?\""
01342 }
01343 if {![_exists $name $node]} {
01344 return -code error "node \"$node\" does not exist in tree \"$name\""
01345 }
01346
01347 # Process the arguments ...
01348
01349 if {[llength $args] > 0} {
01350 # Setting the value. This may have to create
01351 # the attribute array for this particular
01352 # node
01353
01354 variable ${name}::attribute
01355 if {![info exists attribute($node)]} {
01356 # No attribute data for this node,
01357 # so create it as we need it now.
01358 GenAttributeStorage $name $node
01359 }
01360 upvar ${name}::$attribute($node) data
01361
01362 return [set data($key) [lindex $args end]]
01363 } else {
01364 # Getting the value
01365
01366 return [_get $name $node $key]
01367 }
01368 }
01369
01370
01371
01372
01373
01374
01375
01376
01377
01378
01379
01380
01381
01382
01383 ret ::struct::tree::_append (type name , type node , type key , type value) {
01384 if {![_exists $name $node]} {
01385 return -code error "node \"$node\" does not exist in tree \"$name\""
01386 }
01387
01388 variable ${name}::attribute
01389 if {![info exists attribute($node)]} {
01390 # No attribute data for this node,
01391 # so create it as we need it.
01392 GenAttributeStorage $name $node
01393 }
01394
01395 upvar ${name}::$attribute($node) data
01396 return [append data($key) $value]
01397 }
01398
01399
01400
01401
01402
01403
01404
01405
01406
01407
01408
01409
01410
01411
01412 ret ::struct::tree::_lappend (type name , type node , type key , type value) {
01413 if {![_exists $name $node]} {
01414 return -code error "node \"$node\" does not exist in tree \"$name\""
01415 }
01416
01417 variable ${name}::attribute
01418 if {![info exists attribute($node)]} {
01419 # No attribute data for this node,
01420 # so create it as we need it.
01421 GenAttributeStorage $name $node
01422 }
01423
01424 upvar ${name}::$attribute($node) data
01425 return [lappend data($key) $value]
01426 }
01427
01428
01429
01430
01431
01432
01433
01434
01435
01436
01437
01438 ret ::struct::tree::_leaves (type name) {
01439 variable ${name}::children
01440
01441 set res {}
01442 foreach n [array names children] {
01443 if {[llength $children($n)]} continue
01444 lappend res $n
01445 }
01446 return $res
01447 }
01448
01449
01450
01451
01452
01453
01454
01455
01456
01457
01458
01459
01460
01461 ret ::struct::tree::_size (type name , type args) {
01462 variable ${name}::rootname
01463 if {[llength $args] > 1} {
01464 return -code error \
01465 "wrong # args: should be \"[list $name] size ?node?\""
01466 } elseif {[llength $args] == 1} {
01467 set node [lindex $args 0]
01468
01469 if { ![_exists $name $node] } {
01470 return -code error "node \"$node\" does not exist in tree \"$name\""
01471 }
01472 } else {
01473 # If the node is the root, we can do the cheap thing and just count the
01474 # number of nodes (excluding the root node) that we have in the tree with
01475 # array size.
01476
01477 return [expr {[array size ${name}::parent] - 1}]
01478 }
01479
01480 # If the node is the root, we can do the cheap thing and just count the
01481 # number of nodes (excluding the root node) that we have in the tree with
01482 # array size.
01483
01484 if { [string equal $node $rootname] } {
01485 return [expr {[array size ${name}::parent] - 1}]
01486 }
01487
01488 # Otherwise we have to do it the hard way and do a full tree search
01489 variable ${name}::children
01490 set size 0
01491 set st [list ]
01492 foreach child $children($node) {
01493 lappend st $child
01494 }
01495 while { [llength $st] > 0 } {
01496 set node [lindex $st end]
01497 ldelete st end
01498 incr size
01499 foreach child $children($node) {
01500 lappend st $child
01501 }
01502 }
01503 return $size
01504 }
01505
01506
01507
01508
01509
01510
01511
01512
01513
01514
01515
01516
01517
01518
01519
01520
01521
01522
01523 ret ::struct::tree::_splice (type name , type parentNode , type from , optional to =end , type args) {
01524
01525 if { ![_exists $name $parentNode] } {
01526 return -code error "node \"$parentNode\" does not exist in tree \"$name\""
01527 }
01528
01529 if { [llength $args] == 0 } {
01530 # No node name given; generate a unique node name
01531 set node [GenerateUniqueNodeName $name]
01532 } else {
01533 set node [lindex $args 0]
01534 }
01535
01536 if { [_exists $name $node] } {
01537 return -code error "node \"$node\" already exists in tree \"$name\""
01538 }
01539
01540 variable ${name}::children
01541 variable ${name}::parent
01542
01543 if {[string equal $from "end"]} {
01544 set from [expr {[llength $children($parentNode)] - 1}]
01545 } elseif {[regexp {^end-([0-9]+)$} $from -> n]} {
01546 set from [expr {[llength $children($parentNode)] - 1 - $n}]
01547 }
01548 if {[string equal $to "end"]} {
01549 set to [expr {[llength $children($parentNode)] - 1}]
01550 } elseif {[regexp {^end-([0-9]+)$} $to -> n]} {
01551 set to [expr {[llength $children($parentNode)] - 1 - $n}]
01552 }
01553
01554 # Save the list of children that are moving
01555 set moveChildren [lrange $children($parentNode) $from $to]
01556
01557 # Remove those children from the parent
01558 ldelete children($parentNode) $from $to
01559
01560 # Add the new node
01561 _insert $name $parentNode $from $node
01562
01563 # Move the children
01564 set children($node) $moveChildren
01565 foreach child $moveChildren {
01566 set parent($child) $node
01567 }
01568
01569 return $node
01570 }
01571
01572
01573
01574
01575
01576
01577
01578
01579
01580
01581
01582
01583
01584 ret ::struct::tree::_swap (type name , type node1 , type node2) {
01585 # Can't swap the magic root node
01586 variable ${name}::rootname
01587 if {[string equal $node1 $rootname] || [string equal $node2 $rootname]} {
01588 return -code error "cannot swap root node"
01589 }
01590
01591 # Can only swap two real nodes
01592 if {![_exists $name $node1]} {
01593 return -code error "node \"$node1\" does not exist in tree \"$name\""
01594 }
01595 if {![_exists $name $node2]} {
01596 return -code error "node \"$node2\" does not exist in tree \"$name\""
01597 }
01598
01599 # Can't swap a node with itself
01600 if {[string equal $node1 $node2]} {
01601 return -code error "cannot swap node \"$node1\" with itself"
01602 }
01603
01604 # Swapping nodes means swapping their labels and values
01605 variable ${name}::children
01606 variable ${name}::parent
01607
01608 set parent1 $parent($node1)
01609 set parent2 $parent($node2)
01610
01611 # Replace node1 with node2 in node1's parent's children list, and
01612 # node2 with node1 in node2's parent's children list
01613 set i1 [lsearch -exact $children($parent1) $node1]
01614 set i2 [lsearch -exact $children($parent2) $node2]
01615
01616 lset children($parent1) $i1 $node2
01617 lset children($parent2) $i2 $node1
01618
01619 # Make node1 the parent of node2's children, and vis versa
01620 foreach child $children($node2) {
01621 set parent($child) $node1
01622 }
01623 foreach child $children($node1) {
01624 set parent($child) $node2
01625 }
01626
01627 # Swap the children lists
01628 set children1 $children($node1)
01629 set children($node1) $children($node2)
01630 set children($node2) $children1
01631
01632 if { [string equal $node1 $parent2] } {
01633 set parent($node1) $node2
01634 set parent($node2) $parent1
01635 } elseif { [string equal $node2 $parent1] } {
01636 set parent($node1) $parent2
01637 set parent($node2) $node1
01638 } else {
01639 set parent($node1) $parent2
01640 set parent($node2) $parent1
01641 }
01642
01643 # Swap the values
01644 # More complicated now with the possibility that nodes do not have
01645 # attribute storage associated with them.
01646
01647 variable ${name}::attribute
01648
01649 if {
01650 [set ia [info exists attribute($node1)]] ||
01651 [set ib [info exists attribute($node2)]]
01652 } {
01653 # At least one of the nodes has attribute data. We simply swap
01654 # the references to the arrays containing them. No need to
01655 # copy the actual data around.
01656
01657 if {$ia && $ib} {
01658 set tmp $attribute($node1)
01659 set attribute($node1) $attribute($node2)
01660 set attribute($node2) $tmp
01661 } elseif {$ia} {
01662 set attribute($node2) $attribute($node1)
01663 unset attribute($node1)
01664 } elseif {$ib} {
01665 set attribute($node1) $attribute($node2)
01666 unset attribute($node2)
01667 } else {
01668 return -code error "Impossible condition."
01669 }
01670 } ; # else: No attribute storage => Nothing to do {}
01671
01672 return
01673 }
01674
01675
01676
01677
01678
01679
01680
01681
01682
01683
01684
01685
01686
01687 ret ::struct::tree::_unset (type name , type node , type key) {
01688 if {![_exists $name $node]} {
01689 return -code error "node \"$node\" does not exist in tree \"$name\""
01690 }
01691
01692 variable ${name}::attribute
01693 if {![info exists attribute($node)]} {
01694 # No attribute data for this node,
01695 # nothing to do.
01696 return
01697 }
01698
01699 upvar ${name}::$attribute($node) data
01700 catch {unset data($key)}
01701
01702 if {[array size data] == 0} {
01703 # No attributes stored for this node, squash the whole array.
01704 unset attribute($node)
01705 unset data
01706 }
01707 return
01708 }
01709
01710
01711
01712
01713
01714
01715
01716
01717
01718
01719
01720
01721
01722
01723
01724
01725
01726
01727 ret ::struct::tree::_walk (type name , type node , type args) {
01728 set usage "$name walk node ?-type {bfs|dfs}? ?-order {pre|post|in|both}? ?--? loopvar script"
01729
01730 if {[llength $args] > 7 || [llength $args] < 2} {
01731 return -code error "wrong # args: should be \"$usage\""
01732 }
01733
01734 if { ![_exists $name $node] } {
01735 return -code error "node \"$node\" does not exist in tree \"$name\""
01736 }
01737
01738 set args [WalkOptions $args 2 $usage]
01739 # Remainder is 'a n script'
01740
01741 foreach {loopvariables script} $args break
01742
01743 if {[llength $loopvariables] > 2} {
01744 return -code error "too many loop variables, at most two allowed"
01745 } elseif {[llength $loopvariables] == 2} {
01746 foreach {avar nvar} $loopvariables break
01747 } else {
01748 set nvar [lindex $loopvariables 0]
01749 set avar {}
01750 }
01751
01752 # Make sure we have a script to run, otherwise what's the point?
01753 if { [string equal $script ""] } {
01754 return -code error "no script specified, or empty"
01755 }
01756
01757 # Do the walk
01758 variable ${name}::children
01759 set st [list ]
01760 lappend st $node
01761
01762 # Compute some flags for the possible places of command evaluation
01763 set leave [expr {[string equal $order post] || [string equal $order both]}]
01764 set enter [expr {[string equal $order pre] || [string equal $order both]}]
01765 set touch [string equal $order in]
01766
01767 if {$leave} {
01768 set lvlabel leave
01769 } elseif {$touch} {
01770 # in-order does not provide a sense
01771 # of nesting for the parent, hence
01772 # no enter/leave, just 'visit'.
01773 set lvlabel visit
01774 }
01775
01776 set rcode 0
01777 set rvalue {}
01778
01779 if {[string equal $type "dfs"]} {
01780 # Depth-first walk, several orders of visiting nodes
01781 # (pre, post, both, in)
01782
01783 array set visited {}
01784
01785 while { [llength $st] > 0 } {
01786 set node [lindex $st end]
01787
01788 if {[info exists visited($node)]} {
01789 # Second time we are looking at this 'node'.
01790 # Pop it, then evaluate the command (post, both, in).
01791
01792 ldelete st end
01793
01794 if {$leave || $touch} {
01795 # Evaluate the script at this node
01796 WalkCall $avar $nvar $name $node $lvlabel $script
01797 # prune stops execution of loop here.
01798 }
01799 } else {
01800 # First visit of this 'node'.
01801 # Do *not* pop it from the stack so that we are able
01802 # to visit again after its children
01803
01804 # Remember it.
01805 set visited($node) .
01806
01807 if {$enter} {
01808 # Evaluate the script at this node (pre, both).
01809 #
01810 # Note: As this is done before the children are
01811 # looked at the script may change the children of
01812 # this node and thus affect the walk.
01813
01814 WalkCall $avar $nvar $name $node "enter" $script
01815 # prune stops execution of loop here.
01816 }
01817
01818 # Add the children of this node to the stack.
01819 # The exact behaviour depends on the chosen
01820 # order. For pre, post, both-order we just
01821 # have to add them in reverse-order so that
01822 # they will be popped left-to-right. For in-order
01823 # we have rearrange the stack so that the parent
01824 # is revisited immediately after the first child.
01825 # (but only if there is ore than one child,)
01826
01827 set clist $children($node)
01828 set len [llength $clist]
01829
01830 if {$touch && ($len > 1)} {
01831 # Pop node from stack, insert into list of children
01832 ldelete st end
01833 set clist [linsert $clist 1 $node]
01834 incr len
01835 }
01836
01837 for {set i [expr {$len - 1}]} {$i >= 0} {incr i -1} {
01838 lappend st [lindex $clist $i]
01839 }
01840 }
01841 }
01842 } else {
01843 # Breadth first walk (pre, post, both)
01844 # No in-order possible. Already captured.
01845
01846 if {$leave} {
01847 set backward $st
01848 }
01849
01850 while { [llength $st] > 0 } {
01851 set node [lindex $st 0]
01852 ldelete st 0
01853
01854 if {$enter} {
01855 # Evaluate the script at this node
01856 WalkCall $avar $nvar $name $node "enter" $script
01857 # prune stops execution of loop here.
01858 }
01859
01860 # Add this node's children
01861 # And create a mirrored version in case of post/both order.
01862
01863 foreach child $children($node) {
01864 lappend st $child
01865 if {$leave} {
01866 set backward [linsert $backward 0 $child]
01867 }
01868 }
01869 }
01870
01871 if {$leave} {
01872 foreach node $backward {
01873 # Evaluate the script at this node
01874 WalkCall $avar $nvar $name $node "leave" $script
01875 }
01876 }
01877 }
01878
01879 if {$rcode != 0} {
01880 return -code $rcode $rvalue
01881 }
01882 return
01883 }
01884
01885 ret ::struct::tree::_walkproc (type name , type node , type args) {
01886 set usage "$name walkproc node ?-type {bfs|dfs}? ?-order {pre|post|in|both}? ?--? cmdprefix"
01887
01888 if {[llength $args] > 6 || [llength $args] < 1} {
01889 return -code error "wrong # args: should be \"$usage\""
01890 }
01891
01892 if { ![_exists $name $node] } {
01893 return -code error "node \"$node\" does not exist in tree \"$name\""
01894 }
01895
01896 set args [WalkOptions $args 1 $usage]
01897 # Remainder is 'n cmdprefix'
01898
01899 set script [lindex $args 0]
01900
01901 # Make sure we have a script to run, otherwise what's the point?
01902 if { ![llength $script] } {
01903 return -code error "no script specified, or empty"
01904 }
01905
01906 # Do the walk
01907 variable ${name}::children
01908 set st [list ]
01909 lappend st $node
01910
01911 # Compute some flags for the possible places of command evaluation
01912 set leave [expr {[string equal $order post] || [string equal $order both]}]
01913 set enter [expr {[string equal $order pre] || [string equal $order both]}]
01914 set touch [string equal $order in]
01915
01916 if {$leave} {
01917 set lvlabel leave
01918 } elseif {$touch} {
01919 # in-order does not provide a sense
01920 # of nesting for the parent, hence
01921 # no enter/leave, just 'visit'.
01922 set lvlabel visit
01923 }
01924
01925 set rcode 0
01926 set rvalue {}
01927
01928 if {[string equal $type "dfs"]} {
01929 # Depth-first walk, several orders of visiting nodes
01930 # (pre, post, both, in)
01931
01932 array set visited {}
01933
01934 while { [llength $st] > 0 } {
01935 set node [lindex $st end]
01936
01937 if {[info exists visited($node)]} {
01938 # Second time we are looking at this 'node'.
01939 # Pop it, then evaluate the command (post, both, in).
01940
01941 ldelete st end
01942
01943 if {$leave || $touch} {
01944 # Evaluate the script at this node
01945 WalkCallProc $name $node $lvlabel $script
01946 # prune stops execution of loop here.
01947 }
01948 } else {
01949 # First visit of this 'node'.
01950 # Do *not* pop it from the stack so that we are able
01951 # to visit again after its children
01952
01953 # Remember it.
01954 set visited($node) .
01955
01956 if {$enter} {
01957 # Evaluate the script at this node (pre, both).
01958 #
01959 # Note: As this is done before the children are
01960 # looked at the script may change the children of
01961 # this node and thus affect the walk.
01962
01963 WalkCallProc $name $node "enter" $script
01964 # prune stops execution of loop here.
01965 }
01966
01967 # Add the children of this node to the stack.
01968 # The exact behaviour depends on the chosen
01969 # order. For pre, post, both-order we just
01970 # have to add them in reverse-order so that
01971 # they will be popped left-to-right. For in-order
01972 # we have rearrange the stack so that the parent
01973 # is revisited immediately after the first child.
01974 # (but only if there is ore than one child,)
01975
01976 set clist $children($node)
01977 set len [llength $clist]
01978
01979 if {$touch && ($len > 1)} {
01980 # Pop node from stack, insert into list of children
01981 ldelete st end
01982 set clist [linsert $clist 1 $node]
01983 incr len
01984 }
01985
01986 for {set i [expr {$len - 1}]} {$i >= 0} {incr i -1} {
01987 lappend st [lindex $clist $i]
01988 }
01989 }
01990 }
01991 } else {
01992 # Breadth first walk (pre, post, both)
01993 # No in-order possible. Already captured.
01994
01995 if {$leave} {
01996 set backward $st
01997 }
01998
01999 while { [llength $st] > 0 } {
02000 set node [lindex $st 0]
02001 ldelete st 0
02002
02003 if {$enter} {
02004 # Evaluate the script at this node
02005 WalkCallProc $name $node "enter" $script
02006 # prune stops execution of loop here.
02007 }
02008
02009 # Add this node's children
02010 # And create a mirrored version in case of post/both order.
02011
02012 foreach child $children($node) {
02013 lappend st $child
02014 if {$leave} {
02015 set backward [linsert $backward 0 $child]
02016 }
02017 }
02018 }
02019
02020 if {$leave} {
02021 foreach node $backward {
02022 # Evaluate the script at this node
02023 WalkCallProc $name $node "leave" $script
02024 }
02025 }
02026 }
02027
02028 if {$rcode != 0} {
02029 return -code $rcode $rvalue
02030 }
02031 return
02032 }
02033
02034 ret ::struct::tree::WalkOptions (type theargs , type n , type usage) {
02035 upvar 1 type type order order
02036
02037 # Set defaults
02038 set type dfs
02039 set order pre
02040
02041 while {[llength $theargs]} {
02042 set flag [lindex $theargs 0]
02043 switch -exact -- $flag {
02044 "-type" {
02045 if {[llength $theargs] < 2} {
02046 return -code error "value for \"$flag\" missing"
02047 }
02048 set type [string tolower [lindex $theargs 1]]
02049 set theargs [lrange $theargs 2 end]
02050 }
02051 "-order" {
02052 if {[llength $theargs] < 2} {
02053 return -code error "value for \"$flag\" missing"
02054 }
02055 set order [string tolower [lindex $theargs 1]]
02056 set theargs [lrange $theargs 2 end]
02057 }
02058 "--" {
02059 set theargs [lrange $theargs 1 end]
02060 break
02061 }
02062 default {
02063 break
02064 }
02065 }
02066 }
02067
02068 if {[llength $theargs] == 0} {
02069 return -code error "wrong # args: should be \"$usage\""
02070 }
02071 if {[llength $theargs] != $n} {
02072 return -code error "unknown option \"$flag\""
02073 }
02074
02075 # Validate that the given type is good
02076 switch -exact -- $type {
02077 "dfs" - "bfs" {
02078 set type $type
02079 }
02080 default {
02081 return -code error "bad search type \"$type\": must be bfs or dfs"
02082 }
02083 }
02084
02085 # Validate that the given order is good
02086 switch -exact -- $order {
02087 "pre" - "post" - "in" - "both" {
02088 set order $order
02089 }
02090 default {
02091 return -code error "bad search order \"$order\":\
02092 must be both, in, pre, or post"
02093 }
02094 }
02095
02096 if {[string equal $order "in"] && [string equal $type "bfs"]} {
02097 return -code error "unable to do a ${order}-order breadth first walk"
02098 }
02099
02100 return $theargs
02101 }
02102
02103
02104
02105
02106
02107
02108
02109
02110
02111
02112
02113
02114
02115
02116
02117
02118
02119 ret ::struct::tree::WalkCall (type avar , type nvar , type tree , type node , type action , type cmd) {
02120
02121 if {$avar != {}} {
02122 upvar 2 $avar a ; set a $action
02123 }
02124 upvar 2 $nvar n ; set n $node
02125
02126 set code [catch {uplevel 2 $cmd} result]
02127
02128 # decide what to do upon the return code:
02129 #
02130 # 0 - the body executed successfully
02131 # 1 - the body raised an error
02132 # 2 - the body invoked [return]
02133 # 3 - the body invoked [break]
02134 # 4 - the body invoked [continue]
02135 # 5 - the body invoked [struct::tree::prune]
02136 # everything else - return and pass on the results
02137 #
02138 switch -exact -- $code {
02139 0 {}
02140 1 {
02141 return -errorinfo [ErrorInfoAsCaller uplevel WalkCall] \
02142 -errorcode $::errorCode -code error $result
02143 }
02144 3 {
02145 # FRINK: nocheck
02146 return -code break
02147 }
02148 4 {}
02149 5 {
02150 upvar order order
02151 if {[string equal $order post] || [string equal $order in]} {
02152 return -code error "Illegal attempt to prune ${order}-order walking"
02153 }
02154 return -code continue
02155 }
02156 default {
02157 upvar 1 rcode rcode rvalue rvalue
02158 set rcode $code
02159 set rvalue $result
02160 return -code break
02161 #return -code $code $result
02162 }
02163 }
02164 return {}
02165 }
02166
02167 ret ::struct::tree::WalkCallProc (type tree , type node , type action , type cmd) {
02168
02169 lappend cmd $tree $node $action
02170 set code [catch {uplevel 2 $cmd} result]
02171
02172 # decide what to do upon the return code:
02173 #
02174 # 0 - the body executed successfully
02175 # 1 - the body raised an error
02176 # 2 - the body invoked [return]
02177 # 3 - the body invoked [break]
02178 # 4 - the body invoked [continue]
02179 # 5 - the body invoked [struct::tree::prune]
02180 # everything else - return and pass on the results
02181 #
02182 switch -exact -- $code {
02183 0 {}
02184 1 {
02185 return -errorinfo [ErrorInfoAsCaller uplevel WalkCallProc] \
02186 -errorcode $::errorCode -code error $result
02187 }
02188 3 {
02189 # FRINK: nocheck
02190 return -code break
02191 }
02192 4 {}
02193 5 {
02194 upvar order order
02195 if {[string equal $order post] || [string equal $order in]} {
02196 return -code error "Illegal attempt to prune ${order}-order walking"
02197 }
02198 return -code continue
02199 }
02200 default {
02201 upvar 1 rcode rcode rvalue rvalue
02202 set rcode $code
02203 set rvalue $result
02204 return -code break
02205 }
02206 }
02207 return {}
02208 }
02209
02210 ret ::struct::tree::ErrorInfoAsCaller (type find , type replace) {
02211 set info $::errorInfo
02212 set i [string last "\n (\"$find" $info]
02213 if {$i == -1} {return $info}
02214 set result [string range $info 0 [incr i 6]] ;# keep "\n (\""
02215 append result $replace ;# $find -> $replace
02216 incr i [string length $find]
02217 set j [string first ) $info [incr i]] ;# keep rest of parenthetical
02218 append result [string range $info $i $j]
02219 return $result
02220 }
02221
02222
02223
02224
02225
02226
02227
02228
02229
02230
02231
02232 ret ::struct::tree::GenerateUniqueNodeName (type name) {
02233 variable ${name}::nextUnusedNode
02234 while {[_exists $name "node${nextUnusedNode}"]} {
02235 incr nextUnusedNode
02236 }
02237 return "node${nextUnusedNode}"
02238 }
02239
02240
02241
02242
02243
02244
02245
02246
02247
02248
02249
02250
02251 ret ::struct::tree::KillNode (type name , type node) {
02252 variable ${name}::parent
02253 variable ${name}::children
02254 variable ${name}::attribute
02255
02256 # Remove all record of $node
02257 unset parent($node)
02258 unset children($node)
02259
02260 if {[info exists attribute($node)]} {
02261 # FRINK: nocheck
02262 unset ${name}::$attribute($node)
02263 unset attribute($node)
02264 }
02265 return
02266 }
02267
02268
02269
02270
02271
02272
02273
02274
02275
02276
02277
02278
02279 ret ::struct::tree::GenAttributeStorage (type name , type node) {
02280 variable ${name}::nextAttr
02281 variable ${name}::attribute
02282
02283 set attr "a[incr nextAttr]"
02284 set attribute($node) $attr
02285 return
02286 }
02287
02288
02289
02290
02291
02292
02293
02294
02295
02296
02297
02298
02299 ret ::struct::tree::Serialize (type name , type node , type tvar) {
02300 upvar 1 $tvar tree
02301
02302 variable ${name}::attribute
02303 variable ${name}::parent
02304
02305 # 'node' is the root of the tree to serialize. The precondition
02306 # for the call is that this node is already stored in the list
02307 # 'tvar', at index 'rootidx'.
02308
02309 # The attribute data for 'node' goes immediately after the 'node'
02310 # data. the node information is _not_ yet stored, and this command
02311 # has to do this.
02312
02313
02314 array set r {}
02315 set loc($node) 0
02316
02317 lappend tree $node {}
02318 if {[info exists attribute($node)]} {
02319 upvar ${name}::$attribute($node) data
02320 lappend tree [array get data]
02321 } else {
02322 # Encode nodes without attributes.
02323 lappend tree {}
02324 }
02325
02326 foreach n [DescendantsCore $name $node] {
02327 set loc($n) [llength $tree]
02328 lappend tree $n $loc($parent($n))
02329
02330 if {[info exists attribute($n)]} {
02331 upvar ${name}::$attribute($n) data
02332 lappend tree [array get data]
02333 } else {
02334 # Encode nodes without attributes.
02335 lappend tree {}
02336 }
02337 }
02338
02339 return $tree
02340 }
02341
02342
02343 ret ::struct::tree::CheckSerialization (type ser , type avar , type pvar , type cvar , type rnvar) {
02344 upvar 1 $avar attr $pvar p $cvar ch $rnvar rn
02345
02346 # Overall length ok ?
02347
02348 if {[llength $ser] % 3} {
02349 return -code error \
02350 "error in serialization: list length not a multiple of 3."
02351 }
02352
02353 set rn {}
02354 array set p {}
02355 array set ch {}
02356 array set attr {}
02357
02358 # Basic decoder pass
02359
02360 foreach {node parent nattr} $ser {
02361
02362 # Initialize children data, if not already done
02363 if {![info exists ch($node)]} {
02364 set ch($node) {}
02365 }
02366 # Attribute length ok ? Dictionary!
02367 if {[llength $nattr] % 2} {
02368 return -code error \
02369 "error in serialization: malformed attribute dictionary."
02370 }
02371 # Remember attribute data only for non-empty nodes
02372 if {[llength $nattr]} {
02373 set attr($node) $nattr
02374 }
02375 # Remember root
02376 if {$parent == {}} {
02377 lappend rn $node
02378 set p($node) {}
02379 continue
02380 }
02381 # Parent reference ok ?
02382 if {
02383 ![string is integer -strict $parent] ||
02384 ($parent % 3) ||
02385 ($parent < 0) ||
02386 ($parent >= [llength $ser])
02387 } {
02388 return -code error \
02389 "error in serialization: bad parent reference \"$parent\"."
02390 }
02391 # Remember parent, and reconstruct children
02392
02393 set p($node) [lindex $ser $parent]
02394 lappend ch($p($node)) $node
02395 }
02396
02397 # Root node information ok ?
02398
02399 if {[llength $rn] < 1} {
02400 return -code error \
02401 "error in serialization: no root specified."
02402 } elseif {[llength $rn] > 1} {
02403 return -code error \
02404 "error in serialization: multiple root nodes."
02405 }
02406 set rn [lindex $rn 0]
02407
02408 # Duplicate node names ?
02409
02410 if {[array size ch] < ([llength $ser] / 3)} {
02411 return -code error \
02412 "error in serialization: duplicate node names."
02413 }
02414
02415 # Cycles in the parent relationship ?
02416
02417 array set visited {}
02418 foreach n [array names p] {
02419 if {[info exists visited($n)]} {continue}
02420 array set _ {}
02421 while {$n != {}} {
02422 if {[info exists _($n)]} {
02423 # Node already converted, cycle.
02424 return -code error \
02425 "error in serialization: cycle detected."
02426 }
02427 set _($n) .
02428 # root ?
02429 if {$p($n) == {}} {break}
02430 set n $p($n)
02431 if {[info exists visited($n)]} {break}
02432 set visited($n) .
02433 }
02434 unset _
02435 }
02436 # Ok. The data is now ready for the caller.
02437
02438 return
02439 }
02440
02441
02442
02443
02444
02445
02446
02447 ret ::struct::tree::K ( type x , type y ) { set x }
02448
02449 if { [package vcompare [package provide Tcl] 8.4] < 0 } {
02450 ret ::struct::tree::lset ( type var , type index , type arg ) {
02451 upvar 1 $var list
02452 set list [::lreplace [K $list [set list {}]] $index $index $arg]
02453 }
02454 }
02455
02456 ret ::struct::tree::ldelete (type var , type index , optional end ={)} {
02457 upvar 1 $var list
02458 if {$end == {}} { end = $index}
02459 list = [lreplace [K $list [ list = {}]] $index $end]
02460 return
02461 }
02462
02463
02464
02465
02466 namespace ::struct {
02467
02468
02469
02470 namespace import -force tree::tree_tcl
02471 }
02472