我正在尝试使用TCL程序来读取TCL模块文件并将它们翻译成另一种语言。到目前为止,这种方法运作良好。由于太复杂的原因,我不得不在模块文件的不同部分对待“put stderr”。我正在寻求帮助,试图找到一种方法来做到这一点。
下面是一个名为“modfile”的极简缩模块文件。这个“modfile”由第二个tcl程序翻译或“获取”。
proc ModulesHelp { } {
puts stderr "(1) This is a help message"
}
puts stderr "(2) Here in modfile"
ModulesHelp中的puts语句必须与第二个puts语句区别对待。请注意,任何解决方案都不能改变“modfile”。那个档案不在我的掌控之中。
以下是我尝试解决方案:
#!/usr/bin/env tclsh
proc myPuts { stream msg } {
global putMode
puts stdout "putMode: $putMode" # <====== HERE 1
puts stdout $msg
}
proc report { message } {
puts stderr "$message"
}
proc execute-modulefile { m } {
global MODFILE putMode
set putMode "normal"
set slave "__mod"
interp create $slave
interp alias $slave puts {} myPuts
interp alias $slave report {} report
interp eval $slave {global putMode }
interp eval $slave [list "set" "putMode" $putMode]
interp eval $slave [list "set" "m" $m]
set errorVal [interp eval $slave {
set sourceFailed [catch {source $m } errorMsg]
if {[info procs "ModulesHelp"] == "ModulesHelp" } {
set putMode "InHelp" # <======= HERE 2
ModulesHelp
}
if {$sourceFailed} {
report $errorMsg
return 1
}
}]
interp delete $slave
return $errorVal
}
eval execute-modulefile $argv
要运行这个,我执行:$ ./try.tcl modfile,显然上面的脚本是“try.tcl”,而modulefile是“modfile”。我在tcl 8.4的linux系统上运行它。
我希望发生的是,在标有“HERE 2”的行中,我喜欢以某种方式将“putMode”的全局变量从“normal”更改为“InHelp”,以便我可以更改行中的行为标有“HERE 1”。无论我试图做什么,我都不能通过在“HERE 2”做一些事来改变putMode在“HERE 1”的值。置于“HERE1”的声明总是说“正常”。
使用全局变量似乎是最简单的解决方案,但如果有人可以告诉我如何使用命名空间或其他技术,我也会对此感到高兴。
感谢您的任何见解。
我非常感谢其他人查看我的问题的时间。我正在尝试使用建议的解决方案而我并没有看到它。这是我对解决方案的新尝试(这根本不起作用)。有人可以建议我如何修改此代码以将“putMode”更改为inHelp,其中“HERE 2”是?还有一些特殊的东西需要去“HERE 1”吗?
#!/usr/bin/env tclsh
proc myPuts { stream msg } {
global putMode
puts stdout "putMode: $putMode" # <=== HERE 1
puts stdout $msg
}
proc report { message } {
puts stderr "$message"
}
proc PutModeTrace {childInterp operation realPutMode} {
# Alias the main array element for the purposes of this procedure
upvar \#0 PutMode($childInterp) realPutMode
if {$operation eq "read"} {
interp eval $childInterp [list set putMode $realPutMode]
} elseif {$operation eq "write"} {
set realPutMode [interp eval $childInterp {set putMode}]
}
}
proc execute-modulefile { m } {
global MODFILE putMode
set putMode "normal"
set slave [interp create]
interp alias $slave puts {} myPuts
interp alias $slave report {} report
interp eval $slave {global putMode }
interp eval $slave [list "set" "putMode" $putMode]
interp eval $slave [list "set" "m" $m]
interp eval $slave [list "set" "slave" $slave]
interp eval $slave {trace add variable putMode {read write} PutModeTrace}
interp alias $slave PutModeTrace {} PutModeTrace $slave
set errorVal [interp eval $slave {
set sourceFailed [catch {source $m } errorMsg]
if {[info procs "ModulesHelp"] == "ModulesHelp" } {
set start "help(\[\["
set end "\]\])"
PutModeTrace $slave "write" "inHelp" # <=== HERE 2
puts stdout $start
ModulesHelp
puts stdout $end
}
if {$sourceFailed} {
report $errorMsg
return 1
}
}]
interp delete $slave
return $errorVal
}
eval execute-modulefile $argv
答案 0 :(得分:1)
问题是奴隶和主人是不同的口译员。这意味着每个翻译都有自己的
您不能简单地从主服务器更改主服务器中的变量,因此最简单的解决方案是:
interp alias $slave InHelp {} set ::putMode InHelp
并改为调用此别名。
其他一些说明:
另一种选择是在调用puts
时更改InHelp
别名。实施例
proc InHelp {slave} {
interp alias $slave puts {} HelpPuts
}
并将其与interp alias $slave {} InHelp $slave
您不必为从站指定名称。只是做
set slave [interp create]
不必引用单个单词,所以
list "a" "b" "c"
等于
list a b c
如果您需要参数扩展(并且至少使用Tcl 8.5),请使用{*}$argv
代替eval。
但是因为execute-modfile只接受一个参数,execute-modfile [lindex $argv 0]
应该完成这项工作。
答案 1 :(得分:1)
作为Johannes writes,变量在不同的解释器中是完全独立的;它们根本没有共享。
但是,您可以使用trace
和一些aliases将事物连接在一起。我将展示如何为一个简单的标量变量(父类具有它们的数组,可能每个子解释器一个),假设您永远不想在主解释器触发器中设置变量在孩子身上留下痕迹。
interp eval $child {trace add variable putMode {read write} PutModeTrace}
interp alias $child PutModeTrace {} PutModeTrace $child
proc PutModeTrace {childInterp varName elementName operation} {
# Ignore the elementName argument
# Alias the main array element for the purposes of this procedure
upvar \#0 PutMode($childInterp) realPutMode
if {$operation eq "read"} {
interp eval $childInterp [list set putMode $realPutMode]
} elseif {$operation eq "write"} {
set realPutMode [interp eval $childInterp {set putMode}]
}
}
这使得无论何时子解释器读取或写入putMode
变量,读/写都会反映到主变量中。
虽然映射命令(通过别名)更容易,但如果您使用的是Tcl 8.6,我建议在stderr
上堆叠和取消堆栈custom transformations。 (但这是一种更为复杂的技术。)
答案 2 :(得分:0)
感谢所有帮助。我花了一段时间来了解提出的建议。这是执行我想要的代码:
#!/usr/bin/env tclsh
proc myPuts { stream msg } {
global putMode
if {$putMode != "inHelp"} {
puts stderr $msg
} else {
puts stdout $msg
}
}
proc report { message } {
puts stderr "$message"
}
proc setPutMode { value } {
global putMode
set putMode $value
}
proc execute-modulefile { m } {
global MODFILE putMode
set putMode "normal"
set slave [interp create]
interp alias $slave puts {} myPuts
interp alias $slave setPutMode {} setPutMode
interp alias $slave report {} report
interp eval $slave {global putMode }
interp eval $slave [list "set" "putMode" $putMode]
interp eval $slave [list "set" "m" $m]
interp eval $slave [list "set" "slave" $slave]
interp eval $slave {trace add variable putMode {read write} PutModeTrace}
interp alias $slave PutModeTrace {} PutModeTrace $slave
set errorVal [interp eval $slave {
set sourceFailed [catch {source $m } errorMsg]
if {[info procs "ModulesHelp"] == "ModulesHelp" } {
set start "help(\[\["
set end "\]\])"
setPutMode "inHelp"
puts stdout $start
ModulesHelp
puts stdout $end
setPutMode "normal"
}
if {$sourceFailed} {
report $errorMsg
return 1
}
}]
interp delete $slave
return $errorVal
}
eval execute-modulefile $argv
再次感谢。