Nand2Tetris — Kapitel 11
Der Jack-Compiler
# Jack Compiler
# Eingabe: .jack-Datei
# Ausgabe: .vm-Datei
# Geltungsbereich: Symboltabelle + VM-Codegenerierung
# Bewegung:
# Verwalten Sie Klassen-/Statik-/Feldsymbole und Unterprogramm-/Lokal-/Argumentsymbole getrennt.
# Konvertieren Sie let, do, return und expression in VM-Befehle
class SymbolTable:
def __init__(self):
self.class_scope = {}
self.subroutine_scope = {}
self.counts = {"static": 0, "field": 0, "argument": 0, "var": 0}
def start_subroutine(self):
self.subroutine_scope = {}
self.counts["argument"] = 0
self.counts["var"] = 0
def define(self, name, type_name, kind):
index = self.counts[kind]
self.counts[kind] += 1
symbol = {
"type": type_name,
"kind": kind,
"index": index,
}
if kind in ("static", "field"):
self.class_scope[name] = symbol
else:
self.subroutine_scope[name] = symbol
def var_count(self, kind):
return self.counts[kind]
def find(self, name):
if name in self.subroutine_scope:
return self.subroutine_scope[name]
return self.class_scope.get(name)
class VMWriter:
SEGMENT = {
"static": "static",
"field": "this",
"argument": "argument",
"var": "local",
}
def __init__(self):
self.lines = []
def write_push(self, segment, index):
self.lines.append(f"push {segment} {index}")
def write_pop(self, segment, index):
self.lines.append(f"pop {segment} {index}")
def write_arithmetic(self, command):
self.lines.append(command)
def write_label(self, label):
self.lines.append(f"label {label}")
def write_goto(self, label):
self.lines.append(f"goto {label}")
def write_if(self, label):
self.lines.append(f"if-goto {label}")
def write_call(self, name, arg_count):
self.lines.append(f"call {name} {arg_count}")
def write_function(self, name, local_count):
self.lines.append(f"function {name} {local_count}")
def write_return(self):
self.lines.append("return")
class CompilerExample:
def __init__(self):
self.symbols = SymbolTable()
self.vm = VMWriter()
def write_variable_push(self, name):
symbol = self.symbols.find(name)
segment = VMWriter.SEGMENT[symbol["kind"]]
self.vm.write_push(segment, symbol["index"])
def write_variable_pop(self, name):
symbol = self.symbols.find(name)
segment = VMWriter.SEGMENT[symbol["kind"]]
self.vm.write_pop(segment, symbol["index"])
def compile_let_simple(self, name):
# let name = expression;
# Angenommen, das Ausdrucksergebnis befindet sich oben im Stapel, platzieren Sie es an der Variablenposition
self.write_variable_pop(name)
def compile_do_call(self, full_name, arg_count):
# Funktionsaufruf ausführen; Verwendet den Rückgabewert nicht, daher wird temp als 0 verworfen.
self.vm.write_call(full_name, arg_count)
self.vm.write_pop("temp", 0)
def compile_return_void(self):
self.vm.write_push("constant", 0)
self.vm.write_return()
//Jacks Code: sei x = 10 + 20;
//Beispiel für eine VM-Konvertierung
push constant 10
push constant 20
add
pop local 0
//Jack-Code: do Output.printInt(x);
//Beispiel für eine VM-Konvertierung
push local 0
call Output.printInt 1
pop temp 0