Tcl: [info level] vs. [info frame] - tcl

What is the difference between frames and levels of execution?
I wrote a small example to find out. I realized that the level number was incremented every time a proc was called.
On the other hand, the frame number was incremented on every
proc call
source command
eval command
etc.
For this reason, I started to think of levels as of a subset of frames. Now I'm debugging some real-life code and I realized that the level number can increase without the frame number being increased too. How to make sense of this stuff?

Stack levels, which is what info level talks about, are the important ones, because they're the ones that you can use upvar and uplevel to access into. This means that the Tcl “stack“ is really a tree, since you can use uplevel to go up to a higher level and start a new branch, with each step deeper being taken by a call to a procedure (or procedure-like thing) or a call to namespace eval; the info level command really talks about the current branch traced all the way back to the root.
The info frame command walks the tree of frames rather differently — I think it uses the actual way in which they are stacked, with frames being able to appear twice — and reports very different information about each of those frames. Personally, I only really find that it is useful for debugging.
Let's try a simple example. This is an interactive session.
% proc foo {y} {set x 1;bar $y $x $y}
% proc bar {a b args} {
puts [info level 0]
puts [info level -1]
puts [info frame 0]
puts [info frame -1]
}
% foo 3
bar 3 1 3
foo 3
type proc line 4 cmd {info frame 0} proc ::bar level 0
type proc line 1 cmd {bar $y $x $y} proc ::foo level 1
OK, as you can see, info level is reporting the actual values that were used and does not report itself, whereas info frame is reporting what that level is currently doing.
Here's a more complex example:
% proc foo {y} {set x 1;grill $y $x $y}
% proc grill {a b c} {uplevel 1 [list bar $a $b $c]}
% proc bar {args} {
for {set i 1} {$i<=[info level]} {incr i} {puts $i-->[info level $i]}
for {set i 1} {$i<=[info frame]} {incr i} {puts $i==>[info frame $i]}
}
% foo 3
1-->foo 3
2-->bar 3 1 3
1==>type eval line 1 cmd {foo 3} level 2
2==>type proc line 1 cmd {grill $y $x $y} proc ::foo level 1
3==>type proc line 1 cmd {uplevel 1 [list bar $a $b $c]} proc ::grill
4==>type eval line 1 cmd {bar 3 1 3} proc ::grill
5==>type proc line 3 cmd {info frame $i} proc ::bar level 0
As you can see, info level does not see that the uplevel happened, but info frame does. But only info level can really tell you what the arguments are. (Note that if you put the code in a file that you source, info frame reports even more information.)

Related

I am trying to form a rectangle with 2 pairs of Coordinate in TCL

I am new to TCL and hardware design. I am currently doing a script that could verify all the ports at an edge and make a rectangle box that cover all the ports at an edge.
Since I am new to TCL, I think there is something wrong in the syntax.
The error msg is:
Error: Invalid coordinates '$bbox_coor_x1 [expr $bbox_coor_y2-30]' in list. (NDMUI-100)
Error: Empty or invalid geometry specified for the '-boundary' argument of the command. (NDMUI-337).
Please help me.
Your problem is that variables and commands are not interpolated inside curly braces. Please learn the difference between curlies and double quotes.
For example:
set x 5
set y 10
# Curlies
puts {$x $y}
--> $x $y (literally)
puts {$x [expr $y+1]}
--> $x [expr $y+1] (literally)
# Double quotes
puts "$x $y"
--> 5 10
# List command
puts [list $x $y]
--> 5 10
puts [list $x [expr $y+1]]
--> 5 11
When making a list of lists, like a bbox, anything inside outer-most curlies will interpolate:
puts "{$x $x} {$y $y}"
--> {5 5} {10 10}
One more thing, note that lindex can take multiple indexes. Do this instead of calling lindex twice.
lindex $bbox 0 1

I can't understand how uplevel works

