Skip to content

Commit ac1de99

Browse files
committed
add simple include file processing
This will not work on the ESP32 for large files, due to limited memory. But this is only the first step. Next we'll add a database for storing defines from include files.
1 parent 3e8c0d5 commit ac1de99

File tree

4 files changed

+74
-32
lines changed

4 files changed

+74
-32
lines changed

esp32_ulp/preprocess.py

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,29 +32,31 @@ def WRITE_RTC_FIELD(rtc_reg, low_bit, value):
3232

3333
class Preprocessor:
3434
def __init__(self):
35+
self._defines_db = None
3536
self._defines = {}
3637

38+
def parse_define_line(self, line):
39+
line = line.strip()
40+
if not line.startswith("#define"):
41+
# skip lines not containing #define
42+
return {}
43+
line = line[8:].strip() # remove #define
44+
parts = line.split(None, 1)
45+
if len(parts) != 2:
46+
# skip defines without value
47+
return {}
48+
identifier, value = parts
49+
tmp = identifier.split('(', 1)
50+
if len(tmp) == 2:
51+
# skip parameterised defines (macros)
52+
return {}
53+
value = "".join(nocomment.remove_comments(value)).strip()
54+
return {identifier: value}
55+
3756
def parse_defines(self, content):
38-
result = {}
3957
for line in content.splitlines():
40-
line = line.strip()
41-
if not line.startswith("#define"):
42-
# skip lines not containing #define
43-
continue
44-
line = line[8:].strip() # remove #define
45-
parts = line.split(None, 1)
46-
if len(parts) != 2:
47-
# skip defines without value
48-
continue
49-
identifier, value = parts
50-
tmp = identifier.split('(', 1)
51-
if len(tmp) == 2:
52-
# skip parameterised defines (macros)
53-
continue
54-
value = "".join(nocomment.remove_comments(value)).strip()
55-
result[identifier] = value
56-
self._defines = result
57-
return result
58+
self._defines.update(self.parse_define_line(line))
59+
return self._defines
5860

5961
def expand_defines(self, line):
6062
found = True
@@ -70,6 +72,16 @@ def expand_defines(self, line):
7072

7173
return line
7274

75+
def process_include_file(self, filename):
76+
defines = self._defines
77+
78+
with open(filename, 'r') as f:
79+
for line in f:
80+
result = self.parse_defines(line)
81+
defines.update(result)
82+
83+
return defines
84+
7385
def expand_rtc_macros(self, line):
7486
clean_line = line.strip()
7587
if not clean_line:

tests/fixtures/incl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#define CONST1 42
2+
#define MACRO(x,y) x+y
3+
#define MULTI_LINE abc \
4+
xyz
5+
#define CONST2 99

tests/fixtures/incl2.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#define CONST2 123
2+
#define CONST3 777

tests/preprocess.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,19 @@ def replace_defines_should_return_remove_comments():
2727
def test_parse_defines():
2828
p = Preprocessor()
2929

30-
assert p.parse_defines("") == {}
31-
assert p.parse_defines("// comment") == {}
32-
assert p.parse_defines(" // comment") == {}
33-
assert p.parse_defines(" /* comment */") == {}
34-
assert p.parse_defines(" /* comment */ #define A 42") == {} # #define must be the first thing on a line
35-
assert p.parse_defines("#define a 1") == {"a": "1"}
36-
assert p.parse_defines(" #define a 1") == {"a": "1"}
37-
assert p.parse_defines("#define a 1 2") == {"a": "1 2"}
38-
assert p.parse_defines("#define f(a,b) 1") == {} # macros not supported
39-
assert p.parse_defines("#define f(a, b) 1") == {} # macros not supported
40-
assert p.parse_defines("#define f (a,b) 1") == {"f": "(a,b) 1"} # f is not a macro
41-
assert p.parse_defines("#define f (a, b) 1") == {"f": "(a, b) 1"} # f is not a macro
42-
assert p.parse_defines("#define RTC_ADDR 0x12345 // start of range") == {"RTC_ADDR": "0x12345"}
30+
assert p.parse_define_line("") == {}
31+
assert p.parse_define_line("// comment") == {}
32+
assert p.parse_define_line(" // comment") == {}
33+
assert p.parse_define_line(" /* comment */") == {}
34+
assert p.parse_define_line(" /* comment */ #define A 42") == {} # #define must be the first thing on a line
35+
assert p.parse_define_line("#define a 1") == {"a": "1"}
36+
assert p.parse_define_line(" #define a 1") == {"a": "1"}
37+
assert p.parse_define_line("#define a 1 2") == {"a": "1 2"}
38+
assert p.parse_define_line("#define f(a,b) 1") == {} # macros not supported
39+
assert p.parse_define_line("#define f(a, b) 1") == {} # macros not supported
40+
assert p.parse_define_line("#define f (a,b) 1") == {"f": "(a,b) 1"} # f is not a macro
41+
assert p.parse_define_line("#define f (a, b) 1") == {"f": "(a, b) 1"} # f is not a macro
42+
assert p.parse_define_line("#define RTC_ADDR 0x12345 // start of range") == {"RTC_ADDR": "0x12345"}
4343

4444

4545
@test
@@ -181,6 +181,29 @@ def test_expand_rtc_macros():
181181
assert p.expand_rtc_macros("READ_RTC_FIELD(1, 2)") == "\treg_rd 1, 2 + 1 - 1, 2"
182182

183183

184+
@test
185+
def test_process_include_file():
186+
p = Preprocessor()
187+
188+
defines = p.process_include_file('fixtures/incl.h')
189+
assert defines['CONST1'] == '42'
190+
assert defines['CONST2'] == '99'
191+
assert defines.get('MULTI_LINE', None) == 'abc \\' # correct. line continuations not supported
192+
assert 'MACRO' not in defines
193+
194+
195+
@test
196+
def test_process_include_file_with_multiple_files():
197+
p = Preprocessor()
198+
199+
defines = p.process_include_file('fixtures/incl.h')
200+
defines = p.process_include_file('fixtures/incl2.h')
201+
202+
assert defines['CONST1'] == '42', "constant from incl.h"
203+
assert defines['CONST2'] == '123', "constant overridden by incl2.h"
204+
assert defines['CONST3'] == '777', "constant from incl2.h"
205+
206+
184207
if __name__ == '__main__':
185208
# run all methods marked with @test
186209
for t in tests:

0 commit comments

Comments
 (0)