CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
Ardupilot

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

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