#!@PYTHON@
-# All non-english comments are NOT in swedish, they are norwegian!
+#
+# The bugs you find are made by Tom Cato Amundsen <tomcato@xoommail.com>
+# Send patches/questions/bugreports to a mailinglist:
+# gnu-music-discuss@gnu.org
+# bug-gnu-music@gnu.org
+# help-gnu-music@gnu.org
+#
# TODO:
# * center option (??)
-# * mudela-book should treat \begin{verbatim} that contains inline mudela
-# correctly.
# * make mudela-book understand usepackage{geometry}
# * check that linewidth set in \paper is not wider than actual linewidth?
# * the following fails because mudelabook doesn't care that the
# - mudela-book will scan the mudela code to find out if it has to add
# paper-definition and \score{\notes{...}}
# - possible to define commands that look like this: \mudela{ c d e }
-
+# - don't produce .dvi output, it was a silly idea...
+# 0.5.1:
+# - removed init/mudela-book-defs.py, \mudela is defined inside mudela-book
+# - fragment and nonfragment options will override autodetection of type of
+# in mudela-block (voice contents or complete code)
+# 0.5.2:
+# - fixed verbatim option behaviour: don't show \begin{mudela} \end{mudela}
+# and show both mudela code and music
+# - veryverbatim option, same as verbatim but show \begin{mudela}
+# and \end{mudela}. (saves keystrokes in mudela-book-doc.doc)
+# - intertext="bla bla bla" option
+# - mudela-book now understand latex \begin{verbatim}
+# 0.5.3:
+# - bf: \mudela{ \times 2/3{...} }
+# * \t in \times is not tab character and
+# * dont treat the first '}' as command ending
+# 0.5.4: (Mats B)
+# - .fly and .sly files in \mudelafile{} are treated as standalone Lilypond.
+# - Fragments, .fly and .sly files are in \relative mode.
+# 0.5.5: (Mats B)
+# - bf: Default fragments have linewidth=-1.0
+# - Added 'singleline' and 'multiline' options.
+# 0.5.6:
+# - \mudelafile{} set linewidth correct, -1 for .sly and texlinewidth for .fly
+# - changes to Mudela_output
+# - changed RE to search for pre/postMudelaExample to make it possible to
+# comment out a definition.
+# - use sys.stderr and sys.stdout instead of print
import os
import string
import re
outdir = 'out'
initfile = ''
-program_version = '0.5'
+program_version = '0.5.6'
+include_path = ['.']
out_files = []
20:'twenty', 26:'twentysix'}
fontsize_pt2i = {'11pt':11, '13pt':13, '16pt':16, '20pt':20, '26pt':26}
+# perhaps we can do without this?
+
begin_mudela_re = re.compile ('^ *\\\\begin{mudela}')
+begin_verbatim_re = re.compile ('^ *\\\\begin{verbatim}')
+end_verbatim_re = re.compile ('^ *\\\\end{verbatim}')
extract_papersize_re = re.compile('\\\\documentclass[\[, ]*(\w*)paper[\w ,]*\]\{\w*\}')
extract_fontsize_re = re.compile('[ ,\[]*([0-9]*)pt')
begin_mudela_opts_re = re.compile('\[[^\]]*\]')
documentclass_re = re.compile('\\\\documentclass')
twocolumn_re = re.compile('\\\\twocolumn')
onecolumn_re = re.compile('\\\\onecolumn')
-preMudelaExample_re = re.compile('\\\\def\\\\preMudelaExample')
-postMudelaExample_re = re.compile('\\\\def\\\\postMudelaExample')
-boundingBox_re = re.compile('%%BoundingBox: ([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)')
+mudela_file_re = re.compile('\\\\mudelafile{([^}]+)}')
+file_ext_re = re.compile('.+\\.([^.}]+$)')
+preMudelaExample_re = re.compile('^\s*\\\\def\\\\preMudelaExample')
+postMudelaExample_re = re.compile('^\s*\\\\def\\\\postMudelaExample')
+boundingBox_re = re.compile('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)')
+intertext_re = re.compile("intertext=\"([^\"]*)\"")
def file_exist_b(name):
try:
int(s.groups()[3])-int(s.groups()[1]))
+def find_file (name):
+ for a in include_path:
+ try:
+ nm = os.path.join (a, name)
+ f = open (nm)
+ return nm
+ except IOError:
+ pass
+ return ''
+
+
class CompileStatus:
pass
class SomethingIsSeriouslyBroken:
class Mudela_output:
+ """ Using only self.code_type to deside both value of linewith and
+ if we have to put code into \score{...} was silly. Now we use:
+ self.code_type: show us what need to be added.
+ None : init value
+ 'NOTES' : add \context Voice{ ... }
+ 'CONTEXT' : add \score{ ... }
+ 'COMPLETE' : jupp
+ self.single_line_b: 0 : linewidth=-1 1: linewith=textwidth
+ """
def __init__ (self, basename):
self.basename = basename
self.temp_filename = "%s/%s" %(outdir, 'mudela-temp.ly')
self.__lines = []
# 'tex' or 'eps'
self.graphic_type = 'tex'
- self.code_type = 'unknown'
+ self.code_type = None
+ self.single_line_b = 1
+ self.optlist = []
def write (self, line):
- # match only if there is nothing but whitespace before \begin HACK
+ # match only if there is nothing but whitespace before \begin.
+ # we should not have to do this RE for every line
if re.search('^\s*\\\\begin{mudela}', line):
- self.scan_begin_statement(line)
- else:
- if self.code_type == 'unknown':
- if re.search('^\s*\\\\score', line) or \
- re.search('^\s*\\\\paper', line) or \
- re.search('^\s*\\\\header', line) or \
- re.search('^\s*[A-Za-z]*\s*=', line):
- self.code_type = 'ly'
+ r = begin_mudela_opts_re.search(line)
+ if r:
+ o = r.group()[1:-1]
+ self.optlist = re.compile('[\s,]*').split(o)
+ else:
+ self.optlist = []
+ else: # ugh this is NOT bulletproof...
+ if not self.code_type:
+ if re.search('^\s*%', line) or re.search('^\s*$', line):
+ pass
+ elif re.search('^\s*\\\\context', line):
+ self.code_type = 'CONTEXT'
+ self.single_line_b = 0
+ elif re.search('^\s*\\\\score', line) or \
+ re.search('^\s*\\\\paper', line) or \
+ re.search('^\s*\\\\header', line) or \
+ re.search('^\s*\\\\version', line) or \
+ re.search('^\s*\\\\include', line) or \
+ re.search('^\s*[A-Za-z]*\s*=', line):
+ self.code_type = 'COMPLETE'
+ self.single_line_b = 0
+ else:
+ self.code_type = 'NOTES'
+ self.single_line_b = 1
self.__lines.append(line)
- def scan_begin_statement(self, line):
- r = begin_mudela_opts_re.search(line)
- if r:
- o = r.group()[1:-1]
- optlist = re.compile('[\s,]*').split(o)
- else:
- optlist = []
- if 'fragment' in optlist:
- print "warning: obsolete option: fragment"
- if 'floating' in optlist:
- print "warning: obsolete option: floating, change to eps"
- if 'eps' in optlist:
+ def write_red_tape(self):
+ if 'eps' in self.optlist:
self.graphic_type = 'eps'
+ #self.single_line_b = 1
+ if 'fragment' in self.optlist:
+ self.code_type = 'NOTES'
+ if 'nonfragment' in self.optlist:
+ self.code_type = 'COMPLETE'
+ if 'multiline' in self.optlist:
+ self.single_line_b = 0
for pt in fontsize_pt2i.keys():
- if pt in optlist:
+ if pt in self.optlist:
Props.setMudelaFontsize(fontsize_pt2i[pt], 'block')
- def write_red_tape(self):
self.file.write ('\\include \"paper%d.ly\"\n' \
% Props.getMudelaFontsize())
s = fontsize_i2a[Props.getMudelaFontsize()]
- if self.code_type == 'fly':
+ if 'singleline' in self.optlist:
+ self.single_line_b = 1
+ if self.single_line_b:
linewidth_str = 'linewidth = -1.\cm;'
else:
linewidth_str = 'linewidth = %i.\\pt;' % Props.getLineWidth()
+ linewidth_str
+ "castingalgorithm = \Gourlay; \n}")
#+ "castingalgorithm = \Wordwrap; indent = 2.\cm; \n}")
- if self.code_type == 'fly':
- self.file.write('\\score{\n\\notes{')
+ if self.code_type == 'CONTEXT':
+ self.file.write('\\score{\n\\notes\\relative c{')
+ if self.code_type == 'NOTES' :
+ self.file.write('\\score{\n\\notes\\relative c{\\context Voice{')
def close (self):
- if self.code_type == 'unknown':
- self.code_type = 'fly'
- if self.code_type == 'ly':
- self.write_red_tape()
- for l in self.__lines:
- self.file.write(l)
- else:
- self.write_red_tape()
- for l in self.__lines:
- self.file.write(l)
+ self.write_red_tape()
+ for l in self.__lines:
+ self.file.write(l)
+ if self.code_type == 'CONTEXT':
self.file.write('}}')
-
+ elif self.code_type == 'NOTES':
+ self.file.write('}}}')
self.file.close()
inf = outdir + self.basename + '.ly'
status = os.system ('diff -q %s %s' % (self.temp_filename, inf))
if status:
os.rename (self.temp_filename, inf)
- if need_recompile_b(inf, outf):
+
+ recompile_b = need_recompile_b(inf, outf)
+ if recompile_b:
out_files.append((self.graphic_type, inf))
+ return recompile_b
+
def insert_me_string(self):
- "Returns a string that can be used directly in latex."
+ "ugh the name of this function is wrong"
if self.graphic_type == 'tex':
return ['tex', self.basename]
elif self.graphic_type == 'eps':
def __init__ (self, name):
self.output_fn = '%s/%s' % (outdir, name)
self.__lines = []
- def open_verbatim (self, line):
+ def open_verbatim (self, line, level):
self.__lines.append('\\begin{verbatim}\n')
- s = re.sub('[\s,]*verbatim[\s]*', '', line)
- s = re.sub('\[\]', '', s)
- self.__lines.append(s);
+ if level == 2:
+ s = re.sub('veryverbatim[\s,]*', '', line)
+ s = re.sub('intertext=\"([^\"]*)\"[\s,]*', '', s)
+ s = re.sub(',\s*\]', ']', s)
+ s = re.sub('\[\]', '', s)
+ self.__lines.append(s);
def close_verbatim (self):
self.__lines.append('\\end{verbatim}\n')
def write (self, s):
if s != '':
e = os.system('cd %s; lilypond %s' %(outdir, s))
if e:
- print "error: lilypond exited with value", e
+ sys.stderr.write("error: lilypond exited with value %i\n" % e)
sys.exit(e)
for g in g_vec:
if g[0] == 'eps':
def write_outfile(self):
file = open(self.output_fn+'.latex', 'w')
file.write('% Created by mudela-book\n')
+ last_line = None
for line in self.__lines:
if type(line)==type([]):
- if line[0] == 'tex':
- file.write('\\preMudelaExample\\input %s\n\postMudelaExample '\
- # TeX applies the prefix of the main source automatically.
+ if last_line == '\n':
+ file.write(r'\vspace{0.5cm}')
+ if line[0] == 'tex':
+ file.write('\\preMudelaExample \\input %s \\postMudelaExample\n'\
% (line[1]+'.tex'))
-# % (outdir+line[1]+'.tex'))
if line[0] == 'eps':
ps_dim = ps_dimention(outdir+line[1]+'.eps')
- file.write('\\parbox{%ipt}{\includegraphics{%s}}\n' \
+ file.write('\\noindent\\parbox{%ipt}{\includegraphics{%s}}\n' \
% (ps_dim[0], line[1]+'.eps'))
-# % (ps_dim[0], outdir+line[1]+'.eps'))
else:
file.write(line)
+ if type(last_line)==type([]):
+ if line=='\n':
+ file.write(r'\vspace{0.5cm}')
+ last_line = line
file.close()
+
+# given parameter s="\mudela[some options]{CODE} some text and commands"
+# it returns a tuple:
+# (CODE, integer)
+# where the last number is the index of the ending '}'
+def extract_command(s):
+ start_found_b = 0
+ count = 0
+ start = 0
+ for idx in range(len(s)):
+ if s[idx] == '{':
+ if not start_found_b:
+ start = idx
+ start_found_b = 1
+ count = count + 1
+ if s[idx] == '}':
+ count = count - 1
+ if (start_found_b == 1) and (count == 0):
+ break
+ return s[start+1:idx], idx
+
class Tex_input:
def __init__ (self, filename):
for fn in [filename, filename+'.tex', filename+'.doc']:
except:
continue
raise IOError
+
def get_lines (self):
lines = self.infile.readlines ()
(retlines, retdeps) = ([],[self.filename])
retlines = retlines + ls[0]
retdeps = retdeps + ls[1]
except:
- print "warning: can't find %s, let's hope latex will" \
- % r_inp.groups()[0]
+ sys.stderr.write("warning: can't find %s, let's hope latex will\n" % r_inp.groups()[0])
retlines.append (line)
elif r_inc:
try:
retlines = retlines + ls[0]
retdeps = retdeps + ls[1]
except:
- print "warning: can't find %s, let's hope latex will" \
- % r_inc.groups()[0]
+ sys.stderr.write("warning: can't find %s, let's hope latex will" % r_inc.groups()[0])
retlines.append (line)
else:
+ # This code should be rewritten, it looks terrible
r_mud = defined_mudela_cmd_re.search(line)
if r_mud:
+ # TODO document this
ss = "\\\\verb(?P<xx>[^a-zA-Z])\s*\\\\%s\s*(?P=xx)" \
% re.escape(r_mud.group()[1:])
+ # just append the line if the command is inside \verb|..|
if re.search(ss, line):
retlines.append(line)
continue
while 1:
opts = r_mud.groups()[2]
+ cmd_start_idx = r_mud.span()[0]
+ if cmd_start_idx > 0:
+ retlines.append(line[:cmd_start_idx])
+
+ cmd_data, rest_idx = extract_command(line[cmd_start_idx:])
+ rest_idx = rest_idx + cmd_start_idx + 1
if opts == None:
opts = ''
else:
opts = ', '+opts
- (start, rest) = string.split(line, r_mud.group(), 1)
- retlines.append(start+'\n')
+
v = string.split(defined_mudela_cmd[r_mud.groups()[0]], '\n')
for l in v[1:-1]:
- l = re.sub(r'\\fontoptions', opts, l)
- l = re.sub(r'\\maininput', r_mud.groups()[3], l)
+ l = string.replace(l, '\\fontoptions', opts)
+ l = string.replace(l, '\\maininput', cmd_data)
retlines.append(l)
- r_mud = defined_mudela_cmd_re.search(rest)
+ r_mud = defined_mudela_cmd_re.search(line[rest_idx:])
if not r_mud:
- retlines.append(rest)
+ rs = line[rest_idx:]
+ while rs[0] == " ":
+ rs = rs[1:]
+ if rs != "\n":
+ retlines.append(line[rest_idx:])
break;
- line = rest
+ line = line[rest_idx:]
else:
retlines.append (line)
- else:
- return (retlines, retdeps)
+ return (retlines, retdeps)
class Main_tex_input(Tex_input):
def do_it(self):
preMudelaDef = postMudelaDef = 0
(lines, self.deps) = self.get_lines ()
+ #HACK
+ latex_verbatim = 0
for line in lines:
if documentclass_re.search (line):
p = self.extract_papersize_from_documentclass (line)
preMudelaDef = 1
elif postMudelaExample_re.search (line):
postMudelaDef = 1
+ elif begin_verbatim_re.search (line):
+ latex_verbatim = 1
+ elif end_verbatim_re.search (line):
+ latex_verbatim = 0
elif begin_document_re.search (line):
if not preMudelaDef:
self.mudtex.write ('\\def\\preMudelaExample{}\n')
if not postMudelaDef:
self.mudtex.write ('\\def\\postMudelaExample{}\n')
- elif begin_mudela_re.search (line):
+
+ elif mudela_file_re.search(line):
+ r = mudela_file_re.search(line)
+
+ self.mudela = Mudela_output(self.gen_basename())
+ fn = r.group (1)
+ full_path = find_file (fn)
+ if not full_path:
+ sys.stderr.write("error: can't find file '%s'\n" % fn)
+ sys.exit (1)
+
+ f = open (full_path, 'r')
+ lines =f.readlines ()
+ self.mudela.write ('%% This is a copy of file %s\n' % full_path)
+ for x in lines:
+ self.mudela.write (x)
+ r = file_ext_re.search(fn)
+ if r:
+ if r.group(1) == 'fly':
+ self.mudela.optlist.append('multiline')
+ stat =self.mudela.close ()
+ if stat:
+ sys.stdout.write("(File %s needs recompiling)\n" % full_path)
+ self.mudtex.write (self.mudela.insert_me_string())
+ self.deps.append (full_path)
+ del self.mudela
+ self.mudela = None
+ self.fine_count = self.fine_count + 1
+ continue
+ elif begin_mudela_re.search (line) and not latex_verbatim:
Props.clear_for_new_block()
if __debug__:
if self.mode == 'mudela':
if r:
o = r.group()[1:][:-1]
optlist = re.compile('[ ,]*').split(o)
+ m = intertext_re.search(r.group())
+ if m:
+ self.intertext = m.groups()[0]
+ else:
+ self.intertext = None
else:
optlist = []
- if ('verbatim' in optlist) or (Props.force_verbatim_b):
+ if ('veryverbatim' in optlist):
+ self.verbatim = 2
+ elif ('verbatim' in optlist) or (Props.force_verbatim_b):
self.verbatim = 1
- self.mudtex.open_verbatim (line)
- continue
else:
self.verbatim = 0
- self.mudela = Mudela_output (self.gen_basename ())
- elif end_mudela_re.search (line):
+ if self.verbatim:
+ self.mudtex.open_verbatim (line, self.verbatim)
+ self.mudela = Mudela_output (self.gen_basename ())
+ self.mudela.write (line)
+ continue
+ elif end_mudela_re.search (line) and not latex_verbatim:
if __debug__:
if self.mode != 'mudela':
raise AssertionError
- if self.mudela:
- self.mudela.close ()
- self.mudtex.write (self.mudela.insert_me_string())
- del self.mudela
- self.mudela = None
- self.fine_count = self.fine_count + 1
- else:
- self.mudtex.write (line)
+
+ if self.verbatim:
+ if self.verbatim == 2:
+ self.mudtex.write (line)
self.mudtex.close_verbatim ()
+ self.mudela.close ()
+ if self.verbatim and self.intertext:
+ self.mudtex.write(self.intertext)
+ self.mudtex.write (self.mudela.insert_me_string())
+ del self.mudela
+ self.mudela = None
+ self.fine_count = self.fine_count + 1
self.mode = 'latex'
continue
- if self.mode == 'mudela' and not self.verbatim:
+
+ if self.mode == 'mudela':
self.mudela.write (line)
+ if self.verbatim:
+ self.mudtex.write (line)
else:
self.mudtex.write (line)
self.set_sections(line)
--force-mudela-fontsize=??pt force fontsize for all inline mudela
--force-verbatim make all mudela verbatim\n
--dependencies write dependencies
+ --include include path
--init mudela-book initfile
"""
)
def write_deps (fn, out, deps):
- out_fn = outdir + '/' + fn
- print '`writing `%s\'\n\'' % out_fn
+ out_fn = os.path.join (outdir, fn)
+
+ sys.stdout.write('writing `%s\'\n' % out_fn)
f = open (out_fn, 'w')
- f.write ('%s: %s\n'% (outdir + '/' + out + '.dvi',
+ target = re.sub (os.sep + os.sep, os.sep, os.path.join (outdir, out + '.latex'))
+ f.write ('%s: %s\n'% (target,
reduce (lambda x,y: x + ' '+ y, deps)))
f.close ()
outname = ''
try:
(options, files) = getopt.getopt(
- sys.argv[1:], 'hd:o:', ['outdir=', 'outname=',
+ sys.argv[1:], 'hd:o:I:', ['outdir=', 'outname=',
'default-mudela-fontsize=',
'force-mudela-fontsize=',
- 'help', 'dependencies',
+ 'help', 'dependencies', 'include=',
'force-verbatim', 'init='])
except getopt.error, msg:
- print "error:", msg
+ sys.stderr.write("error: %s" % msg)
sys.exit(1)
do_deps = 0
for opt in options:
o = opt[0]
a = opt[1]
- if o == '--outname' or o == '-o':
+ if o == '--include' or o == '-I':
+ include_path.append (a)
+ elif o == '--outname' or o == '-o':
if len(files) > 1:
#HACK
- print "Mudela-book is confused by --outname on multiple files"
+ sys.stderr.write("Mudela-book is confused by --outname on multiple files")
sys.exit(1)
outname = a
- if o == '--outdir' or o == '-d':
+ elif o == '--outdir' or o == '-d':
outdir = a
- if o == '--help' or o == '-h':
+ elif o == '--help' or o == '-h':
help ()
- if o == '--dependencies':
+ elif o == '--dependencies':
do_deps = 1
- if o == '--default-mudela-fontsize':
+ elif o == '--default-mudela-fontsize':
if not fontsize_pt2i.has_key(a):
- print "Error: illegal fontsize:", a
- print " accepted fontsizes are: 11pt, 13pt, 16pt, 20pt, 26pt"
+ sys.stderr.write("Error: invalid fontsize: %s" % a)
+ sys.stderr.write(" accepted fontsizes are: 11pt, 13pt, 16pt, 20pt, 26pt")
sys.exit()
Props.setMudelaFontsize(fontsize_pt2i[a], 'init')
- if o == '--force-mudela-fontsize':
+ elif o == '--force-mudela-fontsize':
if not fontsize_pt2i.has_key(a):
- print "Error: illegal fontsize:", a
- print " accepted fontsizes are: 11pt, 13pt, 16pt, 20pt, 26pt"
+ sys.stderr.write("Error: invalid fontsize: %s" % a)
+ sys.stderr.write(" accepted fontsizes are: 11pt, 13pt, 16pt, 20pt, 26pt")
sys.exit()
Props.force_mudela_fontsize = fontsize_pt2i[a]
- if o == '--force-verbatim':
+ elif o == '--force-verbatim':
Props.force_verbatim_b = 1
- if o == '--init':
+ elif o == '--init':
initfile = a
if outdir[-1:] != '/':
outdir = outdir + '/'
- std_init_filename = ''
- for p in string.split(os.environ['LILYINCLUDE'], ':'):
- try:
- std_init_filename = p+os.sep+'mudela-book-defs.py'
- break
- except:
- continue
- if std_init_filename == '':
- print "error: Can't find mudela-book-defs.py"
- os.exit(-1)
- f = open(std_init_filename)
- s = f.read()
- f.close()
- defined_mudela_cmd = eval(s)
-
+ # r""" ... """ means: leave escape seqs alone.
+ defined_mudela_cmd = {'mudela': r"""
+\begin{mudela}[eps, singleline \fontoptions]
+ \context Staff <
+ \context Voice{
+ \maininput
+ }
+ >
+\end{mudela}
+"""}
if initfile != '':
f = open(initfile)
s = f.read()
defined_mudela_cmd[i] = d[i]
del d
- c = defined_mudela_cmd.keys()[0]
- for x in defined_mudela_cmd.keys()[1:]:
- c = c + '|'+x
+ c = string.join (defined_mudela_cmd.keys(), '|')
+
defined_mudela_cmd_re = re.compile("\\\\(%s)(\[(\d*pt)\])*{([^}]*)}" %c)
if not os.path.isdir(outdir):
my_depname = my_outname + '.dep'
inp = Main_tex_input (input_filename, my_outname)
inp.do_it ()
-# os.system('latex %s/%s.latex' % (outdir, my_outname))
if do_deps:
write_deps (my_depname, my_outname, inp.deps)