'''
TODO:
-
- WIP:lots of stuff
+ LOTS: we get all notes out now, rest after 1.4
+
+ * lyrics (partly done)
+ * bars
+ * slurs,ties
+ * staff settings
+ * tuplets
+ * grace
+ * ornaments
+ * midi settings
+ * titling
+ * chords entry mode
+ * repeats, percent repeats
'''
import tempfile
-sys.path.append ('@datadir@/python')
-import gettext
-gettext.bindtextdomain ('lilypond', '@localedir@')
-gettext.textdomain('lilypond')
-_ = gettext.gettext
+# if set, LILYPONDPREFIX must take prevalence
+# if datadir is not set, we're doing a build and LILYPONDPREFIX
+datadir = '@local_lilypond_datadir@'
+if os.environ.has_key ('LILYPONDPREFIX') \
+ or '@local_lilypond_datadir@' == '@' + 'local_lilypond_datadir' + '@':
+ datadir = os.environ['LILYPONDPREFIX']
+else:
+ datadir = '@local_lilypond_datadir@'
+sys.path.append (os.path.join (datadir, 'python'))
+sys.path.append (os.path.join (datadir, 'python/out'))
program_name = 'mup2ly'
-help_summary = _("Convert mup to ly")
-output = 0
+program_version = '@TOPLEVEL_VERSION@'
+original_dir = os.getcwd ()
+temp_dir = os.path.join (original_dir, '%s.dir' % program_name)
+errorport = sys.stderr
+keep_temp_dir_p = 0
+verbose_p = 0
+
+localedir = '@localedir@'
+try:
+ import gettext
+ gettext.bindtextdomain ('lilypond', localedir)
+ gettext.textdomain ('lilypond')
+ _ = gettext.gettext
+except:
+ def _ (s):
+ return s
+
-# lily_py.py -- options and stuff
+program_name = 'mup2ly'
+help_summary = _ ("Convert mup to LilyPond source")
+
+option_definitions = [
+ ('', 'd', 'debug', _ ("debug")),
+ ('NAME[=EXP]', 'D', 'define', _ ("define macro NAME [optional expansion EXP]")),
+ ('', 'h', 'help', _ ("this help")),
+ ('FILE', 'o', 'output', _ ("write output to FILE")),
+ ('', 'E', 'pre-process', _ ("only pre-process")),
+ ('', 'V', 'verbose', _ ("verbose")),
+ ('', 'v', 'version', _ ("print version number")),
+ ('', 'w', 'warranty', _ ("show warranty and copyright")),
+ ]
+
+
+################################################################
+# lilylib.py -- options and stuff
#
# source file of the GNU LilyPond music typesetter
-# BEGIN Library for these?
-# cut-n-paste from ly2dvi
+# Handle bug in Python 1.6-2.1
+#
+# there are recursion limits for some patterns in Python 1.6 til 2.1.
+# fix this by importing pre instead. Fix by Mats.
-program_version = '@TOPLEVEL_VERSION@'
-if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
- program_version = '1.3.142'
+# todo: should check Python version first.
+try:
+ import pre
+ re = pre
+ del pre
+except ImportError:
+ import re
+
+# Attempt to fix problems with limited stack size set by Python!
+# Sets unlimited stack size. Note that the resource module only
+# is available on UNIX.
+try:
+ import resource
+ resource.setrlimit (resource.RLIMIT_STACK, (-1, -1))
+except:
+ pass
+try:
+ import gettext
+ gettext.bindtextdomain ('lilypond', localedir)
+ gettext.textdomain ('lilypond')
+ _ = gettext.gettext
+except:
+ def _ (s):
+ return s
-original_dir = os.getcwd ()
-temp_dir = '%s.dir' % program_name
-keep_temp_dir_p = 0
-verbose_p = 0
+program_version = '@TOPLEVEL_VERSION@'
+if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
+ program_version = '1.5.54'
def identify ():
sys.stdout.write ('%s (GNU LilyPond) %s\n' % (program_name, program_version))
def warranty ():
identify ()
sys.stdout.write ('\n')
- sys.stdout.write (_ ('Copyright (c) %s by' % ' 2001'))
+ sys.stdout.write (_ ('Copyright (c) %s by' % ' 2001--2002'))
sys.stdout.write ('\n')
sys.stdout.write (' Han-Wen Nienhuys')
sys.stdout.write (' Jan Nieuwenhuizen')
sys.stdout.write ('\n')
def progress (s):
- if s[-1] != '\n':
- s = s + '\n'
- sys.stderr.write (s)
+ errorport.write (s + '\n')
def warning (s):
- sys.stderr.write (_ ("warning: ") + s)
- sys.stderr.write ('\n')
+ progress (_ ("warning: ") + s)
+
+def user_error (s, e=1):
+ errorport.write (program_name + ":" + _ ("error: ") + s + '\n')
+ sys.exit (e)
-
def error (s):
- sys.stderr.write (_ ("error: ") + s)
- sys.stderr.write ('\n')
+ '''Report the error S. Exit by raising an exception. Please
+ do not abuse by trying to catch this error. If you do not want
+ a stack trace, write to the output directly.
+
+ RETURN VALUE
+
+ None
+
+ '''
+
+ progress (_ ("error: ") + s)
raise _ ("Exiting ... ")
def getopt_args (opts):
return str
def help ():
- sys.stdout.write (_ ("Usage: %s [OPTION]... FILE") % program_name)
- sys.stdout.write ('\n\n')
- sys.stdout.write (help_summary)
- sys.stdout.write ('\n\n')
- sys.stdout.write (_ ("Options:"))
- sys.stdout.write ('\n')
- sys.stdout.write (options_help_str (option_definitions))
- sys.stdout.write ('\n\n')
- sys.stdout.write (_ ("Report bugs to %s") % 'bug-gnu-music@gnu.org')
- sys.stdout.write ('\n')
- sys.exit (0)
-
-
+ ls = [(_ ("Usage: %s [OPTION]... FILE") % program_name),
+ ('\n\n'),
+ (help_summary),
+ ('\n\n'),
+ (_ ("Options:")),
+ ('\n'),
+ (options_help_str (option_definitions)),
+ ('\n\n'),
+ (_ ("Report bugs to %s") % 'bug-lilypond@gnu.org'),
+ ('\n')]
+ map (sys.stdout.write, ls)
+
def setup_temp ():
+ """
+ Create a temporary directory, and return its name.
+ """
global temp_dir
if not keep_temp_dir_p:
temp_dir = tempfile.mktemp (program_name)
os.mkdir (temp_dir, 0777)
except OSError:
pass
-
+
+ return temp_dir
+
+
+def system (cmd, ignore_error = 0, quiet =0):
+ """Run CMD. If IGNORE_ERROR is set, don't complain when CMD returns non zero.
+
+ RETURN VALUE
+
+ Exit status of CMD
+ """
-def system (cmd, ignore_error = 0):
if verbose_p:
progress (_ ("Invoking `%s\'") % cmd)
+
st = os.system (cmd)
if st:
- msg = ( _ ("error: ") + _ ("command exited with value %d") % st)
+ name = re.match ('[ \t]*([^ \t]*)', cmd).group (1)
+ msg = name + ': ' + _ ("command exited with value %d") % st
if ignore_error:
- sys.stderr.write (msg + ' ' + _ ("(ignored)") + ' ')
+ if not quiet:
+ warning (msg + ' ' + _ ("(ignored)") + ' ')
else:
error (msg)
def cleanup_temp ():
if not keep_temp_dir_p:
if verbose_p:
- progress (_ ('Cleaning up `%s\'') % temp_dir)
- system ('rm -rf %s' % temp_dir)
+ progress (_ ("Cleaning %s...") % temp_dir)
+ shutil.rmtree (temp_dir)
-def set_setting (dict, key, val):
- try:
- val = string.atof (val)
- except ValueError:
- #warning (_ ("invalid value: %s") % `val`)
- pass
+def strip_extension (f, ext):
+ (p, e) = os.path.splitext (f)
+ if e == ext:
+ e = ''
+ return p + e
- try:
- dict[key].append (val)
- except KeyError:
- warning (_ ("no such setting: %s") % `key`)
- dict[key] = [val]
+def cp_to_dir (pattern, dir):
+ "Copy files matching re PATTERN from cwd to DIR"
+ # Duh. Python style portable: cp *.EXT OUTDIR
+ # system ('cp *.%s %s' % (ext, outdir), 1)
+ files = filter (lambda x, p=pattern: re.match (p, x), os.listdir ('.'))
+ map (lambda x, d=dir: shutil.copy2 (x, os.path.join (d, x)), files)
+
+
+# Python < 1.5.2 compatibility
+#
+# On most platforms, this is equivalent to
+#`normpath(join(os.getcwd()), PATH)'. *Added in Python version 1.5.2*
+if os.path.__dict__.has_key ('abspath'):
+ abspath = os.path.abspath
+else:
+ def abspath (path):
+ return os.path.normpath (os.path.join (os.getcwd (), path))
+
+if os.__dict__.has_key ('makedirs'):
+ makedirs = os.makedirs
+else:
+ def makedirs (dir, mode=0777):
+ system ('mkdir -p %s' % dir)
+
+
+def mkdir_p (dir, mode=0777):
+ if not os.path.isdir (dir):
+ makedirs (dir, mode)
+
+
+# if set, LILYPONDPREFIX must take prevalence
+# if datadir is not set, we're doing a build and LILYPONDPREFIX
+datadir = '@local_lilypond_datadir@'
+
+if os.environ.has_key ('LILYPONDPREFIX') :
+ datadir = os.environ['LILYPONDPREFIX']
+else:
+ datadir = '@local_lilypond_datadir@'
+
+
+while datadir[-1] == os.sep:
+ datadir= datadir[:-1]
+
+sys.path.insert (0, os.path.join (datadir, 'python'))
+
+################################################################
# END Library
+output = 0
+
#
# PMX cut and paste
#
self.id = id
self.start_chord = None
self.end_chord = None
+
def calculate (self):
s =self.start_chord
e= self.end_chord
if e and s:
- s.note_suffix = s.note_suffix + '('
- e.note_prefix = ')' + e.note_prefix
+ s.note_suffix = s.note_suffix + '-('
+ e.note_prefix = e.note_suffix + "-)"
else:
sys.stderr.write ("\nOrphaned slur")
def dump (self):
str = ''
- if not self.entries:
- #return '\n'
- #ugh ugh
- return '\n%s = {}\n\n' % self.idstring ()
+ #if not self.entries:
+ # #return '\n'
+ # #ugh ugh
+ # return '\n%s = {}\n\n' % self.idstring ()
ln = ' '
one_two = ("One", "Two")
if self.staff.voices [1 - self.number].entries:
str = str + ln
id = self.idstring ()
- str = '''%s = \\notes {
+ str = '''%s = \\context Voice = %s \\notes {
%s
}
-'''% (id, str)
+'''% (id, id, str)
return str
def calculate_graces (self):
class Clef:
def __init__ (self, cl):
self.type = cl
- def dump(self):
- return '\\clef %s;' % self.type
+
+ def dump (self):
+ return '\\clef %s' % self.type
+
+key_sharps = ('c', 'g', 'd', 'a', 'e', 'b', 'fis')
+key_flats = ('BUG', 'f', 'bes', 'es', 'as', 'des', 'ges')
+
+class Key:
+ def __init__ (self, sharps, flats):
+ self.flats = flats
+ self.sharps = sharps
+
+ def dump (self):
+ if self.sharps and self.flats:
+ k = '\\keysignature %s ' % 'TODO'
+ elif self.sharps:
+ k = '\\notes\\key %s \major' % key_sharps[self.sharps]
+ elif self.flats:
+ k = '\\notes\\key %s \major' % key_flats[self.flats]
+ return k
+
+class Time:
+ def __init__ (self, frac):
+ self.frac = frac
+
+ def dump (self):
+ return '\\time %d/%d' % (self.frac[0], self.frac[1])
+
clef_table = {
'b':'bass' ,
# ugh
self.voices = (Voice (0), Voice (1))
- # self.voice_idx = 0
self.clef = None
+ self.time = None
+ self.key = None
self.instrument = 0
self.number = n
v.number = i
i = i+1
- def set_clef (self, letter):
- clstr = clef_table[letter]
- self.voices[0].add_nonchord (Clef (clstr))
+ #def set_clef (self, letter):
+ # clstr = clef_table[letter]
+ # self.voices[0].add_nonchord (Clef (clstr))
- #def current_voice (self):
- # return self.voices[self.voice_idx]
- #
- #def next_voice (self):
- # self.voice_idx = (self.voice_idx + 1)%len (self.voices)
-
def calculate (self):
for v in self.voices:
v.calculate ()
def idstring (self):
return 'staff%s' % encodeint (self.number)
-
+
def dump (self):
str = ''
refs = ''
for v in self.voices:
- str = str + v.dump()
- refs = refs + '\n \\' + v.idstring ()
-
+ if v.entries:
+ # urg
+ if v == self.voices[0]:
+ if self.clef:
+ refs = refs + self.clef.dump ()
+ if self.time:
+ refs = refs + self.time.dump ()
+ if self.key:
+ refs = refs + self.key.dump ()
+ if refs:
+ refs = '\n ' + refs
+ str = str + v.dump()
+ refs = refs + '\n \\' + v.idstring ()
str = str + '''
%s = \context Staff = %s <%s
>
self.chord_suffix = ''
self.note_prefix = ''
self.note_suffix = ''
+
+ # maybe use import copy?
+ def copy (self):
+ ch = Chord ()
+ #for i in self.pitches:
+ # ch.pitches.append (i)
+ ch.pitches = self.pitches[:]
+ ch.multimeasure = self.multimeasure
+ ch.dots = self.dots
+ ch.basic_duration = self.basic_duration
+ #for i in self.scripts:
+ # ch.scripts.append (i)
+ ch.scripts = self.scripts[:]
+ ch.grace = self.grace
+
+ ch.chord_prefix = self.chord_prefix
+ ch.chord_suffix = self.chord_suffix
+ ch.note_prefix = self.note_prefix
+ ch.note_suffix = self.note_suffix
+ return ch
+
def dump (self):
str = ''
for p in self.pitches:
if str:
str = str + ' '
- str = str + pitch_to_lily_string (p) + sd
+ str = str + pitch_to_lily_string (p)
- for s in self.scripts:
- str = str + '-' + s
str = self.note_prefix +str + self.note_suffix
if len (self.pitches) > 1:
- str = '<%s>' % str
+ str = '<<%s>>' % str
elif self.multimeasure:
- str = 'R' + sd
+ str = 'R'
elif len (self.pitches) == 0:
- str = 'r' + sd
+ str = 'r'
+
+ str = str + sd
+ for s in self.scripts:
+ str = str + '-' + s
str = self.chord_prefix + str + self.chord_suffix
# http://www.arkkra.com/doc/uguide/contexts.html
-contexts = [
+contexts = (
'header',
'footer',
'header2',
'voice',
'grids',
'music',
- ]
+ )
class Parser:
- def __init__ (self, filename):
+ def __init__ (self, lines):
self.parse_function = self.parse_context_music
self.staffs = []
self.current_voices = []
self.last_oct = 0
self.tuplets_expected = 0
self.tuplets = []
- self.last_basic_duration = 4
-
- self.parse (filename)
-
- #def set_staffs (self, number):
- # self.staffs = map (lambda x: Staff (x), range (0, number))
+ self.clef = None
+ self.time = None
+ self.key = None
- #def current_staff (self):
- # return self.staffs[self.staff_idx]
-
- #def current_voice (self):
- # return self.current_staff ().current_voice ()
-
- #def next_staff (self):
- # self.staff_idx = (self.staff_idx + 1)% len (self.staffs)
+ self.parse (lines)
def parse_compound_location (self, line):
- colon = string.index (line, ':')
+ colon = string.index (line, ':')
s = line[:colon]
debug (s)
line = line[colon + 1:]
for j in i:
f.append (j)
return f
-
+
def range_to_list (s):
if string.find (s, '-') >= 0:
debug ('s: ' + s)
name = (ord (line[0]) - ord ('a') + 5) % 7
# FIXME: does key play any role in this?
alteration = 0
+ debug ('NOTE: ' + `line`)
line = string.lstrip (line[1:])
while line:
if len (line) > 1 and line[:2] == '//':
elif line[0] == '-':
oct = oct - 1
else:
- skipping (_ ("%s") % line[0])
+ skipping (line[0])
line = string.lstrip (line[1:])
return (oct, name, alteration)
def parse_chord (self, line):
+ debug ('CHORD: ' + line)
line = string.lstrip (line)
ch = Chord ()
if not line:
- ch = self.current_voices[0].last_chord ()
+ #ch = self.current_voices[0].last_chord ()
+ ch = self.last_chord.copy ()
else:
m = re.match ('^([0-9]+)([.]*)', line)
if m:
ch.basic_duration = string.atoi (m.group (1))
line = line[len (m.group (1)):]
if m.group (2):
- ch.basic_duration = len (m.group (2))
- line = line[len (m.group (1)):]
+ ch.dots = len (m.group (2))
+ line = line[len (m.group (2)):]
else:
- ch.basic_duration = self.current_voices[0].last_chord ().basic_duration
+ #ch.basic_duration = self.current_voices[0].last_chord ().basic_duration
+ ch.basic_duration = self.last_chord.basic_duration
line = string.lstrip (line)
if len (line) > 1 and line[:2] == '//':
line = 0
#ugh
if not line:
- duration = ch.basic_duration
- ch = self.current_voices[0].last_chord ()
- ch.basic_duration = duration
-
+ debug ('nline: ' + line)
+ #ch = self.current_voices[0].last_chord ()
+ n = self.last_chord.copy ()
+ n.basic_duration = ch.basic_duration
+ n.dots = ch.dots
+ ch = n
+ debug ('ch.pitsen:' + `ch.pitches`)
+ debug ('ch.dur:' + `ch.basic_duration`)
+ else:
+ debug ('eline: ' + line)
+
while line:
if len (line) > 1 and line[:2] == '//':
line = 0
break
elif line[:1] == 'mr':
ch.multimeasure = 1
- line = line[1:]
+ line = line[2:]
elif line[:1] == 'ms':
ch.multimeasure = 1
- line = line[1:]
+ line = line[2:]
elif line[0] in 'rs':
+ line = line[1:]
pass
elif line[0] in 'abcdefg':
- pitch = self.parse_note (line)
+ m = re.match ('([a-g][-#&+]*)', line)
+ l = len (m.group (1))
+ pitch = self.parse_note (line[:l])
debug ('PITCH: ' + `pitch`)
ch.pitches.append (pitch)
- line = 0
+ line = line[l:]
break
else:
- skipping (_ ("%s") % line[0])
- line = string.lstrip (line[1:])
+ skipping (line[0])
+ line = line[1:]
+ line = string.lstrip (line)
+ debug ('CUR-VOICES: ' + `self.current_voices`)
map (lambda x, ch=ch: x.add_chord (ch), self.current_voices)
-
+ self.last_chord = ch
+
+ def parse_lyrics_location (self, line):
+ line = line.lstrip (line)
+ addition = 0
+ m = re.match ('^(between[ \t]+)', line)
+ if m:
+ line = line[len (m.group (1)):]
+ addition = 0.5
+ else:
+ m = re.match ('^(above [ \t]+)', line)
+ if m:
+ line = line[len (m.group (1)):]
+ addition = -0.5
+ else:
+ addlyrics = 1
+
def parse_voice (self, line):
- chords = string.split (line, ';')
+ line = string.lstrip (line)
+ # `;' is not a separator, chords end with ';'
+ chords = string.split (line, ';')[:-1]
+ # mup resets default duration and pitch each bar
+ self.last_chord = Chord ()
+ self.last_chord.basic_duration = 4
map (self.parse_chord, chords)
def init_context_header (self, line):
def parse_context_header (self, line):
debug ('header: ' + line)
-
+ skipping (line)
+
def init_context_footer (self, line):
self.parse_function = self.parse_context_footer
def parse_context_footer (self, line):
debug ('footer: ' + line)
+ skipping (line)
def init_context_header2 (self, line):
self.parse_function = self.parse_context_header2
def parse_context_header2 (self, line):
debug ('header2: ' + line)
+ skipping (line)
def init_context_footer2 (self, line):
self.parse_function = self.parse_context_footer2
def parse_context_footer2 (self, line):
debug ('footer2: ' + line)
+ skipping (line)
def init_context_score (self, line):
self.parse_function = self.parse_context_score
def parse_context_score (self, line):
debug ('score: ' + line)
+ line = string.lstrip (line)
+ # ugh: these (and lots more) should also be parsed in
+ # context staff. we should have a class Staff_properties
+ # and parse/set all those.
+ m = re.match ('^(time[ \t]*=[ \t]*([0-9]+)[ \t]*/[ \t]*([0-9]+))', line)
+ if m:
+ line = line[len (m.group (1)):]
+ self.time = Time ((string.atoi (m.group (2)),
+ string.atoi (m.group (3))))
+
+ m = re.match ('^(key[ \t]*=[ \t]*([0-9]+)[ \t]*(#|@))', line)
+ if m:
+ line = line[len (m.group (1)):]
+ n = string.atoi (m.group (2))
+ if m.group (3) == '#':
+ self.key = Key (n, 0)
+ else:
+ self.key = Key (0, n)
+ skipping (line)
def init_context_staff (self, line):
self.parse_function = self.parse_context_staff
def parse_context_staff (self, line):
debug ('staff: ' + line)
+ skipping (line)
def init_context_voice (self, line):
self.parse_function = self.parse_context_voice
def parse_context_voice (self, line):
debug ('voice: ' + line)
+ skipping (line)
def init_context_grids (self, line):
- self.parse_function = self.parse_context_line
+ self.parse_function = self.parse_context_grids
def parse_context_grids (self, line):
debug ('grids: ' + line)
+ skipping (line)
def init_context_music (self, line):
self.parse_function = self.parse_context_music
debug ('music: ' + line)
line = string.lstrip (line)
if line and line[0] in '0123456789':
- line = string.lstrip (self.parse_compound_location (line))
+ line = self.parse_compound_location (line)
self.parse_voice (line)
else:
- skipping (_ ("%s") % line)
-
- def parse (self, file):
+ m = re.match ('^(TODOlyrics[ \t]+)', line)
+ if m:
+ line = line[len (m.group (1)):]
+ self.parse_lyrics_location (line[7:])
+ self.parse_lyrics (line)
+ else:
+ skipping (line)
+
+ def parse (self, lines):
# shortcut: set to official mup maximum (duh)
# self.set_staffs (40)
- lines = open (file).readlines ()
for line in lines:
- debug ('LINE: ' + line)
+ debug ('LINE: ' + `line`)
m = re.match ('^([a-z]+2?)', line)
if m:
self.parse_function (line)
for c in self.staffs:
+ # hmm
+ if not c.clef and self.clef:
+ c.clef = self.clef
+ if not c.time and self.time:
+ c.time = self.time
+ if not c.key and self.key:
+ c.key = self.key
c.calculate ()
def dump (self):
\score {
<%s
- >
+ >
+ \paper {}
+ \midi {}
}
''' % refs
return str
+
+class Pre_processor:
+ def __init__ (self, raw_lines):
+ self.lines = []
+ self.active = [1]
+ self.process_function = self.process_line
+ self.macro_name = ''
+ self.macro_body = ''
+ self.process (raw_lines)
+
+ def process_line (self, line):
+ global macros
+ m = re.match ('^([ \t]*([a-zA-Z]+))', line)
+ s = line
+ if m:
+ word = m.group (2)
+ debug ('MACRO?: ' + `word`)
+ if word in pre_processor_commands:
+ line = line[len (m.group (1)):]
+ eval ('self.process_macro_%s (line)' % word)
+ s = ''
+ else:
+ if macros.has_key (word):
+ s = macros[word] + line[len (m.group (1)):]
+ if not self.active [-1]:
+ s = ''
+ return s
+
+ def process_macro_body (self, line):
+ global macros
+ # dig this: mup allows ifdefs inside macro bodies
+ s = self.process_line (line)
+ m = re.match ('(.*[^\\\\])(@)(.*)', s)
+ if m:
+ self.macro_body = self.macro_body + '\n' + m.group (1)
+ macros[self.macro_name] = self.macro_body
+ debug ('MACROS: ' + `macros`)
+ # don't do nested multi-line defines
+ self.process_function = self.process_line
+ if m.group (3):
+ s = m.group (3)
+ else:
+ s = ''
+ else:
+ self.macro_body = self.macro_body + '\n' + s
+ s = ''
+ return s
+
+ # duh: mup is strictly line-based, except for `define',
+ # which is `@' terminated and may span several lines
+ def process_macro_define (self, line):
+ global macros
+ # don't define new macros in unactive areas
+ if not self.active[-1]:
+ return
+ m = re.match ('^[ \t]*([a-zA-Z][a-zA-Z1-9_]*)(([^@]*)|(\\\\@))(@)?', line)
+ n = m.group (1)
+ if m.group (5):
+ if m.group (2):
+ e = m.group (2)
+ else:
+ e = ''
+ macros[n] = e
+ debug ('MACROS: ' + `macros`)
+ else:
+ # To support nested multi-line define's
+ # process_function and macro_name, macro_body
+ # should become lists (stacks)
+ # The mup manual is undetermined on this
+ # and I haven't seen examples doing it.
+ #
+ # don't do nested multi-line define's
+ if m.group (2):
+ self.macro_body = m.group (2)
+ else:
+ self.macro_body = ''
+ self.macro_name = n
+ self.process_function = self.process_macro_body
-option_definitions = [
- ('', 'd', 'debug', _ ("debug")),
- ('', 'h', 'help', _ ("this help")),
- ('FILE', 'o', 'output', _ ("write output to FILE")),
- ('', 'V', 'verbose', _ ("verbose")),
- ('', 'v', 'version', _ ("print version number")),
- ('', 'w', 'warranty', _ ("show warranty and copyright")),
- ]
+ def process_macro_ifdef (self, line):
+ m = re.match ('^[ \t]*([a-zA-Z][a-zA-Z1-9_]*)', line)
+ if m:
+
+ active = self.active[-1] and macros.has_key (m.group (1))
+ debug ('ACTIVE: %d' % active)
+ self.active.append (active)
+
+ def process_macro_ifndef (self, line):
+ m = re.match ('^[ \t]*([a-zA-Z][a-zA-Z1-9_]*)', line)
+ if m:
+ active = self.active[-1] and not macros.has_key (m.group (1))
+ self.active.append (active)
+
+ def process_macro_else (self, line):
+ debug ('ELSE')
+ self.active[-1] = not self.active[-1]
+
+ def process_macro_endif (self, line):
+ self.active = self.active[:-1]
+
+ def process (self, raw_lines):
+ s = ''
+ for line in raw_lines:
+ ls = string.split (self.process_function (line), '\n')
+ for i in ls:
+ if i:
+ s = s + string.rstrip (i)
+ if s and s[-1] == '\\':
+ s = string.rstrip (s[:-1])
+ elif s:
+ self.lines.append (s)
+ s = ''
+
debug_p = 0
+only_pre_process_p = 0
def debug (s):
if debug_p:
progress ('DEBUG: ' + s)
+
def skipping (s):
- if debug_p:
+ if verbose_p or debug_p:
progress ('SKIPPING: ' + s)
(sh, long) = getopt_args (__main__.option_definitions)
help ()
sys.exit (2)
+macros = {}
+pre_processor_commands = (
+ 'define',
+ 'else',
+ 'endif',
+ 'ifdef',
+ 'ifndef',
+ )
for opt in options:
o = opt[0]
pass
elif o== '--debug' or o == '-d':
debug_p = 1
+ elif o== '--define' or o == '-D':
+ if string.find (a, '=') >= 0:
+ (n, e) = string.split (a, '=')
+ else:
+ n = a
+ e = ''
+ macros[n] = e
+ elif o== '--pre-process' or o == '-E':
+ only_pre_process_p = 1
elif o== '--help' or o == '-h':
help ()
sys.exit (0)
# sys.stdout.flush ()
# handy emacs testing
-if not files:
- files = ['template.mup']
+# if not files:
+# files = ['template.mup']
+if not files:
+ files = ['-']
+
for f in files:
+
+ if f == '-':
+ h = sys.stdin
+ elif f and not os.path.isfile (f):
+ f = strip_extension (f, '.mup') + '.mup'
+ h = open (f)
+ progress ( _("Processing `%s'..." % f))
+ raw_lines = h.readlines ()
+ p = Pre_processor (raw_lines)
+ if only_pre_process_p:
+ if not output:
+ output = os.path.basename (re.sub ('(?i).mup$', '.mpp', f))
+ else:
+ e = Parser (p.lines)
+ if not output:
+ output = os.path.basename (re.sub ('(?i).mup$', '.ly', f))
+ if output == f:
+ output = os.path.basename (f + '.ly')
+
if f == '-':
- f = ''
+ output = '-'
+ out_h = sys.stdout
+ else:
+ out_h = open (output, 'w')
- progress ( _("Processing %s..." % f))
- e = Parser (f)
- if not output:
- output = os.path.basename (re.sub ('(?i).mup$', '.ly', f))
-
- if output == f:
- output = os.path.basename (f + '.ly')
-
- progress (_ ("Writing %s...") % output)
+ progress (_ ("Writing `%s'...") % output)
tag = '%% Lily was here -- automatically converted by %s from %s' % ( program_name, f)
- ly = tag + '\n' + e.dump ()
+ if only_pre_process_p:
+ # duh
+ ly = string.join (p.lines, '\n')
+ else:
+ ly = tag + '\n\n' + e.dump ()
- o = open (output, 'w')
- o.write (ly)
- o.close ()
- print ly
+ out_h.write (ly)
+ out_h.close ()
+ if debug_p:
+ print (ly)