Programmierung 2 Übersetzer: Das Frontend Sebastian Hack [email protected] Klaas Boesche [email protected] Sommersemester 2012 1 Vom Programm zur Maschine Was passiert eigentlich mit unseren Java-Programmen? Wie werden Sie ausgeführt? Was macht ein Übersetzer? Was macht das Laufzeitsystem? Wie werden Konzepte wie I Variable I Methoden I Klassen I Vererbung in das überführt, was ein Prozessor versteht? Nächstes Projekt ist ein kleiner Compiler 2 Big Picture Hierarchie Programm Bibliotheken Laufzeitsystem Betriebssystem Prozessor / Hardware 3 Big Picture In Java Programm Bibliotheken Laufzeitsystem / VM Betriebssystem Prozessor / Hardware 3 Big Picture In C/C++ Programm Bibliotheken Laufzeitsystem Betriebssystem Prozessor / Hardware 3 Laufzeitsystem Abstrahiert von Betriebssystem und Prozessor Speicherverwaltung Threads Laden/Nachladen von Bibliotheken 4 Übersetzer (engl.: Compiler) Ein Übersetzer überprüft ein Programm auf Korrektheit bzgl. Syntax und statischer Semantik transformiert das Programm in eine Form, in der es vom Prozessor ausgeführt werden kann Verbindet das Programm mit dem Laufzeitsystem 5 Übersetzer (engl.: Compiler) Die Grenzen zwischen Compiler und Laufzeitsystem sind heutzutage fließend: In C/C++ befindet sich das Laufzeitsystem in einer Bibliothek, die zum Programm gebunden wird (Linker) Teilweise werden Teile der Laufzeitbibliothek vom Compiler in das Programm eingefügt In Java ist ein Teil des Übersetzers im Laufzeitsystem integriert Java Programme werden nicht direkt in Maschinensprache übersetzt sondern in Bytecode für eine virtuelle Maschine Diese beherbergt einen weiteren Übersetzer, der die letzte Stufe der Übersetzung vornimmt 6 Compiler Architektur Compiler bestehen aus drei Teilen Frontend 1 2 3 Transformation Backend Frontend I Zerteilen des Quelltextes I Aufbau des abstrakte Syntaxbaums (AST) I Semantische Analyse (Namensanalyse, Typanalyse, etc.) Transformation I Optimierung I Kopplung an das Laufzeitsystem Backend I Abbildung von Hochsprachenkonstrukten auf Maschinenniveau I Erzeugung von Maschinencode 7 Compiler Frontend Das Frontend besteht wiederum aus mehreren Teilen: Frontend Lexer 1 2 3 Zerteiler Analyse Lexer I Zerteilt den Eingabetext in sog. Tokens I Entfernt Whitespace“ und Kommentare ” Zerteiler (engl.: Parser) I Analysiert die Sprache auf syntaktische Korrektheit I Erstellt den abstrakten Syntaxbaum (AST) Analyse (auf dem AST) I Kopplung der Verwendung von Bezeichnern an deren Vereinbarungen I Typüberprüfung 8 Compiler Lexer Programme sind Text Ô Strom von Zeichen Zerteilt den Zeichenstrom in ein Strom von Tokens Für die Syntaxanalyse ist nicht der genaue Text, sondern die Art des Textes wichtig Token besteht aus I Text im Programm, wenn wichtig I Koordinaten (Dateiname, Zeilen- und Spaltennummer) Ô Für evtl. Fehlermeldungen I Art des Tokens (+, while, Bezeichner, Literal, etc.) Bezeichnernamen sind später noch wichtig, daher merken Lexer erkennt auch Zeichensequenzen, die keine gültigen Tokens sind. Ô Bei Java zum Beispiel: 123 abc , 0 xABXZ , 1 e * 9 Lexer Beispiel PUBLIC(1,1) TYPE INT(1,8) IDENT(1,12,"m") LPAREN(1,13) TYPE INT(1,14) IDENT(1,18,"x") public int m ( int x ) { return x + 1; } LBRACE wird zu folgendem Tokenstrom RETURN(2,3) RPAREN(1,19) (1,21) IDENT(2,10,"x") PLUS(2,12) INT LITERAL(2,14,1) SEMI(2,15) RBRACE(3,1) 10 Zerteiler (engl.: Parser) Lexer nicht ausreichend zur Feststellung der korrekten Syntax I 123 + * 234 kann problemlos in Tokens zerlegt werden I Ist aber strukturell falsch Struktur gegeben durch kontextfreie Grammatik (häufig BNF) Zerteiler überprüft diese Struktur Bestimmt somit gültige Sequenzen von Tokens Grammatik bestimmt die Struktur des AST Ô Erzeugung des AST beim Zerteilen 11 Zerteiler (engl.: Parser) Lexer nicht ausreichend zur Feststellung der korrekten Syntax I 123 + * 234 kann problemlos in Tokens zerlegt werden I Ist aber strukturell falsch Struktur gegeben durch kontextfreie Grammatik (häufig BNF) Zerteiler überprüft diese Struktur Bestimmt somit gültige Sequenzen von Tokens Grammatik bestimmt die Struktur des AST Ô Erzeugung des AST beim Zerteilen Bemerkung Es existieren Verfahren, wie man aus einer Grammatik automatisch ein Programm erzeugen kann, dass Texte dieser Grammatik zerteilt 11 Zerteiler Auszug aus der Grammatik für unser Beispiel MethodDecl Params ParamDecl Body Stmts Stmt ReturnStmt Expr Literal IdentUse Type Modifier = = = = = = = = = = = = Modifier Type IDENT LPAREN Params RPAREN Body Params COMMA ParamDecl | ParamDecl | Type IDENT LBRACE Stmts RBRACE Stmt Stmts | ReturnStmt | ... RETURN Expr SEMI Expr PLUS Expr | Literal | IdentUse | ... INT_LITERAL | ... IDENT TYPE_INT | ... PUBLIC | ... | public int m ( int x ) { return x + 1; } 12 Zerteiler Beispiel MethodDecl3 public int m ( int x ) { return x + 1; } Type2 Params Body 1 : PUBLIC(1,1) , 2 : TYPE INT(1,8) , 3 : IDENT(1,12,"m") , 4 : LPAREN(1,13) , 5 : TYPE INT(1,14) , 6 : IDENT(1,18,"x") , ParamDecl ReturnStmt9 Type5 Ident6 Expr11 7 : RPAREN(1,19) , 8 : LBRACE (1,21) , 9 : RETURN(2,3) , 10 : IDENT(2,10,"x") , 11 : PLUS(2,12) , 12 : INT LITERAL(2,14,1) , 13 : SEMI(2,15) , 14 : RPAREN(3,1) IdentUse10 Literal12 13 Zerteiler Beispiel MethodDecl = Modifier Type IDENT LPAREN Params RPAREN Body Params = ParamDecl COMMA Params | ParamDecl | ParamDecl = Type IDENT Body = LBRACE Stmts RBRACE Stmts = Stmt Stmts | Stmt = ReturnStmt | ... ReturnStmt = RETURN Expr SEMI Expr = Expr PLUS Expr | Literal | IdentUse | ... Literal = INT_LITERAL | ... IdentUse = IDENT Type = TYPE_INT | ... Modifier = PUBLIC | ... | 14 Zerteiler Beispiel MethodDecl = Modifier Type IDENT LPAREN Params RPAREN Body Params = ParamDecl COMMA Params | ParamDecl MethodDecl3 | ParamDecl = Type IDENT Body = LBRACE Stmts RBRACE Type2 Body Params Stmts = Stmt Stmts | Stmt = ReturnStmt | ... ReturnStmt = RETURN Expr SEMI Expr = Expr PLUS Expr ReturnStmt9 ParamDecl | Literal | IdentUse | ... Literal = INT_LITERAL | ... Type5 Ident6 Expr11 IdentUse = IDENT Type = TYPE_INT | ... Modifier = PUBLIC | ... | IdentUse10 Literal12 14 Abstrakter Syntaxbaum (engl.: Abstract Syntax Tree, AST) Grammatik der Sprache gibt Struktur des AST vor Nichtterminale bilden die Knoten des AST Tokens (Terminale) sind Attribute der Knoten Baumdarstellung macht Weglassen vieler Tokens möglich Trennzeichen, wie ;, {, (, etc. Klammerung kann verworfen werden 15 Analyse Zerteiler kann nur syntaktische Korrektheit des Programms feststellen Verletzung der statischen Semantik können nicht geprüft werden, z.B.: I Bezeichner zweimal vereinbart I implements mit Klasse statt Interface I Typisierung int x = true ; I usw. Zwei Kernaufgaben: I Namensanalyse I Typanalyse 16 Namensanalyse Bezeichner werden verwendet, um Bezüge auf definierte Entitäten herzustellen I Verwendung einer Variable in einem Ausdruck I Verwendung einer Klasse zum vereinbaren einer Referenz Der Übersetzer muss diese Bezüge wieder herstellen Im AST existiert der Bezug indirekt über Namen Body VarDecli ... Assign int x , i ; ... x = 3 + i; TypeINT TYPE Expr+ Literal3 IdentUsei 17 Namensanalyse Angenommen wir überprüfen Typen in x = 3 + i Dazu benötigen wir den Typen von i Dieser ist an der Vereinbarung vermerkt Suchen im Baum? Body VarDecli ... Assign int x , i ; ... x = 3 + i; TypeINT TYPE Expr+ Literal3 IdentUsei 18 Definitionstabelle Wir erstellen eine Definitionstabelle Bildet Bezeichner auf AST-Knoten seiner Vereinbarung ab Existiert zusätzlich zum AST ... i x Body VarDeclx ... ... VarDecli TypeINT TYPE ... Assign Expr+ ... Literal3 IdentUsei Beim Berechnen des Typs von x = 3 + i Nachschlagen der Vereinbarung in der Definitionstabelle 19 Definitionstabelle Was ist bei mehreren Vereinbarungen eines Bezeichners? class A { int m ( int x ) { int a = x + 1; return a ; } int n ( int y ) { int a = y + 1; return a ; } } a zweimal vereinbart (in m und in n) Offensichtlich haben Bezeichner Gültigkeitsbereiche I Verwendung von a in m bezieht sich auf die Vereinbarung in m I Vereinbarung nicht im ganzen Programm gültig Hierarchische Definitionstabellen 20 Definitionstabelle class A { int m ( int x ) { int a = x + 1; return a ; } int n ( int y ) { int a = y + 1; return a ; } } ren pa t a ... n m a ... ClassDeclA MethodDeclm MethodDecln VarDecla VarDecla 21 Hierarchische Definitionstabelle Einige Knotentypen erzeugen neue Umgebungen I Pakete, Klassen, Methoden, Blöcke ({ ... }) Jeder Knoten kann mit einer Referenz auf die Definitionstabelle ausgestattet werden, in dem er sich befindet Nachschlagen der Vereinbarung durch Nachschauen in der Definitionstabelle wenn nicht gefunden, dann konsultiere Tabelle des nächstäußeren Bereichs Beispiel: class A { int x ; int n ( int p ) { return x + p ; } } x wird nicht im Block von n gefunden, aber in dem von A Ô mit x in x + p ist also das Feld x von A gemeint 22 Namensanalyse Namensräume class A { class name { } int name ; int name () { return 0; } } name ist offensichtlich dreimal(!) vereinbart in A Kein Problem! In Java halten sich Klassen, Variable und Methoden in unterschiedlichen Namensräumen auf Es werden für Klassen, Variable und Methoden getrennte Definitionstabellen angelegt 23 Namensanalyse Zusammenfassung Assoziierung von Auftreten eines Bezeichners mit Vereinbarungen Vereinbarungen haben Gültigkeitsbereich Vereinbarung desselben Bezeichners in unterschiedlichen Blöcken möglich Analyse implementiert durch hierarchische Definitionstabelle Mehrere getrennte Namensräume möglich Hinweis Diese Prinzipien findet man in so gut wie fast allen modernen Hochsprachen 24 Namensanalyse Java Quiz public class Main { public static void main ( String [] args ) { String s = new String ( " Hello World " ); System . out . println ( s ); } } class String { private final java . lang . String s ; public String ( java . lang . String s ) { this . s = s ; } public java . lang . String toString () { return s ; } } Was gibt das Programm aus? 25 Namensanalyse Java Quiz public class Main { public static void main ( String [] args ) { String s = new String ( " Hello World " ); System . out . println ( s ); } } class String { private final java . lang . String s ; public String ( java . lang . String s ) { this . s = s ; } public java . lang . String toString () { return s ; } } Was gibt das Programm aus? Exception in thread "main" java.lang.NoSuchMethodError: main 25