0.1 Lewostronna rekurencja Sprawdź czy poniższa gramatyka E jest zgodna z LL(1), tzn. czy umożliwia przeprowadzenie analizy bez powrotu z wyprzedzeniem o jeden symbol. Wyjaśnienie pojęcia LL(1): Pierwsze L w LL(1) oznacza przeglądanie wejścia od lewej do prawej. Drugie L oznacza lewostronne wyprowadzenie (ang. leftmost), 1 oznacza, że do podejmowania decyzji o wyborze produkcji można wczytać co najwyżej jeden symbol terminalny. E ::= EOT T O ::= + - T ::= TMF F M ::= F ::= (E) id Zbiór FIRST dla dowolnego ciągu symboli gramatyki X jest zbiorem terminali, od których zaczynają się ciągi wyprowadzane z X; jeżeli z X można wyprowadzić ɛ, to ɛ jest także z FIRST(X). FIRST - Algorytm: 1. Jeśli X jest terminalem, to FIRST(X) = {X}. 2. Jeśli mamy produkcję ɛ dla X, to dodajemy ɛ do FIRST(X). 3. Jeśli X jest nieterminalem, wówczas dla wszystkich produkcji postaci: X Y 1, Y 2, Y 3,... należy wykonać następujący algorytm: (a) dodaj do FIRST(X) zbiór FIRST(Y 1 ). (b) jeśli FIRST(Y 1 ) zawiera ɛ, FIRST(Y 2 ). (c) jeśli FIRST(Y 2 ) zawiera ɛ... itd. wówczas dodaj do FIRST(X) zbiór FIRST(E) = FIRST(E) FIRST(T) = FIRST(T) = { (, id } FIRST(O) = {+, } FIRST(T) = FIRST(F) = { (, id } FIRST(M) = { } FIRST(F) = { (, id } 1
FOLLOW - Algorytm: 1. Dla symbolu startowego S w FOLLOW(S) umieszczamy (jeśli S nie pojawia się w innych produkcjach). 2. dla każdej produkcji A αbβ, gdy FIRST(β) nie zawiera ɛ, wszystkie symbole z FIRST(β) umieszczamy w FOLLOW(B). 3. Jeżeli FIRST(β) zawiera ɛ, lub występuje produkcja A αb, wówczas do FOLLOW(B) dodajemy FOLLOW(A). FOLLOW(E) = FIRST(O) { ) } = {+,, )} FOLLOW(O) = FIRST(T) = FIRST(F) = { (, id } FOLLOW(T) = FIRST(M) FOLLOW(E) = {, +,, )} FOLLOW(M) = FIRST(F) = { (, id } FOLLOW(F) = FOLLOW(T) = FIRST(M) = { } Sprawdzenie I reguły gramatycznej, tzn. czy zbiory symboli pierwszych dla każdej produkcji z alternatywą po obu jej stronach są zbiorami rozłącznymi: FIRST(X) FIRST(Y) =. E: FIRST(E) FIRST(T) = FIRST(T) = {(, id} reguła niespełniona. O: {+} { } = reguła spełniona. T: FIRST(T) FIRST(F) = FIRST(F) = {(, id} reguła niespełniona. O: Brak alternatywy - reguła spełniona. F: { ( } {id} = reguła spełniona. II reguła spełniona bowiem żadna produkcja nie generuje znaku pustego ɛ. Produkcje: E, T - eliminacja lewostronnej rekurencji poprzez zastosowanie prawostronnej rekurencji. 2
Lewostronna rekurencja oraz sposoby jej eliminacji: Gramatyka jest lewostronnie rekurencyjna, jeśli ma terminal A taki, że istnieje wyprowadzenie A ::= Aα dla napisu α. Powoduje to następujący problem: jeżeli analizujemy tekst of lewej strony nigdy nie dotrzemy do α zatrzymując się za każdy razem na A. 1. W przypadku terminala bezpośrednio rekurencyjnego zastępujemy produkcję o postaci A ::= Aα β następującymi produkcjami: A ::= βa A ::= αa ɛ 2. W przypadku terminala pośrednio lewostronnie rekurencyjnego S ::= Aa b A ::= Ac Sd ɛ należy zastosować rozwinięcie wszystkich produkcji tak, by każda z nich zawierała: po prawej stronie wyłącznie terminale, nieterminale w pozycjach prawostronnie rekurencyjnych lub nieterminal znajdujący się po lewej stronie w pozycji lewostronnie rekurencyjnej, a następnie wykonaniu eliminacji bezpośredniej lewostronnej rekurencji dla wszystkich produkcji. Poprawiona gramatyka: E ::= TE E ::= OTE ɛ O ::= + - T ::= FT T ::= MFT ɛ M ::= 3
F ::= (E) id Ponowne sprawdzenie reguł gramatycznych dla poprawionej gramatyki. I reguła gramatyczna (sprawdzamy tylko poprawione produkcje): E: brak alternatywy - reguła spełniona. E : FIRST(O) ɛ = {+, } ɛ = - reguła spełniona. T: brak alternatywy - reguła spełniona. T : FIRST(M) ɛ = { } ɛ = - reguła spełniona. II reguła gramatyczna (sprawdzamy tylko produkcje generujące : A, T ): Dla każdego symbolu nieterminalnego A sprawdzamy czy zbiory symboli pierwszych i następnych są zbiorami rozłącznymi: FIRST(A) FOLLOW(A) =. A : FIRST(E ) = FIRST(O) ɛ = {+, } ɛ = {+,, ɛ} FOLLOW(E ) = FOLLOW(E) = FIRST(E ) FOLLOW(E ) = {+,, ɛ} = - reguła spełniona. T : FIRST(T ) = FIRST(M) ɛ = { } ɛ = {, ɛ} FOLLOW(T ) = FOLLOW(T) = FIRST(E ) = FIRST(O) ɛ = {+,, ɛ} FIRST(T ) FOLLOW(T ) = {, ɛ} {+,, ɛ} = ɛ - reguła spełniona, bowiem występowanie ɛ jest równoznaczne brakowi symbolu. Wyprowadzenie napisu id id z poprawionej gramatyki E: E T E F T E idt E idmf T E id F T E id idt E id idɛe id idɛɛ id id. 0.2 Lewostronna faktoryzacja Proces faktoryzacji gramatyki polega na tym, że kiedy nie jest jasne, którą z produkcji wybrać do rozwinięcia nieterminala w drzewie składniowym, należy przepisać tę produkcję, a decyzję o ich wyborze odłożyć do chwili aż przeczytanych zostanie więcej znaków wejściowych. Produkcja α α ::= αβ αβδ 4
zostanie zamieniona na równoważne następujące za pomocą lewostronnej faktoryzacji. Lewostronna faktoryzacja: Należy zastąpić produkcję A ::= αξ 1 αξ 2 αξ 3... αξ n produkcjami: A ::= αa A ::= ξ 1 ξ 2... ξ n. α ::= αβφ φ ::= δ ɛ lub Lewostronna faktoryzacja: Należy zastąpić produkcję A ::= α αξ produkcjami: A ::= αa A ::= ξ ɛ Weźmy następujące dwie produkcje: instr if wyr then instr else instr instr if wyr then instr Jeśli na wejściu widzimy if, wówczas nie wiemy, którą produkcję wybrać. Należy wtedy wyciągnąć pierwszą - wspólną dla obu produkcji część przed nawias, otrzymując następujące produkcje: instr if wyr then instr cześć-else część-else else instr ɛ. 5