Eigene Programmiersprache erster Teil Aufgabe 1 Code Generierung für arithmetische Ausdrücke Als erstes sollen Sie die Klassen ILSample.java und ILSample2.java studieren. In diesen Klassen wird zur Laufzeit Java-Bytecode generiert, genau genommen eine Klasse mit einer Methode, die ein vorgegebenes Interface implementiert. Der Bytecode wird mit der Library BCEL (Byte Code Engineering Library) generiert. Die Methode buildClass ist so konstruiert, dass eine Klasse erstellt wird, die das beim Aufruf mitgegebene Interface implementiert. Die eigentliche CodeGenerierungsmethode wird in einem Objekt mitgegeben, das 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/ Sie sollten 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 der Methode. Aufgabe: Generieren Sie Code für die Berechnung von arithmetischen Ausdrücken (Expressions). Hinweise: • • • Konstante Werte im Ausdruck müssen zuerst im Konstanten-Pool abgelegt und die passende Instruktion dazu generiert werden. Nehmen Sie die Klasse ILSample und ILSample2 als Vorlagen. Ihr Parser muss in der codeGen-Methode übergebenen start-Methode aufgerufen werden. Statt den Wert direkt zu berechnen, soll Code für den Ausdruck generiert werden und dann nachträglich berechnet werden (durch Aufruf der generierten Methode). Das Gerüst für den Aufruf sieht folgendermassen aus: public static void main(String[] args) throws Exception Scanner.init("4.2 + 3.2*2"); CodeGen.init(); Scanner.scan(); ICalculator calculator = (ICalculator) CodeGen.buildClass(ICalculator.class, new Calculator ()); // Die generierte Methode wird aufgerufen. System.out.println("\Result: " + calculator.calc(0)); } Wobei die Calculator Klasse das ICodeGenStart Interface implementiert. In der start Methode wird dann einfach expr()aufgerufen, das den Parse Vorgang startet. Beim Parsen wird dann der Java Byte Code generiert. class Calculator implements ICodeGenStart { … Die BCEL Bibliothek ist eine interne Bibliothek und deshalb werden vom Compiler Warnungen generiert. Sie können Sie aber mit der Compiler Option -XDignore.symbol.file ausschalten. Aufgabe 2 Code Generierung mit Variablen Jetzt wollen wir einen Schritt weiter gehen und Code für Ausdrücke mit Variablen generieren. Erstellen Sie dafür eine Klasse Programm und folgendes Interface; public interface IProgram { double main(double arg); } und passen Sie Ihren Aufruf an: … IProgram program = (IProgram) CodeGen.buildClass(IProgram.class, new Program ()); // Die generierte Methode wird aufgerufen. System.out.println("\nProgram Result: " + program.main(49)); Anbei die Grammatik unserer (ersten) Programmiersprache. expr = term {("+" | "-”) term} term = factor {("*" | "/") factor} factor = number | ident | "(" expr ")" assignement = ident "=" expr ";" statement = assignment statementSequence = statement {statement} program = statementSequence Assignement: einer lokalen Variablen wird ein Ausdruck zugewiesen, z.B. a = 6+2. Zusätzlich sollen in Ausdrücken (expr) ebenfalls Variablen zugelassen werden, z.B. i =i -1 Statement: Eine Anweisung ist ein Assignment (wird später noch erweitert). Program: besteht aus einer oder mehreren Anweisungen. Eine Zuweisung besteht aus einem Variablennamen (dem ein Speicherplatz zugewiesen wurde) und einem Ausdruck. Damit der Parser weiss, welcher Speicherplatz für welche Variabel reserviert wurde, wird eine Liste geführt. Beispiel-Programm x = arg0; a = 1; b = 2; c = 3; value = a*x*x + b*x + c; Die Parameter seien in den vordefinierten Variablen arg0, arg1, … und der Rückgabe Wert in value gespeichert. Hinweis: • • Die Methode local der Klasse CodeGen liefert die Nummer des Slots der benannten lokalen Double-Variablen oder generiert bei Bedarf eine neue SlotNummer; diese können für die entsprechenden Load und Store Operation verwendet werden. Vergessen Sie nicht, dass am Schluss der Methode ein DRETURN stehen muss und der berechnete Wert (in der Variablen "value") auf dem Stack abgelegt sein muss. Beispiele: Zuweisung a = expr() // Erzeugen einer neuen Variablen a int slot = CodeGen.local("a"); // Store-Instruktion, die das oberste // Stack-Element in a speichert. CodeGen.il.append(new DSTORE(slot)); a kommt in einem Ausdruck vor, z.B a+2: int slot = CodeGen.local("a"); // Adresse von a // Load-Instruktion, die den Wert von a auf den Stack pushed CodeGen.il.append (new DLOAD(slot));