Why do I get an error ?
#!/usr/bin/tclsh
proc add {a} {
uplevel 1 $a
puts $a
}
set n 0
add $n
I can't understand how uplevel works
uplevel executes some code in a different "stack frame" -- each time you invoke a procedure (and some other ways), Tcl adds an execution frame to the call stack.
Here's an example:
proc foo {} {
set fooVar 42
bar {expr {$fooVar + 21}}
}
proc bar {code} {
puts "in bar, code is: [list $code]"
puts "in bar, the fooVar variable [expr {[info exists fooVar] ? "does" : "does not"}] exist"
uplevel 1 $code
}
Running the foo procedure:
% foo
in bar, code is: {expr {$fooVar + 21}}
in bar, the fooVar variable does not exist
63
This is demonstrating that, because we're executing the code in one frame up in the call stack, the code fragment does have access to a local variable in that frame.
If we try to run bar with the same code block from the glocal scope, we'll
see an error:
% bar {expr {$fooVar + 21}}
in bar, code is: {expr {$fooVar + 21}}
in bar, the fooVar variable does not exist
can't read "fooVar": no such variable
But if we set the variable in the global scope, it works as expected:
% set fooVar -1
-1
% bar {expr {$fooVar + 21}}
in bar, code is: {expr {$fooVar + 21}}
in bar, the fooVar variable does not exist
20
Together with upvar, the uplevel command allows you to implement your own control structures -- Tcl is really an incredibly flexible language. An example:
proc foreachWithIndex {variableNames aList code} {
lassign $variableNames idxVar elemVar
upvar 1 $idxVar idx
upvar 1 $elemVar elem
set idx 0
foreach elem $aList {
uplevel 1 $code
incr idx
}
}
foreachWithIndex {i e} {a b c d} {puts "$i -> $e"}
0 -> a
1 -> b
2 -> c
3 -> d
If we run your code, we get the error message: invalid command name "0".
Let's investigate by printing the stack trace:
% puts $errorInfo
invalid command name "0"
while executing
"0"
("uplevel" body line 1)
invoked from within
"uplevel 1 $a"
(procedure "add" line 2)
invoked from within
"add $n"
That looks curious, but it is fine because the value of $a is 0 because the argument to add is the value of $n which is 0. The arguments to uplevel (after an optional first pseudo-numeric argument, which you are supplying; good!) are a script (with multiple arguments being concatenated). The string 0 can be a script, if you happen to have a command in scope whose name is the single digit 0, but that's pretty unusual.
Once it's got the script to run, uplevel 1 runs the script in the context of the caller of the current procedure. (Technically, it steps up 1 stack frame and runs it there. Tcl stack frames actually form a tree, though there's usually just one branch on the tree at a time; uplevel forms another branch.)
The two common forms of uplevel are uplevel 1 (for “run in the caller”) and uplevel #0 (for “run in the global scope”). There are others, but they're uncommon. Don't use uplevel 0; it's exactly like eval and confusion reigns if you get obscure like that.
It's good practice to usually keep your uplevel script forms to one of:
An argument passed in by the caller. (This is your script's case.)
A constant script.
A single command call generated by calling list. You can use {*}expansion when generating the list; this is a good way to handle a list of values or a command prefix.
All of these have been found to be generally non-confusing and fairly easy to use correctly (without weird hazards). Other options tend to be tricky.
I'm guessing your code should have actually been written like this:
proc add {varName} {
upvar 1 $varName v
set v [expr {$v + 1}]
# Or maybe: incr v
puts $v
}
set n 0
add n
The upvar command uses the same level resolution rules as uplevel, but instead aliases a named variable in that scope to the other local name in the current scope. A call to upvar #0 foo foo is functionally equivalent to the apparently simpler global foo.

Getting the line number of executing code in TCL

how to print the line number of executing TCL script?
#! /usr/bin/tclsh
set a "100"
set b "200"
set c [expr $a + $b]
puts [info script] ;# it will display the script name which it is executing.
# similarly I need to print the script line number.
puts $c
You have to use info frame to get this done simply.
info frame ?number?
This command provides access to all frames on the stack, even those hidden from info level. If number is not specified, this command returns a number giving the frame level of the command. This is 1 if the command is invoked at top-level. If number is specified, then the result is a dictionary containing the location information for the command at the numbered level on the stack.
If number is positive (> 0) then it selects a particular stack level (1 refers to the top-most active command, i.e., info frame itself, 2 to the command it was called from, and so on); otherwise it gives a level relative to the current command (0 refers to the current command, i.e., info frame itself, -1 to its caller, and so on).
We are going to make use of the dictionary returned by the info frame command. One of the key is 'line' which contains the line number of the script.
Have a simple proc as,
proc printLine {frame_info} {
# Getting value of the key 'line' from the dictionary
# returned by 'info frame'
set result [dict get [info frame $frame_info] line]
}
In general, the resulting dictionary from [info frame $frame_info] will be something like,
type source line 17 file /home/dinesh/stackoverflow/test cmd {printLine [info frame] } proc ::B level 1
From this, we are just getting the key value 'line' with dict get
Just call this proc with the current frame number of that context which can be achieved with info frame itself.
i.e.
set lineNumber [printLine [info frame]]; #Place this line in your code.
A demonstration of this logic is as below.
printLineNumber.tcl
#!/usr/bin/tclsh
proc printLine {frame_info} {
# Getting value of the key 'line' from the dictionary
# returned by 'info frame'
set result [dict get [info frame $frame_info] line]
}
proc D {} {
puts "proc D"
puts [ printLine [info frame] ]
}
proc C {} {
puts "proc C"
puts [ printLine [info frame] ]
D
}
proc B {} {
puts "proc B"
puts [ printLine [info frame] ]
C
}
proc A {} {
puts "proc A"
puts [ printLine [info frame] ]
B
}
puts "Global"
puts [ printLine [info frame] ]
A
Documentation : info, dict

Expanded TCL interpreter in TCL

I have implemented many TCL extensions for a specific tool in the domain of formal methods (extensions are implemented in C but I do not want solution to rely on this fact). Thus, the users of my tool can use TCL for prototyping algorithms. Many of them are just linear list of commands (they are powerfull), e.g.:
my_read_file f
my_do_something a b c
my_do_something_else a b c
Now, I am interested in timing. It is possible to change the script to get:
puts [time [my_read_file f] 1]
puts [time [my_do_something a b c] 1]
puts [time [my_do_something_else a b c] 1]
Instead of this I want to define procedure xsource that executes a TCL script and get/write timing for all my commands. Some kind of a profiler. I wrote a naive implementation where the main idea is as follows:
set f [open [lindex $argv 0] r]
set inputLine ""
while {[gets $f line] >= 0} {
set d [expr [string length $line] - 1]
if { $d >= 0 } {
if { [string index $line 0] != "#" } {
if {[string index $line $d] == "\\"} {
set inputLine "$inputLine [string trimright [string range $line 0 [expr $d - 1]]]"
} else {
set inputLine "$inputLine $line"
set inputLine [string trimleft $inputLine]
puts $inputLine
puts [time {eval $inputLine} 1]
}
set inputLine ""
}
}
}
It works for linear list of commands and even allows comments and commands over multiple lines. But it fails if the user uses if statements, loops, and definition of procedures. Can you propose a better approach? It must be pure TCL script with as few extensions as possible.
One way of doing what you're asking for is to use execution traces. Here's a script that can do just that:
package require Tcl 8.5
# The machinery for tracking command execution times; prints the time taken
# upon termination of the command. More info is available too (e.g., did the
# command have an exception) but isn't printed here.
variable timerStack {}
proc timerEnter {cmd op} {
variable timerStack
lappend timerStack [clock microseconds]
}
proc timerLeave {cmd code result op} {
variable timerStack
set now [clock microseconds]
set then [lindex $timerStack end]
set timerStack [lrange $timerStack 0 end-1]
# Remove this length check to print everything out; could be a lot!
# Alternatively, modify the comparison to print more stack frames.
if {[llength $timerStack] < 1} {
puts "[expr {$now-$then}]: $cmd"
}
}
# Add the magic!
trace add execution source enterstep timerEnter
trace add execution source leavestep timerLeave
# And invoke the magic, magically
source [set argv [lassign $argv argv0];set argv0]
# Alternatively, if you don't want argument rewriting, just do:
# source yourScript.tcl
Then you'd call it like this (assuming you've put it in a file called timer.tcl):
tclsh8.5 timer.tcl yourScript.tcl
Be aware that this script has a considerable amount of overhead, as it inhibits many optimization strategies that are normally used. That won't matter too much for uses where you're doing the real meat in your own C code, but when it's lots of loops in Tcl then you'll notice a lot.
You can wrap your commands which you want to measure. And name wrappers exactly as the original ones (renaming original procs before). After that, when instrumented command is executed it actually executes the wrapper, which executes the original procedure and measure the time of execution. The example below (Tcl 8.5).
proc instrument {procs} {
set skip_procs {proc rename instrument puts time subst uplevel return}
foreach p $procs {
if {$p ni $skip_procs} {
uplevel [subst -nocommands {
rename $p __$p
proc $p {args} {
puts "$p: [time {set r [__$p {*}\$args]}]"
return \$r
}
}]
}
}
}
proc my_proc {a} {
set r 1
for {set i 1} {$i <= $a} {incr i} {
set r [expr {$r * $i}]
}
return $r
}
proc my_another_proc {a b} {
set r 0
for {set i $a} {$i <= $b} {incr i} {
incr r $i
}
return $r
}
instrument [info commands my_*]
puts "100 = [my_proc 100]"
puts "200 = [my_proc 100]"
puts "100 - 200 = [my_another_proc 100 200]"
You might want to look at the command "info complete". It can tell you if what you have accumulated so far looks complete from the point of view of most common Tcl syntax markers. It will deal with command input that might be spread across multiple physical lines.

