• Keine Ergebnisse gefunden

3.7 Stand der Forschung

4.1.1 Kontextfreie Grammatiken

Es sei𝙶 = (Γ, 𝚅, 𝚂, 𝙿)eine kontextfreie Grammatik und𝙻(𝙶)eine Sprache, die durch𝙶beschrieben wird. Werden Grammatiken in einem Compiler verwendet, sind zwei Probleme von besonderem Interesse [94].

Membership Problem

Definition 4.1.10 (Membership Problem) Gegeben sei ein String𝚡 ∈Γ. Die Lösung desMembership Problemsist die Antwort auf die Frage, ob𝚡 ∈ 𝙻(𝙶)gilt.

Das Lösen desMembership Problemsbeantwortet die Frage, ob ein Eingabestring zu einer Sprache gehört. Bei kontextfreien Grammatiken ist das Lösen des Mem-bership Problems ein elementarer Bestandteil der lexikalischen Analyse [2]. Dabei werden durch den lexikalischen Scanner (Lexer) Zeichen im Quellcode zu Lex-emenzusammengefasst. Zu jedem Lexem gibt der Lexer ein Terminalsymbol aus, dass in diesem Zusammenhang auch alsTokenbezeichnet wird42. Token und Lexeme repräsentieren die Bezeichner, Schlüsselwörter und Operatoren der Eingabesprache.

42Lexeme wären z. B. Eingabesymbole wie-1,123,123.45, die alle durch einen Token wie z. B.

NUMBERrepräsentiert werden.

Kapitel 4 CRYPTO-COMPILER

Satz 4.1.3 Das Membership Problem ist allgemein unentscheidbar für Typ-0 Gram-matiken und entscheidbar für GramGram-matiken vom Typ-1. Dabei lässt sich für Typ-1 und Typ-2 Grammatiken eine Lösung in polynomialer und bei Typ-3-Grammatiken in linearer Laufzeit finden.

Das Membership Problem ist bei regulären Grammatiken in linearer Laufzeit lös-bar. Deshalb wird die Grammatik eines Lexers in der Regel als Typ-3 Grammatik angegeben [152].

Parsing Problem

Definition 4.1.11 (Parsing Problem) Gegeben sei ein String𝚡 ∈ 𝙻(𝙶). Das Parsing Problemist die Aufgabe, durch Anwendung der Regeln aus𝙿vom Start-symbol𝚂nach𝚡abzuleiten.

EinParserversucht das Parsing Problem durch Anwendung der Ableitungsregeln zu lösen (syntaktische Analyse). Dieser Vorgang ist äquivalent zur Erstellung eines Parsebaumes, der die grammatische Struktur der Token aus dem Eingabestroms wiedergibt43. Die inneren Knoten des Parsebaumes bestehen aus Nicht-Terminal-symbolen und die Blätter sind entweder der leere Stringϵoder Terminalsymbole.

Die Wurzel des Baumes entspricht dem Startsymbol𝚂. Wird der Parser in einem Compiler verwendet, führt der Parser bei der Anwendung der Ableitungsre-gelnsemantische Aktionenaus. Diese semantischen Aktionen erzeugen in einem Compiler den Zwischencode, der in die Zielsprache übersetzt wird [2].

Parser lassen sich alsTop-down-oderBottom-up-Parserklassifizieren. Bei einem Top-down Parser werden (virtuell) Ableitungsbäume von der Wurzel zu den Blättern erstellt. Bei einem Bottom-up Parser ist es umgekehrt.

Mit universellen Parsern wie dem CYK (Cocke-Younger-Kasami) Algorithmus (Bottom-up Parser) [77] oder dem Algorithmus von Earley (Top-down Parser) [57] lassen sich alle Eingaben parsen, die zu einer kontextfreien Sprache gehören.

Die Parser haben eine Worst-Case Laufzeit von𝒪(𝚗3), wobei𝚗der Länge des Strings𝚡entspricht.

In der Praxis kommen üblicherweise LL- bzw. LR-Parser zum Einsatz, da mit ihnen in der Regel eine lineare Laufzeit erreicht werden kann. Allerdings eignen sie sich nur zum Parsen von Sprachen, die durch Untergruppen kontextfreier Grammatiken beschrieben werden [22].

DerLL-Parserist ein Top-down-Parser, der aus einem Eingabestring, einem Stack und einer Parsing-Tabelle besteht. Die Elemente des Eingabestrings entsprechen Token, d. h. Terminalsymbolen, während die Elemente auf dem Stack

Terminal-43Ein Parser erzeugt nicht zwangsläufig einen Parsebaum.

Formale Grammatiken und Sprachen 4.1

