Compilerbau II Skript



Ähnliche Dokumente
Erwin Grüner

4. Lernen von Entscheidungsbäumen. Klassifikation mit Entscheidungsbäumen. Entscheidungsbaum

Grundbegriffe der Informatik

50. Mathematik-Olympiade 2. Stufe (Regionalrunde) Klasse Lösung 10 Punkte

Theoretische Grundlagen der Informatik

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren

1 topologisches Sortieren

Programmierung 2. Übersetzer: Code-Erzeugung. Sebastian Hack. Klaas Boesche. Sommersemester

Programmiersprachen und Übersetzer

Motivation. Formale Grundlagen der Informatik 1 Kapitel 5 Kontextfreie Sprachen. Informales Beispiel. Informales Beispiel.

Programmierkurs Java

Grundlagen der Theoretischen Informatik, SoSe 2008

OECD Programme for International Student Assessment PISA Lösungen der Beispielaufgaben aus dem Mathematiktest. Deutschland

Künstliche Intelligenz Maschinelles Lernen

Outlook. sysplus.ch outlook - mail-grundlagen Seite 1/8. Mail-Grundlagen. Posteingang

Das Briefträgerproblem

Vorlesung Diskrete Strukturen Graphen: Wieviele Bäume?

Einführung in die Programmierung

Einfache Ausdrücke Datentypen Rekursive funktionale Sprache Franz Wotawa Institut für Softwaretechnologie

Mächtigkeit von WHILE-Programmen

Lineare Gleichungssysteme

Würfelt man dabei je genau 10 - mal eine 1, 2, 3, 4, 5 und 6, so beträgt die Anzahl. der verschiedenen Reihenfolgen, in denen man dies tun kann, 60!.

Systeme 1. Kapitel 6. Nebenläufigkeit und wechselseitiger Ausschluss

2.11 Kontextfreie Grammatiken und Parsebäume

Übungen zu Einführung in die Informatik: Programmierung und Software-Entwicklung: Lösungsvorschlag

Informationsblatt Induktionsbeweis

Anmerkungen zur Übergangsprüfung

Suchmaschinen. Universität Augsburg, Institut für Informatik SS 2014 Prof. Dr. W. Kießling 23. Mai 2014 Dr. M. Endres, F. Wenzel Lösungsblatt 6

Kapitel MK:IV. IV. Modellieren mit Constraints

Erstellen von x-y-diagrammen in OpenOffice.calc

Grundlagen der Programmierung Prof. H. Mössenböck. 3. Verzweigungen

Manager. von Peter Pfeifer, Waltraud Pfeifer, Burkhard Münchhagen. Spielanleitung

Theoretische Informatik 2 (WS 2006/07) Automatentheorie und Formale Sprachen 19

Kapitel 2: Formale Sprachen Kontextfreie Sprachen. reguläre Grammatiken/Sprachen. kontextfreie Grammatiken/Sprachen

Binäre Bäume Darstellung und Traversierung

1 Vom Problem zum Programm

Einfache Arrays. Annabelle Klarl. Einführung in die Informatik Programmierung und Softwareentwicklung

368 4 Algorithmen und Datenstrukturen

Kapitel 4 Die Datenbank Kuchenbestellung Seite 1

Objektorientierte Programmierung

8 Diskrete Optimierung

4. Jeder Knoten hat höchstens zwei Kinder, ein linkes und ein rechtes.

Primzahlen und RSA-Verschlüsselung

Erweiterung der Aufgabe. Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen:

1 Mathematische Grundlagen

WS 2009/10. Diskrete Strukturen

Lineare Funktionen. 1 Proportionale Funktionen Definition Eigenschaften Steigungsdreieck 3

Mathematischer Vorbereitungskurs für Ökonomen

Scala kann auch faul sein

ecaros2 - Accountmanager

Algorithmen II Vorlesung am

Professionelle Seminare im Bereich MS-Office

Formale Systeme, WS 2012/2013 Lösungen zu Übungsblatt 4

Grundbegriffe der Informatik

3 Wie bekommen Sie Passwortlevel 3 und einen Installateurscode?

MORE Profile. Pass- und Lizenzverwaltungssystem. Stand: MORE Projects GmbH

Grundlagen der höheren Mathematik Einige Hinweise zum Lösen von Gleichungen

Äquivalente Grammatiken / attributierte Grammatik

Repetitionsaufgaben Wurzelgleichungen

Gleichungen Lösen. Ein graphischer Blick auf Gleichungen

Übungsaufgaben Tilgungsrechnung

Der Aufruf von DM_in_Euro 1.40 sollte die Ausgabe 1.40 DM = Euro ergeben.

Mit dem Tool Stundenverwaltung von Hanno Kniebel erhalten Sie die Möglichkeit zur effizienten Verwaltung von Montagezeiten Ihrer Mitarbeiter.

