Jaka jest różnica między parserami LR, SLR i LALR?

Jaka jest rzeczywista różnica między parserami LR, SLR i LALR? Wiem, że SLR i LALR są rodzajami parserów LR, ale jaka jest rzeczywista różnica, jeśli chodzi o ich tabele parsowania?

I jak pokazać, czy Gramatyka to LR, SLR, czy LALR? W przypadku gramatyki LL musimy tylko pokazać, że każda komórka tabeli parsowania nie powinna zawierać wielu reguł produkcyjnych. Jakieś podobne zasady dla LALR, SLR i LR?

Na przykład, jak możemy pokazać, że gramatyka

S --> Aa | bAc | dc | bda
A --> d

Czy LALR(1), ale nie SLR (1)?


EDIT (ybungalobill): nie dostałem satysfakcjonującej odpowiedzi, jaka jest różnica między LALR A LR. Tak więc tabele LALR są mniejsze, ale mogą rozpoznawać tylko podzbiór gramatyk LR. Czy ktoś mógłby rozwinąć więcej na temat różnicy między LALR i LR proszę? LALR(1) i LR(1) będą wystarczające do uzyskania odpowiedzi. Oba z nich używają 1 token look-ahead i oba są oparte na tabeli! Jakie są inaczej?

Author: ybungalobill, 2010-04-20

7 answers

Parsery SLR, LALR i LR mogą być zaimplementowane przy użyciu dokładnie tej samej maszyny sterowanej tabelą.

Zasadniczo algorytm parsowania pobiera następny token wejściowy T i konsultuje bieżący stan S (i powiązane tabele lookahead, GOTO i reduction), aby zdecydować, co zrobić:

  • SHIFT: jeśli bieżąca tabela mówi, aby przesunąć na tokenie T, para (S,T) jest popychana na stos parsowania, stan jest zmieniany zgodnie z tym, co mówi tabela GOTO dla bieżącego tokena (np. GOTO (T)), pobierany jest kolejny Token wejściowy T', a proces powtarza
  • REDUCE: każdy stan mA 0, 1 lub wiele możliwych redukcji, które mogą wystąpić w stanie. Jeśli parserem jest LR lub LALR, token jest sprawdzany przed zestawami lookahead dla wszystkich ważnych redukcji dla stanu. Jeśli token pasuje do zestawu lookahead dla redukcji Dla reguły gramatycznej G = R1 R2 .. Rn, następuje redukcja i przesunięcie stosu: nazywa się akcję semantyczną dla G, stos jest N (od Rn) razy, para (S,G) jest popychany na stos, nowy stan S' jest ustawiony na GOTO (G), A cykl powtarza się z tym samym tokenem T. Jeśli parser jest parserem SLR, istnieje co najwyżej jedna reguła redukcji dla stanu, więc akcja redukcji może być wykonana na ślepo bez szukania, która redukcja ma zastosowanie. Jest to przydatne dla parsera SLR, aby wiedzieć, czy jest redukcja, czy nie; łatwo powiedzieć, czy każdy stan wyraźnie rejestruje liczbę redukcji związanych z nim, a liczba ta jest potrzebne do wersji L(AL)R w praktyce i tak.
  • Błąd: jeśli nie jest możliwe przesunięcie lub zmniejszenie, deklarowany jest błąd składni.

Więc, jeśli wszyscy używają tej samej maszyny, jaki to ma sens?

Rzekomą wartością w lustrzance jest jej prostota w implementacji; nie musisz skanować możliwych redukcji sprawdzając zestawy lookahead, ponieważ istnieje co najwyżej jeden, i jest to jedyna realna akcja, jeśli nie ma wyjść przesunięcia ze stanu. Która redukcja applies mogą być dołączone specjalnie do stanu, więc maszyna parsująca lustrzanki nie musi na niego polować. W praktyce parsery L(AL)R obsługują użytecznie większy zestaw langauges i jest tak mało dodatkowej pracy do wdrożenia, że nikt nie implementuje lustrzanki inaczej niż jako ćwiczenie akademickie.

Różnica między LALR i LR ma związek z tabelą generator . Generatory parserów LR śledzą wszystkie możliwe redukcje z określonych stanów i ich precyzyjny wygląd; kończy się ze Stanami, w których każda redukcja jest związana z jej dokładnym zestawem spojrzeń z jej lewego kontekstu. To ma tendencję do budowania raczej dużych zestawów Stanów. Generatory parserów LALR są skłonne łączyć Stany, jeśli tabele GOTO i zestawy lookhead dla redukcji są zgodne i nie kolidują; daje to znacznie mniejszą liczbę stanów, za cenę nie być w stanie odróżnić pewnych sekwencji symboli, które LR może odróżnić. Tak więc, parsery LR mogą analizować większy zestaw języków niż parsery LALR, ale mają znacznie większe tabele parserów. W praktyce można znaleźć gramatyki LALR, które są na tyle blisko docelowych langauges, że rozmiar maszyny stanowej jest wart optymalizacji; miejsca, w których parser LR byłby lepszy, są obsługiwane przez doraźne sprawdzanie poza parserem.

