#!/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)