Bundesverband Flachglas Großhandel Isolierglasherstellung Veredlung e.v. U g -Werte-Tabellen nach DIN EN 673. Flachglasbranche.

Übersicht Programmablaufsteuerung

Java Kurs für Anfänger Einheit 5 Methoden

1 C H R I S T O P H D R Ö S S E R D E R M A T H E M A T I K V E R F Ü H R E R

Fallbeispiel: Eintragen einer Behandlung

Algorithmen und Datenstrukturen

Lineare Gleichungssysteme

Lösungsvorschlag für die Probeklausuren und Klausuren zu Algebra für Informations- und Kommunikationstechniker bei Prof. Dr.

Übungen Programmieren 1 Felix Rohrer. Übungen

7 Rechnen mit Polynomen

Einführung in die Programmierung

Anleitung über den Umgang mit Schildern

Zeichen bei Zahlen entschlüsseln

Kapiteltests zum Leitprogramm Binäre Suchbäume

Alle Schlüssel-Karten (blaue Rückseite) werden den Schlüssel-Farben nach sortiert und in vier getrennte Stapel mit der Bildseite nach oben gelegt.

Informatik Grundlagen, WS04, Seminar 13

LU-Zerlegung. Zusätze zum Gelben Rechenbuch. Peter Furlan. Verlag Martina Furlan. Inhaltsverzeichnis. 1 Definitionen.

Wir arbeiten mit Zufallszahlen

Algorithmische Mathematik

Die Gleichung A x = a hat für A 0 die eindeutig bestimmte Lösung. Für A=0 und a 0 existiert keine Lösung.

2. Lernen von Entscheidungsbäumen

3.2 Binäre Suche. Usr/local/www/ifi/fk/menschen/schmid/folien/infovk.ppt 1

Windows. Workshop Internet-Explorer: Arbeiten mit Favoriten, Teil 1

Einführung. Vorlesungen zur Komplexitätstheorie: Reduktion und Vollständigkeit (3) Vorlesungen zur Komplexitätstheorie. K-Vollständigkeit (1/5)

Dieses Tutorial gibt eine Übersicht der Form Klassen von Struts, welche Besonderheiten und Unterschiede diese aufweisen.