Więc: wszystkie trzy używają tej samej maszyny. Lustrzanka jest "łatwa" w tym sensie, że można zignorować niewielką część maszyny, ale po prostu nie warto się kłopotać. LR parsuje szerszy zbiór langauges, ale stanowe stoły są dość duże. To pozostawia LALR jako praktyczny wybór.

Biorąc to wszystko pod uwagę, warto wiedzieć, że parsery GLR mogą analizować dowolny język bezkontekstowy, używając bardziej skomplikowanych maszyn, ale dokładnie tych samych tabel (w tym mniejszej wersji używanej przez LALR). Oznacza to, że GLR jest ściśle silniejszy niż LR, LALR i SLR; w zasadzie, jeśli możesz napisać standardową gramatykę BNF, GLR będzie analizować zgodnie z nią. Na różnica w mechanizmie polega na tym, że GLR jest skłonny wypróbować wiele Parsów, gdy występują konflikty między tabelą GOTO i zestawami or lookahead. (Jak GLR robi to efektywnie jest geniuszem [nie moim], ale nie zmieści się w tym poście).

To dla mnie niezmiernie przydatny fakt. Buduję analizatory programów i transformatory kodu i parsery są niezbędne, ale "nieciekawe"; interesującą pracą jest to, co robisz z analizowanym wynikiem, a więc skupiam się na pracy po parsowaniu. Korzystanie z GLR oznacza, że mogę stosunkowo łatwo budować działające gramatyki, w porównaniu do hakowania gramatyki, aby dostać się do lalr użytecznej postaci. Ma to duże znaczenie, gdy próbujesz radzić sobie z językami nieakademickimi, takimi jak C++ lub Fortran, gdzie dosłownie potrzebujesz tysięcy reguł, aby dobrze obsługiwać cały język, i nie chcesz spędzić życia próbując zhakować reguły gramatyczne, aby spełnić ograniczenia LALR (lub nawet LR).

Jako swego rodzaju słynny przykład, C++ jest uważany za niezwykle trudny do parse... przez facetów robiących parsowanie LALR. C++ jest proste do analizy przy użyciu maszyn GLR przy użyciu prawie zasad podanych w tylnej części podręcznika C++ reference manual. (Mam dokładnie taki parser i obsługuje nie tylko C++, ale także różne dialekty. Jest to możliwe tylko w praktyce, ponieważ używamy parsera GLR, IMHO).

[EDIT listopad 2011: rozszerzyliśmy nasz parser do obsługi wszystkich C++11. GLR to ułatwiło. Edytuj Sierpień 2014: teraz obsługa całego C++17. Nic się nie popsuło ani nie pogorszyło, GLR nadal jest miauczeniem kota.]

 48
Author: Ira Baxter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-18 04:16:19

Parsery LALR łączą podobne stany w gramatyce LR, aby stworzyć tabele Stanów parserów, które są dokładnie tego samego rozmiaru, co równoważne gramatyki SLR, które są zwykle o rząd wielkości mniejsze niż czyste tabele parsowania LR. Jednak dla gramatyk LR, które są zbyt złożone, aby mogły być LALR, te połączone stany powodują konflikty parserów lub wytwarzają parser, który nie w pełni rozpoznaje oryginalną gramatykę LR.

BTW, wspominam o tym kilka rzeczy w mojej tabeli parsowania MLR (k) algorytm TUTAJ .

Dodatek

Krótka odpowiedź jest taka, że tabele parsowania LALR są mniejsze, ale Maszyny parsera są takie same. Dana gramatyka LALR wytworzy znacznie większe tabele parsowania, jeśli wszystkie stany LR są generowane, z dużą ilością zbędnych (prawie identycznych) Stanów.

Tabele LALR są mniejsze, ponieważ podobne (nadmiarowe) Stany są scalane razem, skutecznie wyrzucając informacje kontekstowe / wyglądające, które kodują oddzielne Stany. Zaletą jest to, że otrzymujesz znacznie mniejsze tabele parsowania dla tej samej gramatyki.

