All except the most recent version of tcl (ver 8.0) are completely string based interpretive. The newest version includes some binary phases to increase speed. The language is procedure based, with all operations taking a list of string parameters. Even the built in tcl commands are approached in this fashion. The string parameters are handled in a list, with list operatives being built into the language. When dealing with C the items in the list become strings in the *argv[] array which is supplied to the procedure, in a similar fashion in which the operating system hands the command line parameters to the main() function of a program.
The list of parameters is interpreted before being passed to the procedure which is called. Any variables which are in the parameter list are replaced with their contents. This replacement is similar to "glob" replacement by a shell, where a "*.html" in a command is replaced by a list of all of the files ending in the ".html" extension.
Once a procedure has received its arguments, it processes and then returns. A numeric value is returned by most built in commands, with '1' indicating success and '0' indicating failure. Some of the more complex commands can also return string values.
Variables in tcl/tk are "declared" by setting them to a value. Each variable can be "set" multiple times, and exists within scope until "unset". When setting a variable all that is required is the variable name. When using the variable the name must be preceded by a "$".
A few quick notes to get you started: the comment symbol in tcl/tk is "#"
and it works up until the next end of line character. The selection of "#"
is rather convenient as it allows tclsh to ignore the line which is used by
UNIX to determine what shell to run. The "puts" command
takes a string and prints it to the screen. Now for your first tclsh program.
Not wanting to break with tradition, here is "hello world":
The output looks like this:
Now that we can put things to the screen, how about playing with some
variables. The following program uses the "set" operator to put the string
"world" in the variable "stuff". Notice that the "$stuff" is interpreted by
the "puts" command, and the contents are printed, not the word "stuff".
Simple enough. So what if you actually want a "$" to show up in your text?
Like in C the back-slash ("\") character allows you to display "non-printable"
characters. The following code will print: "hello stuff"
Like the "set" operator there is also an "unset" operator. The "unset"
command removes the variable from existence, and any further references to
that variable will cause an error. The following code will print the error:
"can't read `stuff`: no such variable".
Everything seen so far has been dealing with text. There is a good reason for this, everything in tcl is text. This is not to say that numbers can not be handled, but to do so, tcl requires special commands. Numbers are strings with just numbers in them. A mix of both numbers and letters will most often result in tcl interpreting the string as a word.
The "set" and "unset" operators work just as they did above with strings
when dealing with numerics. The "incr" operator takes a variable and
increments it by a specified value. The following piece of code plays with
a variable called "number" to give you an example of the "incr" operator.
Notice that the "incr" operator is given the name "number" without the
"$", this is because we don't want the interpreter to give "incr" the value
in "number", but the name of the variable itself.
Tcl also handles negative numbers, scientific, hexadecimal, octal, and
floating point. The following code shows off all of these things:
The square and brace brackets have special meaning in tcl, and effect how the interpreter handles the string contained within. The brace brackets force no interpretation, and everything inside is passed to a command as it appears. There are numerous places where a programmer might not want interpretation to happen, and this will become more clear in the sections on procedures and controls.
The square brackets mean evaluation. In essence any string inside of the square brackets is treated as a tcl script and run by the interpreter. This parallels the back-quote in many shell programming languages.
A useful operation is the "expr" command, this is short for expression and
it returns the results of an equation contained in the string passed to it.
The combination of the square brackets and the "expr" operator allows
manipulation of variables. The following code sets a variable called
"number" to 3, and then sets a variable "stuff" to be 10 more than what is
contained within "number". Notice that the contents of "number" are not
effected.
The above code works by evaluating the script between the square brackets,
and replacing this code with the returned value from the script. The script
has its variables interpreted before it is run, so the "expr" command sees
the string: "3 + 10". The "expr" command takes the string, realizes that
we want the numbers added together and returns a string containing the
resulting "13". This resulting string replaces where the square brackets
were, and so we get "set stuff 13", which of course puts the value "13" in
the variable called "stuff".
The semi-colon character allows the programmer to issue multiple commands
on the same line. This becomes useful if we wish to put a couple of operations
inside of a set of square brackets. The following code outputs "hello world"
to the screen as well as the contents of "stuff".
The return value from a script within square brackets is whatever the last
line in the script returns. If we had put the "puts" after the "expr" in
the above example, the contents of stuff would have been the empty string,
since "puts" returns the empty string.
The semi-colon is a neat trick, but it really isn't necessary. It is
perfectly legal to have end of line characters inside of square brackets. The
following code does the same thing as the above example.
What good is a programming language without being able to call procedures? Tcl supports the basic procedure stuff that you would expect, in fact all of the commands which have been looked at so far have just been procedures which are provided for you by the language. Calling a procedure is as simple as calling any of the commands, just give the name of the procedure plus any arguments you wish to pass it. The procedure must be defined before its first calling. Recursive procedures are supported.
The "proc" key-word is what allows you to define a procedure. After "proc"
you give the name of the procedure, the name of arguments and then the script
which is to be executed when the procedure is called. The arguments must
be there, even when you don't need any. There are a couple of ways of
dealing with this: give the name "args" and then ignore it, or use a
set of brace brackets. The script which is executed when the procedure is
called is also contained in brace brackets, this follows from the fact that
we do not want the contents interpreted during the definition of the procedure,
only during execution. The following code is a new twist on our familiar
"hello world"
Accessing the arguments passed to a routine can be done in two ways. If
you know how many arguments you want, then simply specify them inside of
brace brackets. The second method is using the key-word "args" instead of
an argument name, this puts all of the arguments in a variable called "args"
which can then be accessed as a string or list. The following example takes
exactly two arguments:
Notice that specifying items inside of quotes tells tcl that the whole thing
is an argument. The second call to print passes two arguments, the third
call to print attempts to pass seven arguments and thus fails.
By using the "args" key-word in your argument list, you obtain a sort of
catch-all. All of the arguments that aren't taken up by variables before the
"args" are put into a variable called "args". This variable can be accessed
like any other.
The section on strings discusses some ways of getting at
each of the space-separated items in the "args" variable. Tcl also includes
a fair amount of list processing techniques which are also useful, these can
be found by looking at the web sites mentioned, or in the man pages.
The return value of a procedure is implicitly the return value of the last
statement in the script. If you wish to force leaving of a procedure early,
or return a specific value that isn't returned by some other line, use the
key-word "return". This works just like the return statement in C. A simple
procedure to add two numbers and return the sum is given below.
Strings in tcl/tk are a lot like those in C. We have already seen examples of variable substitution using the "$" operator, but this is not the only special character. As was mentioned in the introduction to variables the "\" character can precede a "$" to print a "$" and not have it interpreted as a special character. Tcl also provides a fairly standard array of "\" characters:
Everything in tcl is based on strings, so it is fitting that there be a
series of string manipulation routines. The string routines are all based
on a single command "string" which takes as its first parameter an indicator
of what you would like to do with the string. Features exist for finding the
length, ranges, and doing comparisons. The following code illustrates some
of these features
The "string compare" operator evaluates greater than as coming later in the
alphabet. Whenever any counting is done on strings (e.g. the "index" and
"range" commands) the first character is accessed using "0". This mimics the
C behaviour of strings as arrays. Other "string" operations include "first",
"last", and "match", which all look for sub-patterns in the string. The
"tolower" and "toupper" operators return an all lower case or upper case
version of the string, respectively. The "trim", "trimleft", and "trimright"
operators are used to remove white-space from the left, right, or both sides of
the string. The trim operators can also be given specific characters to remove
instead of the white-space.
The "regexp" operator allows some of the rudimentary regular expression operations to be performed on a string. The typical regular expression operators are supported: ".", "^", "$", "\", "[...]", "(...)", "*", "+", "?", and "|". The "regexp" operator takes an argument for the expression to be evaluated and an argument for the string to be operated on. The "regexp" command returns a '1' or '0' indicating success and failure. Another command called "regsub" does the regular expression operations using substitution. For more information on either of these see the man pages.
Control flow in tcl/tk is performed using procedures. In most cases the control flow statement takes several scripts as arguments, some of the scripts being conditions for execution and some for the script to be executed if the condition is true.
The "if" statement takes two scripts as arguments, the first is the
condition, and the second is what is to be run if the condition is true. The
"if" statement can also be paired with "elsif" and "else" statements. The
following code shows an example
This simple example shows the usage of each of the key-words. The resulting
scripts to be executed can be multiple lines and multiple commands. Be
careful that the opening brace bracket is on the same line as the "if",
"elsif", or "else" key-word, otherwise it won't work.
The "while" statement takes two scripts as arguments, the first is the
condition, and the second is the script that is executed multiple times
until the condition is no longer met. If the condition is not met in the
first place then the execute script is not run.
This example will print the word "hello" three times.
The "for" statement is unnecessary, as everything that can be done with a
"for" can be done with a "while". The "for" statement, builds in some of the
common things that happen in a "while" as a short-cut for the programmer. The
"for" statement behaves in a similar fashion to its C counterpart. This
command takes four scripts. The first script is where variables are
initialized, the second is the stopping condition, the third script is for
incrementing, and the last is the actual body of the loop.
This funky little one line example does the same thing as the previous example
using a "while" statement, just a little more compactly.
The "foreach" statement is very useful when trying to process lists or parse.
This command takes three arguments, the first is a variable name, the second
is a string (or list), and the third is a script. For each item in the list
the third script gets executed. Upon each iteration of the loop the first
variable gets the next word in the list. The following code prints the
words "one", "two", and "three" on separate lines.
A common use for this statement is to process the "args" variable in a
subroutine. The next example shows a procedure which returns the sum of
a series of numbers.
The "switch" statement is a shortcut for multiple if-elsif clauses, using
the same condition variable. The structure of the "switch" is a little
different from the other control statements. It also takes an argument and
a script, but the script has to contain a specific format. The following
code fragment should help to make this clear.
In the above fragment the contents of variable "x" is compared to the string
"a". If the value of "x" is "a" then perform "-", which in this case means to
execute the next script which is available. The next available script here is
that following the "b" condition. If the "a" to contents of "x"
comparison fails, then the "switch" compares against the next item, which in
this case is the "b". This process continues until a match is found, or the
end of the "switch" statement is found. For example, if the contents of "x"
was "a", then "got an a or b" would be output to the screen. Like with the
"if" statements, multi-command, multi-line scripts can be used within the
"switch" as long as the opening brace bracket is on the same line as the
condition.
Tcl does not handle errors very well, the addition of the tk toolkit
actually makes the error handling even uglier. Typical response of tcl to
errors is to bail out. Tk is a little better in that it brings up an
exception window, but there is not much that can be done after that. In
the most frequent cases the error has been caused from a syntax or illegal
operation, and so forcing the exit of a program is almost expected, so why
the concern? A useful aspect of most scripting languages is an interface out
to the command line so that programs can be run from the operating system.
The problem arises in what these programs return. Many UNIX programs are not
very careful about their exit codes, and the meaning of the codes varies from
program to program. Unfortunately a non-zero error code is interpreted by
tcl as a fatal error. The "exec" command takes a string which is the
command to pass through to the operating system. The following example uses
the UNIX date program to print the date to the screen.
The date program is relatively well behaved, if it had returned with a non-zero
exit code the tcl program would have stopped. The solution to this is the
"catch" statement. The "catch" operation catches any exceptions thrown by a
command, and returns '1' or '0' indicating success or error of the command.
An added bonus is that "catch" can also take a variable for the output of
a command. The following code snipet is a safe version of the previous
example.