ADE: Flex 2.5.4Jan Skýpala
Flex znamená Fast lexical analyzer generator, tedy generátor rychlé lexikální
analýzy. Souvisí se zpracováním vstupu zapsaného v nějakém jazyce (přečtěte si
prosím text o překladu programu před čtením tohoto článku). Flex je utilitou pro generování „scannerů“ - programů, které umí rozpoznávat
ve svém vstupu jednotlivé prvky nějakého jazyka na základě uvedených vzorů. Flex
čte vstupní soubor (nebo standardní vstup, když mu není žádný vstupní soubor
zadán), který obsahuje popis generovaného scanneru. Tento popis je složen jednak
z regulárních výrazů - vzorů popisujících prvky jazyka a céčkovských rutin. Jako
output je vygenerován zdroják v céčku, který obsahuje hlavní funkci yylex().
Tento soubor může být zkompilován a slinkován s knihovnou libfl.a a když bude
spuštěn, bude analyzovat svůj vstup na výskyt jednotlivých prvků odpovídajících
zadaným vzorům. Když je daný vzor rozpoznán, je vykonán odpovídající kód v
céčku.
Zkusme si nyní naprosto banální příklad takového scanneru. Vytvořte soubor
nazvaný třeba pokus1.l (přípona .l je standardem pro vstup pro lex či flex),
který bude obsahovat: %%
PC printf(„Amiga“); Nyní spusťte flex pokus1.l a bude vygenerován soubor nový, se jménem lex.yy.c
(to je standard, pomocí parametru t lze jako výstup zvolit standardní výstup a
ten pak přesměrovat do souboru jiného jména). Nyní spusťte gcc lex.yy.c -lfl a
na disk bude vygenerován program (a.out), který bude číst standardní vstup, a
kdekoliv se v něm objeví PC, bude zaměněno za Amiga.
Základní funkce scanneru vygenerovaného flexem je číst standardní vstup, a když
v něm není nalezen žádný vzor, pouze jej zkopíruje na výstup. Pokud ale je vzor
rozpoznán, je vykonána odpovídající akce. Náš malý příklad měl jediné pravidlo,
v němž PC je vzorem a printf(„Amiga“); je akce. Znaky %% označují začátek
pravidel.
To byl pochopitelně velmi jednoduchý příklad. Před ony dva znaky označující
začátek pravidel lze uvést jména proměnných, které budete používat. Takže tam
přidejte lines=0;. Přidejte pravidlo, jehož vzorem bude
(známý céčkový
dvojznak pro nový řádek) a jeho akci fines++; (mezi pravidly vynechte volný
řádek). Za pravidla přidejte opět dva znaky %% a za ně můžete připojit libovolné
céčkové rutiny. Takže tam napište: main()
{
yylex();
printf(„ %d
“, lines);
} Když teď tento lexový program přeložíte až do spustitelného programu a
spustíte, zjistíte, že po přechroustání vstupu bude navíc vypsán počet řádku
vstupu.
Pochopitelně není vhodné číst vstup pouze z konzoly či používat přesměrování
standardního vstupu, proto když otevřete (standardní céčkovou funkci fopen())
soubor do proměnné se jménem yyin, bude vstup čten z tohoto souboru místo ze
standardního vstupu. Podobně yyout může specifikovat výstupní soubor.
Ve vzorech lze pochopitelně specifikovat nejen nový řádek, ale třeba i tabulátor
( ), znak tečka bude označovat jakýkoliv znak. [0-9] znamená jednu číslici,
[0-9]+ znamená několik číslic (ale alespoň jedna), kdežto použití hvězdičky
místo plusu znamená žádná nebo několik číslic.
Shrňme si znovu formát vstupního souboru. Ten je rozdělen do tří částí:
definice, pravidla a uživatelský kód. Mezi dvěma částmi je vždy řádek, na němž
jsou pouze znaky %%. V definicích je možné uvést proměnné, které pak budou v
analyzátoru uvedeny. Je zde také možné definovat symbolické jméno pro nějaké
symboly, například řádek DIGIT [0-9] zajistí, že kdykoliv později v pravidlech
uvedeme {DIGIT}, bude toto nahrazeno zadefinovaným výrazem.
Část s pravidly nám umožňuje ke každému vzoru zadat akci (kód v céčku), která
bude provedena, je-li ve vstupu rozpoznán nějaký text odpovídající uvedenému
vzoru. Odpovídá-li text více vzorům, bude vybrán vzor, podle něhož je text
nejdelší. Pokud existuje více vzorů popisujících stejnou maximální délku textu,
bude použit vzor, který je uvedený jako první.
Akce uvedené v části s pravidly jsou zahrnuty přímo do generované funkce yylex()
a tato funkce vrací hodnotu typu integer. To je velice vhodné, jelikož do akce
můžeme dát např. return(NUMBER);, kde NUMBER bude symbol označující číslo a
tento symbol bude pak zpracovávat syntaktická analýza. Takto je zapojený
lexikální analyzátor do syntaktického analyzátoru vygenerovaného yacc-em (nebo
bisonem) a pro každý rozpoznaný vstupní symbol nám funkce yylex() vrátí jeho
kód, pro nerozpoznané se vloží rutina pro vypsání chyby.
Do poslední části si pak můžete zapsat podpůrné rutiny, které budete potřebovat
volat v akcích, případně nějaké funkce navíc, které přímo souvisí s lexikální
analýzou, a tudíž je vhodné je držet v jednom souboru s definicí scanneru.
Vraťme se ještě k definic pravidel. Jak si určitě pozorný čtenář všiml, to že
tečka je použita jako vyjádření jakéhokoliv znaku (tudíž je většinou použita pro
odhalení chyby, jelikož bude vybrána právě když nesedí žádné jiné pravidlo), což
ovšem zabraňuje jejímu použití jako symbolu vstupu. To lze pochopitelně velmi
jednoduše vyřešit - stačí ji uzavřít do uvozovek a bude rozpoznána takto.
Podobně jsou použity jiné znaky - kromě již výše zmíněných hranatých závorek je
to třeba lomítko: vzor R/S nám rozpozná na vstupu symbol R pouze tehdy, je-li za
ním symbol S. Takto lze kontextově odlišit přímo v lexikální analýze různé
významy stejného symbolu.
Pochopitelně je vhodné také třeba rozpoznat nějaký symbol v závislosti na tom,
co bylo před ním. Protože se ve vstupu nelze jednoduše vracet (vstup může být
složen z více souborů), lze definovat stavy, v kterých se scanner nachází. Takto
lze například zadefinovat pro Pascal if BEGIN(IF);
<IF>then BEGIN(INITIAL);
then error(); Pochopitelně to chce uvést více kódu ke každému pravidlu, ale takto
zadefinovaná pravidla nám umožňují již v lexikální analýze rozpoznat, kdy je
příkaz then uveden na špatném místě (ve skutečnosti by ale bylo správné tuto
kontrolu uvést až v syntaktické analýze; jedno praktické využití se ale nabízí -
při začátku poznámky v programu bude nastaven nový stav a vše přicházející se
bude řídit podle nového podmíněného pravidla až do konce poznámky).
Bylo by pochopitelně neužitečné, kdyby funkce yylex() uměla číst vstup pouze ze
souboru. Jak se dalo předpokládat, umí tato funkce číst vstup i z oblasti paměti
(bud řetězce ukončeného nulou anebo obecně z kusu paměti o zadané délce), pomocí
podmíněných pravidel lze ale i velice rozumně zvládnout i vkládání souborů do
sebe (např. to, co dělá direktiva #include v céčku). Po rozpoznání direktivy
uvozující jméno vkládaného souboru se podmíněným pravidlem načte jméno souboru,
automat upraví na nový vstup a do zásobníku se uloží, že se automat vnořil.
Jakmile dojde scanner na konec tohoto souboru, zavolá funkci yywrap(), která má
vrátit jedničku, pokud se skutečně jedná o konec vstupu, nulu pokud bude vstup
ještě pokračovat - tehdy se nastaví pokračování ve čtení původního souboru.
Jazyk pro Flex toho umí mnohem více, ale dále se podrobnostmi nebudu zabývat.
Každý, kdo si chce napsat vlastní programovací jazyk či alespoň nějaký interní
jazyk pro svou aplikaci, se vše dočte v obsáhlé (111kB) dokumentaci.
Novinkou u Flexe je možnost generování výstupu pro jazyk C++, kde je scanner
vytvořen jako třída yyFlexLexer, která drží potřebné informace pro scanner jako
svá data (na rozdíl od výstupu vygenerovaného pro obyčejné céčko, kde jsou
vygenerovány jako globální proměnné), což umožňuje mít v programu scannerů
několik, případně i reenratntní scannery. Potřebné funkce jsou pak pochopitelně
dostupné jako metody objektu této třídy a u vstupu a výstupu je místo souboru
použit stream. Vygenerovaný soubor se bude jmenovat lex.yy.cc, vstupní soubor je
pochopitelně stejný při generování jak pro C tak pro C++. Generování pro C++ lze
zapnout parametrem -+, případně uvedením %option c++ na začátku vstupního
souboru.
Flex vám ušetří spoustu práce, pokud chcete psát nějakou lexikální analýzu a
nemáte čas nakódovat si vlastní scanner. Ten může být pochopitelně rychlejší,
ale ne o mnoho, Flex generuje velice rychlé automaty řízené tabulkami. Pokud
skutečně máte spoustu času a chuti, sežeňte si o tom literaturu a napište si
scanner vlastní, jinak použijte Flex.
A pochopitelně Flex je napsán s pomocí Flexe (a Bisona).
Flex 2.5.4 |
Hodnocení: 9,0 z 10 |
Autor: V. Paxson |
Cena: - |
Typ: freeware |
+ |
podmíněná pravidla, C++
interface, zdarma |
- |
další programovací jazyk |
Vytlačiť článok
Pozn.: články boli naskenované ako text a preto obsahujú aj zopár chýb. Taktiež neručíme za zdrojové kódy (Asm, C, Arexx, AmigaGuide, Html) a odkazy na web. Dúfame, že napriek tomu vám táto databáza dobre poslúži.
Žiadna časť nesmie byť reprodukovaná alebo inak šírená bez písomného povolenia vydavatela © ATLANTIDA Publishing
none
|