Wadą jest to, że nie wszystkie gramatyki LR mogą być kodowane jako tabele LALR, ponieważ bardziej złożone gramatyki mają bardziej skomplikowane spojrzenia, co skutkuje dwoma lub więcej Stanami zamiast jednego połączonego stanu.

Główną różnicą jest to, że algorytm do tworzenia tabel LR przenosi więcej informacji między przejściami ze stanu do stanu, podczas gdy algorytm LALR nie. Więc algorytm LALR nie może powiedz, czy dany scalony stan powinien być rzeczywiście pozostawiony jako dwa lub więcej oddzielnych Stanów.

 17
Author: David R Tribble,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-13 22:07:11

Yet another answer (YAA).

Algorytmy parsowania dla SLR( 1), LALR(1) i LR(1) są identyczne, jak powiedział Ira Baxter,
jednak tabele parserów mogą być różne ze względu na algorytm generowania parserów.

Generator parserów SLR tworzy maszynę stanową LR (0) i oblicza look-aheads z gramatyki (FIRST I FOLLOW). Jest to podejście uproszczone i może zgłaszać konflikty, które tak naprawdę nie istnieją w maszynie stanu LR (0).

An LALR generator parserów tworzy maszynę stanową LR (0) i oblicza look-aheads z maszyny stanowej LR(0) (poprzez przejścia końcowe). Jest to poprawne podejście, ale czasami zgłasza konflikty, które nie istniałyby w maszynie stanowej LR(1).

Kanoniczny generator parserów LR oblicza maszynę stanową LR(1) I look-aheads są już częścią maszyny stanowej LR(1). Te tabele parserów mogą być bardzo duże.

Minimalny generator parsera LR oblicza stan LR(1) maszyna, ale łączy zgodne Stany podczas procesu, a następnie oblicza look-aheads z minimalnej maszyny stanu LR (1). Tabele parserów są tej samej wielkości lub nieco większe niż tabele parserów LALR, co daje najlepsze rozwiązanie.

LRSTAR 9.1 może generować Minimalne parsery LR (1) i LR (*) w C++. Zobacz też ten diagram co pokazuje różnicę między parserami LR.

[Full disclosure: LRSTAR is my product]

 12
Author: Paul B Mann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-12 13:23:54

Przypuśćmy, że parser bez lookahead chętnie parsuje ciągi dla Twojej gramatyki.

Na podanym przykładzie pojawia się ciąg dc, co robi? Czy redukuje to do S, ponieważ {[0] } jest poprawnym ciągiem wytworzonym przez tę gramatykę? A może próbowaliśmy przeanalizować bdc, ponieważ nawet to jest akceptowalny ciąg znaków?

Jako ludzie wiemy, że odpowiedź jest prosta, po prostu musimy pamiętać, czy właśnie parsowaliśmy b czy nie. Ale komputery są głupie :)

Ponieważ parser SLR(1) miał dodatkową moc nad LR (0) do wykonania lookahead, wiemy, że żadna ilość lookahead nie może nam powiedzieć, co mamy zrobić w tym przypadku; zamiast tego musimy spojrzeć wstecz w naszą przeszłość. W ten sposób na ratunek przychodzi kanoniczny parser LR. Pamięta kontekst z przeszłości.

Sposób, w jaki zapamiętuje ten kontekst, polega na tym, że dyscyplinuje się, że kiedykolwiek spotka b, zacznie kroczyć ścieżką do czytania bdc, jako jedna z możliwości. Więc kiedy widzi d wie, czy już kroczy ścieżką. Tak więc parser CLR(1) może robić rzeczy, których Parser SLR(1) nie może!

Ale teraz, ponieważ musieliśmy zdefiniować tak wiele ścieżek, Stany maszyny stają się bardzo duże!

Więc łączymy te same wyglądające ścieżki, ale zgodnie z oczekiwaniami może to spowodować problemy zamieszania. Jesteśmy jednak gotowi podjąć ryzyko kosztem zmniejszenia rozmiaru.

To jest parser LALR(1).


Teraz Jak to zrobić algorytmicznie.

Podczas rysowania zestawów konfiguracyjnych dla powyższego języka, zobaczysz konflikt shift-reduce w dwóch stanach. Aby je usunąć, możesz rozważyć SLR(1), który podejmuje decyzje patrząc na follow, ale zauważysz, że nadal nie będzie w stanie. W ten sposób można narysować zestawy konfiguracyjne ponownie, ale tym razem z ograniczeniem, że ilekroć obliczysz zamknięcie, dodawane dodatkowe produkcje muszą mieć ścisłe przestrzeganie (s). Poleć dowolny podręcznik o tym, co powinno być dalej.

 5
