| 1 | # stack.tcl -- |
|---|
| 2 | # |
|---|
| 3 | # Stack implementation for Tcl. |
|---|
| 4 | # |
|---|
| 5 | # Copyright (c) 1998-2000 by Ajuba Solutions. |
|---|
| 6 | # |
|---|
| 7 | # See the file "license.terms" for information on usage and redistribution |
|---|
| 8 | # of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
|---|
| 9 | # |
|---|
| 10 | # RCS: @(#) $Id: stack.tcl,v 1.13 2005/10/03 17:52:22 andreas_kupries Exp $ |
|---|
| 11 | |
|---|
| 12 | namespace eval ::struct {} |
|---|
| 13 | |
|---|
| 14 | namespace eval ::struct::stack { |
|---|
| 15 | # The stacks array holds all of the stacks you've made |
|---|
| 16 | variable stacks |
|---|
| 17 | |
|---|
| 18 | # counter is used to give a unique name for unnamed stacks |
|---|
| 19 | variable counter 0 |
|---|
| 20 | |
|---|
| 21 | # Only export one command, the one used to instantiate a new stack |
|---|
| 22 | namespace export stack |
|---|
| 23 | } |
|---|
| 24 | |
|---|
| 25 | # ::struct::stack::stack -- |
|---|
| 26 | # |
|---|
| 27 | # Create a new stack with a given name; if no name is given, use |
|---|
| 28 | # stackX, where X is a number. |
|---|
| 29 | # |
|---|
| 30 | # Arguments: |
|---|
| 31 | # name name of the stack; if null, generate one. |
|---|
| 32 | # |
|---|
| 33 | # Results: |
|---|
| 34 | # name name of the stack created |
|---|
| 35 | |
|---|
| 36 | proc ::struct::stack::stack {args} { |
|---|
| 37 | variable stacks |
|---|
| 38 | variable counter |
|---|
| 39 | |
|---|
| 40 | switch -exact -- [llength [info level 0]] { |
|---|
| 41 | 1 { |
|---|
| 42 | # Missing name, generate one. |
|---|
| 43 | incr counter |
|---|
| 44 | set name "stack${counter}" |
|---|
| 45 | } |
|---|
| 46 | 2 { |
|---|
| 47 | # Standard call. New empty stack. |
|---|
| 48 | set name [lindex $args 0] |
|---|
| 49 | } |
|---|
| 50 | default { |
|---|
| 51 | # Error. |
|---|
| 52 | return -code error \ |
|---|
| 53 | "wrong # args: should be \"stack ?name?\"" |
|---|
| 54 | } |
|---|
| 55 | } |
|---|
| 56 | |
|---|
| 57 | # FIRST, qualify the name. |
|---|
| 58 | if {![string match "::*" $name]} { |
|---|
| 59 | # Get caller's namespace; append :: if not global namespace. |
|---|
| 60 | set ns [uplevel 1 [list namespace current]] |
|---|
| 61 | if {"::" != $ns} { |
|---|
| 62 | append ns "::" |
|---|
| 63 | } |
|---|
| 64 | |
|---|
| 65 | set name "$ns$name" |
|---|
| 66 | } |
|---|
| 67 | if {[llength [info commands $name]]} { |
|---|
| 68 | return -code error \ |
|---|
| 69 | "command \"$name\" already exists, unable to create stack" |
|---|
| 70 | } |
|---|
| 71 | |
|---|
| 72 | set stacks($name) [list ] |
|---|
| 73 | |
|---|
| 74 | # Create the command to manipulate the stack |
|---|
| 75 | interp alias {} $name {} ::struct::stack::StackProc $name |
|---|
| 76 | |
|---|
| 77 | return $name |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | ########################## |
|---|
| 81 | # Private functions follow |
|---|
| 82 | |
|---|
| 83 | # ::struct::stack::StackProc -- |
|---|
| 84 | # |
|---|
| 85 | # Command that processes all stack object commands. |
|---|
| 86 | # |
|---|
| 87 | # Arguments: |
|---|
| 88 | # name name of the stack object to manipulate. |
|---|
| 89 | # args command name and args for the command |
|---|
| 90 | # |
|---|
| 91 | # Results: |
|---|
| 92 | # Varies based on command to perform |
|---|
| 93 | |
|---|
| 94 | proc ::struct::stack::StackProc {name cmd args} { |
|---|
| 95 | # Do minimal args checks here |
|---|
| 96 | if { [llength [info level 0]] == 2 } { |
|---|
| 97 | return -code error "wrong # args: should be \"$name option ?arg arg ...?\"" |
|---|
| 98 | } |
|---|
| 99 | |
|---|
| 100 | # Split the args into command and args components |
|---|
| 101 | set sub _$cmd |
|---|
| 102 | if { [llength [info commands ::struct::stack::$sub]] == 0 } { |
|---|
| 103 | set optlist [lsort [info commands ::struct::stack::_*]] |
|---|
| 104 | set xlist {} |
|---|
| 105 | foreach p $optlist { |
|---|
| 106 | set p [namespace tail $p] |
|---|
| 107 | lappend xlist [string range $p 1 end] |
|---|
| 108 | } |
|---|
| 109 | set optlist [linsert [join $xlist ", "] "end-1" "or"] |
|---|
| 110 | return -code error \ |
|---|
| 111 | "bad option \"$cmd\": must be $optlist" |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | uplevel 1 [linsert $args 0 ::struct::stack::$sub $name] |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | # ::struct::stack::_clear -- |
|---|
| 118 | # |
|---|
| 119 | # Clear a stack. |
|---|
| 120 | # |
|---|
| 121 | # Arguments: |
|---|
| 122 | # name name of the stack object. |
|---|
| 123 | # |
|---|
| 124 | # Results: |
|---|
| 125 | # None. |
|---|
| 126 | |
|---|
| 127 | proc ::struct::stack::_clear {name} { |
|---|
| 128 | set ::struct::stack::stacks($name) [list ] |
|---|
| 129 | return |
|---|
| 130 | } |
|---|
| 131 | |
|---|
| 132 | # ::struct::stack::_destroy -- |
|---|
| 133 | # |
|---|
| 134 | # Destroy a stack object by removing it's storage space and |
|---|
| 135 | # eliminating it's proc. |
|---|
| 136 | # |
|---|
| 137 | # Arguments: |
|---|
| 138 | # name name of the stack object. |
|---|
| 139 | # |
|---|
| 140 | # Results: |
|---|
| 141 | # None. |
|---|
| 142 | |
|---|
| 143 | proc ::struct::stack::_destroy {name} { |
|---|
| 144 | unset ::struct::stack::stacks($name) |
|---|
| 145 | interp alias {} $name {} |
|---|
| 146 | return |
|---|
| 147 | } |
|---|
| 148 | |
|---|
| 149 | # ::struct::stack::_peek -- |
|---|
| 150 | # |
|---|
| 151 | # Retrieve the value of an item on the stack without popping it. |
|---|
| 152 | # |
|---|
| 153 | # Arguments: |
|---|
| 154 | # name name of the stack object. |
|---|
| 155 | # count number of items to pop; defaults to 1 |
|---|
| 156 | # |
|---|
| 157 | # Results: |
|---|
| 158 | # items top count items from the stack; if there are not enough items |
|---|
| 159 | # to fulfill the request, throws an error. |
|---|
| 160 | |
|---|
| 161 | proc ::struct::stack::_peek {name {count 1}} { |
|---|
| 162 | variable stacks |
|---|
| 163 | if { $count < 1 } { |
|---|
| 164 | error "invalid item count $count" |
|---|
| 165 | } |
|---|
| 166 | |
|---|
| 167 | if { $count > [llength $stacks($name)] } { |
|---|
| 168 | error "insufficient items on stack to fill request" |
|---|
| 169 | } |
|---|
| 170 | |
|---|
| 171 | if { $count == 1 } { |
|---|
| 172 | # Handle this as a special case, so single item pops aren't listified |
|---|
| 173 | set item [lindex $stacks($name) end] |
|---|
| 174 | return $item |
|---|
| 175 | } |
|---|
| 176 | |
|---|
| 177 | # Otherwise, return a list of items |
|---|
| 178 | set result [list ] |
|---|
| 179 | for {set i 0} {$i < $count} {incr i} { |
|---|
| 180 | lappend result [lindex $stacks($name) "end-${i}"] |
|---|
| 181 | } |
|---|
| 182 | return $result |
|---|
| 183 | } |
|---|
| 184 | |
|---|
| 185 | # ::struct::stack::_pop -- |
|---|
| 186 | # |
|---|
| 187 | # Pop an item off a stack. |
|---|
| 188 | # |
|---|
| 189 | # Arguments: |
|---|
| 190 | # name name of the stack object. |
|---|
| 191 | # count number of items to pop; defaults to 1 |
|---|
| 192 | # |
|---|
| 193 | # Results: |
|---|
| 194 | # item top count items from the stack; if the stack is empty, |
|---|
| 195 | # returns a list of count nulls. |
|---|
| 196 | |
|---|
| 197 | proc ::struct::stack::_pop {name {count 1}} { |
|---|
| 198 | variable stacks |
|---|
| 199 | if { $count > [llength $stacks($name)] } { |
|---|
| 200 | error "insufficient items on stack to fill request" |
|---|
| 201 | } elseif { $count < 1 } { |
|---|
| 202 | error "invalid item count $count" |
|---|
| 203 | } |
|---|
| 204 | |
|---|
| 205 | if { $count == 1 } { |
|---|
| 206 | # Handle this as a special case, so single item pops aren't listified |
|---|
| 207 | set item [lindex $stacks($name) end] |
|---|
| 208 | set stacks($name) [lreplace $stacks($name) end end] |
|---|
| 209 | return $item |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | # Otherwise, return a list of items |
|---|
| 213 | set result [list ] |
|---|
| 214 | for {set i 0} {$i < $count} {incr i} { |
|---|
| 215 | lappend result [lindex $stacks($name) "end-${i}"] |
|---|
| 216 | } |
|---|
| 217 | |
|---|
| 218 | # Remove these items from the stack |
|---|
| 219 | incr i -1 |
|---|
| 220 | set stacks($name) [lreplace $stacks($name) "end-${i}" end] |
|---|
| 221 | |
|---|
| 222 | return $result |
|---|
| 223 | } |
|---|
| 224 | |
|---|
| 225 | # ::struct::stack::_push -- |
|---|
| 226 | # |
|---|
| 227 | # Push an item onto a stack. |
|---|
| 228 | # |
|---|
| 229 | # Arguments: |
|---|
| 230 | # name name of the stack object |
|---|
| 231 | # args items to push. |
|---|
| 232 | # |
|---|
| 233 | # Results: |
|---|
| 234 | # None. |
|---|
| 235 | |
|---|
| 236 | proc ::struct::stack::_push {name args} { |
|---|
| 237 | if { [llength $args] == 0 } { |
|---|
| 238 | error "wrong # args: should be \"$name push item ?item ...?\"" |
|---|
| 239 | } |
|---|
| 240 | foreach item $args { |
|---|
| 241 | lappend ::struct::stack::stacks($name) $item |
|---|
| 242 | } |
|---|
| 243 | } |
|---|
| 244 | |
|---|
| 245 | # ::struct::stack::_rotate -- |
|---|
| 246 | # |
|---|
| 247 | # Rotate the top count number of items by step number of steps. |
|---|
| 248 | # |
|---|
| 249 | # Arguments: |
|---|
| 250 | # name name of the stack object. |
|---|
| 251 | # count number of items to rotate. |
|---|
| 252 | # steps number of steps to rotate. |
|---|
| 253 | # |
|---|
| 254 | # Results: |
|---|
| 255 | # None. |
|---|
| 256 | |
|---|
| 257 | proc ::struct::stack::_rotate {name count steps} { |
|---|
| 258 | variable stacks |
|---|
| 259 | set len [llength $stacks($name)] |
|---|
| 260 | if { $count > $len } { |
|---|
| 261 | error "insufficient items on stack to fill request" |
|---|
| 262 | } |
|---|
| 263 | |
|---|
| 264 | # Rotation algorithm: |
|---|
| 265 | # do |
|---|
| 266 | # Find the insertion point in the stack |
|---|
| 267 | # Move the end item to the insertion point |
|---|
| 268 | # repeat $steps times |
|---|
| 269 | |
|---|
| 270 | set start [expr {$len - $count}] |
|---|
| 271 | set steps [expr {$steps % $count}] |
|---|
| 272 | for {set i 0} {$i < $steps} {incr i} { |
|---|
| 273 | set item [lindex $stacks($name) end] |
|---|
| 274 | set stacks($name) [lreplace $stacks($name) end end] |
|---|
| 275 | set stacks($name) [linsert $stacks($name) $start $item] |
|---|
| 276 | } |
|---|
| 277 | return |
|---|
| 278 | } |
|---|
| 279 | |
|---|
| 280 | # ::struct::stack::_size -- |
|---|
| 281 | # |
|---|
| 282 | # Return the number of objects on a stack. |
|---|
| 283 | # |
|---|
| 284 | # Arguments: |
|---|
| 285 | # name name of the stack object. |
|---|
| 286 | # |
|---|
| 287 | # Results: |
|---|
| 288 | # count number of items on the stack. |
|---|
| 289 | |
|---|
| 290 | proc ::struct::stack::_size {name} { |
|---|
| 291 | return [llength $::struct::stack::stacks($name)] |
|---|
| 292 | } |
|---|
| 293 | |
|---|
| 294 | # ### ### ### ######### ######### ######### |
|---|
| 295 | ## Ready |
|---|
| 296 | |
|---|
| 297 | namespace eval ::struct { |
|---|
| 298 | # Get 'stack::stack' into the general structure namespace. |
|---|
| 299 | namespace import -force stack::stack |
|---|
| 300 | namespace export stack |
|---|
| 301 | } |
|---|
| 302 | package provide struct::stack 1.3.1 |
|---|