!(0) + o 1("). Es ist damit möglich, dass mehrere Familien geschlossener Orbits gleichzeitig abzweigen.

Welche Bereiche gibt es auf der Internetseite vom Bundes-Aufsichtsamt für Flugsicherung?

Elternzeit Was ist das?

Auf der linken Seite wählen Sie nun den Punkt Personen bearbeiten.

Beweisbar sichere Verschlüsselung

Java Kurs für Anfänger Einheit 4 Klassen und Objekte

Grundlagen der Programmierung 2. Bäume

Inventur. Bemerkung. / Inventur

Transkript:

Universität Siegen Fachbereich 12, Angewandte Informatik und Elektrotechnik Dozent: Dr. Kurt Sieber; Compilerbau II Skript Christian Uhrhan 18. März 2011

Inhaltsverzeichnis 1 Syntaxgerichtete Übersetzung 1 1.1 Semantische Regeln............................... 3 1.2 Auswertungsreihenfolge............................ 4 1.3 Topologische Sortierung............................ 4 1.4 Auswertungsreihenfolge für S-Attributierte Grammatiken......... 6 2 Attrebutauswertung bei L-Attributierten Grammatiken 7 3 Syntaxgerichtete Übersetzungsschemata (ÜS) 10 3.1 Semantik eines SDT.............................. 10 3.2 Übersetzung von S- bzw. L-attributierten Grammatiken in SDT s..... 10 4 Generierung von Zwischencode 11 4.1 Übersetzung arithmetischer Ausdrücke in 3-Adress-Code.......... 13 4.2 Übersetzung boolscher Ausdrücke....................... 13 4.3 Jumpcode für boolsche Ausdrücke...................... 14 4.4 Backpatching.................................. 17 4.4.1 Boolsche Ausdrücke.......................... 17 4.4.2 Anweisungen.............................. 17 5 Laufzeitumgebungen 17 5.1 Speicherorganisation.............................. 17 5.2 Aufteilung zwischen Stack und Heap..................... 17 5.3 Zugriff auf globale Daten........................... 17 5.4 Bestimmung des Access Links......................... 17 5.5 Prozeduren auf Parameterposition...................... 17 5.6 Alternative zu Access Links: Displays.................... 17 5.7 calling sequences und return sequences.................... 17 5.7.1 Design-Prinzipien............................ 17 5.7.2 Aufteilung der calling sequence.................... 17 5.7.3 Codeerzeugung für Programme mit Prozeduren (Pascal-artige Sprache)................................... 17 5.7.4 Vorgehensweise bei der Übersetzung................. 17 5.7.5 Übersetzung einer Prozedurdeklaration............... 17 5.7.6 Übersetzung eines Prozeduraufrufs.................. 17 6 Codegenerierung (Drachenbuch Kpt. 8) 17 6.1 Befehlssatz der Zielmaschine (RISC-Architektur).............. 17 6.2 Basisblöcke und Flussgraphen......................... 17 6.3 Optimierung der Basisblöcke......................... 17 6.4 Vom (optimierten) DAG zurück zum Zwischencode............. 17 6.5 Ein einfacher Codegenerator.......................... 17 6.6 Globale Register Vergabe........................... 17 i

7 Instruction Selection (Drachenbuch Kpt. 8.9) 17 7.1 Generierung von optimalem Code für Ausdrücke.............. 17 7.1.1 Algorithmus zur Berechnung der Ershov-Zahl............ 17 ii

1 Syntaxgerichtete Übersetzung Bisherige Sicht: Aus einer KFG G wird ein Parser erzeugt, also ein deterministischer Kellerautomat, der die Sprache L(G) akzeptiert. D.h.: Der Parser antwortet nur mit Ja oder Nein je nachdem, ob das Eingabewort w L(G) oder w / L(G). In der Praxis: Der Parser sollte eine Antwort zurückliefern, z. B.: (a) einen abstrakten Syntaxbaum für das Eingabewort w (mit dem man anschließend weiterarbeitet, z. B. Typüberprüfung in der semantischen Analyse oder Generierung von Zwischencode). (b) oder schon eine Übersetzung des Eingabewortes w in Zwischencode (c) oder in einfachen Fällen (z. B. wenn w Eingabe für einen Taschenrechner) schon ein Ergebnis. Deshalb als Eingabe für Parser-Generatoren (JavaCup, yacc, SML-Yacc, O Caml- Yacc) nicht nur eine KFG, sondern man ergänzt die Produktionen der KFG durch sogenannte semantische Regeln oder semantische Aktionen. Beispiel: 1. Yacc-Eingabe: Spezifikation eines Taschenrechners (siehe AB01). expr : exp + term {$$ = $1 + $3; } Zugehörige Theorie: Attributierte Grammatiken (Verallgemeinerung von AG: syntaxgerichtete Definitionen (engl. SDD) Verwandter Mechanismus: syntaxgerichtete Übersetzungen (engl. SDT)) Beispiel: 2. für attributierte Grammatiken: Arithmetische Ausdrücke für Taschenrechner Nonterminals: L, E, T, F Terminalzeichen: n, +,, (, ), digit Jedem Nonterminal wird ein einziges Attribut zugeordnet, nämlich val (= value). Dem Terminalzeichen digit ist ein Attribut lexval zugeordnet (wird vom Scanner geliefert). Die Produktionen und sematische Regeln lauten: 1

Produktion semantische Regel (1) L En L.val = E.val (2) E E 1 + T E.val = E 1.val + T.val (3) E T E.val = T.val (4) T T 1 F T.val = T 1.val F.val (5) T F T.val = F.val (6) F (E) F.val = E.val (7) F digit F.val = digit.lexval Mit den semantischen Regeln werden die Attributwerte für jeden Syntaxbaum berechnet, z. B. attributierter Syntaxbaum für 3 5 + 4n L val = 19 E val = 19 n E val = 15 + T val = 4 T val = 15 F val = 4 T val = 3 F val = 5 digit lexval = 4 F val = 3 digit lexval = 5 digit lexval = 3 Definition 1 (Attributierte Grammatik). Sei G = (N, T, P, S) eine KFG, V = N T Eine attributierte Grammatik über G besteht aus einer endlichen Menge A von Attributen (oder Attributnamen) einem Wertebereich D a a A zwei Abbildungen: syn : V P(A) inh : V P(A) mit inh(s) = inh(x) = x T 2

Sprechweise: a syn(x) heißt synthetisches Attribut von X a inh(x) heißt geerbtes Attribut von X Es gilt: Attr(X) = syn(x) inh(x) einer Menge sem(p) von semantischen Regeln für jede Produktion p P. 1.1 Semantische Regeln Wie sehen die semantischen Regeln aus? Sei p eine Produktion der Form X 0 X 1... X m (wobei aus jedem X i die Position i ablesbar ist). Dann ist jede semantische Regel für p von der Form: mit X i0.a 0 = f(x i1.a 1,..., X ik.a k ) i 0,..., i k {0,..., m} (d.h. i 0,..., i k sind Positionen in p) a j Attr(X ij ) (d.h. a j ist ein zu X ij f : D a1 D ak D a0 passendes Attribut) (d.h. f hat passenden Typ ) X i0.a 0 kommt nicht in den Argumenten von f vor Weiter muss gelten: Für jedes a syn(x 0 ) enthält sem(p) genau eine Regel mit linker Seite X 0.a Für jedes a inh(x i ) mit i {1,..., m} enthält sem(p) genau eine Regel mit linker Seite X i.a. Im ersten Fall dürfen als Attribute von f nur Attribute von X 1,..., X m oder geerbte Attribute von X 0 auftreten. Weitere Regeln dürfen in sem(p) nicht vorkommen. Beispiel: 3. Aus vorhergehendem Beispiel val ist synthetisches Attribut für die Nonterminals L, E, T, F lexval ist synthetisches Attribut für digit Keine geerbten Attribute Weiteres Beispiel: Nicht linksrekursive KFG für arithmetische Ausdrücke. Hier: nur ein Teil davon: Attributierter Syntaxbaum für 3 5: T val = 15 F val = 3 inh = 3 T syn = 15 digit lexval = 3 F val = 5 inh = 15 T syn = 15 digit lexval=5 ɛ 3

(1) T F T T.inh = T.val T.val = T.syn (2) T F T 1 T 1.inh = T.inh F.val T.syn = T 1.syn (3) T ɛ T.syn = T.inh (4) F digit F.val = digit.lexval 1.2 Auswertungsreihenfolge Gibt es überhaupt eine Reihenfolge, in der die Attribute ausgewertet werden? Wenn ja, welche? Dazu: Betrachte den sogenannten Abhängigkeitsgraphen zu einem attributierten Syntaxbaum (dependency graph). Der Abhängigkeitsgraph ist ein gerichteter Graph und ist wie folgt definiert: (a) Die Knoten des Abhängigkeitsgraphen sind alle Attributvorkommen im attributierten Syntaxbaum, d.h. für jeden mit X markierten Knoten des Syntaxbaums und jedes a Attr(X) gibt es einen Knoten X.a im Abhängigkeitsgraphen. (b) Die Kanten des Abhängigkeitsgraphen geben die Abhängigkeit zwischen den Attributvorkommen wieder, d.h. für jede semantische Regel X.a = f(..., Y.b,... ) gibt es eine Kante vom passenden Y.b zum passenden X.a. Beachte: Die Kante für ein synthetisches Attribut X.a verläuft vom Kind- zum Vaterknoten oder von X.b nach X.a, wobei b geerbtes Attribut. Die Kante für ein geerbtes Attribut X.a verläuft vom Vater- zum Kindknoten oder zwischen Geschwisterknoten. Beispiel: 4. Nicht linksrekursive Grammatik für arithmetische Ausdrücke, Syntaxbaum für 3 5 z. B. wegen T 1.inh = T.inh F.val gibt es Kanten von 5 nach 6 und von 4 nach 6. Am Abhängigkeitsgraphen kann man erkennen, ob es eine Auswertungsreihenfolge gibt (und kann sie dann auch finden). 1.3 Topologische Sortierung Definition 2. Eine topologische Sortierung eines gerichteten Graphen ist eine lineare Anordnung N 1,..., N k aller Knoten des Graphen, so dass gilt: Wenn eine Kante von N i nach N j verläuft, dann ist i < j. 4

Beispiel: 5. (1),...,(9) ist eine mögliche topologische Sortierung im Beispiel. Eine andere: (1),(3),(5),(2),(4),(6),...,(9) Bemerkung 1. Eine topologische Sortierung existiert genau dann, wenn der Graph keine Zyklen besitzt. ist klar Wenn kein Zykel existiert, dann existiert ein Knoten ohne Vorgänger. Diesen nimmt man als ersten der Sortierung. Dann induktiv weiter mit Restgraph. Problem: Topologische Sortierung ist zwar effizient möglich. Aber: Man will es nicht für jeden Syntaxbaum wieder neu durchführen. Frage: Kann man für eine attributierte Grammatik an Hand der semantischen Regeln eine Auswertungsstrategie für alle Syntaxbäume festlegen (oder wenigstens überprüfen, ob alle Abhängigkeitsgraphen zu attributierten Syntaxbaum zyklenfrei sind)? Das Problem ist entscheidbar, aber nur in exponentialer Laufzeit (in Abhängigkeit von der Größe der attributierten Grammatik) nicht in der Praxis durchführbar. Deshalb: Man schränkt sich auf spezielle einfache attributierte Grammatiken ein, die eine einfache Auswertungsstrategie ermöglichen. Definition 3 (S- und L-Attributierte Grammatik). 1. Eine attributierte Grammatik heißt S-Attributiert, wenn sie nur synthetische Attribute hat. 2. Eine attributierte Grammatik heißt L-Attributiert, wenn für alle geerbten Attribute folgendes gilt: Wenn X i.a geerbtes Attribut zur Produktion A X 1... X n, dann darf X i.a nur abhängen von Beispiel: 6. a) geerbten Attributen von A b) beliebigen Attributen von X 1,..., X i 1 (die links von X i stehen) c) beliebigen Attributen von X i selbst, wobei unter den Attributen von X i keine Zyklen entstehen dürfen. 1. linksrekursive attributierte Grammatik für arithmetische Ausdrücke (mit einzigem Attribut val ) ist S-Attributiert 5