Author: Kang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-16 03:48:01

Podstawowa różnica między tabelami parserów generowanymi za pomocą SLR vs LR polega na tym, że akcje reduce są oparte na poniższym zestawie dla tabel SLR. Może to być zbyt restrykcyjne, ostatecznie powodując konflikt shift-reduce.

Parser LR, z drugiej strony, bazuje na decyzji reduce tylko na zestawie terminali, które mogą faktycznie śledzić redukcję nie-terminala. Ten zbiór terminali jest często właściwym podzbiorem poniższego zbioru takich Nie-terminali i dlatego ma mniej możliwość kolidowania z działaniami zmianowymi.

Parsery LR są z tego powodu potężniejsze. Tabele parsowania LR mogą być jednak bardzo duże.

Parser LALR zaczyna się od idei budowania tabeli parsowania LR, ale łączy wygenerowane stany w sposób, który powoduje znacznie mniejszy rozmiar tabeli. Minusem jest to, że mała szansa na konflikty byłaby wprowadzona dla niektórych gramatyk, których w przeciwnym razie uniknęłaby tabela LR.

Parsery LALR są nieco mniejsze potężne niż parsery LR, ale nadal potężniejsze niż parsery SLR. YACC i inne tego typu Generatory parserów używają LALR z tego powodu.

P. S. dla zwięzłości, SLR, LALR i LR powyżej naprawdę oznaczają SLR( 1), LALR(1) i LR(1), więc jeden token lookahead jest implikowany.

 4
Author: tgoneil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-30 03:13:44

Parsery SLR rozpoznają właściwy podzbiór gramatyk rozpoznawanych przez parsery LALR(1), Które z kolei rozpoznają właściwy podzbiór gramatyk rozpoznawanych przez parsery LR(1).

Każdy z nich jest skonstruowany jako maszyna stanowa, z każdym stanem reprezentującym pewien zestaw reguł produkcji gramatyki (i pozycji w każdym) podczas parsowania danych wejściowych.

Smocza Księga przykład gramatyki LALR(1), która nie jest SLR jest taki:

S → L = R | R
L → * R | id
R → L

Oto jeden z Stany dla tej gramatyki:

S → L•= R
R → L•

wskazuje pozycję parsera w każdej z możliwych produkcji. Nie wie, w której z produkcji jest, dopóki nie dotrze do końca i nie spróbuje zmniejszyć.

Tutaj, parser może albo przesunąć {[4] } lub zmniejszyć R → L.

Parser SLR (aka LR(0)) określiłby, czy może zmniejszyć, sprawdzając, czy następny symbol wejściowy znajduje się w follow set z R (tj. zestaw wszystkich terminali w gramatyce, która może następować R). Ponieważ = znajduje się również w tym zestawie, parser SLR napotyka konflikt shift-reduce.

Parser LALR (1) używa zestawu wszystkich terminali, które mogą podążać za tą konkretną produkcją R, która jest tylko $ (tj. koniec wejścia). Tak więc, nie ma konfliktu.

Jak zauważyli poprzedni komentatorzy, parsery LALR(1) mają taką samą liczbę stanów jak parsery SLR. Algorytm propagacji lookahead jest używany do przypięcia lookaheads do Produkcje stanu lustrzanki z odpowiednich stanów LR (1). Powstały parser LALR(1) może wprowadzać konflikty redukuj-redukuj, które nie są obecne w parserze LR(1), ale nie może wprowadzać konfliktów shift-reduce.

W twoim przykładzie, następujący stan LALR (1) powoduje konflikt shift-reduce w implementacji lustrzanki:

S → b d•a / $
A → d• / c

Symbol po / jest następującym zestawem dla każdej produkcji w parserze LALR (1). W lustrzance, follow(A) zawiera a, które również mogą być przesunięte.

 4
Author: Klaus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-05 05:25:50

Jedna prosta odpowiedź jest taka, że wszystkie gramatyki LR(1) są gramatykami LALR (1). W porównaniu do LALR(1), LR(1) ma więcej stanów w powiązanej maszynie skończonych Stanów(ponad dwukrotnie więcej stanów). I to jest główny powód, dla którego gramatyki LALR(1) wymagają więcej kodu do wykrywania błędów składniowych niż gramatyki LR(1). I jeszcze jedną ważną rzeczą, którą należy wiedzieć odnośnie tych dwóch gramatyk, jest to, że w gramatykach LR(1) możemy mieć mniej konfliktów reduce/reduce. Ale w LALR (1) jest wieksza mozliwosc redukcji / reduce konflikty.

 0
Author: Anil Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-12 05:08:54