Programátorská část dokumentace

Na tomto místě jsou popsány všechny stěžejní unity, které projekt obsahuje.

Unita UnitHlavniOkno

V seznamu v levé části okna (ListBox1) jsou uvedeny všechny procedury patřící do projektu. Objekty procedur (exempláře třídy Procedura) jsou uloženy ve vlastnosti ListBox1.Items.Objects. Když uživatel dvakrát za sebou klikne na název některé z procedur, vytvoří se nové vlákno ZpracovaniZdrojovehoKodu, jehož konstruktoru se předají: objekt třídy Procedura reprezentující právě spuštěnou proceduru, plocha, na niž se kreslí (ZImage1) a konečně také seznam všech ostatních procedur. To proto, že třída ZpracovaniZdrojovehoKodu nemá přístup k objektům hlavního okna.

Třída TForm1 dále obstarává nahrávání a ukládání projektu, využívá při tom metod třídy Procedura (viz dále).

Unita UnitZpracovaniZdrojovehoKodu

Úmluva: u popisu této unity se slova procedura a parametr objevují se dvěma rozdílnými významy. Jednou se jimi myslí Delphovská procedura a Delphovský parametr, podruhé procedura Želví grafiky či parametr procedury Želví grafiky. Aby byl text srozumitelnější, kdykoliv bude mít některé ze zmíněných slov ten druhý význam, bude podtrženo.

Jádrem celé aplikace je třída ZpracovaniZdrojovehoKodu. Jak název napovídá, třída zpracovává zdrojový kód programu vytvořeného v Želví grafice. Třída ZpracovaniZdrojovehoKodu je potomkem třídy TThread, zpracování zdrojového kódu tedy probíhá jako samostatné vlákno. To má dobrý důvod. Kdyby tomu tak nebylo, při provádění prográmku by byly naprosto vyřazeny všechny ostatní funkce aplikace Želví grafika. Nefungovala by tlačítka, nešlo by hýbat s hlavním oknem. A to až do doby, než by prográmek skončil. Když pustíme zpracování zdrojového kódu jako zvláštní vlákno, výše uvedené problémy odpadnou a navíc lze provádění prográmku kdykoliv ukončit tlačítkem na horní liště.

Zdrojový kód se zpracovává klasickým algoritmem -- rekurzivním sestupem. Jednotlivé kroky nebudu popisovat do detailu, podrobnosti si zájemce může sám zjistit z komentářů ve zdrojovém kódu. Metoda, která je volána ihned po aktivaci vlákna, se nazývá Execute. Sama však nic nedělá, jen zavolá metodu pustProceduru. Ani tato metoda sama nic nedělá a ihned volá jinou metodu -- spustProceduru -- pouze s tím rozdílem, že volání je uzavřeno do bloku try, takže tato metoda je schopná ihned ošetřit všechny výjimky, které při zpracování kódu vzniknou. Vzhledem k tomu, že typů výjimek je velmi mnoho, má jejich zpracování na starosti více metod. Kód je tak přehlednější. Za zmínku stojí metoda napoveda, která otevře editační okénko s procedurou, která byla zpracovávána, když k výjimce došlo. Kurzor nastaví na poslední zpracovanou pozici. Uživatel tak má možnost dozvědět se, na kterém místě nastal problém.

Metoda spustProceduru nejprve zavolá metodu preberParametry, která přebere od uživatele parametry (pakliže spuštěná procedura nějaké bere), tzn. otevře několik oken se sdělením "Tato procedura vyžaduje parametr XY typu ABC...". Parametry jsou ukládány na zásobník, kde si je později (viz další odstavec) přebere metoda run. Metoda preberParametry se volá poněkud krkolomným způsobem: parametr se metodě předá pomocí globální proměnné proceduraPrebirajiciParametry. Důvod je ten, že metoda preberParametry pracuje s vizuálními prvky (okny) a musí být (z mně ne tak zcela jasných důvodů) provedena hlavním vláknem aplikace, tedy zavolána pomocí metody Synchronize. Metoda Synchronize je však s odpuštěním tak idiotská, že jejím parametrem smí být pouze bezparametrická metoda. Tudíž, chceme-li do preberParametry nějaký parametr propasírovat, nezbývá než použít globální proměnnou (přesněji řečeno privátní členskou proměnnou). Takovýchto krkolomných volání uvidíte ve zdrojovém kódu ještě více. (Pokud tento odstavec nechápete, doporučuji prostudovat tu část Delphovské nápovědy, která pojednává o multithreadingu.)