2. nicht linksrekursive attributierte Grammatik für arithmetische Ausdrücke (mit val, syn, inh) ist L-Attributiert, denn die sematischen Regeln für das einzige geerbte Attribut inh lauten: T F T T.inh = F.val ist korrekt (da F links von T ) T F T 1 T 1.inh = T.inh F.val ist korrekt (da T Kopf der Prod. und F links von T 1 ) 1.4 Auswertungsreihenfolge für S-Attributierte Grammatiken Da es nur synthetische Attribute gibt, ist jeder Attributwert nur von den Attributwerten der Söhne abhängig (alle Kanten des Abhängigkeitsgraphen verlaufen von unten nach oben). Der Abhängigkeitsgraph ist sogar ein Baum. Jede bottom up -Auswertung ist möglich, insbesondere die postorder-reihenfolge. postorder ist dadurch charakterisiert, dass jeder Knoten hinter seinen Kindern und hinter seinen linken Geschwistern liegt. Auswertung in postorder-reihenfolge lässt sich rekursiv definieren: Algorithmus 1. postorder-eval(n) führe postorder-eval(c) für alle Kinder C von N in der Reihenfolge von links nach rechts durch, berechne alle Attribute von N aus den Attributen der Kinder (wenn N keine Kinder hat, also mit Terminalzeichen markiert, erhält man den Attributwert vom Scanner) Die Prozedur postorder-eval setzt voraus, dass der Syntaxbaum schon vorliegt. Es geht aber auch ohne explizite Konstruktion des Syntaxbaums. Die postorder-reihenfolge passt zum LR-Parser: Die zeitliche Reihenfolge, in der die Zeichen im Keller auftauchen (durch shift oder reduce) entspricht der postorder-reihenfolge im Syntaxbaum. Die Attributwerte lassen sich während des Parsens berechnen, nämlich wie folgt: Zusätzlich zum Zustands und/oder Zeichenkeller führt man einen Keller für die Attributwerte ein: Bei jeder shift-aktion, legt man das Tupel der Attributwerte oben auf den Attributkeller Bei jeder reduce-aktion A X 1... X n berechnet man die Attributwerte für A aus denen von X 1,..., X n (die im Keller oben liegen) und ersetzt letztere durch das Attributtupel für A. L-Attributierte Grammatiken sind (vermutlich) nicht immer mit LR-Parsern verträglich. Da die gängigen Parser-Generatoren LR-Parser generieren, lassen sie nur synthetische Attribute in den semantischen Regeln zu. Mit Tricks kann man geerbte Attribute vermeiden. Beispiel: 7. Attribute inh, syn, val für nicht linksrekursive Grammatik. Man ersetzt inh, syn durch ein synthetisches Attribut fct : int int, das syn in Abhängigkeit von inh berechnet (d.h. inh ist der Parameter von fct, syn ist das Ergebnis von fct). 6

