forked from micropython/micropython-esp32-ulp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpreprocess.py
163 lines (131 loc) · 5.22 KB
/
preprocess.py
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#
# This file is part of the micropython-esp32-ulp project,
# https://github.com/micropython/micropython-esp32-ulp
#
# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file.
# SPDX-License-Identifier: MIT
from . import nocomment
from .util import split_tokens
from .definesdb import DefinesDB
class RTC_Macros:
@staticmethod
def READ_RTC_REG(rtc_reg, low_bit, bit_width):
return '\treg_rd ' + ', '.join((
rtc_reg,
'%s + %s - 1' % (low_bit, bit_width),
low_bit
))
@staticmethod
def WRITE_RTC_REG(rtc_reg, low_bit, bit_width, value):
return '\treg_wr ' + ', '.join((
rtc_reg,
'%s + %s - 1' % (low_bit, bit_width),
low_bit,
value
))
@staticmethod
def READ_RTC_FIELD(rtc_reg, low_bit):
return RTC_Macros.READ_RTC_REG(rtc_reg, low_bit, 1)
@staticmethod
def WRITE_RTC_FIELD(rtc_reg, low_bit, value):
return RTC_Macros.WRITE_RTC_REG(rtc_reg, low_bit, 1, value + ' & 1')
class Preprocessor:
def __init__(self):
self._defines_db = None
self._defines = {}
def parse_define_line(self, line):
line = line.strip()
if not line.startswith("#define"):
# skip lines not containing #define
return {}
line = line[8:].strip() # remove #define
parts = line.split(None, 1)
if len(parts) != 2:
# skip defines without value
return {}
identifier, value = parts
tmp = identifier.split('(', 1)
if len(tmp) == 2:
# skip parameterised defines (macros)
return {}
value = "".join(nocomment.remove_comments(value)).strip()
return {identifier: value}
def parse_defines(self, content):
for line in content.splitlines():
self._defines.update(self.parse_define_line(line))
return self._defines
def expand_defines(self, line):
found = True
while found: # do as many passed as needed, until nothing was replaced anymore
found = False
tokens = split_tokens(line)
line = ""
for t in tokens:
lu = self._defines.get(t, t)
if lu == t and self._defines_db:
lu = self._defines_db.get(t, t)
if lu == t and t == 'BIT':
# Special hack: BIT(..) translates to a 32-bit mask where only the specified bit is set.
# But the reg_wr and reg_rd opcodes expect actual bit numbers for argument 2 and 3.
# While the real READ_RTC_*/WRITE_RTC_* macros take in the output of BIT(x), they
# ultimately convert these back (via helper macros) to the bit number (x). And since this
# preprocessor does not (aim to) implement "proper" macro-processing, we can simply
# short-circuit this round-trip via macros and replace "BIT" with nothing so that
# "BIT(x)" gets mapped to "(x)".
continue
if lu != t:
found = True
line += lu
return line
def process_include_file(self, filename):
with self.open_db() as db:
with open(filename, 'r') as f:
for line in f:
result = self.parse_define_line(line)
db.update(result)
return db
def expand_rtc_macros(self, line):
clean_line = line.strip()
if not clean_line:
return line
macro = clean_line.split('(', 1)
if len(macro) != 2:
return line
macro_name, macro_args = macro
macro_fn = getattr(RTC_Macros, macro_name, None)
if macro_fn is None:
return line
macro_args, _ = macro_args.rsplit(')', 1) # trim away right bracket. safe as comments already stripped
macro_args = macro_args.split(',') # not safe when args contain ',' but we should not have those
macro_args = [x.strip() for x in macro_args]
return macro_fn(*macro_args)
def use_db(self, defines_db):
self._defines_db = defines_db
def open_db(self):
class ctx:
def __init__(self, db):
self._db = db
def __enter__(self):
# not opening DefinesDB - it opens itself when needed
return self._db
def __exit__(self, type, value, traceback):
if isinstance(self._db, DefinesDB):
self._db.close()
if self._defines_db:
return ctx(self._defines_db)
return ctx(self._defines)
def preprocess(self, content):
self.parse_defines(content)
with self.open_db():
lines = nocomment.remove_comments(content)
result = []
for line in lines:
line = self.expand_defines(line)
line = self.expand_rtc_macros(line)
result.append(line)
result = "\n".join(result)
return result
def preprocess(content, use_defines_db=True):
preprocessor = Preprocessor()
preprocessor.use_db(DefinesDB())
return preprocessor.preprocess(content)