2 # mup2ly.py -- mup input converter
4 # source file of the GNU LilyPond music typesetter
10 LOTS: we get all notes out now, rest after 1.4
12 * lyrics (partly done)
22 * repeats, percent repeats
38 # if set, LILYPONDPREFIX must take prevalence
39 # if datadir is not set, we're doing a build and LILYPONDPREFIX
40 datadir
= '@local_lilypond_datadir@'
41 if os
.environ
.has_key ('LILYPONDPREFIX') \
42 or '@local_lilypond_datadir@' == '@' + 'local_lilypond_datadir' + '@':
43 datadir
= os
.environ
['LILYPONDPREFIX']
45 datadir
= '@local_lilypond_datadir@'
47 sys
.path
.append (os
.path
.join (datadir
, 'python'))
48 sys
.path
.append (os
.path
.join (datadir
, 'python/out'))
50 program_name
= sys
.argv
[0]
51 program_version
= '@TOPLEVEL_VERSION@'
52 original_dir
= os
.getcwd ()
53 temp_dir
= os
.path
.join (original_dir
, '%s.dir' % program_name
)
54 errorport
= sys
.stderr
58 localedir
= '@localedir@'
61 gettext
.bindtextdomain ('lilypond', localedir
)
62 gettext
.textdomain ('lilypond')
69 program_name
= 'mup2ly'
70 help_summary
= _ ("Convert mup to LilyPond source.")
72 option_definitions
= [
73 ('', 'd', 'debug', _ ("debug")),
74 ('NAME[=EXP]', 'D', 'define', _ ("define macro NAME [optional expansion EXP]")),
75 ('', 'h', 'help', _ ("print this help")),
76 ('FILE', 'o', 'output', _ ("write output to FILE")),
77 ('', 'E', 'pre-process', _ ("only pre-process")),
78 ('', 'V', 'verbose', _ ("be verbose")),
79 ('', 'v', 'version', _ ("print version number")),
80 ('', 'w', 'warranty', _ ("show warranty and copyright")),
84 ################################################################
85 # lilylib.py -- options and stuff
87 # source file of the GNU LilyPond music typesetter
89 # Handle bug in Python 1.6-2.1
91 # there are recursion limits for some patterns in Python 1.6 til 2.1.
92 # fix this by importing pre instead. Fix by Mats.
94 # todo: should check Python version first.
102 # Attempt to fix problems with limited stack size set by Python!
103 # Sets unlimited stack size. Note that the resource module only
104 # is available on UNIX.
107 resource
.setrlimit (resource
.RLIMIT_STACK
, (-1, -1))
113 gettext
.bindtextdomain ('lilypond', localedir
)
114 gettext
.textdomain ('lilypond')
120 program_version
= '@TOPLEVEL_VERSION@'
121 if program_version
== '@' + 'TOPLEVEL_VERSION' + '@':
122 program_version
= '1.5.54'
125 sys
.stdout
.write ('%s (GNU LilyPond) %s\n' % (program_name
, program_version
))
129 sys
.stdout
.write ('\n')
130 sys
.stdout
.write (_ ('Copyright (c) %s by') % '2001--2005')
131 sys
.stdout
.write ('\n')
132 sys
.stdout
.write (' Han-Wen Nienhuys')
133 sys
.stdout
.write (' Jan Nieuwenhuizen')
134 sys
.stdout
.write ('\n\n')
135 sys
.stdout
.write (_ ("Distributed under terms of the GNU General Public License."))
136 sys
.stdout
.write (_ ("It comes with NO WARRANTY."))
137 sys
.stdout
.write ('\n')
140 errorport
.write (s
+ '\n')
143 progress (_ ("warning: ") + s
)
145 def user_error (s
, e
=1):
146 errorport
.write (program_name
+ ":" + _ ("error: ") + s
+ '\n')
150 '''Report the error S. Exit by raising an exception. Please
151 do not abuse by trying to catch this error. If you do not want
152 a stack trace, write to the output directly.
160 progress (_ ("error: ") + s
)
161 raise _ ("Exiting ... ")
163 def getopt_args (opts
):
164 '''Construct arguments (LONG, SHORT) for getopt from list of options.'''
179 def option_help_str (o
):
180 '''Transform one option description (4-tuple ) into neatly formatted string'''
198 return ' ' + sh
+ sep
+ long + arg
201 def options_help_str (opts
):
202 '''Convert a list of options into a neatly formatted string'''
208 s
= option_help_str (o
)
209 strs
.append ((s
, o
[3]))
215 str = str + '%s%s%s\n' % (s
[0], ' ' * (w
- len(s
[0]) + 3), s
[1])
219 ls
= [(_ ("Usage: %s [OPTIONS]... FILE") % program_name
),
225 (options_help_str (option_definitions
)),
227 (_ ("Report bugs to %s.") % 'bug-lilypond@gnu.org'),
229 map (sys
.stdout
.write
, ls
)
233 Create a temporary directory, and return its name.
236 if not keep_temp_dir_p
:
237 temp_dir
= tempfile
.mktemp (program_name
)
239 os
.mkdir (temp_dir
, 0777)
246 def system (cmd
, ignore_error
= 0, quiet
=0):
247 """Run CMD. If IGNORE_ERROR is set, don't complain when CMD returns non zero.
255 progress (_ ("Invoking `%s\'") % cmd
)
259 name
= re
.match ('[ \t]*([^ \t]*)', cmd
).group (1)
260 msg
= name
+ ': ' + _ ("command exited with value %d") % st
263 warning (msg
+ ' ' + _ ("(ignored)") + ' ')
271 if not keep_temp_dir_p
:
273 progress (_ ("Cleaning %s...") % temp_dir
)
274 shutil
.rmtree (temp_dir
)
277 def strip_extension (f
, ext
):
278 (p
, e
) = os
.path
.splitext (f
)
284 def cp_to_dir (pattern
, dir):
285 "Copy files matching re PATTERN from cwd to DIR"
286 # Duh. Python style portable: cp *.EXT OUTDIR
287 # system ('cp *.%s %s' % (ext, outdir), 1)
288 files
= filter (lambda x
, p
=pattern
: re
.match (p
, x
), os
.listdir ('.'))
289 map (lambda x
, d
=dir: shutil
.copy2 (x
, os
.path
.join (d
, x
)), files
)
292 # Python < 1.5.2 compatibility
294 # On most platforms, this is equivalent to
295 #`normpath(join(os.getcwd()), PATH)'. *Added in Python version 1.5.2*
296 if os
.path
.__dict
__.has_key ('abspath'):
297 abspath
= os
.path
.abspath
300 return os
.path
.normpath (os
.path
.join (os
.getcwd (), path
))
302 if os
.__dict
__.has_key ('makedirs'):
303 makedirs
= os
.makedirs
305 def makedirs (dir, mode
=0777):
306 system ('mkdir -p %s' % dir)
309 def mkdir_p (dir, mode
=0777):
310 if not os
.path
.isdir (dir):
314 # if set, LILYPONDPREFIX must take prevalence
315 # if datadir is not set, we're doing a build and LILYPONDPREFIX
316 datadir
= '@local_lilypond_datadir@'
318 if os
.environ
.has_key ('LILYPONDPREFIX') :
319 datadir
= os
.environ
['LILYPONDPREFIX']
321 datadir
= '@local_lilypond_datadir@'
324 while datadir
[-1] == os
.sep
:
325 datadir
= datadir
[:-1]
327 sys
.path
.insert (0, os
.path
.join (datadir
, 'python'))
329 ################################################################
340 return chr (i
+ ord ('A'))
343 actab
= {-2: 'eses', -1: 'es', 0 : '', 1: 'is', 2:'isis'}
345 def pitch_to_lily_string (tup
):
348 nm
= chr((n
+ 2) % 7 + ord ('a'))
366 def rat_simplify (r
):
377 def rat_multiply (a
,b
):
381 return rat_simplify ((x
*p
, y
*q
))
383 def rat_divide (a
,b
):
385 return rat_multiply (a
, (q
,p
))
398 return rat_simplify ((x
*q
+ p
*y
, y
*q
))
405 def rat_larger (a
,b
):
406 return rat_subtract (a
, b
)[0] > 0
408 def rat_subtract (a
,b
):
409 return rat_add (a
, rat_neg (b
))
411 def rat_to_duration (frac
):
414 while rat_larger (d
, frac
):
415 d
= rat_multiply (d
, (1,2))
418 frac
= rat_subtract (frac
, d
)
420 if frac
== rat_multiply (d
, (1,2)):
422 elif frac
== rat_multiply (d
, (3,4)):
435 def __init__ (self
,nums
):
438 return ' %{ FIXME: meter change %} '
441 def __init__ (self
, ch
):
447 def __init__ (self
,id):
449 self
.start_chord
= None
450 self
.end_chord
= None
452 def calculate (self
):
457 s
.note_suffix
= s
.note_suffix
+ '-('
458 e
.note_prefix
= e
.note_suffix
+ "-)"
460 sys
.stderr
.write ("\nOrphaned slur")
463 def __init__ (self
, n
):
468 self
.current_slurs
= []
471 def toggle_slur (self
, id):
473 for s
in self
.current_slurs
:
475 self
.current_slurs
.remove (s
)
476 s
.end_chord
= self
.chords
[-1]
479 s
.start_chord
= self
.chords
[-1]
480 self
.current_slurs
.append (s
)
481 self
.slurs
.append (s
)
483 def last_chord (self
):
484 if len (self
.chords
):
485 return self
.chords
[-1]
488 ch
.basic_duration
= 4
491 def add_chord (self
, ch
):
492 self
.chords
.append (ch
)
493 self
.entries
.append (ch
)
495 def add_nonchord (self
, nch
):
496 self
.entries
.append (nch
)
499 return 'staff%svoice%s ' % (encodeint (self
.staff
.number
) , encodeint(self
.number
))
503 #if not self.entries:
506 # return '\n%s = {}\n\n' % self.idstring ()
508 one_two
= ("One", "Two")
509 if self
.staff
.voices
[1 - self
.number
].entries
:
510 ln
= ln
+ '\\voice%s\n ' % one_two
[self
.number
]
511 for e
in self
.entries
:
514 str = str + ln
+ next
+ ' '
518 if len (ln
) +len (next
) > 72:
525 id = self
.idstring ()
527 str = '''%s = \\context Voice = %s {
534 def calculate_graces (self
):
537 for c
in self
.chords
:
538 if c
.grace
and not lastgr
:
539 c
.chord_prefix
= c
.chord_prefix
+ '\\grace { '
540 elif not c
.grace
and lastgr
:
541 lastc
.chord_suffix
= lastc
.chord_suffix
+ ' } '
545 def calculate (self
):
546 self
.calculate_graces ()
551 def __init__ (self
, cl
):
555 return '\\clef %s' % self
.type
557 key_sharps
= ('c', 'g', 'd', 'a', 'e', 'b', 'fis')
558 key_flats
= ('BUG', 'f', 'bes', 'es', 'as', 'des', 'ges')
561 def __init__ (self
, sharps
, flats
):
566 if self
.sharps
and self
.flats
:
567 k
= '\\keysignature %s ' % 'TODO'
569 k
= '\\key %s \major' % key_sharps
[self
.sharps
]
571 k
= '\\key %s \major' % key_flats
[self
.flats
]
575 def __init__ (self
, frac
):
579 return '\\time %d/%d' % (self
.frac
[0], self
.frac
[1])
594 def __init__ (self
, n
):
596 self
.voices
= (Voice (0), Voice (1))
605 for v
in self
.voices
:
610 #def set_clef (self, letter):
611 # clstr = clef_table[letter]
612 # self.voices[0].add_nonchord (Clef (clstr))
614 def calculate (self
):
615 for v
in self
.voices
:
619 return 'staff%s' % encodeint (self
.number
)
625 for v
in self
.voices
:
628 if v
== self
.voices
[0]:
630 refs
= refs
+ self
.clef
.dump ()
632 refs
= refs
+ self
.time
.dump ()
634 refs
= refs
+ self
.key
.dump ()
638 refs
= refs
+ '\n \\' + v
.idstring ()
640 %s = \context Staff = %s <<%s
643 ''' % (self
.idstring (), self
.idstring (), refs
)
647 def __init__ (self
, number
, base
, dots
):
650 self
.replaces
= tuplet_table
[number
]
656 length
= rat_multiply (length
, (3,2))
658 length
= rat_multiply (length
, (7,4))
660 length
= rat_multiply (length
, (1,self
.replaces
))
662 (nb
,nd
) =rat_to_duration (length
)
667 def add_chord (self
, ch
):
668 ch
.dots
= self
.note_dots
669 ch
.basic_duration
= self
.note_base
670 self
.chords
.append (ch
)
672 if len (self
.chords
) == 1:
673 ch
.chord_prefix
= '\\times %d/%d { ' % (self
.replaces
, self
.number
)
674 elif len (self
.chords
) == self
.number
:
675 ch
.chord_suffix
= ' }'
680 self
.multimeasure
= 0
682 self
.basic_duration
= 0
685 self
.chord_prefix
= ''
686 self
.chord_suffix
= ''
687 self
.note_prefix
= ''
688 self
.note_suffix
= ''
690 # maybe use import copy?
693 #for i in self.pitches:
694 # ch.pitches.append (i)
695 ch
.pitches
= self
.pitches
[:]
696 ch
.multimeasure
= self
.multimeasure
698 ch
.basic_duration
= self
.basic_duration
699 #for i in self.scripts:
700 # ch.scripts.append (i)
701 ch
.scripts
= self
.scripts
[:]
702 ch
.grace
= self
.grace
704 ch
.chord_prefix
= self
.chord_prefix
705 ch
.chord_suffix
= self
.chord_suffix
706 ch
.note_prefix
= self
.note_prefix
707 ch
.note_suffix
= self
.note_suffix
715 if self
.basic_duration
== 0.5:
718 sd
= '%d' % self
.basic_duration
719 sd
= sd
+ '.' * self
.dots
720 for p
in self
.pitches
:
723 str = str + pitch_to_lily_string (p
)
726 str = self
.note_prefix
+str + self
.note_suffix
728 if len (self
.pitches
) > 1:
730 elif self
.multimeasure
:
732 elif len (self
.pitches
) == 0:
736 for s
in self
.scripts
:
739 str = self
.chord_prefix
+ str + self
.chord_suffix
776 # http://www.arkkra.com/doc/uguide/contexts.html
791 def __init__ (self
, lines
):
792 self
.parse_function
= self
.parse_context_music
794 self
.current_voices
= []
795 self
.forced_duration
= None
798 self
.tuplets_expected
= 0
806 def parse_compound_location (self
, line
):
807 colon
= string
.index (line
, ':')
810 line
= line
[colon
+ 1:]
812 self
.current_voices
= []
813 ##self.current_staffs = []
814 map (self
.parse_location
, string
.split (s
, '&'))
817 def parse_location (self
, line
):
818 m
= re
.match ('^([-,0-9]+) *([-,0-9]*)', string
.lstrip (line
))
820 def range_list_to_idxs (s
):
830 def range_to_list (s
):
831 if string
.find (s
, '-') >= 0:
833 l
= map (string
.lstrip
,
834 string
.split (s
, '-'))
835 r
= range (string
.atoi (l
[0]) - 1,
838 r
= (string
.atoi (s
) - 1,)
841 ranges
= string
.split (s
, ',')
842 l
= flatten (map (range_to_list
, ranges
))
846 staff_idxs
= range_list_to_idxs (m
.group (1))
848 voice_idxs
= range_list_to_idxs (m
.group (2))
852 while s
> len (self
.staffs
) - 1:
853 self
.staffs
.append (Staff (s
))
855 self
.current_voices
.append (self
.staffs
[s
].voices
[v
])
857 def parse_note (self
, line
):
860 name
= (ord (line
[0]) - ord ('a') + 5) % 7
861 # FIXME: does key play any role in this?
863 debug ('NOTE: ' + `line`
)
864 line
= string
.lstrip (line
[1:])
866 if len (line
) > 1 and line
[:2] == '//':
870 alteration
= alteration
+ 1
872 alteration
= alteration
- 1
879 line
= string
.lstrip (line
[1:])
880 return (oct, name
, alteration
)
882 def parse_chord (self
, line
):
883 debug ('CHORD: ' + line
)
884 line
= string
.lstrip (line
)
887 #ch = self.current_voices[0].last_chord ()
888 ch
= self
.last_chord
.copy ()
890 m
= re
.match ('^([0-9]+)([.]*)', line
)
892 ch
.basic_duration
= string
.atoi (m
.group (1))
893 line
= line
[len (m
.group (1)):]
895 ch
.dots
= len (m
.group (2))
896 line
= line
[len (m
.group (2)):]
898 #ch.basic_duration = self.current_voices[0].last_chord ().basic_duration
899 ch
.basic_duration
= self
.last_chord
.basic_duration
901 line
= string
.lstrip (line
)
902 if len (line
) > 1 and line
[:2] == '//':
906 debug ('nline: ' + line
)
907 #ch = self.current_voices[0].last_chord ()
908 n
= self
.last_chord
.copy ()
909 n
.basic_duration
= ch
.basic_duration
912 debug ('ch.pitsen:' + `ch
.pitches`
)
913 debug ('ch.dur:' + `ch
.basic_duration`
)
915 debug ('eline: ' + line
)
918 if len (line
) > 1 and line
[:2] == '//':
921 elif line
[:1] == 'mr':
924 elif line
[:1] == 'ms':
927 elif line
[0] in 'rs':
930 elif line
[0] in 'abcdefg':
931 m
= re
.match ('([a-g][-#&+]*)', line
)
932 l
= len (m
.group (1))
933 pitch
= self
.parse_note (line
[:l
])
934 debug ('PITCH: ' + `pitch`
)
935 ch
.pitches
.append (pitch
)
941 line
= string
.lstrip (line
)
942 debug ('CUR-VOICES: ' + `self
.current_voices`
)
943 map (lambda x
, ch
=ch
: x
.add_chord (ch
), self
.current_voices
)
946 def parse_lyrics_location (self
, line
):
947 line
= line
.lstrip (line
)
949 m
= re
.match ('^(between[ \t]+)', line
)
951 line
= line
[len (m
.group (1)):]
954 m
= re
.match ('^(above [ \t]+)', line
)
956 line
= line
[len (m
.group (1)):]
961 def parse_voice (self
, line
):
962 line
= string
.lstrip (line
)
963 # `;' is not a separator, chords end with ';'
964 chords
= string
.split (line
, ';')[:-1]
965 # mup resets default duration and pitch each bar
966 self
.last_chord
= Chord ()
967 self
.last_chord
.basic_duration
= 4
968 map (self
.parse_chord
, chords
)
970 def init_context_header (self
, line
):
971 self
.parse_function
= self
.parse_context_header
973 def parse_context_header (self
, line
):
974 debug ('header: ' + line
)
977 def init_context_footer (self
, line
):
978 self
.parse_function
= self
.parse_context_footer
980 def parse_context_footer (self
, line
):
981 debug ('footer: ' + line
)
984 def init_context_header2 (self
, line
):
985 self
.parse_function
= self
.parse_context_header2
987 def parse_context_header2 (self
, line
):
988 debug ('header2: ' + line
)
991 def init_context_footer2 (self
, line
):
992 self
.parse_function
= self
.parse_context_footer2
994 def parse_context_footer2 (self
, line
):
995 debug ('footer2: ' + line
)
998 def init_context_score (self
, line
):
999 self
.parse_function
= self
.parse_context_score
1001 def parse_context_score (self
, line
):
1002 debug ('score: ' + line
)
1003 line
= string
.lstrip (line
)
1004 # ugh: these (and lots more) should also be parsed in
1005 # context staff. we should have a class Staff_properties
1006 # and parse/set all those.
1007 m
= re
.match ('^(time[ \t]*=[ \t]*([0-9]+)[ \t]*/[ \t]*([0-9]+))', line
)
1009 line
= line
[len (m
.group (1)):]
1010 self
.time
= Time ((string
.atoi (m
.group (2)),
1011 string
.atoi (m
.group (3))))
1013 m
= re
.match ('^(key[ \t]*=[ \t]*([0-9]+)[ \t]*(#|@))', line
)
1015 line
= line
[len (m
.group (1)):]
1016 n
= string
.atoi (m
.group (2))
1017 if m
.group (3) == '#':
1018 self
.key
= Key (n
, 0)
1020 self
.key
= Key (0, n
)
1023 def init_context_staff (self
, line
):
1024 self
.parse_function
= self
.parse_context_staff
1026 def parse_context_staff (self
, line
):
1027 debug ('staff: ' + line
)
1030 def init_context_voice (self
, line
):
1031 self
.parse_function
= self
.parse_context_voice
1033 def parse_context_voice (self
, line
):
1034 debug ('voice: ' + line
)
1037 def init_context_grids (self
, line
):
1038 self
.parse_function
= self
.parse_context_grids
1040 def parse_context_grids (self
, line
):
1041 debug ('grids: ' + line
)
1044 def init_context_music (self
, line
):
1045 self
.parse_function
= self
.parse_context_music
1047 def parse_context_music (self
, line
):
1048 debug ('music: ' + line
)
1049 line
= string
.lstrip (line
)
1050 if line
and line
[0] in '0123456789':
1051 line
= self
.parse_compound_location (line
)
1052 self
.parse_voice (line
)
1054 m
= re
.match ('^(TODOlyrics[ \t]+)', line
)
1056 line
= line
[len (m
.group (1)):]
1057 self
.parse_lyrics_location (line
[7:])
1058 self
.parse_lyrics (line
)
1062 def parse (self
, lines
):
1063 # shortcut: set to official mup maximum (duh)
1064 # self.set_staffs (40)
1066 debug ('LINE: ' + `line`
)
1067 m
= re
.match ('^([a-z]+2?)', line
)
1071 if word
in contexts
:
1072 eval ('self.init_context_%s (line)' % word
)
1075 warning (_ ("no such context: %s") % word
)
1078 debug ('FUNC: ' + `self
.parse_function`
)
1079 self
.parse_function (line
)
1081 for c
in self
.staffs
:
1083 if not c
.clef
and self
.clef
:
1085 if not c
.time
and self
.time
:
1087 if not c
.key
and self
.key
:
1095 for s
in self
.staffs
:
1096 str = str + s
.dump ()
1097 refs
= refs
+ '\n \\' + s
.idstring ()
1111 class Pre_processor
:
1112 def __init__ (self
, raw_lines
):
1115 self
.process_function
= self
.process_line
1116 self
.macro_name
= ''
1117 self
.macro_body
= ''
1118 self
.process (raw_lines
)
1120 def process_line (self
, line
):
1122 m
= re
.match ('^([ \t]*([a-zA-Z]+))', line
)
1126 debug ('MACRO?: ' + `word`
)
1127 if word
in pre_processor_commands
:
1128 line
= line
[len (m
.group (1)):]
1129 eval ('self.process_macro_%s (line)' % word
)
1132 if macros
.has_key (word
):
1133 s
= macros
[word
] + line
[len (m
.group (1)):]
1134 if not self
.active
[-1]:
1138 def process_macro_body (self
, line
):
1140 # dig this: mup allows ifdefs inside macro bodies
1141 s
= self
.process_line (line
)
1142 m
= re
.match ('(.*[^\\\\])(@)(.*)', s
)
1144 self
.macro_body
= self
.macro_body
+ '\n' + m
.group (1)
1145 macros
[self
.macro_name
] = self
.macro_body
1146 debug ('MACROS: ' + `macros`
)
1147 # don't do nested multi-line defines
1148 self
.process_function
= self
.process_line
1154 self
.macro_body
= self
.macro_body
+ '\n' + s
1158 # duh: mup is strictly line-based, except for `define',
1159 # which is `@' terminated and may span several lines
1160 def process_macro_define (self
, line
):
1162 # don't define new macros in unactive areas
1163 if not self
.active
[-1]:
1165 m
= re
.match ('^[ \t]*([a-zA-Z][a-zA-Z1-9_]*)(([^@]*)|(\\\\@))(@)?', line
)
1173 debug ('MACROS: ' + `macros`
)
1175 # To support nested multi-line define's
1176 # process_function and macro_name, macro_body
1177 # should become lists (stacks)
1178 # The mup manual is undetermined on this
1179 # and I haven't seen examples doing it.
1181 # don't do nested multi-line define's
1183 self
.macro_body
= m
.group (2)
1185 self
.macro_body
= ''
1187 self
.process_function
= self
.process_macro_body
1189 def process_macro_ifdef (self
, line
):
1190 m
= re
.match ('^[ \t]*([a-zA-Z][a-zA-Z1-9_]*)', line
)
1193 active
= self
.active
[-1] and macros
.has_key (m
.group (1))
1194 debug ('ACTIVE: %d' % active
)
1195 self
.active
.append (active
)
1197 def process_macro_ifndef (self
, line
):
1198 m
= re
.match ('^[ \t]*([a-zA-Z][a-zA-Z1-9_]*)', line
)
1200 active
= self
.active
[-1] and not macros
.has_key (m
.group (1))
1201 self
.active
.append (active
)
1203 def process_macro_else (self
, line
):
1205 self
.active
[-1] = not self
.active
[-1]
1207 def process_macro_endif (self
, line
):
1208 self
.active
= self
.active
[:-1]
1210 def process (self
, raw_lines
):
1212 for line
in raw_lines
:
1213 ls
= string
.split (self
.process_function (line
), '\n')
1216 s
= s
+ string
.rstrip (i
)
1217 if s
and s
[-1] == '\\':
1218 s
= string
.rstrip (s
[:-1])
1220 self
.lines
.append (s
)
1225 only_pre_process_p
= 0
1228 progress ('DEBUG: ' + s
)
1231 if verbose_p
or debug_p
:
1232 progress ('SKIPPING: ' + s
)
1234 (sh
, long) = getopt_args (__main__
.option_definitions
)
1236 (options
, files
) = getopt
.getopt (sys
.argv
[1:], sh
, long)
1242 pre_processor_commands
= (
1255 elif o
== '--debug' or o
== '-d':
1257 elif o
== '--define' or o
== '-D':
1258 if string
.find (a
, '=') >= 0:
1259 (n
, e
) = string
.split (a
, '=')
1264 elif o
== '--pre-process' or o
== '-E':
1265 only_pre_process_p
= 1
1266 elif o
== '--help' or o
== '-h':
1269 elif o
== '--verbose' or o
== '-V':
1271 elif o
== '--version' or o
== '-v':
1274 elif o
== '--output' or o
== '-o':
1280 # writes to stdout for help2man
1283 # sys.stdout.flush ()
1285 # handy emacs testing
1287 # files = ['template.mup']
1296 elif f
and not os
.path
.isfile (f
):
1297 f
= strip_extension (f
, '.mup') + '.mup'
1300 progress ( _("Processing `%s'..." % f
))
1301 raw_lines
= h
.readlines ()
1302 p
= Pre_processor (raw_lines
)
1303 if only_pre_process_p
:
1305 output
= os
.path
.basename (re
.sub ('(?i).mup$', '.mpp', f
))
1307 e
= Parser (p
.lines
)
1309 output
= os
.path
.basename (re
.sub ('(?i).mup$', '.ly', f
))
1311 output
= os
.path
.basename (f
+ '.ly')
1317 out_h
= open (output
, 'w')
1319 progress (_ ("Writing `%s'...") % output
)
1321 tag
= '%% Lily was here -- automatically converted by %s from %s' % ( program_name
, f
)
1322 if only_pre_process_p
:
1324 ly
= string
.join (p
.lines
, '\n')
1326 ly
= tag
+ '\n\n' + e
.dump ()