Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ardupilot
GitHub Repository: Ardupilot/ardupilot
Path: blob/master/Tools/autotest/logger_metadata/enum_parse.py
9589 views
1
#!/usr/bin/env python3
2
3
'''
4
AP_FLAKE8_CLEAN
5
'''
6
7
import argparse
8
import os
9
import re
10
import sys
11
12
topdir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../../')
13
topdir = os.path.realpath(topdir)
14
15
16
class EnumDocco(object):
17
18
vehicle_map = {
19
"Rover": "Rover",
20
"Sub": "ArduSub",
21
"Copter": "ArduCopter",
22
"Plane": "ArduPlane",
23
"Tracker": "AntennaTracker",
24
"Blimp": "Blimp",
25
}
26
27
def __init__(self, vehicle):
28
self.vehicle = vehicle
29
self.enumerations = []
30
31
class EnumEntry(object):
32
def __init__(self, name, value, comment):
33
self.name = name
34
self.value = value
35
self.comment = comment
36
37
def match_enum_line(self, line):
38
# attempts to extract name, value and comment from line.
39
40
# Match: " FRED, // optional comment"
41
m = re.match(r"\s*([A-Z0-9_a-z]+)\s*,? *(?://[/>]* *(.*) *)?$", line)
42
if m is not None:
43
return (m.group(1), None, m.group(2))
44
45
# Match: " FRED, /* optional comment */"
46
m = re.match(r"\s*([A-Z0-9_a-z]+)\s*,? *(?:/[*] *(.*) *[*]/ *)?$", line)
47
if m is not None:
48
return (m.group(1), None, m.group(2))
49
50
# Match: " FRED = 17, // optional comment"
51
m = re.match(r"\s*([A-Z0-9_a-z]+)\s*=\s*([-0-9]+)\s*,?(?:\s*//[/<]*\s*(.*) *)?$",
52
line)
53
if m is not None:
54
return (m.group(1), m.group(2), m.group(3))
55
56
# Match: " FRED = 17, // optional comment"
57
m = re.match(r"\s*([A-Z0-9_a-z]+) *= *([-0-9]+) *,?(?: */* *(.*) *)? *[*]/ *$",
58
line)
59
if m is not None:
60
return (m.group(1), m.group(2), m.group(3))
61
62
# Match: " FRED = 1U<<0, // optional comment"
63
m = re.match(r"\s*([A-Z0-9_a-z]+) *= *[(]?1U? *[<][<] *(\d+)(?:, *// *(.*) *)?",
64
line)
65
if m is not None:
66
return (m.group(1), 1 << int(m.group(2)), m.group(3))
67
68
# Match: " FRED = 0xabc, // optional comment"
69
m = re.match(r"\s*([A-Z0-9_a-z]+) *= *(?:0[xX]([0-9A-Fa-f]+))(?:, *// *(.*) *)?",
70
line)
71
if m is not None:
72
return (m.group(1), int(m.group(2), 16), m.group(3))
73
74
'''start discarded matches - lines we understand but can't do anything
75
with:'''
76
# Match: " FRED = 17, // optional comment"
77
m = re.match(r"\s*([A-Z0-9_a-z]+) *= *(\w+) *,?(?: *// *(.*) *)?$",
78
line)
79
if m is not None:
80
return (None, None, None)
81
# Match: " FRED = FOO(17), // optional comment"
82
m = re.match(r"\s*([A-Z0-9_a-z]+) *= *(\w+) *\\( *(\w+) *\\) *,?(?: *// *(.*) *)?$",
83
line)
84
if m is not None:
85
return (None, None, None)
86
87
# Match: " FRED = 1U<<0, // optional comment"
88
m = re.match(r"\s*([A-Z0-9_a-z]+) *= *[(]?3U? *[<][<] *(\d+)(?:, *// *(.*) *)?",
89
line)
90
if m is not None:
91
return (m.group(1), 1 << int(m.group(2)), m.group(3))
92
93
# Match: "#define FRED 1 // optional comment"
94
m = re.match(r"#define\s*([A-Z0-9_a-z]+)\s+(-?\d+) *(// *(.*) *)?$", line)
95
if m is not None:
96
return (m.group(1), m.group(2), m.group(4))
97
98
if m is None:
99
raise ValueError("Failed to match (%s)" % line)
100
101
def enumerations_from_file(self, source_file):
102
def debug(x):
103
pass
104
# if source_file == "/home/pbarker/rc/ardupilot/libraries/AP_HAL/AnalogIn.h":
105
# debug = print
106
state_outside = "outside"
107
state_inside = "inside"
108
109
state = state_outside
110
111
enumerations = []
112
with open(source_file) as f:
113
enum_name = None
114
in_class = None
115
while True:
116
line = f.readline()
117
# debug(f"{state} line: {line}")
118
if line == "":
119
break
120
line = line.rstrip()
121
# print("state=%s line: %s" % (state, line))
122
# Skip single-line comments - unless they contain LoggerEnum tags
123
if re.match(r"\s*//.*", line) and "LoggerEnum" not in line:
124
continue
125
# Skip multi-line comments
126
if re.match(r"\s*/\*.*", line):
127
while "*/" not in line:
128
line = f.readline()
129
continue
130
if state == "outside":
131
if re.match("class .*;", line) is not None:
132
# forward-declaration of a class
133
continue
134
m = re.match(r"class *([:\w]+)", line)
135
if m is not None:
136
in_class = m.group(1)
137
continue
138
m = re.match(r"namespace *(\w+)", line)
139
if m is not None:
140
in_class = m.group(1)
141
continue
142
m = re.match(r".*enum\s*(class)? *([\w]+)\s*(?::.*_t)? *{(.*)};", line)
143
if m is not None:
144
# all one one line! Thanks!
145
enum_name = m.group(2)
146
debug("ol: %s: %s" % (source_file, enum_name))
147
entries_string = m.group(3)
148
entry_names = [x.strip() for x in entries_string.split(",")]
149
count = 0
150
entries = []
151
for entry in entry_names:
152
entries.append(EnumDocco.EnumEntry(enum_name, count, None))
153
count += 1
154
new_enumeration = EnumDocco.Enumeration(enum_name, entries)
155
enumerations.append(new_enumeration)
156
continue
157
158
m = re.match(r".*enum\s*(class)? *([\w]+)\s*(?::.*_t)? *{", line)
159
if m is not None:
160
enum_name = m.group(2)
161
debug("%s: %s" % (source_file, enum_name))
162
entries = []
163
last_value = None
164
state = state_inside
165
skip_enumeration = False
166
continue
167
168
# // @LoggerEnum: NAME - can be used around for #define sets
169
m = re.match(r".*@LoggerEnum: *([\w:]+)", line)
170
if m is not None:
171
enum_name = m.group(1)
172
debug("%s: %s" % (source_file, enum_name))
173
entries = []
174
last_value = None
175
state = state_inside
176
skip_enumeration = False
177
continue
178
179
continue
180
if state == "inside":
181
if re.match(r"\s*enum.*$", line):
182
# Allow @LoggerEnum around Enum for name override
183
continue
184
if re.match(r"\s*$", line):
185
continue
186
if re.match(r"#if", line):
187
continue
188
if re.match(r"#endif", line):
189
continue
190
if re.match(r"#else", line):
191
continue
192
if re.match(r".*}\s*\w*(\s*=\s*[\w:]+)?;", line) or "@LoggerEnumEnd" in line:
193
# potential end of enumeration
194
if not skip_enumeration:
195
if enum_name is None:
196
raise Exception("WT??")
197
if in_class is not None:
198
enum_name = "::".join([in_class, enum_name])
199
new_enumeration = EnumDocco.Enumeration(enum_name, entries)
200
enumerations.append(new_enumeration)
201
# print("Got enum (%s)" % enum_name)
202
# for entry in new_enumeration.entries:
203
# print(" %s: %u (%s)" % (entry.name, entry.value, entry.comment))
204
state = state_outside
205
continue
206
(name, value, comment) = self.match_enum_line(line)
207
if name is None:
208
skip_enumeration = True
209
continue
210
debug(" name=(%s) value=(%s) comment=(%s)\n" % (name, value, comment))
211
if value is None:
212
if last_value is None:
213
value = 0
214
last_value = 0
215
else:
216
last_value += 1
217
value = last_value
218
else:
219
value = int(value)
220
last_value = value
221
# print("entry=%s value=%s comment=%s" % (name, value, comment))
222
entries.append(EnumDocco.EnumEntry(name, value, comment))
223
return enumerations
224
225
class Enumeration(object):
226
227
def __init__(self, name, entries):
228
# print("creating enum %s" % name)
229
self.name = name
230
self.entries = entries
231
232
def __str__(self):
233
return f"EnumDocco.Enumeration: {self.name} [{len(self.entries)} entries]"
234
235
def search_for_files(self, dirs_to_search):
236
_next = []
237
for _dir in dirs_to_search:
238
for entry in os.listdir(_dir):
239
if "AP_Scripting/lua" in _dir:
240
continue
241
if "modules" in _dir:
242
continue
243
if "examples" in _dir:
244
continue
245
filepath = os.path.join(_dir, entry)
246
if os.path.isdir(filepath):
247
_next.append(filepath)
248
continue
249
(name, extension) = os.path.splitext(filepath)
250
if extension not in [".cpp", ".h"]:
251
continue
252
if filepath.endswith("libraries/AP_HAL/utility/getopt_cpp.h"):
253
continue
254
# Failed to match ( IOEVENT_PWM = EVENT_MASK(1),)
255
if filepath.endswith("libraries/AP_IOMCU/iofirmware/iofirmware.cpp"):
256
continue
257
self.files.append(filepath)
258
if len(_next):
259
self.search_for_files(_next)
260
261
def parse_files(self):
262
for _file in self.files:
263
self.enumerations.extend(self.enumerations_from_file(_file))
264
265
def get_enumerations(self):
266
self.files = []
267
self.search_for_files([os.path.join(topdir, x) for x in [
268
self.vehicle_map[self.vehicle],
269
"libraries"]])
270
self.parse_files()
271
return self.enumerations
272
273
def run(self):
274
self.get_enumerations()
275
276
277
if __name__ == '__main__':
278
parser = argparse.ArgumentParser(description="Parse parameters.")
279
parser.add_argument("-v", "--verbose", dest='verbose', action='store_true', default=False, help="show debugging output")
280
parser.add_argument("--vehicle", required=True, help="Vehicle type to generate for")
281
282
args = parser.parse_args()
283
284
s = EnumDocco(args.vehicle)
285
286
if args.vehicle not in s.vehicle_map:
287
print("Invalid vehicle (choose from: %s)" % str(s.vehicle_map.keys()))
288
sys.exit(1)
289
290
s.run()
291
292
if args.verbose:
293
for e in s.enumerations:
294
print(e)
295
296