diff --git a/scripts/process-text-files.py b/scripts/process-text-files.py index 6cf4b20..12e9b32 100644 --- a/scripts/process-text-files.py +++ b/scripts/process-text-files.py @@ -78,6 +78,84 @@ class Cmd(jwutils.Cmd): return data return r + @staticmethod + def _indent_cpp_macros(data, src, target, context=None): + + def __ctx(context, key, default): + if key in context: + return context[key] + return default + + def format_line(context, indent, directive, rhs): + if indent < 0: + indent = 0 + spaces = __ctx(context, 'spaces', 2) + indent_macro = __ctx(context, 'indent-macro', 0) + indent_def = __ctx(context, 'indent-definiton', 40) + indent_comment = __ctx(context, 'indent-comment', 70) + rhs = rhs.strip() + directive = directive.strip() + r = '#' + ' ' * indent * spaces + directive + if len(rhs) == 0: + return r + '\n' + r += ' ' + if indent_macro > 1: + r = r.ljust(indent_macro - 1) + if not directive in [ 'define' ]: + return (r + rhs).rstrip() + '\n' + #slog(NOTICE, "dissecting", rhs) + m = re.match("^\s*(\w+(\([^)]*\))*)(\s+(.*))*", rhs) + if m is None: + raise Exception("invalid rhs", rhs) + macro = m.group(1) + def_and_comment = m.group(4) + #slog(NOTICE, "macro=>{}<, def+comment=>{}<".format(macro, def_and_comment)) + r += macro + if def_and_comment is None: + return r.rstrip() + '\n' + r += ' ' + if indent_def > 1: + r = r.ljust(indent_def - 1) + parts = re.split("(//|/\*)", def_and_comment) + if len(parts) <= 1: + return r + def_and_comment + '\n' + if len(parts) == 2: + raise Exception("Failed to dissect definition + comment", def_and_comment) + return (r + parts[0].strip()).ljust(indent_comment) + parts[1].strip() + ' ' + ''.join(parts[2:]).strip() + '\n' + + skip_outer = True + if context is not None: + if 'skip-outer' in context: + skip_outer = context['skip-outer'] + r = '' + lnum = 0 + level = -1 if context['skip-outer'] else 0 + lines = data.splitlines() + for line in iter(lines): + lnum += 1 + m = re.match("^\s*#\s*(\w+)(\W*)(.*)", line) + if m is None: + r += line + '\n' + continue + directive = m.group(1) + rhs = (m.group(2) + m.group(3)).strip() + #slog(NOTICE, "{}: directive=>{}<, rhs=>{}<".format(level, directive, rhs)) + if directive is None or rhs is None: + raise Exception("Syntax error in line", lnum, ":", line) + if directive in [ "if", "ifdef", "ifndef" ]: + r += format_line(context, level, directive, rhs) + level += 1 + continue + if directive in [ "else" ]: + r += format_line(context, level - 1, directive, rhs) + continue + if directive in [ "endif" ]: + level -= 1 + r += format_line(context, level, directive, rhs) + continue + r += format_line(context, level, directive, rhs) + return r + @staticmethod def _cleanup_spaces(data, src, target, context=None): lines = data.splitlines() @@ -346,6 +424,34 @@ class CmdIndentMakefileEquals(Cmd): if self._replace_in_file(path, replacements, func=self._indent_pattern, context=context): slog(NOTICE, "+ aligned equals :", path) +class CmdIndentCppMacros(Cmd): + + def __init__(self): + super(CmdIndentCppMacros, self).__init__("indent-cpp-macros", "Indent and beautify C/C++ preprocessor macros") + + def add_parser(self, parsers): + p = super(CmdIndentCppMacros, self).add_parser(parsers) + p.add_argument('-w', "--spaces", help="Number of spaces per indentation level", type=int, default=2) + p.add_argument('-s', "--skip-outer", help="Skip the outmost macro (read multiple-inclusion guards)", action='store_true', default=True) + p.add_argument('-S', "--skip-outer-name-regex", help="Regex for file names that --skip-outer should be applied to", default="\.h$|\.H$|\.hpp$") + return p + + def process(self, args, files): + slog(NOTICE, "Beautifying", len(files), "C++ files:") + context = dict() + context["spaces"] = args.spaces + replacements = {"blah": "blub"} # just a dummy to use _replace_in_file, TODO: obviate the need + for dir, name in files: + path = dir + '/' + name + if self._replace_in_file(path, replacements, func=self._cleanup_spaces): + slog(NOTICE, "+ purged spaces :", path) + if args.skip_outer and re.search(args.skip_outer_name_regex, path): + context["skip-outer"] = True + else: + context["skip-outer"] = False + if self._replace_in_file(path, replacements, func=self._indent_cpp_macros, context=context): + slog(NOTICE, "+ indented C++ macros :", path) + class CmdCleanupSpaces(Cmd): def __init__(self):