420 likes | 555 Views
L L G E N. Generator analizatorów składniowych. GENERATOR L LGEN. Zadaniem generatora LLgen jest wygenerowanie analizatora składniowego, inaczej parsera (w języku C) metodą zejść rekurencyjnych, bez nawrotów;
E N D
L L G E N Generator analizatorów składniowych
GENERATOR L LGEN • Zadaniem generatora LLgen jest wygenerowanie analizatora składniowego, inaczej parsera (w języku C) metodą zejść rekurencyjnych, bez nawrotów; • Kod źródłowy generowany jest przez LLgen’a w oparciu o plik zawierający specyfikację; • W specyfikacji można korzystać z rozszerzonych specyfikacji gramatyk LL(1). • Ponieważ LLgen zawiera wbudowane mechanizmy statycznego i dynamicznego rozstrzygania konfliktów, pozwala on na korzystanie z gramatyk niejednoznacznych; 2
GENERATOR LLGEN • Schemat organizacji działania LLgen’a: scan.l gram.g L E X LLgen scan.c gram.c Lpars.c Lpars.h G C C scane.exe plik.txt 3 WYNIK
GENERATOR LLGEN • flex –l scan.l (użycie generatora LEX) lex.yy.c • LLgen gram.g (uzycie generatora LLgen dla pliku specyfikacji gram.g) Lpars.c i Lparse.h • gcc lex.yy.c Lpars.c gram.c (kompilacja C++) • ./a.out < plik.in (analiza pliku) 4 4
GENERATOR LLGEN • Generator LLgen domyślnie korzysta z zewnętrznego analizatora leksykalnego, (wygenerowanego za pomocą LEX’a). W tym celu wywoływana jest funkcja yylex(); • Plik Lpars.h, który powstaje podczas pracy generatora LLgen, zawiera definicje przypisujące stałe liczbowe nazwom zadeklarowanych token-ów; 5
GNENRATOR LLGEN • Sposoby na wykorzystanie innego analizatora są następujące: • umieścić implementację skanera bezpośrednio w specyfikacji gramatyki (w bloku ’{’ ’}’ lub w zewnętrznym pliku; • W specyfikacji wskazać nazwę funkcji, którą Llgen ma wywołać; %lexical nazwa_funkcji; • w razie potrzeby włączyć do analizatora leksykalnego plikLpars.h; 6
GENERATOR LLGEN • LLgen jest narzędziem wierszowym, do którego plik specyfikacji przygotowujemy w zwykłym pliku tekstowym, czasem w kilku plikach; • Każdy wygenerowany kod źródłowy zawiera produkcje, dyrektywy generatora LLgen i deklaracje i kod w języku C 7
TWORZENIE SPECYFIKACJI • Każdy produkcja ze specyfikacji dla programu LLgen składa się z: nieterminalu, znaku „ : ” i prawej strony produkcji. Zakończona jest średnikiem; • Alternatywne prawe strony produkcji rozdzielane są znakiem „|”; • Prawa strona produkcji może składać się z terminali, nieterminali i akcji semantycznych; nieterminal : prawa strona produkcji ; 8
TWORZENIE SPECYFIKACJI • Reguły tworzenia specyfikacji: • białe spacje są ignorowane, jednak nie mogą występować w obrębie nazwy; • Komentarze wprowadzamy po znaku „/*” a zamykamy „*/”; • Komentarze nie mogą być zagnieżdżone; • Komentarze mogą wystąpić w każdym miejscu, gdzie dozwolone jest wystąpienie nazwy; 9
TWORZENIE SPECYFIKACJI • Reguły tworzenia specyfikacji c.d. : • Nazwy symboli terminalnych i nieterminalnych mogą być dowolnej długości. Maja one składnię taką, jak identyfikatory języka C; • Nazwy symboli nie mogą kolidować ze słowami kluczowymi języka C; • Wielkość liter w nazwie jest rozróżnialna; 10
TWORZENIE SPECYFIKACJI • Reguły tworzenia specyfikacji c.d. : • Nazwy symboli mogą być dowolnej długości, jednak w LLgen znaczących jest 50 pierwszych znaków; • Wszystkie nazwy generowane i wykorzystywane przez LLgen rozpoczynają się prefiksem LL; 11
DEKLARACJA TERMINALI • Terminale, które nie są literałami deklarujemy: %token ken; • Jeśli mamy kilka terminali do deklaracji możemy to zrobić tak: %token nazwa1, nazwa2, nazwa3; • Każde użycie terminala musi być poprzedzone jego deklaracją; 12
DEKLRACJE TERMINALI • Terminale, które są literałami są ujmowane w apostrofy; • LLgen rozpoznaje także (podobnie jak C) zestaw literałów specjalnych, tzn.: • nowa linia ‘\n’ • tabulator ‘\t’ • powrót karetki ‘\r’ • apostrof ‘\’’ • wycofanie znaku ‘\b’ • odwrotny ukośnik ‘\\’ • liczba oktalna ‘\xxx’ 13
DEKLRACJE TERMINALI ZAPAMIETAJ!!! Napotkana w pliku specyfikacji nazwa, która nie była zadeklarowana jako token, będzie traktowana przez LLgen jako symbol nieterminalny; 14
DEKLRACJE TERMINALI • Nieterminal jest implementowany jako funkcja języka C; • W LLgenie możemy korzystać ze zmiennych lokalnych funkcji. Generator pozwala je deklarować, w nawiasach klamrowych, jedynie po lewej stronie produkcji za symbolem nieterminalnym, np.: A {int zmienna;} : S ken T ; 15
DEKLRACJE TERMINALI • Przez akcję semantyczną rozumiemy dowolną pojedynczą instrukcję (grupę instrukcji) napisaną w języku C, które są ujęte w nawiasy klamrowe; • W LLgenie akcje semantyczne możemy wstawić jedynie po prawej stronie produkcji, np.: A {int zmienna} : S ken {licznik=1;} T ; 16
NIETERMINAL STARTOWY • Analizatory generowane przez LLgen mogą posiadać wiele nieterminali startowych; • Deklaracja nieterminalu startowego inaczej aksjomatu wygląda następująco: %start funkcja , nazwa_nieterminala; • np.: %start parse, S; 17
KOMPILACJA • Polecenie, które służy do uruchomienia generatora to LLgen. Polecenie to jest wywoływane dla pilku specyfikacji (rozszerzenie g), np.: LLgen gram.g • Generator Llgen na wyjściu produkuje trzy pliki: • gram.c – plik w C zwierający implementację parsera; • Lpars.h – plik zawierający interfejs analizatora leksykalnego; • Lpars.c – szkielet parsera i tablica sterująca; 18
OPCJA -V • Czasem warto jest przy uruchamianiu i testowaniu parsera korzystać z opcji –v; • Dzięki opcji –v wygenerowany zostanie plik LL.output, który będzie zwierał informacje o nierozwiązanych konfliktach, które pojawiły się w gramatyce; 19
ROZSZERZENIE SKŁADNI GRAMATRYK • Rozszerzenia standardowej składni gramatyk bezkontekstowych: • * (*liczba) – domknięcie zwrotne • + (+liczba) – domknięcie dodatnie ; • ? – operator opcjonalności; • [...] – możliwość grupowania symboli; 20
Przykład • Niech ={a,b}. Rozważmy język regularny L=L(b*a). Wówczas: S : B A ; B : ‘b’ * ; A : ‘a’ ; S : B A ; B : | ‘b’ B ; A : ‘a’ ; 21
Przykład • Niech ={a,b}. Rozważmy język L={b, ab, aab, aaab}. Wówczas: S : A B ; A : | ‘a’ C ; C : | ‘a’ ; B : ‘b’ ; S : A B ; A : ‘a’ *3 ; B : ‘b’ ; 22
Przykład • Niech ={a,b}. Rozważmy język L={ab, aab, aaab}. Wówczas: S : A B ; A : ‘a’ C ; C : | ‘a’ ; B : ‘b’ ; S : A B ; A : ‘a’ +3 ; B : ‘b’ ; 23
Przykład • Niech ={a,b}. Rozważmy język L={b, ab}. Wówczas: S : A B ; A : | ‘a’ ; B : ‘b’ ; S : A B ; A : ‘a’ ? ; B : ‘b’ ; 24
Przykład • Niech ={a,b}. Rozważmy język L={A * : |A|=2}. Wówczas: S : ‘a’ B | ‘b’ B ; B : ‘a’ | ‘b’ ; S : [ ‘a’ | ‘b’ ] +2 ; 25
PORÓWNANIE • Rozważmy gramatykę, która nie jest gramatyką LL(1). Porównajmy pracochłonność procedury dostosowania gramatyki a bezpośrednią implemantacją gramatyki w generatorze LLgen’; • Niech =[a,b}. Napiszmy program akceptujący język bezkontekstowy L={A* : A=an bn ; n }; 26
PORÓWNANIE { int ilosc_a, ilosc_b; } %start parse , S; S : A B { if (ilosc_a= = ilosc_b) puts(’’OK.’’); else puts(’’Blad’’); } ; 27
PORÓWNANIE • Usuwamy lewostronną rekurencję; A : ’a’ { ilosc_a=1; } | A ’a’ { ilosc_a++; } ; B : ‘b’ { ilosc_b=1; } | B ‘b’ { ilosc_b++; } ; A : ’a’ { ilosc_a=1; } | ’a’ A { ilosc_a++; } ; B : ‘b’ { ilosc_b=1; } | ‘b’ B { ilosc_b++; } ; 28
PORÓWNANIE A : ’a’ C { ilosc_a++; } ; C : { ilosc_a=0; } | ’a’ C { ilosc_a++; } ; B : ’b’ D { ilosc_b++; } ; D : { ilosc_b=0; } | ’b’ D { ilosc_b++; } ; 29
PORÓWNANIE S : {ilosc_a=ilosc_b=0} A B { if (ilosc_a= = ilosc_b) puts(’’OK.’’); else puts(’’Blad’’); } ; A : [ ’a’ {ilosc_a++} ] + ; B : [ ’b’ {ilosc_b++} ] + ; 30
LLSYMB • LLsymb jest globalną zmienną całkowitą, która może przyjmować różne wartości. To jaka wartość będzie przyjęta, zależy od położenia głowicy czytającej po prawej stronie produkcji: Możliwe wartości: • Jeśli przeczytany został token, to LLsymb przechowuje token; • Po grupowaniu i alternatywie w zmiennej znajduje się podglądany token; 31
TWORZENIE SPECYFIKACJI • W pliku ze specyfikacją do generatora LLgen obowiązkowo powinna znaleźć się implementacja funkcji main; %start parse, S ; int main(){ parse(); return 0; } 32
TWORZENIE SPECYFIKACJI • W pliku ze specyfikacją do generatora LLgen powinna także znaleźć się implementacja funkcji LLmessage; • Funkcja ta jest automatycznie wywoływana przez parser, gdy wystąpi bład składniowy; void LLmessage ( int tk ); Nie zwraca żadnej wartości Ma jeden parametr typu całkowitego 33
TWORZENIE SPECYFIKACJI • Zmienna tk przyjmuje następujące wartości: • gdy oczekiwany był token „tk” – tk > 0; • gdy wczytany został nieoczekiwany token i został on usunięty – tk = 0; • gdy nie został napotkany oczekiwany koniec pliku i pozostałe wejście będzie pominięte – tk = - 1; 34
Przykład • Działanie generatora LLgen najlepiej zobaczyć na przykładzie. Na wejściu znajduje się ciąg słów złożonych z alfabetu naturalnego, słowa kończą się znakiem dwukropka i są rozdzielane przecinkiem. Dany na wejście ciąg zawiera co najmniej jedno słowo.... 35
KONFLIKTY • W trakcie pracy generatora składniowego może dojść do konfliktu polegającego na: • nie jesteśmy w stanie określić, którą z prawych stron należy rozwijać – konflikt alternatyw; • Konstrukcja która jest aktualnie przetwarzana zawiera domkniecie i trudno określić, czy wejście jest jej dalszym ciągiem, czy też rozpoczyna inną konstrukcję – konflikt powtórzeń; 36
KONFLIKTY • Konflikt alternatyw można rozstrzygnąć na dwa sposoby: • dynamiczne rozstrzyganie konfliktu alternatywy: %if (warunek) • statyczne rozstrzyganie konfliktu alternatyw: %prefer %if(1) %aviod %if(0) 37
Przykład • Rozważmy zadanie badania parzystości liczby binarnej; • Analizator leksykalny rozpoznaje i zwraca liczby binarne; %% [01] { return yytext[0]; } 38
Przykład { int wczytcyfra; } %start parse, S; S : ’0’ { wczytcyfra = 0; } R | ’1’ { wczytcyfra = 1; } R ; R : %if (wczytcyfra ==0 ) {puts(”parzysta”);} | {puts(”nieparzysta”);} | S ; 39
ROZWIĄZYWANIE KONFLIKTÓW • Przykład użycia mechanizmu statycznego rozwiązywania konfliktów alternatyw jest problemem tzw. „wiszącego else”; • Problem ten omówimy szczegółowo na wykładzie poświęconym generatorowi YACC; 40
ROZWIĄZYWANIE KONFLIKTÓW • Konflikt powtórzeń jest rozstrzygany przy pomocy słowa kluczowego %while; %while ( warunek ) • W ewaluacji warunku bardzo przydatne może być makro języka C, które jest generowane przez Llgen na podstawie słowa kluczowego %first; %first fmac, nonterm ; 41
KONIEC KONIEC WYKŁADU SZÓSTEGO