-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathassembler.py
More file actions
135 lines (116 loc) · 4.37 KB
/
assembler.py
File metadata and controls
135 lines (116 loc) · 4.37 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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import argparse
from lark import Lark, Transformer, Token, Tree, UnexpectedInput
def get_cli_args() -> argparse.Namespace:
argparser = argparse.ArgumentParser()
argparser.add_argument(
"a_file",
metavar="LMC_SOURCE",
help="LMC source file",
)
return argparser.parse_args()
grammar = r"""
?start: _instruction? (_NL _instruction)* _NL*
_NL: NEWLINE
COMMENT: _COMMENT_STR (_NL _COMMENT_STR)*
_COMMENT_STR: /\s*(#|\/\/)[^\n]*/+
_instruction: labeldef? op
?op: ("hlt"i | "cob"i) -> hlt
| "add"i (label | addr) -> add
| "sub"i (label | addr) -> sub
| "sta"i (label | addr) -> sta
| "lda"i (label | addr) -> lda
| "bra"i (label | addr) -> bra
| "brz"i (label | addr) -> brz
| "brp"i (label | addr) -> brp
| "inp"i -> inp
| "out"i -> out
| "dat"i value -> dat
| "org"i addr -> org
?addr: ADDR
?value: [SIGNED_NUMBER]
?label: LABEL
labeldef: LABEL -> labeldef
LABEL: /[a-zA-Z_][_a-zA-Z0-9]*/
ADDR: /0/ | /[1-9][0-9]/
%import common.NEWLINE
%import common.WS_INLINE
%import common.SH_COMMENT
%import common.SIGNED_NUMBER
%ignore WS_INLINE
%ignore COMMENT
"""
class Assembler(Transformer):
labels: dict[str, int]
location: int
memory: list[tuple[int, int | tuple[int, int | str]]]
def __init__(self):
super().__init__()
self._parser = Lark(grammar, parser="lalr", transformer=self)
self.reset()
def reset(self) -> None:
self.labels = {}
self.memory = []
self.location = 0
def start(self, lines: list[Tree]) -> list[tuple[int, int]]:
for tree in lines:
match tree:
case Tree("labeldef", [addr]):
self.labels[addr.value.lower()] = self.location
case Tree("org", [addr]):
self.location = addr.value
case Tree("dat", [item]):
value = 0 if item is None else item.value
if not -999 <= value <= 999:
raise UnexpectedInput(
f"[red]Value {value} out of range [-999 ... +999] "
f"at line {item.line}, column {item.column}[/]"
)
self.memory.append((self.location, value))
self.location += 1
case Tree("hlt", _):
self.memory.append((self.location, 0))
self.location += 1
case Tree("add", [addr]):
self.memory.append((self.location, (100, addr.value)))
self.location += 1
case Tree("sub", [addr]):
self.memory.append((self.location, (200, addr.value)))
self.location += 1
case Tree("sta", [addr]):
self.memory.append((self.location, (300, addr.value)))
self.location += 1
case Tree("lda", [addr]):
self.memory.append((self.location, (500, addr.value)))
self.location += 1
case Tree("bra", [addr]):
self.memory.append((self.location, (600, addr.value)))
self.location += 1
case Tree("brz", [addr]):
self.memory.append((self.location, (700, addr.value)))
self.location += 1
case Tree("brp", [addr]):
self.memory.append((self.location, (800, addr.value)))
self.location += 1
case Tree("inp", _):
self.memory.append((self.location, 901))
self.location += 1
case Tree("out", _):
self.memory.append((self.location, 902))
self.location += 1
for index, item in enumerate(self.memory):
if isinstance(item[1], tuple):
location, (opcode, label) = item
addr = opcode + int(self.labels.get(str(label).lower(), label))
self.memory[index] = (location, addr)
return self.memory
@staticmethod
def ADDR(token: Token) -> Token:
token.value = int(token.value)
return token
@staticmethod
def SIGNED_NUMBER(token: Token) -> Token:
token.value = int(token.value)
return token
def run(self, src: str):
self.reset()
return self._parser.parse(src)