T F T T F T 1 T ɛ T.val = T.fct(F.val) T.fct = λx : int.t 1.fct(x F.val) T.fct = λx : int.x 2 Attrebutauswertung bei L-Attributierten Grammatiken Für jeden Syntaxbaum einer L-Attributierten Grammatik gilt: Aus den geerbten Attributen eines Knotens n lassen sich alle Attribute seiner Kinder und die synthetischen Attribute von n berechnen Beweis. Induktion über die Höhe des Knotens h h = 0 d. h. n ist ein Blatt Die synthetischen Attribute kommen von außen, geerbte sind nicht vorhanden h > 0 d. h. n ist mit Nonterminal X markiert und die Kinder mit X 1,..., X n, wobei X X 1... X n eine Produktion ist. Aus den geerbten von X lassen sich die geerbten von X 1 berechnen (Def. L-Attr.). X X 1... X n Nach Induktionsannahme erhält man daraus die synthetischen von X 1. Daraus erhält man die geerbten Attribute von X 2 (per Def. L-Attr.), bis man alle Attribute von X 1,..., X n kennt. Daraus lassen sich die synthetischen Attribute von X berechnen. Aus dem Beweis ist erkennbar, wie man die Attribute rekursiv berechnen kann und in welcher Reihenfolge sie bei der Rekursion berechnet werden. Sei eval die Auswertungsfunktion zu einer L-Attributierten Grammatik. Die Argumente von eval sind: ein Knoten des Syntaxbaums das Tupel der geerbten Attribute dieses Knotens Das Ergebnis von eval ist das Tupel der synthetischen Attribute des Knotens eval kann wie folgt rekursiv definiert werden: Wenn n ein Blatt mit Terminalzeichen a ist, dann existieren keine geerbten Attribute und eval(n) liefert das Tupel der synthetischen Attribute. 7

