-
Notifications
You must be signed in to change notification settings - Fork 1
/
cpputils.py
124 lines (116 loc) · 4.61 KB
/
cpputils.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
#!/usr/bin/python3
"""
Process files generated by the C preprocessor
"""
import os
import logging
import re
import sys
logging.basicConfig(level=logging.INFO,
format='%(levelname)-8s [%(filename)s:%(lineno)d] %(message)s')
logger = logging
class CppFile():
"""
GCC testsuites use a lot of macros to generate variant test functions.
We want to extract the formatted source code from each of those variants
"""
attribute_pattern = re.compile(r'__attribute__\(\([^\)]*\)\)')
PARAM_PAT = r'\([a-zA-Z0-9_ ,*()[\]]*\)'
NAME_PAT = r'\w+'
# Replace some overly complicated parameter types used in the gcc test suite.
# These tend to confuse our identification of function names and return types
FUNC_DEF_SUBS = (
('sizeof (int8_t)', '8'),
('sizeof (uint8_t)', '8'),
('sizeof (int16_t)', '16'),
('sizeof (uint16_t)', '16'),
('sizeof (int32_t)', '32'),
('sizeof (uint32_t)', '32'),
('sizeof (int64_t)', '64'),
('sizeof (uint64_t)', '64'),
('sizeof (_Float16)', '16'),
('sizeof (float)', '32'),
('sizeof (double)', '64'),
('(*restrict a)', '*a'),
('(1024 / 8)', '32'),
('(1024 / 16)', '64'),
('(1024 / 32)', '32'),
('(1024 / 64)', '16'),
)
logger = logging.getLogger('CppFile')
if not logger.hasHandlers():
handler = logging.StreamHandler()
formatter = logging.Formatter('%(levelname)-8s [%(filename)s:%(lineno)d] %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.WARNING)
name_param_pattern = re.compile(fr'({NAME_PAT})\s*({PARAM_PAT})')
main_pattern = re.compile(r'^main\s+(.*)')
@classmethod
def get_function_signature(cls, line:str):
"""
split a function definition into return type, function name, and parameter list
"""
# get rid of any attributes first
line = re.sub(cls.attribute_pattern, '', line)
# remove any sizeof evaluations
for sub in CppFile.FUNC_DEF_SUBS:
(old, new) = sub
if old in line:
cls.logger.debug("Replacing %s with %s", old, new)
line = line.replace(old, new)
cls.logger.debug("matching %s", line)
# match for function name and parameter list
m = re.search(cls.name_param_pattern, line)
if not m:
return None
name = m.group(1)
params = m.group(2)
# remove the name and parameter list from the line
# and assign everything else to the return type
ret_type = re.sub(cls.name_param_pattern, '', line).strip(' ')
return (ret_type, name, params)
def __init__(self, file_name):
self.functions = {}
self.logger.debug("Entering CppFile:__init__(%s)", file_name)
if not os.path.exists(file_name):
self.logger.error("Unable to locate %s", file_name)
sys.exit()
with open(file_name, encoding='utf-8') as source_file:
line = source_file.readline()
while line:
l = line.rstrip()
fun_body = []
if l is None or l.startswith("#"):
line = source_file.readline()
continue
func_tuple = self.get_function_signature(l)
if func_tuple:
fun_return_type = func_tuple[0]
fun_name = func_tuple[1]
fun_sig = func_tuple[2]
line = source_file.readline()
fun_body = [f"{fun_return_type} {fun_name}{fun_sig}",]
fun_found = True
elif re.search(CppFile.main_pattern, l):
self.logger.info("Found a main routine")
line = source_file.readline()
fun_body = ['int main()',]
fun_found = True
else:
fun_found = False
if fun_found:
while line:
if line is None:
#end of function and end of file
break
l = line.rstrip()
if not l or not l.strip():
# end of function
line = source_file.readline()
break
fun_body.append(l)
line = source_file.readline()
self.functions[fun_name] = "\n".join(fun_body)
else:
line = source_file.readline()