Path: blob/master/Tools/autotest/logger_metadata/enum_parse.py
9589 views
#!/usr/bin/env python312'''3AP_FLAKE8_CLEAN4'''56import argparse7import os8import re9import sys1011topdir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../../')12topdir = os.path.realpath(topdir)131415class EnumDocco(object):1617vehicle_map = {18"Rover": "Rover",19"Sub": "ArduSub",20"Copter": "ArduCopter",21"Plane": "ArduPlane",22"Tracker": "AntennaTracker",23"Blimp": "Blimp",24}2526def __init__(self, vehicle):27self.vehicle = vehicle28self.enumerations = []2930class EnumEntry(object):31def __init__(self, name, value, comment):32self.name = name33self.value = value34self.comment = comment3536def match_enum_line(self, line):37# attempts to extract name, value and comment from line.3839# Match: " FRED, // optional comment"40m = re.match(r"\s*([A-Z0-9_a-z]+)\s*,? *(?://[/>]* *(.*) *)?$", line)41if m is not None:42return (m.group(1), None, m.group(2))4344# Match: " FRED, /* optional comment */"45m = re.match(r"\s*([A-Z0-9_a-z]+)\s*,? *(?:/[*] *(.*) *[*]/ *)?$", line)46if m is not None:47return (m.group(1), None, m.group(2))4849# Match: " FRED = 17, // optional comment"50m = re.match(r"\s*([A-Z0-9_a-z]+)\s*=\s*([-0-9]+)\s*,?(?:\s*//[/<]*\s*(.*) *)?$",51line)52if m is not None:53return (m.group(1), m.group(2), m.group(3))5455# Match: " FRED = 17, // optional comment"56m = re.match(r"\s*([A-Z0-9_a-z]+) *= *([-0-9]+) *,?(?: */* *(.*) *)? *[*]/ *$",57line)58if m is not None:59return (m.group(1), m.group(2), m.group(3))6061# Match: " FRED = 1U<<0, // optional comment"62m = re.match(r"\s*([A-Z0-9_a-z]+) *= *[(]?1U? *[<][<] *(\d+)(?:, *// *(.*) *)?",63line)64if m is not None:65return (m.group(1), 1 << int(m.group(2)), m.group(3))6667# Match: " FRED = 0xabc, // optional comment"68m = re.match(r"\s*([A-Z0-9_a-z]+) *= *(?:0[xX]([0-9A-Fa-f]+))(?:, *// *(.*) *)?",69line)70if m is not None:71return (m.group(1), int(m.group(2), 16), m.group(3))7273'''start discarded matches - lines we understand but can't do anything74with:'''75# Match: " FRED = 17, // optional comment"76m = re.match(r"\s*([A-Z0-9_a-z]+) *= *(\w+) *,?(?: *// *(.*) *)?$",77line)78if m is not None:79return (None, None, None)80# Match: " FRED = FOO(17), // optional comment"81m = re.match(r"\s*([A-Z0-9_a-z]+) *= *(\w+) *\\( *(\w+) *\\) *,?(?: *// *(.*) *)?$",82line)83if m is not None:84return (None, None, None)8586# Match: " FRED = 1U<<0, // optional comment"87m = re.match(r"\s*([A-Z0-9_a-z]+) *= *[(]?3U? *[<][<] *(\d+)(?:, *// *(.*) *)?",88line)89if m is not None:90return (m.group(1), 1 << int(m.group(2)), m.group(3))9192# Match: "#define FRED 1 // optional comment"93m = re.match(r"#define\s*([A-Z0-9_a-z]+)\s+(-?\d+) *(// *(.*) *)?$", line)94if m is not None:95return (m.group(1), m.group(2), m.group(4))9697if m is None:98raise ValueError("Failed to match (%s)" % line)99100def enumerations_from_file(self, source_file):101def debug(x):102pass103# if source_file == "/home/pbarker/rc/ardupilot/libraries/AP_HAL/AnalogIn.h":104# debug = print105state_outside = "outside"106state_inside = "inside"107108state = state_outside109110enumerations = []111with open(source_file) as f:112enum_name = None113in_class = None114while True:115line = f.readline()116# debug(f"{state} line: {line}")117if line == "":118break119line = line.rstrip()120# print("state=%s line: %s" % (state, line))121# Skip single-line comments - unless they contain LoggerEnum tags122if re.match(r"\s*//.*", line) and "LoggerEnum" not in line:123continue124# Skip multi-line comments125if re.match(r"\s*/\*.*", line):126while "*/" not in line:127line = f.readline()128continue129if state == "outside":130if re.match("class .*;", line) is not None:131# forward-declaration of a class132continue133m = re.match(r"class *([:\w]+)", line)134if m is not None:135in_class = m.group(1)136continue137m = re.match(r"namespace *(\w+)", line)138if m is not None:139in_class = m.group(1)140continue141m = re.match(r".*enum\s*(class)? *([\w]+)\s*(?::.*_t)? *{(.*)};", line)142if m is not None:143# all one one line! Thanks!144enum_name = m.group(2)145debug("ol: %s: %s" % (source_file, enum_name))146entries_string = m.group(3)147entry_names = [x.strip() for x in entries_string.split(",")]148count = 0149entries = []150for entry in entry_names:151entries.append(EnumDocco.EnumEntry(enum_name, count, None))152count += 1153new_enumeration = EnumDocco.Enumeration(enum_name, entries)154enumerations.append(new_enumeration)155continue156157m = re.match(r".*enum\s*(class)? *([\w]+)\s*(?::.*_t)? *{", line)158if m is not None:159enum_name = m.group(2)160debug("%s: %s" % (source_file, enum_name))161entries = []162last_value = None163state = state_inside164skip_enumeration = False165continue166167# // @LoggerEnum: NAME - can be used around for #define sets168m = re.match(r".*@LoggerEnum: *([\w:]+)", line)169if m is not None:170enum_name = m.group(1)171debug("%s: %s" % (source_file, enum_name))172entries = []173last_value = None174state = state_inside175skip_enumeration = False176continue177178continue179if state == "inside":180if re.match(r"\s*enum.*$", line):181# Allow @LoggerEnum around Enum for name override182continue183if re.match(r"\s*$", line):184continue185if re.match(r"#if", line):186continue187if re.match(r"#endif", line):188continue189if re.match(r"#else", line):190continue191if re.match(r".*}\s*\w*(\s*=\s*[\w:]+)?;", line) or "@LoggerEnumEnd" in line:192# potential end of enumeration193if not skip_enumeration:194if enum_name is None:195raise Exception("WT??")196if in_class is not None:197enum_name = "::".join([in_class, enum_name])198new_enumeration = EnumDocco.Enumeration(enum_name, entries)199enumerations.append(new_enumeration)200# print("Got enum (%s)" % enum_name)201# for entry in new_enumeration.entries:202# print(" %s: %u (%s)" % (entry.name, entry.value, entry.comment))203state = state_outside204continue205(name, value, comment) = self.match_enum_line(line)206if name is None:207skip_enumeration = True208continue209debug(" name=(%s) value=(%s) comment=(%s)\n" % (name, value, comment))210if value is None:211if last_value is None:212value = 0213last_value = 0214else:215last_value += 1216value = last_value217else:218value = int(value)219last_value = value220# print("entry=%s value=%s comment=%s" % (name, value, comment))221entries.append(EnumDocco.EnumEntry(name, value, comment))222return enumerations223224class Enumeration(object):225226def __init__(self, name, entries):227# print("creating enum %s" % name)228self.name = name229self.entries = entries230231def __str__(self):232return f"EnumDocco.Enumeration: {self.name} [{len(self.entries)} entries]"233234def search_for_files(self, dirs_to_search):235_next = []236for _dir in dirs_to_search:237for entry in os.listdir(_dir):238if "AP_Scripting/lua" in _dir:239continue240if "modules" in _dir:241continue242if "examples" in _dir:243continue244filepath = os.path.join(_dir, entry)245if os.path.isdir(filepath):246_next.append(filepath)247continue248(name, extension) = os.path.splitext(filepath)249if extension not in [".cpp", ".h"]:250continue251if filepath.endswith("libraries/AP_HAL/utility/getopt_cpp.h"):252continue253# Failed to match ( IOEVENT_PWM = EVENT_MASK(1),)254if filepath.endswith("libraries/AP_IOMCU/iofirmware/iofirmware.cpp"):255continue256self.files.append(filepath)257if len(_next):258self.search_for_files(_next)259260def parse_files(self):261for _file in self.files:262self.enumerations.extend(self.enumerations_from_file(_file))263264def get_enumerations(self):265self.files = []266self.search_for_files([os.path.join(topdir, x) for x in [267self.vehicle_map[self.vehicle],268"libraries"]])269self.parse_files()270return self.enumerations271272def run(self):273self.get_enumerations()274275276if __name__ == '__main__':277parser = argparse.ArgumentParser(description="Parse parameters.")278parser.add_argument("-v", "--verbose", dest='verbose', action='store_true', default=False, help="show debugging output")279parser.add_argument("--vehicle", required=True, help="Vehicle type to generate for")280281args = parser.parse_args()282283s = EnumDocco(args.vehicle)284285if args.vehicle not in s.vehicle_map:286print("Invalid vehicle (choose from: %s)" % str(s.vehicle_map.keys()))287sys.exit(1)288289s.run()290291if args.verbose:292for e in s.enumerations:293print(e)294295296