-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathassembler.py
More file actions
110 lines (86 loc) · 2.62 KB
/
assembler.py
File metadata and controls
110 lines (86 loc) · 2.62 KB
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
from argparse import ArgumentParser
from pathlib import Path
import re
import sqlite3
OPCODES = {
'NOP': 0,
'PUSH': 1,
'POP': 2,
'DUP': 4,
'PICK': 5,
'ADD': 6,
'SUB': 8,
'AND': 10,
'OR': 12,
'NOT': 14,
'JUMP': 15,
'JUMPP': 16,
'RET': 16,
'JUMPZ': 17,
'JUMPPZ': 18,
'CALL': 19,
'CALLP': 20,
'READ': 22,
'WRITE': 24,
'FIN': 254
}
def assemble(assembly: str) -> bytes:
assembly_pattern = re.compile(r'^\s*(?:(\w+)(\[(?:\d+|\w+)\])?)?\s*(?:;.*)?$')
label_pattern = re.compile(r'^\s*([a-zA-Z]+):\s*(?:;.*)?$')
lines = [line.strip() for line in assembly.splitlines(keepends=False)]
labels = {}
offset = 0
for line in lines:
match = assembly_pattern.match(line)
if match is not None:
if match.group(2) is not None:
offset += 2
elif match.group(1) is not None:
offset += 1
continue
match = label_pattern.match(line)
if match is not None:
labels[match.group(1)] = offset
result = []
for i, line in enumerate(lines):
if not line:
continue
match = assembly_pattern.match(line)
if not match:
if label_pattern.match(line):
continue
raise ValueError(f'Invalid assembly at line {i}')
mnemonic = match.group(1)
if not mnemonic:
continue
if mnemonic not in OPCODES:
raise ValueError(f'Unknown opcode "{mnemonic}"')
result.append(OPCODES[mnemonic])
arg = match.group(2)
if arg is not None:
arg = arg[1:-1]
if arg.isalpha():
try:
arg = labels[arg]
except KeyError:
raise ValueError(f'Invalid label {arg}')
else:
arg = int(arg, base=0)
if arg > 256 or arg < -255:
raise ValueError(f'Invalid arg value {arg}')
result.append(arg)
return bytes(result)
def serialize_program(program: bytes, output_path: str):
Path(output_path).unlink(missing_ok=True)
conn = sqlite3.connect(output_path)
conn.execute('CREATE TABLE program(program BLOB);')
conn.execute(f'INSERT INTO program VALUES(?);', (program,))
conn.commit()
conn.close()
if __name__ == '__main__':
parser = ArgumentParser()
parser.add_argument('input')
parser.add_argument('output')
args = parser.parse_args()
program = assemble(Path(args.input).read_text())
serialize_program(program, args.output)