Programmiersprachen - Compiler 1 Code Erzeugung für arithmetische Ausdrücke In diesem Praktikum werden ein Scanner und ein Parser eingesetzt, um arithmetische Ausdrücke mit natürlichen Zahlen auszuwerten und zu berechnen, z.B.: ( 1 + 1 ) * 5 = 10 . Dabei soll die Berechnung mit Code durchgeführt werden, welcher zur Laufzeit JavaBytecode generiert und anschliessend aufgerufen wird. Eine Grundstruktur von Scanner und Parser ist vorgegeben. Ebenso vorgegeben ist ein Grundgerüst einer Klasse, welche die Verwendung der Java-Bytecode Generatoren und die Reflexion in Java demonstrieren. Der Ablauf vom Zeichenstrom bis zum Java-Bytecode stellt folgender Grafik dar: Der Scanner Mit dem Scanner wird der arithmetische Ausdruck (Zeichenstrom) in folgende Teile (Tokens) zerlegt: Zahlen, Operatoren und Klammern. Dieser Tokenstrom wird dann dem Parser übergeben. Der Parser Der Parser in unserem Beispiel kann Ausdrücke aus der Sprache mit folgender Grammatik parsen: Ausdruck = Term (("+" | "-") Term)* Term = Faktor (("*" | "/") Faktor)* Faktor = Zahl | "("Ausdruck ")" Ausdruck, Term und Faktor sind sog. Produktionen. Für jede Produktion muss im Parser eine Methode gleichen Namens bestehen, welche gemäss Angaben in der Grammatik rekursiv die Methoden der anderen Produktionen aufruft, aus denen die Produktion selbst besteht. So ruft z.B. die Methode Factor diejenige Methode der Produktion Ausdruck (expr) auf. Aufgabe 1 Der Scanner soll so umgeschrieben werden, dass Werte vom Typ Double verarbeitet werden können. Dazu muss die Methode readNumber im Scanner so angepasst werden, dass Fliesskommazahlen erkannt werden können (ohne Exponenten). überlegen Sie sich den Automaten für Fliesskommazahlen mit folgender Grammatik Ziffer {Ziffer} ['.' {Ziffer}] und implementieren Sie diesen im Scanner. Aufgabe 2 Der Parser ist noch unvollständig. Ihre Aufgabe ist es den Parser so zu ergänzen, dass er einfache arithmetische Ausdrücke direkt ausrechnen kann. Aufgabe 3 Scanner und Parser sind nun funktionstüchtig. Statt den Wert direkt zu berechnen soll nun aber entsprechender Java-Bytecode erstellt und zur Ausführung gebracht werden. Als erstes sollen sie die Klassen ILSample.java und ILSample2.java studieren. In diesen Klassen wird zur Laufzeit Java-Bytecode erzeugt, genau genommen eine Klasse mit einer Methode. Dieser Bytecode wurde mit der Library BCEL (Byte Code Engineering Library) generiert. Die Methode buildClass ist so konstruiert, dass eine Klasse erzeugt wird, die das beim Aufruf mitgegebene Interface implementiert. Die eigentliche CodeGenerierung wird in einem Objekt mitgegeben, dass das Interface ICodeGenStart implementiert (Generierung in der start() Methode). Innerhalb der Methode werden Funktionen der BCEL Bibliothek aufgerufen. BCEL ermöglicht dem Programmierer Java Class-Dateien zu erstellen. Mehr Informationen unter: http://jakarta.apache.org/bcel/ Ihre Aufgabe ist es nun während der Laufzeit Bytecode einer Klasse mit einer Methode zu generieren, die beliebige arithmetische Ausdrücke berechnen kann. Dazu müssen sie folgende Klassen aus der BCEL verstehen: ConstantPoolGen Generiert man in BCEL eine Klasse, wird ihr automatisch ein so genannter Konstanten-Pool angehängt. In diesem Konstanten-Pool können beliebig Konstanten gespeichert werden. Einige Bytecode Operationen beziehen ihre Werte aus diesem Konstanten-Pool. InstructionList In der Instruktionsliste werden die Instruktionsobjekte abgelegt. Die Instruktionsobjekte und die Reihenfolge in der sie in der Instruktionsliste abgelegt wurden definieren die Funktion einer generischen Methode. Nehmen Sie nun die Klasse ILSample als Vorlage. Generieren Sie eine Klasse mit einer Methode. Damit die Methode auch arithmetische Ausdrücke berechnen kann, müssen sie den Konstanten-Pool und die Instruktionsliste von der Klasse Parser aus richtig abfüllen. Viel Spass bei der Aufgabe Hinweis: • • • JCreator Benutzer: keine besondere Einstellungen nötig Netbeans Benutzer: müssen das Working Directory noch auf /build/classes setzen (in Project Properties Æ Run) Eclipse Benutzer: viel Glück