Appendix E. A Brief Introduction to Tcl

There’s no getting around it. If you intend to use IBM’s Full System Simulator (SystemSim) for nontrivial simulation, you need to know Tcl, the Tool Command Language. In the past, Tcl made me very uncomfortable: It’s wordy like a regular high-level language, but the words have no structure—no commas, parentheses, semicolons, or equal signs. But as I learned Tcl, I realized that its bothersome aspects were really strengths. I’ve since developed a great deal of respect for the language.

This appendix isn’t going to present Tcl in full. The goal is to explain enough of the topic so that you can read and write scripts that take full advantage of SystemSim’s capabilities. Throughout this treatment, I’ll do my best to compare Tcl to the C language that we’re familiar with. One application is based on the Sieve of Eratosthenes code from Chapter 4, “Debugging and Simulating Applications.” But first, let’s start with a simple Hello World! script.

Introducing Tcl

The AppendixE folder has no subfolders, just two files: square.tcl and sieve.tcl. Change to this directory and execute the following command:

tclsh square.tcl

This script computes five squared and returns Result is 25!. Listing E.1 presents the content of square.tcl.

Example E.1. Basic Tcl: square.tcl

# Compute 5^2, print to console
set num 5
set 5_squared [expr $num * $num]
puts "Result is $5_squared!"

This script might not look impressive, but it provides a wealth of information about Tcl:

  • set assigns values to variables, expr receives expressions and returns a value, and puts prints strings to the console.

  • Simple syntax. Commands don’t have to be followed by semicolons, arguments aren’t enclosed in parentheses or separated by commas, no equals sign in assignment statements.

  • Variables are untyped, variable names can start with a number, and string constants are enclosed in double quotes.

  • A variable’s value is referenced by preceding the variable name with $, and variables can be referenced directly inside a string.

  • Tcl comments start with #.

  • The result of a command (for example, expr 5*5) can be accessed like a variable if surrounded by square brackets.

  • No main function or any specifically marked starting point; Tcl commands are interpreted one command after the next.

This last point is important. Every line in a Tcl script is a command followed by zero or more space-separated arguments. Nearly every command returns a value, and all values are processed as strings. For example, expr doesn’t know or care that $num holds a numeric value; all it knows is that 5 * 5 is a string that can be evaluated. It performs the evaluation and returns the result as a new string: 25.

If the code in Listing E.1 makes sense, the rest of this discussion won’t provide any difficulty. The next sections present higher-level constructs in Tcl: conditional statements, lists, loops, and procedures.

Higher-Level Tcl

Tcl control structures and aggregates operate like those in C/C++, but the syntax differs in many respects. This section presents Tcl conditional statements, lists, arrays, and loops. Throughout the discussion, keep in mind that every Tcl statement is a command and that Tcl syntax relies on curly braces rather than parentheses.

Tcl Conditional Statement: if...elseif...else

Tcl checks for conditions in much the same way that C does. The word if is followed by an expression and statements to be executed if the expression is valid. The optional elseif and else are followed by statements to be executed if the expression is invalid.

An expression is valid if it produces a nonzero number or a string equaling true or yes as opposed to false or no. These expressions rely on == to compare for equality and other operators include !=, >, <, >=, and <=. The following code presents a simple example:

set hour_of_day 1
if [expr $hour_of_day > 12] {
   puts "Evening"
} elseif [expr $hour_of_day < 12] {
   puts "Morning"
} else {
   puts "Noon"
}

It’s important to see that elseif and else are not placed on separate lines; doing so will cause an error. This is because elseif and else are not separate commands. They are part of the overall if command.

Tcl List Processing

Like a C array, a Tcl list is a single structure containing multiple values. A Tcl list is created by the list command followed by space-separated elements. List elements can be of any type, including other lists. For example, the following statement assigns to example_list a list comprising four elements. The last element is a second list:

set example_list [list "Hi there!" abc $var [list 1 2 3]]

List elements are accessed with the lindex command. For example,

puts [lindex $example_list 1]

prints abc, the element of example_list at index 1. Table E.1 presents lindex and other Tcl list commands.

Table E.1. Basic Tcl List Commands

Command

Description

lappend lname e1

Places element e1 at the end of lname

lindex lname ind

