1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
#!/usr/bin/env python
# Programme to convert all single-line or multi-line "/* ... */"
# comments to "//" form in C or C++ code. Input source code file is
# stdin, output file is stdout.
import sys
import re
# Restore default signal handling for SIGPIPE as suggested by
# http://www.velocityreviews.com/forums/t332662-ioerror-errno-32-broken-pipe.html
# so that stdout can be sent (unix-only) to a pipelined process that terminates
# early such as cmp.
import signal
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
ifsingleline = True
previous_continue = False
for line in sys.stdin.readlines():
start_comment = line.find("/*")
# FIXME?: the simple rules below to ignore all special strings
# in a line where the first instance is quoted obviously
# need generalization. However, second instances
# of special strings are unlikely in practice so we will go with
# this simple rule until it is proved something more is required.
# Ignore all "/*" instances on a line where the first one is
# quoted.
if start_comment >=0 and re.search(r'^[^"]*("[^"]*"[^"]*)*"[^"]*/\*[^"]*"', line):
start_comment = -1
start_special = line.find("//*")
# FIXME?
# Ignore all "//*" instances on a line where the first one is
# quoted.
if start_special >= 0 and re.search(r'^[^"]*("[^"]*"[^"]*)*"[^"]*//\*[^"]*"', line):
start_special = -1
# if start_special corresponds to start_comment, then ignore start_comment
# to deal with issue of recursive changes to an original line of the
# form "/******..."
if start_special >= 0 and start_special == start_comment -1:
start_comment = -1
end_comment = line.find("*/")
# FIXME?
# Ignore all "*/" instances on a line where the first one is
# quoted.
if end_comment >= 0 and re.search(r'^[^"]*("[^"]*"[^"]*)*"[^"]*\*/[^"]*"', line):
end_comment = -1
start_special = line.find("//")
# FIXME?
# Ignore all "//" instances on a line where the first one is
# quoted.
if start_special >= 0 and re.search(r'^[^"]*("[^"]*"[^"]*)*"[^"]*//[^"]*"', line):
start_special = -1
# If unquoted // before /* and not in the middle of a comment block,
# then ignore both /* and */ beyond //.
if ifsingleline and start_special >= 0 and start_special <= start_comment -1:
start_comment = -1
end_comment = -1
# Note trailing "\n" has not (yet) been removed from line so
# that the next to last character is at position len(line) - 3.
if end_comment >=0 and end_comment != len(line) - 3:
if ifsingleline and start_comment >=0:
# Skip most further processing for a line with embedded
# comments outside of multiline blocks of comments.
start_comment = -1
end_comment = -1
else:
sys.stderr.write(line)
raise RuntimeError, "Cannot interpret trailing character(s) after */ for this line"
# Note trailing "\n" has not (yet) been removed from line so
# that the next to last character is at position len(line) - 3.
# print "start_comment = ", start_comment
# print "end_comment = ", end_comment
# print "start_special = ", start_special
if ifsingleline:
if start_comment >=0 and start_comment < end_comment and end_comment == len(line) - 3:
# Single-line comment case.
# Strip trailing "\n" (Unix line endings, only) since that might
# cause trouble with stripping trailing white space below.
# This trailing "\n" will be added back at the end of this block.
line = re.sub(r'^(.*)\n$', "\\1", line)
# Convert single-line comment.
# \1 corresponds to zero or more leading characters before "/*".
# \3 corresponds to zero or more characters between "/*" and
# "*/".
# N.B. preserves indentation.
line = re.sub(r'^(.*)(/\*)(.*)\*/$', "\\1//\\3", line)
# strip trailing white space (if any).
line = line.rstrip()
# Add back (Unix-only) line ending.
line = line + "\n"
elif end_comment >=0:
sys.stderr.write(line)
raise RuntimeError, "Trailing */ for a line which is not a comment"
elif start_comment >=0:
# Convert first line of multiline comment.
# N.B. preserves indentation.
line = line.replace("/*", "//", 1)
ifsingleline = False
else:
# Case where dealing with multi-line comment.
if start_comment >=0:
sys.stderr.write(line)
raise RuntimeError, "/* in the middle of a comment block"
# strip trailing "\n" (Unix line endings, only) since that might
# cause trouble with stripping trailing white space below.
# This trailing "\n" will be added back at the end of this block.
line = re.sub(r'^(.*)\n$', "\\1", line)
if end_comment < 0:
# Convert multiline comment line that is not start line
# or end line of that comment.
# Replace " *" after zero or more blanks (the standard form
# produced by uncrustify) if it is there by "//".
# N.B. preserves indentation.
if re.search(r'^( *) \*', line):
line = re.sub(r'^( *) \*', "\\1//", line)
else:
# If standard indented form not found....
line = "//" + line
else:
# Convert last line of multiline comment.
# Try converting vacuous form (initial blanks + " */")
# to initial blanks (if any) + "//".
# This consumes the blank in " */" that is customary as
# the last line of a multi-line block.
# N.B. preserves indentation.
# \1 contains the initial blanks
if re.search(r'^( *) \*/$', line):
line = re.sub(r'^( *) \*/$', "\\1//", line)
# Try converting non-vacuous standard form produced by
# uncrustify (initial blanks + " *" + any string + "*/")
# to initial blanks + "//" + any string.
# N.B. preserves indentation.
# \1 contains the initial blanks
# \2 contains the "any" string preceding "*/".
elif re.search(r'^( *) \*(.*)\*/$', line):
line = re.sub(r'^( *) \*(.*)\*/$', "\\1//\\2", line)
# If all previous conversion attempts failed....
# N.B. Does not preserve indentation.
else:
line = re.sub(r'^(.*)\*/$', "//\\1", line)
# strip trailing white space (if any).
line = line.rstrip()
ifsingleline = True
# Add back (Unix-only) line ending.
line = line + "\n"
# If previous comment continuation exists, check whether it is
# valid, i.e., whether it is followed by another line consisting
# zero or more blanks followed by a comment.
if previous_continue:
if re.search(r'^ *//', line):
previous_continue = False
else:
sys.stderr.write(line_old)
sys.stderr.write(line)
raise RuntimeError, "Comment continuation not allowed unless next line is a comment"
# End with some special processing for all lines which previously
# or now include "//".
start_special = line.find("//")
if start_special >= 0:
# Special transforms to get rid of left-over "\*" and "*\"
# forms which have historically been used to frame
# multi-block comments by some PLplot developers.
# Replace leading "// \*" ==> "//"
line = re.sub(r'^// \\\*(.*)$', "//\\1", line)
# Remove "*\" from end of comment lines
line = re.sub(r'^//(.*)\*\\$', "//\\1", line)
# Convert long "horizontal line" comment forms to standard form.
line = re.sub(r'^// *[-*=/+_\\# ]{50,200}$', "//--------------------------------------------------------------------------", line)
# Look for trailing continuation after comment lines and
# complain if you find any.
start_special = line.rfind("\\")
# Note line has trailing "\n" so that the last character
# is at position len(line) - 2.
if start_special >=0 and start_special == len(line) -2:
# Strings are immutable so this is a copy not a reference.
line_old = line
previous_continue = True
sys.stdout.write(line)
if not ifsingleline:
sys.stderr.write(line)
raise RuntimeError, "Last line of file leaves unterminated comment block."
if previous_continue:
sys.stderr.write(line_old)
raise RuntimeError, "Last line of file is a continued comment."
sys.exit()
|