Skip to content

Commit 1ba31b8

Browse files
assemble module: move functions into Assembler class
1 parent 8d45fda commit 1ba31b8

File tree

3 files changed

+75
-69
lines changed

3 files changed

+75
-69
lines changed

esp32_ulp/__main__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import sys
22

3-
from .assemble import assemble
3+
from .assemble import Assembler
44

55

66
def main(fn):
77
with open(fn) as f:
88
lines = f.readlines()
99

10-
symbols, code = assemble(lines)
10+
assembler = Assembler()
11+
symbols, code = assembler.assemble(lines)
1112

1213
print('Symbols:')
1314
for name, value in sorted(symbols.items()):

esp32_ulp/assemble.py

Lines changed: 57 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,60 +4,62 @@
44

55
from . import opcodes
66

7+
class Assembler:
8+
def __init__(self):
9+
self.symbols = {}
10+
self.code = []
11+
self.addr = 0
712

8-
def parse_line(line):
9-
"""
10-
parse one line of assembler into label, opcode, args
11-
12-
a line looks like (label, opcode, args, comment are all optional):
13-
14-
label: opcode arg1, arg2, ... # rest-of-line comment
15-
"""
16-
line = line.split('#', 1)[0].rstrip()
17-
if not line:
18-
return
19-
has_label = line[0] not in '\t '
20-
if has_label:
21-
label_line = line.split(None, 1)
22-
if len(label_line) == 2:
23-
label, line = label_line
24-
else: # 1
25-
label, line = label_line[0], None
26-
label = label.rstrip(':')
27-
else:
28-
label, line = None, line.lstrip()
29-
if line is None:
30-
opcode, args = None, ()
31-
else:
32-
opcode_args = line.split(None, 1)
33-
if len(opcode_args) == 2:
34-
opcode, args = opcode_args
35-
args = tuple(arg.strip() for arg in args.split(','))
36-
else: # 1
37-
opcode, args = opcode_args[0], ()
38-
return label, opcode, args
39-
40-
41-
def parse(lines):
42-
parsed = [parse_line(line) for line in lines]
43-
return [p for p in parsed if p is not None]
44-
45-
46-
def assemble(lines):
47-
symbols = {}
48-
code = []
49-
addr = 0
50-
for label, opcode, args in parse(lines):
51-
if label is not None:
52-
if label in symbols:
53-
raise Exception('label %s is already defined.' % label)
54-
symbols[label] = addr
55-
if opcode is not None:
56-
func = getattr(opcodes, 'i_' + opcode, None)
57-
if func is None:
58-
raise Exception('Unknown opcode: %s' % opcode)
59-
instruction = func(*args)
60-
code.append(instruction)
61-
addr += 1
62-
return symbols, code
13+
def parse_line(self, line):
14+
"""
15+
parse one line of assembler into label, opcode, args
16+
17+
a line looks like (label, opcode, args, comment are all optional):
18+
19+
label: opcode arg1, arg2, ... # rest-of-line comment
20+
"""
21+
line = line.split('#', 1)[0].rstrip()
22+
if not line:
23+
return
24+
has_label = line[0] not in '\t '
25+
if has_label:
26+
label_line = line.split(None, 1)
27+
if len(label_line) == 2:
28+
label, line = label_line
29+
else: # 1
30+
label, line = label_line[0], None
31+
label = label.rstrip(':')
32+
else:
33+
label, line = None, line.lstrip()
34+
if line is None:
35+
opcode, args = None, ()
36+
else:
37+
opcode_args = line.split(None, 1)
38+
if len(opcode_args) == 2:
39+
opcode, args = opcode_args
40+
args = tuple(arg.strip() for arg in args.split(','))
41+
else: # 1
42+
opcode, args = opcode_args[0], ()
43+
return label, opcode, args
44+
45+
46+
def parse(self, lines):
47+
parsed = [self.parse_line(line) for line in lines]
48+
return [p for p in parsed if p is not None]
49+
50+
51+
def assemble(self, lines):
52+
for label, opcode, args in self.parse(lines):
53+
if label is not None:
54+
if label in self.symbols:
55+
raise Exception('label %s is already defined.' % label)
56+
self.symbols[label] = self.addr
57+
if opcode is not None:
58+
func = getattr(opcodes, 'i_' + opcode, None)
59+
if func is None:
60+
raise Exception('Unknown opcode: %s' % opcode)
61+
instruction = func(*args)
62+
self.code.append(instruction)
63+
self.addr += 1
64+
return self.symbols, self.code
6365

tests/assemble.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from esp32_ulp.assemble import parse_line, parse, assemble
1+
from esp32_ulp.assemble import Assembler
22

33
src = """\
44
# line 1
@@ -14,28 +14,31 @@
1414

1515

1616
def test_parse_line():
17+
a = Assembler()
1718
lines = src.splitlines()
1819
# note: line number = index + 1
19-
assert parse_line(lines[0]) == None
20-
assert parse_line(lines[1]) == None
21-
assert parse_line(lines[2]) == ('start', 'wait', ('42', ))
22-
assert parse_line(lines[3]) == None
23-
assert parse_line(lines[4]) == None
24-
assert parse_line(lines[5]) == (None, 'ld', ('r0', 'r1', '0', ))
25-
assert parse_line(lines[6]) == (None, 'st', ('r0', 'r1', '0', ))
26-
assert parse_line(lines[7]) == (None, 'halt', ())
27-
assert parse_line(lines[8]) == ('end', None, ())
20+
assert a.parse_line(lines[0]) == None
21+
assert a.parse_line(lines[1]) == None
22+
assert a.parse_line(lines[2]) == ('start', 'wait', ('42', ))
23+
assert a.parse_line(lines[3]) == None
24+
assert a.parse_line(lines[4]) == None
25+
assert a.parse_line(lines[5]) == (None, 'ld', ('r0', 'r1', '0', ))
26+
assert a.parse_line(lines[6]) == (None, 'st', ('r0', 'r1', '0', ))
27+
assert a.parse_line(lines[7]) == (None, 'halt', ())
28+
assert a.parse_line(lines[8]) == ('end', None, ())
2829

2930

3031
def test_parse():
32+
a = Assembler()
3133
lines = src.splitlines()
32-
result = parse(lines)
34+
result = a.parse(lines)
3335
assert None not in result
3436

3537

3638
def test_assemble():
39+
a = Assembler()
3740
lines = src.splitlines()
38-
symbols, code = assemble(lines)
41+
symbols, code = a.assemble(lines)
3942
assert {'start', 'end'} <= set(symbols)
4043
assert symbols['start'] == 0
4144
assert symbols['end'] == 4

0 commit comments

Comments
 (0)