Tcl:一个可嵌入的命令语言 :Tcl 是用于工具命令语言的一个解释器。
它由作为基本命令解释器嵌入到工具(比如编辑器、调试器 等)中的一个库包组成。
Tcl 提供了(a)用于简单的文本命令语言的分析器,(b)一组内置的实用命 令,和(c)一个 C 接口,工具通过它用特定于工具的命令增加内置命令。
Tcl 在与窗口组件库整合的 时候特别有吸引力:它通过了提供变量、过程、表达式等机制增进了组件的可编程性;它允许用户 编程组件的外观和动作二者;并且它在交互式程序之间提供一个简单但强力的通信机制。
本文出现于 1990 Winter USENIX Conference Proceedings1. 介绍 Tcl 代表了“tool command language - 工具命令语言”。
它由一个库包组成,程序可以把它用作自己 的命令语言的基础。
Tcl 的开发由两项观察所推动。
第一项观察是,通用可编程命令语言通过允许 用户用命令语言写程序来扩展工具的内置设施,从而扩大了工具的能力。
在强力的命令语言之中最 众所周知的例子是 UNIX shell[5] 和 Emacs 编辑器[8]。
在各自情况下,出现的有着不同寻常能力的 计算环境,在很大程度上是因为能获得可编程的命令语言。
第二个促成它的观察是交互式应用正在增长。
在 1970 年代晚期和 1980 年代早期的分时环境中, 几乎所有的程序都是面向批处理的。
典型的使用交互式的命令 shell 来调用它们。
除了 shell 之 外,只有少数其他的程序是交互式的,比如编辑器和邮件器。
正好相反,今天使用的个人工作站, 带有它们自己的光栅显示器和鼠标,鼓励了一种不同的系统结构,在这里大量的程序是交互式的, 并且最常见的交互方式是直接用鼠标操纵单独的应用。
此外,今天能获得的大显示器使很多交互式 的应用立即活跃起来成为可能,而对于在十年前很小的屏幕这是不实际的。
不幸的是,很少的今天的交互式程序拥有 shell 或 Emacs 命令语言的能力。
在这里好的命令语言 是存在着的,它们趋向与特定的程序捆绑在一起。
每个新的交互式程序都要求开发一个新的命令语 言。
在多数情况下,应用程序员没有时间或爱好去实现一个通用设施(特别是在应用自身很简单的 时候),所以结果的命令语言趋向于带有不充分的功能和笨拙的语法。
Tcl 是一个独立于应用的命令语言。
它作为一个 C 库包存在,可以用于很多不同的程序中。
Tcl 库 提供了用于简单但完全可编程的命令语言的一个分析器。
这个库还实现了提供了通用的编程构造的 一组内置命令,比如变量、列表、表达式、条件、循环和过程。
单个的应用程序可以用特定于应用 的命令来扩展基本的 Tcl 语言。
Tcl 库还提供一组实用工具例程来简化特定于工具的命令的实现。
我相信 Tcl 在窗口环境中是特别有用的,它提供了两项优势。
首先,它可以用做编制应用的界面的 一个通用机制。
如果一个工具基于 Tcl,则应当相对容易的去修改应用的用户界面,并使用新命令 来扩展这个界面。
其次和更重要的是,Tcl 为工具之间通信提供一种统一的框架。
如果在所有的工 具中统一使用了它,Tcl 将使工具在一起工作得比今天的状况更加优雅。
本文余下部分组织如下:第 2 节描述用户见到的 Tcl 语言。
第 3 节讨论在应用程序中如何使用 Tcl,包括在应用程序和 Tcl 库之间的 C 语言接口。
第 4 节描述在窗口环境中如何使用 Tcl 来定制 界面动作和外观。
第 5 节展示如何使用 Tcl 作为进程间通信的媒介,和为什么这很重要。
第 6 表述 Tcl 实现的状态和一些初步的性能测量。
第 7 节把 Tcl 与 Lisp、Emacs 和 NeWS 做对比,第 8 节 总结本文。
2. Tcl 语言在某种意义上,Tcl 语言的语法是不重要的:任何编程语言,不管它是 C[6]、Forth[4]、Lisp[1] 还 是 Postscript[2] 都可以提供同 Tcl 大体相同的可编程性和通信上的优势。
这提示了最佳实现途径是 借用现存的语言,并集中于为使用这门语言提供一个方便的框架。
但是,可嵌入的命令语言的所处 环境对语言提出了一组不同寻常的约束,后面将描述它们。
我最终决定了从头设计一个新语言更有 可能满足这些约束,并比现存的语言带有更少的实现努力。
Tcl 是不寻常的因为它提供两种不同的接口: 给用户发起 Tcl 命令的一个文本接口,和给它所嵌入 的应用的一个过程接口。
这些接口的每个都必须是简单的、强力的和高效的。
在语言设计中有四个 主要的因素:[1] 语言用于命令。
几乎所有 Tcl“程序”都是短小的,很多只有一行长。
多数程序将是键入的,执行 语言用于命令。
一次或者几次,接着就丢弃了。
这提示了这门语言应当有一个简单的语法,以便于键入命令。
多数 现存的编程语言都有复杂的语法;在写长程序的时候有益,但如果用做命令语言就笨拙了。
[2] 语言必须是可编程的。
它应当包含通用编程构造,比如变量、过程、条件和循环,这样用户可 语言必须是可编程的。
以通过写 Tcl 过程来扩展内置的命令。
可扩展性也要求简单的语法:这使 Tcl 程序生成其他 Tcl 程 序变得容易了。
[3] 语言必须允许一个简单而高效的解释器。
由于 Tcl 库要包含到许多小程序中,特别是在没有共 语言必须允许一个简单而高效的解释器。
享库的机器上,解释器必须不占用太多的内存。
用来解释 Tcl 命令的机制必须足够快,可用于每秒 发生上百次的事件,比如鼠标移动。
[4] 语言必须允许对 C 应用的一个简单接口。
它必须易于让 C 应用调用这个解释器,并易于让它们 应用的一个简单接口。
用特定于应用的命令来扩展内置的命令。
这个因素是我决定不使用 Lisp 作为命令语言的原因之 一:Lisp 的基本数据类型和存储管理机制与 C 实在是不同,很难在它们之间建立清晰而简单的接 口。
对 Tcl 我使用了对于 C 最自然的数据类型(字符串)。
2.1. Tcl 语言语法 Tcl 的基本语法类似于 UNIX shell:命令由用空格或 TAB 分隔的一个或多个字段组成。
第一个字段 是命令的名字,它可以是内置命令、特定于应用的命令、或者是由一系列的 Tcl 命令组成的过程。
在第一个后面的字段都作为参数传递给命令。
如同在 UNIX shell 中那样,换行字符用做命令分隔 符,分号也可用来分隔在同一行上的命令。
不同于 UNIX shell,每个 Tcl 命令返回一个字符串结 果,或者是空串,如果不适宜返回值的话。
在 Tcl 中有四个补充的语法构造,它们给予语言一种类似 Lisp 的风格。
使用花括号来组合复杂的 参数;它们充当可嵌套的引用字符。
如果参数的第一个字符是左花括号,则这个参数不以空白终 结。
转而,它终结于相匹配的右花括号。
传递给这个命令的参数由在花括号中间的所有东西组成, 并剥除围绕的花括号。
例如,命令set a {dog cat {horse cow mule} bear}将收到两个参数:“a”和“dog cat {horse cow mule} bear”。
这个特定命令将把变量 a 设置为等于第 二个参数的一个字符串。
如果参数包围在花括号中,则不对这个参数做下面描述的其他替换。
花括 号最常见的用途是把一个 Tcl 子程序指定为到 Tcl 命令的参数。
在 Tcl 中第二个语法构造是是方括号,它用于引发命令替换。
如果在参数中出现了左方括号,则从 这个左方括号一直到相匹配的右方括号的所有东西都作为一个命令来对待,并由 Tcl 解释器递归的 执行。
命令的结果接着替换到这个方括号包围的字符串所在的位置上。
例如,考虑命令set a [format {Santa Claus is %s years old} 99]format 命令做类似 printf 的格式化并返回字符串“Santa Claus is 99 years old”,接着把它传递给 set 并赋值到变量 a。
第三个语法构造是美元号,它用于变量替换。
如果它出现在参数中,则随后 的字符作为变量的名字对待;变量的内容被替换到参数中这个美元符号和名字所在的位置上。
例 如,命令set b 99 set a [format {Santa Claus is %s years old} $b]导致 a 有同前面段落中的简单命令相同的最终值。
变量替换不是严格必须的,因为有其他方式来达 到相同的效果,但是它减少了键入。
最后一个语法构造是反斜杠字符,可以用它把特殊字符插入到参数中,比如花括号或非打印字符。
2.2. 数据类型 在 Tcl 中只有一种数据类型:字符串。
所有命令、到命令的参数、命令返回的结果和变量的值都是 ASCII 字符串。
Tcl 始终使用字符串便于在 Tcl 库过程和包围它的应用的 C 代码之间来回传递信 息。
这使它易于在不同类型的机器之间来回传递有关 Tcl 的信息。
尽管在 Tcl 中所有的东西都是字符串,很多命令都希望它们的字符串参数有特定的格式。
这里的字 符串有三种特定的通用格式:列表、表达式和命令。
列表只是包含用空白分隔的一个或多个字段的 字符串,类似于命令。
可以使用花括来包围复杂的列表元素;这些复杂的列表元素自身经常也是列 表,类似于 Lisp。
例如,字符串dog cat {horse cow mule} bear是有四个元素的一个列表,其中第三个元素是有三个元素的列表。
Tcl 提供一组列表操纵的命令, 比如建立列表、提取元素、和计算列表长度。
字符串的第二种常见形式是数值表达式。
Tcl 表达式同 C 中的表达式有着同样的操作符合优先级。
Tcl 命令 expr 把字符串作为表达式来求值并返回结果(当然是作为字符串)。
例如,命令expr {($a < $b) || ($c != 0)}在变量 a 小于变量 b 或者变量 c 是零的时候返回“1”,否则返回“0”。
一些其他的命令,比如 if 和 for, 期望它们的一个或多个参数是表达式。
字符串的第三种常见解释是命令(或命令的序列)。
这种形式的参数用在实现控制结构的 Tcl 命令 中。
例如,考虑下列命令:if {$a < $b} { set tmp $a set a $b set b $tmp }这里的 if 命令接受两个参数,每个都是用花括号界定的。
If 是内置命令,它把它的第一个参数作为 表达式来求值;如果结果非零,则 if 把它的第二个参数作为 Tcl 命令执行。
这个特定命令在变量 a 小于 b 的时候交换 a 和 b 的值。
Tcl 还允许用户定义用 Tcl 语言写的命令过程。
我称谓这些过程为 tclproc,为的是区别于用 C 写 成的其他过程。