Almost every Tcl surprise comes from one question: when does the interpreter replace $name or [command] with a value, and when does it leave the text alone? This lesson gives the whole rule. It is short, and once it is clear, the quoting problems that trouble EDA scripts become easy to predict.
Reading time: about 7 minutes.
2.1 Words
Tcl reads one command at a time. A command ends at a newline or a semicolon. It is split into words separated by spaces. The first word is the command name, and the others are its arguments.
set name acc8
puts "design: $name"
puts {design: $name}
puts "length: [string length $name]"
puts "a\tb"
puts "cost is \$5"
This prints:
design: acc8
design: $name
length: 4
a b
cost is $5
The second line printed the text $name because it sat inside braces. The fourth line ran string length and printed its result. The last two show the backslash: \t became a tab, and \$ kept the dollar sign as an ordinary character.
2.2 The three substitutions
After splitting a command into words, Tcl performs three kinds of substitution on each word, and only these three.
- Variable substitution.
$nameis replaced by the value of the variable. Use${name}when characters that could belong to a name follow it. - Command substitution.
[command args]is replaced by the result of running the command. - Backslash substitution. A backslash followed by a character is replaced by a special character (
\ttab,\nnewline) or by the character itself (\$,\[,\\).
Figure 2.1 shows the order for one line.
Substitution happens once. The result of a substitution is never looked at again for more substitution. That is the reason this works as expected:
set x 3
set y {$x}
puts $y
puts [subst $y]
This prints:
$x
3
The variable y holds the four characters $x. Printing it gives those characters. Only the subst command, which asks for substitution on purpose, turns them into 3.
2.3 Grouping with braces and quotes
A word that contains spaces needs grouping. Tcl has two ways.
- Double quotes group the word and still allow all three substitutions inside.
- Braces group the word and allow none. What is between the braces is passed on exactly as written. Braces nest.
set a "one two"
puts [llength $a]
puts [llength {$a}]
puts [llength "$a three"]
This prints:
2
1
3
The first call sees two words. The second sees the single word $a, with the dollar sign, because the braces prevented substitution. The third substituted $a inside the quotes and then counted three words.
A good habit follows from this. Use braces for code and patterns that should be passed on untouched, such as the body of a loop or a regular expression. Use double quotes when you want variables and commands filled in.
2.4 A worked bug: the name that swallowed the suffix
Let us consider a script that builds a gated clock name.
set clk_name clk
catch {puts "$clk_name_gated"} err
puts $err
puts "${clk_name}_gated"
This prints:
can't read "clk_name_gated": no such variable
clk_gated
Tcl read the longest possible variable name, clk_name_gated, because underscores are legal name characters. The fix is to put braces around the name: ${clk_name}. The same problem appears when you build a file name such as $design_final.rpt and the variable design_final does not exist. If a variable lookup fails and the name looks longer than you wrote, check where the name ends.
2.5 Order of evaluation inside a command
Command substitution runs from the inside out and from left to right.
puts [string length [string trim " abc "]]
This prints:
3
string trim runs first, giving abc. Then string length runs on that result. Finally puts prints it. If a command inside brackets fails, the whole outer command fails with the inner error.
2.6 Semicolons, newlines and comments
A semicolon ends a command on the same line.
set p 1; set q 2; puts [expr {$p + $q}]
This prints 3.
A comment starts with #, but only where a command could start. That is why a comment after a command on the same line needs a semicolon first.
set v 5 ;# fine
puts $v
This prints 5. Without the semicolon, the # would be treated as one more argument to set, and the command would fail with a wrong number of arguments.
2.7 A command is a list of words
Because a command is split into words, a command can be stored in a variable and run later.
set cmd {puts "ok"}
eval $cmd
This prints ok. The braces kept the command intact until eval evaluated it. Later lessons build tool command lines this way, and the next section explains why braces matter there.
2.8 Reading tool command lines as words
Let us apply the rule to an Innovus line. To be run at the Innovus prompt. Check the options against your release with setOptMode -help and getOptMode -help.
Legacy UI.
setOptMode -opt_add_insts true
set v [getOptMode -opt_add_insts -quiet]
The first line has three words: the command setOptMode, the option -opt_add_insts and the value true. Tcl does not know what the option means. It passes the words to the tool, and the tool decides. The second line runs getOptMode inside the brackets. With -quiet, the command returns only the value, so v receives it. Without -quiet, the command prints a line to the log and console (name, value, type and whether you set it), which is easier to read but harder to use in a script.
The whole of Tcl syntax is this idea applied again and again: words, three substitutions, braces to prevent them, and quotes to group while still allowing them.
2.9 Why an interviewer asks about this
"What is the difference between braces and double quotes?" is one of the most common Tcl questions in CAD interviews. A short answer is that both group words, but quotes allow substitution and braces do not. A better answer adds that Tcl substitutes only once, that braces are used for code bodies and regular expressions, and that ${name} protects a variable name from the characters that follow it. Expect a follow-up asking you to predict the output of a line like puts {$x}.
Key takeaways
- A command is split into words, and three substitutions apply to each word: variable, command and backslash.
- Double quotes group and allow substitution, braces group and prevent it, and substitution happens only once.
- Use
${name}when characters that could belong to a name follow the variable. - A comment needs a command start, so put a semicolon before a trailing
#.