From 46eedfceb55e98e9ae27020f2f51b227118ce9c7 Mon Sep 17 00:00:00 2001 From: "matthias@arch" Date: Mon, 5 Sep 2022 03:08:19 +0200 Subject: [PATCH] Added gen_enum_str --- gen_enum_str.py | 168 ++++++++++++++++++++++++++++++++++++++++++++++++ src/Makefile | 1 + 2 files changed, 169 insertions(+) create mode 100644 gen_enum_str.py diff --git a/gen_enum_str.py b/gen_enum_str.py new file mode 100644 index 0000000..e9332ce --- /dev/null +++ b/gen_enum_str.py @@ -0,0 +1,168 @@ +#!/bin/python3 +""" +A python script to generate to_string functions for all enumerations in a cpp file. +Copyright © 2022 Matthias Quintern. +This software comes with no warranty. +This software is licensed under the GPL3 +""" +from os import path, listdir, chdir +from sys import argv, exit +import re + + +filetypes = [".hpp", ".cpp", ".tpp"] + +def error(s): + print(s) + exit(1) + + +def getEnumStrF(name: str, enum: dict, spaces: int): + s = " " * spaces + s += f"const std::map<{name}, std::string> {name}ToStringMap" + " { // generated by gen_enum_str.py\n" + for name_, val in enum.items(): + s += " " * (spaces + 4) + s += "{ " + name_ + ", \"" + name_ + "\" },\n" + s += " " * spaces + "};\n" + s += " " * spaces + "const std::string& to_string(" + name + " v) { return " + f"{name}ToStringMap.at(v);" + " }; // generated by gen_enum_str.py\n\n" + return s + + +def process_file(inputfile: str, outputfile: str): + print("process_file:", inputfile) + if not path.isfile(inputfile): error("File does not exist:" + inputfile) + + with open(inputfile, "r") as file: + lines = file.readlines() + + # FIND ENUMS + enums_start = [] + enums_stop = [] + in_enum = False + for i in range(len(lines)): + if re.match(r" *enum [a-zA-Z0-9_-]+ {.*", lines[i]) and not "gen_enum_str.py" in lines[i]: + if in_enum: error("process_file: could not find end of enum starting at line " + str(enums_start.pop())) + in_enum = True + enums_start.append(i) + + if in_enum: + if re.match(r" *[^/*]?.*}.*", lines[i]): + enums_stop.append(i) + in_enum = False + if len(enums_start) != len(enums_stop): error("process_file: could not find end of enum starting at line " + str(enums_start.pop())) + + # create a single string with the enum + enums = [] + for i in range(len(enums_start)): + enums.append("") + j = enums_start[i] + while j <= enums_stop[i]: + enums[i] += lines[j] + j += 1 + + # get name and vars + enum_names = [] + enum_dicts = [] + spaces = [] + var = "[a-zA-Z0-9_-]+" + enumvar = "(" + var + r"(=*-?\d+)?)" + restring = "enum (" + var + "){(" + enumvar + ",)*" + enumvar + ",?}" + for i in range(len(enums)): + enums[i] = enums[i].replace("\n", "").replace("enum ", "enumü").replace(" ", "").replace("enumü", "enum ") + match = re.match(restring, enums[i]) + if not match: error("Invalid enum at line " + str(enums_start[i])) + + spaces.append(lines[enums_start[i]].find("e")) + enum_names.append(match.groups()[0]) + enum_val = 0 + enum = {} + for m in re.finditer(enumvar, enums[i][len("enum " + enum_names[i]):]): + if "=" in m.group(): + enum_val = int(m.group()[m.group().find("=")+1:]) + var_end = m.group().find("=") + else: + var_end = len(m.group()) + enum[m.group()[0:var_end]] = enum_val + enum_val += 1 + print("enum:", enum_names[i], enum) + enum_dicts.append(enum) + + for i in reversed(range(len(enum_names))): + lines.insert(enums_stop[i] + 1, getEnumStrF(enum_names[i], enum_dicts[i], spaces[i])) + lines[enums_start[i]] = lines[enums_start[i]].strip("\n") + " // processed by gen_enum_str.py\n" + + with open(outputfile, "w") as file: + file.writelines(lines) + + +def process_path(path_): + if path.isfile(path_) and path.splitext(path_)[-1] in filetypes: + process_file(path_, path_) + + elif path.isdir(path_): + for p in listdir(path_): + if path.isfile(p) and path.splitext(p)[-1] in filetypes: + process_file(p, p) + elif path.isdir(p): + chdir(p) + process_path(p) + chdir("..") + else: + print("Invalid path:", path_) + + + + +def print_help(): + """ +Synposis: get_enum_str.py ... ... +-o output: file to path. Only when 1 input file. +-h --help help +-r recurse: Recurse through the directory and process all cpp/hpp files. Implies -i +-i in place: Edit the file in place +""" + +def missing_arg(arg): + print("Missing argument for", arg) + exit(1) + +if __name__ == "__main__": + output_file = None + input_files = [] + in_place = False + recurse = False + i = 1 + while i in range(1, len(argv)): + if argv[i] == "--help" or argv[i] == "-h": + print_help() + exit(0) + if argv[i] == "-o": + if len(argv) > i + 1: output_file = argv[i+1] + else: missing_arg(argv[i]) + i += 1 + elif argv[i] == "-r": + recurse = True + elif argv[i] == "-i": + in_place = True + else: + input_files.append(argv[i]) + i += 1 + + if output_file is None and not in_place and not recurse: + error("Missing output path or -i") + if output_file and (recurse or in_place or len(input_files) > 1): + error("Error: -o does not work with -r, -i or multiple input paths.") + if len(input_files) == 0: + error("Missing input files.") + if in_place: + output_file = input_files[0] + + if (recurse): + for f in input_files: + process_path(f) + else: + process_file(input_files[0], output_file) + + + + diff --git a/src/Makefile b/src/Makefile index 7338aec..4b17100 100755 --- a/src/Makefile +++ b/src/Makefile @@ -45,6 +45,7 @@ $(OBJECT_DIRS): # install: $(LIB) $(HEADER_INST) install -D -m 755 $< $(DESTDIR)/usr/lib/$(subst ../,,$<) + install -D -m 755 ../gen_enum_str.py $(DESTDIR)/usr/bin/gz-gen-enum $(OBJECT_DIR)/%.stamp: %.hpp $(OBJECT_DIR) install -D -m 644 $< $(DESTDIR)/usr/include/gz-util/$<