780 likes | 928 Views
Compiler und Interpreter. Klaus Becker 2010. Compiler und Interpreter. Teil 1. Syntax und Semantik im Überblick. Karol / Myka. Karol. MyKa(rol). Aufgabe. links while nichtVorWand: ziegelHinlegen schritt #while. if nichtVorWand: if nichtVorZiegel: schritt schritt
E N D
Compiler und Interpreter Klaus Becker 2010
Teil 1 Syntax und Semantik im Überblick
Karol / Myka Karol MyKa(rol)
Aufgabe links while nichtVorWand: ziegelHinlegen schritt #while if nichtVorWand: if nichtVorZiegel: schritt schritt schritt schritt else: ziegelHinlegen #if else: links #if Versuche anhand weiterer Tests die Regeln der Sprache MyKa herauszufinden: (a) Wie können Programme der Sprache aufgebaut werden? markeSetzen schritt while nichtAufMarke: while nichtVorWand: schritt #while links #while
Aufgabe links while nichtVorWand: ziegelHinlegen schritt #while if nichtVorWand: if nichtVorZiegel: schritt schritt schritt schritt else: ziegelHinlegen #if else: links #if Versuche anhand weiterer Tests die Regeln der Sprache MyKa herauszufinden: (b) Welche Bedeutung haben die Sprachkonstrukte? markeSetzen schritt while nichtAufMarke: while nichtVorWand: schritt #while links #while
Exkurs - MyKa Anweisung schritt links rechts ziegelHinlehen ziegelAufheben markeSetzen markeLoeschen pass links while nichtVorWand: ziegelHinlegen schritt #while Bedeutung einen Schritt vorwärts bewegen - sofern möglich um 90° nach links drehen um 90° nach rechts drehen einen Ziegen in das vor dem Roboter liegende Feld hinlegen - sofern möglich einen Ziegen von dem vor dem Roboter liegenden Feld aufheben - sofern möglich eine Marke auf das Feld setzen, auf dem sich der Roboter befindet eine Marke löschen, die sich auf dem Feld des Roboters befindet - sofern möglich mache nichts if nichtVorWand: if nichtVorZiegel: schritt schritt schritt schritt else: ziegelHinlegen #if else: links #if markeSetzen schritt while nichtAufMarke: while nichtVorWand: schritt #while links #while
Exkurs - MyKa Bedingung vorWand nichtVorWand vorZiegel nichtVorZiegel aufMarke nichtAufMarke links while nichtVorWand: ziegelHinlegen schritt #while Bedeutung Befindet sich der Roboter vor einer Wand? Befindet sich der Roboter nicht vor einer Wand? Befindet sich der Roboter vor einem Ziegel? Befindet sich der Roboter nicht vor einem Ziegel? Befindet sich der Roboter auf einer Marke? Befindet sich der Roboter nicht auf einer Marke? if nichtVorWand: if nichtVorZiegel: schritt schritt schritt schritt else: ziegelHinlegen #if else: links #if markeSetzen schritt while nichtAufMarke: while nichtVorWand: schritt #while links #while
Exkurs - MyKa Kontrollstruktur Sequenz: "Anweisung" "Anweisung" ... "Anweisung" Fallunterscheidung: if "Bedingung": "Anweisungssequenz" else: "Anweisungssequenz" #if Wiederholung: while "Bedingung": "Anweisungssequenz" #while links while nichtVorWand: ziegelHinlegen schritt #while Bedeutung Sequenz: Führe die Anweisungen der Reihe nach aus. Fallunterscheidung: Wenn die Bedingung erfüllt ist, dann führe die erste Anweisungssequenz aus, ansonsten die zweite Anweisungssequenz. Wiederholung: Solange die Bedingung erfüllt ist, führe die Anweisungssequenz aus. if nichtVorWand: if nichtVorZiegel: schritt schritt schritt schritt else: ziegelHinlegen #if else: links #if markeSetzen schritt while nichtAufMarke: while nichtVorWand: schritt #while links #while
Exkurs - MyKa Syntax und Semantik - informell oder formal? Eine informelle Beschreibung von Syntax und Semantik liefert einen ersten Überblick über die Struktur und Bedeutung der Sprachelemente der Programmiersprache. Bei einer informellen Beschreibung bleiben meist aber noch Fragen offen. Im Fall der Programmiersprache MyKa ist beispielsweise noch nicht geklärt, ob es auch leere Anweisungssequenzen geben kann (z.B. in der Anweisung while nichtVorWand: #while). Ungeklärt ist auch noch, wie sich ein mehrfaches Setzen einer Marke auswirkt. Alle diese Fragen werden geklärt, wenn Syntax und Semantik präzise beschrieben werden. Für die Programmiersprache MyKa wird das in den folgenden Abschnitten nachgeholt. links while nichtVorWand: ziegelHinlegen schritt #while if nichtVorWand: if nichtVorZiegel: schritt schritt schritt schritt else: ziegelHinlegen #if else: links #if markeSetzen schritt while nichtAufMarke: while nichtVorWand: schritt #while links #while
Fachkonzept - Syntax Die Syntax einer Sprache beschreibt, welche Kombinationen von Zeichen fehlerfreie Programme der Sprache bilden. myka_syn = {Programm1, Programm2, ...} Eine Präzisierung dieser Menge kann z.B. mit Hilfe einer Grammatik vorgenommen werden. links while nichtVorWand: ziegelHinlegen schritt #while if nichtVorWand: if nichtVorZiegel: schritt schritt schritt schritt else: ziegelHinlegen #if else: links #if Programmx markeSetzen schritt while nichtAufMarke: while nichtVorWand: schritt #while links #while
Fachkonzept - Semantik Die Semantik einer Sprache beschreibt, welche Bedeutung den Einheiten der Sprache zugeordnet wird. myka_sem: (programm, zustand_vorher) --> zustand_nachher Eine Präzisierung dieser Zuordnung kann z.B. mit Hilfe eines Interpreters vorgenommen werden.
Teil 2 Scanner und Parser im Überblick
Myka syntaktisch korrektes Programm Programm mit Syntaxfehler Struktur-darstellung d. Programms
Aufgabe Untersuche verschiedene Programme (siehe inf-schule) auf syntaktische Korrektheit. Schaue dir die Quelltexte zunächst genau an und stelle Vermutungen über Syntaxfehler auf. Gib die vorgegebenen MyKa-Programm-Quelltexte in das linke obere Fenster ein. Erzeuge mit der Schaltfläche [scannen / parsen] ein MyKaList-Programm - das ist eine mit Hilfe von Listen erstellte strukturierte Darstellung des MyKa-Programms. In welchen Fällen funktioniert das, in welchen Fällen nicht? Die Programme 3, 4 und 5 sind aus unterschiedlichen Gründen syntaktisch nicht korrekt. Gegen welche Regeln wird hier wohl verstoßen?
Aufgabe links while nichtVorWand: ziegelHinlegen schritt #while Versuche anhand weiterer Tests zu erschließen, wie ein syntaktisch korrektes MyKa-Programm mit Hilfe von Listen strukturiert als MyKaList-Programm dargestellt wird. Bei der Erzeugung eines MyKaList-Programms werden zusätzliche Informationen über den Analysevorgang ausgegeben. Diese Informationen im Detail zu verstehen ist schwierig. Vielleicht hast du trotzdem eine Idee, um was es hier geht. Scanner erzeugt: LexToken(ELANW,'links',9,0) LexToken(WH,'while',10,6) LexToken(BED,'nichtVorWand',10,12) LexToken(DP,':',10,24) LexToken(ELANW,'ziegelHinlegen',11,28) LexToken(ELANW,'schritt',12,45) LexToken(WH_ENDE,'#while',13,53) Parser erzeugt: [ ['links'], ['while', ['nichtVorWand'], [['ziegelHinlegen'], ['schritt']]] ]
Fachkonzept - Scanner Ein (lexikalischer) Scanner ist eine Programmeinheit, die eine Zeichenfolge nach vorgegebenen Mustern in lexikalische Einheiten zerlegt oder anzeigt, dass eine solche Zerlegung nicht möglich ist.
Fachkonzept - Parser Ein Parser ist eine Programmeinheit, die analysiert, ob eine Tokenfolge zu einem Programmquelltext vorgegebene Grammatikregeln befolgt. Ist das nicht der Fall, so wird eine Fehlermeldung erzeugt. Andernfalls wird eine strukturierte Darstellung des Programmquelltextes erzeugt, die die von den Grammatikregeln verlangte syntaktische Struktur widerspiegelt.
Teil 3 Interpreter und Compiler im Überblick
Myka Ausführung von MyKaList-Programmen Übersetzen von MyKaList-Programmen
Aufgabe Ein MyKaList-Programm kann man mit der Schaltfläche [Anw. ausführen] schrittweise ausführen. Probiere das mit verschiedenen Testprogrammen aus und beobachte die Entwicklung im MyKaList-Fenster. Beachte, dass der MyKaList-Editor nur zum Anzeigen von MyKaList-Programmen dient. Veränderungen an MyKaList-Programmen kann man hier nicht vornehmen. Ausführung von MyKaList-Programmen
Aufgabe Mit der Schaltfläche [Code erzeugen] lässt sich ein MyKaList-Programm in ein sog. MyKaGoto-Programm übersetzen. Probiere das mit verschiedenen Testprogrammen aus. Versuche mit Hilfe gezielter Experimente die Syntax und Semantik der Code-Sprache MyKaGoto zu erschließen. Übersetzen von MyKaList-Programmen
Fachkonzept - Interpreter Ein Interpreter für eine Programmiersprache ist ein universelles Programm (Algorithmus), das jedes Programm der zu interpretierenden Programmiersprache schrittweise ausführen kann. MyKaList-Programm
Exkurs - MyKaGoto links while nichtVorWand: ziegelHinlegen schritt #while MyKa-Programm benutzt Kontrollstrukturen benutzt Sprungbefehle links label .L0 if nichtVorWand: goto .L1 else: goto .L2 label .L1 ziegelHinlegen schritt goto .L0 label .L2 MyKaGoto-Programm [ (None, ['links']) ('.L0', ['noop']) (None, ['if', ['nichtVorWand'], ['goto', '.L1'], ['goto', '.L2']]) ('.L1', ['noop']) (None, ['ziegelHinlegen']) (None, ['schritt']) (None, ['goto', '.L0']) ('.L2', ['noop']) ] strukturiertes MyKaGoto-Programm
Fachkonzept - Compiler Ein Compiler (im engeren Sinn) ist ein universelles Programm (Algorithmus), das jedes Programm einer Quell-Programmiersprache in ein äquivalentes Programm einer Ziel-Programmiersprache übersetzt. MyKaList-Programm MyKaGoToList-Programm
Fachkonzept - Compiler Ein Compiler (im weiteren Sinn) ist ein System, das aus Scanner, Parser, Codererzeuger und Codeoptimierer besteht. links while nichtVorWand: ziegelHinlegen schritt #while LexToken(ELANW,'links',9,0) LexToken(WH,'while',10,6) LexToken(BED,'nichtVorWand',10,12) LexToken(DP,':',10,24) LexToken(ELANW,'ziegelHinlegen',11,28) LexToken(ELANW,'schritt',12,45) LexToken(WH_ENDE,'#while',13,53) [ ['links'], ['while', ['nichtVorWand'], [['ziegelHinlegen'], ['schritt']]] ] [ (None, ['links']), ('.L0', ['noop']), (None, ['if', ['nichtVorWand'], ['goto', '.L1'], ['goto', '.L2']]), ('.L1', ['noop']), (None, ['ziegelHinlegen']), (None, ['schritt']), (None, ['goto', '.L0']), ('.L2', ['noop']) ]
Teil 4 Entwicklung eines Compilers - MyWhile
Station - MyWhile Die Sprache While ist eine sehr einfache Programmiersprache, die auf vieles verzichtet und nur ganz wenige Konstrukte zur Verfügung stellt. Diese Sprache wird wegen ihrere Einfachheit oft für theoretische Untersuchungen genutzt. Wir benutzen eine Variante von While, die wir MyWhile nennen. Die Sprache MyWhile ist nicht ganz so restriktiv wie die Sprache While, aber dennoch so einfach, dass Funktionsprinzipien von Interpretern und Compilern in einem überschaubaren Kontext verdeutlicht werden können. x = 24 y = 15 d = x - y while d != 0: if d > 0: x = x - y else: y = y - x #if d = x - y #while
Station - MyWhile x = 24 y = 15 d = x - y while d != 0: if d > 0: x = x - y else: y = y - x #if d = x - y #while elem. Anweisung x = 0 neu = alt x = x + 1 y1 = x0 - 2 z = x + y x = x - y pass Struktur "Variable" = "Zahl" "Variable" = "Variable" "Variable" = "Variable" + "Zahl" "Variable" = "Variable" - "Zahl" "Variable" = "Variable" + "Variable" "Variable" = "Variable" - "Variable" Bedingung x == 0 zahl != 0 x2 > 0 y < 0 Struktur "Variable" == 0 "Variable" != 0 "Variable" > 0 "Variable" < 0 Als Bezeichner von Variablen sind alle Zeichenketten erlaubt, die aus Kleinbuchstaben und Ziffern bestehen und mit einem Buchstaben beginnen. Als Zahlen dürfen hier nur die ganzen Zahlen benutzt werden.
Station - MyWhile x = 24 y = 15 d = x - y while d != 0: if d > 0: x = x - y else: y = y - x #if d = x - y #while Kontrollstruktur Sequenz: "Anweisung" "Anweisung" ... "Anweisung" Fallunterscheidung: if "Bedingung": "Anweisungssequenz" else: "Anweisungssequenz" #if Wiederholung: while "Bedingung": "Anweisungssequenz" #while Bedeutung Sequenz: Führe die Anweisungen der Reihe nach aus. Fallunterscheidung: Wenn die Bedingung erfüllt ist, dann führe die erste Anweisungssequenz aus, ansonsten die zweite Anweisungssequenz. Wiederholung: Solange die Bedingung erfüllt ist, führe die Anweisungssequenz aus.
Aufgabe Ist das folgende Programm ein syntaktisch korrektes MyWhile-Programm? x=24 y=15 d=x-y while d != 0 : if d > 0 : x = x - y else: y=y-x#if d=x-y #while Warum ist die Klärung der Frage schwierig?
Station - LEX und YACC LEX ist ein Programm, das Scanner automatisiert erzeugen kann. Gibt man LEX eine genaue Beschreibung der Token vor, so erzeugt LEX einen endlichen Automaten, der Token erkennt. YACC (Akronym für yet another compiler compiler) ist ein Programm, das Parser automatisiert erzeugen kann. Gibt man YACC die Grammatik einer (Programmier-) Sprache vor, so erzeugt YACC einen Shift-Reduce-Parser zur Erkennung der Sprache, die durch die Grammatik beschrieben wird. Wir benutzen im Folgenden die Python-Implementierung PLY von LEX und YACC.
Station - Scanner Ein (lexikalischer) Scanner ist eine Programmeinheit, die eine Zeichenfolge nach vorgegebenen Mustern in lexikalische Einheiten zerlegt oder anzeigt, dass eine solche Zerlegung nicht möglich ist. x = 24 y = 15 d = x - y while d != 0: if d > 0: x = x - y else: y = y - x #if d = x - y #while Scanner Quelltext (VAR,'x') (ZUW,'=') (ZAHL,'24') (VAR,'y') (ZUW,'=') (ZAHL,'15') (VAR,'d') (ZUW,'=') (VAR,'x') (MINUS,'-') (VAR,'y') (WHILE,'while') (VAR,'d') (UG,'!=') (NULL,'0') (DP,':') ... Tokenfolge / Fehlermeldung
Station - Scanner Der Aufbau lexikalischer Einheiten wird in der Regel mit Hilfe regulärer Ausdrücke beschrieben. Als Beispiel betrachten wir Variablenbezeichner. Variablenbezeichner beginnen mit einem Kleinbuchstaben. Danach können beliebig viele Kleinbuchstaben oder Ziffern folgen. Dieses Muster lässt sich mit dem regulären Ausdruck [a-z][a-z0-9]* beschreiben. # Beschreibung der Token t_VAR = r'[a-z][a-z0-9]*' t_ZAHL = r'[\+|-]?[1-9][0-9]*' t_NULL = r'0' t_WHILE = r'while' t_IF = r'if' t_ELSE = r'else' t_PASS = r'pass' t_ENDWH = r'\#while' t_ENDIF = r'\#if' t_ZUW = r'=' t_PLUS = r'\+' t_MINUS = r'-' t_GL = r'==' t_UG = r'!=' t_GR = r'\>' t_KL = r'\<' t_DP = r':' (VAR,'x') (ZUW,'=') (ZAHL,'24') (VAR,'y') (ZUW,'=') (ZAHL,'15') (VAR,'d') (ZUW,'=') (VAR,'x') (MINUS,'-') (VAR,'y') (WHILE,'while') (VAR,'d') (UG,'!=') (NULL,'0') (DP,':') (IF,'if') ... Das Programm LEX ist in der Lage, ausgehend von einer Tokenbeschreibung in Form regulärer Ausdrücke ein System zur lexikalischen Analyse zu erzeugen. Letztlich generiert LEX aus den regulären Ausdrücken endliche Automaten, die die Analyse von Zeichenketten vornehmen.
Aufgabe # Beschreibung der Token t_VAR = r'[a-z][a-z0-9]*' t_ZAHL = r'[\+|-]?[1-9][0-9]*' t_NULL = r'0' t_WHILE = r'while' t_IF = r'if' t_ELSE = r'else' t_PASS = r'pass' t_ENDWH = r'\#while' t_ENDIF = r'\#if' t_ZUW = r'=' t_PLUS = r'\+' t_MINUS = r'-' t_GL = r'==' t_UG = r'!=' t_GR = r'\>' t_KL = r'\<' t_DP = r':' (VAR,'x') (ZUW,'=') (ZAHL,'24') (VAR,'y') (ZUW,'=') (ZAHL,'15') (VAR,'d') (ZUW,'=') (VAR,'x') (MINUS,'-') (VAR,'y') (WHILE,'while') (VAR,'d') (UG,'!=') (NULL,'0') (DP,':') (IF,'if') ... Aufgabe: (a) Welche Zeichenketten passen auf das Token-Muster ZAHL?. Gib Beispiele für solche Zeichenketten an. Beachte die Sonderrolle der Zahl Null, für die ein eigenes Token-Muster vorgesehen ist. (b) Die Festlegung der Token-Muster ist in der vorliegenden Form nicht eindeutig. So passt z.B. die Zeichenkette 'if' auf zwei verschiedene Token-Muster. Welche sind das? Gibt es weitere problematische Zeichenketten?
Station - Scanner # reservierte Wörter reserved = { 'if' : 'IF', 'else' : 'ELSE', 'while' : 'WHILE', 'pass': 'PASS' } # Namen der Token tokens = ['VAR', 'ZAHL', 'NULL', 'ZUW', 'PLUS', 'MINUS', 'GL', 'UG', 'GR', 'KL', 'DP', 'ENDWH', 'ENDIF'] tokens = tokens + list(reserved.values()) # Beschreibung der Token def t_VAR(t): r'[a-z][a-z0-9]*' t.type = reserved.get(t.value, 'VAR') # Überprüfung auf reservierte Wörter return t t_ZAHL = r'[\+|-]?[1-9][0-9]*' t_NULL = r'0' t_ZUW = r'=' t_PLUS = r'\+' t_MINUS = r'-' t_GL = r'==' t_UG = r'!=' t_GR = r'\>' t_KL = r'\<' t_DP = r':' t_ENDWH = r'\#while' t_ENDIF = r'\#if' ... Token-Muster von MyWhile
Station - Scanner import ply.lex as lex from syntaxWhile import * # Testprogramm programm = ''' x = 24 y = 15 d = x - y while d != 0: if d > 0: x = x - y else: y = y - x #if d = x - y #while ''' # Erzeugung des Scanners scanner = lex.lex(debug=0) # lexikalische Analyse mit Erzeugung der Token scanner.input(programm) token = [] tok = scanner.token() while tok: token = token + [tok] tok = scanner.token() # Ausgabe for tok in token: print(tok) >>> LexToken(VAR,'x',2,1) LexToken(ZUW,'=',2,3) LexToken(ZAHL,'24',2,5) LexToken(VAR,'y',3,8) LexToken(ZUW,'=',3,10) LexToken(ZAHL,'15',3,12) LexToken(VAR,'d',4,15) LexToken(ZUW,'=',4,17) LexToken(VAR,'x',4,19) LexToken(MINUS,'-',4,21) LexToken(VAR,'y',4,23) LexToken(WHILE,'while',5,25) LexToken(VAR,'d',5,31) LexToken(UG,'!=',5,33) LexToken(NULL,'0',5,36) LexToken(DP,':',5,37) LexToken(IF,'if',6,43) LexToken(VAR,'d',6,46) LexToken(GR,'>',6,48) LexToken(NULL,'0',6,50) LexToken(DP,':',6,51) LexToken(VAR,'x',7,61) LexToken(ZUW,'=',7,63) LexToken(VAR,'x',7,65) LexToken(MINUS,'-',7,67) LexToken(VAR,'y',7,69) LexToken(ELSE,'else',8,75) LexToken(DP,':',8,79) LexToken(VAR,'y',9,89) ...
Aufgabe Aufgabe: (a) Probiere das selbst einmal aus. Teste auch andere Quelltexte. Teste u.a. den Quelltext: x=24y=15d=x-ywhiled!=0:ifd>0:x=x-yelse:y=y-x#ifd=x-y#while Teste auch solche Quelltexte, die sich nicht in die vorgegebenen Token zerlegen lassen. Wie reagiert der Scanner auf Variablenbezeichner der Gestalt 007? Hast du eine Vermutung? Was macht der Scanner mit einem unsinnigen Quelltext wie z.B. :while 7:? Hast du eine Vermutung? (b) Versuche, durch Tests herauszufinden, welche Bedeutung die zusätzlichen Zahlangaben in den Token haben. (c) Ändere selbst die Beschreibung der Token in sinnvoller Weise ab und teste die neuen Festlegungen.
Aufgabe Aufgabe: Das oben gezeigte MyWhile-Programm könnte man auch in einer Java-ähnlichen Schreibweise darstellen. Ändere die Beschreibung der Token so ab, dass sie auf die Java-ähnliche Schweibweise passt. x = 24; y = 15; d = x - y; while (d != 0) { if (d > 0) { x = x - y; } else { y = y - x; }; d = x - y; };
Station - Parser / Syntaxanalyse Ein Parser ist eine Programmeinheit, die analysiert, ob eine Tokenfolge zu einem Programmquelltext vorgegebene Grammatikregeln befolgt. Ist das nicht der Fall, so wird eine Fehlermeldung erzeugt. Andernfalls wird eine strukturierte Darstellung des Programmquelltextes erzeugt, die die von den Grammatikregeln verlangte syntaktische Struktur widerspiegelt. Parser (VAR,'x') (ZUW,'=') (ZAHL,'24') (VAR,'y') (ZUW,'=') (ZAHL,'15') (VAR,'d') (ZUW,'=') (VAR,'x') (MINUS,'-') (VAR,'y') (WHILE,'while') (VAR,'d') (UG,'!=') (NULL,'0') (DP,':') ... Tokenfolge Struktur / Fehlermeldung ok
Station - Parser / Syntaxanalyse Zunächst muss die Grammatik der Sprache MyWhile festgelegt werden. Die Terminalsymbole der Grammatik sind die Tokennamen. Die Nichtterminalsymbole ergeben sich aus den linken Seiten der folgenden Produktionen. Startsymbol ist das Symbol auf der linken Seite der ersten Produktion. # Produktionen anweisungsfolge -> anweisung anweisungsfolge anweisungsfolge -> anweisung anweisung -> zuweisung anweisung -> PASS anweisung -> WHILE bedingung DP anweisungsfolge ENDWH anweisung -> IF bedingung DP anweisungsfolge ELSE DP anweisungsfolge ENDIF zuweisung -> VAR ZUW term term -> VAR op zahl term -> VAR op VAR term -> zahl term -> VAR zahl -> NULL zahl -> ZAHL op -> PLUS op -> MINUS bedingung -> VAR rel NULL rel -> GL rel -> UG rel -> GR rel -> KL x = 24 y = 15 d = x - y while d != 0: if d > 0: x = x - y else: y = y - x #if d = x - y #while VAR ZUW ZAHL VAR ZUW ZAHL VAR ZUW VAR MINUS VAR WHILE VAR UG NULL DP IF VAR GR NULL DP VAR ZUW VAR MINUS VAR ELSE DP VAR ZUW VAR MINUS VAR ENDIF VAR ZUW VAR MINUS VAR ENDWH Grammatik von MyWhile
Aufgabe Aufgabe: Zeige, dass man mit den Produktionen der Grammatik eine Ableitung der Tokenfolge zum Demo-Programm erzeugen kann. # Produktionen anweisungsfolge -> anweisung anweisungsfolge anweisungsfolge -> anweisung anweisung -> zuweisung anweisung -> PASS anweisung -> WHILE bedingung DP anweisungsfolge ENDWH anweisung -> IF bedingung DP anweisungsfolge ELSE DP anweisungsfolge ENDIF zuweisung -> VAR ZUW term term -> VAR op zahl term -> VAR op VAR term -> zahl term -> VAR zahl -> NULL zahl -> ZAHL op -> PLUS op -> MINUS bedingung -> VAR rel NULL rel -> GL rel -> UG rel -> GR rel -> KL x = 4 while x > 0: x = x - 1 #while VAR ZUW ZAHL WHILE VAR GR NULL DP VAR ZUW VAR MINUS ZAHL ENDWH Grammatik von MyWhile
Station - Parser / Syntaxanalyse def p_anweisungsfolge_anweisung_anweisungsfolge(p): 'anweisungsfolge : anweisung anweisungsfolge' p[0] = None def p_anweisungsfolge_anweisung(p): 'anweisungsfolge : anweisung' p[0] = None def p_anweisung_zuw(p): 'anweisung : zuweisung' p[0] = None def p_anweisung_pass(p): 'anweisung : PASS' p[0] = None def p_anweisung_wh(p): 'anweisung : WHILE bedingung DP anweisungsfolge ENDWH' p[0] = None def p_anweisung_if(p): 'anweisung : IF bedingung DP anweisungsfolge ELSE DP anweisungsfolge ENDIF' p[0] = None def p_zuweisung(p): 'zuweisung : VAR ZUW term' p[0] = None def p_term_var_op_zahl(p): 'term : VAR op zahl' p[0] = None def p_term_var_op_var(p): 'term : VAR op VAR' p[0] = None def p_term_zahl(p): 'term : zahl' p[0] = None ... YACC-Implementierung anweisungsfolge -> anweisung anweisungsfolge anweisungsfolge -> anweisung anweisung -> zuweisung anweisung -> PASS anweisung -> WHILE bedingung DP anweisungsfolge ENDWH anweisung -> IF bedingung DP anweisungsfolge ELSE DP anweisungsfolge ENDIF zuweisung -> VAR ZUW term term -> VAR op ZAHL term -> VAR op VAR term -> zahl ...
Station - Parser / Syntaxanalyse import ply.lex as lex import ply.yacc as yacc from syntaxWhile import * # Testprogramm programm = ''' x = 24 y = 15 d = x - y while d != 0: if d > 0: x = x - y else: y = y - x #if d = x - y #while ''' # Erzeugung des Scanners scanner = lex.lex(debug=0) # Erzeugung des Parsers parser = yacc.yacc(debug=False) # syntaktische Analyse parser.parse(programm, debug=0) # Ausgabe print("ok!") >>> ok!
Aufgabe Aufgabe: (a) Probiere das selbst einmal aus. Teste Quelltexte, die den Syntaxregeln von MyWhile entsprechen und teste auch fehlerhafte Quelltexte. Wie zeigt sich in der vorliegenden Implementierung, ob der Quelltext fehlerfrei ist? (b) Du kannst ja auch einmal versuchen, die Grammatik von MyWhile in sinnvoller Weise zu ergänzen oder abzuändern.
Aufgabe Aufgabe: Ändere die Grammatik (und Tokenbeschreibungen) so ab, dass folgendes Programm erkannt wird: x = 24; y = 15; d = x - y; while (d != 0) { if (d > 0) { x = x - y; } else { y = y - x; }; d = x - y; };
Station - Parser / Strukturgerüst Ein Parser ist eine Programmeinheit, die analysiert, ob eine Tokenfolge zu einem Programmquelltext vorgegebene Grammatikregeln befolgt. Ist das nicht der Fall, so wird eine Fehlermeldung erzeugt. Andernfalls wird eine strukturierte Darstellung des Programmquelltextes erzeugt, die die von den Grammatikregeln verlangte syntaktische Struktur widerspiegelt. Parser [ ['=', ('VAR', 'x'), [('ZAHL', '24')]], ['=', ('VAR', 'y'), [('ZAHL', '15')]], ['=', ('VAR', 'd'), ['-', ('VAR', 'x'), ('VAR', 'y')]], ['while', ['!=', ('VAR', 'd'), ('ZAHL', '0')], [ ['if', ['>', ('VAR', 'd'), ('ZAHL', '0')], [ ['=', ('VAR', 'x'), ['-', ('VAR', 'x'), ('VAR', 'y')]] ], [ ['=', ('VAR', 'y'), ['-', ('VAR', 'y'), ('VAR', 'x')]] ] ], ['=', ('VAR', 'd'), ['-', ('VAR', 'x'), ('VAR', 'y')]] ] ] ] (VAR,'x') (ZUW,'=') (ZAHL,'24') (VAR,'y') (ZUW,'=') (ZAHL,'15') (VAR,'d') (ZUW,'=') (VAR,'x') (MINUS,'-') (VAR,'y') (WHILE,'while') (VAR,'d') (UG,'!=') (NULL,'0') (DP,':') ... Tokenfolge Struktur / Fehlermeldung
Station - Parser / Strukturgerüst Idee: Die Grammatikregeln werden um Beschreibungen zur Erzeugung des Strukturgerüsts erweitert. # Produktionen anweisungsfolge -> anweisung anweisungsfolge anweisungsfolge -> anweisung anweisung -> zuweisung anweisung -> PASS anweisung -> WHILE bedingung DP anweisungsfolge ENDWH anweisung -> IF bedingung DP anweisungsfolge ELSE DP anweisungsfolge ENDIF zuweisung -> VAR ZUW term term -> VAR op zahl term -> VAR op VAR term -> zahl term -> VAR zahl -> NULL zahl -> ZAHL op -> PLUS op -> MINUS bedingung -> VAR rel NULL rel -> GL rel -> UG rel -> GR rel -> KL # erweiterte Produktionen ... zahl -> ZAHL | | p[0] p[1] p[0] = p[1] term -> zahl | | p[0] p[1] p[0] = [('ZAHL', p[1])] zuweisung -> VAR ZUW term | | | | p[0] p[1]p[2]p[3] p[0] = [p[2], ('VAR', p[1]), p[3]] anweisung -> zuweisung | | p[0] p[1] p[0] = p[1]
Station - Parser / Strukturgerüst x = 4 while x > 0: x = x - 1 #while Quelltext VAR ZUW ZAHL WHILE VAR GR NULL DP VAR ZUW VAR MINUS ZAHL ENDWH Tokenfolge anweisungsfolge -> anweisung anweisungsfolge -> anweisung anweisung -> anweisung WHILE bedingung DP anweisungsfolge ENDWH -> anweisung WHILE bedingung DP anweisung ENDWH -> anweisung WHILE bedingung DP zuweisung ENDWH -> anweisung WHILE bedingung DP VAR ZUW term ENDWH -> anweisung WHILE bedingung DP VAR ZUW VAR op ZAHL ENDWH -> anweisung WHILE bedingung DP VAR ZUW VAR MINUS ZAHL ENDWH -> anweisung WHILE VAR rel NULL DP VAR ZUW VAR MINUS ZAHL ENDWH -> anweisung WHILE VAR GR NULL DP VAR ZUW VAR MINUS ZAHL ENDWH -> # anweisung -> zuweisung zuweisung WHILE VAR GR NULL DP VAR ZUW VAR MINUS ZAHL ENDWH -> # zuweisung -> VAR ZUW term VAR ZUW term WHILE VAR GR NULL DP VAR ZUW VAR MINUS ZAHL ENDWH -> # term -> zahl VAR ZUW zahl WHILE VAR GR NULL DP VAR ZUW VAR MINUS ZAHL ENDWH -> # zahl -> ZAHL VAR ZUW ZAHL WHILE VAR GR NULL DP VAR ZUW VAR MINUS ZAHL ENDWH Rechtsableitung der Tokenfolge Grammatikregeln
Station - Parser / Strukturgerüst VAR ZUW ZAHL WHILE VAR GR NULL DP VAR ZUW VAR MINUS VAR ENDWH zahl -> ZAHL | | p[0] p[1] p[0] = p[1]zahl: '4' VAR ZUW zahl WHILE VAR GR NULL DP VAR ZUW VAR MINUS VAR ENDWH term -> zahl | | p[0] p[1] p[0] = [('ZAHL', p[1])]term: [('ZAHL', '4')] VAR ZUW term WHILE VAR GR NULL DP VAR ZUW VAR MINUS ZAHL ENDWH zuweisung -> VAR ZUW term | | | | p[0] p[1]p[2]p[3] p[0] = [p[2], ('VAR', p[1]), p[3]]zuweisung: ['=', ('VAR', 'x'), [('ZAHL', '4')]] zuweisung WHILE VAR GR NULL DP VAR ZUW VAR MINUS ZAHL ENDWH anweisung -> zuweisung | | p[0] p[1] p[0] = p[1] anweisung: ['=', ('VAR', 'x'), [('ZAHL', '4')]] x = 4 while x > 0: x = x - 1 #while erweiterte Grammatikregel produzierte Struktur VAR ZUW ZAHL WHILE VAR GR NULL DP VAR ZUW VAR MINUS ZAHL ENDWH Rechtsableitung der Tokenfolge ( rückwärts betrachtet) - erweitert um die Erzeugung des Strukturgerüsts Grammatik von MyWhile [ ['=', ('VAR', 'x'), [('ZAHL', '4')]], ['while', ['>', ('VAR', 'x'), ('ZAHL', '0')], [ ['=', ('VAR', 'x'), ['-', ('VAR', 'x'), ('ZAHL', '1')]] ] ] ]