334
|
1
|
|
2 import re
|
|
3 import pyyacc
|
382
|
4 from baselex import BaseLexer
|
334
|
5 from . import Token, CompilerError, SourceLocation
|
342
|
6 from .target import Target, Label
|
334
|
7
|
340
|
8
|
341
|
9 def bit_type(value):
|
345
|
10 assert value < (2**32)
|
341
|
11 assert value >= 0
|
|
12 t = 'val32'
|
345
|
13 for n in [16, 12, 8, 5, 3]:
|
341
|
14 if value < (2**n):
|
|
15 t = 'val{}'.format(n)
|
|
16 return t
|
|
17
|
334
|
18
|
382
|
19 class AsmLexer(BaseLexer):
|
|
20 def __init__(self, kws):
|
|
21 tok_spec = [
|
|
22 ('REAL', r'\d+\.\d+', lambda typ, val: (typ, float(val))),
|
|
23 ('HEXNUMBER', r'0x[\da-fA-F]+', self.handle_number),
|
|
24 ('NUMBER', r'\d+', self.handle_number),
|
|
25 ('ID', r'[A-Za-z][A-Za-z\d_]*', self.handle_id),
|
|
26 ('SKIP', r'[ \t]', None),
|
|
27 ('LEESTEKEN', r':=|[\.,=:\-+*\[\]/\(\)]|>=|<=|<>|>|<|}|{', lambda typ, val: (val, val)),
|
|
28 ('STRING', r"'.*?'", lambda typ, val: (typ, val[1:-1])),
|
|
29 ('COMMENT', r";.*", None)
|
|
30 ]
|
|
31 super().__init__(tok_spec)
|
|
32 self.kws = kws
|
334
|
33
|
382
|
34 def handle_id(self, typ, val):
|
|
35 if val.lower() in self.kws:
|
|
36 typ = val.lower()
|
|
37 return (typ, val)
|
334
|
38
|
382
|
39 def handle_number(self, typ, val):
|
|
40 if val.startswith('0x'):
|
|
41 val = int(val[2:], 16)
|
|
42 else:
|
|
43 val = int(val)
|
|
44 typ = bit_type(val)
|
|
45 return typ, val
|
334
|
46
|
|
47
|
|
48 class Parser:
|
341
|
49 def add_rule(self, prod, rhs, f):
|
|
50 """ Helper function to add a rule, why this is required? """
|
|
51 if prod == 'instruction':
|
|
52 def f_wrap(*args):
|
|
53 i = f(args)
|
381
|
54 if i:
|
|
55 self.emit(i)
|
341
|
56 else:
|
|
57 def f_wrap(*rhs):
|
|
58 return f(rhs)
|
|
59 self.g.add_production(prod, rhs, f_wrap)
|
|
60
|
|
61 def __init__(self, kws, instruction_rules, emit):
|
334
|
62 # Construct a parser given a grammar:
|
375
|
63 tokens2 = ['ID', 'NUMBER', ',', '[', ']', ':', '+', '-', '*', '=',
|
341
|
64 pyyacc.EPS, 'COMMENT', '{', '}',
|
345
|
65 pyyacc.EOF, 'val32', 'val16', 'val12', 'val8', 'val5', 'val3']
|
341
|
66 tokens2.extend(kws)
|
|
67 self.kws = kws
|
|
68 g = pyyacc.Grammar(tokens2)
|
|
69 self.g = g
|
340
|
70 # Global structure of assembly line:
|
334
|
71 g.add_production('asmline', ['asmline2'])
|
|
72 g.add_production('asmline', ['asmline2', 'COMMENT'])
|
|
73 g.add_production('asmline2', ['label', 'instruction'])
|
|
74 g.add_production('asmline2', ['instruction'])
|
|
75 g.add_production('asmline2', ['label'])
|
|
76 g.add_production('asmline2', [])
|
|
77 g.add_production('label', ['ID', ':'], self.p_label)
|
340
|
78
|
|
79 # Add instruction rules for the target in question:
|
|
80 for prod, rhs, f in instruction_rules:
|
341
|
81 self.add_rule(prod, rhs, f)
|
340
|
82
|
334
|
83 #g.add_production('instruction', [])
|
|
84 g.start_symbol = 'asmline'
|
341
|
85 self.emit = emit
|
|
86 self.p = g.generate_parser()
|
342
|
87 # print('length of table:', len(self.p.action_table))
|
334
|
88
|
|
89 # Parser handlers:
|
|
90
|
|
91 def p_label(self, lname, cn):
|
341
|
92 lab = Label(lname.val)
|
334
|
93 self.emit(lab)
|
|
94
|
341
|
95 def parse(self, lexer):
|
334
|
96 self.p.parse(lexer)
|
|
97
|
|
98
|
381
|
99 class BaseAssembler:
|
|
100 """ Assembler base class, inherited by assemblers specific for a target """
|
346
|
101 def __init__(self, target):
|
334
|
102 self.target = target
|
340
|
103 assert isinstance(target, Target)
|
381
|
104
|
|
105 def make_parser(self):
|
|
106 self.parser = Parser(self.target.asm_keywords, self.target.assembler_rules, self.emit)
|
382
|
107 self.lexer = AsmLexer(self.target.asm_keywords)
|
346
|
108
|
|
109 def emit(self, *args):
|
|
110 self.stream.emit(*args)
|
334
|
111
|
|
112 # Top level interface:
|
|
113 def parse_line(self, line):
|
340
|
114 """ Parse line into assembly instructions """
|
382
|
115 self.lexer.feed(line)
|
|
116 self.parser.parse(self.lexer)
|
334
|
117
|
346
|
118 def assemble(self, asmsrc, stream):
|
334
|
119 """ Assemble this source snippet """
|
381
|
120 if type(asmsrc) is str:
|
|
121 pass
|
|
122 elif hasattr(asmsrc, 'read'):
|
334
|
123 asmsrc2 = asmsrc.read()
|
|
124 asmsrc.close()
|
|
125 asmsrc = asmsrc2
|
340
|
126 # TODO: use generic newline??
|
|
127 # TODO: the bothersome newline ...
|
346
|
128 self.stream = stream
|
334
|
129 for line in asmsrc.split('\n'):
|
340
|
130 self.parse_line(line)
|
334
|
131
|
381
|
132 def flush(self):
|
|
133 pass
|