Kann mir jemand ein einfaches Beispiel für LL-Analyse im Vergleich zu LR-Analyse geben?
Auf hoher Ebene besteht der Unterschied zwischen LL-Parsing und LR-Parsing darin, dass LL-Parser am Startsymbol beginnen und versuchen, Produktionen anzuwenden, um zur Zielzeichenfolge zu gelangen, während LR-Parser an der Zielzeichenfolge beginnen und versuchen, wieder am Start anzukommen Symbol.
Eine LL-Analyse ist eine Ableitung von links nach rechts ganz links. Das heißt, wir betrachten die Eingabesymbole von links nach rechts und versuchen, eine Ableitung ganz links zu konstruieren. Dies geschieht, indem Sie am Startsymbol beginnen und das Nichtterminal ganz links wiederholt erweitern, bis wir zur Zielzeichenfolge gelangen. Eine LR-Analyse ist eine Ableitung von links nach rechts ganz rechts. Dies bedeutet, dass wir von links nach rechts scannen und versuchen, eine Ableitung ganz rechts zu erstellen. Der Parser wählt kontinuierlich eine Teilzeichenfolge der Eingabe aus und versucht, sie auf ein Nichtterminal zurückzusetzen.
Während einer LL-Analyse wählt der Parser kontinuierlich zwischen zwei Aktionen:
Als Beispiel für diese Grammatik:
int
Dann würde int + int + int
ein LL (2) -Parser (der zwei Lookahead-Token verwendet) die Zeichenfolge wie folgt analysieren:
Production Input Action
---------------------------------------------------------
S int + int + int Predict S -> E
E int + int + int Predict E -> T + E
T + E int + int + int Predict T -> int
int + E int + int + int Match int
+ E + int + int Match +
E int + int Predict E -> T + E
T + E int + int Predict T -> int
int + E int + int Match int
+ E + int Match +
E int Predict E -> T
T int Predict T -> int
int int Match int
Accept
Beachten Sie, dass wir in jedem Schritt das Symbol ganz links in unserer Produktion betrachten. Wenn es sich um ein Terminal handelt, passen wir es an, und wenn es sich um ein Nicht-Terminal handelt, sagen wir voraus, wie es aussehen wird, indem wir eine der Regeln auswählen.
In einem LR-Parser gibt es zwei Aktionen:
Beispielsweise könnte ein LR (1) -Parser (mit einem Token Lookahead) dieselbe Zeichenfolge wie folgt analysieren:
Workspace Input Action
---------------------------------------------------------
int + int + int Shift
int + int + int Reduce T -> int
T + int + int Shift
T + int + int Shift
T + int + int Reduce T -> int
T + T + int Shift
T + T + int Shift
T + T + int Reduce T -> int
T + T + T Reduce E -> T
T + T + E Reduce E -> T + E
T + E Reduce E -> T + E
E Reduce S -> E
S Accept
Es ist bekannt, dass die beiden von Ihnen erwähnten Parsing-Algorithmen (LL und LR) unterschiedliche Eigenschaften aufweisen. LL-Parser sind in der Regel einfacher von Hand zu schreiben, aber sie sind weniger leistungsfähig als LR-Parser und akzeptieren einen viel kleineren Satz von Grammatiken als LR-Parser. LR-Parser gibt es in vielen Geschmacksrichtungen (LR (0), SLR (1), LALR (1), LR (1), IELR (1), GLR (0) usw.) und sind weitaus leistungsfähiger. Sie sind in der Regel auch viel komplexer und werden fast immer von Tools wie yacc
oder generiert bison
. LL-Parser gibt es auch in vielen Geschmacksrichtungen (einschließlich LL (*), das vom ANTLR
Tool verwendet wird), obwohl LL (1) in der Praxis am häufigsten verwendet wird.
Als schamloser Plug, wenn Sie mehr über das Parsen von LL und LR erfahren möchten, habe ich gerade einen Compilerkurs unterrichtet und auf der Kurswebsite einige Handouts und Vorlesungsfolien zum Parsen bereitgestellt. Ich würde gerne auf einen von ihnen näher eingehen, wenn Sie denken, dass es nützlich wäre.
Josh Haberman behauptet in seinem Artikel LL und LR Parsing Demystified , dass LL Parsing direkt der polnischen Notation entspricht , während LR der umgekehrten polnischen Notation entspricht . Der Unterschied zwischen PN und RPN ist die Reihenfolge des Durchlaufens des Binärbaums der Gleichung:
Laut Haberman zeigt dies den Hauptunterschied zwischen LL- und LR-Parsern:
Eine ausführliche Erklärung, Beispiele und Schlussfolgerungen finden Sie in Habermans Artikel .
quelle
Das LL verwendet Top-Down, während das LR den Bottom-Up-Ansatz verwendet.
Wenn Sie eine Programmiersprache analysieren:
quelle
Die LL-Analyse ist im Vergleich zu LR behindert. Hier ist eine Grammatik, die für einen LL-Parser-Generator ein Albtraum ist:
Ein FunctionDef sieht bis zum ';' genau wie ein FunctionDecl aus oder '{' wird angetroffen.
Ein LL-Parser kann nicht zwei Regeln gleichzeitig verarbeiten, daher muss er entweder FunctionDef oder FunctionDecl auswählen. Aber um zu wissen, was richtig ist, muss es nach einem ';' suchen. oder '{'. Zur Zeit der Grammatikanalyse scheint der Lookahead (k) unendlich zu sein. Zur Analysezeit ist es endlich, kann aber groß sein.
Ein LR-Parser muss nicht nachsehen, da er zwei Regeln gleichzeitig verarbeiten kann. So können LALR (1) -Parser-Generatoren diese Grammatik problemlos verarbeiten.
Angesichts des Eingabecodes:
Ein LR-Parser kann das analysieren
ohne sich darum zu kümmern, welche Regel erkannt wird, bis sie auf ein ';' oder ein '{'.
Ein LL-Parser wird am 'int' aufgehängt, weil er wissen muss, welche Regel erkannt wird. Deshalb muss es nach einem ';' suchen. oder '{'.
Der andere Albtraum für LL-Parser ist die Rekursion in einer Grammatik. Linke Rekursion ist eine normale Sache in Grammatiken, kein Problem für einen LR-Parser-Generator, aber LL kann damit nicht umgehen.
Sie müssen also Ihre Grammatiken mit LL auf unnatürliche Weise schreiben.
quelle
Ableitung ganz links Beispiel: Eine kontextfreie Grammatik G enthält die Produktionen
z → xXY (Regel: 1) X → Ybx (Regel: 2) Y → bY (Regel: 3) Y → c (Regel: 4)
Berechnen Sie den String w = 'xcbxbc' mit der Ableitung ganz links.
z ⇒ xXY (Regel: 1) ⇒ xYbxY (Regel: 2) ⇒ xcbxY (Regel: 4) ⇒ xcbxbY (Regel: 3) ⇒ xcbxbc (Regel: 4)
Beispiel für die Ableitung ganz rechts: K → aKK (Regel: 1) A → b (Regel: 2)
Berechnen Sie den String w = 'aababbb' mit der Ableitung ganz rechts.
K ⇒ aKK (Regel: 1) ⇒ aKb (Regel: 2) ⇒ aaKKb (Regel: 1) ⇒ aaKaKKb (Regel: 1) ⇒ aaKaKbb (Regel: 2) ⇒ aaKabbb (Regel: 2) ⇒ aababbb (Regel: 2)
quelle