Wenn n ein innerer Knoten mit geerbten Attributen inh ist und wenn n i (i = 1,..., k) sind, die Kinder von n mit geerbten Attributen inh i, dann lässt sich eval(n, inh) rekursiv berechnen durch: Algorithmus 2. (1) for i = 1 to k do * berechne inh i mit den semantischen Regeln aus inh und den zuvor berechneten syn j, inh j (j < i) * berechne syn i durch rekursiven Aufruf eval(n i, inh i ) (2) berechne syn (die synthetischen Attribute von n) mit den semantischen Regeln aus den in (1) berechneten Attributen Durch die rekursive Definition von eval ist die Auswertungsreihenfolge definiert. Wenn die geerbten links und die synthetischen rechts stehen, lässt sie sich wie folgt veranschaulichen: Intuition: Man startet mit den geerbten Attributen der Wurzel und geht am Baum entlang bis man bei den synthetischen Attributen der Wurzel ankommt. Umsetzung in ein Programm: Im Aufruf eval(n, inh) hängen Anzahl und Typ der geerbten Attribute, im Tupel inh hängen von der Markierung des Knotens n ab. Man muss eval aufteilen in Funktionen eval X (für jedes Nonterminal X), die sich gegenseitig rekursiv aufrufen. eval X ist zuständig für alle Knoten mit Markierung X. Speziell: Da das Startzeichen S keine geerbten Attribute besitzt, erhält man die synthetischen Attribute der Wurzel n durch den Aufruf eval(n). Beachte: 1. Die rekursive Struktur (Aufrufstruktur) der Funktionen eval X gleiche wie beim Recursive Descent Parser (RDP). ist die Deshalb gilt: Wenn für die KFG ein RDP existiert (z. B. wenn sie LL(1) ist), dann lässt sich die Attributauswertung] (für eine L-Attributierung) in den RDP einbauen. Idee: Die parameterlose Prozeduren A() aus dem RDP ersetzt durch Prozeduren A(inh), die die geerbten Attribute als Parameter haben und die synthetischen Attribute als Ergebnis liefern. Die neuen Prozeduren arbeiten gleichzeitig das Eingabewort ab und berechnen die Attribute. 8

