Tcl

References

Introduction

Tcl is a string-based command language. The basic mechanisms are all related to strings and string substitutions. Tcl stands for Tool Command Language. It is designed to be a glue that assembles software building blocks into applications. Tcl is interpreted when the application runs.

The main Tcl/Tk program is wish. Wish stands for windowing shell, and with it you can create graphical applications that run on all platforms. Tk adds Tcl commands that are used to create graphical user interfaces. You can run Tcl without Tk if you do not need a graphical interface. In this case the program is tclsh.

Tcl commands

Tcl casts everything into the mold of a command, even programming constructs like variable assignment and procedure definition. Tcl adds a tiny amount of syntax needed to properly invoke commands, and leaves all the hard work up to the command implementation. 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., spaces or tabs) is used to separate the command name and its arguments. A newline or semicolon is used to terminate a command. Tcl does not interpret the arguments to the commands except for:

  • grouping, to allow multiple words in one argument
  • substitution, which is used with programming variables and nested command calls.

Double quotes and curly braces are used to group words together into one argument. Double quotes allow substitutions to occur in the group, curly braces prevent substitutions.

puts stdout {Hello, World!}

A nested command is delimited by square brackets, [ ]. The Tcl interpreter takes everything between the brackets and evaluates it as a command.

set len [string length foobar]

The behavior of the Tcl command processor can be summarized in three basic steps:

  • Argument grouping.
  • Value substitution of nested commands, variables, and backslash escapes.
  • Command invocation.

It is up to the command to interpret its arguments.

Tcl uses the pound character, #, for comments. The # must occur at the beginning of a command. A # that occurs elsewhere is not treated specially. An easy trick to append a comment to the end of a command is to precede the # with a semicolon to terminate the previous command.

Variables

The set command is used to assign a value to a variable. It is not necessary to declare Tcl variables before you use them. The value of a variable is obtained later with the dollar-sign syntax.

set var 5
set b $var

The expr command is used to parse and evaluate math expressions.

set pi [expr 2*asin(1.0)]

You can make expr operate more efficiently by grouping the entire expression in curly braces.

expr {7.2 / 4}

Backslash substitution is used to quote characters that have special meaning to the interpreter.

set dollar \$foo
=> $foo
set x $dollar
=> $foo

The Tcl interpreter assumes that variable names contain only letters, digits, and the underscore. The construct $foo.o represents a concatenation of the value of foo and the literal “.o”.

Procedures

A Tcl procedure is used just like any of the other built-in Tcl commands. The basic syntax to define a procedure is:

proc name arglist body

The procedure name is case sensitive. Procedure names and variable names do not conflict with each other. As a convention, begin procedure names with uppercase letters and variable names with lowercase letters.

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

It is not really necessary to use the variable c in this example. Even the return command is optional because the Tcl interpreter returns the value of the last command in the body as the value of the procedure.

String processing

The string command is really a collection of operations you can perform on strings. The first argument to string determines the operation. A few of the more commonly used string operations are:

  • length
  • compare, equal and match
  • tolower, totitle, and toupper
  • trim, trimright, and trimleft

The following example calculates the length of the value of a variable.

set name "Brent Welch"
string length $name
=> 11

Compare two strings:

if {[string compare $s1 $s2] == 0} {
  # strings are equal
}

Check if two strings are equal:

if {[string equal $s1 $s2]} {
  # strings are equal
}

Match all strings that begin with a:

string match a* alpha
=> 1

To match all two-letter strings:

string match ?? XY
=> 1

To match all strings that begin with either a or b:

string match {[ab]*} cello
=> 0

The format command is similar to the C printf function:

format "%-20s %3d" Label 2
=> Label               2

Tcl lists

A Tcl list is a sequence of values. A list has its elements separated by white space. Braces or quotes can be used to group words with white space into a single list element.

The list command constructs a list out of its arguments so that there is one list element for each argument. If any of the arguments contain special characters, the list command adds quoting to ensure that they are parsed as a single element of the resulting list.

set x {1 2}
=> 1 2
set y foo
=> foo
set l1 [list $x "a b" $y]
=> {1 2} {a b} foo
set l2 "\{$x\} {a b} $y"
=> {1 2} {a b} foo      ;# llength $l2 => 3
set l3 "$x {a b} $y"
=> 1 2 {a b} foo        ;# llength $l3 => 4

Append elements to the end of a list:

lappend new 1 2
=> 1 2
lappend new 3 "4 5"
=> 1 2 3 {4 5}
set new
=> 1 2 3 {4 5}

Splicing lists together:

set x {4 5 6}
set y {2 3}
set z 1
concat $z $y $x
=> 1 2 3 4 5 6

Get the number of elements in a list:

llength {a b {c d} "e f g" h}
=> 5
llength {}
=> 0

Get a particular element of a list:

set x {1 2 3}
lindex $x 1
=> 2

Delete a list element by value:

proc ldelete { list value } {
  set ix [lsearch -exact $list $value]
  if {$ix >= 0} {
    return [lreplace $list $ix $ix]
  } else {
    return $list
  }
}

Sort a list:

lsort -ascii {a Z n2 n100}
=> Z a n100 n2
lsort -dictionary {a Z n2 n100}
=> a n2 n100 Z

Control Structure Commands

A conditional if then else command. The then and else keywords are optional.

if {$x == 0} {
  puts stderr "Divide by zero!"
} else {
  set slope [expr $y/$x]
}

The switch command is used to branch to one of many command bodies depending on the value of an expression. Using switch for an exact match:

switch -exact -- $value {
  foo { doFoo; incr count(foo) }
  bar { doBar; return $count(foo) }
  default { incr count(other) }
}

Using switch with substitutions in the patterns:

switch -regexp -- $value \
      ^$key { body1 }\
      \t### { body2 }\
      {[0-9]*} { body3 }

Comments in switch commands:

switch -- $value {
      # this comment confuses switch
      pattern { # this comment is ok }
}

A while loop to read standard input:

set numLines 0 ; set numChars 0
while {[gets stdin line] >= 0} {
      incr numLines
      incr numChars [string length $line]
}

The foreach command loops over a command body assigning one or more loop variables to each of the values in one or more lists:

set i 1
foreach value {1 3 5 7 11 13 17 19 23} {
    set i [expr $i*$value]
}
set i
=> 111546435

The for command is similar to the C for statement:

for {set i 0} {$i < 10} {incr i 3} {
    lappend aList $i
}
set aList=> 0 3 6 9

You can control loop execution with the break and continue commands. The break command causes immediate exit from a loop, while the continue command causes the loop to continue with the next iteration. There is no goto command in Tcl.

The catch command is used to trap errors. It takes two arguments:

catch command ?resultVar?

A standard catch phrase:

if {[catch { command arg1 arg2 ... } result]} {
    puts stderr $result
} else {
    # command was ok, result contains the return value
}

The error command raises an error condition that terminates a script unless it is trapped with the catch command. The command takes up to three arguments:

error message ?info? ?code?

The return command is used to return from a procedure. It is needed if return is to occur before the end of the procedure body, or if a constant value needs to be returned. As a matter of style, also use return at the end of a procedure, even though a procedure returns the value of the last command executed in the body.