TCL脚本语言-3-置换:TCL的灵魂
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
而右边的代码中,同样是三个单词,只不过后面的两个单词被双引号括着,所以在执行 if 命令之前,解释器会先对这两个单词进行置换,把里面的$a 用 a 的值替代,然后把替换后 的单词作为参数传递给 if 命令。
下面的代码会进入死循环,为什么?大家自行分析。
作者:雷雨后
Email: leiyuhou010@gmail.com
;#第一条命令 class 及其两个参数 ;#成员变量,保护类型,可以被继承
constructor {name sex} { set m_name $name set m_sex $sex
}
;#构造函数
public method PrintInfo {} { puts "CPerson [GetInfo]"
TCL、Python 和软件测试自动化
13
基本词法和概念
在进一步深入了解 TCL 的语法之前,弄清楚 TCL 脚本的几个基本概念以及 TCL 解释器 解释执行一个脚本的基本流程,是很有必要的。
一切都是命令和及其参数
TCL 脚本语法的本质其实非常简单: 1. 一个脚本是由一个或多个命令以及其参数顺序排列而成;命令之间用换行字符或者
分号分隔;TCL 中的一切都是命令及其参数。 2. 一个命令语句包括一个命令字以及零个或多个该命令的参数;命令和参数以及参数
之间用空格或者 Tab 分隔; 3. 如果任何地方出现可以进行置换的操作,那么就会按照规则进行置换; 例如下面的一段代码实际上是由三个命令组成的:
class CPerson { protected variable m_name protected variable m_sex
"if {$a>100}"
第一种情况,{$a>100}和后面的{之间没有空格,那么就会报错:因为不符合单词的语 法。第二种情况,{直接拿到了 if 命令的第二行,那么 TCL 认为这里的 if 命令在本行就结束, 那么 if 命令就只有一个参数,显然也会报错。
作者:雷雨后
Email: leiyuhou010@gmail.com
set a 100
就包含了三个单词:set、a 和 100 但是也有一些特殊情况:
双引号
如果一个单词以一个双引号字符开始,那么这个单词会以下一个双引号作为结束。如果 在这两个双引号之间出现了分号“;”、反方括号“]”、以及空格或者换行字符,那么这些字 符都作为单词中的字面字符出现,它们不具备任何特殊含义。开始和结束的双引号不作为这 个单词内的字符。例如:
TCL、Python 和软件测试自动化
19
wrong # args: should be "set varName ?newValue?"
while executing
"set a 100
#创建变量 a,不合法的注释"
上面的错误,是我们经常犯的错误。 上面例子中,for 循环命令中的注释,似乎和前面介绍的 TCL 注释规则相违背,注释并 没有从“#”直到一行结束,而是只到花括号结束。仔细分析,实际上并不违背:例如 for 的第一个参数,包括里面的注释,是整个作为一个单词被传递给 for 命令来解释的,for 命令 执行这个单词的时候,就会正确的解释其中的注释。
下面就是一个 do 命令的简单实现,使用 TCL 写成:
#定义 do 命令 proc do { body while_key cond} {
if { $while_key != "while" } { error "the second parameter must be \"while\"."
}
三行代码,三个换行符只有最后一个是 if 命令的分隔符;而前两个相对于 if 命令而言, 只是其第二个参数中的字符而已。初学者往往会把上面的代码写成下面两种形式:
if {$a>100}{ puts “a = $a ”
}
;#条件判断的}后面没有空格,直接跟着{
if {$a>100} {
puts “a = $a ” }
TCL、Python 和软件测试自动化
18
set a 100 set i 0 set sum 0
while "$i <= $a" { incr sum $i incr i
}
;#如果换成{$i<=$a},就不会死循环了。
puts $sum
需要指出的是,上面解释执行过程中的第三步是可以嵌套的,也就是说,在执行一个过 程的时候,在这个过程内部也可以通过这个过程来执行另外一块脚本代码。就比如上面的 while 循环,它的第一个参数是条件表达式”$i<=$a”,第二个参数是一段代码;在执行 while 命令的过程中,首先计算条件表达式,根据表达式的值确定是否执行下面的代码,如果条件 满足,那么就执行。执行这一段代码的过程和执行 while 的类似:先划分成命令序列,再顺 序执行每一条命令。这一段代码如何执行,是由 while 命令来控制,而不是 TCL 解释器所控 制的。所以,while 不过是一个命令!
;#条件判断的后面直接回车,把执行体的{放到下一行
两种写法都是错误的。错误代码分别如下:
extra characters after close-brace while executing
"if {$a>100}{ puts "a = $a "
}
wrong # args: no script following "$a>100" argument while executing
while executing "set b { \{ Sat "Hello" } }"
最后一行代码出错了,因为 set 第三个单词内,嵌套的花括号不能匹配上。如果一个单 词是用花括号括起来的,那么里面所有的置换都不会发生。我们回归头来看看前面 CPerson 类的定义,类的定义部分就是一个用花括号括起来的,包含多行字符创的单词。
set a 100 ”set” “a” “100” set b “First Second” set b First Second set c “The first line
The second line”
;#命令 1 ;#命令 2 ;#命令 3 ;#命令 4,错误
上面代码中,前面两个命令是完全等价的,虽然命令 2 中三个单词都用双引号括起来, 但是三个单词和命令 1 中的三个单词完全一样。执行后变量 a 的值都为 100;命令 3 中第三 个单词用引号括起来,其中包含了空格,如果没有引号,象命令 4 中所示,那么就会出现错 误。因为 set 命令不能够接受三个参数。最后一个命令中也包含三个单词,最后一个单词是 用引号括起来的跨行字符串,里面包含了换行符号。
for {set sum 0;set i 0;#初始化} {$i<=100} {incr i;#递增} { incr sum $i
}
puts $sum
第三行注释不正确,因为这里#以及后面的字符都被解释成了 set 命令的参数,所以会报 错:
作者:雷雨后
Email: leiyuhou010@gmail.com
}
;#public 方法,输出对象信息,可以被继承 ;#调用了成员函数 GetInfo
public method GetInfo {} {
return "name=$m_name; sex=$m_sex" ;#返回对象信息
}
}
;#第一条命令结束。
CPerson a “Lei Yuhou” Male a PrintInfo
把脚本解析成命令序列
Yes
全部命令执行?
No
取下一条命令
该命令分解成单词 必要的置换操作
执行该命令
执行结束
理解了上面的脚本执行过程,就可以理解上一节所说的“一定程度”是什么意思了:执 行器在执行一条命令的之前,首先把命令划分成单词,然后根据单词的写法来进行置换。
在上一节左边的代码中,代码被划分成三个单词,但是后面两个单词都是用花括号括着 的,所以没有不会发生置换,直接把它们作为参数调用 if 命令。
命令之间只有两种分隔符号:分号或者换行符号。所以如果我们要把两个命令写在一行, 那么必须使用分号分隔它们。例如上面的后两个命令:
CPerson a “Lei Yuhou” Male ; a PrintInfo 要注意的是,并不是所有的分号和换行符号都是命令分隔符!!例如下面的代码:
if {$a>100} { puts “a = $a ”
没有 do...while?自己实现
TCL 中没有 do...while 循环,但是我们明白了上面的道理之后,完全可以自己实现一个 do 命令,作为我们自己的一种控制结构。这在其他的 Python、Pascal 等语言中是很难想象的: python 里面没有 switch,你试着在 python 实现一个 switch,看能否实现?
TCL、Python 和软件测试自动化
15
什么时候回车字符作为命令结束符,什么时候又不是?为什么会出现第一种错误?回答 这些问题之前,我们必须弄清楚另外一个重要概念:单词!
单词、引号、括号
一个脚本是由多个命令顺序排列而成;而一个命令则是由多个单词组成。单词是由空格 来进行分隔的一个字符串,例如下面的命令中:
脚本注释
理解了脚本的执行过程,就不难理解 TCL 中的注释规则: 当 TCL 解释器在解释执行脚本的时候,如果期望下一个单词是一个命令,但是这个单 词的第一个字符是“#”,那么从这个“#”开始直到这一行的结束,都被 TCL 当作注释而忽 略过去。 下面是几个例子:
#这是一行合法的注释 set a 100 ;#创建变量 a,合法的注释 set a 100 #创建变量 a,不合法的注释;
;#第二条命令 ;#第三条命令
作者:雷雨后
Email: leiyuhou010@gmail.com
TCL、Python 和软件测试自动化
14
上面代码很长,但是实际上只有三个命令:第一个是 class 命令,带有两个参数,第一 个是类名,第二个是类的定义体;第二个命令是 CPerson 命令,实际上刚才的 class 命令在 执行之后就定义了一个新的 TCL 命令,命令名就是我们声明的类名;第三个命令的命令字 是 a,这是我们刚才创建的对象,实际上 CPerson 命令在执行的时候又创建了一个 TCL 命令, 其名字就是我们给出的对象名。
set a { {青山依旧在,} {几度夕阳红。}
}
set b { \{ Sat "Hello" \} }
puts $a puts $b
set b { \{ Sat "Hello" } }
代码执行结果如下:
{青山依旧在,} {几度夕阳红。}
\{ Sat "Hello" \} wrong # args: should be "set varName ?newValue?"
用双引号括起来的单词中,所有的置换类型都可以发生。具体我们后面在置换一节中详 细解释。
花括号
如果一个单词以字符“{”开始,那么它必须以相对应的反括号字符“}”作为结束。单
作者:雷雨后
Email: leiyuhou010@gmail.com
TCL、Python 和软件测试自动化
16
词内部的花括号可以嵌套,但是它们必须配对出现;如果某一个花括号前面是反斜线,那么 TCL 解释器在寻找配对括号的时候该括号不计算在内。该单词是所有出现在花括号在内的原 始字符,但是不包含开始和结束的花括号。例如下面的代码:
作者:雷雨后
Email: leiyuhou010@gmail.com
TCL、Python 和软件测试自动化
17
序列; 2. 按照从上到下的顺序,逐个执行命令; 3. 执行一个命令的时候,
a) 如果组成命令的单词中存在变量、反斜线换或者命令置换,会根据单词的特点 进行置换;
b) 置换后,把第一个单词作为命令字,到命令库中找到对应的命令执行体; c) 如果找到,把后面的单词作为参数传入给命令,执行命令; d) 如果没有找到,那么就通过一定的机制继续寻找,如果找不到,就抛Baidu Nhomakorabea异常; 4. 所有的命令执行完,那么脚本执行就结束。
通过上面的分析,可以看到,下面的两块代码在“一定程度上”是等价的:
if {$a>100} { puts “a = $a”
}
if “$a>100” “ puts \“a = $a\”
”
为什么说是“一定程度”呢,我们有必要了解 TCL 解释执行脚本的过程。
解释执行过程
TCL 解释器执行脚本的过程就是: 1. 根据前面介绍的命令分隔规则和单词划分规则,将脚本文件内容解析成多个命令的
下面的代码会进入死循环,为什么?大家自行分析。
作者:雷雨后
Email: leiyuhou010@gmail.com
;#第一条命令 class 及其两个参数 ;#成员变量,保护类型,可以被继承
constructor {name sex} { set m_name $name set m_sex $sex
}
;#构造函数
public method PrintInfo {} { puts "CPerson [GetInfo]"
TCL、Python 和软件测试自动化
13
基本词法和概念
在进一步深入了解 TCL 的语法之前,弄清楚 TCL 脚本的几个基本概念以及 TCL 解释器 解释执行一个脚本的基本流程,是很有必要的。
一切都是命令和及其参数
TCL 脚本语法的本质其实非常简单: 1. 一个脚本是由一个或多个命令以及其参数顺序排列而成;命令之间用换行字符或者
分号分隔;TCL 中的一切都是命令及其参数。 2. 一个命令语句包括一个命令字以及零个或多个该命令的参数;命令和参数以及参数
之间用空格或者 Tab 分隔; 3. 如果任何地方出现可以进行置换的操作,那么就会按照规则进行置换; 例如下面的一段代码实际上是由三个命令组成的:
class CPerson { protected variable m_name protected variable m_sex
"if {$a>100}"
第一种情况,{$a>100}和后面的{之间没有空格,那么就会报错:因为不符合单词的语 法。第二种情况,{直接拿到了 if 命令的第二行,那么 TCL 认为这里的 if 命令在本行就结束, 那么 if 命令就只有一个参数,显然也会报错。
作者:雷雨后
Email: leiyuhou010@gmail.com
set a 100
就包含了三个单词:set、a 和 100 但是也有一些特殊情况:
双引号
如果一个单词以一个双引号字符开始,那么这个单词会以下一个双引号作为结束。如果 在这两个双引号之间出现了分号“;”、反方括号“]”、以及空格或者换行字符,那么这些字 符都作为单词中的字面字符出现,它们不具备任何特殊含义。开始和结束的双引号不作为这 个单词内的字符。例如:
TCL、Python 和软件测试自动化
19
wrong # args: should be "set varName ?newValue?"
while executing
"set a 100
#创建变量 a,不合法的注释"
上面的错误,是我们经常犯的错误。 上面例子中,for 循环命令中的注释,似乎和前面介绍的 TCL 注释规则相违背,注释并 没有从“#”直到一行结束,而是只到花括号结束。仔细分析,实际上并不违背:例如 for 的第一个参数,包括里面的注释,是整个作为一个单词被传递给 for 命令来解释的,for 命令 执行这个单词的时候,就会正确的解释其中的注释。
下面就是一个 do 命令的简单实现,使用 TCL 写成:
#定义 do 命令 proc do { body while_key cond} {
if { $while_key != "while" } { error "the second parameter must be \"while\"."
}
三行代码,三个换行符只有最后一个是 if 命令的分隔符;而前两个相对于 if 命令而言, 只是其第二个参数中的字符而已。初学者往往会把上面的代码写成下面两种形式:
if {$a>100}{ puts “a = $a ”
}
;#条件判断的}后面没有空格,直接跟着{
if {$a>100} {
puts “a = $a ” }
TCL、Python 和软件测试自动化
18
set a 100 set i 0 set sum 0
while "$i <= $a" { incr sum $i incr i
}
;#如果换成{$i<=$a},就不会死循环了。
puts $sum
需要指出的是,上面解释执行过程中的第三步是可以嵌套的,也就是说,在执行一个过 程的时候,在这个过程内部也可以通过这个过程来执行另外一块脚本代码。就比如上面的 while 循环,它的第一个参数是条件表达式”$i<=$a”,第二个参数是一段代码;在执行 while 命令的过程中,首先计算条件表达式,根据表达式的值确定是否执行下面的代码,如果条件 满足,那么就执行。执行这一段代码的过程和执行 while 的类似:先划分成命令序列,再顺 序执行每一条命令。这一段代码如何执行,是由 while 命令来控制,而不是 TCL 解释器所控 制的。所以,while 不过是一个命令!
;#条件判断的后面直接回车,把执行体的{放到下一行
两种写法都是错误的。错误代码分别如下:
extra characters after close-brace while executing
"if {$a>100}{ puts "a = $a "
}
wrong # args: no script following "$a>100" argument while executing
while executing "set b { \{ Sat "Hello" } }"
最后一行代码出错了,因为 set 第三个单词内,嵌套的花括号不能匹配上。如果一个单 词是用花括号括起来的,那么里面所有的置换都不会发生。我们回归头来看看前面 CPerson 类的定义,类的定义部分就是一个用花括号括起来的,包含多行字符创的单词。
set a 100 ”set” “a” “100” set b “First Second” set b First Second set c “The first line
The second line”
;#命令 1 ;#命令 2 ;#命令 3 ;#命令 4,错误
上面代码中,前面两个命令是完全等价的,虽然命令 2 中三个单词都用双引号括起来, 但是三个单词和命令 1 中的三个单词完全一样。执行后变量 a 的值都为 100;命令 3 中第三 个单词用引号括起来,其中包含了空格,如果没有引号,象命令 4 中所示,那么就会出现错 误。因为 set 命令不能够接受三个参数。最后一个命令中也包含三个单词,最后一个单词是 用引号括起来的跨行字符串,里面包含了换行符号。
for {set sum 0;set i 0;#初始化} {$i<=100} {incr i;#递增} { incr sum $i
}
puts $sum
第三行注释不正确,因为这里#以及后面的字符都被解释成了 set 命令的参数,所以会报 错:
作者:雷雨后
Email: leiyuhou010@gmail.com
}
;#public 方法,输出对象信息,可以被继承 ;#调用了成员函数 GetInfo
public method GetInfo {} {
return "name=$m_name; sex=$m_sex" ;#返回对象信息
}
}
;#第一条命令结束。
CPerson a “Lei Yuhou” Male a PrintInfo
把脚本解析成命令序列
Yes
全部命令执行?
No
取下一条命令
该命令分解成单词 必要的置换操作
执行该命令
执行结束
理解了上面的脚本执行过程,就可以理解上一节所说的“一定程度”是什么意思了:执 行器在执行一条命令的之前,首先把命令划分成单词,然后根据单词的写法来进行置换。
在上一节左边的代码中,代码被划分成三个单词,但是后面两个单词都是用花括号括着 的,所以没有不会发生置换,直接把它们作为参数调用 if 命令。
命令之间只有两种分隔符号:分号或者换行符号。所以如果我们要把两个命令写在一行, 那么必须使用分号分隔它们。例如上面的后两个命令:
CPerson a “Lei Yuhou” Male ; a PrintInfo 要注意的是,并不是所有的分号和换行符号都是命令分隔符!!例如下面的代码:
if {$a>100} { puts “a = $a ”
没有 do...while?自己实现
TCL 中没有 do...while 循环,但是我们明白了上面的道理之后,完全可以自己实现一个 do 命令,作为我们自己的一种控制结构。这在其他的 Python、Pascal 等语言中是很难想象的: python 里面没有 switch,你试着在 python 实现一个 switch,看能否实现?
TCL、Python 和软件测试自动化
15
什么时候回车字符作为命令结束符,什么时候又不是?为什么会出现第一种错误?回答 这些问题之前,我们必须弄清楚另外一个重要概念:单词!
单词、引号、括号
一个脚本是由多个命令顺序排列而成;而一个命令则是由多个单词组成。单词是由空格 来进行分隔的一个字符串,例如下面的命令中:
脚本注释
理解了脚本的执行过程,就不难理解 TCL 中的注释规则: 当 TCL 解释器在解释执行脚本的时候,如果期望下一个单词是一个命令,但是这个单 词的第一个字符是“#”,那么从这个“#”开始直到这一行的结束,都被 TCL 当作注释而忽 略过去。 下面是几个例子:
#这是一行合法的注释 set a 100 ;#创建变量 a,合法的注释 set a 100 #创建变量 a,不合法的注释;
;#第二条命令 ;#第三条命令
作者:雷雨后
Email: leiyuhou010@gmail.com
TCL、Python 和软件测试自动化
14
上面代码很长,但是实际上只有三个命令:第一个是 class 命令,带有两个参数,第一 个是类名,第二个是类的定义体;第二个命令是 CPerson 命令,实际上刚才的 class 命令在 执行之后就定义了一个新的 TCL 命令,命令名就是我们声明的类名;第三个命令的命令字 是 a,这是我们刚才创建的对象,实际上 CPerson 命令在执行的时候又创建了一个 TCL 命令, 其名字就是我们给出的对象名。
set a { {青山依旧在,} {几度夕阳红。}
}
set b { \{ Sat "Hello" \} }
puts $a puts $b
set b { \{ Sat "Hello" } }
代码执行结果如下:
{青山依旧在,} {几度夕阳红。}
\{ Sat "Hello" \} wrong # args: should be "set varName ?newValue?"
用双引号括起来的单词中,所有的置换类型都可以发生。具体我们后面在置换一节中详 细解释。
花括号
如果一个单词以字符“{”开始,那么它必须以相对应的反括号字符“}”作为结束。单
作者:雷雨后
Email: leiyuhou010@gmail.com
TCL、Python 和软件测试自动化
16
词内部的花括号可以嵌套,但是它们必须配对出现;如果某一个花括号前面是反斜线,那么 TCL 解释器在寻找配对括号的时候该括号不计算在内。该单词是所有出现在花括号在内的原 始字符,但是不包含开始和结束的花括号。例如下面的代码:
作者:雷雨后
Email: leiyuhou010@gmail.com
TCL、Python 和软件测试自动化
17
序列; 2. 按照从上到下的顺序,逐个执行命令; 3. 执行一个命令的时候,
a) 如果组成命令的单词中存在变量、反斜线换或者命令置换,会根据单词的特点 进行置换;
b) 置换后,把第一个单词作为命令字,到命令库中找到对应的命令执行体; c) 如果找到,把后面的单词作为参数传入给命令,执行命令; d) 如果没有找到,那么就通过一定的机制继续寻找,如果找不到,就抛Baidu Nhomakorabea异常; 4. 所有的命令执行完,那么脚本执行就结束。
通过上面的分析,可以看到,下面的两块代码在“一定程度上”是等价的:
if {$a>100} { puts “a = $a”
}
if “$a>100” “ puts \“a = $a\”
”
为什么说是“一定程度”呢,我们有必要了解 TCL 解释执行脚本的过程。
解释执行过程
TCL 解释器执行脚本的过程就是: 1. 根据前面介绍的命令分隔规则和单词划分规则,将脚本文件内容解析成多个命令的