L E X Generator analizatorów leksykalnych
GENERATOR L E X Zadaniem generatora LEX jest wygenerowanie kodu źródłowego analizatora leksykalnego (domyślnie) w języku C; Kod źródłowy generowany jest przez LEX a w oparciu o plik zawierający wszystkie reguły przetwarzania; Plik z regułami tworzony jest przez samego użytkownika; 2
GENERATOR L E X Schemat organizacji działania LEX a: scan.l L E X G C C scan.c scan.exe WYNIK plik.txt 3
GENERATOR L E X flex l scan.l (użycie generatora LEX) gcc scan.c -o scan.exe (kompilacja C++) scan.exe < plik.txt (analiza plik.txt) 4
GENERATOR L E X Ważną cechą analizatora jest możliwość wykorzystania go do większych aplikacji; Każdy wygenerowany kod źródłowy zawiera bowiem funkcję, dzięki której można podłączyć analizator leksykalny do innych aplikacji. Funkcja o której mowa to yylex(); 5
TWORZENIE PLIKU REGUŁ Każdy plik ze specyfikacją dla programu LEX powinien składać się z trzech sekcji; Pierwsza sekcja to sekcja definicji; W sekcji definicji umieszczamy, jak sama nazwa wskazuje, definicje i deklaracje zmiennych, stałych, deklaracje stanów oraz makra procesora; 6
TWORZENIE PLIKU REGUŁ Sekcja definicji może zawierać fragment kodu, który system przepisze bezpośrednio do analizatora leksykalnego; Kod ten musi być odpowiednio opakowany ; Otwarcie fragmentu bezpośrednio przepisywanego do analizatora powinno być poprzedzone znacznikiem %{, natomiast jej zamknięcie %}; 7
TWORZENIE PLIKU REGUŁ Przykład budowy sekcji definicji: %{ #include<stdio.h> int zmienna; int yylex(); int zmienna_druga=1; %} 8
TWORZENIE PLIKU REGUŁ Druga sekcja to sekcja przetwarzania; W sekcji przetwarzania umieszczamy wszelkie reguły postępowania, zgodnie z którymi wygenerowany będzie analizator; Reguły postępowania to inaczej przepisy na to co analizator ma zrobić gdy napotyka na określony problem (symbol); 9
TWORZENIE PLIKU REGUŁ Budowa reguły przetwarzania opiera się na dwóch zasadniczych częściach: wzorca i operacji; Jej budowa wygląda więc: wzorzec operacja Wzorzec jest zapisywany jako wyrażenie regularne; Operacja jest blokiem instrukcji języka C; 10
TWORZENIE PLIKU REGUŁ Przykład budowy reguły przetwarzania: (a+b)*a(a+b) 2 printf( W słowie A trzeci symbol od końca jest równy a ); 11
WYRAŻENIA REGULARNE 1. Symbole operacji logicznych: ab - konkatenacja; a b - alternatywa; a* - domknięcie zwrotne (domknięcie); a+ - domknięcie dodatnie (domknięcie po odjęciu słowa pustego); a? opcjonalność (symbol a nie występuje lub występuje jeden raz); 13
WYRAŻENIA REGULARNE 1. Powtarzanie symbolu: a{n} powtórzenie symbolu a n razy; a{n,m} zakres powtarzania symbolu (czyli a n,,a m ); () określają stopień ważności (c(a d) (e+) ); 14
WYRAŻENIA REGULARNE 1. Klasy znaków: [a-z] - oznacza dowolny znak z zakresu od małej litery a do małej litery z ; [^a-z] oznacza dowolny znak spoza klasy [a-z] (jak gdyby negacja zakresu); [a-zxy] oznacza dowolny znak z zakresu [a-z] lub wielką literę X lub Y; [0-9] oznacza dowolną cyfrę od 0 do 9;
WYRAŻENIA REGULARNE 1. Klasy znaków (c.d.):. - oznacza dowolny znak nie będący znakiem końca linii; \... poprzedza sekwencje specjalne (podobnie jak w C) np.: \n oznacza znak końca linii; \t oznacza znak tabulatora; 16
TWORZENIE PLIKU REGUŁ Przykład budowy reguły przetwarzania: (a+b)*a(a+b) 2 printf( W słowie A trzeci symbol od końca jest równy a ); Powinniśmy wpisać: (a b)*a(a b)*a(a b) b){2} printf( W słowie A trzeci symbol od końca jest równy a ); 17
Przykład Wyrażenie regularne akceptujące adres strony internetowej: [Ww]{3} \. [A-Za-z0-9._-]+ \. [A-Za-z]{3} \. [Pp] [Ll] 19
Przykład Wyrażenie regularne akceptujące słowa kluczowe w Adzie begin i end: [A-Za-z]{3,5} [A-Za-z]{5} [A-Za-z]{3} [Bb][Ee][Gg][Ii][Nn] [Ee][Nn][Dd] 20
Przykład Wyrażenie regularne akceptujące wszelkie identyfikatory (zmienne, stałe ) w C: [A-Za-z _ ] [A-Za-z0-9 _ ]* 21
Przykład Wyrażenie regularne akceptujące datę: ([0-9]{2} \- [0-9]{2} \- [0-9]{4}) ([0-9]{2} \. [0-9]{2} \. [0-9]{4}) ([0-9]{4} \- [0-9]{2} \- [0-9]{2}) ([0-9]{4} \. [0-9]{2} \. [0-9]{2}) ([0-9]{2} (\- \.) [0-9]{2} (\- \.) [0-9]{4}) 22 ([0-9]{4} (\- \.) [0-9]{2} (\- \.) [0-9]{2})
Przykład Wyrażenie regularne akceptujące wszelkie adresy poczty e-mail: [A-Za-z0-9. _ -]+@[A-Za-z0-9. _ ]+\.[A-Za-z]{2,4} 23
TWORZENIE PLIKU REGUŁ Trzecią sekcją składową pliku ze specyfikacją dla programu LEX jest sekcja podprogramów; W skład sekcji podprogramów mogą, jak sama nazwa wskazuje, wchodzić definicje funkcje, które będą następnie wykorzystywane przez analizator leksykalny; 24
TWORZENIE PLIKU REGUŁ Deklaracja funkcji wchodzących w skład tej sekcji poddana jest regułą tworzenia funkcji w języku C; Funkcja yywrap() jest to funkcja o specjalnym znaczeniu. Gdy wywołujemy (uprzednio wygenerowany) analizator leksykalny, funkcja yywrap() jest wykonywana zawsze po przetworzeniu danych wejściowych; 25
Przykład Przykład funkcji znajdującej się w sekcji podprogramów: int main() { return yylex(); } Wymaga ona oczywiście wcześniejszej deklaracji yylex a w postaci zwrotu: int yylex(); 26
TWORZENIE PLIKU REGUŁ Przy tworzeniu pliku reguł przetwarzania można korzystać z zmiennych globalnych; Deklaracja tego typu zmiennych znajduje się w pierwszej sekcji (sekcji definicji) pliku; Zmienne globalne, mogą być oczywiście wykorzystywane w każdej następnej sekcji, np. inkrementowane, zmieniane, czy odczytywane; 27
TWORZENIE PLIKU REGUŁ Oprócz zmiennych globalnych można także korzystać ze zmiennych wbudowanych; Mamy dwie szczególnie ważne zmienne wbudowane yyleng i yytext; Zmienna yyleng jest typu int i okresla długość dopasowania; Zmienna yytext jest znakiem (char) a dokładniej ciągiem znaków, wskazuje na leskem (odnajduje w strumieniu danych wejściowych, zapis który pasował do wzorca); 28
TWORZENIE PLIKU REGUŁ Pożyteczną rolę odgrywają także definicje regularne; Tworzenie definicji regularnej to inaczej przypisanie wyrażeniu regularnemu pewnego identyfikatora; Identyfikator ten może być wykorzystany później w polu wzorca; Definicja regularna jest tworzona w sekcji definicji; 29
Przykład Definicje regularną umieszczamy co prawda w sekcji definicji, jednakże po bloku bezpośrednio przepisywanym do analizatora; Zobaczmy następujący przykład, który pokazuje sposób korzystania z zadeklarowanego identyfikatora; 30
Przykład %{ #include<stdio.h> int yylex(); %} identyfikator [Ii] [Ff] %% {identyfikator} {printf( Wczytano leksem IF );} %% 31
TWORZENIE PLIKU REGUŁ Wszystkie trzy omówione sekcje składowe pliku ze specyfikacją dla programu LEX oddzielane są podwójnym znakiem procenta - %%; Schemat pliku ze specyfikacją reguł dla programu LEX możemy więc zademonstrować w tabeli: 32
TWORZENIE PLIKU REGUŁ SEKCJA DEFINICJI %% SEKCJA REGUŁ PRZETWARZANIA %% SEKCJA PODPROGRAMÓW...
NIEJEDNOZNACZNOŚĆ Gdy przeanalizowaliśmy już zasadę działania generatora LEX, możemy przyjrzeć się istotnemu problemowi niejednoznaczności przy działaniu LEX; Przykład: m* {cout<< * ;} mmm {cout<< + ;} Dostarczamy strumień danych postaci: mmmmmmmmmm;mmm 34
NIEJEDNOZNACZNOŚĆ ZASADA NAJDŁUŻSZEGO DOPASOWANIA określa, iż jeśli mamy dwie lub więcej reguł dla których wzorce są spełnione, to wybierana jest ta reguła dla której dopasowanie wzorca jest najdłuższe; ZASADA WCZEŚNIEJSZEGO DOPASOWANIA mówi iż, gdy dopasowania maja identyczną długość, wybrana jest reguła, która została umieszczona pierwsza w pliku specyfikacji; 35
NIEJEDNOZNACZNOŚĆ Stosując zatem zasadę pierwszą w przykładzie, dane wyjściowe będą wyglądały: *;+ Gdybyśmy zaś mieli dane wejściowe postaci mmm to stosując drugą zasadę dostaniemy na wyjściu + 36
RETRAKCJA Pojęcie retrakcji wiąże się ze sposobem działania analizatora; Analizator przetwarza wiele wzorców równolegle w poszukiwaniu najlepszego (tzn. najdłuższego). Porzuca on z czasem wzorce mniej obiecujące koncentrując się na wzorcach, które mogą dać dłuższe dopasowania; W sytuacji niepowodzenia wraca do porzuconych wzorców;... 37
LEWY KONTEKST Przetwarzając dane wejściowe może pojawic się potrzeba wykorzystania informacji o pewnym kontekście w ramach którego zachodzi dopasowanie; Możemy wyróżnic lewy kontekst (zwany inaczej stanem) i prawy kontekst; Aby móc korzystac ze stanu należy go najpierw zadeklarowa w sekcji definicji %s <nazwa_stanu> 38
LEWY KONTEKST Następnie aby zmienic stan w analizatorze należy wykonac instrukcję BEGIN <nazwa_stanu>; Różne implementacje programu LEX stosują różne oznaczenia stanu początkowego. <0> (np. w MKS LEX) <INITIAL> (np. w GNU FLEX) 39
LEWY KONTEKST %{ %} %s nazwa_stanu %% <nazwa_stanu>\ BEGIN <INITIAL>; <nazwa_stanu>. ; \ nazwa_stanu; BEGIN 40
PRAWY KONTEKST Prawy kontekst jest operatorem podglądu; Pozwala on na spojrzenie w przód strumienia danych wejściowych; [0-9]+. [0-9]*/[^0-9.] {puts( REAL );} 41
KONIEC KONIEC WYKŁADU TRZECIEGO