The following is my take on how commands are parsed in TCL (Tool Command Language). I didn't test these by myself. It's just my interpretation of the following sources:
https://www.ee.columbia.edu/~shane/projects/sensornet/part1.pdf
https://tmml.sourceforge.net/doc/tcl/Tcl.html
The rules
There are a few simple rules.
- Escaping characters always works as in C:
\n \r \t \\ \" \{ \[ \$ \xhh \ooo \uhhhh \Uhhhhhhhh
. These have a special meaning" " { } [ ] $
. - Commands are separated by either ; or newline. Words are separated by space.
- Stuff like loops, functions, if-else are all commands. They don't have special syntax.
- The expr command is a bit complicated. I'll explain it later, after the main algorithm.
The algorithm
- Start from leftmost character and go rightwards to determine the words. Space means word ends and a new word begins.
- If the first character of a word is
" { [
we will go until the matching" } ]
and count the whole thing as a word. These can nest. You can think of the first" { [
as +1 value, and" } ]
as -1 value. The word ends when we reach 0 and then encounter a space. Examples:
set len [string length foobar]
Here word1:set
word2:len
and word3:string length foobar
set len [expr [string length foobar] + $x]
word1:set
word2:len
word3:expr [string length foobar] + $x
puts stdout "The length of $s is [string length $s]."
word1:puts
word2:stdout
word3:The length of $s is [string length $s].
Note: [
can also start in the middle of a word.
puts stdout $x+$y=[expr $x + $y]
word1:puts
word2:stdout
word3:$x+$y=[expr $x + $y]
set concat $a$b$c
word1:set
word2:concat
word3:$a$b$c
One interesting case where " doesn't end a word because there's a [
that needs to be closed first:
puts "results [format "%f %f" $x $y]"
word1:puts
word2:results [format "%f %f" $x $y]
One interesting case where "
doesn't start a group because it's not the first character of a word. Same applies to {
too but not [
. [
can start in the middle of a word as well.
set silly a"b
word1:set
word2:silly
word3:a"b
- Ok, now that we have our words, now we go one word after the other and perform substitutions depending on what kind of a word it is.
- For plain words and
" "
words we perform variable substitution:
-- $name
becomes the value of the variable name
-- \$name
becomes the string $name
-- ${name}
becomes the value of the variable name
, but in this case variable can be made up of pretty much any letter. So ${.o}
becomes the value of the variable .o
-- $name(index)
becomes the value at index index
of array name
. Not sure, but extra processing seems to be done on index too.
- For
{ }
words variable substitution is not done. Content is taken verbatim. - For plain words and
" "
words after $ substitution every[ ]
part is processed separately as a new TCL-process and it returns a string. This string is then pasted in place of[ ]
.
Examples:
set len [string length foobar]
word1:set
word2:len
word3:string length foobar
word3 is a []
word so parse it in a new shell:
word1:string
word2:length
word3:foobar
This command returns 6
when executed, so we now have
word1:set
word2:len
word3:6
set x 7
set len [expr [string length foobar] + $x]
After the inner command we have
set len [expr 6 + $x]
And len = 6+7 = 13
puts stdout "The length of $s is [string length $s]."
# Result: The length of Hello is 5.
puts stdout {The length of $s is [string length $s].}
# Result: The length of $s is [string length $s].
The quotes (") are redundant in the following:
puts stdout "[expr $x + $y]"
proc Diag {a b} {
expr sqrt($a * $a + $b * $b)
}
Due to {}
the contents of the proc are not parsed but passed to proc as is. proc command then uses eval to parse them.
while {$i <= $x}
In while blocks make sure to use { }
so that the contents are not parsed, but sent to while command as is. while command then uses eval to check the condition on each iteration.
set x xvalue
set y "foo {$x} bar"
# Result: foo {xvalue} bar
Here word3:foo {$x} bar
, and it's a " "
word. So we do $ substitution and treat { }
as normal characters.
set x [cmd1][cmd2]
Here word3 is the concatenation of the results of cmd1 and cmd2.
set x $
A lone $
character is interpreted as a regular $
character.
set y [set x 0][incr x][incr x]
Prints 012
set x 6
set y 7
puts [format "The answer: %d" [expr {$x * $y}]]
Doesn't look scary anymore after knowing the rules above.
Expr
This command can take any number of arguments. It first concatenates them all, then evaluates the entire thing in math context. It's better to put the entire expr in a { }
or " "
and pass it as a single argument, instead of relying on expr's variadic nature. This makes sure we don't get rounding errors while translating back and forth between strings and floats.
Top comments (0)