Returns the element of lname with index ind

linsert lname ind e1

Returns a list whose elements are those of lname, but the value at index ind is e1 and following elements are incremented

list e1 e2 e3

Creates a list from elements e1, e2, and e3

llength lname

Returns the number of elements in lname

lreplace lname first last e1 e2 e3

Returns a list whose elements are those of lname, but values from first to last are the given elements

lrange lname first last

Returns a list of elements in lname from first to last

lset lname ind e1

Replaces the element at index ind in list lname with element e1

The following script uses a number of these commands to manipulate a list of integers:

set num_list [list 0 1 2 3 4]

# Append {5 6 7} to the end
lappend num_list 5 6 7

# Replace the first four values with {7 6 5 4}
set num_list [lreplace $num_list 0 3 7 6 5 4]

# Insert element {3} into the middle of the list
set num_list [linsert $num_list [expr [llength $num_list]/2] 3]

# Create a list from the middle elements
set new_list [lrange $num_list 2 6]

The commands lreplace and linsert don’t change the input list. They return a second list whose values are those of the input list after modification. Also, lreplace, linsert, and lrange operate on the values of the list ($num_list), whereas lappend operates on the list itself.

Tcl Arrays

Arrays are particularly important because SystemSim only allows access to SPU statistics through Tcl arrays. A Tcl array is similar to a list, but each element of the array is a match between a string name and a string value. Arrays in Tcl serve the same role as hash maps in other languages.

Tcl arrays aren’t allocated in advance, but are populated as new elements are added. For example, an array arr can be set to contain three elements with the following code:

set arr(first) = "First value"
set arr(2) = "Second value"
set arr("Third value") = "Third value"

Array indices are always strings, and array elements are referenced by enclosing the indices in parentheses. In this example, $arr(2) will be replaced with "Second value" whenever it appears in code. Table E.2 lists a number of Tcl commands that operate on arrays. In each case, aname is the name of an array.

Table E.2. Basic Tcl Array Commands

Command

Description

array get aname

Returns a list of each name/value pair in aname

array set aname list

Places the name/value pairs in list to aname

array size aname

Returns the number of elements in aname

array names aname

Returns the list of names in aname

Name/value pairs are processed as two-element lists. array set requires that the input list contain an even number of elements.

The following code shows how these commands are used in practice:

# Create and initialize array
array set nums {first 1 second 2 third 3}

# Access an element of the array (Result: 2)
puts $nums(second)

# Get size of array (Result: 3)
puts [array size nums]

# Get names in array (Result: second first third)
puts [array names nums]

The array names command doesn’t return the array names in order. This is because Tcl arrays are based on hash tables, and the name of the string has no relation to where it’s stored.

Loop Iteration: for and foreach

The for and foreach commands both create loop structures. A for loop iterates as long as an expression remains valid, and foreach iterates across the elements in one or more Tcl lists.

The syntax of the for statement is given by the following:

for start test next body

The Tcl interpreter executes the start command once, and then checks the test expression. If the expression is valid, it performs the commands in body and executes the next command. The interpreter continues this process (check test, run body, run next) until test is invalid.

The for command is commonly used to iterate a specific number of times. In this usage, the start command assigns a number to a counter, the test expression compares the counter to the final value, and the next command increments or decrements the counter. This is shown in the following example:

for {set i 0} {$i < 10} {incr i} {
   puts "Count = $i"
}

This example prints out the integers from 0 to 10. The command {incr i} is short for the following:

{set i [expr $i+1]}

The foreach command is simpler than the for command, and iterates over the elements in one or more lists. The basic foreach statement is given by this:

foreach var list body

When the command starts, the interpreter sets var equal to the first element of list and then processes the body commands. Then it sets var equal to each succeeding element in list, and processes body after each assignment. The following code iterates through each element in color_list:

set color_list [list red orange yellow green]
foreach color $color_list {
   puts "The current color is $color"
}

When a Tcl script is invoked with command-line parameters, the parameters are placed in a list called argv. The number of parameters is placed in a variable called argc. The following code iterates through each of the parameters to see whether the script was called with the -v option.

foreach param $argv {
   if {$param == "-v"} {
      puts "The -v option was used."
   }
}

