3 # fixcc -- indent and space lily's c++ code
5 # This file is part of LilyPond, the GNU music typesetter.
7 # LilyPond is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # LilyPond is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 # Performs string substitution on files, then applies astyle
21 # (http://astyle.sourceforge.net)
23 # Remove prefiltering as the equivalent formatting becomes available in
24 # astyle, or as the prefiltering is deemed un-necessary.
25 # Soon, this script might be replaced by a simple invocation of astyle
42 PREFERRED_ASTYLE_VERSION = "Artistic Style Version 2.04"
48 # delete trailing whitespace
53 # space before parenthesis open; astyle -xd does this except for foo().
54 ('([\w\)\]])\(', '\\1 ('),
55 # delete inline double spaces
57 # delete space before parenthesis close
59 # delete spaces after prefix
60 ('(--|\+\+) *([\w\(])', '\\1\\2'),
61 # delete spaces before postfix
62 ('([\w\)\]]) *(--|\+\+)', '\\1\\2'),
64 # delete space around operator
65 ('([\w\(\)\]]) *(\.|->) *([\w\(\)])', '\\1\\2\\3'),
66 # delete space after operator
67 ('(::) *([\w\(\)])', '\\1\\2'),
69 # delete superflous space around operator
70 ('([\w\(\)\]]) +(&&|\|\||<=|>=|!=|\|=|==|\+=|-=|\*=|/=|\?|<|>|\+|-|=|/|:|&|\||\*) +([\w\(\)])', '\\1 \\2 \\3'),
72 # trailing operator, but don't un-trail close angle-braces > nor pointer *, and not before a preprocessor line
73 ('(?<!\s) (::|&&|\|\||<=|>=|!=|\|=|==|\+=|-=|\*=|/=|\?|<|\+|-|=|/|:|&XXX|\||\*XXX) *\n( *)([^\s#])', '\n\\2\\1 \\3'),
74 # space after `operator'
75 ('(\Woperator) *([^\w\s])', '\\1 \\2'),
76 # trailing parenthesis open
78 # dangling parenthesis close: Disabled to leave ADD_TRANSLATOR format in place
81 ('\n( *),', ',\n\\1'),
82 # delete space after case, label
83 ('(\W(case|label) [\w]+) :', '\\1:'),
84 # delete space before comma
86 # delete space before semicolon
87 ('([^;]) +;', '\\1;'),
91 # delete backslash before empty line (emacs' indent region is broken)
97 # delete empty first lines
98 ('(/\*\n)\n*', '\\1'),
99 # delete empty last lines
100 ('\n*(\n\*/)', '\\1'),
101 ## delete newline after start?
103 ## delete newline before end?
108 # Recognize special sequences in the input.
110 # (?P<name>regex) -- Assign result of REGEX to NAME.
111 # *? -- Match non-greedily.
112 # (?m) -- Multiline regex: Make ^ and $ match at each line.
113 # (?s) -- Make the dot match all characters including newline.
114 # (?x) -- Ignore whitespace in patterns.
122 \#[ \t]*define[ \t]+([^\n]*\\\n)*[^\n]*))''',
128 [ \t]*/\*.*?\*/))''',
130 'singleline_comment':
132 ^.*? # leave leading spaces for the comment snippet
135 [ \t]*//[^\n]*\n))''',
139 " # leave the leading " character visible to CXX rules
154 \#[ \t]*include[ \t]*<[^>]*>))''',
159 def replacement_text (self):
162 def filter_text (self):
163 return self.replacement_text ()
165 class Substring (Chunk):
166 def __init__ (self, source, start, end):
171 def replacement_text (self):
172 s = self.source[self.start:self.end]
174 sys.stderr.write ('CXX Rules')
177 sys.stderr.write ('.')
178 #sys.stderr.write ('\n\n***********\n')
179 #sys.stderr.write (i[0])
180 #sys.stderr.write ('\n***********\n')
181 #sys.stderr.write ('\n=========>>\n')
182 #sys.stderr.write (s)
183 #sys.stderr.write ('\n<<=========\n')
184 s = re.sub (i[0], i[1], s)
186 sys.stderr.write ('done\n')
190 class Snippet (Chunk):
191 def __init__ (self, type, match, format):
198 def replacement_text (self):
199 return self.match.group ('match')
201 def substring (self, s):
202 return self.match.group (s)
205 return `self.__class__` + ' type = ' + self.type
207 class Multiline_comment (Snippet):
208 def __init__ (self, source, match, format):
215 def replacement_text (self):
216 s = self.match.group ('match')
218 sys.stderr.write ('COMMENT Rules')
219 for i in rules[COMMENT]:
221 sys.stderr.write ('.')
222 s = re.sub (i[0], i[1], s)
225 snippet_type_to_class = {
226 'multiline_comment': Multiline_comment,
227 # 'string': Multiline_comment,
228 # 'include': Include_snippet,
231 def find_toplevel_snippets (s, types):
233 sys.stderr.write ('Dissecting')
237 res[i] = re.compile (snippet_res[format][i])
241 ## found = dict (map (lambda x: (x, None),
245 map (lambda x, f = found: f.setdefault (x, None),
248 # We want to search for multiple regexes, without searching
249 # the string multiple times for one regex.
250 # Hence, we use earlier results to limit the string portion
252 # Since every part of the string is traversed at most once for
253 # every type of snippet, this is linear.
257 sys.stderr.write ('.')
261 if not found[type] or found[type][0] < index:
263 m = res[type].search (s[index:endex])
268 if snippet_type_to_class.has_key (type):
269 cl = snippet_type_to_class[type]
270 snip = cl (type, m, format)
271 start = index + m.start ('match')
272 found[type] = (start, snip)
276 or found[type][0] < found[first][0]):
281 # Limiting the search space is a cute
282 # idea, but this *requires* to search
283 # for possible containing blocks
284 # first, at least as long as we do not
285 # search for the start of blocks, but
286 # always/directly for the entire
287 # @block ... @end block.
289 endex = found[first][0]
292 snippets.append (Substring (s, index, len (s)))
295 (start, snip) = found[first]
296 snippets.append (Substring (s, index, start))
297 snippets.append (snip)
299 index = start + len (snip.match.group ('match'))
303 def nitpick_file (outdir, file):
304 s = open (file).read ()
307 for i in rules[GLOBAL_CXX]:
308 t = re.sub (i[0], i[1], t)
310 # FIXME: Containing blocks must be first, see
311 # find_toplevel_snippets.
312 # We leave simple strings be part of the code
316 'singleline_comment',
322 chunks = find_toplevel_snippets (t, snippet_types)
323 #code = filter (lambda x: is_derived_class (x.__class__, Substring),
326 t = string.join (map (lambda x: x.filter_text (), chunks), '')
330 os.system ('mv %s %s~' % (file, file))
332 fixt = os.path.join (outdir,
333 os.path.basename (file))
337 if s != t or indent_p:
340 def indent_file (file):
341 # Astyle aborts on unrecognized options,
342 # so wait until everyone has 2.04 before considering:
343 # --attach-namespaces --indent-namespaces \
344 # --max-code-length=80 --pad-first-paren-out \
346 --options=none --quiet -n \
347 --style=gnu --indent=spaces=2 \
348 --max-instatement-indent=60 \
350 --align-pointer=name --pad-oper \
351 --keep-one-line-blocks \
355 sys.stderr.write (astyle)
356 sys.stderr.write ('\n')
361 sys.stdout.write (r'''
363 fixcc [OPTION]... FILE...
367 --lazy skip astyle, if no changes
368 --sloppy accept any astyle version
372 Typical use with LilyPond:
374 fixcc $(find flower lily -name '*cc' -o -name '*hh' | grep -v /out)
379 global indent_p, outdir, verbose_p
380 (options, files) = getopt.getopt (sys.argv[1:], '',
381 ['help', 'lazy', 'outdir=',
383 for (o, a) in options:
389 elif o == '--outdir':
391 elif o == '--sloppy':
392 PREFERRED_ASTYLE_VERSION = "Artistic Style"
393 elif o == '--verbose':
405 def check_astyle_version():
406 cmd = "astyle --version"
407 process = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE)
408 stdout, stderr = process.communicate()
409 if PREFERRED_ASTYLE_VERSION in stderr:
416 socketdir = '/tmp/fixcc'
417 socketname = 'fixcc%d' % os.getpid ()
420 files = do_options ()
421 if not check_astyle_version():
422 print "Warning: try to use %s." % PREFERRED_ASTYLE_VERSION
423 print "Please limit use of this version to files with changed code."
425 print "Too many files with this version. See `astyle --help`"
427 if outdir and not os.path.isdir (outdir):
430 sys.stderr.write ('%s...\n' % i)
431 nitpick_file (outdir, i)
434 ## TODO: make this compilable and check with g++
442 public: static char* foo ();
443 std::map<char*,int>* bar (char, char) { return 0; }
451 operator << (ostream & os, String d);
453 typedef struct _t_ligature
456 struct _t_ligature * next;
459 typedef std::map < AFM_Ligature const *, int > Bar;
462 Copyright (C) 1997--2015 Han-Wen Nienhuys <hanwen@cs.uu.nl>
480 a [*++ a] = (char*) foe (*i, &bar) *
482 int operator double ();
483 std::map<char*,int> y =*bar(-*a ,*b);
484 Interval_t<T> & operator*= (T r);
486 int compare (Pqueue_ent < K, T > const& e1, Pqueue_ent < K,T> *e2);
488 if (abs (f)*2 > abs (d) *FUDGE)
491 for (; i<x foo(); foo>bar);
501 1 && * Moment::unsmob (lf);
502 line_spanner_ = make_spanner ("DynamicLineSpanner", rq ? rq->*self_scm
510 cookie_io_functions_t Memory_out_stream::functions_ = {
511 Memory_out_stream::reader,
515 int compare (Array < Pitch> *, Array < Pitch> *);
516 original_ = (Grob *) & s;
517 Drul_array< Link_array<Grob> > o;
520 header_.char_info_pos = (6 + header_length) * 4;
521 return ly_bool2scm (*ma < * mb);
533 span_ = make_spanner ("StaffSymbol", SCM_EOL);
539 span_ = make_spanner (StaffSymbol, SCM_EOL);
545 test_file = 'fixcc.cc'
546 open (test_file, 'w').write (TEST)
547 nitpick_file (outdir, test_file)
548 sys.stdout.write (open (test_file).read ())
550 if __name__ == '__main__':