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
= 'mup2ly'
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', _ ("this help")),
76 ('FILE', 'o', 'output', _ ("write output to FILE")),
77 ('', 'E', 'pre-process', _ ("only pre-process")),
78 ('', 'V', 'verbose', _ ("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--2002'))
131 sys
.stdout
.write ('\n')
132 sys
.stdout
.write (' Han-Wen Nienhuys')
133 sys
.stdout
.write (' Jan Nieuwenhuizen')
134 sys
.stdout
.write ('\n')
135 sys
.stdout
.write (_ (r
'''
136 Distributed under terms of the GNU General Public License. It comes with
138 sys
.stdout
.write ('\n')
141 errorport
.write (s
+ '\n')
144 progress (_ ("warning: ") + s
)
146 def user_error (s
, e
=1):
147 errorport
.write (program_name
+ ":" + _ ("error: ") + s
+ '\n')
151 '''Report the error S. Exit by raising an exception. Please
152 do not abuse by trying to catch this error. If you do not want
153 a stack trace, write to the output directly.
161 progress (_ ("error: ") + s
)
162 raise _ ("Exiting ... ")
164 def getopt_args (opts
):
165 '''Construct arguments (LONG, SHORT) for getopt from list of options.'''
180 def option_help_str (o
):
181 '''Transform one option description (4-tuple ) into neatly formatted string'''
199 return ' ' + sh
+ sep
+ long + arg
202 def options_help_str (opts
):
203 '''Convert a list of options into a neatly formatted string'''
209 s
= option_help_str (o
)
210 strs
.append ((s
, o
[3]))
216 str = str + '%s%s%s\n' % (s
[0], ' ' * (w
- len(s
[0]) + 3), s
[1])
220 ls
= [(_ ("Usage: %s [OPTION]... FILE") % program_name
),
226 (options_help_str (option_definitions
)),
228 (_ ("Report bugs to %s") % 'bug-lilypond@gnu.org'),
230 map (sys
.stdout
.write
, ls
)
234 Create a temporary directory, and return its name.
237 if not keep_temp_dir_p
:
238 temp_dir
= tempfile
.mktemp (program_name
)
240 os
.mkdir (temp_dir
, 0777)
247 def system (cmd
, ignore_error
= 0, quiet
=0):
248 """Run CMD. If IGNORE_ERROR is set, don't complain when CMD returns non zero.
256 progress (_ ("Invoking `%s\'") % cmd
)
260 name
= re
.match ('[ \t]*([^ \t]*)', cmd
).group (1)
261 msg
= name
+ ': ' + _ ("command exited with value %d") % st
264 warning (msg
+ ' ' + _ ("(ignored)") + ' ')
272 if not keep_temp_dir_p
:
274 progress (_ ("Cleaning %s...") % temp_dir
)
275 shutil
.rmtree (temp_dir
)
278 def strip_extension (f
, ext
):
279 (p
, e
) = os
.path
.splitext (f
)
285 def cp_to_dir (pattern
, dir):
286 "Copy files matching re PATTERN from cwd to DIR"
287 # Duh. Python style portable: cp *.EXT OUTDIR
288 # system ('cp *.%s %s' % (ext, outdir), 1)
289 files
= filter (lambda x
, p
=pattern
: re
.match (p
, x
), os
.listdir ('.'))
290 map (lambda x
, d
=dir: shutil
.copy2 (x
, os
.path
.join (d
, x
)), files
)
293 # Python < 1.5.2 compatibility
295 # On most platforms, this is equivalent to
296 #`normpath(join(os.getcwd()), PATH)'. *Added in Python version 1.5.2*
297 if os
.path
.__dict
__.has_key ('abspath'):
298 abspath
= os
.path
.abspath
301 return os
.path
.normpath (os
.path
.join (os
.getcwd (), path
))
303 if os
.__dict
__.has_key ('makedirs'):
304 makedirs
= os
.makedirs
306 def makedirs (dir, mode
=0777):
307 system ('mkdir -p %s' % dir)
310 def mkdir_p (dir, mode
=0777):
311 if not os
.path
.isdir (dir):
315 # if set, LILYPONDPREFIX must take prevalence
316 # if datadir is not set, we're doing a build and LILYPONDPREFIX
317 datadir
= '@local_lilypond_datadir@'
319 if os
.environ
.has_key ('LILYPONDPREFIX') :
320 datadir
= os
.environ
['LILYPONDPREFIX']
322 datadir
= '@local_lilypond_datadir@'
325 while datadir
[-1] == os
.sep
:
326 datadir
= datadir
[:-1]
328 sys
.path
.insert (0, os
.path
.join (datadir
, 'python'))
330 ################################################################
341 return chr (i
+ ord ('A'))
344 actab
= {-2: 'eses', -1: 'es', 0 : '', 1: 'is', 2:'isis'}
346 def pitch_to_lily_string (tup
):
349 nm
= chr((n
+ 2) % 7 + ord ('a'))
367 def rat_simplify (r
):
378 def rat_multiply (a
,b
):
382 return rat_simplify ((x
*p
, y
*q
))
384 def rat_divide (a
,b
):
386 return rat_multiply (a
, (q
,p
))
399 return rat_simplify ((x
*q
+ p
*y
, y
*q
))
406 def rat_larger (a
,b
):
407 return rat_subtract (a
, b
)[0] > 0
409 def rat_subtract (a
,b
):
410 return rat_add (a
, rat_neg (b
))
412 def rat_to_duration (frac
):
415 while rat_larger (d
, frac
):
416 d
= rat_multiply (d
, (1,2))
419 frac
= rat_subtract (frac
, d
)
421 if frac
== rat_multiply (d
, (1,2)):
423 elif frac
== rat_multiply (d
, (3,4)):
436 def __init__ (self
,nums
):
439 return ' %{ FIXME: meter change %} '
442 def __init__ (self
, ch
):
448 def __init__ (self
,id):
450 self
.start_chord
= None
451 self
.end_chord
= None
453 def calculate (self
):
458 s
.note_suffix
= s
.note_suffix
+ '-('
459 e
.note_prefix
= e
.note_suffix
+ "-)"
461 sys
.stderr
.write ("\nOrphaned slur")
464 def __init__ (self
, n
):
469 self
.current_slurs
= []
472 def toggle_slur (self
, id):
474 for s
in self
.current_slurs
:
476 self
.current_slurs
.remove (s
)
477 s
.end_chord
= self
.chords
[-1]
480 s
.start_chord
= self
.chords
[-1]
481 self
.current_slurs
.append (s
)
482 self
.slurs
.append (s
)
484 def last_chord (self
):
485 if len (self
.chords
):
486 return self
.chords
[-1]
489 ch
.basic_duration
= 4
492 def add_chord (self
, ch
):
493 self
.chords
.append (ch
)
494 self
.entries
.append (ch
)
496 def add_nonchord (self
, nch
):
497 self
.entries
.append (nch
)
500 return 'staff%svoice%s ' % (encodeint (self
.staff
.number
) , encodeint(self
.number
))
504 #if not self.entries:
507 # return '\n%s = {}\n\n' % self.idstring ()
509 one_two
= ("One", "Two")
510 if self
.staff
.voices
[1 - self
.number
].entries
:
511 ln
= ln
+ '\\voice%s\n ' % one_two
[self
.number
]
512 for e
in self
.entries
:
515 str = str + ln
+ next
+ ' '
519 if len (ln
) +len (next
) > 72:
526 id = self
.idstring ()
528 str = '''%s = \\context Voice = %s \\notes {
535 def calculate_graces (self
):
538 for c
in self
.chords
:
539 if c
.grace
and not lastgr
:
540 c
.chord_prefix
= c
.chord_prefix
+ '\\grace { '
541 elif not c
.grace
and lastgr
:
542 lastc
.chord_suffix
= lastc
.chord_suffix
+ ' } '
546 def calculate (self
):
547 self
.calculate_graces ()
552 def __init__ (self
, cl
):
556 return '\\clef %s' % self
.type
558 key_sharps
= ('c', 'g', 'd', 'a', 'e', 'b', 'fis')
559 key_flats
= ('BUG', 'f', 'bes', 'es', 'as', 'des', 'ges')
562 def __init__ (self
, sharps
, flats
):
567 if self
.sharps
and self
.flats
:
568 k
= '\\keysignature %s ' % 'TODO'
570 k
= '\\notes\\key %s \major' % key_sharps
[self
.sharps
]
572 k
= '\\notes\\key %s \major' % key_flats
[self
.flats
]
576 def __init__ (self
, frac
):
580 return '\\time %d/%d' % (self
.frac
[0], self
.frac
[1])
595 def __init__ (self
, n
):
597 self
.voices
= (Voice (0), Voice (1))
606 for v
in self
.voices
:
611 #def set_clef (self, letter):
612 # clstr = clef_table[letter]
613 # self.voices[0].add_nonchord (Clef (clstr))
615 def calculate (self
):
616 for v
in self
.voices
:
620 return 'staff%s' % encodeint (self
.number
)
626 for v
in self
.voices
:
629 if v
== self
.voices
[0]:
631 refs
= refs
+ self
.clef
.dump ()
633 refs
= refs
+ self
.time
.dump ()
635 refs
= refs
+ self
.key
.dump ()
639 refs
= refs
+ '\n \\' + v
.idstring ()
641 %s = \context Staff = %s <%s
644 ''' % (self
.idstring (), self
.idstring (), refs
)
648 def __init__ (self
, number
, base
, dots
):
651 self
.replaces
= tuplet_table
[number
]
657 length
= rat_multiply (length
, (3,2))
659 length
= rat_multiply (length
, (7,4))
661 length
= rat_multiply (length
, (1,self
.replaces
))
663 (nb
,nd
) =rat_to_duration (length
)
668 def add_chord (self
, ch
):
669 ch
.dots
= self
.note_dots
670 ch
.basic_duration
= self
.note_base
671 self
.chords
.append (ch
)
673 if len (self
.chords
) == 1:
674 ch
.chord_prefix
= '\\times %d/%d { ' % (self
.replaces
, self
.number
)
675 elif len (self
.chords
) == self
.number
:
676 ch
.chord_suffix
= ' }'
681 self
.multimeasure
= 0
683 self
.basic_duration
= 0
686 self
.chord_prefix
= ''
687 self
.chord_suffix
= ''
688 self
.note_prefix
= ''
689 self
.note_suffix
= ''
691 # maybe use import copy?
694 #for i in self.pitches:
695 # ch.pitches.append (i)
696 ch
.pitches
= self
.pitches
[:]
697 ch
.multimeasure
= self
.multimeasure
699 ch
.basic_duration
= self
.basic_duration
700 #for i in self.scripts:
701 # ch.scripts.append (i)
702 ch
.scripts
= self
.scripts
[:]
703 ch
.grace
= self
.grace
705 ch
.chord_prefix
= self
.chord_prefix
706 ch
.chord_suffix
= self
.chord_suffix
707 ch
.note_prefix
= self
.note_prefix
708 ch
.note_suffix
= self
.note_suffix
716 if self
.basic_duration
== 0.5:
719 sd
= '%d' % self
.basic_duration
720 sd
= sd
+ '.' * self
.dots
721 for p
in self
.pitches
:
724 str = str + pitch_to_lily_string (p
)
727 str = self
.note_prefix
+str + self
.note_suffix
729 if len (self
.pitches
) > 1:
731 elif self
.multimeasure
:
733 elif len (self
.pitches
) == 0:
737 for s
in self
.scripts
:
740 str = self
.chord_prefix
+ str + self
.chord_suffix
777 # http://www.arkkra.com/doc/uguide/contexts.html
792 def __init__ (self
, lines
):
793 self
.parse_function
= self
.parse_context_music
795 self
.current_voices
= []
796 self
.forced_duration
= None
799 self
.tuplets_expected
= 0
807 def parse_compound_location (self
, line
):
808 colon
= string
.index (line
, ':')
811 line
= line
[colon
+ 1:]
813 self
.current_voices
= []
814 ##self.current_staffs = []
815 map (self
.parse_location
, string
.split (s
, '&'))
818 def parse_location (self
, line
):
819 m
= re
.match ('^([-,0-9]+) *([-,0-9]*)', string
.lstrip (line
))
821 def range_list_to_idxs (s
):
831 def range_to_list (s
):
832 if string
.find (s
, '-') >= 0:
834 l
= map (string
.lstrip
,
835 string
.split (s
, '-'))
836 r
= range (string
.atoi (l
[0]) - 1,
839 r
= (string
.atoi (s
) - 1,)
842 ranges
= string
.split (s
, ',')
843 l
= flatten (map (range_to_list
, ranges
))
847 staff_idxs
= range_list_to_idxs (m
.group (1))
849 voice_idxs
= range_list_to_idxs (m
.group (2))
853 while s
> len (self
.staffs
) - 1:
854 self
.staffs
.append (Staff (s
))
856 self
.current_voices
.append (self
.staffs
[s
].voices
[v
])
858 def parse_note (self
, line
):
861 name
= (ord (line
[0]) - ord ('a') + 5) % 7
862 # FIXME: does key play any role in this?
864 debug ('NOTE: ' + `line`
)
865 line
= string
.lstrip (line
[1:])
867 if len (line
) > 1 and line
[:2] == '//':
871 alteration
= alteration
+ 1
873 alteration
= alteration
- 1
880 line
= string
.lstrip (line
[1:])
881 return (oct, name
, alteration
)
883 def parse_chord (self
, line
):
884 debug ('CHORD: ' + line
)
885 line
= string
.lstrip (line
)
888 #ch = self.current_voices[0].last_chord ()
889 ch
= self
.last_chord
.copy ()
891 m
= re
.match ('^([0-9]+)([.]*)', line
)
893 ch
.basic_duration
= string
.atoi (m
.group (1))
894 line
= line
[len (m
.group (1)):]
896 ch
.dots
= len (m
.group (2))
897 line
= line
[len (m
.group (2)):]
899 #ch.basic_duration = self.current_voices[0].last_chord ().basic_duration
900 ch
.basic_duration
= self
.last_chord
.basic_duration
902 line
= string
.lstrip (line
)
903 if len (line
) > 1 and line
[:2] == '//':
907 debug ('nline: ' + line
)
908 #ch = self.current_voices[0].last_chord ()
909 n
= self
.last_chord
.copy ()
910 n
.basic_duration
= ch
.basic_duration
913 debug ('ch.pitsen:' + `ch
.pitches`
)
914 debug ('ch.dur:' + `ch
.basic_duration`
)
916 debug ('eline: ' + line
)
919 if len (line
) > 1 and line
[:2] == '//':
922 elif line
[:1] == 'mr':
925 elif line
[:1] == 'ms':
928 elif line
[0] in 'rs':
931 elif line
[0] in 'abcdefg':
932 m
= re
.match ('([a-g][-#&+]*)', line
)
933 l
= len (m
.group (1))
934 pitch
= self
.parse_note (line
[:l
])
935 debug ('PITCH: ' + `pitch`
)
936 ch
.pitches
.append (pitch
)
942 line
= string
.lstrip (line
)
943 debug ('CUR-VOICES: ' + `self
.current_voices`
)
944 map (lambda x
, ch
=ch
: x
.add_chord (ch
), self
.current_voices
)
947 def parse_lyrics_location (self
, line
):
948 line
= line
.lstrip (line
)
950 m
= re
.match ('^(between[ \t]+)', line
)
952 line
= line
[len (m
.group (1)):]
955 m
= re
.match ('^(above [ \t]+)', line
)
957 line
= line
[len (m
.group (1)):]
962 def parse_voice (self
, line
):
963 line
= string
.lstrip (line
)
964 # `;' is not a separator, chords end with ';'
965 chords
= string
.split (line
, ';')[:-1]
966 # mup resets default duration and pitch each bar
967 self
.last_chord
= Chord ()
968 self
.last_chord
.basic_duration
= 4
969 map (self
.parse_chord
, chords
)
971 def init_context_header (self
, line
):
972 self
.parse_function
= self
.parse_context_header
974 def parse_context_header (self
, line
):
975 debug ('header: ' + line
)
978 def init_context_footer (self
, line
):
979 self
.parse_function
= self
.parse_context_footer
981 def parse_context_footer (self
, line
):
982 debug ('footer: ' + line
)
985 def init_context_header2 (self
, line
):
986 self
.parse_function
= self
.parse_context_header2
988 def parse_context_header2 (self
, line
):
989 debug ('header2: ' + line
)
992 def init_context_footer2 (self
, line
):
993 self
.parse_function
= self
.parse_context_footer2
995 def parse_context_footer2 (self
, line
):
996 debug ('footer2: ' + line
)
999 def init_context_score (self
, line
):
1000 self
.parse_function
= self
.parse_context_score
1002 def parse_context_score (self
, line
):
1003 debug ('score: ' + line
)
1004 line
= string
.lstrip (line
)
1005 # ugh: these (and lots more) should also be parsed in
1006 # context staff. we should have a class Staff_properties
1007 # and parse/set all those.
1008 m
= re
.match ('^(time[ \t]*=[ \t]*([0-9]+)[ \t]*/[ \t]*([0-9]+))', line
)
1010 line
= line
[len (m
.group (1)):]
1011 self
.time
= Time ((string
.atoi (m
.group (2)),
1012 string
.atoi (m
.group (3))))
1014 m
= re
.match ('^(key[ \t]*=[ \t]*([0-9]+)[ \t]*(#|@))', line
)
1016 line
= line
[len (m
.group (1)):]
1017 n
= string
.atoi (m
.group (2))
1018 if m
.group (3) == '#':
1019 self
.key
= Key (n
, 0)
1021 self
.key
= Key (0, n
)
1024 def init_context_staff (self
, line
):
1025 self
.parse_function
= self
.parse_context_staff
1027 def parse_context_staff (self
, line
):
1028 debug ('staff: ' + line
)
1031 def init_context_voice (self
, line
):
1032 self
.parse_function
= self
.parse_context_voice
1034 def parse_context_voice (self
, line
):
1035 debug ('voice: ' + line
)
1038 def init_context_grids (self
, line
):
1039 self
.parse_function
= self
.parse_context_grids
1041 def parse_context_grids (self
, line
):
1042 debug ('grids: ' + line
)
1045 def init_context_music (self
, line
):
1046 self
.parse_function
= self
.parse_context_music
1048 def parse_context_music (self
, line
):
1049 debug ('music: ' + line
)
1050 line
= string
.lstrip (line
)
1051 if line
and line
[0] in '0123456789':
1052 line
= self
.parse_compound_location (line
)
1053 self
.parse_voice (line
)
1055 m
= re
.match ('^(TODOlyrics[ \t]+)', line
)
1057 line
= line
[len (m
.group (1)):]
1058 self
.parse_lyrics_location (line
[7:])
1059 self
.parse_lyrics (line
)
1063 def parse (self
, lines
):
1064 # shortcut: set to official mup maximum (duh)
1065 # self.set_staffs (40)
1067 debug ('LINE: ' + `line`
)
1068 m
= re
.match ('^([a-z]+2?)', line
)
1072 if word
in contexts
:
1073 eval ('self.init_context_%s (line)' % word
)
1076 warning (_ ("no such context: %s") % word
)
1079 debug ('FUNC: ' + `self
.parse_function`
)
1080 self
.parse_function (line
)
1082 for c
in self
.staffs
:
1084 if not c
.clef
and self
.clef
:
1086 if not c
.time
and self
.time
:
1088 if not c
.key
and self
.key
:
1096 for s
in self
.staffs
:
1097 str = str + s
.dump ()
1098 refs
= refs
+ '\n \\' + s
.idstring ()
1112 class Pre_processor
:
1113 def __init__ (self
, raw_lines
):
1116 self
.process_function
= self
.process_line
1117 self
.macro_name
= ''
1118 self
.macro_body
= ''
1119 self
.process (raw_lines
)
1121 def process_line (self
, line
):
1123 m
= re
.match ('^([ \t]*([a-zA-Z]+))', line
)
1127 debug ('MACRO?: ' + `word`
)
1128 if word
in pre_processor_commands
:
1129 line
= line
[len (m
.group (1)):]
1130 eval ('self.process_macro_%s (line)' % word
)
1133 if macros
.has_key (word
):
1134 s
= macros
[word
] + line
[len (m
.group (1)):]
1135 if not self
.active
[-1]:
1139 def process_macro_body (self
, line
):
1141 # dig this: mup allows ifdefs inside macro bodies
1142 s
= self
.process_line (line
)
1143 m
= re
.match ('(.*[^\\\\])(@)(.*)', s
)
1145 self
.macro_body
= self
.macro_body
+ '\n' + m
.group (1)
1146 macros
[self
.macro_name
] = self
.macro_body
1147 debug ('MACROS: ' + `macros`
)
1148 # don't do nested multi-line defines
1149 self
.process_function
= self
.process_line
1155 self
.macro_body
= self
.macro_body
+ '\n' + s
1159 # duh: mup is strictly line-based, except for `define',
1160 # which is `@' terminated and may span several lines
1161 def process_macro_define (self
, line
):
1163 # don't define new macros in unactive areas
1164 if not self
.active
[-1]:
1166 m
= re
.match ('^[ \t]*([a-zA-Z][a-zA-Z1-9_]*)(([^@]*)|(\\\\@))(@)?', line
)
1174 debug ('MACROS: ' + `macros`
)
1176 # To support nested multi-line define's
1177 # process_function and macro_name, macro_body
1178 # should become lists (stacks)
1179 # The mup manual is undetermined on this
1180 # and I haven't seen examples doing it.
1182 # don't do nested multi-line define's
1184 self
.macro_body
= m
.group (2)
1186 self
.macro_body
= ''
1188 self
.process_function
= self
.process_macro_body
1190 def process_macro_ifdef (self
, line
):
1191 m
= re
.match ('^[ \t]*([a-zA-Z][a-zA-Z1-9_]*)', line
)
1194 active
= self
.active
[-1] and macros
.has_key (m
.group (1))
1195 debug ('ACTIVE: %d' % active
)
1196 self
.active
.append (active
)
1198 def process_macro_ifndef (self
, line
):
1199 m
= re
.match ('^[ \t]*([a-zA-Z][a-zA-Z1-9_]*)', line
)
1201 active
= self
.active
[-1] and not macros
.has_key (m
.group (1))
1202 self
.active
.append (active
)
1204 def process_macro_else (self
, line
):
1206 self
.active
[-1] = not self
.active
[-1]
1208 def process_macro_endif (self
, line
):
1209 self
.active
= self
.active
[:-1]
1211 def process (self
, raw_lines
):
1213 for line
in raw_lines
:
1214 ls
= string
.split (self
.process_function (line
), '\n')
1217 s
= s
+ string
.rstrip (i
)
1218 if s
and s
[-1] == '\\':
1219 s
= string
.rstrip (s
[:-1])
1221 self
.lines
.append (s
)
1226 only_pre_process_p
= 0
1229 progress ('DEBUG: ' + s
)
1232 if verbose_p
or debug_p
:
1233 progress ('SKIPPING: ' + s
)
1235 (sh
, long) = getopt_args (__main__
.option_definitions
)
1237 (options
, files
) = getopt
.getopt (sys
.argv
[1:], sh
, long)
1243 pre_processor_commands
= (
1256 elif o
== '--debug' or o
== '-d':
1258 elif o
== '--define' or o
== '-D':
1259 if string
.find (a
, '=') >= 0:
1260 (n
, e
) = string
.split (a
, '=')
1265 elif o
== '--pre-process' or o
== '-E':
1266 only_pre_process_p
= 1
1267 elif o
== '--help' or o
== '-h':
1270 elif o
== '--verbose' or o
== '-V':
1272 elif o
== '--version' or o
== '-v':
1275 elif o
== '--output' or o
== '-o':
1281 # writes to stdout for help2man
1284 # sys.stdout.flush ()
1286 # handy emacs testing
1288 # files = ['template.mup']
1297 elif f
and not os
.path
.isfile (f
):
1298 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 ()