Pak na scénu přichází metoda run. Ta sebere parametry ze zásobníku, nahází je do proměnných a pustí metodu zpracujSkupinuPrikazu. Tato metoda ve smyčce volá metodu nactiPrikaz, která přečte klíčové slovo, a nechá rozhodnout metodu zpracujPrikaz, jakou speciální metodou má být tento konkrétní příkaz zpracován. Příkazů je mnoho, proto zmíním jen ty, jejichž zpracování je neobvyklé nebo náročnější na pochopení.

Za prvé, rozeberme si, co se stane, když interpret narazí na volání procedury. Zpracovávání kódu přejde na metodu zavolejProceduru. Ta projde seznam parametrů, které volaná procedura vyžaduje, a na základě jejich typů (číslo, logické) je ze zdrojového kódu načte a dá na zásobník. Potom na zásobník návratových pozic uloží aktuální pozici, aktuální pozici nastaví na začátek volané procedury a rekurzivně zavolá metodu run. Coby parametr jí předá volanou proceduru. Po skončení nastaví aktuální pozici na pozici uvedenou na vrcholu zásobníku.

Za druhé, trochu komplikovanější je přeskakování kusu kódu. To je zapotřebí, jestliže neplatí podmínka u příkazu pokud, nebo když se má nulakrát provádět nějaká smyčka. K tomuto účelu existuje procedura doctiZa. Pracuje následnovně: ví, že každý příkaz je zakončen středníkem a středník nemá ukončovací funkci pouze tehdy, pokud je uzavřen v uvozovkách nebo složených závorkách (komentář). Tedy, pro interpret není složité najít začátek příkazu. Pak už jenom stačí pamatovat si, jak hluboko jsme se zanořili do vložených bloků (narazíme na začátek blokového příkazu, zvětšíme hloubku, narazíme na příkaz konec, snížíme hloubku). Jakmile je hloubka vnoření nulová a narazíme na klíčové slovo ohraničující konstrukci, kvůli níž kód přeskakujeme, metoda doctiZa skončí a vrátí, po jaký příkaz kód přeskočila (např. nebo pokud, jinak, konec).

Unita ZImage

Unita realizující komponentu TZImage -- plochu, na niž želvička kreslí. Pamatuje si svůj stav, tedy aktuální polohu želvičky, směr, kterým je natočená želvička, dále to, jestli se má želvička vykreslovat a jestli má za sebou zanechávat stopu. Obsahuje metody pro nakreslení čáry a otočení želvičky, pak také metody pro změnu vlastností kreslení (tloušťka čáry, barva). Aby komponenta nemusela želvičku při každém pohybu mazat, obsahuje privátní objekt BM typu TBitmap. Když je zavolána procedura kreslící čáru, nekreslí se přímo na Canvas této komponenty, ale právě do BM. Pak je teprve vykreslen obsah BM na Canvas komponenty a na závěr se na Canvas nakreslí želvička. Lze to přirovnat ke skládání obrázku pomocí průsvitek -- na jedné průsvitce je nakresleno pozadí, na druhé pohybující se objekty.

Unita UnitProcedura

Každá procedura, kterou programátor v jazyce Želví grafiky vytvoří, je objektem třídy Procedura.

Čtení kódu procedury, čtení parametrů

Ke čtení kódu, který je uložen v objektu třídy Procedura, slouží funkce cti. Ta bere jeden parametr -- index znaku. Počet parametrů je uložen ve veřejné členské proměnné parametru, samotné parametry v poli parametry. Parametr je struktura o dvou položkách -- názvu parametru a typu parametru (celý, desetinný, logická hodnota), což je výčtový typ definovaný v unitě UnitObecnyDatovyTyp.

Načtení procedury ze souboru, zápis do souboru

Metoda ulozProceduru je využívána hlavní unitou, když uživatel požádá o uložení projektu. Hlavní unita pak projde seznam všech procedur, u každé zavolá metodu ulozProceduru a předá jí proměnnou typu TextFile, procedura ulozProceduru tak ví, kam má kód zapsat. Obdobné je to se čtením. Formát souboru je následující:

