Station - Ein Code-Erzeuger für strukturierte MyWhile-Programme
Aufgabe des Code-Erzeugers
Der Code-Erzeuger hat die Aufgabe, strukturierte MyWhile-Programme in Programme einer (maschinennahen) Sprache zu übersetzen. Die Arbeitsweise des Code-Erzeugers soll am folgenden Beispiel-Programm verdeutlicht werden.
x = 24 y = 15 d = x - y while d != 0: if d > 0: x = x - y else: y = y - x #if d = x - y #while
Der Code-Erzeuger verarbeitet als Quellcode das zugehörige strukturierte While-Programm:
[
['=', ('VAR', 'x'), [('NAT', '24')]],
['=', ('VAR', 'y'), [('NAT', '15')]],
['=', ('VAR', 'd'), ['-', ('VAR', 'x'), ('VAR', 'y')]],
['while', ['!=', ('VAR', 'd'), ('NAT', '0')],
[
['if', ['>', ('VAR', 'd'), ('NAT', '0')],
[
['=', ('VAR', 'x'), ['-', ('VAR', 'x'), ('VAR', 'y')]]
],
[
['=', ('VAR', 'y'), ['-', ('VAR', 'y'), ('VAR', 'x')]]
]
],
['=', ('VAR', 'd'), ['-', ('VAR', 'x'), ('VAR', 'y')]]
]
]
]
Diesen Quellcode transformiert der Code-Erzeuger in folgenden Zielcode:
[
(None, ['=', ('VAR', 'x'), [('NAT', '24')]]),
(None, ['=', ('VAR', 'y'), [('NAT', '15')]]),
(None, ['=', ('VAR', 'd'), ['-', ('VAR', 'x'), ('VAR', 'y')]]),
('.L3', ['noop']),
(None, ['if', ['!=', ('VAR', 'd'), ('NAT', '0')], ['goto', '.L4'], ['goto', '.L5']]),
('.L4', ['noop']),
(None, ['if', ['>', ('VAR', 'd'), ('NAT', '0')], ['goto', '.L0'], ['goto', '.L1']]),
('.L0', ['noop']),
(None, ['=', ('VAR', 'x'), ['-', ('VAR', 'x'), ('VAR', 'y')]]),
(None, ['goto', '.L2']),
('.L1', ['noop']),
(None, ['=', ('VAR', 'y'), ['-', ('VAR', 'y'), ('VAR', 'x')]]), ('.L2', ['noop']),
(None, ['=', ('VAR', 'd'), ['-', ('VAR', 'x'), ('VAR', 'y')]]),
(None, ['goto', '.L3']),
('.L5', ['noop'])
]
Der Zielcode stellt eine strukturierte Version des folgenden Goto-Programms dar:
x=24 y=15 d=x-y label .L3 if d!=0: goto .L4 else: goto .L5 label .L4 if d>0: goto .L0 else: goto .L1 label .L0 x=x-y goto .L2 label .L1 y=y-x label .L2 d=x-y goto .L3 label .L5
Die Codesprache MyGoto
Syntax und Semantik der Sprache Goto werden hier nur informell beschrieben.
MyGoto-Programme benutzen Sprunganweisungen, um Fallunterscheidungen und Wiederholungen zu modellieren. Es gibt verschiedene Typen von Anweisungen:
- Zuweisungen der Gestalt
"Variable" = "Term" - Festlegung von Sprungmarken der Gestalt
label .L"Zahl" - Sprungbefehle der Gestalt
goto "Spungmarke" - Bedingte Sprungbefehle der Gestalt
if "Bedingung": goto "Spungmarke" else: goto "Sprungmarke" - Passieranweisungen der Gestalt
pass
Zuweisungen und Bedingungen dürfen dabei nur so wie in der Sprache MyWhile gebildet werden.
Bei der Ausführung von Goto-Programmen sind folgende Vorgaben zu beachten:
- Eine Zuweisung wird wie in der Sprache MyWhile ausgeführt. Als nächste Anweisung wird dann die in der nächsten Programmzeile ausgeführt.
- Bei einem Sprungbefehl der Gestalt
goto "Spungmarke"wird die Ausführung mit der Zeile fortgesetzt, die die angegebenen Marke festlegt. - Bei einem bedingten Sprungbefehl wird zunächst die Bedingung ausgewertet, bevor dann in Abhängigkeit des Ergebnisses der Sprung erfolgt.
- Eine Passieranweisung der Gestalt
passwird übergangen. Es wird also in der darauf folgenden Zeile weitergemacht.
Die Arbeitsweise des Code-Erzeugers
Die entscheidende Aufgabe des Code-Erzeugers ist es hier, Fallunterscheidungen und Wiederholungen mit Hilfe von goto-Sprunganweisungen zu modellieren. Wir verdeutlichen die Übersetzungsschablonen anhand einfacher Beispiele.
Fallunterscheidung:
x = 1 if x1 != 0: y = x else: y = 0 #if
Die Kommentare illustrieren die Idee der Übersetzung.
x=1 if x1!=0: # Auswertung der Bedingung goto .L0 # Sprung zum wahr-Fall else: goto .L1 # Sprung zum falsch-Fall label .L0 # wahr-Fall y=x goto .L2 # Sprung zum Ende der Fallunterscheidung label .L1 # wahr-Fall y=0 label .L2 # Ende der Fallunterscheidung
Wiederholung:
x = 5 while x != 0: x = x - 1 #while
Die Kommentare illustrieren die Idee der Übersetzung.
x=5 label .L0 # Beginn der Schleife if x!=0: # Auswertung der Bedingung goto .L1 # Sprung zum Schleifenkörper else: goto .L2 # Sprung aus der Schleife label .L1 # Beginn des Schleifenkörpers x=x-1 goto .L0 # Sprung zum Beginn der schleife label .L2 # Ende der Schleife
Implementierung des Code-Erzeugers
Der Code-Erzeuger wird durch ein Objekt der Klasse UebersetzerWhileList realisiert.
Dieses Objekt verfügt insbesondere über eine Methode uebersetzen, die
letztlich für die Erzeugung des Zielcodes zuständig ist.
class UebersetzerWhileList(object):
def __init__(self):
self.quellcode = None
def setQuellcode(self, q):
self.quellcode = q
def uebersetzen(self):
def c_programm(p):
'programm : anweisungsfolge'
return c_anweisungsfolge(p)
def c_anweisungsfolge(p):
'''anweisungsfolge : anweisung anweisungsfolge
| anweisung'''
if len(p) > 1:
return c_anweisung(p[0]) + c_anweisungsfolge(p[1:])
else:
return c_anweisung(p[0])
def c_anweisung(p):
'''anweisung : VAR ZUW term
| PASS
| WHILE bedingung DP anweisungsfolge ENDWH
| IF bedingung DP anweisungsfolge ELSE DP anweisungsfolge ENDIF'''
if p[0] == "=":
return [(None, p)]
elif p[0] == "pass":
return [(None, ['noop'])]
elif p[0] == 'while':
ergebnis_true = c_anweisungsfolge(p[2])
ergebnis_wh = [('.L' + str(self.zaehler), ['noop']), \
(None, ['if', p[1], \
['goto', '.L' + str(self.zaehler+1)], \
['goto', '.L' + str(self.zaehler+2)]]), \
('.L' + str(self.zaehler+1), ['noop'])] + \
ergebnis_true + \
[(None, ['goto', '.L' + str(self.zaehler)]), \
('.L' + str(self.zaehler+2), ['noop'])]
self.zaehler = self.zaehler + 3
return ergebnis_wh
elif p[0] == 'if':
ergebnis_true = c_anweisungsfolge(p[2])
ergebnis_false = c_anweisungsfolge(p[3])
ergebnis_if = [(None, ['if', p[1], \
['goto', '.L' + str(self.zaehler)], \
['goto', '.L' + str(self.zaehler+1)]]), \
('.L' + str(self.zaehler), ['noop'])] + \
ergebnis_true + \
[(None, ['goto', '.L' + str(self.zaehler+2)])] + \
[('.L' + str(self.zaehler+1), ['noop'])]+ \
ergebnis_false + \
[('.L' + str(self.zaehler+2), ['noop'])]
self.zaehler = self.zaehler + 3
return ergebnis_if
self.zaehler = 0
if self.quellcode != None:
return c_programm(self.quellcode)
else:
return []
Ein Objekt der Klasse UebersetzerWhileList kann also benutzt werden, um strukturierte
While-Programme in strukturierte Goto-Programme zu übersetzen.
from uebersetzerWhileList import *
# Testprogramm
quellcode = [
['=', ('VAR', 'x'), [('ZAHL', '24')]],
['=', ('VAR', 'y'), [('ZAHL', '15')]],
['=', ('VAR', 'd'), ['-', ('VAR', 'x'), ('VAR', 'y')]],
['while', ['!=', ('VAR', 'd'), ('ZAHL', '0')],
[
['if', ['>', ('VAR', 'd'), ('ZAHL', '0')],
[
['=', ('VAR', 'x'), ['-', ('VAR', 'x'), ('VAR', 'y')]]
],
[
['=', ('VAR', 'y'), ['-', ('VAR', 'y'), ('VAR', 'x')]]
]
],
['=', ('VAR', 'd'), ['-', ('VAR', 'x'), ('VAR', 'y')]]
]
]
]
codeerzeuger = UebersetzerWhileList()
# Erzeugung des Zielcodes
codeerzeuger.setQuellcode(quellcode)
zielcode = codeerzeuger.uebersetzen()
# Ausführung des Programms und Ausgabe der Zustände
print('Quellcode:')
print()
for zeile in quellcode:
print(zeile)
print()
print('Zielcode:')
print()
for zeile in zielcode:
print(zeile)
Aufgabe 1
Probiere das selbst einmal aus. Übersetze verschiedene strukturierte MyWhile-Programme.