Einfach man Tcl

Richard Suchenwirth 2002-11-14 - This is the man(ual) page (aka Endekalogue) that describes the complete Tcl syntax, translated to German and with added comments in italics. See also Einfach Tcl, or Elf regels for the Dutch version.---- Dies ist die von mir handübersetzte Kernsyntax (man Tcl(n)). Kursive Zusätze sind von mir. Der juristisch-artige Stil stammt von der Manpage, hat jedoch den Vorteil, dass man jedes Wort auf die Goldwaage legen kann (im Zweifel gilt natürlich die englische Originalversion ;-)

Wer die drauf hat, und dazu die Einzelbestimmungen der wichtigsten built-in-Kommandos kennt (die anderen kann man sich jederzeit durch Manual-Lektüre "nachladen"), kann Tcl. Naja, ein bisschen praktische Übung ist sicher auch von Nutzen...


Die folgenden Regeln definieren die Syntax und Semantik der Sprache Tcl:

(1) Ein Tcl-Script ist ein (Unicode-)String, der ein oder mehrere Kommandos enthält. Semikolons und Zeilenvorschübe sind Kommandotrenner, wenn nicht gequotet. Schliessende eckige Klammern beenden ein Kommando während der Ersetzung, ausser wenn gequotet.

Ein Script kann ein Sourcefile sein, das wieder andere Sourcefiles nachlädt, also auch ein recht komplexer Softwarebrocken, aber auch z.B. in dem Kommando

        if {$i<0} {puts underflow!}

ist "puts underflow!" ein, nur viel kürzeres, Script, das als Argument an das if-Kommando übergeben wird. Unicode (Zeichendarstellung mit etwa 16 Bits) verhält sich zu ASCII etwa wie die rationalen zu den ganzen Zahlen: man kann danach zwar immer noch nicht alles, aber schon unendlich viel mehr ;-) Ein Vorteil von Tcl ist, dass die Unicode-Abhandlung ziemlich transparent erfolgt und überall vorauszusetzen ist.

(2) Ein Kommando wird in zwei Schritten ausgewertet. Zuerst zerlegt es der Tcl-Interpreter in Wörter und nimmt Ersetzungen vor, und zwar für alle Kommandos in der gleichen Weise. Das erste Wort wird als Name des Kommandos verwendet, und es wird dann mit den anderen Wörtern als Argumenten aufgerufen. Die Implementierung des Kommandos ist frei, jedes seiner Argumente in jeglicher Weise zu interpretieren, z.B. als Ganzzahl, Variablenname, Liste, oder Tcl-Script. Verschiedene Kommandos interpretieren ihre Wörter (eventuell) verschieden.

