3 # fixcc -- nitpick lily's c++ code
6 # * maintainable rules: regexp's using whitespace (?x) and match names
8 # * trailing `*' vs. function definition
9 # * do not break/change indentation of fixcc-clean files
10 # * check lexer, parser
11 # * rewrite in elisp, add to cc-mode
12 # * using regexes is broken by design
32 # space before parenthesis open
33 ('([^\( \]])[ \t]*\(', '\\1 ('),
38 # delete inline double spaces
40 # delete space after parenthesis open
42 # delete space before parenthesis close
44 # delete spaces after prefix
45 ('(--|\+\+)[ \t]*([\w\)])', '\\1\\2'),
46 # delete spaces before postfix
47 ('([\w\)\]])[ \t]*(--|\+\+)', '\\1\\2'),
48 # delete space after parenthesis close
49 #('\)[ \t]*([^\w])', ')\\1'),
50 # delete space around operator
51 # ('([\w\(\)\]])([ \t]*)(::|\.)([ \t]*)([\w\(\)])', '\\1\\3\\5'),
52 ('([\w\(\)\]])([ \t]*)(\.|->)([ \t]*)([\w\(\)])', '\\1\\3\\5'),
53 # delete space after operator
54 ('(::)([ \t]*)([\w\(\)])', '\\1\\3'),
55 # delete superflous space around operator
56 ('([\w\(\)\]])([ \t]+)(&&|\|\||<=|>=|!=|\|=|==|\+=|-=|\*=|/=|\?|<|>|\+|-|=|/|:|&|\||\*)([ \t]+)([\w\(\)])', '\\1 \\3 \\5'),
57 # space around operator1
58 ('([\w\)\]]) *(&&|\|\||<=|>=|!=|\|=|==|\+=|-=|\*=|/=|\?|<|>|=|/|:|&|\||\*) *([\w\(])', '\\1 \\2 \\3'),
59 # space around operator2
60 ('([\w\)\]]) *(&&|\|\||<=|>=|!=|\|=|==|\+=|-=|\*=|/=|\?|<|>|=|/|:|&|\||\*) ([^\w\s])', '\\1 \\2 \\3'),
61 # space around operator3
62 ('([^\w\s]) (&&|\|\||<=|>=|!=|\|=|==|\+=|-=|\*=|/=|\?|<|[^-]>|=|/|:|&|\||\*) *([\w\(])', '\\1 \\2 \\3'),
63 # space around operator4
64 ('([\w\(\)\]]) (\*|/|\+|-) *([-:])', '\\1 \\2 \\3'),
65 # space around +/-; exponent
66 ('([\w\)\]])(\+|-)([_A-Za-z\(])', '\\1 \\2 \\3'),
67 ('([_\dA-Za-df-z\)\]])(\+|-)([\w\(])', '\\1 \\2 \\3'),
69 (' (::|&&|\|\||<=|>=|!=|\|=|==|\+=|-=|\*=|/=|\?|<|>|\+|-|=|/|:|&XXX|\||\*XXX)[ \t]*\n([ \t]*)', '\n\\2\\1 '),
71 ##('(bool|char|const|delete|int|stream|unsigned|void|size_t|struct \w+|[A-Z]\w*|,|;|&&|<|[^-]>|\|\||-|\+)[ \t]*(\*|&)[ \t]*', '\\1 \\2'),
72 ('(bool|char|const|delete|int|stream|unsigned|void|size_t|struct \w+|[A-Z]\w*|,|;|:|=|\?\)|&&|<|[^-]>|\|\||-|\+)[ \t]*(\*|&)[ \t]*', '\\1 \\2'),
73 #to#('(bool|char|const|delete|int|stream|unsigned|void|([A-Z]\w*)|[,])[ \n\t]*(\*|&)[ \t]*', '\\1 \\3'),
74 # pointer with template
75 ('(( *((bool|char|const|delete|int|stream|unsigned|void|size_t|class[ \t]+\w*|[A-Z]\w*|\w+::\w+|[,])[ \*&],*)+)>) *(\*|&) *', '\\1 \\5'),
76 #to#('(( *((bool|char|delete|int|stream|unsigned|void|(class[ \t]+\w*)|([A-Z]\w*)|[,])[ \*&],*)+)>)[ \t\n]*(\*|&) *', '\\1 \\7'),
77 # unary pointer, minus, not
78 ('(return|=) (\*|&|-|!) ([\w\(])', '\\1 \\2\\3'),
79 # space after `operator'
80 ('(\Woperator) *([^\w\s])', '\\1 \\2'),
81 # delete gratuitous block
82 ('''\n( |\t)\s*{\n\s*(.*?)(?![{}]|\b(do|for|else|if|switch|while)\b);\n\s*}''',
84 # dangling brace close
85 ('\n[ \t]*(\n[ \t]*})', '\\1'),
87 ('\n[ \t]*\n[ \t]*\n', '\n\n'),
88 # dangling parenthesis open
89 #('[ \t]*\n[ \t]*\([ \t]*\n', '('),
91 # dangling parenthesis close
97 # delete gratuitous blocks
98 ('''xx\n([ ]|\t)\s*{\n\s*(.*?)(?![{}]|\b(do|for|else|if|switch|while)\b);\n\s*}''',
101 ('(\w)[ \t]*([^\s]*){([ \t]*\n)', '\\1\\2\n{\n'),
102 # brace open backslash
103 ('(\w[^\n]*){[ \t]*\\\\\n', '\\1\\\n{\\\n'),
105 ('}[ \t]*([^\n]*\w[^\n\\\]*)\n', '}\n\\1\n'),
106 # brace close backslash
107 ('}[ \t]*([^\n]*\w[^\n\\\]*)', '\n}\n\\1'),
108 # delete space after `operator'
109 #('(\Woperator) (\W)', '\\1\\2'),
110 # delete space after case, label
111 ('(\W(case|label) ([\w]+)) :', '\\1:'),
112 # delete space before comma
114 # delete space before semicolon
116 # delete space before eol-backslash
117 ('[ \t]*\\\\\n', '\\\n'),
118 # delete trailing whitespace
121 ## Deuglify code that also gets ugly by rules above.
122 # delete newline after typedef struct
123 ('(typedef struct\s+([\w]*\s){([^}]|{[^}]*})*})\s*\n\s*(\w[\w\d]*;)', '\\1 \\4'),
124 # delete spaces around template brackets
125 #('(dynamic_cast|template|([A-Z]\w*))[ \t]*<[ \t]*(( *(bool|char|int|unsigned|void|(class[ \t]+\w*)|([A-Z]\w*)),?)+)[ \t]?(| [\*&])[ \t]*>', '\\1<\\3\\8>'),
126 ('(dynamic_cast|template|typedef|\w+::\w+|[A-Z]\w*)[ \t]*<[ \t]*(( *(bool|char|const|int|unsigned|void|size_t|class[ \t]+\w*|[A-Z]\w*)( *[\*&]?,|[\*&])*)+)[ \t]?(| [\*&])[ \t]*>', '\\1<\\2\\6>'),
127 ('(\w+::\w+|[A-Z]\w*) < ((\w+::\w+|[A-Z]\w*)<[A-Z]\w*>) >', '\\1<\\2 >'),
128 ('((if|while)\s+\(([^\)]|\([^\)]*\))*\))\s*;', '\\1\n;'),
129 ('(for\s+\(([^;]*;[^;]*;([^\)]|\([^\)]*\))*)\))\s*;', '\\1\n;'),
131 ('(}\s*while\s*)(\(([^\)]|\([^\)]*\))*\))\s*;', '\\1\\2;'),
133 ## Fix code that gets broken by rules above.
134 ##('->\s+\*', '->*'),
135 # delete space before #define x()
136 ('#[ \t]*define (\w*)[ \t]*\(', '#define \\1('),
137 # add space in #define x ()
138 ('#[ \t]*define (\w*)(\(([^\(\)]|\([^\(\)]*\))*\)\\n)',
140 # delete space in #include <>
141 ('#[ \t]*include[ \t]*<[ \t]*([^ \t>]*)[ \t]*(/?)[ \t]*([^ \t>]*)[ \t]*>',
142 '#include <\\1\\2\\3>'),
143 # delete backslash before empty line (emacs' indent region is broken)
144 ('\\\\\n\n', '\n\n'),
149 # delete trailing whitespace
151 # delete empty first lines
152 ('(/\*\n)\n*', '\\1'),
153 # delete empty last lines
154 ('\n*(\n\*/)', '\\1'),
155 ## delete newline after start?
157 ## delete newline before end?
162 # Recognize special sequences in the input.
164 # (?P<name>regex) -- Assign result of REGEX to NAME.
165 # *? -- Match non-greedily.
166 # (?m) -- Multiline regex: Make ^ and $ match at each line.
167 # (?s) -- Make the dot match all characters including newline.
168 # (?x) -- Ignore whitespace in patterns.
179 [ \t]*/\*.*?\*/))''',
181 'singleline_comment':
186 [ \t]*//([ \t][^\n]*|)\n))''',
192 "([^"]|(([^\\]|(\\\\))\\"))*"))''',
204 "#[ \t]*include[ \t]*<[^>]*>''',
209 def replacement_text (self):
212 def filter_text (self):
213 return self.replacement_text ()
215 def ly_is_outdated (self):
218 def png_is_outdated (self):
221 class Substring (Chunk):
222 def __init__ (self, source, start, end):
227 def replacement_text (self):
228 s = self.source[self.start:self.end]
230 sys.stderr.write ('CXX Rules')
233 sys.stderr.write ('.')
234 #sys.stderr.write ('\n*********\n')
235 #sys.stderr.write (i[0])
236 #sys.stderr.write ('\n=========\n')
237 #sys.stderr.write (s)
238 #sys.stderr.write ('\n*********\n')
239 s = re.sub (i[0], i[1], s)
241 sys.stderr.write ('done\n')
245 class Snippet (Chunk):
246 def __init__ (self, type, match, format):
253 def replacement_text (self):
254 return self.match.group ('match')
256 def substring (self, s):
257 return self.match.group (s)
260 return `self.__class__` + ' type = ' + self.type
262 class Multiline_comment (Snippet):
263 def __init__ (self, source, match, format):
270 def replacement_text (self):
271 s = self.match.group ('match')
273 sys.stderr.write ('COMMENT Rules')
274 for i in rules[COMMENT]:
276 sys.stderr.write ('.')
277 s = re.sub (i[0], i[1], s)
280 snippet_type_to_class = {
281 'multiline_comment': Multiline_comment,
282 # 'lilypond_block': Lilypond_snippet,
283 # 'lilypond': Lilypond_snippet,
284 # 'include': Include_snippet,
287 def find_toplevel_snippets (s, types):
289 sys.stderr.write ('Dissecting')
293 res[i] = re.compile (snippet_res[format][i])
297 ## found = dict (map (lambda x: (x, None),
301 map (lambda x, f = found: f.setdefault (x, None),
304 # We want to search for multiple regexes, without searching
305 # the string multiple times for one regex.
306 # Hence, we use earlier results to limit the string portion
308 # Since every part of the string is traversed at most once for
309 # every type of snippet, this is linear.
313 sys.stderr.write ('.')
317 if not found[type] or found[type][0] < index:
319 m = res[type].search (s[index:endex])
324 if snippet_type_to_class.has_key (type):
325 cl = snippet_type_to_class[type]
326 snip = cl (type, m, format)
327 start = index + m.start ('match')
328 found[type] = (start, snip)
332 or found[type][0] < found[first][0]):
337 # Limiting the search space is a cute
338 # idea, but this *requires* to search
339 # for possible containing blocks
340 # first, at least as long as we do not
341 # search for the start of blocks, but
342 # always/directly for the entire
343 # @block ... @end block.
345 endex = found[first][0]
348 snippets.append (Substring (s, index, len (s)))
351 (start, snip) = found[first]
352 snippets.append (Substring (s, index, start))
353 snippets.append (snip)
355 index = start + len (snip.match.group ('match'))
359 def nitpick_file (outdir, file):
360 s = open (file).read ()
362 # FIXME: Containing blocks must be first, see
363 # find_toplevel_snippets.
366 'singleline_comment',
371 chunks = find_toplevel_snippets (s, snippet_types)
372 #code = filter (lambda x: is_derived_class (x.__class__, Substring),
375 t = string.join (map (lambda x: x.filter_text (), chunks), '')
379 os.system ('mv %s %s~' % (file, file))
381 fixt = os.path.join (outdir,
382 os.path.basename (file))
386 if s != t or indent_p:
389 def indent_file (file):
396 --eval '(let ((error nil)
397 (version-control nil))
398 (load-library "cc-mode")
400 (indent-region (point-min) (point-max))
401 (if (buffer-modified-p (current-buffer))
402 (save-buffer)))' ''' % vars ()
403 emacsclient = '''emacsclient\
404 --socket-name=%(socketdir)s/%(socketname)s\
406 --eval '(let ((error nil)
407 (version-control nil))
408 (load-library "cc-mode")
409 (find-file "%(file)s")
411 (indent-region (point-min) (point-max))
412 (if (buffer-modified-p (current-buffer))
413 (save-buffer)))' ''' \
415 'socketdir' : socketdir,
416 'socketname' : socketname, }
418 sys.stderr.write (emacs)
419 sys.stderr.write ('\n')
424 sys.stdout.write (r'''
426 fixcc [OPTION]... FILE...
430 --indent reindent, even if no changes
434 Typical use with LilyPond:
436 fixcc $(find flower kpath-guile lily -name '*cc' -o -name '*hh' | grep -v /out)
438 This script is licensed under the GNU GPL
442 global indent_p, outdir, verbose_p
443 (options, files) = getopt.getopt (sys.argv[1:], '',
444 ['help', 'indent', 'outdir=',
446 for (o, a) in options:
450 elif o == '--indent':
452 elif o == '--outdir':
454 elif o == '--verbose':
469 socketdir = '/tmp/fixcc'
470 socketname = 'fixcc%d' % os.getpid ()
475 os.unlink (os.path.join (socketdir, socketname))
476 os.mkdir (socketdir, 0700)
480 --eval '(let ((error nil)
481 (version-control nil))
482 (load-library "server")
483 (setq server-socket-dir "%(socketdir)s")
484 (setq server-name "%(socketname)s")
486 (while t) (sleep 1000))' ''' \
487 % { 'socketdir' : socketdir,
488 'socketname' : socketname, }
493 while not os.path.exists (os.path.join (socketdir, socketname)):
497 #emacsclient should be faster, but this does not work yet
499 files = do_options ()
500 if outdir and not os.path.isdir (outdir):
503 sys.stderr.write ('%s...\n' % i)
504 nitpick_file (outdir, i)
507 ## TODO: make this compilable and check with g++
515 public: static char* foo ();
516 std::map<char*,int>* bar (char, char) { return 0; }
524 operator << (ostream & os, String d);
526 typedef struct _t_ligature
529 struct _t_ligature * next;
532 typedef std::map < AFM_Ligature const *, int > Bar;
549 a [*++ a] = (char*) foe (*i, &bar) *
551 int operator double ();
552 std::map<char*,int> y =*bar(-*a ,*b);
553 Interval_t<T> & operator*= (T r);
555 int compare (Pqueue_ent < K, T > const& e1, Pqueue_ent < K,T> *e2);
557 if (abs (f)*2 > abs (d) *FUDGE)
560 for (; i<x foo(); foo>bar);
570 1 && * unsmob_moment (lf);
571 line_spanner_ = make_spanner ("DynamicLineSpanner", rq ? rq->*self_scm
579 cookie_io_functions_t Memory_out_stream::functions_ = {
580 Memory_out_stream::reader,
584 int compare (Array < Pitch> *, Array < Pitch> *);
585 original_ = (Grob *) & s;
586 Drul_array< Link_array<Grob> > o;
589 header_.char_info_pos = (6 + header_length) * 4;
590 return ly_bool2scm (*ma < * mb);
607 test_file = 'fixcc.cc'
608 open (test_file, 'w').write (TEST)
609 nitpick_file (outdir, test_file)
610 sys.stdout.write (open (test_file).read ())
612 if __name__ == '__main__':