1 Tcl Fundamentals
Tcl Commands
The basic syntax for a Tcl command is:
command arg1 arg2 arg3 ...The command is either the name of a built-in command or a Tcl procedure. White space (i.e., space or tab) is used to separate the command name and its arguments, and a newline or semicolon is used to terminate a command. The arguments to a command are just strings.
Hello, World!
puts stdout {Hello, World!}=> Hello, World!
It is not necessary to declare Tcl variables before you use them.
The interpreter will create the variable when it is first assigned a value. The value of a variable is obtained later with the dollar-sign syntax illustrated in Example 1-2:
set var 5
=> 5
set b $var=> 5
set b 5The actual implementation is a little different, but not much.
Command Substitution
The second form of substitution is command substitution. A nested command is delimited by square brackets, [ ]. The Tcl interpreter takes everything between the brackets and evaluates it as a command. It rewrites the outer command by replacing the square brackets and everything between them with the result of the nested command. This is similar to the use of backquotes in other shells, except that it has the additional advantage of supporting arbitrary nesting of commands.
set len [string length foobar]=> 6
In the example, the nested command is:
string length foobarThis command returns the length of the string foobar. The string command is described in detail starting on page 43. The nested command runs first. Then command substitution causes the outer command to be rewritten as if it were:
set len 6If there are several cases of command substitution within a single command, the interpreter processes them from left to right. As each right bracket is encountered, the command it delimits is evaluated. This results in a sensible ordering in which nested commands are evaluated first so their result can be used in arguments to the outer command.
Math Expressions
The Tcl interpreter itself does not evaluate math expressions. Instead, the expr command is used to evaluate math expressions. The interpreter treats expr just like any other command, and it leaves the expression parsing up to the expr implementation. The math syntax supported by expr is the same as the C expression syntax. The expr command deals with integer, floating point, and boolean values. Logical operations return either 0 (false) or 1 (true). Integer values are promoted to floating point values as needed. Octal values are indicated by a leading zero (e.g., 033 is 27 decimal). Hexadecimal values are indicated by a leading 0x. Scientific notation for floating point numbers is supported. A summary of the operator precedence is given on page 18.
expr 7.2 / 4=> 1.8
set len [expr [string length foobar] + 7]=> 13
The expression evaluator supports a number of built-in math functions. For a complete listing, see page 19. The following example computes the value of pi:
set pi [expr 2*asin(1.0)]=> 3.1415926535897931
Backslash Substitution
The final type of substitution done by the Tcl interpreter is backslash substitution. This is used to quote characters that have special meaning to the interpreter. For example, you can specify a literal dollar sign, brace, or bracket by quoting it with a backslash. As a rule, however, if you find yourself using lots of backslashes, there is probably a simpler way to achieve the effect you are striving for. In particular, the list command described on page 55 will do quoting for you automatically. In Example 1-7 backslash is used to get a literal $:
set dollar $foo
=> $foo
set x $dollar=> $foo
Only a single round of interpretation is done.
The second set command in the example illustrates an important property of Tcl. The value of dollar does not affect the substitution done in the assignment to x. In other words, the Tcl parser does not care about the value of a variable when it does the substitution. After the example, the value of x and dollar is the string $foo. In general, you do not have to worry about the value of variables until you use eval, which is described in Chapter 10.
You can also use backslash sequences to specify characters with their hexadecimal or octal value:
set escape x1b
set escape 33The value of variable escape is the ASCII ESC character, which has character code 27. The table on page 18 summarizes backslash substitutions.
set totalLength [expr [string length $one] +
[string length $two]]
set s Hello
=> Hello
puts stdout "The length of $s is [string length $s]."
=> The length of Hello is 5.
puts stdout {The length of $s is [string length $s].}=> The length of $s is [string length $s].
In the second command of Example 1-9, the Tcl interpreter does variable and command substitution on the second argument to puts. In the third command, substitutions are prevented so the string is printed as-is.
puts [format "Item: %st%5.3f" $name $value]Here format is used to align a name and a value with a tab. The %s and %5.3f indicate how the remaining arguments to format are to be formatted. Note that the trailing n usually found in a C printf call is not needed because puts provides one for us. For more information about the format command, see page 46.
Square Brackets Do Not Group
The square bracket syntax used for command substitution does not provide grouping. Instead, a nested command is considered part of the current group. In the command below the double quotes group the last argument, and the nested command is just part of that group.
puts stdout "The length of $s is [string length $s]."In the next example the last argument is a nested command. There is no need to explicitly group the nested command because the Tcl parser treats the whole nested command as part of the group.
puts stdout [string length $s]In general, you can place a bracketed command or variable reference anywhere. The following computes a command name:
[findCommand $x] arg arg
set x 7; set y 9
puts stdout $x+$y=[expr $x + $y]=> 7+9=16
In the example the second argument to puts is:
$x+$y=[expr $x + $y]The white space inside the nested command is ignored for the purposes of grouping the argument. By the time Tcl encounters the left bracket, it has already done some variable substitutions to obtain:
7+9=When the left bracket is encountered, the interpreter calls itself recursively to evaluate the nested command. Again, the $x and $y are substituted before calling expr. Finally, the result of expr is substituted for everything from the left bracket to the right bracket. The puts command gets the following as its second argument:
7+9=16Grouping before substitution.
puts stdout "$x + $y = [expr $x + $y]"The double quotes are used for grouping in this case to allow the variable and command substitution on the argument to puts. Note that it is never necessary to explicitly group a nested command with double quotes if it makes up the whole argument. The following is a redundant use of double quotes:
puts stdout "[expr $x + $y]"
proc name arglist bodyThe first argument is the name of the procedure being defined. The second argument is a list of parameters to the procedure. The third argument is a command body that is one or more Tcl commands.
The procedure name is case sensitive, and in fact it can contain any characters. Procedure names and variable names do not conflict with each other. As a convention, this book begins procedure names with uppercase letters and it begins variable names with lowercase letters. Good programming style is important as your Tcl scripts get larger. Tcl coding style is discussed in Chapter 12.
proc Diag {a b} {
set c [expr sqrt($a * $a + $b * $b)]
return $c
}
puts "The diagonal of a 3, 4 right triangle is [Diag 3 4]"=> The diagonal of a 3, 4 right triangle is 5.0
The Diag procedure defined in the example computes the length of the diagonal side of a right triangle given the lengths of the other two sides. The sqrt function is one of many math functions supported by the expr command. The variable c is local to the procedure; it is only defined during execution of Diag. Variable scope is discussed further in Chapter 7. It is not really necessary to use the variable c in this example. The procedure could also be written as:
proc Diag {a b} {
return [expr sqrt($a * $a + $b * $b)]
}The return command is used to return the result of the prodecure. The return command is optional in this example because the Tcl interpreter returns the value of the last command in the body as the value of the procedure. So, the procedure could be reduced to:
proc Diag {a b} {
expr sqrt($a * $a + $b * $b)
}Note the stylized use of curly braces in the example. The curly brace at the end of the first line starts the third argument to proc, which is the command body. In this case, the Tcl interpreter sees the opening left brace, causing it to ignore newline characters and scan the text until a matching right brace is found. Double quotes have the same property. They group characters, including newlines, until another double quote is found. The result of the grouping is that the third argument to proc is a sequence of commands. When they are evaluated later, the embedded newlines will terminate each command. The other crucial effect of the curly braces around the procedure body is to delay any substitutions in the body until the time the procedure is called. For example, the variables a, b, and c are not defined until the procedure is called, so we do not want to do variable substitution at the time Diag is defined.
The proc command supports additional features such as having variable numbers of arguments and default values for arguments. These are described in detail in Chapter 7.
A Factorial Example
To reinforce what we have learned so far, here is a longer example that uses a while loop to compute the factorial function:
proc Factorial {x} {
set i 1; set product 1
while {$i <= $x} {
set product [expr $product * $i]
incr i
}
return $product
}
Factorial 10=> 3628800
The while loop is used to multiply all the numbers from one up to the value of x. The first argument to while is a boolean expression, and its second argument is a command body to execute. The while command evaluates the boolean expression, and then executes the body if the expression is true (non-zero). The while command continues to test the expression and evaluate the command body until the expression is false (zero). Other control structures are described in Chapter 6.
while {$i < $x}
{
set product ...
}Always group expressions and command bodies with curly braces.
set i 1; while $i<=10 {incr i}The loop will run indefinitely. The reason is that the Tcl interpreter will substitute for $i before while is called, so while gets a constant expression 1<=10 that will always be true. You can avoid these kinds of errors by adopting a consistent coding style that groups expressions with curly braces:
set i 1; while {$i<=10} {incr i}The incr command is used to increment the value of the loop variable i. This is a handy command that saves us from the longer command:
set i [expr $i + 1]The incr command can take an additional argument, a positive or negative integer by which to change the value of the variable. Using this form it is possible to eliminate the loop variable i and just modify the parameter x. The loop body can be written like this:
while {$x > 1} {
set product [expr $product * $x]
incr x -1
}
set var {the value of var}
=> the value of var
set name var
=> var
set name
=> var
set $name=> the value of var
set [set name]=> the value of var
Using a variable to store the name of another variable may seem overly complex. However, there are some times when it is very useful. There is even a special command, upvar, that makes this sort of trick easier. The upvar command is described in detail on page 79 in Chapter 7.
Funny Variable Names
The Tcl interpreter makes some assumptions about variable names that make it easy to embed variable references into other strings. By default, it assumes that variable names only contain letters, digits, and the underscore. The construct $foo.o represents a concatenation of the value of foo and the literal ".o".
set foo filename
set object $foo.o
=> filename.o
set a AAA
set b abc${a}def
=> abcAAAdef
set .o yuk!
set x ${.o}y=> yuk!y
The unset Command
You can delete a variable with the unset command:
unset varName varName2 ...Any number of variable names can be passed to the unset command. However, unset will raise an error if a variable is not already defined.
if {![info exists foobar]} {
set foobar 0
} else {
incr foobar
}In Chapter 7, page 80, there is an example that implements a new version of incr, which handles this case.
expr 1 / 3
=> 0
expr 1 / 3.0
=> 0.333333
set tcl_precision 17
=> 17
expr 1 / 3.0
# The trailing 1 is the IEEE rounding digit=> 0.33333333333333331
In Tcl 8.0 and later versions the overhead of conversions is eliminated in most cases by the built-in compiler. The use of tcl_precision is also eliminated so values are always printed with full precision. Even so, Tcl was not designed to support math intensive applications. You may want to implement math-intensive code in a compiled language and register the function as a Tcl command as described in Chapter 41.
if {$answer == "yes"} { ... }However, the string compare command described in Chapter 4 is more reliable because expr may do conversions on strings that look like numbers. This area has improved in Tcl 8.0. The issues with string operations and expr are discussed on page 45.
set y [expr {$x + $y}]
# Here are some parameters
set rate 7.0 ;# The interest rate
set months 60 ;# The loan termOne subtle effect to watch out for is that a backslash effectively continues a comment line onto the next line of the script. In addition, a semicolon inside a comment is not significant. Only a newline terminates comments:
# Here is the start of a Tcl comment
and some more of it; still in the commentThe behavior of a backslash in comments is pretty obscure, but it can be exploited as shown in Example 2-3 on page 25.
if {$x > 1}{puts "x = $x"}
set silly a"b
set x xvalue
set y "foo {$x} bar"=> foo {xvalue} bar
set x [cmd1][cmd2]
set x "This is line one.
This is line two.
This is line three."
set x $