Chapter 7. Emu Extensions to Tcl

Table of Contents
Tcl Language Overview
The Emu Extensions to Tcl

A powerful feature of the Emu system is the ability to modify and extend the system by writing scripts in Tcl. Tcl (which stands for tool command language) is an interpreted language which is easily extended to provide facilities such as these.

Tcl Language Overview

Tcl is somewhat strange as a language in terms of its rules for dealing with variables. In fact the rules are quite simple and small in number so aren't too hard to master. This section gives a brief overview of the language, the reader is referred to one of the Tcl books for more complete coverage.

Commands

Tcl commands consist of a command name and any number of arguments separated by spaces. For example:

% list 1 2 3 4

will construct a list of its four arguments.

All arguments must appear on one line (with one exception discussed later), although in a file you can break a line as long as you precede the newline character with a backslash, eg:

list 1 2 3 4\
     5 6 7 8

is read as one command

list 1 2 3 4 5 6 7 8

You can define new commands (procedures) as we'll see later.

Variables

Variables can be created and given values with the 'set' command:

% set myname Steve
Steve

and examined also with the set command with no second argument:

% set myname
Steve

In tcl, you must be careful to distinguish between the name of a variable (myname above) and its value. To use the value of a variable you must precede it with a dollar sign:

% list 1 2 3 $myname 4
1 2 3 Steve 4

What happens is that the value of myname gets substituted into the command before it is evaluated, so after substitution the command reads:

% list 1 2 3 Steve 4

thus the result is as shown. Had you written:

% list 1 2 3 myname 4

the result would be:

1 2 3 myname 4

since you used the name of the variable (myname) not its value ($myname).

The times that you want to use the name of a variable are with specific commands which will either set the variable for you or evaluate it themselves (like set). Most of the time you will want to use $varname to get the variable's value. Forgetting the dollar sign can cause many many bugs in scripts.

Substitution of Command Results

As well as substituting the values of variables into commands, it is also possible to substitute the result of running a command into another command, in the following manner:

% llength [list 1 2 3 4 5]
5

here the llength command has been used to find the length of a list. Since the square bracket notation has been used, the text inside the brackets is evaluated first:

list 1 2 3 4 5
--> 1 2 3 4 5

and the command becomes:

% llength { 1 2 3 4 5 }

(the braces serve to delimit the value returned by the list command, if you typed:

% llength 1 2 3 4 5

you would be giving five arguments to llength, which is too many. What you really mean is the list of five numbers 1 2 3 4 5, which we can write as { 1 2 3 4 5 }).

Command substitution happens recursively so you can have square brackets inside my square brackets:

% llength [list 1 2 3 [list 2 3 4] 5 ]
5

since the result of the innermost list is {2 3 4}, the result of the outermost list is {1 2 3 {2 3 4} 5} which has five elements (one of which is a list).

Variables are substituted in a command before it is executed. If a variable occurs within a string (i.e., inside quotes) it is substituted into that string:

set myfirst Steve
set mysecond Cassidy
set myname "my name is $myfirst $mysecond"

sets the variable myname to "my name is Steve Cassidy". The only time that substitution doesn't occur is inside curly brackets {}:

% set myname {my name is $myfirst $mysecond}
my name is $myfirst $mysecond

This feature is useful because it delays evaluation of the variables, for example when defining procedures (where the variables will be evaluated when the procedure is run) and in loops (where they are evaluated each time around the loop).

Data Types

The data types that we have seen in Tcl already are numbers, strings, and lists. Lists are in fact really strings with each list element separated by a space and sublists delimited by curly brackets. However, special commands exists to manipulate lists so it's best to think of them as a separate type. Lists can contain a mixture of types; they don't need to be lists of just numbers or strings (or lists).

Arrays are available and can be indexed not only with numbers but with words as keys. Their elements can be set with the syntax:

% set myarray(1) help
help
% set myarray(steve) Cassidy
Cassidy

and used with the usual dollar syntax:

puts "Steve $myarray(steve)"
Steve Cassidy

(puts stands for put string and prints its single argument to the screen).

Arrays are one dimensional but you can pretend that they are multidimensional by just separating the indexes with a comma:

% set myarray(1,2) 10
% set myarray($foo,$bar) 20

but remember that all you are doing is making up a string using the indexes: therefore, myarray(1,2) is not the same as myarray(1, 2) (note the space after the comma).

Procedures

New commands are defined with the proc command, which takes two arguments: an argument list and a script. Both should be enclosed in curly brackets. For example:

proc myproc {first last} {  puts "My name is $first $last" }

Like all other procedure calls, everything must be on one line. However, since the material within curly brackets is taken literally, it can be split over more than one line as long as the opening curly bracket is on the same line as the proc so we can write:

proc myproc {first last} {  
	puts "My name is $first $last"
}

the second argument to the proc command, the script, is just a list of commands to be executed, one per line (or you can use a semicolon to separate commands). You may only refer to local variables (those in the argument list or those that you define in the procedure) unless you include a global definition in the procedure, eg:

set prefix "My name is"

proc myproc {first last} {  
	global prefix
	puts "$prefix $first $last"
}

the reference to $prefix will use the global variable. Setting prefix within this procedure will also change the global value.

Loops

The loop commands enable a script to be repeated a number of times; the curly bracket notation is commonly used to delimit the script and prevent early evaluation. You must also use curly brackets on the condition part of the loop since that is evaluated each time around the loop:

while {$i>0} {
	puts "the value of the variable i is $i"
	set i [expr $i-1]
}

note that you have to use the command expr to evaluate an expression, otherwise it's just a string "$i-1".

for {set i 0} {$i<10} {incr i} {
	puts "the value of the  variable i is $i"	
}

here there are four arguments to the for command an initialisation script, a loop test script (which in interpreted in the same way as the condition in a while loop), a script to evaluate each time around the loop, and the loop body script.

The foreach command iterates over the elements of a list, setting a variable (ll in the example below) to successive elements of the list on each iteration.

foreach ll $mylist {
	puts "this element is $ll"
}