Im folgenden wird angegeben welcher Code zu erzeugen ist:
Bed ::= ... | { BoolConst} "int": value // true=1, false=0Für eine Bedingung BoolConst(value) wird folgender Code erzeugt:
bipush 1oder bipush 0 je nachdem, ob value 1 oder 0 ist (true wird im Scanner als 1 codiert; false als 0).
Im obigen Übersetzungsschema wird der Code für die Bedingung so erzeugt, dass zur Laufzeit immer der Wert der Bedingung als oberstes Kellerelement erscheint.
Anw ::= ... | { IfThenElse } Bed Anw_Seq Anw_SeqFür IfThenElse (bed anw_Seq1 anw_Seq2) wird folgender Code erzeugt:
Im obigen Übersetzungsschema wird zunächst der Code für die Bedingung erzeugt. Danach folgt der Sprungbefehl. Die Sprungbedingung ifnull vergleicht das oberste Kellerelement mit 0. Zur Laufzeit wird, wenn der Vergleich erfolgreich ist, zu label1, d.h. zum else-Teil gesprungen. Nach dem Code für den then-Teil folgt ein Sprung über den else-Teil. Vor dem else-Teil wird die Marke label1 gesetzt, danach folgt der Code für den else-Teil. Zum Schluss wird die Marke label2 generiert.
<Code für bed>
ifnull label1
<Code für anw_Seq1>
jsr label2
label1:
<Code für anw_Seq2>
label2:
Marken im Jasmin-Code werden durch einen Markennamen, gefolgt von einem Doppelpunk und newline, definiert. Markennamen können nicht mit einer Ziffer beginnen und dürfen keines der folgenden Sondeerzeichen enthalten
= : . " -
Anw ::= ...| {WhileAnw} Bed Anw_Seq
Für WhileAnw (bed anw_Seq) wird folgender Code erzeugt:
Es ist üblich den Code für die Bedingung an das Ende der Schleife zu setzen. Dadurch ist der Code etwas günstiger.
jsr label1
label2:
<Code für anw_Seq>
label1:
<Code für bed>
ifnonnull label2
Im folgenden wird angegeben, was in dieser Aufgabe genau zu tun ist:
public void visit(BoolConst boolConst)In der Methode soll der Code nach dem obigen Schema erzeugt werden.
Für die Strategieänderung gibt es zwei Lösungen. Sie benutzen für den Durchlauf des Baumes wie im IdentificationVisitor statt der traverseBottomUp-Methode die accept-Methode. Dabei mussen Sie die Durchlaufstrategie für alle Knoten explizit angeben, d.h. für jeden Knotentyp eine visit-Methode schreiben, in der meist nur childrenAccept-Methode aufgerufen wird. In den IfThenElse- und WhileAnw-Knoten müssen Sie dann die Sohn-Knoten in der richtigen Reihenfolge besuchen, so dass der Code wie oben ausgedruckt wird. Den Baumdurchlauf müssen Sie auch in der main-Methode der Main-Klasse mit accept anstoßen.
Bei der zweiten Lösung behalten Sie die Bottom-up-Strategie, aber geben den Code nicht
direkt aus, sondern speichern ihn in einem Attribut im Baum und können ihn dann in den
IfThenElse- und WhileAnw-Knoten in der gewünschten Reihenfolge zusammenstellen. Dafür definieren Sie im ast.cl ein Code-Attribut für die Knoten für die wir Code erzeugen:
attr "String" code with Anw_Seq, Anw, Bed, Ausdr, Var;und belegen das Code-Attribut z.B. im BoolConst-Knoten:
public void visit( BoolConst boolConst ) {Wenn der Code länger als ein Befehl ist, sollte man es in einem StringBuffer zusammenstellen und dann erst in das Code-Attribut speichern.
boolConst.setCode("bipush " + boolConst.getValue() + "\n");
...Zugriff auf das Code-Attribut des Sohnes sieht folgendermaßen aus, z.B. auf das Code-Attribut der Bedingung im WhileAnw-Knoten
StringBuffer buffer = new StringBuffer();
buffer.append( . . . );
. . .
*.setCode( buffer.toString() );
whileAnw.getBed().getCode();
Sie müssen auch den Code für Anw_Seq0, Anw_Seq1, BinAusdr, IntAusdr , VarAusdr und IdentAusdr in das jeweilige Code-Attribut speichern! Für die weiteren Knotentypen müssen Sie keine eigene visit-Methoden definieren.
public void visit(IfThenElse ifThenElse)In den Methoden soll der Code nach den obigen Schemas erzeugt werden.
public void visit(WhileAnw whileAnw )
program P{
int a, b, c
a + b * c -111
while true do a+b*222 od
if false then b+333 fi
if true
then while true do a+b*444 od
else if false then a+b*555 fi
fi
}
Zusätzlich geben Sie noch Ihre Quelldateien und den lauffähigen Compiler vom Verzeichnis dist ab (Verwenden Sie ant dist dafür.)
Jasmin Dokumentation ist hier: Jasmin Home Page
Bei unserer Identifikation prüft der Compiler, ob alle auf den rechten Seiten der Produktionen benutzten Identifier definiert sind, d. h. ob für sie eine Produktion angegeben ist. Es dürfen auch keine doppelten Definitionen vorkommen. Unser Compiler müsste also beim folgenden Beispiel melden, dass Privat nicht definiert ist und Geschaeftlich doppelt deklariert ist:
Adressbuch::= Privat GeschaeftlichHinweise:
//Privat::= Person*
Geschaeftlich::= Person*
Geschaeftlich::= Person*
Person::= String:Nachname
String:Vorname
String:Telefon
In der CUP-Eingabe:
var ::= IDENT:iAls Namen für die 3. und 4. Komponente eines Symbols kontekatiniert CUP den Namen der value-Komponente (hier i) mit left bzw. right (s. Objekttyp Symbol im Merkblatt3).
{: RESULT = new IdentAusdr(i,ileft, iright); :}
In der Classgen-Eingabe können Sie selbstgewählte Namen verwenden:
Var ::= {IdentAusdr} "String":ident "int":line "int": columnDann können Sie im IdentificationVisitor in der Fehlermeldung auch die Zeilen und Spaltenangabe ausgeben.
| . . .
public void visit(IdentAusdr identAusdr) {
identAusdr.deklinfo = lookup(identAusdr.ident);
System.out.println("Ident: "+identAusdr.ident+" Zeile: "+identAusdr.line+" Spalte: "+ identAusdr.column);
}
Production ::= String: name String*: elementsDer generierte Datentyp StringList wäre geeignet für unsere Aufgabe und Sie könnten Sie in dem IdentificationVisitor verwenden.
Geben Sie als Lösung der Aufgabe