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