Tcl (język programowania)
Tcl (Tool Command Language) – język skryptowy o składni częściowo podobnej do języków skryptowych powłok, oraz częściowo do Lispu. Jest znany z pakietu Tk (Tk Toolkit), który pozwala na pisanie przenośnych graficznych interfejsów użytkownika (GUI) dla wielu systemów operacyjnych oraz pakietu Expect, który pozwala na automatyzację zadań.
Logo języka | |
Paradygmat |
wieloparadygmatowy, obiektowy, funkcyjny, proceduralny, sterowany zdarzeniami, imperatywny |
---|---|
Typowanie | |
Implementacje |
ActiveTcl |
Aktualna wersja stabilna |
9.0.0 |
Twórca | |
Licencja |
oparta na BSD |
Platforma sprzętowa | |
Platforma systemowa | |
Strona internetowa |
Autorem zarówno języka Tcl jak i pakietu Tk jest John Ousterhout. Autorem pakietu Expect jest Don Libes.
Właściwości
edytuj- w Tcl-u wszystkie właściwości uzyskuje się przy pomocy komend, również konstrukcje językowe
- nadzwyczajny przepływ sterowania (głównie break i continue w pętlach) uzyskuje się mechanizmem podobnym do wyjątków
- wszystko może być dodane lub przedefiniowane w czasie działania programu
- każda dana, niezależnie od typu (pod warunkiem, że jest to typ zdefiniowany przez język) może być potraktowana jako napis
- skrajnie uproszczona składnia
- wbudowane sterowanie przez zdarzenia (w tym: zdarzenia na gniazdach, plikach, czasowe lub też definiowane przez użytkownika)
- rozszerzalność (rozszerzenia muszą mieć jedynie postać biblioteki dynamicznej i eksportować odpowiednie symbole)
- w pełni dostępny Unikod (od 1999)
- wieloplatformowy (aktualne implementacje istnieją m.in. na Win32, Uniksie i Mac)
- bardzo łatwy w obsłudze pakiet do tworzenia graficznego interfejsu użytkownika – GUI (Tk).
Programowanie obiektowe w Tcl-u jest dostępne w postaci bibliotek, z których najbardziej znaną jest Incr Tcl. Składniowo bardzo przypomina C++, ale ze względu na pełny dynamizm jest bliżej Smalltalka.
Od wersji 8.6 jest dostępna, jako część dystrybucji, biblioteka do programowania obiektowego TclOO (oparta w dużym stopniu o XOTcl, ale zmodyfikowana na podobieństwo Incr Tcl).
Niektóre aspekty programowania funkcyjnego są dostępne w Tcl-u dzięki temu, że wszelkie „wywołania funkcji” są tam realizowane poprzez komendy, które zawsze można podać przez nazwę.
Składnia
edytujTcl jest językiem komend; program w tym języku składa się z kolejnych wierszy komend, bardzo podobnie do uniksowych powłok, jak sh czy csh, czyli wiersz składa się z kolejnych słów, pierwsze z nich to komenda do wykonania, reszta to jej argumenty. To, co go wyróżnia wśród wielu innych języków skryptowych, to jego minimalistyczna składnia. Jedynymi elementami składniowymi Tcl-a są następujące znaki: $ [ ] { } ( ) " ; ::.
To, co powoduje, że jednocześnie w Tcl-u programy przypominają kod C, jest sposób na tworzenie czegoś podobnego do bloków kodu (czy, podobnie, serii danych). Tekst, co do znaku (włącznie z końcami linii) pomiędzy { a } jeśli jest podany jako argument komendy, jest przekazywany bez żadnych zmian. Komenda może ewentualnie podać kod następnie do komendy eval, aby go wykonać, albo być może zrobić z nim zupełnie co innego. Dlatego właśnie, choć w Tcl-u nie ma odpowiednika pętli do-while, programista może zrobić ją samemu.
W Tcl-u istnieją dwa osobne systemy nazw: osobny dla komend, osobny dla zmiennych. Komendy mają nazwy zawsze dostępne globalnie (nowe komendy mogą być dodawane przez wykonujący się skrypt, również wewnątrz definicji komendy).
Symbole używane w składni Tcl-a mają następujące znaczenie:
- $<zmienna> – oznacza wartość zmiennej. Nazwa zmiennej jest czytana do najbliższego znaku, który nie jest literą, cyfrą lub podkreśleniem (nazwa może zawierać też inne znaki, ale wtedy nazwę trzeba ująć w {}).
- [ ] – wykonuje komendę zawartą między nawiasami; rezultatem wyrażenia jest wartość zwrócona przez komendę
- { } – surowy tekst; cokolwiek jest zawarte pomiędzy klamrami (w tym końce linii) nie jest w ogóle przetwarzane
- ( ) – służą do obsługi tzw. tablic (w innych językach nazywa się to słownikiem lub mapą, w Perlu – hashem); żeby uzyskać z tablicy t wartość o kluczu k, należy napisać $t(k)
- " – napis wypełniany; wszystko wewnątrz "" jest normalnie przetwarzane, ale wewnątrz mogą się też znajdować białe znaki
- ; – oddziela od siebie komendy, jeśli są w tej samej linii
- :: – oddziela elementy przestrzeni nazw (służy też do obsługi zmiennych globalnych).
Podstawowe komendy
edytujPodstawowe komendy Tcl-a to set i proc:
set <zmienna> ?wartość?
Z jednym argumentem zwraca wartość podanej zmiennej. Jeśli podamy drugi argument, jego wartość zostanie zapisana w podanej zmiennej.
proc <nazwa> <argumenty> <ciało>
Definiuje komendę. Argumenty powinny być podane w formie listy.
Tak zdefiniowana komenda (czy też zdefiniowana w jakikolwiek inny sposób) zachowuje się podobnie jak funkcje w innych (imperatywnych) językach programowania; posiada wartość zwracaną, którą zwraca się za pomocą komendy return. Wartością zwracaną jest zawsze tekst (być może różnie interpretowany po stronie odbierającej), domyślnie pusty.
Listy są w Tclu podstawowym (i w sumie chyba jedynym) złożonym typem danych. Listą może być dowolny napis składający się z kilku słów; kolejne słowa są wtedy elementami tej listy. Jeśli jednak wewnątrz zawierają się { ... }, to całość wewnątrz klamer jest pojedynczym elementem listy (być może również składającym się z kilku słów, czyli listą zagnieżdżoną). Tcl posiada mnóstwo komend do operowania na takiej liście, poczynając od:
lindex <lista> <indeks>
Pierwszy argument to lista podana przez wartość, drugi zaś powinien być formą indeksu. Tzn. powinna być to albo liczba całkowita, albo kombinacja ze słowem „end”, np.:
lindex {ala ma kota} end-1
zwróci wartość „ma”.
Operacje takie jak dodawanie i odejmowanie są również dostępne w Tcl-u; służy do tego osobna komenda (będąca niejako oddzielnym interpreterem), „expr”. Np.:
set a 10
set x [expr $a+20]
Konstrukcje strukturalne
edytujW Tcl-u istnieją konstrukcje strukturalne znane np. z języka C, jak konstrukcje warunkowe i pętle. Jak każda właściwość jest to uzyskiwane za pomocą komend, np.:
if <warunek> ?then? <skrypt> ?elseif <skrypt>?... ?else? <skrypt>
To, co jest tutaj określone jako <skrypt>, jest dowolną postacią komendy lub kilku komend, które mają się wykonać, jeśli zostanie spełniony warunek. Natomiast <warunek> jest wyrażeniem arytmetycznym, które należy podać w postaci identycznej jak dla komendy expr. Powinno się je też brać w nawiasy klamrowe, aby zabezpieczyć się przed (najczęściej niepożądanym) obliczaniem wartości tego wyrażenia podczas przygotowywania argumentów dla komendy (wyrażenie powinno zostać podane bez zmian do komendy i dopiero tam obliczone; to samo zresztą powinno się stosować dla komendy expr):
if { $a > 0 } {
set x [expr {$x/$a}]
}
W podobny sposób działa pętla while:
while <warunek> <skrypt>
Pętli do-while, jak zostało wspomniane wcześniej, nie ma (oczywiście jest dostępna w niektórych bibliotekach, np. w jednej z grupy tcllib). Oto pętla for:
for <inicjalizacja> <warunek> <następne> <skrypt>
Którą można napisać np. tak:
for {set i 0} {$i < $argc} {incr i} {
puts "Arg #$i: [lindex $argv $i]"
}
Pętla foreach:
foreach <zmienne> <lista> [<zmienne lista>...] <skrypt>
Argument <zmienne> jest to lista nazw zmiennych (najczęściej jest to jedna zmienna), do których mają być wpisane wartości kolejno pobierane z <listy> (jeśli zabraknie do wypełnienia wszystkich, to zmienne, dla których brakuje, są zapisywane pustym napisem). Można też podać kilka serii „<zmienne> <lista>” i wtedy kilka list może być przetwarzane równolegle. Najprostsza postać użycia to:
foreach i [info vars] { puts $i }
które wypisze listę nazw aktualnie widzianych zmiennych.
Do sterowania przepływem wewnątrz pętli służą komendy break i continue.
Wejście i wyjście
edytujNajczęściej używaną komendą do wypisywania jest komenda puts:
puts ?-nonewline? ?gdzie? <tekst>
Komenda puts domyślnie dodaje znak końca linii, chyba że użyjemy opcji -nonewline. Opcjonalny argument ?gdzie? jest nazwą kanału otwartego do zapisu (domyślnie jest to stdout).
Kanały w Tcl-u są identyfikatorami miejsc, na rzecz których wykonywane są operacje wejścia/wyjścia. Ich nazwy są tworzone przez komendy, które tworzą takie kanały, ale poza tym istnieją trzy kanały dostępne zawsze, które nazywają się stdin, stdout i stderr i oznaczają tzw. standardowe wejście, standardowe wyjście i standardowe wyjście diagnostyczne. Tak więc program „Hello world” w Tcl-u wygląda tak:
puts "Hello, world!"
Komenda puts jest właściwie jedyną komendą obsługującą operacje wyjściowe i to niezależnie od tego, czy wysyła się dane tekstowe, czy binarne. Do operacji wejściowych istnieje więcej komend:
gets <kanał> ?zmienna?
Komenda ta czyta jedną linię danych. Jeśli zostanie podana ?zmienna?, w niej będzie umieszczony wczytany napis a zwrócona zostanie jego długość. Jeśli zmienna nie zostanie podana, wczytana linijka będzie zwrócona jako wynik. Do czytania przeznaczona jest też druga komenda, występująca w dwóch postaciach:
read ?-nonewline? <kanał>
Czyta wszystko z zadanego <kanału> aż do tzw. EOF. Jeśli podano -nonewline, to w zwróconym przez komendę tekście nie będzie znaku końca linii, jeśli byłby on ostatnim znakiem.
read <kanał> <ile>
Czyta z zadanego <kanału> <ile> bajtów danych. Jeśli EOF nastąpił zanim odczytano <ile> znaków, to zostanie zwrócone tylko tyle, ile wczytano. Jeśli <kanał> blokuje odczyt, to komenda zakończy się, dopiero gdy odczytana zostanie wymagana liczba bajtów.
Kanały mogą być też otwarte do różnych innych urządzeń. Standardowa komenda open otwiera dostęp do pliku:
set fd [open filename.txt r]
Komenda ta zwraca identyfikator kanału, jeśli otwarcie powiodło się. Drugi argument komendy to tryb dostępu (r, w lub a, z dodatkiem +). Nazwa może się zaczynać od znaku „|” i wtedy stanowi ona komendę zewnętrzną do wykonania a zwrócony identyfikator identyfikuje potok otwarty do połączenia z tą aplikacją (połączenie to jest jednokierunkowe i zależne od wartości trybu dostępu).
Komenda close zamyka połączenie z plikiem. Dodatkowe komendy związane z plikami to:
eof <kanał>
Zwraca 1, jeśli nastąpił koniec pliku na <kanale>.
fconfigure <kanał> <opcje...>
Ustawia różne opcje konfiguracyjne. Opcje te dotyczą bardzo wielu rzeczy, część z nich jest nawet zależna od platformy. Można w ten sposób ustawić takie rzeczy jak translację (-translation; konwencje końców linii; w Tcl-u obowiązuje zawsze koniec linii <LF>), kodowanie (-encoding), które również pozwala na ustawienie na dane binarne (jest to o tyle istotne, że Tcl stosuje od dawna Unicode dla tekstów), czy też -blocking do obsługi blokowania na kanale.
fileevent <kanał> <readable|writable> <skrypt>
Komenda ta rejestruje <skrypt> jako handler zdarzenia readable lub writable (kanał gotów do odczytu lub zapisu). Skrypt wykona się, jeśli dany kanał zaraportuje gotowość (naturalnie najczęściej zdarza się to na portach szeregowych lub gniazdach). Aby zdarzenie mogło się „zdarzyć”, Tcl powinien wejść w tryb obsługi zdarzeń (wykonuje się to komendami update i tkwait). Można też w tym celu zażądać pakietu Tk, ponieważ w Tk interpreter po zakończeniu przetwarzania skryptu nie kończy działania, tylko wchodzi w pętlę zdarzeń – ale ze względu na zwięzłość opisu tego tematu nie zostanie to tutaj dokładniej opisane.
Wyjątki (kody powrotne)
edytujTcl posiada coś w rodzaju obsługi wyjątków, które w nadzwyczajny sposób sterują zachowaniem się programu. Każda wykonywana komenda poza wartością zwracaną posiada również „kod powrotny” oznaczony odpowiednią liczbą całkowitą. Kod powrotny może być w zasadzie dowolny, ale tylko kilka jest tzw. „standardowych” kodów powrotnych, tzn. rozpoznawanych przez inne komendy i interpreter. Domyślnie kod powrotny wynosi 0 (lub inaczej „ok”), inny kod powrotny można wymusić poprzez napisanie
return -code <kod> <wartość>
Standardowe kody powrotne to: ok, error, return, break, continue (i są to symboliczne oznaczenia od 0 do 4). Jeśli jakaś komenda zwraca jeden ze standardowych kodów powrotnych to komenda wywołująca (lub interpreter) reaguje na to w określony sposób:
- ok – nic się nie dzieje,
- error – następuje przerwanie wykonywania wraz ze „zrzutem stosu” (tzn. zrzut stosu jest dokładany do wartości zwracanej); wartością zwracaną w tym przypadku powinien być komunikat błędu,
- return – nakazuje wykonać natychmiastowy powrót z bieżąco wykonywanej procedury,
- break – wewnątrz pętli powoduje jej przerwanie; poza pętlą następuje przerwanie wykonywania z komunikatem błędu (jak w error),
- continue – wewnątrz pętli powoduje jej „kontynuację”; poza pętlą tak samo jak w break,
- <inny> – jak w „error”, z tym że komunikat błędu informuje o niewłaściwym kodzie powrotnym.
Istnieje także biblioteka, która implementuje już jawnie nazywane wyjątki, na bazie tego jednak mechanizmu.
Do wielu z tych kodów istnieją skrótowe komendy, których jedynym działaniem jest zwrócenie odpowiedniego kodu powrotnego (ewentualnie wraz z wartością zwracaną):
- error – zwraca kod powrotny error wraz z podanym komunikatem błędu,
- break – zwraca kod powrotny break,
- continue – zwraca kod powrotny continue.
To, czym jednak najbardziej zawsze należy się martwić, to jest kod „error”. Można taki wyjątek zgłosić komendą error:
error "Mamy problem"
Błąd może zostać zgłoszony przez komendę, jeśli nie może się ona wykonać, błąd może znajdować się w składni komendy (np. niepoprawna liczba argumentów lub podano tekst niekonwertujący się na liczbę całkowitą), błąd może też być spowodowany niemożnością otwarcia pliku. Wszystkie takie zdarzenia są kwalifikowane jako błędy i ich zgłoszenie powoduje zwykle przerwanie działania skryptu wraz ze zrzutem historii wywołań.
Wyjątek można przechwycić komendą catch:
set failed [catch {open nonexistentfile r} why]
Komenda catch zwróci kod powrotny komendy. Jeśli ta wartość jest różna od zera, to znaczy, że przechwycono wyjątek. Tekst wyjątku (np. to, co było argumentem komendy error) zostanie zapisany w zmiennej podanej jako drugi argument komendy catch.
Komendę catch można wykorzystać zatem zarówno do przechwytywania błędów jak i do zaimplementowania własnych postaci pętli.