如何让用户提供一个字符串 "mangler "作为regexprocexpr的正确方法

问题描述 投票:0回答:1

在我的TclTk项目中,我需要允许我的用户以一种明确的方式来纠错字符串。

我的想法是,允许用户在配置文件中声明一个 "字符串纠错 "procexprfunction...,然后应用到相关的字符串上。

我有点担心如何正确地实现这一点。

到目前为止,我考虑过的可能性有

  • 正则表达式

    这是我的第一个想法,但有两个注意事项。

    • 在Tcl中使用正则表达式的searchreplace似乎很笨拙. 至少在使用了 规章制度 我需要分别传递匹配和替换的部分(与e.g.的做法相反,我需要传递一个复杂的字符串。sed 允许我传递一个复杂的字符串,为我做所有的事情);在这里有 sed 实施 但它们看起来很天真,可能早晚会坏掉。
    • 此外,重构词组本身也很笨拙;使用它们来混淆复杂的字符串往往比应该的更复杂。
  • procs?

    既然目标平台是Tcl,那么为什么不利用Tcl的强大功能来做字符串杂烩呢?"函数 "应该有一个单一的输入和产生一个单一的输出,而且理想的情况是,它应该引导用户做正确的事情(例如,不能定义一个需要两个参数的proc),而且它不可能产生副作用(比如改变应用程序的状态)。

    一个简单的方法是使用 proc mymangler s $body (有 $body 是用户定义的字符串),但有很多事情可能出错。

    • $body 假设一个不同的arg-name(例如 $x 而不是 $s)
    • $body 一无所获
    • $body 在环境中改变变量,...。

    expr句子看起来更像它(总是返回东西,不允许轻易修改环境),但我不能让它们在字符串上工作,而且没有办法在不同意其名称的情况下传递一个变量。

所以,目前我想到的最好的办法是。

set userfun {return $s}      # user-defined string
proc mymangler s ${userfun}
set output [mymangler $input]

有没有更好的方法可以在Tcl中实现用户自定义的字符串编辑器?

string tcl user-input
1个回答
1
投票

你可以使用 apply --用户提供了一个2元素的列表:第二个元素是 "proc body",即做混搭的代码;第一个元素是存放字符串的变量名,这个变量在body中使用。

比如说

set userfun {{str} {string reverse $str}}
set input "some string"
set result [apply $userfun $input]    ;# => "gnirts emos"

当然,你从用户那里得到的代码是任意的Tcl代码。你可以在一个 安全翻译:

set userfun {{str} {exec some malicious code; return [string reverse $str]}}

try {
    set interp [safe::interpCreate]
    set result [$interp eval [list apply $userfun $input]]
    puts "mangled string is: $result"
    safe::interpDelete $interp
} on error e {
    error "Error: $e"
}

结果:

Error: invalid command name "exec"

注释:

  • 使用标准的Tcl命令。apply
  • 用户必须指定在正文中使用的变量名称。
  • 这个方案确实保护了环境。

    set userfun {{str} {set ::env(SOME_VAR) "safe slave"; return $str$str}}
    set env(SOME_VAR) "main"
    
    puts $env(SOME_VAR)
    try {
        set interp [safe::interpCreate]
        set result [$interp eval [list apply $userfun $input]]
        puts "mangled string is: $result"
        safe::interpDelete $interp
    } on error e {
        error "Error: $e"
    }
    puts $env(SOME_VAR)
    

    输出

    main
    mangled string is: some stringsome string
    main
    
  • 如果用户没有返回一个值,那么被篡改的字符串就是简单的空字符串。

1
投票

这种 "简单化 "的方法就像 foreach 中,它需要用户提供一个变量名和一个使用该变量的脚本来评估,是一个很好的方法。如果你不想让它影响程序的其他部分,可以在一个单独的解释器中运行它。

set x 0
proc mymangler {name body} {
   set i [interp create -safe]
   set s "some string to change"
   try {
       # Build the lambda used by apply here instead of making
       # the user do it.
       $i eval [list apply [list $name $body] $s]
   } on error e {
       return $e
   } finally {
      interp delete $i
   }
}
puts [mymangler s { set x 1; string toupper $s }]
puts $x

输出

SOME STRING TO CHANGE
0

如果打电话的人说要用 s 作为一个变量,然后在正文中使用其他的东西,这是在他们身上。和提供一个不返回任何东西的脚本一样。


1
投票

我一般会允许用户指定一个 命令前缀 作为一个 Tcl 列表 (大多数简单的命令名都适合于此),然后你可以将其应用到参数中。

set mangled [{*}$commandPrefix $valueToMangle]

这让人们可以提供任何他们想要的东西,尤其是他们可以使用 apply 和一个lambda项来根据需要来捣鼓东西。当然,如果你是在一个过程中,那么你可能会更好地做。

set mangled [uplevel 1 [list {*}$commandPrefix $valueToMangle]]

这样你就会在调用者的上下文中运行(改变了 1#0 来代替使用全局上下文),这可以帮助保护你的过程不被意外更改,并使使用 upvar 内的mangler更容易。

如果mangling前缀的源头是不受信任的(这意味着什么,很大程度上取决于你的应用和部署),那么你可以在一个单独的解释器中运行mangling代码。

# Make the safe evaluation context; this is *expensive*
set context [interp create -safe]
# You might want to let them define extra procedures too
#     interp invokehidden $context source /the/users/file.tcl

# Use the context
try {
    set mangled [interp eval $context [list {*}$commandPrefix $valueToMangle]]
} on error {msg} {
    # User supplied something bad; error message in $msg
}

有各种方法来支持用户指定转换,但如果你能向他们暴露你正在使用Tcl工作的事实,那么这可能是最简单和最灵活的。

© www.soinside.com 2019 - 2024. All rights reserved.