在TCL扩展TCL解释器

meo*_*lic 4 tcl

我已经为正式方法领域的特定工具实现了许多TCL扩展(扩展在C中实现,但我不希望解决方案依赖于这个事实).因此,我的工具的用户可以使用TCL进行原型设计算法.其中许多只是线性的命令列表(它们很强大),例如:

my_read_file f
my_do_something a b c
my_do_something_else a b c
Run Code Online (Sandbox Code Playgroud)

现在,我对时机感兴趣.可以更改脚本以获取:

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] 
Run Code Online (Sandbox Code Playgroud)

而不是这个我想要定义执行TCL脚本和获取/写入所有命令的时间的过程xsource.某种形式的探查器.我写了一个天真的实现,其主要思想如下:

 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 ""
     }
   }
 }
Run Code Online (Sandbox Code Playgroud)

它适用于线性命令列表,甚至允许多行注释和命令.但是如果用户使用if语句,循环和过程定义,它就会失败.你能提出更好的方法吗?它必须是纯TCL脚本,尽可能少的扩展.

Don*_*ows 5

做你要求的一种方法是使用执行跟踪.这是一个可以做到这一点的脚本:

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
Run Code Online (Sandbox Code Playgroud)

然后你会这样称呼它(假设你把它放在一个名为的文件中timer.tcl):

tclsh8.5 timer.tcl yourScript.tcl
Run Code Online (Sandbox Code Playgroud)

请注意,此脚本具有相当大的开销,因为它会禁止许多通常使用的优化策略.对于你在自己的C代码中使用真正的肉的用途来说,这并不重要,但是当它在Tcl中有很多循环时,你会注意到很多.