1 2# Copyright Aleksey Gurtovoy 2001-2004 3# 4# Distributed under the Boost Software License, Version 1.0. 5# (See accompanying file LICENSE_1_0.txt or copy at 6# http://www.boost.org/LICENSE_1_0.txt) 7# 8# See http://www.boost.org/libs/mpl for documentation. 9 10# $Id$ 11# $Date$ 12# $Revision$ 13 14import fileinput 15import os 16import re 17import string 18import sys 19 20if_else = lambda a,b,c:(a and [b] or [c])[0] 21max_len = 79 22ident = 4 23 24def nearest_ident_pos(text): 25 return (len(text)/ident) * ident 26 27def block_format(limits, text, first_sep=' ', sep=',', need_last_ident=1 ): 28 if sep == ',' and string.find( text, '<' ) != -1: 29 sep = '%s ' % sep 30 31 words = string.split( 32 string.join( string.split( text ), ' ' ) 33 , sep 34 ) 35 36 s = ' ' * limits[0] 37 max_len = limits[1] 38 return '%s\n%s' \ 39 % ( 40 reduce( 41 lambda t,w,max_len=max_len,s=s,sep=sep: 42 if_else(t[1] + len(w) < max_len 43 , ('%s%s%s'% (t[0],t[2],w), t[1]+len(w)+len(t[2]), sep) 44 , ('%s\n%s%s%s'% (t[0],s,sep,w), len(s)+len(w)+len(sep), sep) 45 ) 46 , words 47 , (s,len(s)+len(first_sep),first_sep) 48 )[0] 49 , if_else(need_last_ident,s,'') 50 ) 51 52def handle_args( match ): 53 if re.compile('^\s*(typedef|struct|static)\s+.*?$').match(match.group(0)): 54 return match.group(0) 55 56 return '%s'\ 57 % block_format( 58 (nearest_ident_pos(match.group(1)),max_len) 59 , match.group(3) 60 , match.group(2) 61 , ',' 62 , 0 63 ) 64 65 66def handle_inline_args(match): 67 if len(match.group(0)) < max_len: 68 return match.group(0) 69 70 if match.group(9) == None: 71 return '%s%s<\n%s>\n'\ 72 % ( 73 match.group(1) 74 , match.group(3) 75 , block_format( 76 (nearest_ident_pos(match.group(1))+ident,max_len) 77 , match.group(4) 78 ) 79 ) 80 81 return '%s%s<\n%s>\n%s%s'\ 82 % ( 83 match.group(1) 84 , match.group(3) 85 , block_format( 86 (nearest_ident_pos(match.group(1))+ident,max_len-len(match.group(9))) 87 , match.group(4) 88 ) 89 , string.replace(match.group(1),',',' ') 90 , match.group(9) 91 ) 92 93def handle_simple_list(match): 94 if match.group(1) == 'template': 95 return match.group(0) 96 97 single_arg = re.compile('^\s*(\w|\d)+\s*$').match(match.group(2)) 98 return if_else(single_arg,'%s<%s>','%s< %s >') %\ 99 ( 100 match.group(1) 101 , string.join(string.split(match.group(2)), '') 102 ) 103 104def handle_static(match): 105 if len(match.group(0)) < max_len: 106 return match.group(0) 107 108 (first_sep,sep) = if_else(string.find(match.group(0),'+') == -1, (' ',' '),(' ','+')) 109 return '%s%s\n%s%s' %\ 110 ( 111 match.group(1) 112 , string.join(string.split(match.group(2)), ' ') 113 , block_format( 114 (nearest_ident_pos(match.group(1))+ident,max_len) 115 , match.group(4) 116 , first_sep 117 , sep 118 ) 119 , match.group(5) 120 ) 121 122def handle_typedefs(match): 123 if string.count(match.group(2), ';') == 1: 124 return match.group(0) 125 126 join_sep = ';\n%s' % match.group(1) 127 128 return '%s%s\n' \ 129 % ( 130 match.group(1) 131 , string.join(map(string.strip, string.split(match.group(2), ';')), join_sep) 132 ) 133 134def fix_angle_brackets( match ): 135 return ' '.join( ''.join( match.group(1).split( ' ' ) ) ) + match.group(3) 136 137 138class pretty: 139 def __init__(self, name): 140 self.output = open(name, "w") 141 self.prev_line = '' 142 143 self.re_copyright_start = re.compile( r'^// Copyright .*$' ) 144 self.re_copyright_end = re.compile( r'^// See .* for documentation.$' ) 145 self.reading_copyright = 0 146 self.copyright = None 147 148 self.re_header_name_comment = re.compile( 149 r'^\s*//\s+\$[I]d:\s+(.*?%s\.hpp)\s+[^$]+[$]$' 150 % os.path.splitext( name )[0] 151 ) 152 153 self.header_was_written = 0 154 155 self.re_junk = re.compile(r'^\s*(#|//[^/]|////).*$') 156 self.re_c_comment_start = re.compile(r'^\s*/\*.*') 157 self.re_c_comment_end = re.compile(r'^.*\*/\s*$') 158 self.inside_c_comment = 0 159 160 self.re_empty_line = re.compile(r'^\s*$') 161 self.re_comma = re.compile(r'(\S+)\s*,\s*') 162 self.re_assign = re.compile(r'(\S+[^<|^!|^>])\s*(=+)\s*(\S+)') 163 self.re_marked_empty_comment = re.compile(r'^\s*//\s*$') 164 self.re_typedef = re.compile(r'^\s+typedef\s+.*?;$') 165 self.re_nsl = re.compile(r'^(\s+typedef\s+.*?;|\s*(private|public):\s*|\s*{\s*|\s*(\w|\d|,)+\s*)$') 166 self.re_templ_decl = re.compile(r'^(\s*template\s*<\s*.*?|\s*(private|public):\s*)$') 167 self.re_type_const = re.compile(r'(const)\s+((unsigned|signed)?(bool|char|short|int|long))') 168 #self.re_templ_args = re.compile(r'^(\s*)(, | {2})((.*::.*?,?)+)\s*$') 169 self.re_templ_args = re.compile(r'^(\s*)(, | {2})((\s*(\w+)(\s+|::)\w+\s*.*?,?)+)\s*$') 170 self.re_inline_templ_args = re.compile( 171 r'^(\s+(,|:\s+)?|struct\s+)(\w+)\s*<((\s*(typename\s+)?\w+\s*(=\s*.*|<(\s*\w+\s*,?)+>\s*)?,?)+)\s*>\s+((struct|class).*?)?$' 172 ) 173 174 self.re_simple_list = re.compile(r'(\w+)\s*<((\w|,| |-)+)>') 175 self.re_static_const = re.compile(r'(\s*)((BOOST_STATIC_CONSTANT\(\s*\w+,\s*|enum\s*\w*\s*{\s*)value\s*=)(.*?)([}|\)];)$') 176 self.re_typedefs = re.compile(r'(\s*)((\s*typedef\s*.*?;)+)\s*$') 177 self.re_fix_angle_brackets = re.compile( r'(>(\s*>)+)(,|\n$)' ) 178 self.re_closing_curly_brace = re.compile(r'^(}|struct\s+\w+);\s*$') 179 self.re_namespace_scope_templ = re.compile(r'^template\s*<\s*$') 180 self.re_namespace = re.compile(r'^\n?namespace\s+\w+\s*{\s*\n?$') 181 182 def process(self, line): 183 if self.reading_copyright: 184 if not self.re_copyright_end.match( line ): 185 self.copyright += line 186 return 187 188 self.reading_copyright = 0 189 190 if not self.header_was_written and self.re_copyright_start.match( line ): 191 self.copyright = line 192 self.reading_copyright = 1 193 return 194 195 # searching for header line 196 if not self.header_was_written: 197 if self.re_header_name_comment.match( line ): 198 self.header_was_written = 1 199 match = self.re_header_name_comment.match( line ) 200 self.output.write( \ 201 '\n%s\n' \ 202 '// *Preprocessed* version of the main "%s" header\n' \ 203 '// -- DO NOT modify by hand!\n\n' \ 204 % ( self.copyright, match.group(1) ) 205 ) 206 return 207 208 # skipping preprocessor directives, comments, etc. 209 if self.re_junk.match(line): 210 return 211 212 if self.inside_c_comment or self.re_c_comment_start.match(line): 213 self.inside_c_comment = not self.re_c_comment_end.match(line) 214 return 215 216 # restoring some empty lines 217 if self.re_templ_decl.match(line) and self.re_typedef.match(self.prev_line) \ 218 or not self.re_empty_line.match(line) and self.re_closing_curly_brace.match(self.prev_line) \ 219 or not self.re_empty_line.match(self.prev_line) \ 220 and ( self.re_namespace_scope_templ.match(line) \ 221 or self.re_namespace.match(line) and not self.re_namespace.match(self.prev_line) \ 222 ): 223 line = '\n%s' % line 224 225 # removing excessive empty lines 226 if self.re_empty_line.match(line): 227 if self.re_empty_line.match(self.prev_line) or not self.header_was_written: 228 return 229 230 # skip empty line after typedef 231 if self.re_nsl.match(self.prev_line): 232 return 233 234 # formatting 235 line = self.re_comma.sub( r'\1, ', line ) 236 line = self.re_assign.sub( r'\1 \2 \3', line ) 237 line = self.re_marked_empty_comment.sub( r'\n', line ) 238 line = self.re_type_const.sub( r'\2 \1', line ) 239 line = self.re_templ_args.sub( handle_args, line ) 240 line = self.re_inline_templ_args.sub( handle_inline_args, line ) 241 line = self.re_simple_list.sub( handle_simple_list, line) 242 line = self.re_static_const.sub( handle_static, line ) 243 line = self.re_typedefs.sub( handle_typedefs, line ) 244 line = self.re_fix_angle_brackets.sub( fix_angle_brackets, line ) 245 246 # write the output 247 self.output.write(line) 248 self.prev_line = line 249 250def main( src, dest ): 251 p = pretty( os.path.basename( dest ) ) 252 for line in fileinput.input( src ): 253 p.process(line) 254 255if __name__ == '__main__': 256 main( sys.argv[1], sys.argv[2] ) 257