how to pass variable arguments from one function to other in tcl

I want to pass variable arguments obtained in one function to other function but I am not able to do so. Function gets even number of variable arguments and then it has to be converted in array. Below is the example.
Procedure abc1 gets two arguments (k k) and not form abc1 procedure these have to be passed to proc abc where list to array conversion it to be done. List to array conversion works in proc1 i.e. abc1 but not in second proc i.e. abc
Error obtained is given below
proc abc {args} {
puts "$args"
array set arg $args
}
proc abc1 {args} {
puts "$args"
array set arg $args
set l2 [array get arg]
abc $l2
}
abc1 k k
abc k k
Output:
k k
{k k}
list must have an even number of elements
while executing
"array set arg $l1"
(procedure "abc" line 8)
invoked from within
"abc $l2"
(procedure "abc1" line 5)
invoked from within
"abc1 k k"
(file "vfunction.tcl" line 18)
Best Solution: Expansion Substitution
The right approach is to ensure that the outer procedure (in stack terms) calls the inner one correctly; if multiple arguments are expected, that's what should be supplied. With the advent of Tcl 8.5, that's trivially done with a little language syntax called an expansion substitution:
proc abc1 {args} {
puts "$args"
array set arg $args
set l2 [array get arg]
abc {*}$l2
# Or combine the two lines above into: abc {*}[array get arg]
}
All the {*} does is say that the rest of the word should be broken up (using list syntax rules) and used as multiple arguments instead of Tcl's default “one visual word forms a single word” rules. It's ideal for this.
Old Solution: Eval Command
If you're still using old versions of Tcl for some reason (i.e., Tcl 8.4 or before) then you use the eval command instead of the above syntax:
eval abc $l2
There are some somewhat-more-efficient approaches to the above eval, which you might see in older code; for example:
eval [linsert $l2 0 abc]
eval [list abc] [lrange $l2 0 end]
# ... etc ...
But really they're all rendered obsolete by abc {*}$l2 which is shorter, simpler to write, and faster. (It's just not available in 8.4 or before, and too many deployments have yet to upgrade.) Use the expansion syntax if you can. Indeed, idiomatic Tcl code for 8.5 and later hardly ever needs eval; the extent to which this has proved true has even been rather surprising to the language's maintainers.
There's a big difference between
abc k k
and
abc [array get arg]
In the first case, you're passing two arguments, each of which is k. In the second case you're passing a list of things - in your example, a list of two k's: k k.
Nir's answer knowingly hacks around this problem, but the better solution is to write abc1 so that it calls abc properly:
proc abc1 {args} {
array set arg $args
set l2 [array get arg]
eval abc $l2
# or just
# eval abc $args
}
When you pass abc $l2 you are passing abc1's args as a single argument to abc. So in abc args holds a list with a single item ({k k}).
You can do something like this:
proc abc {args} {
#the next line assumes that if $args has only a single item, then it is
#a list in itself. It's a risky decision to make but should work here.
if { [llength $args] == 1 } { set args [lindex $args 0] }
puts "$args"
array set arg $args
}