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 ('(\Wdo\s*{([^}]|{[^}]*})*}\s*while\s*)(\(([^\)]|\([^\)]*\))*\))\s*;', '\\1\\3;\n'),
120 ## Fix code that gets broken by rules above.
121 # delete space before #define x()
122 ('#[ \t]*define (\w*)[ \t]*\(', '#define \\1('),
123 # add space in #define x ()
124 ('#[ \t]*define (\w*)(\(([^\(\)]|\([^\(\)]*\))*\)\\n)',
126 # delete space in #include <>
127 ('#[ \t]*include[ \t]*<[ \t]*([^ \t>]*)[ \t]*(/?)[ \t]*([^ \t>]*)[ \t]*>',
128 '#include <\\1\\2\\3>'),
129 # delete backslash before empty line (emacs' indent region is broken)
130 ('\\\\\n\n', '\n\n'),
135 # delete trailing whitespace
137 # delete empty first lines
138 ('(/\*\n)\n*', '\\1'),
139 # delete empty last lines
140 ('\n*(\n\*/)', '\\1'),
141 ## delete newline after start?
143 ## delete newline before end?
148 # Recognize special sequences in the input.
150 # (?P<name>regex) -- Assign result of REGEX to NAME.
151 # *? -- Match non-greedily.
152 # (?m) -- Multiline regex: Make ^ and $ match at each line.
153 # (?s) -- Make the dot match all characters including newline.
154 # (?x) -- Ignore whitespace in patterns.
165 [ \t]*/\*.*?\*/))''',
167 'singleline_comment':
172 [ \t]*//([ \t][^\n]*|)\n))''',
178 "([^"]|(([^\\]|(\\\\))\\"))*"))''',
190 "#[ \t]*include[ \t]*<[^>]*>''',
195 def replacement_text (self):
198 def filter_text (self):
199 return self.replacement_text ()
201 def ly_is_outdated (self):
204 def png_is_outdated (self):
207 class Substring (Chunk):
208 def __init__ (self, source, start, end):
213 def replacement_text (self):
214 s = self.source[self.start:self.end]
216 sys.stderr.write ('CXX Rules')
219 sys.stderr.write ('.')
220 #sys.stderr.write ('\n*********\n')
221 #sys.stderr.write (i[0])
222 #sys.stderr.write ('\n=========\n')
223 #sys.stderr.write (s)
224 #sys.stderr.write ('\n*********\n')
225 s = re.sub (i[0], i[1], s)
227 sys.stderr.write ('done\n')
231 class Snippet (Chunk):
232 def __init__ (self, type, match, format):
239 def replacement_text (self):
240 return self.match.group ('match')
242 def substring (self, s):
243 return self.match.group (s)
246 return `self.__class__` + ' type = ' + self.type
248 class Multiline_comment (Snippet):
249 def __init__ (self, source, match, format):
256 def replacement_text (self):
257 s = self.match.group ('match')
259 sys.stderr.write ('COMMENT Rules')
260 for i in rules[COMMENT]:
262 sys.stderr.write ('.')
263 s = re.sub (i[0], i[1], s)
266 snippet_type_to_class = {
267 'multiline_comment': Multiline_comment,
268 # 'lilypond_block': Lilypond_snippet,
269 # 'lilypond': Lilypond_snippet,
270 # 'include': Include_snippet,
273 def find_toplevel_snippets (s, types):
275 sys.stderr.write ('Dissecting')
279 res[i] = re.compile (snippet_res[format][i])
283 ## found = dict (map (lambda x: (x, None),
287 map (lambda x, f = found: f.setdefault (x, None),
290 # We want to search for multiple regexes, without searching
291 # the string multiple times for one regex.
292 # Hence, we use earlier results to limit the string portion
294 # Since every part of the string is traversed at most once for
295 # every type of snippet, this is linear.
299 sys.stderr.write ('.')
303 if not found[type] or found[type][0] < index:
305 m = res[type].search (s[index:endex])
310 if snippet_type_to_class.has_key (type):
311 cl = snippet_type_to_class[type]
312 snip = cl (type, m, format)
313 start = index + m.start ('match')
314 found[type] = (start, snip)
318 or found[type][0] < found[first][0]):
323 # Limiting the search space is a cute
324 # idea, but this *requires* to search
325 # for possible containing blocks
326 # first, at least as long as we do not
327 # search for the start of blocks, but
328 # always/directly for the entire
329 # @block ... @end block.
331 endex = found[first][0]
334 snippets.append (Substring (s, index, len (s)))
337 (start, snip) = found[first]
338 snippets.append (Substring (s, index, start))
339 snippets.append (snip)
341 index = start + len (snip.match.group ('match'))
345 def nitpick_file (outdir, file):
346 s = open (file).read ()
348 # FIXME: Containing blocks must be first, see
349 # find_toplevel_snippets.
352 'singleline_comment',
357 chunks = find_toplevel_snippets (s, snippet_types)
358 #code = filter (lambda x: is_derived_class (x.__class__, Substring),
361 t = string.join (map (lambda x: x.filter_text (), chunks), '')
365 os.system ('mv %s %s~' % (file, file))
367 fixt = os.path.join (outdir,
368 os.path.basename (file))
372 if s != t or indent_p:
375 def indent_file (file):
382 --eval '(let ((error nil)
383 (version-control nil))
384 (load-library "cc-mode")
386 (indent-region (point-min) (point-max))
387 (if (buffer-modified-p (current-buffer))
388 (save-buffer)))' ''' % vars ()
389 emacsclient = '''emacsclient\
390 --socket-name=%(socketdir)s/%(socketname)s\
392 --eval '(let ((error nil)
393 (version-control nil))
394 (load-library "cc-mode")
395 (find-file "%(file)s")
397 (indent-region (point-min) (point-max))
398 (if (buffer-modified-p (current-buffer))
399 (save-buffer)))' ''' \
401 'socketdir' : socketdir,
402 'socketname' : socketname, }
404 sys.stderr.write (emacs)
405 sys.stderr.write ('\n')
410 sys.stdout.write (r'''
412 fixcc [OPTION]... FILE...
416 --indent reindent, even if no changes
420 Typical use with LilyPond:
422 fixcc $(find flower kpath-guile lily -name '*cc' -o -name '*hh' | grep -v /out)
424 This script is licensed under the GNU GPL
428 global indent_p, outdir, verbose_p
429 (options, files) = getopt.getopt (sys.argv[1:], '',
430 ['help', 'indent', 'outdir=',
432 for (o, a) in options:
436 elif o == '--indent':
438 elif o == '--outdir':
440 elif o == '--verbose':
455 socketdir = '/tmp/fixcc'
456 socketname = 'fixcc%d' % os.getpid ()
461 os.unlink (os.path.join (socketdir, socketname))
462 os.mkdir (socketdir, 0700)
466 --eval '(let ((error nil)
467 (version-control nil))
468 (load-library "server")
469 (setq server-socket-dir "%(socketdir)s")
470 (setq server-name "%(socketname)s")
472 (while t) (sleep 1000))' ''' \
473 % { 'socketdir' : socketdir,
474 'socketname' : socketname, }
479 while not os.path.exists (os.path.join (socketdir, socketname)):
483 #emacsclient should be faster, but this does not work yet
485 files = do_options ()
486 if outdir and not os.path.isdir (outdir):
489 sys.stderr.write ('%s...\n' % i)
490 nitpick_file (outdir, i)
495 operator << (ostream & os, String d);
497 typedef struct _t_ligature
500 struct _t_ligature *next;
501 struct _t_ligature * next;
508 a [x] = foe (*i, &bar) *
510 int operator double ();
512 Interval_t<T> &operator*= (T r);
513 int compare (Pqueue_ent < K, T > const& e1, Pqueue_ent < K,T> *e2);
515 if (abs (f)*2 > abs (d) *FUDGE)
518 for (; i < x (); foo > bar);
529 1 && * unsmob_moment (lf);
531 line_spanner_ = make_spanner ("DynamicLineSpanner", rq ? rq->self_scm
539 } cookie_io_functions_t;
546 cookie_io_functions_t Memory_out_stream::functions_ = {
547 Memory_out_stream::reader,
555 test_file = 'fixcc.cc'
556 open (test_file, 'w').write (TEST)
557 nitpick_file (outdir, test_file)
558 sys.stdout.write (open (test_file).read ())
560 if __name__ == '__main__':