340 likes | 486 Views
Y A C C. Generator analizatorów składniowych. GENERATOR YACC. Zadaniem generatora YACC jest wygenerowanie kodu źródłowego analizatora składniowego (domyślnie) w języku C; Pożyteczną rzeczą jest fakt, iż generator Yacc doskonale współpracuje z generatorem analizatorów leksykalnych Lex.
E N D
Y A C C Generator analizatorów składniowych
GENERATOR YACC • Zadaniem generatora YACC jest wygenerowanie kodu źródłowego analizatora składniowego (domyślnie) w języku C; • Pożyteczną rzeczą jest fakt, iż generator Yacc doskonale współpracuje z generatorem analizatorów leksykalnych Lex. • Kod źródłowy generowany jest przez Yacc’a w oparciu o plik ze specyfikacją; • Plik z regułami tworzony jest przez samego użytkownika; 2
GENERATOR YACC • Schemat organizacji działania Yacc’a: scan.l gram.y L E X YACC scan.c y.tab.c y.tab.h G C C scane.exe plik.txt 3 WYNIK
GENERATOR YACC • Plik y.tab.c jest reprezentacją analizatora LALR napisaną w C, razem z innymi procedurami w języku C, które użytkownik mógł przygotować; • Opcja kompilacji "-d" powoduje zapisanie pliku nagłówkowego y.tab.h, w którym będą znajdować się definicje tokenów języka; 4
GENERATOR YACC • flex –l scan.l (użycie generatora LEX) lex.yy.c • yacc -d gram.y (uzycie generatora Yacc dla pliku specyfikacji gram.y) y.tab.c i y.tab.h • gcc y.tab.c lex.yy.c (kompilacja C++) • ./a.out < plik.in (analiza pliku) 5 5
GENERATOR YACC • 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 składniowy do innych aplikacji. Funkcja o której mowa to yyparse(); 6
GENERATOR YACC • Zasady działania programu: • Wygenerowany przez program YACC analizator redukujący działa w oparciu o tablicę LALR; • Jako symbol startowy gramatyki, przyjmowany jest, przez domniemanie, nieterminal znajdujący się po lewej stronie pierwszej produkcji; 7
GENERATOR YACC • Wygenerowany parser ma postać funkcji int yyparse(). Aby uruchomić parser zawarty w tej funkcji potrzebujemy dwóch innych funkcji: main() i yylex(); • Funkcja main(), która wywołuje funkcję yyparse(); • Parser wywołuje lexer (yylex()), kiedy tylko pobiera token z wejścia; • Yacc definiuje nazwy tokenów w parserze jako nazwy preprocesora C w y.tab.h, więc lexer może je użyć. 8
GENERATOR YACC • Gdy lexer znajdzie token, który interesuje parser, zwraca do parsera jego numeryczną wartość, umieszczając ja w zmiennej yyval; • Funkcja yylex może być umieszczona w osobnym module (np. generowana programem lex); • Jeżeli korzystamy z zewnętrznego skanera, przy kompilacji dokładamy opcję "-d„ (y.tab.h, w którym będą zawarte definicje tokenów języka); 9
TWORZENIE PLIKU SPECYFIKACJI • Każdy plik ze specyfikacją dla programu Yacc 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; 10
TWORZENIE PLIKU SPECYFIKACJI • Sekcja definicji może zawierać fragment kodu, który system przepisze bezpośrednio do analizatora składniowego; • Kod ten musi być odpowiednio „opakowany”; • Otwarcie fragmentu bezpośrednio przepisywanego do analizatora powinno być poprzedzone znacznikiem %{, natomiast jej zamknięcie %}; 11
TWORZENIE PLIKU SPECYFIKACJI • Przykład budowy sekcji definicji: %{ #include<iostream.h> int zmienna; int zmienna_druga=1; %} 12
TWORZENIE PLIKU SPECYFIKACJI • 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 dokona redukcji zgodnie ze związaną z tą operacją produkcją; 13
TWORZENIE PLIKU REGUŁ • Budowa reguły przetwarzania opiera się na dwóch zasadniczych częściach: produkcji i operacji; • Jej budowa wygląda więc: produkcja operacja • Produkcja jest zapisana w notacji programu Yacc. Strzałka w produkcji jest zastąpiona znakiem „ : ”; • Operacja jest blokiem instrukcji języka C; 14
TWORZENIE PLIKU SPECYFIKACJI • Jeśli w gramatyce mamy produkcję S->T+T, to przykładowa reguła produkcji może wyglądać nastepująco: S : T ‘+’ T {printf(”liczba + liczba”);} ; • Podobnie jak w przypadku LLgen terminale, które nie są literałami (są nazwane) muszą być zadeklarowane za pomocą słowa kluczowego %token; 15
NOTACJA YACC’a • Rozważmy gramatykę, w której zbiór produkcji jest następujący: E -> E + T; E -> T; T -> T * F; T -> F; F -> ( E ); F -> num; 16
ANALIZA ZSTĘPUJĄCA Sprawdźmy, w jaki sposób można te produkcje przedstawić w notacji Yacc’a? %token num %% E : E ‘+’ T | T ; T : T ‘*’ F | F ; F : ‘(‘ E ’)’ | num ; 17
NOTACJA YACC’a • Niech G będzie gramatyką z produkcjami T->(T) i T->. Wtedy pustą produkcję możemy zapisać w notacji Yacc następująco: %% T : ‘(‘ T ‘)’ | ; 18
AKCJE SEMANTYCZNE • Akcja semantyczna Yacc’a jest sekwencją instrukcji w C; • Symbol $$ odwołuje się do wartości atrybutu skojarzonej z nieterminalem po lewej stronie; • Symbol $i odwołuje się do wartości skojarzonej z i-tym symbolem gramatyki po prawej stronie; • Akcja semantyczna wywoływana jest zawsze, gdy redukujemy według związanej z nią produkcji; 19
AKCJE SEMANTYCZNE • Rozważmy dwie produkcje: E->E+T | T; wyr : wyr ‘+’ term {$$ = $1 +$3;} | term ; 20
y.tab.h %token liczba %% E : E ‘+’ T | T ; T : T ‘-’ F | F ; F : ‘(‘ E ‘)’ | liczba ; #define liczba 257 %{ #include „y.tab.h” %} %% [+-()] return yytext[0]; [0-9]+ return liczba; [ \t] ; . Yy_fatal(”???”); 21+18 num+num wejście 21
WSPÓŁPRACA Z LEX Aby powiązać Yacc z Lex’em musimy stworzyć analizator leksykalny, który będzie zwracał kolejny odnaleziony symbol; %{ #include ”y.tab.h” %} %% [+-() ] return yytext[0]; [0-9]+ return liczba; [ \t] ; . YY_FATAL(”???”); 22
Współpraca z LEX Teraz możemy dopisać analizator składniowy: %token liczba %% S : S ’+’ T | T ; T : T ’-’ W | W ; W : ’(‘ S ’)’ | liczba ; 23
%token liczba %% S : S ‘+’ T | T ; T : T ‘-’ W | W ; W : ‘(‘ S ‘)’ | liczba ; %{ #include ”y.tab.h” %} %% [+ - ()] return yytext[0]; [0-9]+ return liczba; [ \t] ; . YY_FATAL(”???”); 14+21+ -4 OK BŁĄD 24
%token liczba %% S : S ‘+’ T | T ; T : T ‘-’ W | W ; W : ‘(‘ S ‘)’ | liczba ; %{ #include „y.tab.h” %} %% [+ - ()] return yytext[0]; [0-9]+ return liczba; [ \t] ; . Yy_fatal(”???”); 14*4 BŁĄD 25
%token liczba %% S : S ‘+’ T | T ; T : T ‘*’ W | W ; W : ‘(‘ S ‘)’ | liczba ; %{ #include „y.tab.h” %} %% [+*()] return yytext[0]; [0-9]+ return liczba; [ \t] ; . Yy_fatal(”???”); 14+21+4 OK OK 26
Współpraca z LEX Teraz możemy dopisać produkcję akceptującą: %token liczba %% S : S ’+’ T | T ; T : T ’-’ W | W ; Q : S {puts(”OK”);} ; W : ’(‘ S ’)’ | liczba ; 27
PLIK SPECYFIKACJI • Generator Yacc pozwala na korzystanie ze zmiennych; • Oczywiście zmienna z której chcemy skorzystać, musi być uprzednio zadeklarowana w sekcji deklaracji; • Następnie może być przetwarzana, inkrementowana i zmieniana w sekcji reguł; • Sposób deklaracji: typ_zmiennej nazwa_zmiennej=wartość; 28
PLIK SPECYFIKACJI • Oprócz zmiennych Yacc pozwala także na używanie funkcji; • Każda taka funkcja może być używana w regule przetwarzania; • Należy jednak pamiętać o zdefiniowaniu funkcji. Definicja ciała funkcji znajduje się w trzeciej części pliku specyfikacji – części definicji; • Funkcję definiuje się według standardów języka C; 29
Przykład • Załóżmy, że mamy za zadanie napisać analizator dla języka, który składać się powinien, z tej samej liczby nawiasów otwierających co zamykających. Dodatkową trudnością niech będzie konieczność podania przez analizator liczby par nawiasów (otwierających i zamykających); • Produkcje gramatyki łatwo wyznaczyć: S -> ( S ), S ->;... 30
SPECYFIKACJA YACC • W pliku specyfikacji dla generatora Yacc możemy również korzystać z nowych symboli leksykalnych; • Deklarację symbolu leksykalnego poprzedza słowo kluczowe %token np.. %token nazwa_symbolu; • Deklarację tę należy umieścić w sekcji pierwszej pliku specyfikacji;... 31
SPECYFIKACJA YACC • W pliku specyfikacji dla generatora Yacc możemy również korzystać z wbudowanego w program Yacc nieterminala error; • Wbudowany nieterminal error jest bardzo przydatny przy wykrywaniu, eliminacji błędu; • Można go także zastosować przy powrocie po błędzie; 32
Przykład • Zmodyfikujmy nieco rozważaną poprzednio gramatykę dla języka składającego się z jednakowej liczby nawiasów, i rozważmy gramatykę dla języka którym jest niepusty ciąg liczb w nawiasach; • Produkcjie tej gramatyki to: C ->(L); L->Liczba; L->L , Liczba...
KONIEC KONIEC WYKŁADU ÓSMEGO