Übersetzertechnik (in Arbeit!!!)

Slides:



Advertisements
Ähnliche Präsentationen
Algorithmentheorie 08 – Dynamische Programmierung (1)
Advertisements

ALP II: Objektorientierte Programmierung Sommersemester 2006
der Universität Oldenburg
der Universität Oldenburg
der Universität Oldenburg
der Universität Oldenburg
Vorlesung Compilertechnik Sommersemester 2008
Vorlesung Compilertechnik Sommersemester 2008
Objektorientierte Programmierung
Informatik 12 | DAES Compilerbau Wintersemester 2010 / 2011 Dr. Heiko Falk Technische Universität Dortmund Lehrstuhl Informatik 12 Entwurfsautomatisierung.
Einführung in die Programmierung Zusammenfassung
Lineare Suche Divide-and-Conquer-Suche Kombinationssuche
Institut für Informatik Abt. Intelligente Systeme
10. Grundlagen imperativer Programmiersprachen
Imperative Programmierung
der Universität Oldenburg
der Universität Oldenburg
Christos, Kornelia, Jan Christos, Kornelia, Jan Entwicklungsumgebung Versteht unseren Java Programm Code Versteht unseren Java Programm.
Christos, Kornelia, Jan Christos, Kornelia, Jan Entwicklungsumgebung Versteht unseren Java Programm Code Versteht unseren Java Programm.
Kapitel 4 Syntaktische Analyse: LR Parsing.
Parser generieren Yet Another Compiler – Compiler YACC.
FH-Hof Einbindung von JavaScript Anweisungen
Java: Objektorientierte Programmierung
Java: Dynamische Datentypen
Indirekte Adressierung
FH-Hof Indirekte Adressierung Richard Göbel. FH-Hof Einfache Speicherung von Daten Eine "einfache" Deklaration definiert direkt eine Speicherplatz für.
Java: Grundlagen der Sprache
Java: Referenzen und Zeichenketten
Java: Grundlagen der Objektorientierung
Polymorphie (Vielgestaltigkeit)
Objekte und Arbeitsspeicher
Dynamischer Speicher. In einer Funktion wird z.B. mit der Deklaration int i; Speicher auf dem sogenannten Stack reserviert. Wenn die Funktion verlassen.
1 Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Prof. Dr. Th. Ottmann.
Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Tobias Lauer.
Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Prof. Dr. Th. Ottmann.
Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 2 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.
Parsing regulärer Ausdrücke
EINI-I Einführung in die Informatik für Naturwissenschaftler und Ingenieure I Vorlesung 2 SWS WS 99/00 Gisbert Dittrich FBI Unido
Imperative Programmierung
Beispiele für Ausdrucksalgebren
Agenda Motivation Formale Sprachen Compiler Compilerentwicklung
Praxis-Repetitorium JAVA zusätzliche, ergänzende Lehrveranstaltung
PKJ 2005/1 Stefan Dissmann Klassenhierarchie Person Kunde Goldkunde Lieferant Object.
Fachgebiet Software Engineering Übersicht © Albert Zündorf, Kassel University Compilerbau und Reverse Engineering m Vorlesung im Wintersemester.
Programmierung 1 - Repetitorium WS 2002/2003 Programmierung 1 - Repetitorium Andreas Augustin und Marc Wagner Homepage:
DVG Klassen und Objekte
Einführung in die Programmierung Datensammlung
Von der Sprache zum Programm
Programmiersprachen II Integration verschiedener Datenstrukturen
BIT – Schaßan – WS 02/03 Basisinformationstechnologie HK-Medien Teil 1, 11.Sitzung WS 02/03.
Einführung in die Programmierung Wintersemester 2009/10 Prof. Dr. Günter Rudolph Lehrstuhl für Algorithm Engineering Fakultät für Informatik TU Dortmund.
OOP-Begriffe Abstraktion Modellieren Klasse Objekt Attribute Methoden
Dynamische Datentypen
2.4 Rekursion Klassifikation und Beispiele
Grundlagen Wissenschaftlichen Arbeitens Hilal Tekoglu
Automaten, formale Sprachen und Berechenbarkeit II SoSe 2004 Prof. W. Brauer Teil 1: Wiederholung (Vor allem Folien von Priv.-Doz. Dr. Kindler vom WS 2001/02.
Städtisches Gymnasium Beverungen Friedel Berlage
MODULA-2.
Automaten, formale Sprachen und Berechenbarkeit II SoSe 2004 Prof. W. Brauer Teil 3: Potenzreihen und kontextfreie Sprachen (Vgl. Buch von A. Salomaa)
Informatik I : Software höhere Programmiersprachen Java Klassen: hat Methoden (Funktionen) und Daten (Variablen) es kann mehrere Klassen geben nur eine.
Agenda Motivation und Einordnung Syntaxgerichtete Übersetzung
Java Syntaxdiagramme Buchstabe A B Z a z ... Ziffer
Mensch – Maschine - Kommunikation
Inhalt Einordnung und Funktion der lexikalische Analyse Grundlagen
Semantische Analyse und attributierte Grammatiken
Mag. Thomas Hilpold, Universität Linz, Institut für Wirtschaftsinformatik – Software Engineering 1 Algorithmen und Datenstrukturen 1 SS 2002 Mag.Thomas.
The Programming Language Pascal
Wann ist eine Funktion (über den natürlichen Zahlen) berechenbar?
Implementieren von Klassen
 Präsentation transkript:

Übersetzertechnik (in Arbeit!!!) © Günter Riedewald Die Folien sind eine Ergänzung der Vorlesung und nur für den internen Gebrauch konzipiert.

Motivation Programmiersprachen als wichtiges Mittel der Softwaretechnik  Weiterentwicklung der Softwaretechnik erfordert neue Sprachen einschließlich deren Implementation Programmiersprachen: - universell (Universalsprachen, general purpose languages): allgemein einsetzbar und unabhängig von einem konkreten Einsatzgebiet; Compilerentwicklung durch Spezialisten

- spezialisiert (Fachprogrammiersprachen, Spezialsprachen, special purpose languages, domain specific languages): in Syntax und Semantik zugeschnitten auf ein bestimmtes Fachgebiet; wegen stetig wachsendem Bedarf Entwicklung von Compilern/Interpretern auch durch Nichtspezialisten Probleme des Übersetzerbaus treten auch in anderen Gebieten der Informatik auf (z.B. XML) Abstrakte Betrachtung einer Übersetzung: - Analyse der Struktur der Eingabedaten - Strukturabhängige Erzeugung der Ausgabedaten

Gute Kenntnisse über Programmiersprachen und deren Compiler als Voraussetzung für Qualitätssoftware Didaktische Gründe: - Exemplarisches Vorgehen für die Entwicklung größerer Softwaresysteme: klarer logischer Aufbau, Modularisierung, Nutzung von Werkzeugen, Wartbarkeit, Wiederverwendbarkeit - Fachübergreifende Rolle bzgl. Voraussetzungen und eingesetzten Techniken: Automatentheorie, Theorie der formalen Sprachen, Theorie der Programmiersprachen, Prinzipien der Softwaretechnik, Rechnerarchitektur/ Rechnersysteme ...

- Enger Zusammenhang zwischen Theorie und Praxis: gute theoretische Basis als Voraussetzung der Automatisierung der Compilerentwicklung  Vorbildcharakter für andere Informatikbereiche

Literatur A.V. Aho, J.D. Ullman: The Theory of Parsing, Translation and Compiling, Prentice Hall, 1972 A.V. Aho, J.D. Ullman: Principles of Compiler Design Addison-Wesley, 1977 A.V. Aho, R. Sethi, J.D. Ullman (2007 zusätzlich: M. S. Lam): Compilers Principles, Techniques and Tools, (Drachenbuch), Addison Wesley, 1986, 2007

A.V. Aho, R. Sethi, J.D. Ullman: Compilerbau, (Drachenbuch), Addison-Wesley, 1988, 1990 H. Alblas, A. Nymeyer: Practice and Principles of Compiler Building with C, Prentice Hall, 1996 A. W. Appel: modern compiler implementation in Java (in C) – basic techniques, Cambridge University Press, 1997 J. Elder: Compiler Construction – A Recursive Descent Model, Prentice Hall, 1994 D. Gries: Compiler Construction for Digital Computers, Wiley, 1971 In Russ.: Konstruirovanije kompiljatorov dlja cifrovych vyčislitelnych mašin, Mir, 1975

J. Holmes: Object-Oriented Compiler Construction, Prentice-Hall, 1995 U. Kastens: Übersetzerbau, Oldenbourg Verlag, 1990 H. Loeper, W. Otter, H.-J. Jäkel: Compiler und Interpreter für höhere Programmiersprachen, Akademie-Verlag, 1987 K. C. Louden: Compiler Construction – Principles and Practice, PWS Publ. Com., 1997 S. S. Muchnik: Advanced Compiler Design and Implementation, Morgan Kaufmann Publishers, 1997

S. Naumann, H. Langer: Parsing, Teubner Stuttgart, 1994 P. Rechenberg, M. Mössenbeck: Ein Compiler-Generator für Mikrocomputer, Hanser Verlag, 1985 W.M. Waite, G. Goos: Compiler Construction, Springer, 1984 D.A. Watt: Programming Language Processors, Prentice Hall International Series in Computer Science, 1993 D.A. Watt, D. F. Brown: Programming Language Processors in Java – Compilers and Interpreters, Prentice-Hall, 2000

R. Wilhelm, D. Maurer: Übersetzerbau – Theorie, Konstruktion, Generierung, Springer-Lehrbuch, 1992 N. Wirth: Compilerbau, Teubner Studienbücher Informatik, Teubner Stuttgart, 1986 P. D. Terry: Compilers & Compiler Generators – An Introduction with C++, Intern. Thomson Computer Press, 1997 H. Zima: Compilerbau I, II , BI Mannheim, Reihe Informatik 36,37, 1982, 1983

Translatoren - Arten Assemblierer (Assembler): übersetzt aus einer Sprache niederen Niveaus (Sprache mit symbolischer Adressierung, Assemblersprache) in Maschinensprache meist im Verhältnis 1:1 Makroassembler: übersetzt aus einer Sprache niederen Niveaus in Maschinensprache meist im Verhältnis 1:n mit n  1 Compiler: übersetzt aus Sprache von hohem Niveau in Maschinensprache (letztendlich)

Präprozessor: übersetzt aus Obermenge zu einer Sprache in Ausgangssprache Translator auf hohem Niveau: übersetzt aus Sprache hohen Niveaus in andere Sprache hohen Niveaus Decompiler (Disassembler): Regeneriert Quellcode aus Zielcode Self-resident translator: Erzeugung von Zielcode für die Wirtsmaschine (host) Cross-translator: Erzeugung von Zielcode für andere Maschine Interpreter: anweisungsweise Übersetzung und sofortige Ausführung von Quellcode

Interpretativer Compiler: Übersetzung aus einer Sprache hohen Niveaus in eine Zwischensprache mit anschließender Interpretation Emulator: Interpretation des Codes einer anderen Maschine

1 Übersetzerbau - Einführung 1.1 Symbolische Darstellungen Übersetzer Übersetzung Programm P Übersetzer Programm P von P in Quell- in I in Zielsprache Z sprache Q Abarbeitung Eingabe- P Ausgabe- von P daten in Z daten

Interpretation Programm P Inter- Ausgabedaten von P in Quell- preter sprache Q Eingabedaten T-Diagramme (tombstones) Übersetzer P P Q Q  Z Z I

Abarbeitung eines Programms E P A Z Interpreter Q

Modifikationen der T-Diagramme Programm P in L geschrieben P L Maschine M M Bezeichneter Compiler <Name>

1.2 Einführungsbeispiel Übersetzung von Pascal in P-Code P-Code: - Sprache der P-Maschine (abstrakter Rechner) - Zwischensprache in Compilern P P P Pascal Pascal  P-Code P-Code P-Code  M M M M

1.2.1 Implementierungsprinzipien für ausgewählte imperative Sprachkonzepte Imperative Konzepte Variablenkonzept: - Variable als Modell für einen Speicherplatz - Möglichkeit des Lesens und der Wertänderung (Schreiben)  Compilersicht: Speicherplatzzuordnung + Zugriff zum Wert als Ganzes oder in Teilen + Lebensdauer + Gültigkeitsbereich (Zugriffsrechte) Strukturierungskonzept für Datenstrukturen  Compilersicht: Abspeicherungskonzept + Zugriff

Sprachkonzepte zur Ablaufsteuerung  Compilersicht: Umsetzung in Sprünge unterschiedlicher Art Prozedurkonzept: - Nichtrekursive Prozedur  Compilersicht: Ersetzen des Aufrufs durch Anweisungen der Prozedur oder Sprünge mit Rückkehr zur Anweisungsfolge - Rekursive Prozedur  Compilersicht: mehrere gleichzeitige Aufrufe erfordern mehrere Inkarnationen des Speicherbereichs der Prozedur (Verwendung von Kellertechnik) - Parameterübergabe  Compilersicht: zusätzliche Anweisungen

Programmspeicher CODE P-Maschine Datenspeicher STORE 0 SP maxstr Programmspeicher CODE 0 PC codemax Befehlsausführung: Steuerung durch Steuerschleife do PC := PC + 1; Ausführung des Befehls in CODE[PC-1] od - Initialisierung PC := 0

1.2.2 Ausgewählte Sprachkonstrukte und ihre Übersetzung Voraussetzungen: Die Kontextbedingungen sind erfüllt (durch semantische Analyse geprüft). Alle Konstrukte sind syntaktisch eindeutig zerlegbar (Durchführung durch syntaktische Analyse). Ausdrücke Übersetzungsfunktion: - code: Ausdruck x Umgebung  P-Code (Umgebung: u: Variablen  Adressen)

Unterschiedliche Ausdruckbehandlung auf linker und rechter Seite einer Zuweisung  codeR : Übersetzung in Befehle zur Berechnung eines Werts codeL : Übersetzung in Befehle zur Berechnung einer Adresse Wertzuweisung code〚x := e〛u = codeL〚x〛u ; codeR〚e〛u ; sto T T Typ; u Umgebung; x Variable; e Ausdruck

Adresse von x (u(x) = ); w Wert von e STORE nach w  Wirkung von sto: STORE vor   w Abarbeitung  SP-2 SP Adresse von x (u(x) = ); w Wert von e STORE nach w   SP

codeR〚e1 + e2〛u = codeR〚e1〛u; codeR〚e2〛u; add T T Typ; e1, e2 Ausdrücke Wirkung von add: STORE vor  w1 w2 Abarbeitung SP STORE nach  w Abarbeitung w = w1 + w2

codeR〚c〛u = ldc T c, c Konstante vom Typ T STORE vor  Abarbeitung SP STORE nach  c

codeL〚x〛u = ldc a u(x) , x Variable STORE vor  Abarbeitung SP STORE nach  u(x)

codeR〚x〛u = codeL〚x〛u; ind T = ldc a u(x); ind T , x Variable STORE vor w  Abarbeitung u(x) =   SP STORE nach w   Abarbeitung von ldc a u(x)  SP STORE nach w  w von ind T  SP

Beispiel: Übersetzung von x := (x + (3 + y)) , x, y vom Typ i code〚 x := (x + (3 + y))〛u = codeL〚 x〛u; codeR〚(x + (3 + y))〛u; sto i = ldc a u(x); codeR〚 x〛u; codeR〚(3 + y)〛u; add i; sto i = ldc a u(x); ldc a u(x); ind i; codeR〚3〛u; codeR〚y〛u; add i; add i; sto i = ldc a u(x); ldc a u(x); ind i; ldc i 3; ldc a u(y); ind i; add i; add i; sto i

Abarbeitungsschritte: u(x) = , u(y) = , w1 Wert von x, w2 Wert von y, w3 = 3 + w2, w4 = w1 + w3   STORE-Zustände w1 w2 k w1 w2 k  w1 w2 k   w1 w2 k  w1 w1 w2 k  w1 3 w1 w2 k  w1 3  w1 w2 k  w1 3 w2 w1 w2 k  w1 w3 w1 w2 k  w4 w4 w2 k

Bedingte Anweisungen code〚if e then st1 else st2 fi〛u = codeR〚e〛u; fjp l1; code〚st1〛u; ujp l2; l1: code〚st2〛u; l2:... Wirkung von fjp l1: k false  k PC := l1 k true  k Wirkung von ujp l2: PC := l2 Anweisungsfolgen code〚st1; st2〛u = code〚st1〛u; code〚st2〛u

code〚while e do st od〛u = Wiederholungen code〚while e do st od〛u = l1: codeR〚e〛u; fjp l2; code〚st〛u; ujp l1; l2:... Beispiel: code〚while a > b do a := a - b od〛u = l1: codeR〚a > b〛u; fjp l2; code〚 a := a - b 〛u; ujp l1; l2:...= l1: ldc a u(a); ind i; ldc a u(b); ind i; grt i; fjp l2; ldc a u(a); ldc a u(a); ind i; ldc a u(b); ind i; sub i; sto i; ujp l1; l2:...

1.3 Logische Compilerstruktur Physische Compilerstruktur Treiber Syntaktische Semantische Semantische Analyse Analyse Synthese Lexikalische Analyse

Physische Compilerstruktur (Fortsetzung) Treiber Syntaktische Analyse Lexikalische Semantische Semantische Analyse Analyse Synthese

Logische Compilerstruktur (1) Quellprogramm Analyse Lexikalischer Datentabellen Analysator Codierte Terminalfolge Syntaktischer Datenmodule Syntaxbaum Semantischer Dekorierter Semantische Synthese Zielprogramm

Logische Compilerstruktur (2) Quellprogramm Lexikalische Analyse Symbol- Syntaktische Fehlerbehandlung tabellen- Analyse verwaltung Semantische Zwischencode- Code- Code- Erzeugung Optimierung Erzeugung Zielprogramm

2 Lexikalischer Analysator Aufgabe: Transformation des Quellprogramms aus einer Zeichenfolge in eine codierte Folge von Terminalen (Tokenfolge, Grundsymbolfolge) mit Sammlung spezieller Informationen in Tabellen Teilaufgaben: Auffinden und Erkennen eines Lexems (≙ Terminal) Codierung der Lexeme Auslassung überflüssiger Zeichenfolgen Erstellung von Datentabellen

2.1 Auffinden und Erkennen von Lexemen Fakt: Lexeme sind überwiegend durch reguläre Grammatikregeln beschreibbar.  Es existieren endliche Automaten, die genau diese Lexeme akzeptieren. Beispiel: <ganze Zahl> ::= 0|1|...|9|0 <ganze Zahl>|...|9 <ganze Zahl> <Identifikator> ::= A|...|Z|A <Id1>|...|Z <Id1> <Id1> ::= A|...|Z|0|...|9| A <Id1>|...|9 <Id1> <Beschränker> ::= +|-|(|)|/|/ <schräg>|* <Stern>|: <gleich>|;|, <schräg> ::= / <Stern> ::= * <gleich> ::= =

Endliche Automaten zu den Regeln: 0,. ,9 S gZ 0,. ,9 A,. ,Z S Id A, Endliche Automaten zu den Regeln: 0,...,9 S gZ 0,...,9 A,...,Z S Id A,...,9 + - ( ) ; , * * S S St / / : = S gl

Endlicher Automat für Akzeptanz aller Lexeme: A,...,Z A,...,Z,0,...,9 0,...,9 + - ( ) ; , * * / : =

2.2 Kodierung der Lexeme 1 Identifikator 9 ** 2 ganze Zahl 10 ( Kodierung Lexem Kodierung Lexem 1 Identifikator 9 ** 2 ganze Zahl 10 ( 3 BEGIN 11 ) 4 END 12 // 5 REAL 13 := 6 / 14 ; 7 + 15 . 8 -

Semantische Aktionen ADD Aufsammeln von Zeichen eines Lexems in einer Puffervariablen A GC Lesen des nächsten Quellprogrammzeichens, Zwischenablage in der Variablen Char sowie Bestimmung der Art des Zeichens LOOKUP Bestimmung der Kodierung des in A abgelegten Lexems gemäß Tabelle der internen Kodierung (Kodierung 1 bei Nichtvorhandensein) OUT(C,A) Ausgabe eines Lexems (in A) zusammen mit seiner Kodierung (in C) GPC Überlesen aller bedeutungslosen Zeichen und Fortsetzung wie GC sowie Löschen von A

Endlicher Automat mit semantischen Aktionen: A,...,Z LOOKUP(A,C);OUT(C,A) ADD;GC A,...,Z,0,...,9 ADD;GC 0,...,9 OUT(2,A) ADD;GC 0,...,9 GPC ADD;GC + - ( ) ; , ADD;GC LOOKUP(A,C);OUT(C,A) * * ADD;GC ADD;GC OUT(9,A) / ADD;GC / OUT(6,A) ADD;GC OUT(12,A) : = ADD;GC ADD;GC OUT(13,A)

2.5 Automatische Erzeugung von lexikalischen Analysatoren Lexembeschreibung Lexikalischer (reguläre Ausdrücke, Analysator - semantische Rahmensystem Aktionen) Generator Steuerung

Lex-Spezifikation <Deklarationsteil> %% <regulärer Ausdruck> {<semantische Aktion in C>} ... <Nutzerunterprogramme>

Beispiel: # include ´´y.tab.h´´ extern int yyz, yyli; extern symbol *lookup(), *install(); %% ´´\n´´ {yyli ++;} [ ] ; [0-9][0-9]* {symbol *s; if ((s=lookup(yytext))==0) s=install(yytext); ...; return (ZAHL);} if {return (IF);} ... true {keys *k; k=lookkey(yytext); yylval.yyk=k; return (TRUE);} [a-z][a-z 0-9]* {symbol *s; if ((s=lookup(yytext))==0) s=install(yytext); yylval.yysym=s; return (IDENTIFIKATOR);}

3 Syntaktische Analyse Beispiel: Aus  Id ( Aus ) Aus  Id [ Aus ] Id  f Id  z

Syntaxbaum von f(a[z]) Aus Top-down Analyse 11 Id Aus 12 15 Id Aus 13 14 Id Bottom-up Analyse 16 f ( a [ z ] )

3.1 Kellerspeicher in der Syntaxanalyse Top-down Analyse - Grundalgorithmus Voraussetzung: Verwendung eines Analysekellers Schritte des Algorithmus´: Startsymbol in den Keller Wiederholung folgender Schritte bis zum Erreichen eines leeren Kellers: - Nichtterminal an Kellerspitze: Expansion (Ersetzen des Nichtterminals durch rechte Seite einer geeigneten Syntaxregel) - Terminal an Kellerspitze: Vergleich mit aktuellem Element des analysierten Wortes; bei Gleichheit Streichen aus dem Keller, nächstes Element des Wortes wird aktuelles Element; bei Ungleichheit Backtracking

Beispiel:Top-down Analyse von f(a[z]) Aktuelles Element Keller (Spitze links) Operation f(a[z]) Aus Aus  Id ( Aus ) f(a[z]) Id ( Aus ) Id  f f(a[z]) f ( Aus ) 2x Vergleich a[z]) Aus ) Aus  Id [ Aus ] a[z]) Id [ Aus ] ) Id  a a[z]) a [ Aus ] ) 2x Vergleich z]) Aus ] ) Aus  Id  z z]) z] ) 3x Vergleich 

