LL jest zwykle bardziej wydajną techniką analizy niż rekursywne zejście. W rzeczywistości, naiwny parser rekursywno-zejścia będzie w rzeczywistości najgorszym przypadkiem O (k^n) (gdzie n jest wielkością wejściową). Niektóre techniki, takie jak zapamiętywanie (które daje parser Packrat) mogą to poprawić, a także rozszerzyć klasę gramatyk akceptowaną przez analizator składni, ale zawsze istnieje kompromis dotyczący przestrzeni. Parserzy LL są (według mojej wiedzy) zawsze liniowy.
Z drugiej strony, masz intuicję, że parsery rekursywno-opadowe mogą obsłużyć większą klasę gramatyk niż LL. Zejście rekursywne może obsłużyć dowolną gramatykę, która jest LL (*) (to znaczy nieograniczona wcześniejsza), a także mały zestaw niejednoznacznych gramatyk. Dzieje się tak dlatego, że rekurencyjne pochodzenie jest właściwie bezpośrednio zakodowaną implementacją PEG lub Parser Expression Grammar(s). W szczególności operator rozdzielający (a | b
) nie jest przemienny, co oznacza, że a | b
nie jest równy b | a
. Parser rekursywno-spadkowy spróbuje każdej alternatywy w kolejności. Więc jeśli a
dopasuje dane wejściowe, to odniesie sukces, nawet jeśli b
będzie miał wartość dopasowaną do wejścia. Pozwala to na rozwiązywanie klasycznych "długodystansowych" niejednoznaczności, takich jak zwisający problem else
po prostu przez prawidłowe uporządkowanie disjunctions.
Z tym wszystkim, jest możliwe do realizacji parser LL (k) przy użyciu rekursywnego pochodzenia, tak aby działał w czasie liniowym. Odbywa się to przez zasadniczo ustawiając predykcyjne zestawy, tak aby każda procedura parse określiła odpowiednią produkcję dla danego wejścia w stałym czasie. Niestety, taka technika eliminuje obsługę całej klasy gramatyk. Po przejściu do analizy predykcyjnej problemy takie jak zwisanie else
nie są już rozwiązywane z taką łatwością.
Jeśli chodzi o powód, dla którego LL zostałby wybrany jako rekurencyjny, to jest to głównie kwestia wydajności i łatwości konserwacji. Parsery rekurencyjno-descentowe są znacznie łatwiejsze w implementacji, ale zazwyczaj są trudniejsze do utrzymania, ponieważ gramatyka, którą reprezentują, nie istnieje w żadnej deklaratywnej formie. Większość nietrywialnych przypadków użycia parsera wykorzystuje generator parsera, taki jak ANTLR lub Bison. Przy takich narzędziach nie ma znaczenia, czy algorytm jest zakodowany bezpośrednio przez rekursywne pochodzenie, czy sterowany tabelą LL (k).
W interesie warto również zajrzeć do recursive-ascent, który jest algorytmem parsowania zakodowanym bezpośrednio po modulacji rekurencyjnego pochodzenia, ale zdolnym do obsługi dowolnej gramatyki LALR. Chciałbym również zagłębić się w parser combinators, które są funkcjonalnym sposobem komponowania parserów recursive-descent razem.
Hej, czy możesz wyjaśnić. Piszesz _seem, aby był parserem LL (używając tabeli stosu/parsowania) i parsera rekursywnego zrzutu (po prostu używając rekursji) ._. [Ta odpowiedź] (https://stackoverflow.com/a/45977727/2545680) stwierdza, że wszystkie parsery zstępujące to parsery LL. Jestem zdezorientowany –
@MaximKoretskyi To na pewno nieprawda. Parsery LL są podzbiorem analizatorów składających się z góry na dół. – Noldorin
dziękuję, czy może chcesz umieścić swoją odpowiedź na moje pytanie? –