# stack.tcl -- # # Stack implementation for Tcl. # # Copyright (c) 1998-2000 by Ajuba Solutions. # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: stack_tcl.tcl,v 1.3 2010/03/15 17:17:38 andreas_kupries Exp $ namespace eval ::struct::stack { # counter is used to give a unique name for unnamed stacks variable counter 0 # Only export one command, the one used to instantiate a new stack namespace export stack_tcl } # ::struct::stack::stack_tcl -- # # Create a new stack with a given name; if no name is given, use # stackX, where X is a number. # # Arguments: # name name of the stack; if null, generate one. # # Results: # name name of the stack created proc ::struct::stack::stack_tcl {args} { variable I::stacks variable counter switch -exact -- [llength [info level 0]] { 1 { # Missing name, generate one. incr counter set name "stack${counter}" } 2 { # Standard call. New empty stack. set name [lindex $args 0] } default { # Error. return -code error \ "wrong # args: should be \"stack ?name?\"" } } # FIRST, qualify the name. if {![string match "::*" $name]} { # Get caller's namespace; append :: if not global namespace. set ns [uplevel 1 [list namespace current]] if {"::" != $ns} { append ns "::" } set name "$ns$name" } if {[llength [info commands $name]]} { return -code error \ "command \"$name\" already exists, unable to create stack" } set stacks($name) [list ] # Create the command to manipulate the stack interp alias {} $name {} ::struct::stack::StackProc $name return $name } ########################## # Private functions follow # ::struct::stack::StackProc -- # # Command that processes all stack object commands. # # Arguments: # name name of the stack object to manipulate. # args command name and args for the command # # Results: # Varies based on command to perform if {[package vsatisfies [package provide Tcl] 8.5]} { # In 8.5+ we can do an ensemble for fast dispatch. proc ::struct::stack::StackProc {name cmd args} { # Shuffle method to front and then simply run the ensemble. # Dispatch, argument checking, and error message generation # are all done in the C-level. I $cmd $name {*}$args } namespace eval ::struct::stack::I { namespace export clear destroy get getr peek peekr \ trim trim* pop push rotate size namespace ensemble create } } else { # Before 8.5 we have to code our own dispatch, including error # checking. proc ::struct::stack::StackProc {name cmd args} { # Do minimal args checks here if { [llength [info level 0]] == 2 } { return -code error "wrong # args: should be \"$name option ?arg arg ...?\"" } # Split the args into command and args components if {![llength [info commands ::struct::stack::I::$cmd]]} { set optlist [lsort [info commands ::struct::stack::I::*]] set xlist {} foreach p $optlist { set p [namespace tail $p] if {($p eq "K") || ($p eq "lreverse")} continue lappend xlist $p } set optlist [linsert [join $xlist ", "] "end-1" "or"] return -code error \ "bad option \"$cmd\": must be $optlist" } uplevel 1 [linsert $args 0 ::struct::stack::I::$cmd $name] } } # ### ### ### ######### ######### ######### namespace eval ::struct::stack::I { # The stacks array holds all of the stacks you've made variable stacks } # ### ### ### ######### ######### ######### # ::struct::stack::I::clear -- # # Clear a stack. # # Arguments: # name name of the stack object. # # Results: # None. proc ::struct::stack::I::clear {name} { variable stacks set stacks($name) {} return } # ::struct::stack::I::destroy -- # # Destroy a stack object by removing it's storage space and # eliminating it's proc. # # Arguments: # name name of the stack object. # # Results: # None. proc ::struct::stack::I::destroy {name} { variable stacks unset stacks($name) interp alias {} $name {} return } # ::struct::stack::I::get -- # # Retrieve the whole contents of the stack. # # Arguments: # name name of the stack object. # # Results: # items list of all items in the stack. proc ::struct::stack::I::get {name} { variable stacks return [lreverse $stacks($name)] } proc ::struct::stack::I::getr {name} { variable stacks return $stacks($name) } # ::struct::stack::I::peek -- # # Retrieve the value of an item on the stack without popping it. # # Arguments: # name name of the stack object. # count number of items to pop; defaults to 1 # # Results: # items top count items from the stack; if there are not enough items # to fulfill the request, throws an error. proc ::struct::stack::I::peek {name {count 1}} { variable stacks upvar 0 stacks($name) mystack if { $count < 1 } { return -code error "invalid item count $count" } elseif { $count > [llength $mystack] } { return -code error "insufficient items on stack to fill request" } if { $count == 1 } { # Handle this as a special case, so single item peeks are not # listified return [lindex $mystack end] } # Otherwise, return a list of items incr count -1 return [lreverse [lrange $mystack end-$count end]] } proc ::struct::stack::I::peekr {name {count 1}} { variable stacks upvar 0 stacks($name) mystack if { $count < 1 } { return -code error "invalid item count $count" } elseif { $count > [llength $mystack] } { return -code error "insufficient items on stack to fill request" } if { $count == 1 } { # Handle this as a special case, so single item peeks are not # listified return [lindex $mystack end] } # Otherwise, return a list of items, in reversed order. incr count -1 return [lrange $mystack end-$count end] } # ::struct::stack::I::trim -- # # Pop items off a stack until a maximum size is reached. # # Arguments: # name name of the stack object. # count requested size of the stack. # # Results: # item List of items trimmed, may be empty. proc ::struct::stack::I::trim {name newsize} { variable stacks upvar 0 stacks($name) mystack if { ![string is integer -strict $newsize]} { return -code error "expected integer but got \"$newsize\"" } elseif { $newsize < 0 } { return -code error "invalid size $newsize" } elseif { $newsize >= [llength $mystack] } { # Stack is smaller than requested, do nothing. return {} } # newsize < [llength $mystack] # pop '[llength $mystack]' - newsize elements. if {!$newsize} { set result [lreverse [K $mystack [unset mystack]]] set mystack {} } else { set result [lreverse [lrange $mystack $newsize end]] set mystack [lreplace [K $mystack [unset mystack]] $newsize end] } return $result } proc ::struct::stack::I::trim* {name newsize} { if { ![string is integer -strict $newsize]} { return -code error "expected integer but got \"$newsize\"" } elseif { $newsize < 0 } { return -code error "invalid size $newsize" } variable stacks upvar 0 stacks($name) mystack if { $newsize >= [llength $mystack] } { # Stack is smaller than requested, do nothing. return } # newsize < [llength $mystack] # pop '[llength $mystack]' - newsize elements. # No results, compared to trim. if {!$newsize} { set mystack {} } else { set mystack [lreplace [K $mystack [unset mystack]] $newsize end] } return } # ::struct::stack::I::pop -- # # Pop an item off a stack. # # Arguments: # name name of the stack object. # count number of items to pop; defaults to 1 # # Results: # item top count items from the stack; if the stack is empty, # returns a list of count nulls. proc ::struct::stack::I::pop {name {count 1}} { variable stacks upvar 0 stacks($name) mystack if { $count < 1 } { return -code error "invalid item count $count" } set ssize [llength $mystack] if { $count > $ssize } { return -code error "insufficient items on stack to fill request" } if { $count == 1 } { # Handle this as a special case, so single item pops are not # listified set item [lindex $mystack end] if {$count == $ssize} { set mystack [list] } else { set mystack [lreplace [K $mystack [unset mystack]] end end] } return $item } # Otherwise, return a list of items, and remove the items from the # stack. if {$count == $ssize} { set result [lreverse [K $mystack [unset mystack]]] set mystack [list] } else { incr count -1 set result [lreverse [lrange $mystack end-$count end]] set mystack [lreplace [K $mystack [unset mystack]] end-$count end] } return $result # ------------------------------------------------------- set newsize [expr {[llength $mystack] - $count}] if {!$newsize} { set result [lreverse [K $mystack [unset mystack]]] set mystack {} } else { set result [lreverse [lrange $mystack $newsize end]] set mystack [lreplace [K $mystack [unset mystack]] $newsize end] } if {$count == 1} { set result [lindex $result 0] } return $result } # ::struct::stack::I::push -- # # Push an item onto a stack. # # Arguments: # name name of the stack object # args items to push. # # Results: # None. if {[package vsatisfies [package provide Tcl] 8.5]} { proc ::struct::stack::I::push {name args} { if {![llength $args]} { return -code error "wrong # args: should be \"$name push item ?item ...?\"" } variable stacks upvar 0 stacks($name) mystack lappend mystack {*}$args return } } else { proc ::struct::stack::I::push {name args} { if {![llength $args]} { return -code error "wrong # args: should be \"$name push item ?item ...?\"" } variable stacks upvar 0 stacks($name) mystack if {[llength $args] == 1} { lappend mystack [lindex $args 0] } else { eval [linsert $args 0 lappend mystack] } return } } # ::struct::stack::I::rotate -- # # Rotate the top count number of items by step number of steps. # # Arguments: # name name of the stack object. # count number of items to rotate. # steps number of steps to rotate. # # Results: # None. proc ::struct::stack::I::rotate {name count steps} { variable stacks upvar 0 stacks($name) mystack set len [llength $mystack] if { $count > $len } { return -code error "insufficient items on stack to fill request" } # Rotation algorithm: # do # Find the insertion point in the stack # Move the end item to the insertion point # repeat $steps times set start [expr {$len - $count}] set steps [expr {$steps % $count}] if {$steps == 0} return for {set i 0} {$i < $steps} {incr i} { set item [lindex $mystack end] set mystack [linsert \ [lreplace \ [K $mystack [unset mystack]] \ end end] $start $item] } return } # ::struct::stack::I::size -- # # Return the number of objects on a stack. # # Arguments: # name name of the stack object. # # Results: # count number of items on the stack. proc ::struct::stack::I::size {name} { variable stacks return [llength $stacks($name)] } # ### ### ### ######### ######### ######### proc ::struct::stack::I::K {x y} { set x } if {![llength [info commands lreverse]]} { proc ::struct::stack::I::lreverse {x} { # assert (llength(x) > 1) set l [llength $x] if {$l <= 1} { return $x } set r [list] while {$l} { lappend r [lindex $x [incr l -1]] } return $r } } # ### ### ### ######### ######### ######### ## Ready namespace eval ::struct { # Get 'stack::stack' into the general structure namespace for # pickup by the main management. namespace import -force stack::stack_tcl }