Bottom-up Analyse - Grundalgorithmus Schritte des Algorithmus´: Start mit leerem Keller Wiederholung folgender Schritte, bis das Startsymbol allein im Keller ist: - Reduktion: nur möglich, wenn an der Kellerspitze die rechte Seite einer Regel ist ( Ersetzen durch linke Seite) - Einlesen (Verschiebung): Einlesen des nächsten Elements des Wortes in den Keller

Beispiel: Bottom-up Analyse von f(a[z]) Restwort Keller (Spitze rechts) Operation f(a[z])  Lesen f (a[z]) f Reduktion f zu Id (a[z]) Id Lesen (a [z]) Id (a Reduktion a zu Id [z]) Id ( Id Lesen [z ]) Id ( Id [z Reduktionen z zu Id, Id zu Aus ]) Id ( Id [ Aus Lesen ] ) Id ( Id [ Aus ] Reduktion Id [ Aus ] zu Aus ) Id ( Aus Lesen )  Id ( Aus ) Reduktion Id ( Aus ) zu Aus  Aus

3. 2 Deterministische Syntaxanalyse 3. 2 3.2 Deterministische Syntaxanalyse 3.2.1 Top-down Verfahren - Methode des rekursiven Abstiegs Grundmethode: Idee: Syntaktische Regel A  ...wird als Prozedur zur Erkennung von Zeichenketten aus L(A) betrachtet, wobei die rechte Seite der Regel die Struktur vorgibt: Terminal: erwartetes Terminal Nichtterminal: Aufruf der entsprechenden Prozedur Alternativen: Code-Alternativen in der Prozedur

