![]() |
|||
| HSG |
|
Grundsätzlich lassen sich für jede Produktion Objekte erzeugen, die mit anderen beliebig verkettet werden können und die mit geeigneten Attributen und Methoden ausgestattet werden können. Die Idee der vorliegenden Lösung besteht darin, während des Parsens eine Kette von Objekten zu erzeugen. Mit Hilfe der Kette können schließlich die Befehle nacheinander abgearbeitet werden. Repeat-Befehle können erledigt werden, da 'Unter-Ketten' entsprechend häufig abgearbeitet werden.
Als erstes Beispiel betrachten wir: 'fd 100 lt 90'. Jedem reduce-Schritt können Anweisungen hinzugefügt werden, die auf die Werte der rechten Seite der Produktion zugreifen können. Da LR-Parsing bottom-up erfolgt, ist die erste Reduktion B = FD NUMBER. Die Werte von FD und NUMBER sind 'fd' und 100. Man kann auf die Werte mit p[1] und p[2] zugreifen. Es wird ein Objekt vom Typ Anw erzeugt. Die Adresse des Objekts wird mit p[0] = Anw() dem Knoten B zu geordnet. Das Objekt hat eine Methode ausf, der turtle.fd(100) zugeordnet wird. Wer Zugriff auf das Objekt hat, kann auch ausf ausführen. Für den zweiten 'B-Knoten' wird entsprechend verfahren. Dem untersten 'S-Knoten' wird kein Objekt, also None zugeordnet. 'S-Knoten' vom Typ 'S = B S' bekommen ein AKette-Objekt zugeordnet, das über bef Zugriff auf den Befehl hat und das über next das nächste AKette-Objekt erreichen kann. Am Schluss der Ableitung gibt die Parse-Methode den Wert des Startknotens zurück. In unserem Fall ist das das erste AKette-Objekt. Ein AKette-Objekt hat nun eine Funktion ausf, die - falls möglich - zunächst die ausf-Methode des zugehörigen Befehls aufruft und dann - ebenfalls falls möglich - die ausf-Methode des nächsten AKette-Objekts. In Python könnte man sich die Anw-Objekte sparen und direkt mit den Referenzen auf die ausf-Methoden arbeiten. Man sieht am Beispiel, wie man in beliebigen OOP-Sprachen eine fehlende Referenzierung von Funktionen (Funktionen als Parameter) nachbilden kann.
import ply.yacc as yacc
from logolex0 import tokens
class AKette(object):
def __init__(self):
self.next = None
self.bef = None
def ausf(self):
if self.bef != None:
self.bef.ausf()
if self.next != None:
self.next.ausf()
class Anw(object):
def ausf(self): # wird von Objekt überschrieben
pass
def p_0(p):
'logo : befehl logo'
p[0] = AKette()
p[0].next = p[2]
p[0].bef = p[1]
def p_1(p):
'logo :'
p[0] = None
import turtle
def p_2(p):
'befehl : FD NUMBER'
p[0] = Anw()
n = p[2]
def f():
turtle.fd(n)
p[0].ausf = f
def p_3(p):
'befehl : BK NUMBER'
p[0] = Anw()
n = p[2]
p[0].ausf = lambda : turtle.bk(n)
def p_4(p):
'befehl : LT NUMBER'
p[0] = Anw()
n = p[2]
p[0].ausf = lambda : turtle.lt(n)
def p_5(p):
'befehl : RT NUMBER'
p[0] = Anw()
n = p[2]
p[0].ausf = lambda : turtle.rt(n)
def p_6(p):
'befehl : PU'
p[0] = Anw()
p[0].ausf = lambda : turtle.pu()
def p_7(p):
'befehl : PD'
p[0] = Anw()
p[0].ausf = lambda : turtle.pd()
def p_8(p):
"befehl : RP NUMBER '[' logo ']'"
p[0] = Anw()
n = p[2]
kette = p[4]
def f():
for i in range(n):
kette.ausf()
p[0].ausf = f
def p_error(p):
print('Syntax-Fehler!')
pr = yacc.yacc()
Tests
>>>
Generating LALR tables
>>> pr.parse('rp8[rp8[lt45fd30]lt45]').ausf()
>>>
Veranschauliche mit einem Syntax-Baum wie oben die Abarbeitung des Wortes 'rp 8 [ fd 50 lt 45 ]'. Lasse dir zunächst mit JFlap und der Datei minilogo.jff den Syntaxbaum ausgeben. Benutze in einem zweiten Schritt obige svg-Datei und das Programm Inkscape, um die Skizze zu erzeugen.