Každá odrážka ve výše uvedeném schématu odpovídá jednomu řádku souboru, s výjimkou kódu procedury, ten je na tolika řádcích, kolik je v souboru uvedeno. Jednotlivé procedury nejsou nijak odděleny.

Editační okno

Třída Procedura také obsahuje objekt třídy TForm2, tedy editační okno procedury. Když se čte zdrojový text procedury pomocí příkazu cti, nečte se přímo z komponenty RichEdit1 -- textové plochy v editačním okně. Místo toho je uživatel při zavírání editačního okna dotázán, jestli chce proceduru uložit. Odpoví-li Ano, je text v RichEdit1 zkopírován do členské proměnné třídy Procedura s názvem kodProcedury. Stejně tak se pomocí procedury ulozParametry uloží parametry zapsané v komponentě Edit2 editačního okna do pole parametry -- členské proměnné třídy Procedura.

Unita UnitObecnyDatovyTyp

Třída ObecnyDatovyTyp je tu ze dvou důvodů.

Obecný datový typ obsahuje navíc protected proměnnou typ, která značí, o jaký konkrétní typ jde. Potomci obecného datového typu jsou číselný typ a typ logické proměnné. Číselný typ implementuje všechny běžné aritmetické operace, upřesňuje výjimky (dojde-li k dělení nulou, ošetří systémovou výjimku, zároveň však vyvolá novou, typu ODTException), stará se také o to, aby měl výsledek správný typ, a tak dále. Objekt číselného typu má dvě privátní proměnné. Jedna slouží k uložení celého čísla, druhá k uložení desetinného. Vždy je použita právě jedna z nich.

Unita UnitDefiniceVyjimek

Aby se výjimky daly snadno ošetřit, je zaveden jednotný systém jejich ošetřování pro celou aplikaci Želví grafika. Jsou definovány nové výčtové typy, jejichž názvy končí na ExceptionCode -- slouží ke specifikaci, k jaké chybě konkrétně došlo. Jsou definovány nové výjimkové třídy, každá pro jiný typ chyby (chyba při zpracování zdrojového kódu, při výpočtu, práci s proměnnými, běhová chyba). Každá z těchto nových výjimkových tříd má konstruktor, který bere proměnnou, jejíž typ je některý z nově definovaných výčtových typů. Navíc každá tato výjimková třída implementuje vlastnost errType, která slouží ke zjištění konkrétního typu chyby (vrací kód nastavený konstruktorem). Všechny výjimky jsou pak ošetřeny na jednom místě -- v hlavní unitě. Ošetření výjimky znamená zobrazení okna se zprávou (ta závisí na typu errType), případně otevření editačního okna procedury s kurzorem nastaveným na poslední zpracované pozici.

Unita UnitPromenne

Objekt této třídy si pamatuje všechny existující proměnné a poskytuje základní operace s nimi -- vytvoření, vyhledání (včetně ošetření výjimek) a překrývání. Je-li zavolána nová procedura Želví grafiky, zavolá se metoda zvysVysku, která zvýší výšku zásobníku a tak umožní definovat proměnnou se jménem, které už bylo použito u jiné proměnné. Hodnota se překryje. Po skončení procedury se zavolá snizVysku.

Unita UnitZasobnikyParametruAPozic

Když je zavolána procedura Želví grafiky, interpret přečte její parametry a uloží je na zásobník parametrů pomocí metody newParam třídy Zasobniky. Potom uloží aktuální pozici (tj. zpracovávanou proceduru a index posledního zpracovaného znaku zdrojového kódu) na zásobník návratových pozic pomocí procedury newCall třídy Zasobniky. Pak se nastaví aktuální pozice na začátek volané procedury a rekurzivně se zavolá metoda třídy ZpracovaniZdrojovehoKodu, která má na starosti zpracování procedury -- pustProceduru. První, co tato metoda udělá po svém spuštění je, že ze zásobníku parametrů načte parametry, které přebírá, a uloží je do proměnných. Jakmile skončí, ze zásobníku návratových pozic se vyjme nejvrchnější prvek, tedy místo, kam se má zpracování vrátit, a na něj se nastaví aktuální pozice.

Co se týče implementace zásobníků, je použito pole a integerová proměnná udávající výšku zásobníku, tedy index vrcholu. Při překročení naximální povolené výšky některého ze zásobníků (konstanty MAX_PARAM_STACK_DEPTH a MAX_CALL_STACK_DEPTH) je vyvolána výjimka.