Tcl/Tk - sissejuhatus John K. Ousterhout - Univ. of California, Berkeley, Computer Science Division 1988 - töövahendid Sprite projekti raames Tcl - Tool Command Language - stringidel baseeruv laiendatav käsukeel Tk - Toolkit for the X Window System - Tcl laiendus graafiliste kasutajakeskkondade (GUI - graphical user interface) loomiseks Programmid: tclsh - Tcl interpretaator tcl - laiendatud Tcl (TclX) interpretaator wish - Tk interpretaator wishx - laiendatud Tk (TkX) interpretaator expect - Tcl rakendus interaktiivsete programmidega dialoogi pidamiseks dpwish - Tk hajussüsteemide loomiseks (distributed programming) Tcl-DP xf - interaktiivne Tk programmeerimis-keskkond XF (Interface builder) tclsh, tcl, expect - kasutatavad ka ilma X Windows keskkonnata Üldisi omadusi: Lihtne süntaks (mõneti sarnane shell-keeltele, aga reeglipärasem), esialgu harjumatud on nn. substitutsioonireeglid Juhtimisstruktuurid ei kuulu süntaksisse, vaid on tavalised (parameetritega) käsud Programmi töötlemine toimub kahes jaos: - Tcl Parser, analüüs, mille käigus eristatakse käskude koosseisu kuuluvad sõnad ja tehakse vajalikud asendused (substitutions). Sel etapil sõnadele tähendust ei anta - Command Procedure, interpreteerimine, mille tulemuseks on üldiselt string Programm ja andmed on sisuliselt samas formaadis - loomulik on kirjutada programme, mis töötlevad/toodavad programme, samuti siduda neid C-ga Tcl sisaldab kõiki olulisi elemente programmeerimiseks (s.h. protseduurid, interfeis Unixiga, muutujad, nimistud, assotsiatiivmassiivid, stringitöötlus, hargnemised ja tsüklid jne) Tcl ei ole mõeldud suurte süsteemide programmeerimiseks, vaid ennekõike kasutajaliideste kirjutamiseks (expect, Tk, Tcl-DP, ...), kus meid huvitab: - rakenduste omavaheline suhtlemine (nende sisu muutmata) - graafilise kasutajaliidese prototüübi kiire saamine ja testimine - laiendatavus konkreetse rakenduse jaoks Tcl kriitika: - interpretaatori aeglus: 1) vaieldav (mõõta); eesmärgi küsimus 2) kompileerimise võimalus ? - vähene modulaarsus: eesmärgi küsimus Tk - Tcl-l baseeruv vahend X Windows kasutajaliideste programmeerimiseks: - Motif-laadsed aknaelemendid (widgets) - rikkalik valik valmis elemente ning meetodeid nende kombineerimiseks - mõistlikud vaikimisi-väärtused elementide parametriseerimiseks Side graafilise liidese ja Tcl-keskkonna vahel on dünaamiline (näit. muutuja väärtuse muutumine programmis võib kohe kajastuda ekraanil ning mingi välja muutmine ekraanil muudab muutuja väärtust Tcl-keskkonnas) Tk-liidese kirjutamine ei nõua ümberkompileerimist - muudatusi saab teha jooksvalt ning need on kohe nähtavad ekraanil Liidese käitumist on lihtne muuta - elementide seos X sündmustega (event binding) on dünaamiline. Mistahes Tk-rakendus võib juhtida mistahes teise Tk-rakenduse tööd Saab lisada uusi aknaelemente ning meetodeid nendega ümberkäimiseks - kokkulepped C programmide jaoks xf võimaldab Tk-aknaelemente kasutada interaktiivselt - dialoogi käigus genereeritakse Tk-programm. Eeldab siiski Tk põhitõdede tundmist Palju tegelikke/tööstuslikke rakendusi, aktiivne ja arvukas kasutajaskond Tcl - süntaks Programm koosneb käskudest, eraldajateks reavahetus või semikoolon Käsk koosneb sõnadest, eraldajateks tühik või TAB Käsu esimene sõna on käsu nimi, ülejäänud - parameetrid. Analüüsi etapil tehakse kindlate reeglite järgi asendused sõnades (lubatud on asendused käsu nimes, mistahes sõnas võib esineda kuitahes palju asendusi). Parameetrite sisuline tõlgendamine toimub käsu interpreteerimisel Asendusreeglid (käsitleme täpsemalt): - muutujate asendamiseks (variable substitution) - muutuja nimi asendatakse väärtusega - $ - käskude asendamiseks (command substitution) - käsk asendatakse selle käsu töö tulemusega - [ ] - erisümbolite asendamiseks - eritähenduse äravõtmiseks, grupeerimiseks - "" { } \ Asendused tehakse vasakult paremale ühe läbivaatusega - asenduse tulemust ei töödelda samal etapil Kui käsu esimene sõna algab märgiga #, siis tõlgendatakse teksti kuni rea lõpuni kommentaarina Jutumärgid "..." lubavad sõnal sisaldada erisümboleid (tühik, semikoolon, ... ). Kõik asendused TEHAKSE. Jutumärke ei loeta selle sõna osaks, mida nad piiravad Loogelised sulud { ... } lubavad kõiki erisümboleid, asendusi EI TEHTA. Võivad olla üksteisesse sisestatud. Võimaldavad asendusi edasi lükata (näit. andes programmi teksti argumendina teisele programmile). Väliseid sulge ei loeta selle sõna osaks, mida nad piiravad Eritähendust saab muuta sümboliga \ (backslash => backslash substitution): - \erisümbol - tõlgendada tavalise märgina ( näit. \; \$ \{ \} \" \\ jne.) - \ooo - sümbol kaheksandkoodina - \xhh - sümbol 16-koodina - \n - newline-sümbol - \t - TAB-sümbol - \a \b \f \r \v - mitmesugused muud Tcl - set, expr, eval setmuutuja ?väärtus? ?väärtus? tähistab, et väärtus on fakultatiivne argument Muutujale omistatakse väärtus; käsu resultaadiks on igal juhul muutuja (uus) väärtus (NB! tekstina) expr arg ?arg ...? Avaldise arvuline väärtustamine (Tcl-s alati ilmutatud kujul), argumendid konkateneeritakse enne väärtustamist (eraldajaks tühik) eval arg ?arg ...? Argumentidest moodustatud stringi täitmine Tcl-käsuna, kasutusel argumentide "lahtiharutamiseks" Näit. set a 32 ==> 32 set b a ==> a set b ==> a expr $a ==> 32 expr \$$b ==> 32 set p + ==> + set c [expr 3 $p 5] ==> 8 Tcl - unset, append, incr unset muutuja ?muutuja ... ? Kustutada muutuja(d). Tulemus tühisõne. info exists muutuja 1, kui muutuja on defineeritud; 0, kui pole append muutuja väärtus ?väärtus ... ? Lisada antud väärtus(ed) (tekstiliselt) muutuja lõppu (vajadusel muutuja luuakse). Tulemuseks uus väärtus. incr muutuja ?samm? Muutuja väärtust suurendatakse sammu (vaikimisi ühe) võrra. Tulemuseks uus väärtus. Argumendid täisarvulised! Massiivid Massiivid on tekstilise indeksiga: palk(jaanuar) palk(veebruar) ... array names mnimi ?näidis? Tulemuseks nimistu antud massiivi elementide nimedest array size mnimi Tulemuseks elementide arv antud massiivis Tcl - avaldis Arvud (täis- ja reaalarvud): 077 0x3f 63 2.1 7.91e+16 3. Funktsioonid - abs, acos, asin, atan, atan2, ceil, cos, cosh, double, exp, floor, fmod, hypot, int, log, log10, pow, round, sin, sinh, sqrt, tan, tanh: ceil(x) - vähim täisarv, mis pole väiksem x-st floor(x) - suurim täisarv, mis pole suurem x-st int(x) - x täisosa round(x) - täisarv, mis tekib x ümardamisel double(i) - i teisendus reaalarvuks Unaaroperaatorid: -a vastandarv !a loogiline eitus ~a bitikaupa eitus Binaaroperaatorid: a*b korrutamine a/b jagamine, täisarvude jagatise täisosa a%b täisarvude jagamisel tekkiv jääk a+b liitmine a-b lahutamine a<>b a aritm. nihutamine b bitti paremale a=b a>b a<=b a>=b analoogilised võrdlused a==b 1, kui a võrdub b-ga; 0, kui ei võrdu a!=b 1, kui a ei võrdu b-ga; 0, kui võrdub a&b bitikaupa loogiline korrutamine a^b bitikaupa loogiline mitteekvivalents a|b bitikaupa loogiline liitmine a&&b loogiline korrutamine a||b loogiline liitmine a?b:c tingimuslik avaldis; kui a erineb nullist, siis b; kui a on null, siis c Näide. Miinimumi leidmine (NB! expr "ise" teeb muutujate asenduse, analüüsi etapil seda loogeliste sulgude tõttu ei tehta): expr { ($a < $b) ? $a : $b} Prioriteedi järjekord: ümarsulud, unaaroperaatorid, * / % , + - , << >> , < > <= >= , == != , & , ^ , | , && , || , tingimuslik avaldis Tcl - nimistud (lists) Nimistu koosneb nullist või enamast tühikutega eraldatud elemendist. Elementideks võivad olla sõnad (alamnimistud loogelistes sulgudes): yks kaks kolm esimene\ element teine kolmas a { b1 b2 } c { d1 { e11 e12 } } Funktsioonid: lindex nimistu indeks - elemendi eraldamine (indeksid algavad nullist, viimase elemendi indeks end) lindex { a b { c d e } f } 2 ==> c d e concat nimistu ?nimistu...? - ühendada antud nimistute elemendid üheks nimistuks (ei rakendu alamnimistutele rekursiivselt) concat { a bc } { d e } f { g { h i } } ==> a b c d e f g { h i } list väärtus ?väärtus...? - moodustada antud väärtustest nimistu list { a } b { c d { e } } ==> { a } b { c d { e } } llengthnimistu - elementide arv nimistus llength { a { b c } { d { e } } f } ==> 4 linsert nimistu indeks väärtus ?väärtus ...? - lisada väärtus(ed) elementidena antud nimistusse indeksiga määratud elemendi ette linsert { a b { c d } } 2 { e f } g ==> a b { e f } g { c d } lreplace nimistu algus lõpp ?väärtus...? - kustutada nimistust elemendid indeksitega algus kuni lõpp (kaasa arvatud). Väärtus(ed) lisada elementidena kustutamiskohale lreplace { a b { c d } e } 1 2 { w x } y z ==> a { w x } y z e lrange nimistu algus lõpp - moodustada uus nimistu antud nimistu elementidest indeksitega algus kuni lõpp (kaasa arvatud) lrange { a { b c } d { e f } g } 1 3 ==> { b c } d { e f } lappendmuutuja ?väärtus...? - lisada nimistumuutuja lõppu antud väärtused elementidena. Muutuja uus väärtus on ka funktsiooni väärtuseks lappend x $a $b $c on samaväärne set x "$x [list $a $b $c]" lsearch?-meetod? nimistu näidis - näidise otsimine nimistust antud meetodil: -glob csh-tüüpi näidis: * ? [ ] \ -exact täpne vastavus näidisega -regexp näidiseks egrep-tüüpi regulaaravaldis Vaikimisi meetodiks -glob Tulemuseks on esimese näidisega sobiva elemendi indeks või -1, kui sobivat ei leidu lsort ?-meetod? ?-command käsk? nimistu Nimistu leksikograafiline sorteerimine: -ascii (vaikimisi) - tekstiline -integer täisarvuline -real reaalarvuline -increasing (vaikimisi) kasvavalt -decreasing kahanevalt käsk võrdlemisfunktsioon Tulemuseks on sorteeritud nimistu split sõne ?eraldajad? Tulemuseks on nimistu, mille elementideks on etteantud sõne osad antud eraldajate järgi (eraldajad ise jäävad välja) split "/usr/local/info" / ==> { } usr local info split xbaybz ab ==> x { } y z Eraldaja(te) puudumisel on elementideks tähed ja neid eraldavad tühisõned split { a b c } { } ==> a { } b { } c join nimistu ?eraldaja? Tulemuseks on sõne, mis saadakse nimistu elementide ühendamisel (eraldaja pannakse iga kahe elemendi vahele) join { { } usr local info } / ==> /usr/local/info expr [join { 8 3 7 } + ] ==> 18 Tcl -juhtimiskäsud if avald1 ?then? Tcl-prog1 \ ? elseif avald2 ?then? Tcl-prog2 ... \ ? ?else? ?Tcl-progN? NB! avav loogeline sulg ei saa olla uue rea alguses if { $x > $y } { expr $x } { expr $y } Avaldised ja Tcl-programmid peavad üldiselt olema loogelistes sulgudes, et vältida asendusi analüüsi etapil - asendused teeb juhtimiskäsk ise. Väärtuseks täidetud haru poolt tagastatud väärtus või tühisõne while avaldis Tcl-prog Avaldis määrab jätkamistingimuse (tsüklist väljutakse, kui avaldis saab väärtuse null), tulemuseks tühisõne Näide. Nimistu elementide järjekorra vastupidiseks muutmine (argument a, tulemus b): set b "" ; set i [expr [llength $a] - 1] while { $i >= 0 } { lappend b [lindex $a $i] ; set i [expr $i - 1] } break - väljumine kõige sisemisest tsüklist continue - kõige sisemise tsükli sammu lõpetamine, minnakse uuele sammule forTcl-algus avaldis Tcl-vahe Tcl-prog Tcl-algus täidetakse igal juhul üks kord. Avaldis annab jätkamistingimuse. Kui see erineb nullist, siis täidetakse Tcl-prog, seejärel Tcl-vahe ning naastakse jätkamistingimuse arvutamisele. Kui avaldise väärtuseks on null, siis väljutakse. Tulemuseks tühisõne. set b "" for {set i [expr [llength $a]-1] } { $i >= 0 } { set i [expr $i - 1] } { lappend b [lindex $a $i] } foreach tsükiindeks nimistu Tcl-prog Tcl-prog täidetakse iga antud nimistu elemendi jaoks. Tsükliindeks on muutuja jooksva elemendi väärtuse hoidmiseks. Tulemuseks tühisõne. set b "" foreach i $a { set b [linsert $b 0 $i] } switch ?-meetod? ?--? sõne näidis Tcl-prog \ ? näidis Tcl-prog ... ? switch ?-meetod? ?--? sõne { näidis Tcl-prog ? näidis Tcl-prog ... ? } Sõne sobitatakse antud näidis(t)ega ning täidetakse vastav Tcl-programm. Näidiseks võib (viimasena) olla "default". Meetoditeks on -glob (vaikimisi), -exact, või -regexp. Tulemuseks on täidetud haru tulemus või tühisõne. Kui Tcl-prog asemel seisab miinusmärk, siis tähistab see järgmise haruga seotud Tcl-programmi. Esimeses kujus on switch-käsul kuitahes palju parameetrid, teises kujus on lisaks võimalikule meetodile ainult 2 parameetrit - sõne ja nimistu. switch $s { a - b - c { incr i } default { incr j } } source failinimi - väärtustab antud faili sisu Tcl-programmina. Tulemuseks selle programmi tulemus. Tcl - protseduurid Protseduuride defineerimine toimub ühel (globaalsel) tasemel, üksteisesse sisestatud protseduurid puuduvad. proc nimi formparamnimistu Tcl-prog Luuakse protseduur antud nimega. Formaalsed parameetrid antakse nimistuna, mille elementideks on muutujanimed või alamnimistud paaridest muutujanimi ning vaikimisi väärtus (kui mingile parameetrile on antud vaikimisi väärtus, siis peab see olema antud ka kõigile järgnevatele!). Formaalne parameeter "args" nimistu lõpus vastab tegelike parameetrite suvalise pikkusega nimistule. return ?veatöötluslipud? ?väärtus? Kasutatakse protseduurist väljumiseks proc incval { value { increment 1 } } { expr $value + $increment } incval 42 3 ==> 45 incval 16 ==> 17 proc sum args { set s 0 ; foreach i $args { incr s $i } return $s } sum 1 2 3 4 5 ==> 15 global muutuja ?muutuja ... ? - antud nimedega globaalmuutujad muudetakse protseduuri sees kättesaadavaks (vaikimisi on kõik protseduuris esinevad muutujad lokaalsed). See käsk võib esineda kustahes protseduuri sees. Tulemuseks tühisõne. Lahendamisajal on võimalik kasutada ka väljakutsuvate protseduuride keskkonda. Näiteks võib siduda protseduuri lokaalmuutuja mingi teda välja kutsunud protseduuri muutujaga upvar?tase? muutuja lokaalmuutuja ? muutuja lokaalmuutuja ... ? Lokaalmuutuja seotakse muutujaga nime järgi (taseme puudumisel väljakutsuva protseduuri kontekstis, tase #0 - globaalne) Tulemuseks tühisõne. proc muutujatrykk { muutuja } { upvar $muutuja m puts stdout "$muutuja = $m" } set p 4 ; muutujatrykk p ==> p = 4 global x on samaväärne upvar #0 x x uplevel ?tase? arg ? arg ... ? Argumendid ühendatakse tühikute abil Tcl-programmiks (analoogil. eval) ning saadud programm täidetakse antud taseme kontekstis (vaikimisi väljakutsuva protseduuri kontekstis, #0 - globaalses kontekstis). Tulemuseks täidetud programmi tulemus. Näide. Uue juhtimiskäsu defineerimine proc do { indeks algus lopp sisu } { upvar $indeks v for { set v $algus } { $v <= $lopp } { incr v } { uplevel $sisu } } Kasutamine: set a { } do i 1 5 { lappend a [expr $i * $i] } set a ==> 1 4 9 16 25 info level tase Silumisvahend. Pöördumisel ilma parameetrita tase annab taseme numbr i (0 - globaalne, ... ). Antud taseme jaoks on tulemuseks nimistu antud tasemel töötava protseduuri nimest ja parameetritest. Tcl - veatöötlus Veatöötlus on erijuht Tcl üldisest katkestuste töötlemise mehhanismist (exception handling). Globaalmuutujad errorCode ja errorInfo võimaldavad edastada infot katkestuse kohta. Lisaks veasituatsioonidele tekitavad katkestusi käsud error, break, continue, return. catch Tcl-prog ?muutuja? Antud programm täidetakse veatöötluse reziimis. Tulemuseks on programmi katkestuskood, muutuja kaudu saab edasi anda lisainfot katkestuse kohta: 0 - normaalne lõptamine, muutuja väärtuseks programmi töö tulemus 1 - viga programmis, muutuja väärtuseks veateade 2 - programmist väljuti return-käsuga, muutuja väärtuseks tagastatav väärtus 3 - täideti break-käsk, muutujas tühisõne 4 - täideti continue-käsk, muutujas tühisõne ... - kasutaja poolt defineeritavad koodid error teade ?info? ?kood? Genereerida viga. Veateade on töödeldav catch-käsuga, info (kui on mittetühi) salvestatakse globaalmuutujasse errorInfo, kood globaalmuutujasse errorCode. return ?-code katkestuskood ? \ ?-errorinfo info ? ?-errorcode kood? ?sõne? Protseduurist väljumine. Katkestuskood on ok, error, return, break, continue või arv. Saab väärtustada globaalmuutujad errorInfo ja errorCode, sõne annab tagastatava väärtuse või veateate (vaikimisi tühisõne). errorInfo on mõeldud trasseeringu saamiseks (millises kohas viga tekkis) errorCode on nimistu, mille esimene element näitab vea klassi ja järgnevad elemendid (kui esinevad) on klassist sõltuvad täpsustajad. if [ catch { käsk argum } tulemus ] { puts stderr $tulemus } else { # normaalne lõpp, resultaadiks $tulemus }