Jak porządnie opisać język nieskończony? Generatory: gramatyki
Transkrypt
Jak porządnie opisać język nieskończony? Generatory: gramatyki
Wykład 17: powtórka, str. 1 Jak porządnie opisać język nieskończony? • {λ, ab, aabb, oaaabbb, . . .} — co oznaczają kropeczki? n • an bn n 0 — lepiej, ale są niepożądane elementy obce (liczby, zmienne, relacje, . . . ) Język definiujemy na dwa sposoby. Przez • generator — cokolwiek wyprodukuje, uważamy za należące do języka • akceptor — cokolwiek zatwierdzi, uważamy za należące do języka Opis języka programowania przeznaczony dla programisty powinien być generatorem — ma wyjaśniać, jak pisać poprawne programy. Kompilator języka programowania powinien zawierać akceptor — sprawdzający, czy program na jego wejściu należy do języka. Wykład 17: powtórka, str. 2 Generatory: gramatyki bezkontekstowe DEFINICJA: Gramatyka bezkontekstowa G — M • alfabet terminalny Σ; • alfabet nieterminalny lub pomocniczy N (rozłączny z Σ); • skończony zbiór P produkcji postaci A → w, gdzie A ∈ N i w ∈ (N ∪ Σ)∗; • wybrany nieterminal S ∈ N nazywany symbolem początkowym albo aksjomatem gramatyki. Przykład: M Terminale: a, b Nieterminale: A Produkcje: A → λ A → aAa A → bAb Wykład 17: powtórka, str. 3 Jak gramatyka definiuje język? DEFINICJA: Wywodliwość — M • jeśli w P jest produkcja A → w, to dla dowolnych słów u, v ∈ (N ∪ Σ)∗, słowo uwv nazywamy bezpośrednio wywodliwym ze słowa uAv, co oznaczamy uAv ⇒G uwv; • jesli w1 ⇒G w2 , w2 ⇒G w3 , . . . , wn−1 ⇒G wn , to słowo wn nazywamy wywodliwym ze słowa w1 , co oznaczamy w1 ⇒∗G wn . DEFINICJA: Język L(G) generowany przez gramatykę G — M składa się z tych słówn wywodliwych z aksjomatu, które już nie zawierają o def ∗ ∗ nieterminali: L(G) = w ∈ Σ S ⇒ G w . qq A ⇒∗ bbaabb ∈ L(G) Przykład: M Gramatyka: A → λ A → aAa A → bAb Fakt: n o R ∗ M L(G) = ww w ∈ {a, b} A ⇒ bAb ⇒ bbAbb ⇒ bbaAabb ⇒ bbaabb Wykład 17: powtórka, str. 4 Jak gramatyka definiuje język? Przykład: M Gramatyka G: E E T T F F → → → → → → E ⇒ T ⇒ (E ) ⇒ (T + T) ⇒ (a + T) ⇒ ( a + a) ⇒ F +T ∗F L(G) = wyrażenia T E +T F T ∗F a (E ) ⇒ ⇒ ⇒ ⇒ F (E + T) (F + T) (a + F) ⇒ F +F ∗F arytmetyczne zbudowane z a, +, ∗, ( i ) Wykład 17: powtórka, str. 5 Gramatyki prawoliniowe DEFINICJA: M Gramatyka bezkontekstowa G = hΣ, N, P, Si jest prawostronnie liniowa jeśli każda produkcja w P ma postać: • albo A → x (czyli: żadnych nieterminali) • albo A → xB (czyli: tylko jeden nieterminal po prawej) gdzie x ∈ Σ∗ a A, B ∈ N . Przykład: M abc E E C C C C C → → → → → → → aC bC λ aC bC 0C 1C E E E → a → (E ) → E +E bezkontekstowa ale nie prawoliniowa prawoliniowa Wykład 17: powtórka, str. 6 Rodzaje gramatyk Przykład: G1 : E → a C E C C C C C G1 G2 → → → → → → bC λ aC bC 0C 1C G2 : I → a I I I I I → → → → → b Ia Ib I0 I1 L(G1 ) = język identyfikatorów = L(G2 ) jest prawostronnie liniowa dwie gramatyki różnych typów jest bezkontekstowa mogą określać ten sam język ale nie prawostronnie liniowa DEFINICJA: M Język L jest • regularny jeśli ist. gram. prawostronnie liniowa G taka, że L = L(G); • bezkontekstowy jeśli ist. gram. bezkontekstowa G taka, że L = L(G). Przykład: Język identyfikatorów L(G2 ) jest regularny. Wykład 17: powtórka, str. 7 Umowa notacyjna dla gramatyk bezkontekstowych • ponieważ nieterminale oznaczają pojęcia — sugestywne nazwy np. „wyrażenie”, „liczba” • nieterminale ujmujemy w nawiasy kątowe: np. hwyrażeniei, hliczbai h... i • wszystkie produkcje z pojedynczego nieterminalu grupujemy: piszemy zamiast hcyfrai ::= 0 1 . . . 9 hcyfrai→0 hcyfrai→1 ... hcyfrai→9 hliczbai ::= hcyfrai hliczbai hcyfrai hliczbai→hcyfrai hliczbai→hliczbai hcyfrai Wykład 17: powtórka, str. 8 Drzewa wywodu DEFINICJA: M Drzewo wywodu słowa w ∈ Σ∗ w gramatyce bezkontekstowej G = hΣ, N, P, hSii to takie drzewo, które ma: • w liściach symbole terminalne składające się na słowo w, • w pozostałych (wewnętrznych) węzłach nieterminale rozmieszczone wg takich zasad: – jeśli w jakimś węźle znajduje się nieterminal hAi a w jego dzieciach od lewej do prawej symbole a1 , a2 , . . . , an ∈ Σ ∪ N , to hAi → a1 a2 . . . an musi być produkcją z P , – w korzeniu znajduje się nieterminal początkowy hSi. Przykład: hwyrażeniei M Fragment drzewa jest legalny jeśli hwyrażeniei + hskładniki hwyrażeniei → hwyrażeniei + hskładniki jest produkcją z gramatyki. Wykład 17: powtórka, str. 9 Drzewa wywodu Drzewa wywodu słów w gramatyce prawoliniowej są „przeważone” w prawo tak, że stanowią prawie listy: hEi b hCi hEi ::= a hCi hCi ::= λ a hCi b hCi 0 hCi 1 hCi hCi a hCi a hCi 1 b hCi hEi ⇒ a hCi ⇒ a a hCi ⇒ a a 1 hCi ⇒ a a 1 b hCi ⇒ a a 1 b Wykład 17: powtórka, str. 10 Drzewa wywodu hzmiennai ::= hcyfrai ::= hliczbai ::= hatomi ::= hwyrażeniei ::= hskładniki ::= ciąg terminali: 34 ∗ (x + 11) aksjomat a b . . . z 0 1 ... 9 hcyfrai hliczbai hcyfrai hzmiennai hliczbai ( hwyrażeniei ) hwyrażeniei + hskładniki hskładniki hatomi hskładniki * hatomi hwyrażeniei hskładniki ....... ...... ..... ..... . ........................................................ . . .....................hatomi .. ............. ............. .... . ......... . . . . . . . . . . . ....... . . . . . . . . . ...... . . hwyrażeniei . . . ..... .. .... . . . . . . .... . . ... . .... . . hskładniki .. ... . .. . . ... . . . ... ... ....hwyrażeniei ... hatomi ... ... ... ... ... ... hskładniki ... hliczbai ... ... .... ... ... ... .. hatomi hliczbai ... ... .... . ..... ..... hzmiennai hcyfrai hcyfrai ..... ... ... .. .. . .. hskładniki hatomi hliczbai hliczbai hcyfrai hcyfrai 3 4 * ( x + 1 1 ) Wykład 17: powtórka, str. 11 Drzewo wywodu a struktura słowa hwyrażeniei ::= 0 1 . . . 9 ( hwyrażeniei ) hwyrażeniei + hwyrażeniei hwyrażeniei ∗ hwyrażeniei ....... hwyrażeniei ....... ..... ..... ..... hwyrażeniei .... .... . hwyrażeniei hwyrażeniei ......... hwyrażeniei ... ... 2 + 5 ∗. 3 ... ... ... ... hwyrażeniei hwyrażeniei hwyrażeniei .... .... .... ..... hwyrażeniei ..... ..... ...... hwyrażeniei Niejednoznaczność wywodu: istnieją dwa różne drzewa wywodu danego słowa. W tym przypadku sugerują one kolejność działań: • drzewo górne odpowiada wykonaniu dodawania przed mnożeniem • drzewo dolne odpowiada wykonaniu mnożenia przed dodawaniem Wykład 17: powtórka, str. 12 Drzewo wywodu a struktura słowa Gramatyka fragmentu języka polskiego: hzdaniei ::= hgr.podmiotui hgr.orzeczeniai hgr.podmiotui ::= hpodmioti hprzydawkai hgr.podmiotui hgr.orzeczeniai ::= hpodmioti ::= hprzydawkai ::= horzeczeniei ::= hdopełnieniei ::= hokoliczniki ::= horzeczeniei horzeczeniei hdopełnieniei hgr.orzeczeniai hokoliczniki Anka Basia Czesiek gruby stary bije kocha Ankę Basię Cześka w domu nocą Wykład 17: powtórka, str. 13 Drzewo wywodu a struktura słowa hzdaniei hgr.orzeczeniai hgr.podmiotui hgr.orzeczeniai hgr.podmiotui hgr.orzeczeniai hgr.podmiotui hprzydawkaihprzydawkai hpodmioti horzeczenieihdopełnienieihokoliczniki hokoliczniki stary gruby Czesiek bije Ankę w domu nocą Wykład 17: powtórka, str. 14 Drzewo wywodu a struktura słowa hzdaniei hgrupa podmiotui hpodmioti hgrupa orzeczeniai horzeczeniei = CZAS LECI JAK STRZAŁA = CZASOWE MUCHY LUBIĄ STRZAŁĘ hokoliczniki TIME FLIES LIKE AN ARROW hprzydawkai hpodmioti horzeczeniei hdopełnieniei hgrupa podmiotui hgrupa orzeczeniai hzdaniei Znaczenie zależy od drzewa rozbioru również w językach naturalnych. Wykład 17: powtórka, str. 15 Drzewo wywodu a struktura słowa Z punktu widzenia należenia słowa do języka postać drzewa wywodu nie ma znaczenia. Z punktu widzenia przekazywanego sensu postać drzewa wywodu jest bardzo ważna. Przykład: jak policzyć wartość wczytywanej liczby? M hliczbai ::= hcyfrai hcyfrai hliczbai To sugeruje, że trzeba osobno znaleźć wartość pierwszej cyfry i osobno całej reszty a następnie jakoś te wartości połączyć: 3|456 = 3 · 103 + 456 — ale skąd z góry wiedzieć, do jakiej potęgi podnieść 10? hliczbai ::= hcyfrai hliczbai hcyfrai To sugeruje, że trzeba osobno znaleźć wartość ostatniej cyfry i osobno całej reszty a następnie jakoś te wartości połączyć: 345|6 = 345 · 10 + 6 — tak jest łatwiej zaprogramować. Rozumienie słowa oraz operacje na nim odbywają się faktycznie na jego drzewie wywodu. Wykład 17: powtórka, str. 16 Akceptory: automaty skończone ............ ............... .... .......b ... ......a . . .. . ... ... . . .... .... .. . ... . ... . . . .. .. ❫.. ❫.. a . . . . . . . . . . . . . . . . . . . . . ..... q ..... ..✐ ............................. q a✣ ✲ s b p b ❫ r b .......... .....................q ..✐ ............................ ... ... ... ✣ ... .. ... .... ... ..... .... a ............ a t ... ... ... ✣ ... .. ... . . ... ..... ....... b ........... Funkcja przejścia automatu: δ p q r s t a q s r s r b r q t q t δ ∗hp, baabi = δ ∗ δhp, bi , aab D E D E = δ ∗ r, aab = δ ∗ δhr, ai , ab D E D E = δ ∗ r, ab = δ ∗ δhr, ai , b D = δ ∗ r, b = δ ∗ δhr, bi , λ = δ ∗ht, λi = t D E D E E Akceptacja słowa w: jeśli δ ∗hp, wi jest stanem akceptującym. Wykład 17: powtórka, str. 17 Automaty skończone DEFINICJA: Deterministyczny automat skończony M — M • skończony zbiór Q stanów, • skończony zbiór Σ liter, • funkcja przejścia δ : Q × Σ → Q, • wyróżniony stan początkowy q0 ∈ Q, • wyróżniony zbiór stanów końcowych (akceptujących) F ⊆ Q. DEFINICJA: Uogólniona funkcja przejścia — δ ∗ : Q × Σ∗ → Q: q jeśli w = λ δ ∗hq, wi def = δ ∗ δhq, σi , w ′ D E jeśli w = σw ′ dla pewnych σ ∈ Σ i w ′ ∈ Σ∗ DEFINICJA: Automat M akceptuje słowo w jeśli δ ∗hq0 , wi ∈ F DEFINICJA: Język akceptowany przez automat M = hQ, Σ, δ, q0 , F i: def L(M ) = n w ∈ Σ∗ M akceptuje w o = n w ∈ Σ∗ δ ∗hq0 , wi ∈ F o Wykład 17: powtórka, str. 18 Automaty skończone Przykład: JMęzyk liczb naturalnych: ✲ {0} + {1} · {0, 1} ∗ = {0} ∪ 1w w ∈ {0, 1} ∗ n o p 1 ............................ . .... 1 0, .. ... ...... ........................... ✸ 0 ❄ r n o wv w ∈ {a, b} & v ∈ {a, b, 0, 1} ∗ 1 ❄❄ ........................ . ... 0,.....1.... śm ........ . ... ...... . . . . . . . . . . . . . . . . ❦ Przykład: JMęzyk identyfikatorów: {a, b} · {a, b, 0, 1} ∗ = q 0✲ p ✲ ................ . .... a, ..b, ... 0, 1 ... ... ... .. ... . .. .. ❫ . a .... ...... .....................q .....................✶ .......... q b 0 1 ❄❄ ........................ .. ... ... 1 a, b,...0, .. śm ........ . .... . . . . . . . . ❦............. Wykład 17: powtórka, str. 19 Automaty skończone TWIERDZENIE: M Dla każdego automatu skończonego M język L(M ) jest regularny. Dla każdego języka regularnego L istnieje automat skończony M taki że L = L(M ). Przykład: G G: Mramatyka prawoliniowa hSi ::= λ abbhSi o n n ∗ L(G) = {abb} = (abb) n 0 stan S: rozpoznano pewną ilość słów abb stan p: rozpoznano pewną ilość słów abb i jeszcze literę a stan q: rozpoznano pewną ilość słów abb i jeszcze słowo ab stan śm: wystąpił błąd b.................................... ................................. . . . . . . . . . . . . . ..... ..✢ ✸ ✲ b p a✲ S q a a ❄ ... ....... s ................ ................................................... ✲ .............. śm b ... ... ... ✣ ... .. . ... .. ... .. b .... ...a, ............. Wykład 17: powtórka, str. 20 Automaty skończone Przykład: M Gramatyka prawoliniowa G: hSi ::= ahAi bhBi hAi ::= a bhBi hBi ::= λ ahAi ......... ..... ....... 1 ... .... .. .... .. ... . ❫.. ... 0✸ ✲ A s S 1s ........... .... .....0, ... 1 .... ... . ... .. ... . . 0 ❫... .. B ... . ... .. ✣ . .... .. ... . . ... ....... ........ 0 ..... ✸ 1 C a✸ ✲ S A .. .... b ..... ... s ◆. b ... ▼..... ..a .. ... B Przykład: M Gramatyka prawoliniowa: hSi ::= 0hAi 1hBi hAi ::= 0hCi 1hAi hBi ::= 0hBi 1hCi hCi ::= λ 0hCi 1hCi a✲ a, b ❄ b ✲ śm ... . ... ... ✣ .. .... ... .. . ... .. b ...... ......a, ....... Wykład 17: powtórka, str. 21 Maszyny ze stosem ............... ....0, 0......→ ... ... .. .. ... . . . ❫.. . Słowo na wejściu: 0 0 11 00 q → 0, Z p ✇ λ ✲ → 0 0 Z ✼ 0 1, 0Z część nieprzeczytana ✛ λ, Z → λ r ..... ............ ................ .. 1, 0 → ... . . . . ............ .......... ❦...... λ Kiedy wędrówka po maszynie się „zatnie”, czyli nie ma już żadnej możliwości pójścia dalej, sprawdzamy, czy słowo zostało zaakceptowane; t.zn., czy spełnione są następujące warunki: • na wejściu jest słowo puste (wszystko już przeczytane); • na stosie jest słowo puste; • sprawdzanie zakończyło się w stanie końcowym (akceptującym). Wykład 17: powtórka, str. 22 Maszyny ze stosem DEFINICJA: Maszyna ze stosem M — M • skończony zbiór Q stanów, • skończony zbiór Σ liter wejściowych, • skończony zbiór Γ symboli stosowych, • Z ∈ Γ — wyróżniony symbol stosowy oznaczający dno stosu, ˜ Q × Γ∗ (częścio• częściowa funkcja przejścia δ : Q × (Σ ∪ {λ}) × Γ → wa — t.zn. może być nieokreślona dla niektórych argumentów), • wyróżniony stan początkowy q0 ∈ Q, • wyróżniony zbiór stanów końcowych (akceptujących) F ⊆ Q. DEFINICJA: Język akceptowany przez M — n o M def ∗ L(M ) = w ∈ Σ δ ∗hq0 , w, Zi = hq ′ , λ, λi dla pewnego q ′ ∈ F gdzie δ ∗ jest uogólnioną funkcją przejścia. Wykład 17: powtórka, str. 23 Maszyny ze stosem a języki bezkontekstowe TWIERDZENIE: M Język akceptowany przez dowolną maszynę stosową jest bezkontekstowy. Uwaga: Nie każdy język bezkontekstowy jest akceptowany przez jakąś maszynę stosową; ale każdy jest akceptowany przez jakąś niedeterministyczną maszynę stosową (nierozpatrywane na tym wykładzie). „Prawie” każdy „praktyczny” język bezkontekstowy jest akceptowany przez jakąś maszynę stosową, co umożliwia kompilowanie programów komputerowych. Wykład 17: powtórka, str. 24 Gramatyki, języki, maszyny gramatyki języki akceptory prawoliniowe bezkontekstowe l l regularne bezkontekstowe l ↑ automaty maszyny ze stosem Wykład 17: powtórka, str. 25 Notacja beznawiasowa Łukasiewicza Notacja Łukasiewicza (w świecie nazywana Polish): Wyrażenia (arytmetyczne, logiczne, itp.) można zapisywać bez nawiasów w postaci operator arg1 arg2 . . . argn Odwrotna notacja Łukasiewicza (Reverse Polish): arg1 arg2 . . . argn operator Jan Łukasiewicz 1878–1956 stosowana w informatyce. Wykład 17: powtórka, str. 26 Notacja beznawiasowa Łukasiewicza Przykład: M ∗ + 2 + 5 1 ∗ 7 3 Zwykła notacja (infix): (2 + 5) ∗ (7 ∗ 3 + 1) Notacja Łukasiewicza (prefix): ∗ + 2 5 + ∗ 7 3 1 Odwrotna notacja polska (postfix): 2 5 + 7 3 ∗ 1 + ∗ Wykład 17: powtórka, str. 27 Obliczanie wyrażeń w ONŁ Założenie: każdy operator op „wie” ile potrzebuje argumentów: A(op). Np. A(+) = 2 , A(¬) = 1 , A(if(...) ... else ...) = 3 . Algorytm: do { jeśli na wejściu jest liczba ℓ, to przełóż ją z wejścia na stos; jeśli na wejściu jest operator op, to { zdejmij ze stosu A(op) liczb, zastosuj do nich operację op, jej wynik włóż na stos; } } while (jest jeszcze coś na wejściu); stos 25 + 73 ∗ 1 + ∗ wejście Wykład 17: powtórka, str. 28 Działania na językach 1. Suma: n def L|M = o w w ∈ L lub w ∈ M 2. Złączenie: def LM = o uv u ∈ L i v ∈ M n 3. Iteracja (domknięcie Kleenego): L∗ def = n u1 u2 . . . un u1 , u2 , . . . , un ∈ L i n 0 o Wykład 17: powtórka, str. 29 Działania na językach Własności działań: • Łączność: K | (L | M ) = (K | L) | M K(LM ) = (KL)M • Przemienność: L|M =M |L Uwaga: złączenie nie jest przemienne! • Elementy neutralne: M |∅=M ΛM = M Λ = M ∅ def = {} — język pusty Λ def = {λ} — język zawierający tylko słowo puste • Rozdzielność: K(L | M ) = KL | KM (L | M )K = LK | M K Wykład 17: powtórka, str. 30 Działania na językach Iteracja: L∗ def = u1 u2 . . . u n u1 , u2 , . . . , u n ∈ L i n 0 L∗ = Λ | L | L2 | L3 | . . . = {λ} | L | LL | LLL | . . . TWIERDZENIE: (rozwiązywanie równania językowego) M Rozwiązaniem równania X = L | XM jest X = LM ∗ . Dowód: Jeśli X = LM ∗ to X = LM ∗ = L (Λ | M | M 2 | M 3 | . . .) = LΛ | LM | LM 2 | LM 3 | . . . = L | (LM | LM 2 | LM 3 | . . .) = L | L (Λ | M | M 2 | . . .) M = L | LM ∗M = L | XM Wykład 17: powtórka, str. 31 Uproszczony schemat kompilacji program źródłowy ciąg leksemów ✕ analiza leksykalna drzewo wywodu ✕ drzewo i tablice symboli ✕ analiza syntaktyczna analiza semantyczna KOMPILATOR generacja kodu ✯ kod qqqqqqqqqq qqqqq qqq qqqqq qqq docelowy qqq qq qqq qqq qqq q q dane do wyniki programu Wykład 17: powtórka, str. 32 Analizator leksykalny czyli skaner — przykład ✲ pocz B✲ E✲ b E✲ e N✲ be G en D • słowa kluczowe: BEG, END (wielkość liter nieistotna) • znaki operacji: +, * • liczby całkowite postaci hcyfraihcyfrai∗ • komentarze postaci /* hcokolwieki */ • separatory postaci hspacjaihspacjai∗ q klucz ✶ +✲ oper * ............................ ....hcyfrai ✲ licz .. . . .......................... . hcyfrai ❦ /✲ ciach *✲ cgw ......... .... ......* ... ... ... .... ... .. . ❫. . *✲cgwgw /✲ .............. ..... .. ... ..................... ■ . ✣... .. .. ................................... . .. ... ... ✲ separ .. .......... . . . .......................... hspacjai ❦ hspacjai koment Wykład 17: powtórka, str. 33 Analizator leksykalny czyli skaner — przykład Funkcja przejścia: pocz b be e en licz ciach cgw cgwgw separ b b e e be g n d pl gw cyf ciach sp nieokr oper oper licz ciach separ klucz en klucz licz cgw cgw cgw cgw cgw cgw cgw cgwgw cgw cgw cgw cgw cgw cgw cgw cgw cgw cgw cgwgw cgw koment cgw cgw separ Uwagi: • funkcja przejścia automatu skończonego stany × znaki → stany • zbiór znaków rozszerzony o znak nieokr : „ jakikolwiek inny” • puste miejsca oraz brakujące wiersze oznaczają „stan śmietnikowy” Wykład 17: powtórka, str. 34 Po co wydziela się analizę leksykalną? Włączenie analizy leksykalnej do analizy składniowej jest nietrudne; po co więc jest wydzielona? 1. Analiza leksykalna jest prostsza niż składniowa leksyka syntaktyka gramatyka prawoliniowa bezkontekstowa akceptor automat maszyna stosowa 2. Analizator leksykalny czyta tekst, więc jego działanie zajmuje dużo czasu. Warto więc go optymalizować — a to robi się inaczej niż w składniowym. 3. Czytanie tekstu jest zależne od platformy, analiza składniowa jest niezależna. Rozdzielenie ich poprawia więc przenośność (portability ) kompilatorów. Wykład 17: powtórka, str. 35 Różne metody analizy syntaktycznej Rozbiór słowa — budowanie jego drzewa wywodu w danej gramatyce bezkontekstowej. Synonimy: rozbiór = analiza syntaktyczna = parsing. Parser — program (procedura, podprogram) dokonujący rozbioru. Budowy drzewa wywodu można dokonywać • albo od strony korzenia (rozbiór zstępujący ), • albo od strony liści (rozbiór wstępujący ). Jest wiele metod rozbioru danego słowa; różnią się • zakresem stosowalności (do jakich rodzajów gramatyk bezkontekstowych się nadają), • oraz efektywnością (jak szybko działają i ile potrzebują pamięci). Programy do automatycznego tworzenia parserów potrafią na ogół dobrać optymalny (albo przynajmniej niezły) parser do danej gramatyki. Wykład 17: powtórka, str. 36 Koszty • Do każdej jednoznacznej gramatyki bezkontekstowej można zbudować parser, działający w czasie O(n3 ), gdzie n jest długością ciągu terminali na wejściu. • Ta złożoność wynika z działania przez próbowanie i wycofywanie się w przypadku złego dopasowania; drzewo wywodu jest wielokrotnie budowane i niszczone. • Niezbyt bolesne ograniczenia na gramatyki umożliwiają istnienie parsera, działającego w czasie O(n), czyli znacznie szybciej. • Wszystkie praktycznie stosowane parsery działają w czasie O(n). Wykład 17: powtórka, str. 37 Rekursywny parser zstępujący • Analiza top-down. • Po jednej funkcji rekursywnej dla każdego nieterminala. Funkcja dla nieterminalu hXi – wywoływana jest po to, żeby skonstruować drzewo wywodu początkowych leksemów z wejścia, mające w korzeniu nieterminal hXi; – żeby móc zadecydować, której produkcji z nieterminalu hXi użyć, jest wywoływana w sytuacji, w której znany (wczytany) jest już pierwszy liść (leksem) tego drzewa; kończy działanie w sytuacji, kiedy znany jest już pierwszy leksem spoza korony tego drzewa; – może zasygnalizować błąd i program ją wywołujący stara się znaleźć inną produkcję do zastosowania. Wykład 17: powtórka, str. 38 Rekursywny parser zstępujący Boolean Boolean A(drzewo* drz); B(drzewo* drz); hBi ::= whBi w hBi hBi w drz1 Boolean B(drzewo* drz) { drzewo drz1; if (nowyleks(’w’)) if (B(&drz1)){ *drz = ...; return true; } else { *drz = ...; return true; } else return false; } hBi w Wykład 17: powtórka, str. 39 Rekursywny parser zstępujący Boolean A(drzewo* drz); Boolean B(drzewo* drz); hAi ::= yhAihBiz x hAi y hAi hBi drz2 ......................................... ................. ......... ....... ......... ..... .... . . . ... .. . . ... .. . ... . . ... . . ... ... .. .. .. .. .. . drz1 Boolean A(drzewo* drz) { drzewo drz1, drz2; if (nowyleks(’y’)) if (A(&drz1)) if (B(&drz2)) if (nowyleks(’z’)) { *drz = ...; return true; } else blad("brakuje terminalu z"); else blad("brakuje nieterminalu <B>"); else blad("brakuje nieterminalu <A>"); else if (nowyleks(’x’)) { *drz = ...; return true; } else return false; } z hAi x Wykład 17: powtórka, str. 40 Przekształcenie gramatyki dla parsera zstępującego Zmiana kolejności produkcji i „wyłączenie przed nawias”: hWi ::= hSi ::= hCi ::= hLi ::= hSi hSi + hWi hSi hSi - hWi hCi hCi * hSi hCi hCi / hSi hLi ( hWi ) 0 1 0 hLi 1 hLi hWi ::= hSi n o + hWi - hWi λ hSi ::= hCi n o * hSi / hSi λ hCi ::= ( hWi ) hLi n o hLi ::= 0 hLi λ n o 1 hLi λ Wykład 17: powtórka, str. 41 Parser zstępujący — problemy Lewostronna rekursja w gramatyce powoduje nieskończone obliczenie Przykład: inna gramatyka: hWi::=hSi hWi+hSi M Boolean fW (drzewo* t) { if (fS (&t1 )) { *t = hWi ; return TRUE; } else hSi t1 if (fW (&t1 )) { if (nowyleks(’+’,&nleks)) if (fS (&t2 )) { hWi *t = hWi + ; return TRUE; } hSi t1 t2 else blad("po + brakuje <S>"); else return FALSE; else return FALSE; } Wykład 17: powtórka, str. 42 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” — hwyrażeniei + hskładniki zamiast hwyrażeniei::=hskładniki hwyrażeniei - hskładniki nn o o piszemy hwyrażeniei ::= hskładniki + − hskładniki ∗ wyrażenie ✲ składnik + ✛ − ✛ ✲ Wykład 17: powtórka, str. 43 Leczenie lewostronnej rekursji Gramatyka oryginalna z lewostronną rekursją: hWi::=hSi hWi + hSi hWi hWi - hSi hSi::=hCi hSi * hCi hCi hSi / hCi hCi::=hLi ( hWi ) hLi::=0 1 hLi 0 hLi 1 Gramatyka wyleczona z iteracją: hWi::=hSi nn o o + − hSi ∗ hSi::=hCi nn o o ∗ / hCi ∗ hCi::=( hWi ) hLi n o n o hLi::= 0 1 0 1 ∗ Wykład 17: powtórka, str. 44 Parser zstępujący bez nawrotów Przykład: hmiarai::=hodległośći n o n hwagai o M hodległośći::=n0 . . . 9o n0 . . . 9o ∗ m hwagai::= 0 . . . 9 0 . . . 9 ∗ kg hmiarai ⇒ hodległośći hmiarai ⇒ hwagai ⇒∗ ⇒∗ 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). Wykład 17: powtórka, str. 45 Rozbiór wstępujący — gramatyki z pierwszeństwem Rozbiór wstępujący — budujemy drzewo rozbioru od liści. W ciągu symboli wejściowych musimy znaleźć podstawę czyli uchwyt najbliższej redukcji, czyli podciąg który zostanie zredukowany w pierwszym kroku. hMi ::= a ( hMi a ) a Następnie dokonujemy redukcji. (.... hMi a..... Znowu wyszukujemy podstawę. I dokonujemy redukcji. ).... ... . . . . .. ... . . . . . ... . .. .... .. ......... ....... . ........................ . hMi ..a )..... .... ... . . . . .. ... .... ... .... . . . . . .. .. ...... .... ...... ........... ........................................................................ (..... Te kroki powtarzamy aż do skonstruowania całego drzewa. hMi W jaki sposób znajdujemy podstawę redukcji? Wykład 17: powtórka, str. 46 Rozbiór wstępujący — gramatyki z pierwszeństwem W jaki sposób znajdujemy podstawę redukcji? Relacje wpisane w tablicę związane są z kolejnością redukowania poszczególnych symboli: . • jeśli x = y to symbole x i y będą się redukować razem (jeśli stoją koło siebie i jeden należy do podstawy, to drugi też); • jeśli x ⋖ y to symbol x powinien zaczekać, aż y zostanie zredukowany (jeśli stoją koło siebie i y należy do podstawy, to x nie należy do podstawy); • jeśli x ⋗ y to symbol y powinien zaczekać, aż x zostanie zredukowany (jeśli stoją koło siebie i x należy do podstawy, to y nie należy do podstawy); • jeśli nie ma między nimi żadnej relacji, to nie powinny stać obok siebie (jeśli stoją koło siebie, to jest to błąd). hMi ::= a ( hMi a ) Tablica pierwszeństwa redukcji: ( hMi a ) ( hMi a ) . ⋖ = ⋖ . = . ⋗ = ⋗ Wykład 17: powtórka, str. 47 Rozbiór wstępujący — gramatyki z pierwszeństwem W jaki sposób znajdujemy podstawę redukcji? hMi ::= a ( hMi a ) a Tablica pierwszeństwa redukcji: (.... hMi a..... (..... ).... ... . . .. . ... . . . . . ... .. .. .... .. .......... ....... . . ......................... ( hMi a ) hMi . ..a ).... .... ... . . . . .. ... .... ... .... . . . . . .. ...... .. ...... .... .......... ........................................................................... hMi ( hMi a ) . ⋖ = ⋖ . = . ⋗ = ⋗ • Jak skonstruować tablicę pierwszeństw? • Dla jakich gramatyk da się to zrobić? Wykład 17: powtórka, str. 48 Konstrukcja tablic pierwszeństwa Niech G = hΣ, N, P, Si będzie gramatyką bezkontekstową. DEFINICJA: M Niech A ∈ N . n o n o fst A def = x ∈ Σ ∪ Nn istnieje w P jakaś produkcjao A → xv S fst+ A def fst+ B B ∈ N & B ∈ fst A = fst A ∪ A → wx lst A def = x ∈ Σ ∪ Nn istnieje w P jakaś produkcja o S lst+ B B ∈ N & B ∈ lst A lst+ A def = lst A ∪ Przykład: M hMi ::= a ( hMi a ) — fst hMi = {a, (} — fst hMi Przykład: ) hMi ::= a hQi a ) M hQi ::= ( hMi = {a, hQi} fst hMi = {a, hQi} ∪ fst+ hQi = {a, hQi, (} + Wykład 17: powtórka, str. 49 Konstrukcja tablic pierwszeństwa Niech G = hΣ, N, P, Si będzie gramatyką bezkontekstową. DEFINICJA: . M Relacje =, ⋖, ⋗ w Σ ∪ N określone są następująco: . def x = y ⇐⇒ w P istnieje produkcja A → wxyv x ⋖ y ⇐⇒ w P istnieje produkcja A → wxBv taka że y ∈ fst+ B A def . . . x B. . . y. . . def x ⋗ y ⇐⇒ A w P istnieje produkcja A → wByv taka że x ∈ lst+ B . . . B y. . . ...x lub A w P istnieje produkcja A → wBCv taka że x ∈ lst+ B i y ∈ fst+ C . . . B C. . . . . . x y. . . Wykład 17: powtórka, str. 50 Zakres stosowalności gramatyk z pierwszeństwem Przykład: hWi ::= hSi hWi + hSi M hSi ::= ℓ ( hWi ) Ponieważ ( i hWi sąsiadują w tej samej . produkcji, więc ( = hWi. Ponieważ hWi ∈ fst+ hWi , więc ( ⋖ hWi. Dla tej gramatyki nie da się więc skonstruować tablicy pierwszeństwa. Przykład: hAi ::= a hBi a M hBi ::= a Nie wiadomo, do jakiego nieterminalu redukować a. Dla tej gramatyki nie da się zastosować analizy z pierwszeństwem. DEFINICJA: M Gramatyka bezkontekstowa G = hΣ, N, P, Si jest gramatyką z pierwszeństwem jeśli • dla każdych dwóch symboli x, y ∈ Σ ∪ N , zachodzi najwyżej jeden ze . związków x = y, x ⋖ y, x ⋗ y (może nie zachodzić żaden), • nie istnieją dwie różne produkcje o tej samej prawej stronie, ani produkcja o pustej prawej stronie: hAi → λ. Wykład 17: powtórka, str. 51 Poprawianie na gramatykę z pierwszeństwem Przykład: M hWi ::= hSi hWi + hSi hSi ::= ℓ ( hWi ) hWi hSi + ℓ ( ) . . = = ⋗ ⋗ . ⋖ ⋖ = ⋗ ⋗ . =⋖ ⋖ ⋖ ⋖ ⋗ ⋗ hWi hSi + ℓ ( ) hWi hSi + hWi ::= hVi hVi ::= hSi hVi + hSi hSi ::= ℓ ( hWi ) hWi hSi + ℓ ( ) hVi . = ℓ ( ⋗ ⋖ ⋖ ⋗ . = hVi ) . = ⋗ ⋗ ⋖ ⋖ ⋖ ⋗ . = ⋖ ⋗ ⋗ Wykład 17: powtórka, str. 52 Użycie tablicy pierwszeństwa do rozbioru hWi hWi hSi + ℓ ( ) hWi ::= hVi hVi ::= hSi hVi + hSi hSi ::= ℓ ( hWi ) . = . = hVi ⋖ . . ⋖ = = STOS: ⋗ ..................... hVi + hSi ..... hSi ℓ ( ... ... . hWi ). hVi hVi + hSi hSi ℓ ℓ hSi + ℓ ( ⋗ ⋖ ⋖ ⋗ ⋖ ⋗ . = ⋖ hVi ⋖ ⋗ ⋖ ) . = ⋗ ⋖ ⋖ ⋗ ⋖ ⋖ ⋗ ⋖ ⋗ ⋗ ⋗ ⋗ ⋗ ⋗ ⋗ ⋖ WEJŚCIE: ℓ + ( ℓ + ℓ ) + ℓ Zakończenie pracy: Na stosie: tylko i jedno drzewo z nieterminalem początkowym w korzeniu. Na wejściu: tylko . Wykład 17: powtórka, str. 53 Użycie tablicy pierwszeństwa do rozbioru inicjalizacje (); do { switch (pierwszenstwo [szczyt_stosu()][leks]) { case nieokr: blad ("Te symbole nie moga wystepowac obok siebie: ", znak_z_symb (szczyt_stosu()), znak_z_symb (leks)); break; case mniejsze: case rowne: drz = drzewo_z_terminalu (leks); na_stos (drz); nast_leks(); break; case wieksze: redukcja(); break; } } while (wys_stosu != 2 || szczyt_stosu() != wyr || leks != pusty); drz = ze_stosu (); drukuj_drzewo (drz); Wykład 17: powtórka, str. 54 Wykonanie redukcji hWi ::= hVi hVi ::= hSi hVi + hSi hSi ::= ℓ ( hWi ) • zdejmujemy ze stosu symbole między ⋖ and ⋗; • wyszukujemy właściwą produkcję za pomocą automatu skończonego zbudowanego wg gramatyki; • wkładamy na stos nieterminal z lewej strony tej produkcji. hWi → hVi hVi → hVi+hSi V2 ✛ hVi 3 ✛ + hVi → hSi hSi → ℓ hSi → (hWi) S2 ✛ ( 2 ✛ hWi W ✛ hVi V1 ✛ hSi S1 ✛ ℓ 1 ✛ ) ✛ Wykład 17: powtórka, str. 55 Gramatyki z pierwszeństwem — podsumowanie • Bezpośrednia wstępująca konstrukcja drzewa wywodu z użyciem stosu. • Zalety: – prostota; – o wiele szybsze działanie i mniejsza zajętość pamięci niż w rozbiorach zstępujących; – możliwość dobrej sygnalizacji błędów. • Wady: – konieczność poprawienia gramatyki przez dodanie nowych nieterminali i produkcji — czasem bardzo wielu. Wykład 17: powtórka, str. 56 PAMIĘĆ Programowanie niskiego poziomu rejestr arytmetyczny (akumulator) 0 1 2 3 4 5 6 7 8 9 10 11 · · · n • adresowana (czyli numerowana) pamięć złożona z komórek, z których każda może pomieścić bajt; dla uproszczenia zakładam, że nie bajt tylko liczbę całkowitą; • rejestr arytmetyczny także mogący pomieścić liczbę całkowitą; większość operacji odbywa się między rejestrem arytmetycznym a komórką pamięci; • komendy (patrz niżej) są kodowane liczbami całkowitymi i wpisywane do komórek pamięci. Wykład 17: powtórka, str. 57 PAMIĘĆ Programowanie niskiego poziomu 0 1 2 3 4 5 6 7 8 9 10 11 rejestr arytmetyczny (akumulator) e r o ... ✛ ...s....t............................... ..✒ . . . . . . . . . . . . . . . . . . ...... ..✙ ... loadn ...... . . . . . . ........d. a KOMENDY PRZESYŁANIA: . . . . . . . . . . . . . . ........................ loa loadn n — załaduj do akumulatora liczbę n loada n — załaduj do akumulatora liczbę z komórki o adresie n store n — zapamiętaj zawartość akumulatora w kom. o adresie n · · · load = ładować store = przechowywać, magazynować n Wykład 17: powtórka, str. 58 PAMIĘĆ Programowanie niskiego poziomu 0 1 2 3 4 5 6 7 8 9 10 11 rejestr arytmetyczny (akumulator) ....... .... ...............✲ .... .... .............................. .. add KOMENDY ARYTMETYCZNE: . . . .... sub .......................... add n — dodaj do akumulatora mul liczbę z kom. o adresie n; wynik w akumulatorze sub n — odejmij od akumulatora liczbę z kom. o adresie n; wynik w akumulatorze mul n — pomnóż akumulator przez liczbę z kom. o adresie n; wynik w akumulatorze · · · n add = dodawać subtract = odejmować multiply = mnożyć Wykład 17: powtórka, str. 59 PAMIĘĆ Programowanie niskiego poziomu 0 1 2 3 4 5 6 7 8 9 10 11 rejestr arytmetyczny (akumulator) ........................ incr . . . . . . . . . . ... .... ..✙ ....... ... decr ............ KOMENDY DZIAŁAŃ NA KOMÓRKACH: incr n — powiększ o 1 zawartość kom. o adresie n decr n — zmniejsz o 1 zawartość kom. o adresie n increment = zwiększać decrement = zmniejszać · · · n Wykład 17: powtórka, str. 60 PAMIĘĆ Programowanie niskiego poziomu 0 1 2 3 4 5 6 7 8 9 10 11 rejestr arytmetyczny (akumulator) komenda KOMENDY SKOKÓW: komenda ......... komenda ..✛ goto n — przejdź do komórki ... goto . ... o adresie n komenda ..zergoto zergoto n — jeśli w akumul. zero, . komenda ..plsgoto . to przejdź do kom. komenda .. mingoto . . . o adresie n skok ........ plsgoto n — jeśli akum. dodatni, komenda to przejdź do kom. komenda mingoto n — stop — · · · n go to = idź do o adresie n jeśli akum. ujemny, to przejdź do kom. o adresie n zatrzymaj działanie Wykład 17: powtórka, str. 61 Programowanie niskiego poziomu Prosty adres komendy: liczba całkowita oznaczająca • albo samą liczbę, np. loadn 500 oznacza: załaduj do akumulatora liczbę 500; • albo adres komórki, której dotyczy komenda, np. loada 500 oznacza: załaduj do akumulatora liczbę z komórki 500. Modyfikowany adres komendy: postaci n+[k] — adres n modyfikowany zawartością komórki o adresie k, należy tą zawartość do adresu n dodać. Np. jeśli pod adresem 500 stoi liczba 1, to komenda add 200+[500] oznacza: dodaj do akumulatora zawartość komórki 201; a jeśli pod adresem 500 stoi liczba 5, to ta sama komenda add 200+[500] oznacza: dodaj do akumulatora zawartość komórki 205. Wykład 17: powtórka, str. 62 Organizacja pamięci Przykład: M void sil(int n, int* w) { int w1; if (n==0) *w = 1; else { sil(n-1,&w1); *w = n*w1; } } s sil(3,&s); n ❑ w w1 3 n w ✕ w1 2 n w ✕ w1 1 n 0 w ✕ w1