SLR-, LALR- und LR-Parser können alle mit genau denselben tischgesteuerten Maschinen implementiert werden.
Grundsätzlich sammelt der Parsing-Algorithmus das nächste Eingabetoken T und konsultiert den aktuellen Status S (und die zugehörigen Lookahead-, GOTO- und Reduktionstabellen), um zu entscheiden, was zu tun ist:
- SHIFT: Wenn die aktuelle Tabelle auf dem Token T SHIFT sagt, wird das Paar (S, T) auf den Analysestapel verschoben, und der Status wird entsprechend den Angaben in der GOTO-Tabelle für das aktuelle Token geändert (z. B. GOTO (T)). ) wird ein weiteres Eingabetoken T 'abgerufen und der Vorgang wiederholt
- REDUZIEREN: Jeder Zustand hat 0, 1 oder viele mögliche Reduzierungen, die im Zustand auftreten können. Wenn der Parser LR oder LALR ist, wird das Token anhand von Lookahead-Sets auf alle gültigen Reduzierungen für den Status überprüft. Wenn das Token mit einem Lookahead-Satz für eine Reduktion für die Grammatikregel G = R1 R2 übereinstimmt. Rn tritt eine Stapelreduktion und -verschiebung auf: Die semantische Aktion für G wird aufgerufen, der Stapel wird n (von Rn) mal gepoppt, das Paar ( S, G) wird auf den Stapel geschoben, der neue Zustand S 'wird auf GOTO (G) gesetzt und der Zyklus wird mit demselben Token T wiederholt. Wenn der Parser ein SLR-Parser ist, gibt es höchstens eine Reduktionsregel für die Zustand und so kann die Reduktionsaktion blind durchgeführt werden, ohne zu suchen, welche Reduktion gilt. Für einen SLR-Parser ist es hilfreich zu wissen, ob dies der Fall isteine Reduzierung oder nicht; Dies ist leicht zu erkennen, wenn jeder Zustand die Anzahl der damit verbundenen Reduzierungen explizit aufzeichnet und diese Anzahl in der Praxis ohnehin für die L (AL) R-Versionen erforderlich ist.
- FEHLER: Wenn weder SHIFT noch REDUCE möglich sind, wird ein Syntaxfehler deklariert.
Also, wenn sie alle die gleichen Maschinen benutzen, worum geht es dann?
Der angebliche Wert von SLR ist die einfache Implementierung. Sie müssen die möglichen Reduzierungen nicht durchsuchen, um Lookahead-Sets zu überprüfen, da es höchstens eine gibt. Dies ist die einzig praktikable Aktion, wenn keine SHIFT-Exits aus dem Status vorhanden sind. Welche Ermäßigung gilt, kann spezifisch an den Staat geknüpft werden, sodass die SLR-Parsing-Maschinerie nicht danach suchen muss. In der Praxis verarbeiten L (AL) R-Parser einen nützlich größeren Satz von Sprachen und sind so wenig zusätzliche Arbeit zu implementieren, dass niemand SLR implementiert, außer als akademische Übung.
Der Unterschied zwischen LALR und LR hat mit der Tabelle zu tun Generator. LR-Parser-Generatoren verfolgen alle möglichen Reduzierungen aus bestimmten Zuständen und deren genaue Lookahead-Einstellung. Sie erhalten Zustände, in denen jede Reduzierung mit dem genauen Lookahead-Satz aus dem linken Kontext verknüpft ist. Dies führt tendenziell zu ziemlich großen Mengen von Staaten. LALR-Parser-Generatoren sind bereit, Zustände zu kombinieren, wenn die GOTO-Tabellen und Lookhead-Sets für Reduzierungen kompatibel sind und keine Konflikte verursachen. Dies erzeugt eine erheblich geringere Anzahl von Zuständen, um bestimmte Symbolsequenzen, die LR unterscheiden kann, nicht unterscheiden zu können. LR-Parser können also einen größeren Satz von Sprachen analysieren als LALR-Parser, haben jedoch sehr viel größere Parsertabellen. In der Praxis kann man LALR-Grammatiken finden, die nahe genug an den Zielsprachen liegen, sodass die Größe der Zustandsmaschine optimiert werden sollte.
Also: Alle drei benutzen die gleiche Maschinerie. SLR ist "einfach" in dem Sinne, dass Sie ein kleines Stück der Maschine ignorieren können, aber es ist einfach nicht die Mühe wert. LR analysiert einen breiteren Satz von Sprachen, aber die Statustabellen sind in der Regel ziemlich groß. Damit bleibt LALR die praktische Wahl.
Nach alledem ist es wichtig zu wissen, dass GLR-Parser jede kontextfreie Sprache analysieren können, indem sie kompliziertere Maschinen verwenden, aber genau dieselben Tabellen (einschließlich der von LALR verwendeten kleineren Version). Dies bedeutet, dass GLR strikt leistungsfähiger ist als LR, LALR und SLR. Wenn Sie eine Standard-BNF-Grammatik schreiben können, analysiert GLR diese entsprechend. Der Unterschied in der Maschinerie besteht darin, dass GLR bereit ist, mehrere Analysen durchzuführen, wenn Konflikte zwischen der GOTO-Tabelle und / oder Lookahead-Sets bestehen. (Wie GLR dies effizient macht, ist einfach genial [nicht meins], passt aber nicht in diesen SO-Beitrag).
Das ist für mich eine enorm nützliche Tatsache. Ich baue Programmanalysatoren und Codetransformatoren und Parser sind notwendig, aber "uninteressant"; Die interessante Arbeit ist, was Sie mit dem analysierten Ergebnis machen, und daher liegt der Fokus auf der Arbeit nach dem Parsen. Mit GLR kann ich relativ einfach funktionierende Grammatiken erstellen, verglichen mit dem Hacken einer Grammatik, um in eine LALR-verwendbare Form zu gelangen. Dies ist sehr wichtig, wenn Sie versuchen, sich mit nicht-akademischen Sprachen wie C ++ oder Fortran zu befassen, bei denen Sie buchstäblich Tausende von Regeln benötigen, um die gesamte Sprache gut zu handhaben, und Sie nicht Ihr Leben damit verbringen möchten, die Grammatikregeln zu hacken die Einschränkungen von LALR (oder sogar LR) erfüllen.
Als eine Art berühmtes Beispiel wird C ++ als extrem schwer zu analysieren angesehen ... von Leuten, die LALR-Analyse durchführen. C ++ lässt sich mit GLR-Maschinen ganz einfach anhand der Regeln analysieren, die im hinteren Teil des C ++ - Referenzhandbuchs angegeben sind. (Ich habe genau einen solchen Parser, der nicht nur Vanilla C ++, sondern auch eine Vielzahl von Hersteller-Dialekten verarbeitet. Dies ist nur in der Praxis möglich, da wir einen GLR-Parser verwenden, IMHO).
[EDIT November 2011: Wir haben unseren Parser auf C ++ 11 erweitert. GLR hat das viel einfacher gemacht. BEARBEITEN Aug 2014: Jetzt wird C ++ 17 vollständig verarbeitet. Nichts ist kaputt gegangen oder schlimmer geworden, GLR ist immer noch der Miau der Katze.]