tcl how to redefine "set" command

77 views Asked by At

I am new comming of tcl. How to use tcl to imitate "set" command?

I try to build like below cold, but below code with error: unknown command name:

proc my_set {varName {value ""}} {
  uplevel 0 [set $varName $value]
}
my_set a "123"
my_set b(a) "good"

puts $a; #expected: 123
puts $b(a); #expected: good
2

There are 2 answers

1
Cyan Ogilvie On BEST ANSWER

In your my_set proc, the script set $varname $value is called, and then its result is passed as an argument to the uplevel command, which will try to run that result as a script.

To have it do what you want:

proc my_set {varname {value ""}} {
    uplevel 1 [list set $varname $value]
}

or

proc my_set {varname {value ""}} {
    upvar 1 $varname target
    set target $value
}

or

proc my_set {varname {value ""}} {
    tailcall set $varname $value
}

The first (using uplevel) constructs the command to run in the caller's frame (the 1 argument) as a list, and then uplevel runs that list as a command 1 frame up the stack.

The second aliases the local variable target in the my_set proc to the variable whose name is in the varname variable, 1 level up the stack. So setting the target variable within the my_set proc also sets the variable called $varname in the caller's frame.

The third (using tailcall) replaces the callframe of the my_set with the command set, which is then executing as if it was called from the parent frame.

Of the three, I would probably pick the second (using upvar) because there is less potential for nasty surprises waiting in your future (uplevel is essentially eval - incautious use can open remote code execution vulnerabilities), and, unless you like obscure constructions for their own sake, the tailcall implementation is probably just too weird.

0
glenn jackman On

A couple of things:

Level 0 is the current proc. To set the value in the caller's stackframe use uplevel 1. Since that's the default, a plain uplevel

Second, you want to give the uplevel command a script. You don't want to actually execute the set command. Typically, you'll create a list of the script.

uplevel [list set $myvar $myvalue]