1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
from argparse import ArgumentParser
from .memory import translate_memory
from .arith_logic import translate_arith_logic
from .compare import translate_compare
from .utils import *
def vm_translate(input_path):
# program name, for static variable labels
input_fn = input_path.split("/")[-1]
prog = input_fn[:-3] if input_fn.endswith(".vm") else input_fn
try:
input_file = open(input_path)
except:
exit_on_error(f"Cannot open input file: {input_fn}", EXIT_CODE_FILE_ERROR)
# load all vm commands from file
vm_cmds = []
line = input_file.readline()
while line:
cmd = line.rstrip("\n").split("//", maxsplit=1)[0].strip()
if cmd:
vm_cmds.append(cmd)
line = input_file.readline()
input_file.close()
asm = ""
for cmd in vm_cmds:
# tokenize vm_line, hand to sub-translators based on first token
match cmd.split():
case []:
continue
case [("push" | "pop") as action, segment, index]:
asm += translate_memory(action, segment, index, prog)
case [("add" | "sub" | "neg" | "and" | "or" | "not") as command]:
asm += translate_arith_logic(command)
case [("lt" | "eq" | "gt") as command]:
asm += translate_compare(command)
case _:
exit_on_error(
f"Syntax error: invalid command: {cmd}", EXIT_CODE_SYNTAX_ERROR
)
output_path = (
input_path[:-3] + ".asm" if input_path.endswith(".vm") else input_path + ".asm"
)
try:
output_file = open(output_path, "w")
except:
exit_on_error(f"Cannot open output file: {output_fn}", EXIT_CODE_FILE_ERROR)
output_file.write(asm)
output_file.close()
print(f"Assembly written to {output_path}")
if __name__ == "__main__":
parser = ArgumentParser("hack-vm")
parser.add_argument("input_path", help="input file in HACK VM")
args = parser.parse_args()
vm_translate(args.input_path)
|