1.命令
Tcl是一门基于命令的脚本语言,每个命令通过换行符或分好隔开。每条命令都包含一个或多个单词,第一个单词是命令名,其他单词是命令的参数,如:
命令 | 命令名 | 参数 |
set a 15 | set | a、15 |
proc power {base p} { set result 1 while {$p > 0} { set result [expr $result*$base] set p [expr $p-1] } return $result } | proc | power、{base p}、 { set result 1 while {$p > 0} { set result [expr $result*$base] set p [expr $p-1] } return $result } |
set result [expr $result*$base]
| set | result、 [expr $result*$base] |
power 2 6 | power | 2、6 |
有些命令是Tcl语言内建的命令,由proc定义的过程也可以当做一个命令来用,用户还可以通过API来自定义命令,使用接口Tcl_CreateObjCommand()
在命令中,每个参数用空格隔开,要注意双引号、中括号和大括号里的内容整体作为一个参数。
在Tcl执行过程中,先解析命令,然后把参数作为这个命令的输入,执行命令时再由命令进一步解析参数。
2.替换
Tcl解析时会把命令分解为单词,然后根据相应的规则对这些单词执行替换操作。Tcl提供了3种形式的替换:变量替换、命令替换和反斜杠替换。
| 符号 | 举例 | 替换 |
变量替换 | $ | set a 15 expr $a*2.2 | 把$a替换为15 |
命令替换 | [] | set b [expr 13*13] | 方括号内的单词必须构成有效的Tcl脚本,里面的内容会替换为执行脚本后的返回值 |
反斜杠替换 | \ | set a bbb\ \$aa\x31 | 把“\ ”“\$”“\x31” 替换成字符空格、$、1。空格和$有特殊意义,所有要使用该字符要进行替换,\x31是把ascii码替换为字符 |
3.引用
Tcl提供了引用的方法,来阻止解释器对一些特殊字符做特殊处理。引用的方法有2种,分别是双引号引用和大括号引用。
● 双引号引用
在双引号引用中,空格、制表符、换行以及分号都做普通字符处理,如:
set msg "Eggs: \$2.18/dozen
Gasoline: \$1.49/gallon"
此时输出为:
Eggs: $2.18/dozen
Gasoline: $1.49/gallon
这里面的空格和换行符做为普通字符处理,但是仍然执行替换操作。
● 大括号引用
大括号提供了更彻底的引用形式,它会取消其中所有特殊字符的特殊意义。在大括号里的所有内容都表现为最原始的字符串,不会有任何替换,如:
set a 15
set b {$a}
此时第二行输出是$a而不是15
4.对象
4.1对象的定义
Tcl里没有专门的变量定义,变量的外在表现形式为$替换操作,内在表现形式为对象。
由set命令来定义一个对象,在Tcl里所有的对象都当作字符串来处理,以下举一些例子来理解Tcl中的对象:
set 123 abc
这里123是一个对象,它的值是abc
set abc 123
这里abc是一个对象,它的值是字符串“123”,不是数字123
特别要指出的是Tcl里的字符串一般都是指的是普通字符串(以\0结尾的),但是有时候还可以是二进制字符串,如
set a“abc\000123”,它的值是abc 123而不是abc
虽然所有的对象都是基于字符串,但是当把对象作为参数传给命令时,命令可以对其做出其他解释,比如
set a 111
set b 222
expr $a+$b
这里expr命令把对象a和对象b被解释为了数字。
命令执行后会返回一个对象
set c [expr $a+$b]
这里expr $a+$执行完后返回一个对象,这个对象的值是333,并把对象c的值设为333
4.1对象的结构
对象的结构体定义如下
typedef struct Tcl_Obj {
int refCount; /* When 0 the object will be freed. */
char *bytes; /* This points to the first byte of the
* object's string representation. The array
* must be followed by a null byte (i.e., at
* offset length) but may also contain
* embedded null characters. The array's
* storage is allocated by ckalloc. NULL means
* the string rep is invalid and must be
* regenerated from the internal rep. Clients
* should use Tcl_GetStringFromObj or
* Tcl_GetString to get a pointer to the byte
* array as a readonly value. */
int length; /* The number of bytes at *bytes, not
* including the terminating null. */
const Tcl_ObjType *typePtr; /* Denotes the object's type. Always
* corresponds to the type of the object's
* internal rep. NULL indicates the object has
* no internal rep (has no type). */
union { /* The internal representation: */
long longValue; /* - an long integer value. */
double doubleValue; /* - a double-precision floating value. */
void *otherValuePtr; /* - another, type-specific value. */
Tcl_WideInt wideValue; /* - a long long value. */
struct { /* - internal rep as two pointers. */
void *ptr1;
void *ptr2;
} twoPtrValue;
struct { /* - internal rep as a pointer and a long,
* the main use of which is a bignum's
* tightly packed fields, where the alloc,
* used and signum flags are packed into a
* single word with everything else hung
* off the pointer. */
void *ptr;
unsigned long value;
} ptrAndLongRep;
} internalRep;
} Tcl_Obj;
成员bytes是一个char*指针,用来存储字符串,length表示bytes存储的字符串的长度。
成员const Tcl_ObjType *typePtr定义了一个对象的类型,这些类型有string、int、bytearray等等,默认的是tclEmptyString类型,即为null,当作二进制字符串处理。因为一个对象有不同的类型,这就解释了为什么对象是字符串,却可以被解释为数字或数组。
假如定义一个对象Tcl_Obj *objPtr,对象被解释为数组,那么由Tcl_SetByteArrayObj()函数可以设置这个对象的数组长度,并把传入数组的内容复制到这个对象的数组里,代码如下
#define SET_BYTEARRAY(objPtr, baPtr) \
(objPtr)->internalRep.twoPtrValue.ptr1 = (void *) (baPtr)
void
Tcl_SetByteArrayObj(
Tcl_Obj *objPtr, /* Object to initialize as a ByteArray. */
const unsigned char *bytes, /* The array of bytes to use as the new
value. May be NULL even if length > 0. */
int length) /* Length of the array of bytes, which must
be >= 0. */
{
ByteArray *byteArrayPtr;
if (Tcl_IsShared(objPtr)) {
Tcl_Panic("%s called with shared object", "Tcl_SetByteArrayObj");
}
TclFreeIntRep(objPtr);
TclInvalidateStringRep(objPtr);
if (length < 0) {
length = 0;
}
byteArrayPtr = ckalloc(BYTEARRAY_SIZE(length));
byteArrayPtr->used = length;//设置数组的长度
byteArrayPtr->allocated = length;
if ((bytes != NULL) && (length > 0)) {
memcpy(byteArrayPtr->bytes, bytes, (size_t) length);//复制二进制字符串的内容
}
objPtr->typePtr = &properByteArrayType;
SET_BYTEARRAY(objPtr, byteArrayPtr);//把数组关联到对象里
}
在通过Tcl的API自定义命令时,可以通过Tcl_SetObjResult()来设置返回的对象。
5. binary命令
之前自己对binary scan和binary format这2个命令存在疑惑,有了上面这些基础总算完全明白这2个命令了。
5.1 binary format
binary format命令是把参数按照一定格式转换为二进制字符串,并返回二进制字符串,格式类型可以查看官方的说明文档,使用如下
binary formatformatString ?arg arg ...?
举例:
binary format a7a*a alpha bravo charlie
返回二进制字符串alpha\000\000bravoc
其中a代表的类型为unicode码,7代表第一个参数要转换的个数,而alpha中只有5个字符,不足的补0。第二个参数转换的类型为a*表示全部字符都转换。第3个参数类型为a,没有指定要转换的个数,那么默认只转第1个字符。
binary format i3 {3 -3 65536 1}
返回\x03\x00\x00\x00\xfd\xff\xff\xff\x00\x00\x01\x00
其中类型是小端的32位有符号整数,指定为第一个参数的前3个数字。
5.2 binary scan
与binary format相反,binary scan是把一个二进制字符串按一定的格式输出到指定的变量,并返回设置的变量的个数,使用如下
binary scanstring formatString ?varName varName ...?
举例:
set str \x05\x00\x00\x00\x07\x00\x00\x00\xf0\xff\xff\xff
binary scan $str i2i* var1 var2
返回2,var1的值是57,var2的值是-16
binary scan abcde\000fghi a6a10 var1 var2
返回1,var1的值是abcde\000,由于字符个数不够,并没有设置var2
5.3使用的对象