PŘECHOD OD REGULÁRNÍHO VÝRAZU KE KONEČNÝM AUTOMATŮM (POČÍTAČOVÁ REALIZACE) napsal Radim Tkačík
Bakalářská práce předložená k získání akademického titulu bakalář Ostravská Univerzita 1997
Vedoucí bakalářské práce: Doc. RNDr. Alica Kelemenová, CSc.
OBSAH
Obsah ....................................................................................................... i Poděkování .......................................... .................................................. ii Kapitola 1 - Úvod ................................................................................... 1 Kapitola 2 - Postupy a algoritmy ....................................................... 2 - 4 Kapitola 3 - Program ....................................................................... 5 - 10 Kapitola 4 - Práce s programem .................................................... 11 - 13 Literatura ................................................................................................ x
i
PODĚKOVÁNÍ
Autor by rád poděkoval svým spolužákům Viktoru Pavliskovi a Hashimu Habiballovi za jejich mnohé podnětné rady a připomínky, dále docentce Kelemenové za její ochotu konzultovat tuto práci a ostatním vyučujícím, kteří mi dali základy k vytvoření této práce. Také by rád poděkoval vysoké škole za poskytnutý software, obzvláště za Delphi 2.
ii
Kapitola 1
ÚVOD Toto je pouze doplňující text k programu. Při čtení tohoto popisu jsou předpokládány od čtenáře základní znalosti z teorie regulárních jazyků a v třetí kapitole i základní znalosti programování. Vysvětlení budou podána pouze u pojmů, které potřebují bližší specifikaci nebo u kterých může dojít k neporozumění díky mnohoznačnosti takovéhoto pojmu. V první části budou popsány stručně postupy a algoritmy, které byly v programu provedeny. V druhé části se pozastavíme nad vnitřní částí programu. V třetí části pak bude uveden krátký popis práce s programem.
1
Kapitola 2
POSTUPY A ALGORITMY Vstupem je regulární výraz zadaný v infixu. Pojmem infix rozumíme notaci zápisu, v kterém se binární operátory vyskytují mezi identifikátory (např. a+b). Regulární výraz může mít na vstupu jednotlivé znaky z abecedy {a,..,z,A,..,Z} plus ještě znak prázdného řetězce epsilon (ε) označeného @ a znaky, které nám zastupují operace zřetězení (.), spojení (+), iterace (*) a mocniny (^). Znak zřetězení není nutno psát. Syntaktická pravidla jsou uvedena v kapitole 3. Dalším krokem je převod na postfix, který se provádí pro následné zpracování počítačem. Pojmem postfix rozumíme notaci zápisu, v kterém se binární operátory vyskytují za identifikátory (např. ab+). Pro lepší představu rozdílu mezi infixovou a postfixovou formou zápisu uvedu příklad: Výraz zapsaný v infixu převedu na výraz zapsaný v postfixu: abc+(a+c)*+a^3 Æ ab.c.ac+*+a^3+
Výraz v postfixu se nyní převede na Zobecněný nedeterministický konečný automat (dále jen ZNKA). Pojmem zobecněný rozumíme, že v konečném automatu se můžou vyskytovat epsilon přechody. Pojmem nedeterministický rozumíme, že v konečném automatu může být více vstupních stavů než jeden a že z jednoho stavu může vystupovat více stejně pojmenovaných přechodů (stejným znakem z abecedy se můžeme dostat do různých stavů) nebo ze stavu nemusí vystupovat žádný přechod. Z postfixu se čte postupně znak za znakem a podle určitých pravidel se na zásobníku (struktura LIFO) vytváří konečný automat (dále jen KA). Z jednoho znaku abecedy (např. a) se vytvoří na vrcholu zásobníku KA: 2 a Když je přečtený znak ., potom se vyberou dva KA z vrcholu zásobníku, provede se na nich operace zřetězení a výsledek se vloží do zásobníku. Např. při a.b se vytvoří KA: b a a b . 1 2 3 4 1 2 4 = 1
Když je přečtený znak +, potom se vyberou dva KA z vrcholu zásobníku, provede se na nich operace spojení a výsledek se vloží do zásobníku. Např. při a+b se vytvoří KA: a ε ε 1 2 b a + 1 2 3 4 5 6 = b 3 4 ε ε 2
Když je přečtený znak *, potom se vybere jeden KA z vrcholu zásobníku, provede se na něj operace iterace a výsledek se vloží do zásobníku. Např. při (ab)* se vytvoří KA: b 2 a a b ε * = 1 2 3 4 1 Kdyby nastala situace načrtnuta níže, iterace by se řešila trochu jinak: a b ε ε * = 1 2 3 4 1 c a c 2 3 b Když je přečtený znak ^, potom se vybere jeden KA z vrcholu zásobníku, provede se na něj operace zřetězení tolikrát, kolikrát je za znakem ^ uvedeno a výsledek se vloží do zásobníku. Např. při a^3 se vytvoří KA: 1
a
2
^3 =
a
1
2
a
a
3
4
Po přečtení a zpracování všech znaků z postfixu získáme na vrcholu zásobníku ZNKA ekvivalentní s námi zadaným regulárním výrazem. Dalším krokem je převod získaného ZNKA na Nedeterministický konečný automat (dále jen NKA) tím způsobem, že odstraníme epsilon přechody. Algoritmus je následující: Pokud vede nějaký stav 1 epsilonem do dalšího stavu 2, podíváme se na všechny stavy, které vedou do toho stavu 1 a všechny přechody, které tam vedou přesměruji na stav 2, na který vede náš aktuální epsilon přechod. Pokud je stav stavem vstupním, potom stav, na který vede epsilon přechod se stane také vstupním. Následně se ještě u výsledného NKA zruší nedosažitelné stavy. NKA je dále převeden na Deterministický konečný automat (dále jen DKA), který ještě nemusí být totální (vysvětleno dále). Převod je proveden pomocí klasické podmnožinové konstrukce (stromem). Strom začíná množinou všech vstupních stavů. Uvededeme příklad, který nám tuto podmnožinovou konstrukci dostatečně přiblíží: 0,1
Př: Převeď NKA strom: {A}
A
{A,B} {A} {A,B} {A,C} {A,B} {A,D} {A,B} {A,D} 4
0
B
1
C
1
1
D
na DKA.
- stavy DKA jsou ve stromu jednotlivé podtrhnuté množiny - tyto stavy DKA označím čísly, takže postupně jak jdou ve stromu odshora dolů je označím 1 až - výsledný DKA vypadá takto:
1
1
0 0
2
0
1
3
1
1
4 0
3
KA se pak ztotální tím, že pro všechny použité přechody, které z nějakého stavu nevystupují, se vytvoří další stav, do kterého se tyto přechody nasměrují. Pojem totálnosti je z tohoto popisu algoritmu zřejmý. Nyní můžeme přistoupit k redukci našeho totálního KA. Zredukovanému KA se také říká podílový automat (dále jen PA). Při algoritmu redukce se používají třídy stavů. Na počátku máme dvě třídy a to třídu všech výstupních stavů a třídu ostatních stavů. Nyní pro každou jednotlivou třídu provedeme stejné kroky: Podíváme se do jakých tříd v jednotlivých přechodech jeden stav z třídy vede, a dáme jej pak do stejné třídy s ostatními stavy, které vedou do stejných tříd ve všech přechodech. Pokud projdeme všechny třídy a žádná se už nerozdělí (tzn. Nevytvoří se už nová třída), znamená to, že už prakticky máme zredukovaný automat (PA). Jenom stačí vzít z každé třídy jeden stav, a zjistit do jakých tříd ve všech přechodech vede. Uvedeme krátký příklad: Př: KA zadaný tabulkou převeď na PA (zredukuj). - KA je zadán: A B C D E
a B B B A E
b D C D E E
1 2 3 4
a 2 2 1 4
b 3
- PA má tvar:
1 4 4
- v prvním kroku máme rozdělení do dvou tříd: 1={A,B,C} 2={D,E} - v dalším kroku bude rozdělení provedeno podle výše zmíněného algoritmu 1 a b 2 a b A 1 2 D 1 2 B 1 1 E 2 2 C 1 2 - vzniknou nám čtyři třídy: 1={A,C} 2={B} 3={D} 4={E} - v dalším kroku se nám už žádná třída nerozdělí, takže jsme u konce
Nyní stačí přečíslovat stavy našeho PA, tak abychom získali normovaný tvar deterministického konečného automatu (dále jen NA). PA převedeme do normovaného tvaru, jestliže množinu stavů ztotožníme s množinou {1,2,....,n} tak, aby pro libovolné i,j:1≤i≤j≤n, měl stav i menší vzdálenost od počátku stavu než stav j. Řekneme, že stav p má menší vzdálenost od počátku stavu než stav q, jestliže existuje u ze ∑*, tž. δ(q0,u)=p, přičemž pro každé v ze ∑*, tž. δ(q0,v)=q platí u
4
Kapitola 3
PROGRAM Algoritmy, jakožto jádro programu, jsou vytvořeny v Turbo Pascalu 7.0 a jsou uloženy ve třech unit-ech. První unit je UNITSYNT.PAS, ve kterém se provádí syntaktická analýza zadaného regulárního výrazu v infixu metodou s předsnímáním jednoho symbolu dopředu bez návratu s rozpoznáváním pěti druhů chyb v zápisu regulárního výrazu a s paralelním zápisem zadaného regulárního výrazu do postfixu. Rozpoznávané chyby jsou: chybí pravá závorka, chybí identifikátor nebo výraz, chybí operátor, přebývá pravá závorka, špatný znak. Druhý unit je UNITMAIN.PAS, který převede regulární výraz zadaný v postfixu na ZNKA. Třetí unit je UNITPREV.PAS, který provádí všechny ostatní přechody (tzn. ZNKA Æ NKA Æ DKA Æ Totální KA Æ PA Æ NA). Ostatní unity jsou již vytvořeny programovacím nástrojem Delphi 2.0, který je použit pro grafickou nadstavbu programu. Hlavním datovým souborem je Delphi projekt PREGJAUT.DPR, který má v sobě informace o všech použitých unitech a formech. Hlavním formem je MainForm, jehož unit se nazývá UNITPRJA.PAS, který vyvolává a obhospodařuje ostatní formy a unity. Form, v kterém se vizualizují provedené algoritmy se nazývá MDIChild. Jeho vlastníkem je MainForm, v kterém může být několik formů typu MDIChild. Unit patřící k formu MDIChild je CHILDWIN.PAS, ve kterém jsou veškeré kroky k vizualizaci provedených algoritmů, či různé jiné věci (jako např. zadání regulárního výrazu, tabulky, kontrola správnosti vyplnění tabulky, apod.). Další unity a k nim patřící okna jsou PREVFORM.PAS (jaké z převodů se mají vypsat na obrazovku), UNIT2.PAS (AboutBox), UNIT3.PAS (úprava vložené tabulky). Syntaktická pravidla pro zápis regulárního výrazu v infixové formě jsou následující (zápis Backusovou-Naurovou formou) - ε je prázdný znak:
::= {+ } ::= {{.} } ::= A | B | .... | Z | a | b | .... | z | @ ::= <pom1> | <pom2> <pom1> ::= <pom3> <pom2> ::= ( ) <pom3> <pom3> ::= ε | {*} | ^ {} ::= 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
5
Při zápisu struktury automatu v počítači je plně použito pointerů. Slovní zápis této struktury by byl dosti složitý a nebylo by to účelné, proto si uvedeme pouze deklaraci v programu a přibližný grafický zápis. Šipky v grafickém zápisu jsou chápány jako ukazatelé na adresu v paměti počítače. Za grafickým zápisem bude uveden krátký komentář. Deklarace hlavní struktury v programu je: type typstavu = set of (i,o,s,n); Tspojeni = ^Tstav; {stavy} Tspojeni1 = ^Tstav1; {přechody 1,0,ε} Tstav1 = record spoj : char; next : Tspojeni1; up : Tspojeni; end; Tstav = record key : integer; typ : typstavu; next : Tspojeni; down : Tspojeni1; end;
KA automat v takovémto tvaru: c
1
a
2
b
3
vypadá graficky v počítačové realizaci nějak takto: head
1 i
n
2
3
down
down
down
a
up
tail
o
b
up
c
up
tail1
Head označuje prvotní pointer, z kterým přistupujeme k ostatním položkám ve struktuře. Tail je použit jako zarážka pro stavy a Tail1 jako zarážka pro přechody. Na další stav a přechod se odkazujeme pomocí next, tzn. že když jsme s proměnnou actual ve stavu 2, tak následující stav je actual^.next. První přechod z actual je v actual^.down^.next. Přechodem bctual se dostaneme do stavu bctual^.up^.next. Spoj může být znak z abecedy {a,..,z,A,..,Z} plus ještě znak prázdného řetězce epsilon (ε) označeného @. Key je číslo stavu typu integer. Typ je typ stavu, který může 6
být buď vstupní (i), výstupní (o), vstupní i výstupní (s) nebo „obyčejný“ (n). Nyní bychom si krátce popsali slovně jednotlivé algoritmy, jak vypadají uvnitř programu. Tyto popisy algoritmů si nekladou nárok na úplnost, ale spíše by měly vystihnout hlavní myšlenky. Přechod z postfixového tvaru regulárního výrazu na ZNKA zde není třeba popisovat, protože je naprosto totožný s tím v teoretické části. Zmínili bychom se pouze o použité struktuře, která je deklarován takto: type
Thlavnispojeni = ^Thlavnistav; Thlavnistav = record key : Tspojeni; next : Thlavnispojeni; end;
Tato struktura definuje zásobník struktur typu Tspojeni. Je to ten zásobník, o kterém byla řeč v kapitole 2. Přechod ze ZNKA na NKA. Vstupem je ZNKA. Procházíme postupně všechny přechody v jednotlivých stavech a pokud narazíme na epsilon přechod (označený @), pak tento epsilon přechod smažeme. Nyní se nacházíme v nějakém stavu, který máme označený actual. Zjistíme, který přechod z jakéhokoli stavu vede do stavu actual; v tom stavu, ve kterém přechod vede do actual potom přidáme další přechod (pojmenovaný podle onoho přechodu), který povede do stavu, do kterého vedl již smazaný epsilon přechod. Tento algoritmus je platný, až na jednu výjimku a to pokud vede epsilon přechod ze vstupního stavu, potom ten stav kam povede epsilon přechod se stane dalším vstupním stavem. V algoritmu se ještě provádí kontrola, zda nechceme vytvořit přechod, který vede do stejného stavu jako jiný, stejně pojmenovaný přechod. Na konec se ještě odstraní nedosažitelné stavy. Tímto jsme v původně zadaném ZNKA vytvořili NKA, který je výstupem našeho algoritmu. Následuje popis přechodu z NKA na DKA. Používáme tři struktury. Z jedné struktury (nazveme ji vstupní) se čte - je to náš vstupní NKA. V další struktuře (nazveme ji výstupní) se postupně vytváří výstupní DKA má stejnou deklaraci jako je u NKA. Poslední strukturou (nazveme ji pomocná) je struktura deklarovaná následovně: type TspoluStavy = ^TStavyspolu; TspoluStavy1 = ^TStavyspolu1; TStavyspolu1 = record key : Tspojeni; next : TspoluStavy1; end; TStavyspolu = record key : Tspojeni;
7
next : TspoluStavy; down : TspoluStavy1; end;
Myslím, že ji není třeba blíže vysvětlovat, pouze si zdůrazníme, že key z TspoluStavy1 (nazveme jej key2) ukazuje v programu na stav vstupního NKA a key z TspoluStavy (nazveme jej key1) ukazuje v programu na stav výstupního DKA. Na začátku algoritmu přeuspořádáme přechody v jednotlivých stavech vstupního NKA tak, aby byly sloučeny po skupinách podle svých jmen. Dále vytvoříme nový key1 v pomocné struktuře, který bude ukazovat na taky právě vytvořený stav od výstupního DKA, který bude označen jako vstupní. Pod aktuální (právě vytvořený) key1 si vytvoříme jeden nebo více key2 podle toho kolik je vstupních stavů v NKA, které budou k těmto key2 přiřazeny. Nyní si zjistíme všechny druhy (jména) přechodů ve stavech z NKA, na které ukazují key2 v právě aktuálním key1. Teď procházíme postupně každé jméno přechodu a provádíme hlavní kroky. Vytvoříme si nový key1. Vezmeme si stav, který nám nabízí právě aktuální key2, který v tomto případě bude to první key2 pod prvním key1. V tomto stavu budeme kontrolovat jednotlivé přechody a jestli bude mít některý přechod stejné jméno jako právě hledaný přechod jdeme dál. Pod předchvíli vytvořeným key1 budeme vytvářet key2, které budou postupně ukazovat na stavy z NKA, na které ukazují všechny přechody, které budou hledaného jména v prohledávaném stavu z NKA. Nyní budeme kontrolovat, jestli právě vytvořené key2 pod key1 se již nevyskytují pod jiným key1. Jestli ano, potom právě vytvořené key1 a s ním i jeho key2 smažeme a vyhledáme v jakém key1 se již vyskytovali, v kterém zjistíme stav z DKA na který ukazuje, do kterého pak přesměrujeme přechod, který vytvoříme pod právě aktuálním stavem z DKA, který nazveme podle nalezeného přechodu. Jestli se nevyskytují, potom vytvoříme další stav v DKA na který bude ukazovat přechod z DKA, který vytvoříme pod právě aktuálním stavem z DKA, který nazveme podle nalezeného přechodu. Nyní vezmeme další jméno přechodu. Po kontrole a zpracování všech jmen přechodů vezmeme od aktuálního key1 další key1 jako aktuální a budeme znovu dělat vše od zjišťování všech druhů (jmen) přechodů. Toto budeme provádět tak dlouho, dokud bude ještě existovat další key1. Pokud už další key1 existovat nebude, potom jsme na konci a naším výstupem je vytvořený DKA. Během algoritmu ještě nesmíme zapomenout na přidání výstupních stavů. Výstupním stavem se stane ten stav v DKA, na který ukazuje některý z key1, který obsahuje aspoň jeden stav z NKA, který je výstupní. Nyní vytvořený DKA ztotálníme. Vstupem bude náš DKA a výstupem bude ztotálněný DKA. Vytvoříme si nový stav v DKA, který povede ve všech přechodech sám na sebe. Nejprve si zjistíme všechny jména přechodů ve vstupním DKA. Potom procházíme postupně každé jméno přechodu a jestli se toto jméno nevyskytuje pod právě kontrolovaným stavem, 8
pak přidáme nový přechod s hledaným jménem pod kontrolovaný stav. Tento přechod povede na nový stav, který jsme na začátku vytvořili. Takhle to děláme v každém stavu v DKA. Pokud se nevytvoří ani jeden přechod, tak smažeme ten vytvořený stav v DKA. Podílový automat bude výstupem dalšího algoritmu, jehož vstupem bude totální DKA. Kromě struktury vstupního DKA používáme ještě dvě další struktury. Jedna z nich je deklarovaná následovně: type
TpodilStavy TStavypodil trida : next : end;
= ^TStavypodil; = record integer; TpodilStavy;
Je to pomocná struktura, kde se zaznamenává v jaké třídě leží stav z automatu. Položka trida (nyní ji nazveme trida2) se používá pro označení třídy při vytváření podílového automatu. Před začátkem algoritmu bude v této struktuře vytvořeno tolik stavů, kolik jich je ve vstupním DKA. Další struktura je deklarována takto: type
TukazStav = ^Tstavukaz; TukazStav1 = ^Tstavukaz1; Tstavukaz1 = record spoj : char; next : TukazStav1; up : TukazStav; end; Tstavukaz = key : typ : next : down : ukaz : ukaz2 : trida : end;
record integer; typstavu; TukazStav; TukazStav1; TpodilStavy; Tspojeni; integer;
Tato struktura bude nyní hlavní, která se bude používat. Do této struktury se překonvertuje náš vstupní DKA a budou se s ní potom provádět veškeré operace. Oproti struktury vstupního DKA zde je navíc ještě položka ukaz, která je ukazatelem na nějaký ze stavů ve struktuře TpodilStavy - každý ze stavů ve struktuře TukazStav ukazuje přes položku ukaz na stav ve struktuře TpodilStavy. Položka ukaz2 se používá až ke konci algoritmu, když už vytváříme podílový automat hlavní struktury Tspojeni ze struktury TukazStav. Položka trida (nyní ji nazveme trida1) má stejný význam jako
9
ve struktuře TpodilStavy. Na začátku algoritmu rozdělíme stavy do tříd, podle toho jestli jsou výstupní (třída 1) nebo ty ostatní (třída 2) - číslo třídy se uloží pod jednotlivé stavy do položky trida1. Nyní vezmemem postupně každou ze tříd, které máme (pro tentokrát dvě), např. třídu 1. Nyní budeme podle klasického algoritmu pro vytváření PA popsaného v kapitole 2 rozdělovat stavy do dalších tříd. Třídy se budou zapisovat do naší pomocné struktury do položky trida (trida2). Tyto třídy se budou číslovat od nejvyšší třída + 1 a po jedničce se bude číslo třídy zvětšovat dokud to bude třeba. Po skončení rozdělení do tříd přečíslujeme trida2, aby to bylo číslováno od jedničky. Zkontrolujeme jestli se to rovná číslování v trida1. Jestli ano, potom jsme u konce a stačí vytvořit PA podle známého algoritmu (popsaného v kapitole 2). Jestli ne, čísla tříd z trida2 zkopírujeme do trida1 a opakujeme zase algoritmus, ale nyní pro jiný rozsah počtu tříd (zvětšený o nové třídy). Výstupem pak bude PA hlavní struktury Tspojeni. Posledním algoritmem je převod PA na normovaný tvar KA. Začínáme na vstupním stavu, který si označíme 1. Všechny stavy, na které ukazují přechody ve vstupním stavu, označíme čísly 2, 3, atd. Každý nově označený stav dáme do fronty (struktura FIFO). Teď si s fronty vezmeme stav (v našem případě 2) a číslujeme stavy, na které ukazují jeho přechody dál, podle toho, jestli se už ty stavy, na které ukazují přechody očíslovaly nebo ne. Jestli ne, potom to očíslujeme dalším, ještě nevyužitým číslem a vložíme do fronty. Jestli ano, potom tomuto stavu přiřadíme číslo, které se rovná tomu stavu, na který ukazuje přechod. Vezmeme si další stav z fronty a provádíme vše tak dlouho, dokud nebude fronta prázdná. V naší struktuře pak ještě stavy uspořádáme tak, aby se jejich číselné upořádání rovnalo jejich fyzickému upořádání. Výstupem bude normovaný tvar KA. Nakonec této kapitoly bychom se ještě zmínili o formátu souboru programu PREGJAUT. Soubor začíná řetězcem EDIT nebo TABULKA0, podle toho, jestli je první editační okno nebo editační tabulka. Za řetězcem EDIT následuje řetězec, který byl v editačním okně. Za EDIT nebo TABULKA0 je řetězec TABULKA (x myslíme číslo 1 až 6). TABULKA0 je editační tabulka. TABULKA1 je tabulka obsahující ZNKA. TABULKA2 je tabulka obsahující NKA. TABULKA3 je tabulka obsahující DKA. TABULKA4 je tabulka obsahující TKA. TABULKA5 je tabulka obsahující PA. TABULKA6 je tabulka obsahující NA. Za řetězcem TABULKAx je řetězec SPOJE, za kterým jsou vypsány postupně všechny spoje ukončené znakem /. Následuje řetězec STAVY, za kterým jsou vypsány střídavě všechny typy a čísla stavů - ukončení je na znaku /. Dále je řetězec PRIRAZENI, za kterým jsou vypsány postupně všechny přiřazení přechodů od stavů ke stavům (po řádcích). Prázdné přiřazení jsou označeny znakem -. Celý soubor končí znakem /.
10
Kapitola 4
PRÁCE S PROGRAMEM Vše se odehrává v oknech, které se vyvolávají v hlavním okně programu pomocí výběru Nový nebo Otevřít z nabídky soubor. Nabídky a jejich výběry se dají vyvolat nejen myší, ale i pomocí kláves Alt+písmeno z nabídky nebo výběru, které je podtrženo nebo pomocí „rychlých tlačítek“. Klávesovou zkratkou se dá vyvolat pouze funkce převeď (Ctrl+P). Nyní budou probrány jednotlivé nabídky spolu s jejich funkcemi. Z nabídky Soubor se dají vyvolat tyto funkce: • Nový ... slouží k otevření nového okna v hlavním okně programu (tato funkce se dá vyvolat i „rychlím tlačítkem“) • Otevřít ... po vyvolání se otevře nabídka souborů na disku, z které se vybere soubor formátu programu PREGJAUT (s koncovkou .pre) a tento soubor se potom otevře v novém okně (tato funkce se dá vyvolat i „rychlým tlačítkem“) • Zavřít ... zavře právě aktuální okno • Uložit ... uloží právě aktuální okno pod jeho názvem ve formátu programu PREGJAUT (s koncovkou .pre) (tato funkce se dá vyvolat i „rychlým tlačítkem“) • Uložit pod jménem ... uloží právě aktuální okno pod názvem zapsaným nebo vybraným z nabídky souborů na disku ve formátu programu PREGJAUT (s koncovkou .pre) • Konec ... ukončí program PREGJAUT (tato funkce se dá vyvolat i „rychlým tlačítkem“) Z nabídky Editace se dají vyvolat tyto funkce: • Vyjmout ... je funkční pouze v editačním okně pro zápis regulárního výrazu • Kopírovat ... zkopíruje do schránky označenou položku v editačním okně nebo si zapamatuje právě aktivní „uživatelskou“ tabulku pokud je při výběru této funkce tato tabulka aktivní; před zkopírováním editované tabulky se tato tabulka zkontroluje, jestli je správně vyplněna (tato funkce se dá vyvolat i „rychlým tlačítkem“) • Vložit ... pokud byla naposledy kopírována tabulka, potom se právě aktivní okno „vyčistí“ a vloží se tam kopírovaná tabulka; pokud byla kopírována naposledy nějaká položka z editačního okna, potom pokud jsme v editačním okně, tak se nám tam tato položka vloží (tato funkce se dá vyvolat i „rychlím tlačítkem“)
11
• Odstranit ... je funkční pouze v editačním okně pro zápis regulárního výrazu Z nabídky Převody se dají vyvolat tyto funkce: • Převést ... Převede tabulku nebo regulární výraz na konečné automaty označené v nabídce Zobrazit; před vykonáním převodu editované tabulky se tato tabulka zkontroluje, jestli je správně vyplněna (tato funkce se dá vyvolat i „rychlým tlačítkem“ nebo pomocí kombinace kláves Ctrl+P) • Vložit regulární výraz ... „vyčistí“ aktivní okno a vloží zde editační okno pro zápis regulárního výrazu, které se dá libovolně zvětšovat (do délky) (tato funkce se dá vyvolat i „rychlím tlačítkem“) • Vložit tabulku ... „vyčistí“ aktivní okno a vloží zde čistou tabulku pro zápis automatu, kterou lze na všechny strany zvětšovat (její parametry - počet řádků a sloupců - se potom ukazují v dolní části okna); tato tabulka se dá kopírovat a vkládat (tato funkce se dá vyvolat i „rychlím tlačítkem“) • Upravit tabulku ... zobrazí dialogové v okno, v kterém se dá měnit počet řádků a sloupců v editační tabulce, přičemž se smažou všechny ostatní objekty v aktivním okně • Zobrazit ... zobrazí dialogové okno, v kterém se dá zaškrtnout, které převody chceme zobrazit po výběru funkce Převést Z nabídky Okno se dají vyvolat tyto funkce: • Dlaždice ... všechna okna uspořádá jako dlaždice • Kaskáda ... všechna okna uspořádá do kaskády • Uspořádat ikony ... uspořádá všechny minimalizovaná okna • Minimalizovat všechna okna ... všechna okna zminimalizuje • Informace o paměti ... po výběru této funkce se buď objeví u spodního okraje hlavního okna informace o alokované a volné paměti nebo se tato informace schová Z nabídky Nápověda se dají vyvolat tyto funkce: • Obsah ... zobrazí nápovědu k programu PREGJAUT (tato funkce se dá vyvolat i „rychlým tlačítkem“) • Jak používat nápovědu ... zobrazí informace o tom, jak pracovat s nápovědou • O aplikaci PREGJAUT ... zobrazí informace o programu Formát tabulek automatů je následující. Do prvního sloupce (kromě prvního neobsazeného políčka) se zapisují typy jednotlivých stavů, tj. i (vstup),o (výstup),s (vstup i výstup),n („obyčejný“). Do druhého sloupce (taktéž kromě prvního neobsazeného políčka) se zapisují čísla jednotlivých stavů. Do prvního řádku (kromě prvních dvou neobsazených políček) se zapisují jednotlivé jména přechodů. Zbytek políček je zaplněn čísly stavů, podle toho, do kterého stavu jde přechod z určitého stavu. Pokud nejde stav určitým přechodem do nějakého stavu, potom políčko patřící k tomuto
12
vztahu stav-přechod bude prázdné. Pokud jde stav určitým přechodem do více stavů, pak tyto stavy jsou v patřičném políčku odděleny čárkou.
13
LITERATURA
Chytil, M.: Automaty a gramatiky, Matematický seminář SNTL 19, Praha 1984. Demlová, M., Koubek, V.: Algebraická teorie automatů, Matematický seminář SNTL 26, Praha 1990.
x