und Nicht-Terminalsymbolen entsprechen. Die Parsing-Tabelle gibt an, welche Ableitungsregel aus𝙿in Abhängigkeit des aktuellen Tokens im Eingabestring auf das oberste Stackelement angewendet werden soll.

Ein LL-Parser parst den Eingabestring von links nach rechts, was durch die An-gabe des ersten „L“ in „LL“ zum Ausdruck gebracht wird. Das zweite „L“ bedeutet, dass der Parser eineLinksreduktionvornimmt, d. h. dass der Parser immer das am weitesten links stehende Nicht-Terminalsymbol ersetzt. Schaut ein LL-Parser beim Parsen𝚔Tokens44 voraus, wird dieser alsLL(𝚔)-Parserbezeichnet und ver-arbeitet Sprachen, die durch die sogenanntenLL(𝚔)-Grammatikenbeschrieben werden. Ein LL-Parser hat eine lineare Laufzeit, wenn der Parser für eine kon-textfreie LL(𝚔)-Grammatik immer durch Untersuchung der nächsten𝚔Token in der Eingabe (𝚔ist begrenzt) entscheidet, welche Ableitungsregel zu wählen ist [2].

LL-Parser sind einfach im Aufbau und lassen sich mithilfe von Parser-Generato-ren wie z. B. ANTLR automatisch erzeugen [133] [164].

LR-Parsergehören zur Gruppe der Bottom-up-Parser. Sie sind im Gegensatz zu LL-Parsern komplexer im Aufbau, haben aber den Vorteil, dass Syntaxfehler früher erkannt werden können [2]. Im Gegensatz zum LL-Parser nimmt der LR-Parser eineRechtsreduktionenvor, d. h. der Parser versucht immer, das am wei-testen rechts stehende Nicht-Terminalsymbol mithilfe einer Ableitungsregel zu ersetzen. Wie beim LL-Parser kann der LR(𝚔)-Parser im Eingabestring𝚔Token vorausschauen, um die richtige Ableitungsregel zu finden [40].

Der LR-Parser hat einen Eingabestring, eine Parsingtabelle mit Ableitungsregeln und einen Stack. Das oberste Element im Stack repräsentiert den aktuellen Zu-stand des Parsers. Die zwei wesentlichen Schritte sind der ZuZu-standswechsel in Abhängigkeit des nächsten Tokens im Eingabestring (shift) und das Reduzieren der Elemente auf dem Stack in Abhängigkeit der Ableitungsregeln (reduce). Im Reduce-Schritt werden so viele Elemente vom Stack genommen, wie eine be-stimmte Regel Symbole auf der rechten Seite hat. Danach wechselt der Parser seinen Zustand und legt diesen auf den Stack. LR-Parser können mehr Sprachen parsen als LL-Parser, da später entschieden wird, ob es zu einer Eingabe eine passende Produktionsregel gibt [100].

Die ursprüngliche Version eines LR-Parsers von Donald Knuth zeichnet sich durch einen hohen Speicherverbrauch aus. Optimierungen wie derLALR-Parser (Lockahead LR-Parser)oderSLR-Parser (Simple LR-Parser)sind spezielle LR-Parser, die effizienter sind [52].

Bei LR-Parsern können zwei Arten von Konflikten auftreten:

1. Shift-Reduce-Konflikt: Der Parser kann entweder das Symbol aus der Ein-gabe nehmen oder die obersten Elemente des Stacks entfernen.

2. Reduce-Reduce-Konflikt: Eine Reduktion kann durch mindestens zwei Pro-duktionsregeln erfolgen.

44𝚔wird alsLockaheadbezeichnet.

Kapitel 4 CRYPTO-COMPILER

Ein nicht-deterministischer Parser wie derTomita Parserfür kontextfreie Gram-matiken löst Konflikte durch Verzweigungen bzw. Klonen des Parsers [169].

Jeder geklonte Parser endet entweder in einem Parser-Fehler und wird been-det oder vereint sich mit einem anderen Parser, weil beide Parser denselben Reduktionsschritt ausführen. Der Tomita Parser stellt eine Verallgemeinerung des LR(𝚔)-Parsers dar und wird deswegen auch GLR(𝚔)-Parser (Generalized LR(𝚔)-Parsers) genannt [165]. Die Ausführungszeit von deterministischen LR-Parsern ist für kontextfreie Sprachen linear. Da GLR-Parser determinis-tisch sind, ist die Ausführungszeit des Parsers aufgrund der Verzweigungen nicht-linear. Können allerdings Konflikte vom Parser schnell gelöst werden, wirkt sich die Verwendung eines GLR-Parsers kaum auf die Ausführungszeit aus und die Vorteile wie z. B. die Verwendung einer einfacheren Grammatik überwiegen.