3 # fixcc -- nitpick lily's c++ code
6 # * maintainable rules: regexp's using whitespace (?x) and match names
8 # * trailing * vs function definition
9 # * check lexer, parser
10 # * rewrite in elisp, add to cc-mode
30 # space before parenthesis open
31 ('([^\( \]])[ \t]*\(', '\\1 ('),
36 # delete inline double spaces
38 # delete space after parenthesis open
40 # delete space before parenthesis close
42 # delete spaces after prefix
43 ('(--|\+\+)[ \t]*([\w\)])', '\\1\\2'),
44 # delete spaces before postfix
45 ('([\w\)\]])[ \t]*(--|\+\+)', '\\1\\2'),
46 # delete space after parenthesis close
47 #('\)[ \t]*([^\w])', ')\\1'),
48 # delete space around operator
49 # ('([\w\(\)\]])([ \t]*)(::|\.)([ \t]*)([\w\(\)])', '\\1\\3\\5'),
50 ('([\w\(\)\]])([ \t]*)(\.)([ \t]*)([\w\(\)])', '\\1\\3\\5'),
51 # delete space after operator
52 ('(::)([ \t]*)([\w\(\)])', '\\1\\3'),
53 # delete superflous space around operator
54 ('([\w\(\)\]])([ \t]+)(&&|\|\||<=|>=|!=|\|=|==|\+=|-=|\*=|/=|\?|<|>|\+|-|=|/|:|&|\||\*)([ \t]+)([\w\(\)])', '\\1 \\3 \\5'),
55 # space around operator1
56 ('([\w\)\]]) *(&&|\|\||<=|>=|!=|\|=|==|\+=|-=|\*=|/=|\?|<|>|=|/|:|&|\||\*) *([\w\(])', '\\1 \\2 \\3'),
57 # space around operator2
58 ('([\w\)\]]) *(&&|\|\||<=|>=|!=|\|=|==|\+=|-=|\*=|/=|\?|<|>|=|/|:|&|\||\*) ([^\w\s])', '\\1 \\2 \\3'),
59 # space around operator3
60 ('([^\w\s]) (&&|\|\||<=|>=|!=|\|=|==|\+=|-=|\*=|/=|\?|<|>|=|/|:|&|\||\*) *([\w\(])', '\\1 \\2 \\3'),
61 # space around +/-; exponent
62 ('([\w\)\]])(\+|-)([_A-Za-z\(])', '\\1 \\2 \\3'),
63 ('([_\dA-Za-df-z\)\]])(\+|-)([\w\(])', '\\1 \\2 \\3'),
65 (' (::|&&|\|\||<=|>=|!=|\|=|==|\+=|-=|\*=|/=|\?|<|>|\+|-|=|/|:|&XXX|\||\*XXX)[ \t]*\n([ \t]*)', '\n\\2\\1 '),
66 #breaks function definitions
67 #to#(' (::|&&|\|\||<=|>=|!=|\|=|==|\+=|-=|\*=|/=|<|>|\+|-|=|/|&|\||\*)[ \t]*\n([ \t]*)', '\n\\2\\1 '),
69 ('(bool|char|const|delete|int|stream|unsigned|void|(struct \w+)|([A-Z]\w*)|[,]|&&|\|\|)[ \t]*(\*|&)[ \t]*', '\\1 \\4'),
70 #to#('(bool|char|const|delete|int|stream|unsigned|void|([A-Z]\w*)|[,])[ \n\t]*(\*|&)[ \t]*', '\\1 \\3'),
71 # pointer with template
72 ('(( *((bool|char|delete|int|stream|unsigned|void|(class[ \t]+\w*)|([A-Z]\w*)|[,])[ \*&],*)+)>) *(\*|&) *', '\\1 \\7'),
73 #to#('(( *((bool|char|delete|int|stream|unsigned|void|(class[ \t]+\w*)|([A-Z]\w*)|[,])[ \*&],*)+)>)[ \t\n]*(\*|&) *', '\\1 \\7'),
74 # unary pointer, minus, not
75 ('(return|=) (\*|&|-|!) ([\w\(])', '\\1 \\2\\3'),
76 # space after `operator'
77 ('(\Woperator) *([^\w\s])', '\\1 \\2'),
79 ('\n[ \t]*\n[ \t]*\n', '\n\n'),
80 # dangling parenthesis open
81 #('[ \t]*\n[ \t]*\([ \t]*\n', '('),
83 # dangling parenthesis close
90 ('(\w)[ \t]*([^\s]*){([ \t]*\n)', '\\1\\2\n{\n'),
91 # brace open backslash
92 ('(\w[^\n]*){[ \t]*\\\\\n', '\\1\\\n{\\\n'),
94 ('}[ \t]*([^\n]*\w[^\n\\\]*)\n', '}\n\\1\n'),
95 # brace close backslash
96 ('}[ \t]*([^\n]*\w[^\n\\\]*)', '\n}\n\\1'),
97 # delete space after `operator'
98 #('(\Woperator) (\W)', '\\1\\2'),
99 # delete space after case, label
100 ('(\W(case|label) ([\w]+)) :', '\\1:'),
101 # delete space before comma
103 # delete space before semicolon
105 # delete space before eol-backslash
106 ('[ \t]*\\\\\n', '\\\n'),
107 # delete trailing whitespace
110 ## Deuglify code that also gets ugly by rules above.
111 # delete newline after typedef struct
112 ('(typedef struct\s+([\w]*\s){([^}]|{[^}]*})*})\s*\n\s*(\w[\w\d]*;)', '\\1 \\4'),
113 # delete spaces around template brackets
114 #('(dynamic_cast|template|([A-Z]\w*))[ \t]*<[ \t]*(( *(bool|char|int|unsigned|void|(class[ \t]+\w*)|([A-Z]\w*)),?)+)[ \t]?(| [\*&])[ \t]*>', '\\1<\\3\\8>'),
115 ('(dynamic_cast|template|([A-Z]\w*))[ \t]*<[ \t]*(( *(bool|char|int|unsigned|void|(class[ \t]+\w*)|([A-Z]\w*))[,\*&]*)+)[ \t]?(| [\*&])[ \t]*>', '\\1<\\3\\8>'),
116 ('((if|while)\s+\(([^\)]|\([^\)]*\))*\))\s*;', '\\1\n;'),
117 ('(for\s+\(([^;]*;[^;]*;([^\)]|\([^\)]*\))*)\))\s*;', '\\1\n;'),
119 ## Fix code that gets broken by rules above.
120 # delete space before #define x()
121 ('#[ \t]*define (\w*)[ \t]*\(', '#define \\1('),
122 # add space in #define x ()
123 ('#[ \t]*define (\w*)(\(([^\(\)]|\([^\(\)]*\))*\)\\n)',
125 # delete space in #include <>
126 ('#[ \t]*include[ \t]*<[ \t]*([^ \t>]*)[ \t]*(/?)[ \t]*([^ \t>]*)[ \t]*>',
127 '#include <\\1\\2\\3>'),
128 # delete backslash before empty line (emacs' indent region is broken)
129 ('\\\\\n\n', '\n\n'),
134 # delete trailing whitespace
136 # delete empty first lines
137 ('(/\*\n)\n*', '\\1'),
138 # delete empty last lines
139 ('\n*(\n\*/)', '\\1'),
140 ## delete newline after start?
142 ## delete newline before end?
147 # Recognize special sequences in the input.
149 # (?P<name>regex) -- Assign result of REGEX to NAME.
150 # *? -- Match non-greedily.
151 # (?m) -- Multiline regex: Make ^ and $ match at each line.
152 # (?s) -- Make the dot match all characters including newline.
153 # (?x) -- Ignore whitespace in patterns.
164 [ \t]*/\*.*?\*/))''',
166 'singleline_comment':
171 [ \t]*//([ \t][^\n]*|)\n))''',
177 "([^"]|(([^\\]|(\\\\))\\"))*"))''',
189 "#[ \t]*include[ \t]*<[^>]*>''',
194 def replacement_text (self):
197 def filter_text (self):
198 return self.replacement_text ()
200 def ly_is_outdated (self):
203 def png_is_outdated (self):
206 class Substring (Chunk):
207 def __init__ (self, source, start, end):
212 def replacement_text (self):
213 s = self.source[self.start:self.end]
215 sys.stderr.write ('CXX Rules')
218 sys.stderr.write ('.')
219 #sys.stderr.write ('\n*********\n')
220 #sys.stderr.write (i[0])
221 #sys.stderr.write ('\n=========\n')
222 #sys.stderr.write (s)
223 #sys.stderr.write ('\n*********\n')
224 s = re.sub (i[0], i[1], s)
226 sys.stderr.write ('done\n')
230 class Snippet (Chunk):
231 def __init__ (self, type, match, format):
238 def replacement_text (self):
239 return self.match.group ('match')
241 def substring (self, s):
242 return self.match.group (s)
245 return `self.__class__` + ' type = ' + self.type
247 class Multiline_comment (Snippet):
248 def __init__ (self, source, match, format):
255 def replacement_text (self):
256 s = self.match.group ('match')
258 sys.stderr.write ('COMMENT Rules')
259 for i in rules[COMMENT]:
261 sys.stderr.write ('.')
262 s = re.sub (i[0], i[1], s)
265 snippet_type_to_class = {
266 'multiline_comment': Multiline_comment,
267 # 'lilypond_block': Lilypond_snippet,
268 # 'lilypond': Lilypond_snippet,
269 # 'include': Include_snippet,
272 def find_toplevel_snippets (s, types):
274 sys.stderr.write ('Dissecting')
278 res[i] = re.compile (snippet_res[format][i])
282 ## found = dict (map (lambda x: (x, None),
286 map (lambda x, f = found: f.setdefault (x, None),
289 # We want to search for multiple regexes, without searching
290 # the string multiple times for one regex.
291 # Hence, we use earlier results to limit the string portion
293 # Since every part of the string is traversed at most once for
294 # every type of snippet, this is linear.
298 sys.stderr.write ('.')
302 if not found[type] or found[type][0] < index:
304 m = res[type].search (s[index:endex])
309 if snippet_type_to_class.has_key (type):
310 cl = snippet_type_to_class[type]
311 snip = cl (type, m, format)
312 start = index + m.start ('match')
313 found[type] = (start, snip)
317 or found[type][0] < found[first][0]):
322 # Limiting the search space is a cute
323 # idea, but this *requires* to search
324 # for possible containing blocks
325 # first, at least as long as we do not
326 # search for the start of blocks, but
327 # always/directly for the entire
328 # @block ... @end block.
330 endex = found[first][0]
333 snippets.append (Substring (s, index, len (s)))
336 (start, snip) = found[first]
337 snippets.append (Substring (s, index, start))
338 snippets.append (snip)
340 index = start + len (snip.match.group ('match'))
344 def nitpick_file (outdir, file):
345 s = open (file).read ()
347 # FIXME: Containing blocks must be first, see
348 # find_toplevel_snippets.
351 'singleline_comment',
356 chunks = find_toplevel_snippets (s, snippet_types)
357 #code = filter (lambda x: is_derived_class (x.__class__, Substring),
360 t = string.join (map (lambda x: x.filter_text (), chunks), '')
364 os.system ('mv %s %s~' % (file, file))
366 fixt = os.path.join (outdir,
367 os.path.basename (file))
371 if s != t or indent_p:
374 def indent_file (file):
381 --eval '(let ((error nil)
382 (version-control nil))
383 (load-library "cc-mode")
385 (indent-region (point-min) (point-max))
386 (if (buffer-modified-p (current-buffer))
387 (save-buffer)))' ''' % vars ()
388 emacsclient = '''emacsclient\
389 --socket-name=%(socketdir)s/%(socketname)s\
391 --eval '(let ((error nil)
392 (version-control nil))
393 (load-library "cc-mode")
394 (find-file "%(file)s")
396 (indent-region (point-min) (point-max))
397 (if (buffer-modified-p (current-buffer))
398 (save-buffer)))' ''' \
400 'socketdir' : socketdir,
401 'socketname' : socketname, }
403 sys.stderr.write (emacs)
404 sys.stderr.write ('\n')
409 sys.stdout.write (r'''
411 fixcc [OPTION]... FILE...
415 --indent reindent, even if no changes
419 Typical use with LilyPond:
421 fixcc $(find flower kpath-guile lily -name '*cc' -o -name '*hh' | grep -v /out)
423 This script is licensed under the GNU GPL
427 global indent_p, outdir, verbose_p
428 (options, files) = getopt.getopt (sys.argv[1:], '',
429 ['help', 'indent', 'outdir=',
431 for (o, a) in options:
435 elif o == '--indent':
437 elif o == '--outdir':
439 elif o == '--verbose':
454 socketdir = '/tmp/fixcc'
455 socketname = 'fixcc%d' % os.getpid ()
460 os.unlink (os.path.join (socketdir, socketname))
461 os.mkdir (socketdir, 0700)
465 --eval '(let ((error nil)
466 (version-control nil))
467 (load-library "server")
468 (setq server-socket-dir "%(socketdir)s")
469 (setq server-name "%(socketname)s")
471 (while t) (sleep 1000))' ''' \
472 % { 'socketdir' : socketdir,
473 'socketname' : socketname, }
478 while not os.path.exists (os.path.join (socketdir, socketname)):
482 #emacsclient should be faster, but this does not work yet
484 files = do_options ()
485 if outdir and not os.path.isdir (outdir):
488 sys.stderr.write ('%s...\n' % i)
489 nitpick_file (outdir, i)
494 operator << (ostream & os, String d);
496 typedef struct _t_ligature
499 struct _t_ligature *next;
500 struct _t_ligature * next;
507 a [x] = foe (*i, &bar) *
509 int operator double ();
511 Interval_t<T> &operator*= (T r);
512 int compare (Pqueue_ent < K, T > const& e1, Pqueue_ent < K,T> *e2);
514 if (abs (f)*2 > abs (d) *FUDGE)
517 for (; i < x (); foo > bar);
524 1 && * unsmob_moment (lf);
526 line_spanner_ = make_spanner ("DynamicLineSpanner", rq ? rq->self_scm
534 } cookie_io_functions_t;
541 cookie_io_functions_t Memory_out_stream::functions_ = {
542 Memory_out_stream::reader,
550 test_file = 'fixcc.cc'
551 open (test_file, 'w').write (TEST)
552 nitpick_file (outdir, test_file)
553 sys.stdout.write (open (test_file).read ())
555 if __name__ == '__main__':