summaryrefslogtreecommitdiffhomepage
path: root/tcl.html.markdown
blob: af2911c921f9f6353246d6846d918257a356ae07 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
---
language: Tcl
contributors:
    - ["Poor Yorick", "http://pooryorick.com/"]
filename: learntcl.tcl
---

Tcl was created by [John Ousterhout](http://wiki.tcl.tk/John Ousterout) as a
reusable scripting language for chip design tools he was creating.  In 1997 he
was awarded the [ACM Software System
Award](http://en.wikipedia.org/wiki/ACM_Software_System_Award) for Tcl.  Tcl
can be used both as an embeddable scripting language and as a general
programming language.  It can also be used as a portable C library, even in
cases where no scripting capability is needed, as it provides data structures
such as dynamic strings, lists, and hash tables.  The C library also provides
portable functionality for loading dynamic libraries, string formatting and
code conversion, filesystem operations, network operations, and more.  
Various features of Tcl stand out:

* Convenient cross-platform networking API

* Fully virtualized filesystem

* Stackable I/O channels

* Asynchronous to the core

* Full coroutines

* A threading model recognized as robust and easy to use


If Lisp is a list processor, then Tcl is a string processor.  All values are
strings.  A list is a string format.  A procedure definition is a string
format.  To achieve performance, Tcl internally caches structured
representations of these values.  The list commands, for example, operate on
the internal cached representation, and Tcl takes care of updating the string
representation if it is ever actually needed in the script.  The copy-on-write
design of Tcl allows script authors can pass around large data values without
actually incurring additional memory overhead.  Procedures are automatically
byte-compiled unless they use the more dynamic commands such as "uplevel",
"upvar", and "trace".

Tcl is a pleasure to program in.  It will appeal to hacker types who find Lisp,
Forth, or Smalltalk interesting, as well as to engineers and scientists who
just want to get down to business with a tool that bends to their will.  Its
discipline of exposing all programmatic functionality as commands, including
things like loops and mathematical operations that are usually baked into the
syntax of other languages, allows it to fade into the background of whatever
domain-specific functionality a project needs. It's syntax, which is even
lighter that that of Lisp, just gets out of the way.





```tcl
#! /bin/env tclsh

################################################################################
## 1. Guidelines 
################################################################################

# Tcl is not Bash or C!  This needs to be said because standard shell quoting
# habits almost work in Tcl and it is common for people to pick up Tcl and try
# to get by with syntax they know from another language.  It works at first,
# but soon leads to frustration with more complex scripts.
 
# Braces are just a quoting mechanism, not a code block constructor or a list
# constructor.  Tcl doesn't have either of those things.  Braces are used,
# though, to escape special characters in procedure bodies and in strings that
# are formatted as lists.


################################################################################
## 2. Syntax 
################################################################################

# Every line is a command.  The first word is the name of the command, and
# subsequent words are arguments to the command. Words are delimited by
# whitespace.  Since every word is a string, in the simple case no special
# markup such as quotes, braces, or backslash, is necessary.  Even when quotes
# are used, they are not a string constructor, but just another escaping
# character.

set greeting1 Sal 
set greeting2 ut
set greeting3 ations


#semicolon also delimits commands
set greeting1 Sal; set greeting2 ut; set greeting3 ations 


# Dollar sign introduces variable substitution
set greeting $greeting1$greeting2$greeting3


# Bracket introduces command substitution.  The result of the command is
# substituted in place of the bracketed script.  When the "set" command is
# given only the name of a variable, it returns the value of that variable.
set greeting $greeting1$greeting2[set greeting3]


# Command substitution should really be called script substitution, because an
# entire script, not just a command, can be placed between the brackets. The
# "incr" command increments the value of a variable and returns its value.
set greeting $greeting[
	incr i
	incr i
	incr i
]


# backslash suppresses the special meaning of characters
set amount \$16.42


# backslash adds special meaning to certain characters
puts lots\nof\n\n\n\n\n\nnewlines


# A word enclosed in braces is not subject to any special interpretation or
# substitutions, except that a backslash before a brace is not counted when
# looking for the closing brace
set somevar {
    This is a literal $ sign, and this \} escaped
    brace remains uninterpreted
} 


# In a word enclosed in double quotes, whitespace characters lose their special
# meaning 
set name Neo
set greeting "Hello, $name"


#variable names can be any string
set {first name} New


# The brace form of variable substitution handles more complex variable names
set greeting "Hello, ${first name}"


# The "set" command can always be used instead of variable substitution
set greeting "Hello, [set {first name}]"


# To promote the words within a word to individual words of the current
# command, use the expansion operator, "{*}".
```

```tcl
set {*}{name Neo}

# is equivalent to
set name Neo


# An array is a special variable that is a container for other variables.
set person(name) Neo
set person(gender) male
set greeting "Hello, $person(name)"


# A namespace holds commands and variables
namespace eval people {
    namespace eval person1 {
        variable name Neo
    }
}


#The full name of a variable includes its enclosing namespace(s), delimited by two colons:
set greeting "Hello $people::person1::name"



################################################################################
## 3. A Few Notes 
################################################################################

# All other functionality is implemented via commands.  From this point on,
# there is no new syntax.  Everything else there is to learn about Tcl is about
# the behaviour of individual commands, and what meaning they assign to their
# arguments.


# To end up with an interpreter that can do nothing, delete the global
# namespace.  It's not very useful to do such a thing, but it illustrates the
# nature of Tcl.
namespace delete ::


# Because of name resolution behaviour, it's safer to use the "variable" command to 
# declare or to assign a value to a namespace. If a variable called "name" already 
# exists in the global namespace, using "set" here will assign a value to the global variable
# instead of creating a new variable in the local namespace.
namespace eval people {
    namespace eval person1 {
        variable name Neo
    }
}


# The full name of a variable can always be used, if desired.
set people::person1::name Neo



################################################################################
## 4. Commands 
################################################################################

# Math can be done with the "expr" command.
set a 3
set b 4
set c [expr {$a + $b}]

# Since "expr" performs variable substitution on its own, brace the expression
# to prevent Tcl from performing variable substitution first.  See
# "http://wiki.tcl.tk/Brace%20your%20#%20expr-essions" for details.


# The "expr" command understands variable and command substitution
set c [expr {$a + [set b]}]


# The "expr" command provides a set of mathematical functions
set c [expr {pow($a,$b)}]


# Mathematical operators are available as commands in the ::tcl::mathop
# namespace
::tcl::mathop::+ 5 3

# Commands can be imported from other namespaces
namespace import ::tcl::mathop::+
set result [+ 5 3]


# New commands can be created via the "proc" command.
proc greet name {
    return "Hello, $name!"
}

#multiple parameters can be specified
proc greet {greeting name} {
    return "$greeting, $name!"
}


# As noted earlier, braces do not construct a code block.  Every value, even
# the third argument of the "proc" command, is a string.  The previous command
# rewritten to not use braces at all:
proc greet greeting\ name return\ \"Hello,\ \$name!



# When the last parameter is the literal value, "args", it collects all extra
# arguments when the command is invoked
proc fold {cmd args} {
    set res 0
    foreach arg $args {
        set res [$cmd $res $arg]
    }
}
fold ::tcl::mathop::* 5 3 3 ;# ->  45


# Conditional execution is implemented as a command
if {3 > 4} {
    puts {This will never happen}
} elseif {4 > 4} {
    puts {This will also never happen}
} else {
    puts {This will always happen}
}


# Loops are implemented as commands.  The first, second, and third
# arguments of the "for" command are treated as mathematical expressions
for {set i 0} {$i < 10} {incr i} {
    set res [expr {$res + $i}]
}


# The first argument of the "while" command is also treated as a mathematical
# expression
set i 0
while {$i < 10} {
    incr i 2
}


# A list is a specially-formatted string.  In the simple case, whitespace is sufficient to delimit values
set amounts 10\ 33\ 18 
set amount [lindex $amounts 1]


# Braces and backslash can be used to format more complex values in a list.  A
# list looks exactly like a script, except that the newline character and the
# semicolon character lose their special meanings.  This feature makes Tcl
# homoiconic.  There are three items in the following list.
set values {

    one\ two

    {three four}

    five\{six

}


# Since a list is a string, string operations could be performed on it, at the
# risk of corrupting the formatting of the list.
set values {one two three four}
set values [string map {two \{} $values] ;# $values is no-longer a \
    properly-formatted listwell-formed list


# The sure-fire way to get a properly-formmated list is to use "list" commands
set values [list one \{ three four]
lappend values { } ;# add a single space as an item in the list


# Use "eval" to evaluate a value as a script
eval {
    set name Neo
    set greeting "Hello, $name"
}


# A list can always be passed to "eval" as a script composed of a single
# command.
eval {set name Neo}
eval [list set greeting "Hello, $name"]


# Therefore, when using "eval", use [list] to build up a desired command 
set command {set name}
lappend command {Archibald Sorbisol}
eval $command


# A common mistake is not to use list functions when building up a command
set command {set name}
append command { Archibald Sorbisol}
eval $command ;# There is an error here, because there are too many arguments \
    to "set" in {set name Archibald Sorbisol}


# This mistake can easily occur with the "subst" command.
set replacement {Archibald Sorbisol}
set command {set name $replacement}
set command [subst $command] 
eval $command ;# The same error as before:  to many arguments to "set" in \
    {set name Archibald Sorbisol}


# The proper way is to format the substituted value using use the "list"
# command.
set replacement [list {Archibald Sorbisol}]
set command {set name $replacement}
set command [subst $command] 
eval $command


# It is extremely common to see the "list" command being used to properly
# format values that are substituted into Tcl script templates.  There are 
# several examples of this, below.


# The "apply" command evaluates a string as a command.
set cmd {{greeting name} {
    return "$greeting, $name!"
}}
apply $cmd Whaddup Neo


# The "uplevel" command evaluates a script in some enclosing scope.
proc greet {} {
	uplevel {puts "$greeting, $name"}
}

proc set_double {varname value} {
	if {[string is double $value]} {
		uplevel [list variable $varname $value]
	} else {
		error [list {not a double} $value]
	}
}


# The "upvar" command links a variable in the current scope to a variable in
# some enclosing scope
proc set_double {varname value} {
	if {[string is double $value]} {
		upvar 1 $varname var
		set var $value
	} else {
		error [list {not a double} $value]
	}
}


#get rid of the built-in "while" command.
rename ::while {}


# Define a new while command with the "proc" command.  More sophisticated error
# handling is left as an exercise.
proc while {condition script} {
    if {[uplevel 1 [list expr $condition]]} {
        uplevel 1 $script
        tailcall [namespace which while] $condition $script
    }
}


# The "coroutine" command creates a separate call stack, along with a command
# to enter that call stack. The "yield" command suspends execution in that
# stack. 
proc countdown {} {
	#send something back to the initial "coroutine" command
	yield

	set count 3 
	while {$count > 1} {
		yield [incr count -1]
	}
	return 0
}
coroutine countdown1 countdown
coroutine countdown2 countdown
puts [countdown 1] ;# -> 2 
puts [countdown 2] ;# -> 2 
puts [countdown 1] ;# -> 1 
puts [countdown 1] ;# -> 0 
puts [coundown 1] ;# -> invalid command name "countdown1"
puts [countdown 2] ;# -> 1 


```

## Reference

[Official Tcl Documentation](http://www.tcl.tk/man/tcl/)

[Tcl Wiki](http://wiki.tcl.tk)

[Tcl Subreddit](http://www.reddit.com/r/Tcl)