Beispiel: Prozedur zur Regel T  ( Aus )| [ Aus ] procedure T; begin case token of (: match( ( ); Aus; match( ) ); [: match( [ ); Aus; match( ] ); else error; end case; end T; Vor.: token enthält aktuelles Terminal der zu analysierenden Zeichenkette

Definition der Prozedur match: procedure match(expectok); begin if token = expectok then gettoken else error fi; end match; gettoken liest das nächste Zeichen aus der Zeichenkette und ordnet es token zu.

Konstruktion der Zerlegungsprozeduren N  X : procedure N; <parse X> <parse X> bedeutet Anweisungen zur Erkennung von X, wobei - X =  einer Leeranweisung - X = t (Terminal, token) match(t) - X = M (Nichtterminal) M (Aufruf der Prozedur) - X = A B <parse A>; <parse B>

- X = A| B case token of e1: <parse A>; e2: <parse B>; else error; end case; e1  DS(N, A), e2  DS(N, B) - X = A* while token  DS(N, A) do <parse A> od entspricht.

Definition der Entscheidungsmenge (director set) Vor. : A  1| Definition der Entscheidungsmenge (director set) Vor.: A  1| ...| n DS(A, i) = {a  T| a  FIRST1(i) ∨ (i *  ⋀ a  FOLLOW1(A))} Beispiel: 1 A  I T 11/12/13 T  ( A )| [ A ]|  14/15/16 I  a| f| z DS(T, 1) = { ( } DS(I, 1) = {a} DS(T, 2) = { [ } DS(I, 2) = {f} DS(T, 3) = { ], ), } DS(I, 3) = {z}

Top-down Analyse von f(a[z]) Keller () Zeichenkette Keller Zeichenkette A f(a[z]) [ A ] ) I T A ] ) z]) f T I T ] ) T (a[z]) z T ] ) ( A ) T ] ) ]) A ) a[z]) ] ) I T ) ) ) a T )   T ) [z])

