Alternative zu Methoden für rekursive Datenstruktur?
Verfasst: Mittwoch 12. Januar 2011, 18:27
Wie schon mehrfach erwähnt, baue ich aus Spaß einen Python-Interpreter in Python. Dieser zerlegt einen String in eine Liste von Symbolen, baut daraus einen abstrakten Syntaxbaum (AST) und bietet Methoden, die zusammen mit einem minimalen Laufzeitsystem den AST interpretieren können.
Der Scanner, der den String aufbricht, ist eine einzelne Funktion. Der Parser besteht jetzt ebenfalls aus einfachen Funktionen. Also dachte ich, ich könnte vielleicht komplett ohne Klassen auskommen. Den AST repräsentiere ich in guter Lisp-Tradition als (tief) verschachtelte Tupel:
Doch wie verarbeite ich diesen am geschicktesten?
Wären die AST-Knoten Exemplare von Klassen, würde ich entsprechende Methoden definieren:
Ich könnte explizit dispatchen:
Oder als wenn ich ein switch-case hätte:
Oder indirekt, indem ich statt Strings direkt die Funktionsobjekte benutze:
Dann kann ich aber nur eine Funktion auf dem AST definieren. Da fällt mir aber folgendes ein:
Gibt es Alternativen?
Mein Interpreter beherrscht leider keine Decorators und keine lokale Funktionen. Bevor ich diese angehe, wäre es einfacher, Klassen zu implementieren
Stefan
Der Scanner, der den String aufbricht, ist eine einzelne Funktion. Der Parser besteht jetzt ebenfalls aus einfachen Funktionen. Also dachte ich, ich könnte vielleicht komplett ohne Klassen auskommen. Den AST repräsentiere ich in guter Lisp-Tradition als (tief) verschachtelte Tupel:
Code: Alles auswählen
("Suite", [
("Def", "fib", ["n"], ("Suite", [
("If",
("Lt", ("Var", "n"), ("Lit", 2)),
("Return", ("Var", "n")),
("Return",
("Add",
("Call", ("Var", "fib"), [("Sub", ("Var", "n"), ("Lit", 1))]),
("Call", ("Var", "fib"), [("Sub", ("Var", "n"), ("Lit", 2))]))))])),
("Call", ("Var", "fib"), [("Lit", 10)])])Wären die AST-Knoten Exemplare von Klassen, würde ich entsprechende Methoden definieren:
Code: Alles auswählen
class Var:
def eval(self, f):
return f.get(self.name)
class Add:
def eval(self, f):
return self.left.eval(f) + self.right.eval(f)Code: Alles auswählen
def _eval(ast, f):
# dies könnte auch ein eigenes dict sein, aber so ist's kürzer:
return globals()[ast[0] + "_eval"](ast, f)
def Var_eval(ast, f):
return f.get(ast[1])
def Add_eval(ast, f):
return _eval(ast[1], f) + _eval(ast[2], f)Code: Alles auswählen
def _eval(ast, f):
if ast[0] == "Var":
return f.get(ast[1])
if ast[0] == "Add":
return _eval(ast[1]) + _eval(ast[2])
...Code: Alles auswählen
def _eval(ast, f):
return ast[0](ast, f)
(Add_eval, (Var_eval, "n"), (Lit_eval, 1))Code: Alles auswählen
def _eval(ast, f):
return ast[0][0](ast, f)
def _set(ast, f, value):
ast[0][1](ast, f, value)
def Var_set(ast, f, value):
f.set(ast[1], value)
def None_set(ast, f, value):
raise SyntaxError
Var = (Var_eval, Var_set)
Add = (Add_eval, None_set)
(Add, (Var, "n"), (Lit, 1))Mein Interpreter beherrscht leider keine Decorators und keine lokale Funktionen. Bevor ich diese angehe, wäre es einfacher, Klassen zu implementieren
Stefan