Diese Zweiteilung auch der Syntax ist enorm wichtig. Von aussen gesehen, ist die komplette Sprache mit den paar Regeln in diesem Text beschrieben (und z.B. festgelegt, dass der Operator vor den Operanden steht, wie in LISP, bin/sh... Von innen gesehen kann jedes Kommando mit seinen Argumenten machen, was es will, und sie zum Beispiel als Infixnotation (expr) oder mit speziellen Sprachen (regexp) interpretieren und bearbeiten. Da regexp/regsub den runden, geschweiften, eckigen Klammern ganz andere Bedeutung zulegt, werden reguläre Ausdrücke sinnvollerweise durch geschweifte Klammern aussen vor dem Blick des Tcl-Parsers geschützt.

(3) Die Wörter eines Kommandos werden durch Whitespace (Leerzeichen oder Tabulator, nicht Zeilenvorschub - weil Kommandotrenner) getrennt.

Dies gilt auch für Folgen von geschweiften Klammern, z.B. "{1 2}{3 4}". Um einen Syntaxfehler zu vermeiden, muss ein Leerzeichen dazwischen: "{1 2} {3 4}". Erst dadurch sind es zwei Wörter, die jeweils durch {} gruppiert sind.

(4) Wenn das erste Zeichen eines Worts ein Anführungszeichen (") ist, wird das Wort beim nächsten Anführungszeichen beendet. Wenn Semikolons, schliessende eckige Klammern oder Whitespace-Zeichen (inklusive Zeilenvorschub) zwischen den Anführungszeichen stehen, werden sie als normale Zeichen behandelt und sind Teil des Wortes. Kommando-, Variablen- und Backslash-Ersetzungen werden auf den Zeichen des Wortes vorgenommen, wie unten beschrieben. Die Anführungszeichen werden nicht Teil des Wortes.

Also einfach: Anführungszeichen gruppieren ein Wort, wie auch bei Unix und sogar DOS. Sie haben jedoch nicht die Funktion, den Datentyp string besonders anzuzeigen, da in Tcl konzeptuell (vom Parser her gesehen) alles ein String ist. Also sind whitespacelose Wörter wie "foo" und foo äquivalent.

(5) Wenn das erste Zeichen eines Wortes eine öffnende geschweifte Klammer ({) ist, wird das Wort mit der dazu passenden schliessenden geschweiften Klammer beendet. Geschweifte Klammern können innerhalb eines Wortes geschachtelt werden: für jede zusätzliche öffnende geschw.Klammer muss eine zusätzliche schliessende geschw.Klammer folgen (wenn aber geschweifte Klammern durch Backslash gequotet sind, zählen sie nicht bei der Klammerbalance). Auf den Zeichen innerhalb der geschweiften Klammern finden keine Ersetzungen statt, ausser Backslash-Zeilenvorschub. Auch Semikolons, Zeilenvorschübe, schliessende eckige Klammern oder Whitespace werden nicht besonders interpretiert. Das resultierende Wort besteht aus exakt den Zeichen zwischen den äusseren geschw.Klammern. Die äusseren geschw.Klammern gehören nicht zum Wort.

Merkhilfe: Geschweifte Klammern sagen "gruppier mich, aber rühr meinen Inhalt nicht an!" (wie singlequotes '...' in Unix). Insbesondere kann ein Wort in geschweiften Klammern oder Anführungszeichen auch wieder Kommandos enthalten (z.B. der body einer proc, die Anweisungsteile bei if/for/foreach/while...) Durch solche "Selbsteinbettung" können diese wenigen Regeln zum Bau beliebig mächtiger Strukturen verwendet werden...

(6) Wenn ein Wort eine öffnende eckige Klammer ([) enthält, wird Kommando-Ersetzung vorgenommen. Dazu wird der TclInterpreter rekursiv aufgerufen, um die Zeichen nach der öffnenden eckigen Klammer als Tcl-Script auszuwerten. Das Script kann ein oder mehrere Kommandos enthalten und muss mit einer schliessenden eckigen Klammer (]) enden. Das Ergebnis des Scripts (das Ergebnis des letzten ausgeführten Kommandos des Scripts) wird an der Stelle der eckigen Klammern, und aller Zeichen zwischen ihnen, in das Wort hinein-ersetzt. In einem Wort können beliebig viele Kommando-Ersetzungen vorkommen. Kommando-Ersetzung findet nicht statt, wenn das Wort in geschweiften Klammern steht.

Analogie zu Unix: Back-Apostrophe wie in set D=pwd, aber besser schachtelbar

RHO 2010-04-07: Die Kommando-Ersetzung scheint schon zu beginnen, bevor die Zerlegung in Worte abgeschlossen ist. Andernfalls müsste sich im folgenden Beispiel das Kommando "puts" über zu viele Argumente beschweren:

  set charlist [list b c d]
  puts "a, [join $charlist ", "], e"
  => a, b, c, d, e

AK: Siehe Regel (4).

RHO 2010-04-08: Nach Regel (4) müsste das zweite Wort in der zweiten Kommandozeile mit dem zweiten Anführungszeichen enden, also wäre es "a, [join $charlist " (das erste Wort ist puts). Vor keinem der Anführungszeichen steht ein Backslash. Wenn man einen Scanner für Tcl schreiben möchte, der nur Kommentare überspringt und einzelne Worte zurück liefert, kommt man ohne rekursiven Aufruf wohl nicht aus. Oder habe ich da etwas übersehen?

AK: Regel (4) sagt innen drin: "Kommando-, [...]-Ersetzungen werden [...] vorgenommen". D.h. sobald der Parser die oeffnende Klammer [ im Wort sieht, wird Regel (7) mit einem rekursiven Aufruf behandelt.

(7) Wenn ein Wort ein Dollarzeichen ($) enthält, wird Variablen-Ersetzung vorgenommen. Das Dollarzeichen und die nachfolgenden Zeichen werden in dem Wort durch den Wert einer Variablen ersetzt, und zwar in einer der folgenden Formen:

$name name ist der Name einer skalaren Variable; er endet beim ersten Zeichen, das nicht Buchstabe, Ziffer oder Unterstrich ist (hier also eine Restriktion der Sprache von Variablennamen - kann aber durch ${name} aufgehoben werden)

$name(index) name ist der Name einer Array-Variablen, index ist der Name eines Elements in dem Array. name darf nur aus Buchstaben, Ziffern und Unterstrichen bestehen. Kommando-, Variablen-, und Backslash-Ersetzungen werden auf den Zeichen von index ausgeführt

${name} name ist der Name einer skalaren Variable und kann beliebige Zeichen mit Ausnahme von schliessender geschweifter Klammer enthalten.

In einem Wort kann eine beliebige Anzahl von Variablen-Ersetzungen stattfinden. Variablenersetzung findet nicht in Wörtern innerhalb von geschweiften Klammern statt.

Tcl-Arrays sind ein Thema für sich. Hier nur kurz: es sind Hashtabellen, die einen key-String auf einen value-String abbilden, und können damit u.a. C-Arrays, beliebigdimensionale Matrizen usw. implementieren.

(8) Wenn ein Backslash (\) in einem Wort auftritt, wird Backslash-Ersetzung vorgenommen. In allen ausser den folgenden Fällen wird der Backslash entfernt und das folgende Zeichen als normales Zeichen im Wort belassen. So können Anführungszeichen, eckige Klammern und Dollarzeichen in Wörtern stehen, ohne besondere Ersetzung auszulösen. Besondere Backslash-Ersetzungen sind:

  • \a Signalton ("bell", 0x7)
  • \b Backspace 0x8
  • \f Seitenvorschub, form feed 0xC
  • \n Zeilenvorschub, newline 0xA
  • \r Wagenrücklauf, carriage return 0xD
  • \t Tabulator, 0x9
  • \v Vertikaltabulator, 0xB
  • \(newline)whitespace: Der Backslash, Zeilenvorschub, und alle Leerzeichen und Tabulatoren nach dem Zeilenvorschub zusammen werden durch ein einzelnes Leerzeichen ersetzt. Diese Ersetzung erfolgt in einem eigenen Prepass vor dem Parsen und findet auch zwischen geschweiften Klammern statt. Das bei der Ersetzung entstehende Leerzeichen wird als Worttrenner behandelt, wenn es nicht innerhalb von geschweiften Klammern oder Anführungszeichen steht.
  • \\ Backslash (\)
  • \ooo Die 1..3 Oktalziffern ooo geben einen 8-Bit-Wert für das Unicode-Zeichen, durch das sie ersetzt werden.
  • \xhh Die Hexziffern hh geben einen 8-bit-Wert für das Unicode-Zeichen, durch das sie ersetzt werden. Es können beliebig viele Hexziffern folgen, jedoch werden nur die letzten beiden verwendet.
  • \uhhhh Die 1..4 Hexziffern hhhh ergeben einen 16-bit-Wert für das Unicode-Zeichen, durch das sie ersetzt werden.

Backslash-Ersetzung findet nicht auf Wörtern in geschweiften Klammern statt (ausser Backslash-Newline, s.o.)

(9) Wenn ein Hash-Zeichen (#) an einer Stelle steht, an der Tcl das erste Zeichen des ersten Worts eines Kommandos erwartet, dann werden das Hashzeichen und die nachfolgenden Zeichen bis zum nächsten Zeilenvorschub als Kommentar behandelt und ignoriert. Das Zeichen # hat diese Bedeutung nur als erstes Zeichen eines Kommandos.

Allgemein muss man bei Kommentaren vorsichtig sein, da die Intuition hier oft mehr riskiert, als nach dieser Regel gilt. Auskommentieren von Codeteilen wirkt nur, wenn dessen geschweifte Klammern auch balanciert sind; sicherer und stressfreier ist es, statt dessen if 0 {...} herumzuschachteln.

2006-01-06 gg - Auch bei der Benutzung von if 0 {...} erscheinen Syntaxfehler bei nicht balancierten geschweiften Klammern. Es ist allerdings wahr, dass viel Arbeit durch die Benutzung von if 0 {...} erspart wird.

(10) Jedes Zeichen wird vom Tcl-Parser bei der Gruppierung von Worten genau einmal bearbeitet. Wenn zum Beispiel Variablenersetzung stattfindet, dann werden auf dem eingesetzten Wert der Variablen keine weiteren Ersetzungen durchgeführt. Wenn Kommandoersetzung stattfindet, dann wird das in [] eingeschlossene Kommando direkt von dem rekursiven Aufruf des Tcl-Interpreters verarbeitet; vor dem rekursiven Aufruf werden keine Ersetzungen durchgeführt, und auf dem Ergebnis des eingebetteten Scripts finden keine weiteren Ersetzungen statt.

Ersetzungen finden in der Reihenfolge von links nach rechts statt, und jede Ersetzung wird vollständig ausgeführt, bevor die nächste versucht wird. Daher wird eine Kommandofolge wie

        set y [set x 0][incr x][incr x]

immer die Variable y auf den Wert 012 setzen.

Wenn mehrfache Evaluation erwünscht wird, kann man explizit eval aufrufen - oder eingebettete Kommandos: [set x] reagiert wie "$x", kann aber mehrfach geschachtelt werden.

(11) Ersetzungen beeinflussen nicht die Wortgrenzen eines Kommandos. Zum Beispiel wird bei Variablenersetzung der gesamte String-Wert der Variablen als Teil des einzelnen Worts eingesetzt, auch wenn er Leerzeichen enthält.