Beispiel: 8. Nicht linksrekursive KFG für arithmetische Ausdrücke mit L-Attributierung (1) T F T T.inh = F.val T.val = T.syn (2) T T T 1 T 1.inh = T.inh F.val T.syn = T 1.syn (3) T ɛ T.syn = T.inh (4) F digit F.val = digit.lexval Laut Parsing-Tabelle: (1) bei Eingabe digit (2) bei Eingabe (3) bei Eingabe $ (4) bei Eingabe digit Prozeduren T (), F () und T (inh) Algorithmus 3. T(): if input digits then T (F()) (denn F () liefert F.val = T.inh Argument für T else error Resultat T.syn = T.val) T (inh): if input = * then move(); T (inh F ()) else if input = $ then inh else error else error F(): let a = input in if a digits then move(); a (F () liefert F.val und T.inh F.val = T 1.inh ist Argument T 1 Resultat T 1.syn = T.syn (wegen T.syn = T.inh) 9

3 Syntaxgerichtete Übersetzungsschemata (ÜS) Englische Bezeichnung: syntax directed translation scheme (SDT) Eine SDT ist eine KFG, bei der Programmstücke in die rechten Seiten von Produktionen eingebetet sind. Die Programmstücke heißen semantische Aktionen. Sie können an jeder Stelle der rechten Seite zwischen {... } auftreten. Beispiel: 9. Umwandlung von Infix- in Präfix-Notation (1) L En (2) E { print + } E 1 + T (3) E T (4) T { print } T 1 F (5) T F (6) F (E) (7) F number { print(number.val)} 3.1 Semantik eines SDT Die Aktionen sind in der Reihenfolge auszuführen, in der sie im Blattwort des Ableitungsbaums auftauchen. Beispiel: 10. Ableitungsbaum für 3 5 + 4n Also mögliche Implementierung: preorder-reihenfolge ausführen. Ableitungsbaum aufbauen, dann die Aktionen in Frage: Kann man SDT s ausführen, ohne den Syntaxbaum aufzubauen, z. B. während LL- oder LR-parsing? Antwort: Im Allgemeinen nicht, siehe Beispiel. 3.2 Übersetzung von S- bzw. L-attributierten Grammatiken in SDT s Eine S-attributierte Grammatik kann in eine sogenannte Postfix-SDT übersetzt werden, bei der alle Aktionen am Ende der Produktion stehen. Beispiel: 11. KFG für arithmetische Ausdrücke: E E 1 + T {E.val = E 1.val + T.val} E T {E.val = T.val}. 10

Postfix-SDT s können (analog zu S-attributierten Grammatiken) während des bottomup-parsing ausgeführt werden: Die semantischen Aktionen, die hinter einer Regel steht, wird gleichzeitig mit den entsprechenden reduce-schritt ausgeführt. Attributwerte werden dabei auf einem Stack verwaltet, der stehts die gleiche Höhe hat, wie der Zeichen- oder Zustandskeller. Eine L-attributierte Grammatik kann in eine SDT übersetzt werden, indem man für jede Produktion folgendes durchführt: 1. Die Aktionen zur Berechnung der geerbten Attribute eines Nonterminals auf der rechten Seite schreibt man unmittelbar vor dieses Nonterminal (wobei man noch auf die Reihenfolge der verschiedenen Attribute dieses Nonterminals achten muss). 2. Die Aktionen zur Berechnung der synthetischen Attribute (zur linken Seite der Produktion) schreibt man ans rechte Ende der Produktion. Beispiel: 12. Nicht linksrekursive KFG für arithmetische Ausdrücke Ableitungsbaum für 3 5: (1) T F {T.inh = F.val} T {T.val = T.syn} (2) T F {T 1.inh = T.inh F.val} T 1 {T.syn = T 1.syn} (3) T ɛ {T.syn = T.inh} (4) F digit {F.val = digit.lexval} 4 Generierung von Zwischencode Hier: Als Zwischencode wird 3-Adresscode verwendet. Befehle dürfen folgende Form haben: 1. Zuweisungen der Form x = y op z wobei op binärer Operator, x,y,z Adressen 2. Zuweisungen der Form wobei op unärer Operator 3. Kopier-Befehl x = op y x = y 4. Unbedingte Sprünge wobei L eine Marke im Programm ist goto L 11

5. Bedingte Sprünge if x goto L if F alse x goto L 6. Bedingte Sprünge mit Vergleich if x relop y goto L wobei relop ein Vergleichsoperator ist (<,,... ) 7. Kopierbefehl mit Indices x = y[i] y[i] = x Dabei ist y[i] der Speicherplatz, der i Einheiten hinter y steht 8. Kopierbefehl mit Adressrechnung x = &y x = y y = x Dabei steht &y für die Adresse y selbst (statt für den Inhalt von y) und y steht für die Adresse, die durch den Inhalt von y gegeben ist. 12

4.1 Übersetzung arithmetischer Ausdrücke in 3-Adress-Code Idee: Bei der Übersetzung eines geschachtelten Ausdrucks müssen genügend temporäre Adressen generiert werden, in denen man Zwischenergebnisse speichert. Befehl new T emp() liefert stehts eine neue temporäre Adresse. Man benutzt 2 Attribute für das Nonterminal E, nämlich: { E.code beide synthetisch E.addr E.code ist der 3-Adress-Code für E. E.addr ist die Adresse, in der E.code das Ergebnis E abliefert. S-Attributierte Grammatik Produktion semantische Regel (1) S id = E S.code = E.code top.get(id.lexem) = E.addr } {{ } aktuelle Adresse für Token id (2) E E 1 + E 2 E.addr = new T emp() E.code = E 1.code E 2.code E.addr = E 1.addr + E 2.addr (3) E E 1 E.addr = new T emp() E.code = E 1.code E.addr = minus E 1.addr (4) E (E 1 ) E.addr = E 1.addr E.code = E 1.code (5) E id E.addr = top.get(id.lexem) E.code = Besser: Anstelle eines Attributs vom Typ string semantische Aktionen benutzen, die den Code ausgeben, d. h. in den Programmspeicher schreiben. 4.2 Übersetzung boolscher Ausdrücke Boolsche Ausdrücke treten in zwei Rollen auf: 1. zur Berechnung boolscher Werte (analog zu arithmetischen Ausdrücken) 2. Zur Steuerung des Kontrollflusses, d. h. als Bedingung in if-then-else, while,... Im Fall 1 übersetzt man sie wie arithmetische Ausdrücke, im Fall 2 generiert man besser sogenannten Jump Code. Jeder boolsche Ausdruck B hat 3 Attribute: 13

B.code synthetisch { B.true geerbt B.f alse B.code ist der 3-Adress-Code für B. B.true ist die Marke, zu der B.code springt, wenn B true liefert. B.false ist die Marke, zu der B.code springt, wenn B false liefert. 4.3 Jumpcode für boolsche Ausdrücke Produktion B B 1 B 2 B B 1 && B 2 B!B 1 B E 1 rel E 2 B true B false semantische Regel B 1.true = B.true B 1.false = newlabel() B 2.true = B.true B 2.false = B.false B.code = B 1.code label(b 1.false) B 2.code B 1.true = newlabel() B 1.false = B.false B 2.true = B.true B 2.false = B.false B.code = B 1.code label(b 1.true) B 2.code B 1.true = B.false B 1.false = B.true B.code = B 1.code B.code = E 1.code E 2.code gen( if E 1.addr rel.op E 2.addr goto B.true) gen( goto B.true) B.code = gen( goto B.true) B.code = gen( goto B.f alse) Erläuterung: B B 1 B 2 : Wenn B 1 = true, dann B = true (ohne dass B 2 ausgewertet wird), also B 1.true = B.true (d. h. beide haben in diesem Fall das gleiche Sprungziel). Wenn B 1 = false, muss B 2 noch ausgewertet werden, also muss man an den Anfang von B 2 springen, also B 2.false = newlabel() wird in B.code verwendet. Wenn B 2 = true bzw B 2 = false, dann ist auch B = true bzw B = false (weil 14

man B 2 nur auswertet, wenn B 1 = false), also werden beide Sprungziele einfach vererbt: B 2.true = B.true, B 2.false = B.false. B!B 1 : B hat gleichen Code wie B 1. Nur die Sprungziele sind vertauscht. B E 1 rel E 2 : E 1.code und E 2.code liefern ihre Resultate in E 1.addr und E 2.addr ab. Also muss man anschließend die Inhalte der beiden Adressen vergleichen (mit rel.op) und in Abhängigkeit vom Ergebnis des Vergleichs zu B.true bzw. B.f alse springen. Wo kommen B.true und B.false her? Entweder von einem größeren boolschen Ausdruck, in dem B enthalten ist oder von der Anweisung, in der B als Bedingung enthalten ist. Anweisungen S haben 2 Attribute, nämlich: S.code S.next synthetisch geerbt vom Typ Label S.next ist die Marke, bei der es nach der Ausführung von S.code weitergeht. Genauer: Die Ausführung von S.code endet entweder mit einem Sprung zu S.next oder sie endet ohne Sprung (so dass es hinter S.code weitergeht). Produktion P S S assign S if( B ) S 1 S if( B ) S 1 else S 2 semantische Regel S.next = newlabel() P.code = S.code label(s.next) S.code = assign.code B.true = newlabel() B.false = S 1.next = S.next S.code = B.code label(b.true) S 1.code B.true = newlabel() B.f alse = newlabel() S 1.next = S 2.next = S.next S.code = B.code label(b.true) S 1.code gen( goto S.next) label(b.false) S 2.code 15

Produktion S while( B ) S 1 S S 1 S 2 semantische Regel begin = newlabel() B.true = newlabel() B.false = S.next S 1.next = begin S.code = label(begin) B.code label(b.true) S 1.code gen( goto begin) S 1.next = newlabel() S 2.next = S.next S.code = S 1.code label(s 1.next) S 2.code Erläuterungen: P S: Mit newlabel() wird das Programmende markiert S.next = Programmende. S assign: Code ist vorgegeben. 16

4.4 Backpatching 4.4.1 Boolsche Ausdrücke 4.4.2 Anweisungen 5 Laufzeitumgebungen 5.1 Speicherorganisation 5.2 Aufteilung zwischen Stack und Heap 5.3 Zugriff auf globale Daten 5.4 Bestimmung des Access Links 5.5 Prozeduren auf Parameterposition 5.6 Alternative zu Access Links: Displays 5.7 calling sequences und return sequences 5.7.1 Design-Prinzipien 5.7.2 Aufteilung der calling sequence 5.7.3 Codeerzeugung für Programme mit Prozeduren (Pascal-artige Sprache) 5.7.4 Vorgehensweise bei der Übersetzung 5.7.5 Übersetzung einer Prozedurdeklaration 5.7.6 Übersetzung eines Prozeduraufrufs 6 Codegenerierung (Drachenbuch Kpt. 8) 6.1 Befehlssatz der Zielmaschine (RISC-Architektur) 6.2 Basisblöcke und Flussgraphen 6.3 Optimierung der Basisblöcke 6.4 Vom (optimierten) DAG zurück zum Zwischencode 6.5 Ein einfacher Codegenerator 6.6 Globale Register Vergabe 7 Instruction Selection (Drachenbuch Kpt. 8.9) 7.1 Generierung von optimalem Code für Ausdrücke 7.1.1 Algorithmus zur Berechnung der Ershov-Zahl 17