Version 1 of New expand

Updated 2011-01-19 11:23:46 by RLE

## start of expand.tcl

 #
 # prototype expand command
 # Joe Mistachkin
 # 7/31/2003

 set expand_argument_error "wrong # args: should be \"expand ?-expand expand_list? arg ?arg ...?\""
 set expand_list_error "malformed expansion list"

 proc expand_processExpansionList { array_name error_name expand_list argument_count } {
   # get variable to put error message into...
   upvar "1" $error_name local_error

   if {$argument_count > "0"} then {
     # we are going to put the results into the caller's array.
     upvar "1" $array_name local_array

     # set all arguments to default state (not expanded)
     set index "0"
     while {$index < $argument_count} {
       set local_array($index) "0"
       incr index
     }

     # assume success until we find an actual error.
     set result "0"

     # are we processing a "to" range?
     set range(from) "-1"
     set range(to) "-1"

     set last_item ""
     foreach this_item $expand_list {
       if {[string is integer -strict $this_item] != "0"} then {
         if {$range(from) != "-1"} then {
           # mark the end of the range...
           set range(to) $this_item

           # make sure it's a valid range...
           if {$range(from) <= $range(to)} then {
             # mark entire range to be expanded...
             set index $range(from)
             while {$index <= $range(to)} {
               set local_array($index) "1"
               incr index
             }

             # reset range positions...
             set range(from) "-1"
             set range(to) "-1"
           } else {
             # nope, not a valid range.
             set local_error "not a valid range"
             set result "-1"
             break
           }
         } else {
           if {(($this_item >= "0") && ($this_item < $argument_count))} then {
             # mark this argument for expansion...
             set local_array($this_item) "1"
           } else {
             # out of bounds.
             set local_error "argument index out of bounds"
             set result "-1"
             break
           }
         }
       } else {
         switch -exact -- $this_item {
           "to"    {
             if {$range(from) == "-1"} then {
               if {[string is integer -strict $last_item] != "0"} then {
                 set range(from) $last_item
               } else {
                 # invalid start of range...
                 set local_error "invalid index for start of range"
                 set result "-1"
                 break
               }
             } else {
               # already started a range...
               set local_error "range already started"
               set result "-1"
               break
             }
           }
           "end"   {
             # mark last argument to be expanded...
             set local_array([expr {$argument_count - "1"}]) "1"
           }
           default {
             # unknown argument processing directive.
             set local_error "unknown argument processing directive"
             set result "-1"
             break
           }
         }
       }

       # set last item processed to current item...
       set last_item $this_item
     }
   } else {
     # NOTE: not really an error.
     set local_error "nothing to process"
     set result "0"
   }

   return $result
 }

 proc expand { args } {
   global expand_argument_error
   global expand_list_error
   #
   # check for list of things to expand...
   # (default is to expand nothing)
   #
   if {[llength $args] > "0"} then {
     switch -exact -- [lindex $args "0"] {
       "-expand" {
         # we want to expand the specified arguments.
         set expand "1"
         # next, there should be a list of things to expand.
         set index "1"
         # the option and expansion list are NOT included in the argument count.
         set argument_count [expr {[llength $args] - ($index + "1")}]
       }
       "-noexpand" {
         # we want to NOT expand the specified arguments.
         set expand "0"
         # next, there should be a list of things to NOT expand.
         set index "1"
         # the option and expansion list are NOT included in the argument count.
         set argument_count [expr {[llength $args] - ($index + "1")}]
       }
       default {
         # don't expand by default.
         set expand "0"
         # there is no list of things to expand.
         set index "-1"
         # all arguments are passed along.
         set argument_count [llength $args]
       }
     }

     if {(($index == "-1") || ([llength $args] > ($index + "1")))} then {
       if {$index != "-1"} then {
         # get list of args to modify.
         set expand_list [lindex $args $index]

         set expand_error ""
         array set expand_array {}
         if {[expand_processExpansionList expand_array expand_error $expand_list $argument_count] != "0"} then {
           error "$expand_list_error: $expand_error"
         }
       } else {
         # no arguments to modify.
         set expand_list [list]
       }

       # start with an empty list as the command string to evaluate.
       set result [list]
       # start just beyond the end of the expansion list (if any).
       set this_index [expr {$index + "1"}]
       while {$this_index < [llength $args]} {
         # get the translated argument index...
         if {$index != "-1"} then {
           set argument_index [expr {$this_index - ($index + "1")}]
         } else {
           set argument_index $this_index
         }

         if {[info exists expand_array($argument_index)] != "0"} then {
           # is the argument targeted for expansion?
           if {$expand_array($argument_index) != "0"} then {
             set do_expand [expr {$expand ? "1" : "0"}]
           } else {
             set do_expand [expr {$expand ? "0" : "1"}]
           }
         } else {
           set do_expand [expr {$expand ? "0" : "1"}]
         }

         if {$do_expand != "0"} then {
           # add the expanded list to the command string.
           set result [concat $result [lindex $args $this_index]]
         } else {
           # add the unmodified argument to the command string.
           lappend result [lindex $args $this_index]
         }

         incr this_index
       }
     } else {
       # not enough arguments.
       error $expand_argument_error
     }
   } else {
     #
     # NOTE: potential for design change here.
     #
     # return empty string when given no arguments...?
     set result ""
   }

   uplevel "1" $result
 }

 proc expand_test_proc { args } {
   puts stdout "number of args = [llength $args]"
   puts stdout "args = \{ $args \}"
 }

 ## end of expand.tcl

Try these examples:

 expand -expand {1 to 3 end} expand_test_proc [list argument 1] [list argument 2] [list argument 3] [list argument 4] [list argument 5]
 expand -expand {1} expand_test_proc [list this is a test.]
 expand -noexpand {1} expand_test_proc [list this is a test.]
 expand expand_test_proc [list this is a test.]