| ||
Nagelfar 静态语法检查器项目始于 1999 年,托管在 sourceforge 开源社区,直至今年仍在活跃更新。按项目官网的描述,其项目初衷是为了解决“a synthesis script crashing due to a misspelled variable after running for 30 hours”的问题。
TclOK 是由深圳轻思科技有限公司推出的 Tcl / SDC / UPF 检查器,已在 Challensys 官网开放下载。与 Nagelfar 项目初衷不谋而合的是,TclOK 的目标也是为了解决 Tcl 脚本出现低级错误时会导致 EDA 工具长时间运行后异常中止的问题。其不同点在于,Challensys EDA 是在发现已有(国外 / 开源 / 商用)Tcl 静态检查器仍存在过多的漏报与误报,不能真正有效解决上述问题后,才启动了 TclOK 产品的研发工作。
Nagelfar 项目使用纯 Tcl 编写,可在标准 tclsh 解释器上运行。
TclOK 基于 Challensys 自有的 FusionShell 解释器,通过将 FusionShell 解释器的“真实执行”步骤修改为“虚拟执行”,从而达到快速检查的目的。
puts -nonewline stdout 123 ;# legal set arg1 -nonewline set arg2 stdout set arg3 123 puts $arg1 $arg2 $arg3 ;# should PASS, same as above after var inline
Checking file tmp.tcl Line 7: E Wrong number of arguments (3) to "puts"
checking: puts -nonewline stdout 123 checking: set arg1 -nonewline checking: set arg2 stdout checking: set arg3 123 checking: puts -nonewline stdout 123 ****************** TCL CHECK SUMMARY ****************** Total errors : 0 Total unknown commands : 0 Possible unknown commands : 0 Hierarchical summary : tmp.tcl (errors:0, unknown_cmds:0) OK
% tclsh tmp.tcl 123123%
set arg1 -nonewline set arg2 stdout set arg3 123 if $arg3 { puts $arg1 $arg2 $arg3 ;# should PASS, active branch } else { puts this line should not be checked ;# should IGNORE, inactive branch } if $arg1 { # should FAIL, "-nonewline" is not a legal expression # blah blah }
Checking file tmp.tcl Line 5: W No braces around expression in if statement. Line 6: E Wrong number of arguments (3) to "puts" Line 8: E Wrong number of arguments (6) to "puts" Line 11: W No braces around expression in if statement.
checking: set arg1 -nonewline checking: set arg2 stdout checking: set arg3 123 checking: if 123 ... ( expr ): { 123 } checking: puts -nonewline stdout 123 checking: if -nonewline ... Error: invalid bareword "nonewline" in expression "-nonewline"; should be "$nonewline" or "{nonewline}" or "nonewline(...)" or ... Line: 11 File: tmp.tcl ****************** TCL CHECK SUMMARY ****************** Total errors : 1 Total unknown commands : 0 Possible unknown commands : 0 Hierarchical summary : tmp.tcl (errors:1, unknown_cmds:0) FAILED
% tclsh tmp.tcl invalid bareword "nonewline" in expression "-nonewline"; should be "$nonewline" or "{nonewline}" or "nonewline(...)" or ... (parsing expression "-nonewline") invoked from within "if $arg1 { # should FAIL, "-nonewline" is not a legal expression # blah blah }" (file "tmp.tcl" line 11) 123%
set i -3 while { 3 / $i } { # should FAIL after 3 iterations puts $i incr i }
Checking file tmp.tcl
checking: set i -3 checking: while { 3 / $i } ... ( expr ): { 3 / $i } ( expr ): <internal> return -1 checking: while...puts -3 checking: while...incr i ( expr ): { 3 / $i } ( expr ): <internal> return -2 checking: while...puts -2 checking: while...incr i ( expr ): { 3 / $i } ( expr ): <internal> return -3 checking: while...puts -1 checking: while...incr i ( expr ): { 3 / $i } Error: divide by zero Line: 3 File: tmp.tcl ****************** TCL CHECK SUMMARY ****************** Total errors : 1 Total unknown commands : 0 Possible unknown commands : 0 Hierarchical summary : tmp.tcl (errors:1, unknown_cmds:0) FAILED
% tclsh tmp.tcl -3 -2 -1 divide by zero while executing "while { 3 / $i } { # should FAIL after 3 iterations puts $i incr i }" (file "tmp.tcl" line 3)
source good_filepath.tcl ;# should PASS source bad_filepath.tcl ;# should FAIL, file does not exist
Checking file tmp.tcl
checking: source good_filepath.tcl <info> line 1: sourcing script 'good_filepath.tcl' <---- current script checking: puts {I'm good} <info> line 1: source script done, returning back to 'tmp.tcl' <---- current script checking: source bad_filepath.tcl Error: couldn't read file "bad_filepath.tcl": no such file or directory Line: 2 File: tmp.tcl ****************** TCL CHECK SUMMARY ****************** Total errors : 1 Total unknown commands : 0 Possible unknown commands : 0 Hierarchical summary : tmp.tcl (errors:1, unknown_cmds:0) ├─good_filepath.tcl (errors:0, unknown_cmds:0) └─bad_filepath.tcl (Error: file not found) FAILED
% tclsh tmp.tcl I'm good couldn't read file "bad_filepath.tcl": no such file or directory while executing "source bad_filepath.tcl " (file "tmp.tcl" line 2)
从上述几个简单例子可见,开源 Tcl 静态语法检查器普遍存在很大的局限性,对非常简单的 Case 也存在大量的漏报、误报和非必要的警告。我们猜测其局限性主要来源于它们是基于标准的 tclsh 解释器之上开发的,既无法真正去“执行”Tcl 语句(这样就起不到加速检查的效果),而只能进行一些浅显的“表面”检查(这会造成与tclsh实际执行存在诸多差异)。而 Challensys 本身拥有 FusionShell 解释器,而 TclOK 是直接对 FusionShell 解释器底层进行了修改,从而既实现了加速检查,又做到了与解释器实际执行脚本高度一致的报错行为,具有更高的项目实用价值。