If the interpreter encounters continue in a for or foreach construct, it skips the rest of the body commands and proceeds to the next iteration. If it encounters a break command, the interpreter stops processing the for/foreach command and interprets the next statement in the script.

Procedure Declarations

Every high-level language provides a way to encapsulate functionality within subroutines, and Tcl is no exception. Tcl procedures are defined with the proc command and each procedure must have a unique name. This name can be used outside the procedure like a regular Tcl command.

A basic procedure declaration consists of proc, the procedure name, and zero or more arguments in braces. For example, the following declaration states that example_proc accepts three parameters that will be accessed as first, second, and third:

proc example_proc {first second third}

These parameters are untyped, so a call to this procedure might look like this:

example_proc xyz "10 9 8" forty-two

It’s up to the procedure to make sure the parameter values make sense.

A function can accept a variable number of arguments by using a special argument: args. This serves as a list containing all the procedure’s parameters. For example, the following procedure prints an error message if it wasn’t called with two arguments:

proc two_arg {args} {
   if {[llength $args] != 2} {
      puts "This procedure must have two arguments."
      return "failed"
   }
   puts "The procedure was called correctly"
}

The procedure’s return statement is bolded to emphasize its importance. A procedure doesn’t have to have a return, and if doesn’t, it will return the result of the last executed command. But in the preceding example, the procedure returns failed in the event of a miscall. Then it finishes immediately. If two_arg is invoked with a command like

set ret [two_arg a b c]

ret will be set to failed because two_arg was called with three arguments. The final puts statement won’t be executed.

Just as in C functions, variables declared within a procedure are local to that procedure. A global variable can be accessed if two conditions are met:

  1. The variable is created outside the procedure.

  2. The variable inside the procedure is declared with the global keyword.

In Listing E.2, each procedure calls global num to access the size of the list to be analyzed. This script finds all the prime numbers between two and num and prints them to the screen. This uses the same Sieve of Eratosthenes algorithm as the C code in Chapter 4 but divides the process into three procedures.

Example E.2. Advanced Tcl: sieve.tcl

# Initialize the primes array
proc init {} {
   global num
   for {set i 1} {$i <= $num+1} {incr i} {
      lappend primes 0
   }
   sieve $primes
}

# Set composite numbers equal to 1
proc sieve {primes} {
   global num
   for {set i 2} {[expr $i * $i] <= $num} {incr i} {
      if {[lindex $primes $i] == 0} {
         for {set j [expr $i * $i]} 
            {$j <= $num} {incr j $i} {
               set primes [lset primes $j 1]
         }
      }
   }
   disp $primes
}

# Print all prime numbers between 2 and num
proc disp {primes} {
   global num
   for {set i 2} {$i <= $num} {incr i} {
      if {[lindex $primes $i] != 1} {
         puts $i
      }
   }
}

# Start the main processing
set num 250
init

The second-to-last line creates the global variable for the procedures, and the last line starts the computation by calling init. init creates the primes list and passes it to sieve. sieve determines which values are composite and passes the list to disp. disp prints out the prime values. Each procedure call passes parameters by value, which is how Tcl regularly passes parameters.

Conclusion

Tcl is the interface language for IBM’s Full System Simulator, so if you intend to perform simulations involving triggers or emitters, it’s the language to know. But aside from its importance, Tcl is fascinating in its own right.

This chapter began with a brief Tcl script and used it to derive conclusions about the language. Each Tcl statement is a command followed by arguments. Variable values are assigned with set and are referenced by preceding the variable name with $. expr evaluates expressions, puts prints strings, and comments are preceded with #.

From there, the chapter presented Tcl conditional statements, lists, loops, and procedures. The if statement in Tcl is similar to that in C, and the Tcl list bears a slight resemblance to a C array. However, a Tcl list can hold any type of data and can be dynamically increased and reduced in size. A Tcl procedure serves the same purpose as a C function, but global variables have to be specifically identified and a procedure can return any type of value.

A brief treatment such as this can’t do justice to the Tcl language, but it’s a fine starting place for further investigation. In particular, the Tcl/Tk (Tcl toolkit) extension makes it possible to build powerful graphical user interfaces that run on many operating systems. Tcl also has extensions for sockets, databases, and object-oriented development.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.212.170