334
|
1
|
|
2 import re
|
|
3 import pyyacc
|
382
|
4 from baselex import BaseLexer
|
384
|
5 from . import Token, CompilerError, SourceLocation, make_num
|
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):
|
384
|
40 val = make_num(val)
|
382
|
41 typ = bit_type(val)
|
|
42 return typ, val
|
334
|
43
|
|
44
|
|
45 class Parser:
|
341
|
46 def add_rule(self, prod, rhs, f):
|
|
47 """ Helper function to add a rule, why this is required? """
|
|
48 if prod == 'instruction':
|
|
49 def f_wrap(*args):
|
|
50 i = f(args)
|
381
|
51 if i:
|
|
52 self.emit(i)
|
341
|
53 else:
|
|
54 def f_wrap(*rhs):
|
|
55 return f(rhs)
|
|
56 self.g.add_production(prod, rhs, f_wrap)
|
|
57
|
|
58 def __init__(self, kws, instruction_rules, emit):
|
334
|
59 # Construct a parser given a grammar:
|
375
|
60 tokens2 = ['ID', 'NUMBER', ',', '[', ']', ':', '+', '-', '*', '=',
|
341
|
61 pyyacc.EPS, 'COMMENT', '{', '}',
|
345
|
62 pyyacc.EOF, 'val32', 'val16', 'val12', 'val8', 'val5', 'val3']
|
341
|
63 tokens2.extend(kws)
|
|
64 self.kws = kws
|
|
65 g = pyyacc.Grammar(tokens2)
|
|
66 self.g = g
|
340
|
67 # Global structure of assembly line:
|
334
|
68 g.add_production('asmline', ['asmline2'])
|
|
69 g.add_production('asmline', ['asmline2', 'COMMENT'])
|
|
70 g.add_production('asmline2', ['label', 'instruction'])
|
|
71 g.add_production('asmline2', ['instruction'])
|
|
72 g.add_production('asmline2', ['label'])
|
|
73 g.add_production('asmline2', [])
|
|
74 g.add_production('label', ['ID', ':'], self.p_label)
|
340
|
75
|
|
76 # Add instruction rules for the target in question:
|
|
77 for prod, rhs, f in instruction_rules:
|
341
|
78 self.add_rule(prod, rhs, f)
|
340
|
79
|
334
|
80 #g.add_production('instruction', [])
|
|
81 g.start_symbol = 'asmline'
|
341
|
82 self.emit = emit
|
|
83 self.p = g.generate_parser()
|
342
|
84 # print('length of table:', len(self.p.action_table))
|
334
|
85
|
|
86 # Parser handlers:
|
|
87
|
|
88 def p_label(self, lname, cn):
|
341
|
89 lab = Label(lname.val)
|
334
|
90 self.emit(lab)
|
|
91
|
341
|
92 def parse(self, lexer):
|
334
|
93 self.p.parse(lexer)
|
|
94
|
|
95
|
381
|
96 class BaseAssembler:
|
|
97 """ Assembler base class, inherited by assemblers specific for a target """
|
346
|
98 def __init__(self, target):
|
334
|
99 self.target = target
|
340
|
100 assert isinstance(target, Target)
|
381
|
101
|
|
102 def make_parser(self):
|
|
103 self.parser = Parser(self.target.asm_keywords, self.target.assembler_rules, self.emit)
|
382
|
104 self.lexer = AsmLexer(self.target.asm_keywords)
|
346
|
105
|
386
|
106 def prepare(self):
|
|
107 pass
|
|
108
|
346
|
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
|