Parser zstępujący — problemy
Transkrypt
Parser zstępujący — problemy
Wykład 8, str. 1 Parser zstępujący — problemy Lewostronna rekursja w gramatyce powoduje nieskończone obliczenie Wykład 8, str. 1 Parser zstępujący — problemy Lewostronna rekursja w gramatyce powoduje nieskończone obliczenie Przykład: inna gramatyka — MhWi ::= hSi hWi+hSi Wykład 8, str. 1 Parser zstępujący — problemy Lewostronna rekursja w gramatyce powoduje nieskończone obliczenie Przykład: inna gramatyka — MhWi ::= hSi hWi+hSi void fW (Boolean* ok, drzewo* t) { drzewo t1 , t2 ; fS (ok, &t1 ); if (*ok) *t = &(drzewo zrobione z t1 ); else { fW (ok, &t1 ); if (*ok) { if (wejście == ’+’) { nast leks(); fS (ok, &t2 ); if (*ok) *t = &(drzewo zrobione z t1 , +, t2 ); else blad(); } else *ok = FALSE; } } } Wykład 8, str. 1 Parser zstępujący — problemy Lewostronna rekursja w gramatyce powoduje nieskończone obliczenie Przykład: inna gramatyka — MhWi ::= hSi hWi+hSi void fW (Boolean* ok, drzewo* t) { drzewo t1 , t2 ; fS (ok, &t1 ); if (*ok) *t = &(drzewo zrobione z t1 ); else { fW (ok, &t1 ); if (*ok) { if (wejście == ’+’) { nast leks(); fS (ok, &t2 ); if (*ok) *t = &(drzewo zrobione z t1 , +, t2 ); else blad(); } else *ok = FALSE; } } } Wykład 8, str. 2 Leczenie lewostronnej rekursji Problem: produkcje gramatyczne postaci hNi ::= . . . hNi . . . . . . (czyli lewostronnie rekursywne) prowadzą do zapętlenia parsera zstępującego. Wykład 8, str. 2 Leczenie lewostronnej rekursji Problem: produkcje gramatyczne postaci hNi ::= . . . hNi . . . . . . (czyli lewostronnie rekursywne) prowadzą do zapętlenia parsera zstępującego. Takie produkcje potrzebne są dla często spotykanej w składni języków programowania konstrukcji „ciąg . . . grupowany do lewej”. Wykład 8, str. 2 Leczenie lewostronnej rekursji Problem: produkcje gramatyczne postaci hNi ::= . . . hNi . . . . . . (czyli lewostronnie rekursywne) prowadzą do zapętlenia parsera zstępującego. Takie produkcje potrzebne są dla często spotykanej w składni języków programowania konstrukcji „ciąg . . . grupowany do lewej”. Przykład: M hliczbai ::= hcyfrai hcyfrai hliczbai — ciąg cyfr grupowany do prawej (nie ma problemu) 345 = 3x45y = 3 · 102 + 45 Wykład 8, str. 2 Leczenie lewostronnej rekursji Problem: produkcje gramatyczne postaci hNi ::= . . . hNi . . . . . . (czyli lewostronnie rekursywne) prowadzą do zapętlenia parsera zstępującego. Takie produkcje potrzebne są dla często spotykanej w składni języków programowania konstrukcji „ciąg . . . grupowany do lewej”. Przykład: M hliczbai ::= hcyfrai hcyfrai hliczbai — ciąg cyfr grupowany do prawej (nie ma problemu) 345 = 3x45y = 3 · 102 + 45 hliczbai ::= hcyfrai hliczbai hcyfrai — ciąg cyfr grupowany do lewej (jest problem) 345 = x34y5 = 34 · 10 + 5 Wykład 8, str. 3 Leczenie lewostronnej rekursji Wyjście: do gramatyk wprowadzić specjalną konstrukcję oznaczającą „ciąg . . . grupowany do lewej”: Wykład 8, str. 3 Leczenie lewostronnej rekursji Wyjście: do gramatyk wprowadzić specjalną konstrukcję oznaczającą „ciąg . . . grupowany do lewej”: Przykład: M Żeby wyrazić „liczba to niepusty ciąg cyfr grupowany do lewej” — zamiast hliczbai ::= piszemy hcyfrai hliczbai hcyfrai hliczbai ::= hcyfrai hcyfrai∗ Wykład 8, str. 3 Leczenie lewostronnej rekursji Wyjście: do gramatyk wprowadzić specjalną konstrukcję oznaczającą „ciąg . . . grupowany do lewej”: Przykład: M Żeby wyrazić „liczba to niepusty ciąg cyfr grupowany do lewej” — zamiast hliczbai ::= piszemy hcyfrai hliczbai hcyfrai hliczbai ::= hcyfrai hcyfrai∗ liczba - cyfra - Wykład 8, str. 4 Leczenie lewostronnej rekursji Wyjście: do gramatyk wprowadzić specjalną konstrukcję oznaczającą „ciąg . . . grupowany do lewej”: Przykład: M Żeby wyrazić „wyrażenie to niepusty ciąg składników porozdzielanych plusami i minusami grupowany do lewej” — zamiast piszemy hwyrażeniei + hskładniki hwyrażeniei ::= hskładniki hwyrażeniei − hskładniki nn o o hwyrażeniei ::= hskładniki + − hskładniki ∗ Wykład 8, str. 4 Leczenie lewostronnej rekursji Wyjście: do gramatyk wprowadzić specjalną konstrukcję oznaczającą „ciąg . . . grupowany do lewej”: Przykład: M Żeby wyrazić „wyrażenie to niepusty ciąg składników porozdzielanych plusami i minusami grupowany do lewej” — zamiast piszemy hwyrażeniei + hskładniki hwyrażeniei ::= hskładniki hwyrażeniei − hskładniki nn o o hwyrażeniei ::= hskładniki + − hskładniki ∗ wyrażenie - składnik + − - Wykład 8, str. 5 Leczenie lewostronnej rekursji Gramatyka oryginalna z lewostronną rekursją: hWi ::= hSi ::= hCi ::= hLi ::= hSi hWi + hSi hWi hWi - hSi hCi hSi * hCi hCi hSi / hCi hLi ( hWi ) 0 1 hLi 0 hLi 1 Gramatyka wyleczona z iteracją: hWi ::= hSi nn o o + − hSi ∗ hSi ::= nn o o hCi ∗ / hCi ∗ hCi ::= ( hWi ) hLi n on o 0 1 0 1 ∗ hLi ::= Wykład 8, str. 5 Leczenie lewostronnej rekursji Gramatyka oryginalna z lewostronną rekursją: hWi ::= hSi ::= hCi ::= hLi ::= hSi hWi + hSi hWi hWi - hSi hCi hSi * hCi hCi hSi / hCi hLi ( hWi ) 0 1 hLi 0 hLi 1 Gramatyka wyleczona z iteracją: hWi ::= hSi nn o o + − hSi ∗ hSi ::= nn o o hCi ∗ / hCi ∗ hCi ::= hLi ( hWi ) n on o 0 1 0 1 ∗ hLi ::= Wykład 8, str. 5 Leczenie lewostronnej rekursji Gramatyka oryginalna z lewostronną rekursją: hWi ::= hSi ::= hCi ::= hLi ::= hSi hWi + hSi hWi hWi - hSi hCi hSi * hCi hCi hSi / hCi hLi ( hWi ) 0 1 hLi 0 hLi 1 Gramatyka wyleczona z iteracją: hWi ::= hSi nn o o + − hSi ∗ hSi ::= nn o o hCi ∗ / hCi ∗ hCi ::= hLi ( hWi ) n on o 0 1 0 1 ∗ hLi ::= Wykład 8, str. 5 Leczenie lewostronnej rekursji Gramatyka oryginalna z lewostronną rekursją: hWi ::= hSi ::= hCi ::= hLi ::= hSi hWi + hSi hWi hWi - hSi hCi hSi * hCi hCi hSi / hCi hLi ( hWi ) 0 1 hLi 0 hLi 1 Gramatyka wyleczona z iteracją: hWi ::= hSi nn o o + − hSi ∗ hSi ::= o nn o hCi ∗ / hCi ∗ hCi ::= hLi ( hWi ) n on o 0 1 0 1 ∗ hLi ::= Wykład 8, str. 5 Leczenie lewostronnej rekursji Gramatyka oryginalna z lewostronną rekursją: hWi ::= hSi ::= hCi ::= hLi ::= hSi hWi + hSi hWi hWi - hSi hCi hSi * hCi hCi hSi / hCi hLi ( hWi ) 0 1 hLi 0 hLi 1 Gramatyka wyleczona z iteracją: hWi ::= hSi nn o o + − hSi ∗ hSi ::= o nn o hCi ∗ / hCi ∗ hCi ::= hLi ( hWi ) n on o 0 1 0 1 ∗ hLi ::= Wykład 8, str. 5 Leczenie lewostronnej rekursji Gramatyka oryginalna z lewostronną rekursją: hWi ::= hSi ::= hCi ::= hLi ::= hSi hWi + hSi hWi hWi - hSi hCi hSi * hCi hCi hSi / hCi hLi ( hWi ) 0 1 hLi 0 hLi 1 Gramatyka wyleczona z iteracją: hWi ::= hSi nn o o + − hSi ∗ hSi ::= hCi nn o o ∗ / hCi ∗ hCi ::= hLi ::= hLi ( hWi ) n on o 0 1 0 1 ∗ Wykład 8, str. 5 Leczenie lewostronnej rekursji Gramatyka oryginalna z lewostronną rekursją: hWi ::= hSi ::= hCi ::= hLi ::= hSi hWi + hSi hWi hWi - hSi hCi hSi * hCi hCi hSi / hCi hLi ( hWi ) 0 1 hLi 0 hLi 1 Gramatyka wyleczona z iteracją: hWi ::= hSi nn o o + − hSi ∗ hSi ::= hCi nn o o ∗ / hCi ∗ hCi ::= hLi ::= ( hWi ) hLi n on o 0 1 0 1 ∗ Wykład 8, str. 5 Leczenie lewostronnej rekursji Gramatyka oryginalna z lewostronną rekursją: hWi ::= hSi ::= hCi ::= hLi ::= hSi hWi + hSi hWi hWi - hSi hCi hSi * hCi hCi hSi / hCi hLi ( hWi ) 0 1 hLi 0 hLi 1 Gramatyka wyleczona z iteracją: hWi ::= hSi nn o o + − hSi ∗ hSi ::= hCi nn o o ∗ / hCi ∗ hCi ::= hLi ::= ( hWi ) hLi n on o 0 1 0 1 ∗ Wykład 8, str. 5 Leczenie lewostronnej rekursji Gramatyka oryginalna z lewostronną rekursją: hWi ::= hSi ::= hCi ::= hLi ::= hSi hWi + hSi hWi hWi - hSi hCi hSi * hCi hCi hSi / hCi hLi ( hWi ) 0 1 hLi 0 hLi 1 Gramatyka wyleczona z iteracją: hWi ::= hSi nn o o + − hSi ∗ hSi ::= hCi nn o o ∗ / hCi ∗ hCi ::= hLi ::= ( hWi ) hLi n o n o 0 1 0 1 ∗ Wykład 8, str. 6 Leczenie lewostronnej rekursji hliczbai ::= hcyfrai hliczbai hcyfrai // <liczba> ::= <cyfra> | <liczba> <cyfra> void liczba (int* nr_leksemu, drzewo* drz, Boolean* ok) { drzewo drz1; cyfra (nr_leksemu, drz1, ok); if (ok) { // <cyfra> jest w porzadku . . . } else { // <cyfra> nieobecna, probujemy <liczba> <cyfra> liczba (nr_leksemu, drz1, ok); . . . } } Wykład 8, str. 7 Leczenie lewostronnej rekursji hliczbai ::= hcyfrai hcyfrai∗ // <liczba> ::= <cyfra> <cyfra>* void liczba (int* nr_leksemu, drzewo* drz, Boolean* ok) { drzewo drz1; cyfra (nr_leksemu, drz, ok); if (ok) { // <cyfra> jest w porzadku . . . do { cyfra (nr_leksemu, drz1, ok); if (ok) { // jest kolejna <cyfra> . . . } } while (ok); ok = TRUE; } } Wykład 8, str. 8 Program „wyleczonego” parsera zstępującego Wykład 8, str. 8 Program „wyleczonego” parsera zstępującego • Nieterminalowi hnnni gramatyki odpowiada funkcja o nagłówku void nnn(drzewo* drz) której ciało wynika z postaci prawych stron produkcji z hnnni (patrz niżej). Wykład 8, str. 8 Program „wyleczonego” parsera zstępującego • Nieterminalowi hnnni gramatyki odpowiada funkcja o nagłówku void nnn(drzewo* drz) której ciało wynika z postaci prawych stron produkcji z hnnni (patrz niżej). • Wystąpieniu nieterminala hnnni po prawej stronie produkcji odpowiada wywołanie nnn(drz); if (ok) ... Wykład 8, str. 8 Program „wyleczonego” parsera zstępującego • Nieterminalowi hnnni gramatyki odpowiada funkcja o nagłówku void nnn(drzewo* drz) której ciało wynika z postaci prawych stron produkcji z hnnni (patrz niżej). • Wystąpieniu nieterminala hnnni po prawej stronie produkcji odpowiada wywołanie nnn(drz); if (ok) ... • Wystąpieniu terminala t po prawej stronie produkcji odpowiada wywołanie if (leks == ’t’) { nowyleks(); ... Wykład 8, str. 8 Program „wyleczonego” parsera zstępującego • Nieterminalowi hnnni gramatyki odpowiada funkcja o nagłówku void nnn(drzewo* drz) której ciało wynika z postaci prawych stron produkcji z hnnni (patrz niżej). • Wystąpieniu nieterminala hnnni po prawej stronie produkcji odpowiada wywołanie nnn(drz); if (ok) ... • Wystąpieniu terminala t po prawej stronie produkcji odpowiada wywołanie if (leks == ’t’) { nowyleks(); ... • Gwiazdce ∗ odpowiada pętla. Wykład 8, str. 8 Program „wyleczonego” parsera zstępującego • Nieterminalowi hnnni gramatyki odpowiada funkcja o nagłówku void nnn(drzewo* drz) której ciało wynika z postaci prawych stron produkcji z hnnni (patrz niżej). • Wystąpieniu nieterminala hnnni po prawej stronie produkcji odpowiada wywołanie nnn(drz); if (ok) ... • Wystąpieniu terminala t po prawej stronie produkcji odpowiada wywołanie if (leks == ’t’) { nowyleks(); ... • Gwiazdce ∗ odpowiada pętla. • Alternatywie odpowiada budowanie drzewa oraz else . Wykład 8, str. 9 Program „wyleczonego” parsera zstępującego Przykład: ∗ Mhaaai ::= hccci − haaai + hbbbi Wykład 8, str. 9 Program „wyleczonego” parsera zstępującego Przykład: ∗ Mhaaai ::= hccci − haaai + hbbbi void aaa(drzewo* drz) { drzewo drz1; ccc(&drz1); if (ok) { /* z drz1 zrobic nowe drz z aaa w korzeniu */ while (leks == ’-’ || leks == ’+’) { if (leks == ’-’) { nowyleks(); aaa(&drz1); if (ok) /* z drz , - i *drz1 zrobić nowe drz */ else blad(’po - nie wystapil aaa’); } else if (leks == ’+’) { nowyleks(); bbb(&drz1); if (ok) /* z drz , + i *drz1 zrobić nowe drz */ else blad(’po + nie wystapil bbb’); } } } } Wykład 8, str. 10 Parser zstępujący bez nawrotów Przykład: hwagai hmiarai ::= hodległośći M hodległośći ::= hwagai ::= n o n 0 . . . 9 0 . . . n o n 0 ... 9 0 ... hmiarai ⇒ hodległośći hmiarai ⇒ hwagai ⇒∗ ⇒∗ 9 ∗m o 9 ∗ kg o 11111m 11111kg Wykład 8, str. 10 Parser zstępujący bez nawrotów Przykład: hwagai hmiarai ::= hodległośći M hodległośći ::= hwagai ::= n o n 0 . . . 9 0 . . . n o n 0 ... 9 0 ... hmiarai ⇒ hodległośći hmiarai ⇒ hwagai ⇒∗ ⇒∗ 9 ∗m o 9 ∗ kg o 11111m 11111kg Do którego nieterminala redukować? — nie da się postanowić na podstawie pierwszej cyfry, bo nie wiadomo jeszcze, czy na końcu będzie m czy kg. Wykład 8, str. 10 Parser zstępujący bez nawrotów Przykład: hwagai hmiarai ::= hodległośći M hodległośći ::= hwagai ::= n o n 0 . . . 9 0 . . . n o n 0 ... 9 0 ... hmiarai ⇒ hodległośći hmiarai ⇒ hwagai ⇒∗ ⇒∗ 9 ∗m o 9 ∗ kg o 11111m 11111kg Do którego nieterminala redukować? — nie da się postanowić na podstawie pierwszej cyfry, bo nie wiadomo jeszcze, czy na końcu będzie m czy kg. Parsery zstępujące nadają się nie do wszystkich gramatyk bezkontekstowych; nadają się tylko do t.zw. gramatyk LL(1).