LL(k)-Grammatik G = (N, T, P, S) G ist eine kontextfreie Grammatik Für beliebige Paare von Linksableitungen S + A   * x S + A   * y mit FIRSTk(x) = FIRSTk(y) gilt  =  .

Starke LL(k)-Grammatik G = (N, T, P, S) Für jedes Paar von Regeln aus P A  x und A  y (A  x | y) gilt FIRSTk(x FOLLOWk(A))  FIRSTk(y FOLLOWk(A)) = 

Tabellengesteuerte LL(1)-Analyse Analysealgorithmus: Eingabe: Analysetabelle M, Zeichenkette w  T* Ausgabe: Fehlermeldung oder Regelnummernfolge Startkonfiguration: (w, S#, ) Schritte: 1. (ax, A, r) ⊢ (ax, , r i) für M(A, a) = (, i) 2. (ax, a, r) ⊢ (x, , r), a  T 3. (, #, r) erfolgreicher Abschluss 4. Fehler sonst

Analysetabelle für obige Grammatik und Analyse von f(a[z]) a f z ( [ ) ]  A IT,1 IT,1 IT,1 T (A),11 [A],12 ,13 ,13 , 13 I a,14 f,15 z,16 (f(a[z]), A#, ) ⊢ (f(a[z]), IT#, (1)) ⊢ (f(a[z]), fT#, (1 15)) ⊢ ((a[z]), T#, (1 15)) ⊢ ((a[z]), (A)#, (1 15 11)) ⊢ (a[z]), A)#, (1 15 11)) ⊢ (a[z]), IT)#, (1 15 11 1)) ⊢ (a[z]), aT)#, (1 15 11 1 14)) ⊢ ([z]), T)#, (1 15 11 1 14)) ⊢

([z]), T)#, (1 15 11 1 14)) ⊢ ([z]),[A])#, (1 15 11 1 14 12)) ⊢ (z]), A])#, (1 15 11 1 14 12)) ⊢ (z]), IT])#, (1 15 11 1 14 12 1)) ⊢ (z]), zT])#, (1 15 11 1 14 12 1 16)) ⊢ (]), T])#, (1 15 11 1 14 12 1 16)) ⊢ (]), ])#, (1 15 11 1 14 12 1 16 13)) ⊢ (), )#, (1 15 11 1 14 12 1 16 13)) ⊢ (, #, (1 15 11 1 14 12 1 16 13))

Deterministische Syntaxanalyse 3. 2 Deterministische Syntaxanalyse 3.2.2 Bottom-up Verfahren - Einfache Präzedenz Einfache Präzedenz Relationen: R <∙ S: Es existiert eine Regel U  ...R V ... und V + S... . R ≐ S: Es existiert eine Regel U  ...R S... . R ∙> S: Es existiert eine Regel U  ...V W... Und V + ...R sowie W * S... .

Präzedenzmatrix für Beispielgrammatik Aus Id ( ) [ ] a f z Aus ≐ ≐ Id ≐ ∙> ≐ ∙> ( ≐ <∙ <∙ <∙ <∙ ) ∙> ∙> [ ≐ <∙ <∙ <∙ <∙ ] ∙> ∙> a ∙> ∙> ∙> ∙> f ∙> ∙> ∙> ∙> z ∙> ∙> ∙> ∙>

Analyseprozess unter Nutzung der Präzedenzmatrix Wiederholung folgender Schritte: 1. Einspeicherung der Zeichen der Zeichenkette in den Keller, bis an der Kellerspitze ein Zeichen E steht , das in der Relation ∙> mit dem nächsten einzulesenden Zeichen steht 2. Abstieg im Keller bis zum Erreichen des ersten Zeichens A mit y <∙ A, dabei befindet sich y im Keller direkt unter A 3. Reduktion der Zeichenkette A...E im Keller gemäß syntaktischen Regeln

Analyse von f(a[z]) Keller Restwort <∙ f ∙> ( a [ z ] ) <∙ Id ≐ ( <∙ a ∙> [ z ] ) <∙ Id ≐ ( <∙ Id ≐ [ <∙ z ∙> ] ) <∙ Id ≐ ( <∙ Id ≐ [ <∙Id ∙> ] ) <∙ Id ≐ ( <∙ Id ≐ [≐ Aus ≐ ] ∙> ) <∙ Id ≐ ( ≐ Aus ≐ ) ∙> Aus

LR(k)- Analyse Charakterisierung: Entspricht Grundverfahren modifiziert durch Vorausschau auf k Elemente Darstellung durch eine Zustandsmenge zusammen mit Übergangsfunktion in Form einer Analysetabelle, bestehend aus Aktionstabelle (Einlesen und Reduktion) und Sprungtabelle (Fortsetzung nach Reduktion)

Beispiel: Konstruktion der Analysetabelle für A  I ( A )| I [ A ]| I I  a| f| z Zustände und Übergänge des charakteristischen endlichen Automaten Zustand Q0: Zustand Q1: Zustand Q2: T  . A ⊣ T  A .⊣ A  I .( A ) A  .I ( A ) A  I .[ A ] A  .I [ A ] A  I. A  .I Zustand Q3: Zustand Q4: I  .a I  a. I  f. I  .f I  .z Zustand Q5: I  z.

Zustand Q6: Zustand Q7: Zustand Q8: A  I ( .A ) A  I [ .A ] A  I ( A .) A  .I ( A ) A  .I ( A ) A  .I [ A ] A  .I [ A ] Zustand Q9: A  .I A  .I A  I [ A .] I  .a I  .a I  .f I  .f Zustand Q10: I  .z I  .z A  I ( A ). Zustand Q11: A  I [ A ].

Q0 A I Q1 a f z Q2 Q3 Q4 Q5 [ ( I Q7 a f z A I a f z Q9 Q6 Q2 Q3 Q4 Q5 A ] Q8 ) Q11 Q10 Charakteristischer endlicher Automat

Analysetabelle ( [ a f z ] ) ⊣ A I Q0 Q3 Q4 Q5 Q1 Q2 Q1 T  A⊣ Q2 Q6 Q7 ... A  I ... Q3 ... I  a ... Q4 ... I  f ... Q5 ... I  z ... Q6 Q3 Q4 Q5 Q8 Q2 Q7 Q3 Q4 Q5 Q9 Q2 Q8 Q10 Q9 Q11 Q10 ... A  I ( A ) ... Q11 ... A  I [ A ] ...

Syntaxanalyse unter Verwendung einer Analysetabelle (AT): Verwendung zweier Keller: Elementekeller (EK) wie im Grundalgorithmus und Zustandskeller (ZK) Start der Analyse: Q0 im Zustandskeller Ende der Analyse: Altes Startsymbol im Elementekeller; Endezeichen als aktuelles Zeichen Schritte: 1. Einlesen: Aktuelles Zeichen der Eingabe in EK und neuer Zustand gemäß AT (bestimmt aus altem Zustand und Zeichen) in ZK 2. Reduktion:- Reduktion an EK-Spitze gemäß Regel aus AT (bestimmt durch alten Zustand und aktuelles Zeichen) - Entfernen von Zuständen aus ZK (Anzahl gleich Anzahl der Elemente auf r. S. der Regel) - Neuer Zustand gemäß AT (bestimmt durch Zustand an ZK-Spitze und Nichtterminal auf l. S. der Regel) in ZK

Beispiel: Analyse von f ( a [ z ] ) ⊣ Elementekeller Restwort Zustandskeller Aktion f(a[z])⊣ Q0 Einlesen f (a[z])⊣ Q0Q4 Reduktion I (a[z])⊣ Q0Q2 Einlesen I ( a[z])⊣ Q0Q2Q6 Einlesen I ( a [z])⊣ Q0Q2Q6Q3 Reduktion I ( I [z])⊣ Q0Q2Q6Q2 Einlesen I ( I [ z])⊣ Q0Q2Q6Q2Q7 Einlesen I ( I [ z ])⊣ Q0Q2Q6Q2Q7Q5 Reduktion I ( I [ I ])⊣ Q0Q2Q6Q2Q7Q2 Reduktion I ( I [ A ])⊣ Q0Q2Q6Q2Q7Q9 Einlesen I ( I [ A ] )⊣ Q0Q2Q6Q2Q7Q9Q11 Reduktion I ( A )⊣ Q0Q2Q6Q8 Einlesen I ( A ) ⊣ Q0Q2Q6Q8Q10 Reduktion A ⊣ Q0Q1

Konflikte im charakteristischen endlichen Automaten Reduktion-Reduktion: zwei unterschiedliche Reduktionen im gleichen Zustand Beispiel: T  S ⊣ S  a A b A  A b A  c T  . S ⊣ a S  a .A b A S  a A .b b S  a A b. S  .a A b A  .A b A  A .b A  A b. A  .c S c T  S. ⊣ A  c.

Reduktion-Einlesen: Aus einem Zustand mit einer Reduktion führt eine mit einem Terminal bewertete Kante. Beispiel: siehe Zustand Q2 im charakteristischen endlichen Automaten des Standardbeispiels  Grammatikklassifizierung LR(0)-Grammatik: Der charakteristische endliche Automat ist konfliktfrei. SLR(1)-Grammatik: Konflikte lassen sich auf der Basis von einem Element Vorausschau unter Nutzung der FOLLOW-Mengen auflösen.

Zustand Q2 im Standardbeispiel Beispiele: Zustand Q2 im Standardbeispiel A  I .( A ) Einlesen (benötigt werden ( oder [ ) oder A  I .[ A ] Reduktion von I zu A? A  I. FOLLOW1(A) = { ), ], ⊣ }, (, [ ∉ FOLLOW1(A)  - Ist das aktuelle Zeichen ( oder [, dann erfolgt Einlesen. - Ist das aktuelle Zeichen aus FOLLOW1(A), dann erfolgt Reduktion. - Sonst liegt ein syntaktischer Fehler vor. Obiger Reduktion-Reduktion-Konflikt läßt sich auflösen: FOLLOW1(S) = { ⊣ } FOLLOW1(A) = { b }

LALR(1)-Grammatik: analog zur SLR(1)-Grammatik, aber mit „Verfeinerung“ der FOLLOW-Mengen Beispiel: d S T  S .⊣ c A A c FOLLOW1(A) = { a, b} FOLLOW1 1 (A) = { b } A  c. 1 2 A  c. FOLLOW1 2 (A) = { a } a b a b S  d c a. S  d A b. S  A a. S  c b.

LR(k)-Grammatik (-frei) Für beliebige Paare von Ableitungsfolgen S * w1 z w2  w1 v w2 , z  v  P, w2  T* S * w1´ z´ w2´  w1´ v´ w2´ , z´  v´  P, w2´  T* mit w1 v = w1´ v´ und FIRSTk(w2) = FIRSTk(w2´) Folgt z = z´ und v = v´.

Zustände des charakteristischen endlichen Automaten für LR(k)-Grammatiken Ergänzung einer „Situation“ A  x. X z durch einen Kontext K, wobei folgende Eigenschaften erfüllt sein müssen: Anfangszustand enthält Situation S  .w ⊣ Zustand q enthalte Situation A  u .X, K und zu X existieren die Regeln X  w1 ; ...; X  wn , dann enthält q ebenfalls X  .w1 , K´; ...; X  wn , K´ mit K´= FIRSTk( K)

Enthält der Zustand q die Situation A  u Enthält der Zustand q die Situation A  u. x , K , dann erfolgt ein Übergang in einen Zustand mit der Situation A  u. x , K Konflikte im charakteristischen endlichen Automaten: Reduktion-Reduktion: Ein Zustand enthält die Situationen A  u. , K1 und A  v. , K2 mit A ≠ B oder u ≠ v, aber t  K1  K2 Reduktion-Einlesen: Ein Zustand enthält die Situation A  u. , K und besitzt einen Übergang mit dem Terminal t und t  K .

Beispiel: LR(1)-Zustände für Standardbeispiele (Auswahl) Q0: T  Beispiel: LR(1)-Zustände für Standardbeispiele (Auswahl) Q0: T  .A ⊣ Q0  Q2: A  I.(A) , ⊣ A  .I(A) , ⊣ A  I.[A] , ⊣ A  .I[A] , ⊣ A  I. , ⊣ A  .I , ⊣ I  .a , {(, [, ⊣} I  .f , {(, [, ⊣} I  .z , {(, [, ⊣}

3.4 Syntaktische Fehler Aufgaben der Fehlerbehandlung: Lokalisierung des Fehlers Klassifizierung des Fehlers Fehlernachricht: - Fehlerposition - Grad oder Art des Fehlers - Genauere Angaben zum Fehler Stabilisierung und Fortsetzung der Analyse

Fehlerposition: w  T* und es existiert w´  T* , wobei w w´  L(G) (w lässt sich durch w´ zum Wort aus L(G) ergänzen) Für w x, x  T, existiert keine Ergänzung v  T* , so dass w x v  L(G).  x ist eine Fehlerposition Beispiel: In x * (3 – z / y ; ist das „;“ eine Fehlerposition, da x * (3 – z / y richtig ergänzt werden kann, aber x * (3 – z / y ; nicht.

Bemerkungen: Bei LL(1)- und LR(1)-Verfahren der syntaktischen Analyse wird ein Fehler immer an einer Fehlerposition erkannt. Der Fehler muss nicht durch das Zeichen an der Fehlerposition verursacht worden sein.

3.5 Automatische Erzeugung von Syntaxanalysatoren Kontextfreie Grammatik Analysator Rahmen- programm Generator Tabellen

yacc-Spezifikation <Deklarationen> %% <Syntaxregeln und Semantik> <Programmteil>

4 Semantische Analyse 4.1 Symboltabellen Einfache Struktur Hashtabelle mit Verkettungstechnik Beispiel: 1 DE 2 0 A1B 0 3 CAESAR 4 FAUL 5 0 F 0 6 D1 0 C5 0 frei

Symboltabellen mit abgetrennter Schlüsselliste Hashtabelle mit Verkettungstechnik und abgetrennter Schlüsselliste Beispiel: 1 2 0 0 3 4 5 0 0 6 0 frei 2 D E 3 A 1 B 6 C A E S A R 4 F A U L 1 F 2 D 1 2 C 5

Beispiel: Programm mit Blockstruktur BEGIN REAL a, b, c, d; ... BEGIN REAL c, f; L1:... END; BEGIN REAL g, d; L2: BEGIN REAL a; L3:... END

Symboltabelle mit Blockstruktur 1 Beispiel: Symboltabelle für obiges Programm 1 0 4 L1 Blocknummer 2 1 3 f 3 1 4 c Übergeordneter Block 4 3 1 a L3 Anzahl Eintragungen L2 d Adresse g c b a

Symboltabelle mit Blockstruktur 2 Form der Listenelemente: Deklarationen Block Zeiger auf Verkettung Verkettung nummer Information im Block Identifikatoren Blockstruktur Block Anfang Übergeordneter nummer Blockkette Block

Beispiel: Symboltabelle zum obigen Programm c 3 2 / 1 2 / d 4 3 7 1 3 / 5 f 6 2 3 / g 7 3 / / 8 L1 9 2 6 / L2 10 3 4 / L3 11 3 10 /

Blockstrukturtabelle 1 1 4 / 2 2 9 1 3 3 11 1 4 4 1 3

4.2 Realisierung der semantischen Analyse Beispiel: Attributierte Grammatik für Deklarationsfolgen Kontextfreie Basisgrammatik <decl list> ::= VAR <id decl> ; <dec list> <dec list>0 ::= <id decl> ; <dec list>1 <dec list> ::=  <id decl> ::= <type> <id> <type> ::= BOOL <type> ::= INT Kontextbedingung: Kein Identifikator darf mehrfach deklariert werden.

Zuordnung der Attribute Attribut Art Bedeutung Zuordnung Sv  Symboltabelle <dec list> vorläufig Sn  Symboltabelle neu <dec list>, <decl list> IT  (Identifikator, Typ) <id decl> T  Typ <type> I  Identifikator <id> error  true, false <decl list>, <dec list>

Semantische Regeln 1 <decl list>.Sn := <dec list>.Sn <decl list>.error := <dec list>.error <dec list>.Sv := INIT(<id decl>.IT) 2 <dec list>0.Sn := <dec list>1.Sn <dec list>0.error := <dec list>1.error  IN(<dec list>0.Sv, <id decl>.IT) <dec list>1.Sv := ENTRY(<dec list>0.Sv, <id decl>.IT), wenn ¬ IN(<dec list>0.Sv, <id decl>.IT) 3 <dec list>.Sn := <dec list>.Sv <dec list>.error := false 4 <id decl>.IT := IDDECL(<id>.I, <type>.T) 5 <type>.T := ´bool´ 6 <type>.T := ínt´

Dekorierter Syntaxbaum von VAR INT x; BOOL y; <decl list> {(´x´,´int´),(´y´,´bool´)}, false VAR <id decl> ; <dec list> (´x´,´int´) {(´x´,´int´)}, {(´x´,´int´),(´y´,´bool´)}, false <type> <id> <id decl> ; ´int´ ´x´ (´y´,´bool´) <dec list> {(´x´,´int´),(´y´,´bool)}, INT x <type> <id> {(´x´,´int´),(´y´,´bool´)}, ´bool´ ´y´ false BOOL y 

Schachtelung von Programmeinheiten Beispiel: Programm PROGRAM H; PROC B; PROC D; ...; call C; ... END B; END D; ...call B; PROC A; END A; PROC C; ...; call A;...; call D;... ...; call D; END H; END C;

Statische Schachtelung mit Schachtelungstiefe D/1 A/1 C/2 B/2 Dynamische Schachtelung H A D B C D

Datenbereich (Prozedurschachtel, Kellerrahmen) einer Prozedur (eines Blocks) zur Laufzeit Allgemeiner Aufbau Funktions Statischer Dynamischer alter EP Rücksprung wert Vorgänger Vorgänger Wert adresse MP Parameter Lokale Felder Zwischen (statisch) Variablen (semidyn.) ergebnisse SP EP

Variablenadressierung: (Schachtelungstiefe, Relativadresse) Beispiel: PROGRAM H; Adressierung: VAR x1, y; In H: x1 (0, 5) PROC p; y (0, 6) VAR i, x1, k; In p: i (1, 5) ...x1... x1 (1, 6) ...k... k (1, 7) ...y... END p; call p; ...x1... END H;

Aktionen bei Eintritt in eine Prozedur q (p ruft q auf) Setzen des statischen Vorgängers von q Setzen des dynamischen Vorgängers von q (Anfangsadresse des Datenbereichs von p – alter MP-Wert) Retten des alten EP-Wertes Berechnung der aktuellen Parameter Setzen des MP-Registers auf Anfangsadresse des Datenbereiches von q (SP + 1)

Abspeicherung der Rückkehradresse Ausführung des Sprungs auf ersten Befehl des übersetzten q-Programms ----------------------------------------------------------------------------------- Setzen des SP-Registers Berechnung des EP-Wertes und Setzen des EP-Registers sowie Überprüfung auf mögliche Kollision zwischen Keller und Halde

Aktionen bei Verlassen der Prozedur q (Rückkehr nach p) Freigabe des Datenbereichs bis auf eventuellen Funktionswert Setzen der MP-, EP- und SP-Register für die Prozedur p Rückkehr nach p

Beispiel: rekursiver Funktionsaufruf PROGRAM Fak; VAR r : integer; FUNCTION F(n: integer): integer; IF n = 1 THEN F := 1 ELSE F := n * F(n – 1); END F; r := F(3); END Fak; Adressierung r (0, 5) n (1, 5) F (1, 0)

Übersetzung von Fak <Befehle für Aufbau des DB>; lda i 0 6; <Übersetzung des Aufrufs F(3)>; M1: sto i; <Befehle für Abbau des DB> Übersetzung von F(n) <Befehle für Aufbau des DB>; lod i 0 5; ldc i 1; equ i; fjp l1; lda i 0 0; ldc i 1; sto i; ujp l2; l1: lda i 0 0;lod i 0 5; <Übersetzung des Aufrufs F(n – 1)>; M2: mul i; sto i; l2: <Befehle für Abbau des DB und Rückkehr>

Abspeicherung von Datenstrukturen Mehrdimensionale Felder A: ARRAY [u1.. o1, ..., un.. on] OF <type> Speicherabbildungsfunktion Adresse(A[i1, ..., in]) = Adresse(A[u1, ..., un]) + (i1 – u1) * d2 * ...* dn + (i2 – u2) * d3 * ...* dn + ... + (in – un) = k + v (di = oi – ui + 1)

Semidynamische Felder Beispiel: semidynamische Felder in ALGOL 68 begin ... n := ...; ...; m := ...; ... begin [n] int A; [m] int B; A[i] := B[j+3] * k; end

Schablone (Felddeskriptor) eines semidynamischen Feldes A: ARRAY [u1.. o1, ..., un.. on] OF <type> u1 o1 d1 ... un on dn n k´ adr adr = Adresse(A[u1, ..., un]) k´= u1 * d2 * dn + u2 * d3 * dn + ... + un

Datenbereich für inneren Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 20 21 25 26 1 4 4 1 1 17 1 5 5 1 1 21 ... ... Schablone für A Schablone für B 1 4 4 1 5 5 1 1 17 1 1 21 Voraussetzung: Zur Laufzeit gilt n = 4 und m = 5 .

Erzeugte Befehlsfolgen (im Beispiel): Aufbau des DB einschließlich der teilweise gefüllten Schablonen Berechnung von n und der fehlenden Elemente der Schablone für A Neuberechnung der ersten freien Adresse des freien dynamischen Speicherteils Berechnung von m und der fehlenden Elemente der Schablone für B Adressberechnung für A[i] aus der Schablone für A Adressberechnung für B[j+3] aus der Schablone für B

Datenmüllbeseitigung (garbage collection) Arbeitsschritte: Zeigerverfolgung und Markierung besetzter Speicherbereiche  Speicherkarte Herstellung eines Adressbuchs (alte Adressen, neue Adressen) Zeigertransformation (auf neue Adressen) Komprimierung des Speichers (Auslassung von Müllbereichen)

Parameterübergaben Referenzaufruf (call by reference): PROC del(VAR t: Elem;...); Befehlsfolgen: BEGIN ...t := ...END; - Berechnung  ... - Abspeicherung von  auf del(T,...); Platz von t T t LD LD   DB aufrufende Prozedur DB von del

Werteaufruf (call by value): PROC del1 (k: Key;...); Befehlsfolgen: BEGIN...z := k;...END; - Berechnung von w ... aus a del1(a,...); - Abspeicherung von k w auf Platz von k LD w w Wert von a DB von del1

Werte-Resultatsaufruf (call by value-result): Kombination von call by reference und call by value mit Änderung des aktuellen Parameters erst bei Rückkehr (zusätzlicher Umspeicherungsbefehl) T t LD w LD  w Aufruf  LD w´ LD  w´ Rückkehr DB aufrufende Prozedur DB aufgerufene Prozedur

Namensaufruf (call by name): Beispiele: ALGOL 60 procedure p(x); integer x; begin ... i := 2; x := 9; ... end; array a[1 : 9]; Realisierung: integer i; begin ... ... i := 2; a[i] := 9; i := 1; ... p(a[i]); end

procedure R(X, I); begin I := 2; X := 5; I := 3; X := 1 end; ... R(B[J * 2], J); Realisierung: begin J := 2; B[J * 2] := 5; J := 3; B[J * 2] := 1 end

Konkreter Syntaxbaum – abstrakter Syntaxbaum Beispiel: Ausdruck 5 * a Konkreter Syntaxbaum Abstrakte Syntaxbäume <int expr> * <term> num(5) id(a) <term> <int op> <elem> <elem> * <var> * num(5) id(a) <num> ... ... a 5

Quadrupel (Dreiadresscode) Allgemeine Form: [<Operator>, <Operand1>, <Operand2>, <Resultat>] mit Operand, Resultat: - Bezeichner (deklarierte oder temporäre Variable) - Zahl (Konstante, Sprungziel) - leer (ohne Bedeutung)

Unbedingter Sprung: [ujp, <Marke>] Ausdrücke: [<Operator>, <Variable/Konstante>, <Variable/Konstante>, <temporäre Variable>] Beispiel: [add-int, t1, t2, h] Zuweisung: [sto-T, <Konstante/Variable>, , <Variable>] , T Typ Beispiel: [sto-int, x, , y] Unbedingter Sprung: [ujp, <Marke>] Bedingter Sprung: [fjp, <Variable>, <Marke>] Ausführung des Sprungs zur Anweisung mit der Marke, wenn der Wert der Variablen false ist

If-Anweisung IF e THEN st1 ELSE st2 FI: Quad(e) Quadrupel zur Berechnung von e; Wert von e wird h1 zugewiesen [fjp, h1, l1] Quad(st1) Quadrupelfolge für st1 [ujp, l2] l1: Quad(st2) Quadrupelfolge für st2 l2: ... Andere Darstellung von Marken: [label, <Marke>]

Beispiel: IF a < b THEN a := a + 1 ELSE b := b + 1 FI [les-int, a, b, h1] [fjp, h1, l1] [add-int, a, 1, h1] [sto-int, h1, a] [ujp, l2] l1: [add-int, b, 1, h1] [sto-int, h1, b] l2: ...

Tripel (Binärbaum) Allgemeine Form: (<Operator>, <Operand1>, <Operand2>) Repräsentation des Ergebnisses durch das Tripel selbst Graphische Darstellung: Operator Operand1 Operand2

Beispiel: IF a < b THEN a := a + 1 ELSE b := b + 1 FI (1) (les-int, a, b) (2) (fjp, (1), l1) (3) (add-int, a, 1) (4) (sto-int, a, (3)) (5) (ujp, l2) l1: (6) (add-int, b, 1) (7) (sto-int, b, (6)) l2: (8) ...

Erzeugung von Zwischensprachrepräsentationen Methoden: Aufruf semantischer Routinen in Verbindung mit syntaktischer Analyse Verwendung attributierter Grammatiken Verwendung attributierter Translationsgrammatiken Baumtransformation

Speicherbedarf: Die Befehle ldc i, ldc a, ind i, add i, Beispiel: Voraussetzungen: Bezeichnungen: P Feld für P-Code mit Zeiger p auf nächste Eintragstelle, S Analysekeller mit Spitze i Speicherbedarf: Die Befehle ldc i, ldc a, ind i, add i, mul i, neg i benötigen (kodiert) nur je 1 Speicherplatz. Bezeichner und Konstanten brauchen 2 Speicherplätze (Kenncode, Adresse in entsprechender Tabelle). Regeln: <Z> ::= <E> <E> ::= <T>

<E> ::= <E> + <T> P[p] := ´ add i´; p := p + 1; P[p] := ´;´; p := p + 1; <E> ::= - <T> P[p] := ´ neg i´; p := p + 1; P[p] := ´;´; p := p + 1; <T> ::= <F> <T> ::= <T> * <F> P[p] := ´ mul i´; p := p + 1; P[p] := ´;´; p := p + 1;

<F> ::= <Bez> P[p] := ´ ldc a´; p := p + 1; P[p] := S[i]; p := p + 2; P[p] := ´;´; p := p + 1; P[p] := ´ ind i´; p := p + 1; P[p] := ´;´; p := p + 1; <F> ::= <Kon> P[p] := ´ ldc i´; p := p + 1; P[p] := S[i]; p := p + 2; P[p] := ´;´; p := p + 1; 9 <F> ::= (<E>)

Beispiel: Syntaxanalyse und Erzeugung von P-Code Ausdruck: a * (b + 5) Tokenfolge (symbolisch): Beza * ( Bezb + Kon5) Analysekeller S Regel P[p] Beza 7 ldc a a; ind i; <F> 5 <T> * (Bezb 7 ldc a b; ind i; <T> * (<F> 5, 2 <T> * (<E> + Kon5 8 ldc i 5; <T> * (<E> + <F> 5 <T> * (<E> + <T> 3 add i; <T> * (<E>) 9 <T> * <F> 6 mul i <T> 2, 1 <Z>

Beispiel: vorheriges Beispiel unter Verwendung einer attributierten Grammatik Attribute: P synthetisiert P-Code I synthetisiert Bezeichner K synthetisiert Konstante Syntaktische Regeln: s.o. Semantische Regeln: P(<Z>) := P(<E>) P(<E>) := P(<T>) P(<E>0) := CONCAT(P(<E>1), P(<T>), ´add i;´)

P(<E>) := CONCAT(P(<T>), ´neg i;´) 5 P(<T>) := P(<F>) 6 P(<T>0) := CONCAT(P(<T>1), P(<F>), ´mul i;´) 7 P(<F>) := CONCAT(´ldc a ´, I(<Bez>), ´; ind i;´) 8 P(<F>) := CONCAT(´ldc i ´, K(<Kon>), ´;´) 9 P(<F>) := P(<E>)

Beispiel: Z ABCDE E T ABCDE T * F BCD F A ( E BCD ) Bez a E + T A ldc a a; ind i; B ldc a b; ind i; T F C C ldc i 5; D add i; F B Kon 5 E mul i; Bez b

Beispiel: Attributierte Translationsgrammatik für arithmetische Beispiel: Attributierte Translationsgrammatik für arithmetische Ausdrücke <Z>x  <E>p x := p <E>x  <T>p x := p <E>x  <E>q + <T>r ADDy,z,p <x,p> := NEWT; y := q; z := r <E>x  - <T>q NEGy,p <x,p> := NEWT; y := q <T>x  <F>p x := p <T>x  <T>q * <F>r MULTy,z,p <x,p> := NEWT; y := q; z := r

<F>x  <Bez>p x := p <F>x  <Kon>p x := p <F>x  (<E>p) x := p Attributwerte: Speicheradressen <a, b> :=NEWT: a und b wird die Adresse eines freien Speicherplatzes im Speicher T zugewiesen Translation (ohne Attribute): Bez * (Bez + Kon)  ADD MULT

Codeerzeugung im engeren Sinne Arten von Zielcode: Maschinenprogramm mit absoluten Adressen (Load-and-go-Compiler) Maschinenprogramm mit relativen Adressen (relocatable Code)  Bearbeitung vor Abarbeitung: - Zusammenstellung unabhängig voneinander übersetzter Programmkomponenten mit externen Referenzen aufeinander zu einem Lademodul (load module) - Auflösung der externen Referenzen durch Binder (linker) - Überführung in absolut adressiertes, ausführbares Programm durch Ladeprogramm (loader)

Programm in Assemblersprache  Notwendigkeit eines Assemblerlaufes Programm in Programmiersprache einer abstrakten Maschine  zweistufige Übersetzung: Quellsprache  Sprache der abstrakten Maschine  Zielsprache Beispiel: Übersetzung von Pascal in P-Code (Code der P-Maschine) und Übersetzung des P-Codes in Zielsprache

Übersetzung Zwischensprache – Zielsprache Grundlegende Techniken Voraussetzungen: Befehle der Zielmaschine LOAD R O <O>  R STORE R O <R>  O STORE O1 O2 <O1>  O2 ADD-I R O <R> + <O>  R SUB-I R O <R> - <O>  R <a> Inhalt von Speicherplatz a  a Abspeicherung auf Speicherplatz a

R Registeradresse (im Falle 1 Registers ACC) O kann sein: Registeradresse <Registeradresse> Hauptspeicheradresse <Hauptspeicheradresse> = Konstante (Direktoperand) STACK Adresse der Spitze des Kellerspeichers

Beispiel: Tabellengesteuerte Codeerzeugung Voraussetzungen: Übersetzung aus Tripeldarstellung in Befehle obiger Zielmaschine gen(a) erzeugt Befehl a code(t) vertritt Befehlsfolge, die durch Übersetzung des Teilbaums t entstand LB linker Teilbaum RB rechter Teilbaum Eine Variable in den erzeugten Befehlen vertritt den ihr im Hauptspeicher zugeordneten Speicherplatz.

Tabelle für SUB-I (bzw. DIV-I) Variable/Konstante Unterbaum Var. gen( LOAD ACC LB ) code( RB ) / gen( SUB-I ACC RB ) gen( STORE ACC STACK ) Kon. gen( LOAD ACC LB ) gen( SUB-I ACC STACK ) Un code( LB ) code( RB ) ter gen( SUB-I ACC RB ) gen( STORE ACC STACK ) ba code( LB ) um gen( SUB-I ACC STACK )

Tabelle für ADD-I (bzw. MUL-I) Variable/Konstante Unterbaum Var. gen( LOAD ACC LB ) code( RB ) / gen( ADD-I ACC RB ) gen( ADD-I ACC LB ) Kon. Un code( LB ) code( LB ) ter gen( ADD-I ACC RB ) gen( STORE ACC STACK ) ba code( RB ) um gen( ADD-I ACC STACK )

Tabelle für Zuweisung Variable/Konstante Unterbaum Var. gen( STORE RB LB ) code( RB ) gen( STORE ACC LB ) Un code( LB ) code( RB ) ter gen( STORE RB <ACC> ) gen( STORE ACC STACK ) ba code( LB ) um gen( STORE STACK <ACC>)

Beispiel: Zuweisung h := a * ((c + 5 * a) – (c * d)) Tripel (mul-int, 5, a) (add-int, c, (1)) (mul-int, c, d) (sub-int, (2), (3)) (mul-int, a, (4)) (sto-int, (5), h)

Tripel als Binärbaum := (6) h * (5) a - (4) + (2) * (3) c * (1) c d 5 a

Erzeugte Befehlsfolgen LOAD ACC =5 code((1)) code((4)) code((5)) MUL-I ACC a MUL-I ACC a code((1)) code((2)) code((5)) code((6)) ADD-I ACC c STORE ACC h LOAD ACC c code((3)) MUL-I ACC d code((3)) code((4)) STORE ACC STACK code((2)) SUB-I ACC STACK

Generatoren für Codegeneratoren Verwendung von Baumgrammatiken Vorgehensweise: Beschreibung der Codeerzeugung: - Regeln der Baumgrammatik beschreiben die Zwischensprache: <Baumknoten> ::= <Baummuster> - Zuordnung von Schablonenfolgen für Erzeugung der Zielsprachbefehle zu Regeln

Erzeugung des Codegenerators aus vorheriger Beschreibung Codeerzeugung durch Codegenerator: - Überdeckung des Baums zum Zwischensprachprogramm durch Baummuster (in Form von Termen); Auflösung von Mehrdeutigkeiten durch Kostenfunktion - Erzeugung von Zielcode aus Schablonenfolgen gemäß Überdeckung

Beispiel: Baumgrammatikregeln mit Schablonen 1 r.2 ::= word( d.1 ) { LOAD R.2 D.1 } 2 r.1 ::= iadd( r.1, r.2 ) { ADD-I R.1 R.2 } 3  ::= store( word( d.1 ), r.2 ) { STORE R.2 D.1 } r.i Registeradresse; d.i Hauptspeicheradresse einer deklarierten Variable word, iadd, store Operatoren der Zwischensprache (Präfixform)

Quellsprache Zwischensprache A := A + B store(word(d. a), iadd(word(d Quellsprache Zwischensprache A := A + B store(word(d.a), iadd(word(d.a), word(d.b))) Überdeckungsbaum store word iadd d.a word word d.a d.b

Objektorientierte Compilerstruktur (nach D. A. Watt, D. F Objektorientierte Compilerstruktur (nach D. A. Watt, D. F. Brown: Programming Language Processors in Java) Compilertreiber Treiber Parser Checker Encoder Scanner

Compilertreiber public class Compiler { public static void compileProgram(...) { //Generierung des Parsers Parser parser = new Parser(...); //Generierung des semantischen Analysators Checker checker = new Checker(...); //Generierung des Zielcodegenerators Encoder generator = new Encoder(...);

//Aufruf des Parsers mit Erzeugung des abstrakten Syntaxbaums //AST Program theAST = parser.parse(); //Aufruf des semantischen Analysators mit Dekoration von AST checker.check(theAST); //Aufruf des Codegenerators mit Erzeugung des Zielprogramms generator.encode(theAST); } public static void main(String[] args) {...compileProgram(...); ...}

Pakete: AbstractSyntaxTrees: - Klassen zur Definition der AST-Datenstrukturen Je Klasse: Konstruktor zum AST-Aufbau und visitor-Methode zur Verknüpfung mit semantischer Analyse (contextual analyzer) und Codeerzeugung (code generator) - Manipulation der AST-Felder SyntacticAnalyzer: - Parserklassen zur syntaktischen Analyse (Methode des rekursiven Abstiegs) und zur AST-Konstruktion - Hilfsklassen

ContextualAnalyzer: Checker-Klasse zur Durchführung der semantischen Analyse CodeGenerator: Encoder-Klasse führt ausgehend vom AST Speicherzuteilung durch und erzeugt Zielcode

Parser public class Parser{ //Aktuelles Symbol im analysierten Programm private Token currentToken; //Vergleich aktuelles Symbol – erwartetes Symbol private void accept(byte expectedKind){ if (currentToken.kind == expectedKind) currentToken = scanner.scan(); else Fehlermeldung;}

//Nächstes Symbol wird zum aktuellen Symbol private void acceptIt(){ currentToken = scanner.scan();} //Hilfsmethoden //Parsingmethoden private Program parseProgram(){ //Instanzvariable für AST //Syntaxanalyse für Programme //AST-Erzeugung für Programme return //AST für Programm } ...

public Program parse(){ currentToken = scanner public Program parse(){ currentToken = scanner.scan(); Program progAST = parseProgram(); if (currentToken.kind != Token.EOT) Fehlermeldung return progAST} ... }

AST-Konstruktor der Klasse AssignCommand Abstrakte Klasse für AST: public abstract class AST{...} AST-Knoten als Unterklassen der AST-Klasse Abstrakte Klasse für Expression: public abstract class Expression extends AST{...} Konkrete Klassen mit Konstruktor für AST Beispiel: AST-Konstruktor der Klasse AssignCommand public AssignCommand(Vname V, Expression E){ this.V = V; this.E = E;}

Lexikalische Analyse public class Scanner{ ... private boolean isDigit(char c){...} //true, falls c eine Ziffer ist public Token scan(){ return new Token(currentKind, currentSpelling.toString());} } public class Token{...} //Lexembeschreibungen in Aufbau und Codierung

Semantische Analyse Anwendung des visitor pattern: 1 Semantische Analyse Anwendung des visitor pattern: 1. Einrichtung einer Schnittstelle Visitor 2. Für jeden konkreten AST einer Unterklasse A Verwendung einer Visitor-Methode visitA 3. Anreicherung der abstrakten AST-Klasse mit der visit-Methode zum Besuch von AST-Knoten 4. Implementation von visit in jeder Unterklasse 5. Implementation der eigentlichen semantischen Analyseprozeduren im Checker (semantischer Analysator)

Schnittstelle Visitor public interface Visitor{ //visitA-Methoden public Object visitProgram(Program prog, Object arg); ... public Object visitAssignCommand(AssignCommand com, Object arg);}

Implementierung von Visitor in Checker public final class Checker implements Visitor{ //Symboltabelle private IdentificationTable idTable; //Visitor-Methoden public Object visitAssignCommand(Assigncommand com, Object arg){ Type vType = (Type) com.V.visit(this, null); Type eType = (Type) com.E.visit(this, null); if(! eType.equals(vType)) Fehlermeldung return null; }

... //Start der semantischen Analyse public void check(Program prog){ //Initialisierung der Symboltabelle idTable = new IdentificationTable(); prog.visit(this, null); }

Erweiterung der AST-Klasse public abstract class AST{ ... public abstract Object visit(Visitor v, Object arg); } Implementierung von visit in den AST-Unterklassen public class A extends ...{ public Object visit(Visitor v, Object arg){ return v.visitA(this, arg);}

Codeerzeugung Nutzung des visitor patterns mit Implementierung der Visitor-Methoden in der Klasse Encoder: public final class Encoder implements Visitor{ ... //Visitor-Methoden public Object visitAssignCommand(AssignCommand com, Object arg){ com.E.visit(this, arg); encodeAssign(com.V); return null;} public void encode(Program prog){ prog.visit(this, null);} }