Skip to content

Instantly share code, notes, and snippets.

@pansila
Created September 11, 2018 02:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pansila/b48d0e72e907ef692bf15fa60e8aa232 to your computer and use it in GitHub Desktop.
Save pansila/b48d0e72e907ef692bf15fa60e8aa232 to your computer and use it in GitHub Desktop.
Add comments to mark the start and stop position of C macro directives, including ifdef, else, endif, etc., to make them more legible
#!/bin/python
import sys, os
import argparse
class macro_directive(object):
def __init__(self, name, comment):
self.name = name[1:] if name[0] == "#" else name
self.comment = comment
self.path = True
self.last_seen = 0
class macro_commenter(object):
lines = 0
def __init__(self, skip_lines):
self.namestack = []
self.skip_lines = skip_lines
def get_commenter(self, directive):
try:
return getattr(self, "comment_" + directive.name)
except:
return None
def comment_ifdef(self, fp, directive, line):
directive.path = True
directive.last_seen = self.lines
self.namestack.append([directive])
fp.write(line)
def comment_ifndef(self, fp, directive, line):
directive.path = False
directive.last_seen = self.lines
self.namestack.append([directive])
fp.write(line)
def comment_else(self, fp, directive, line):
line = line.rstrip()
last_stack = self.namestack[-1]
last_directive = last_stack[-1]
if directive.comment == None and self.should_comment(last_directive):
comments = [self.left_delimter(el.path) + el.comment for el in last_stack]
comment = " && ".join(comments)
fp.write(line + " /* " + comment + " */\n")
else: # don't comment if there was one
fp.write(line + "\n")
last_directive.last_seen = self.lines
del directive
def comment_endif(self, fp, directive, line):
line = line.rstrip()
last_stack = self.namestack.pop()
# always add the comment for endif
if directive.comment == None:
comments = [el.comment for el in last_stack]
comment = ", ".join(comments)
fp.write(line + " /* " + comment + " */\n")
else:
fp.write(line + "\n")
del last_stack
del directive
def comment_elif(self, fp, directive, line):
line = line.rstrip()
last_stack = self.namestack[-1]
last_directive = last_stack[-1]
directive.path = None
directive.last_seen = self.lines
if self.should_comment(last_directive):
comments = [self.left_delimter(el.path) + el.comment for el in last_stack]
comment = ", ".join(comments)
fp.write(line + " /* " + comment + " */\n")
else:
fp.write(line + "\n")
last_stack.append(directive)
def comment_if(self, fp, directive, line):
directive.path = None
directive.last_seen = self.lines
self.namestack.append([directive])
fp.write(line)
def comment_defined(self, fp, directive, line):
pass
def should_comment(self, directive):
return self.lines - directive.last_seen > self.skip_lines
@staticmethod
def left_delimter(path):
if path == None:
return "?"
if path:
return "!"
else:
return ""
@classmethod
def count_lines(cls):
cls.lines += 1
class macro_parser(object):
def __init__(self, fp, in_place, skip_lines):
self.origfile = fp
self.in_place = in_place
self.tempfile = fp+".tmp"
self.commenter = macro_commenter(skip_lines)
def process(self, fp, directive, line):
if directive is None:
fp.write(line)
return
commenter = self.commenter.get_commenter(directive)
if commenter is None:
fp.write(line)
else:
commenter(fp, directive, line)
def parse(self, line):
directive = None
line = line.lstrip()
if line.startswith("#"):
parts = line.split()
if parts[0] == "#":
parts.pop(0)
parts[0] = "#" + parts[0]
name = parts[0]
comment = parts[1] if len(parts) > 1 else None
directive = macro_directive(name, comment)
return directive
elif line.startswith("/*"):
idx = line.find("*/")
if idx > 0:
line = line[idx+2:]
return self.parse(line)
return directive
def run(self):
with open(self.origfile) as origfp, open(self.tempfile, "w") as tempfp:
for line in origfp:
self.commenter.count_lines()
directive = self.parse(line)
if directive is None:
tempfp.write(line)
continue
self.process(tempfp, directive, line)
if self.in_place == 1:
os.remove(self.origfile)
os.rename(self.tempfile, self.origfile)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--in-place", type=int, default=0, help="whether to modify the file in place")
parser.add_argument("--skip-line", type=int, default=5, help="least lines to comment between two macros")
parser.add_argument("file", type=str, help="need a file path to proceed")
args = parser.parse_args()
macro_parser = macro_parser(args.file, args.in_place, args.skip_line)
macro_parser.run()
@pansila
Copy link
Author

pansila commented Sep 11, 2018

Usage

$ python mcomment.py test.h

Example

Before

#define MACRO_0

#ifdef MACRO_1
...
#else
...
#endif

#ifdef MACRO_2
...
#elif MACRO_3
...
#else
//#endif
/*#endif*/
#endif

After

#define MACRO_0

#ifdef MACRO_1
...
#else // !MACRO_1
...
#endif // MACRO_1

#ifdef MACRO_2
...
#elif MACRO_3
...
#else // !MACRO_2 && !MACRO_3
//#endif
/*#endif*/
#endif // MACRO_2, MACRO_3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment