Version 10 of let

Updated 2016-03-20 03:46:13 by pooryorick
# let.tcl
# Copyright 2001 by Larry Smith
# Wild Open Source, Inc
# For license terms see "COPYING"
#
# let is a replacement for the "set" command.  It allows
# multiple assignment, and supports a variety of assignment
# operators:
#
# let a b c = 1     ;#  this sets a b and c to 1
# let a b c = 1 + 4 ;# "=" uses expr to process the value to assign
# let a b c += 1    ;# computed assignments allowed, +-*/&| supported
# let a b c := info commands ;# uses eval to process value
# let a b c @= 1 2 3;# "hoisting" assignment, foreach replacement
# let a b c @:= info commands;# uses eval and hoists for assignment
# let a ++          ;# incr and
# let a --          ;#   decr are supported.

proc let { args } {
  if { [llength $args ] == 2 } {
    if [string equal [ lindex $args 1 ] "++" ] {
      set result [ uplevel incr [ lindex $args 0 ] ]
    } elseif [string equal [ lindex $args 1 ] "--" ] {
      set result [ uplevel incr [ lindex $args 0 ] -1 ]
    } else {
      set result [ uplevel set "$args" ]
    }
  } else {
    regexp {([^=:+\-*/&|@]*)([:+\-*/&|@]?)([@]*)=(.*)} $args -> vars op optional rest
    if ![ info exists op ] {
      return -code error -errorcode 1 "no valid assignment operator in $args"
    }
    switch -- $op {
      : {
        if [llength [info commands [lindex $rest 0]]] {
          set result [uplevel $rest]
        } else {
          set result $rest                ;# this should always work...
        }
        if { "$optional" == "@" } {
          set max [ llength $result ]
          foreach var $vars res $result {
            uplevel 1 [ list set $var $res ]
          }
        } else {
          foreach var $vars {
            set result [ uplevel set $var \"$result\" ]
          }
        }
      }
      @ {
        if { "$optional" == ":" } {
          set rest [uplevel $rest]
        }
        set max [ llength $rest ]
        if { $max == 1 } {
          eval set rest $rest
          set max [ llength $rest ]
        }
        foreach var $vars res $rest {
          set result [ uplevel 1 [ list set $var $res ]]
        }
      }
      + - - - * - / - & - | {
        foreach var $vars {
          set result [ uplevel set $var \[ expr \$$var $op ( $rest ) \] ]
        }
      }
      = -
      default {
        if { [ catch { set result [ uplevel expr $rest ] } ] } {
          set result $rest              ;# this should always work...
        }
        foreach var $vars {
          set result [ uplevel set $var \"$result\" ]
        }
      }
    }
  }
  return $result
}

DKF - I'd prefer it if stand-alone = was not used, but rather always working with := instead. That's proved to work fairly well. And automagic passing of stuff through eval is not necessary; better to get people to say what they mean IMHO.


Recently someone on comp.lang.tcl asked about the use of the word hoisting above. What is its meaning in this context?

AMG: Multiple assignments in the style of [foreach] or [lassign]: variables are set to corresponding values, where correspondence is determined by list position. [let a b c @= 1 2 3] would be like [foreach {a b c} {1 2 3} {break}] or [lassign {1 2 3} a b c].


What is the expected output of

  let a b c @:= info commands;# uses eval and hoists for assignment

What I am seeing, with the above code, is that nothing whatsoever is being done.

PYK 2016-03-19: Can confirm. I attempted to fix this bug today, but after working with it for a bit, decided that this code has too many issues to salvage. The alleged functionality has been largely implemented in lassign these days anyway. I move to delete this entire page on 2016-03-31.


See also