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.