#!/usr/bin/env python def usage(): print ''' slyce.py makes smaller files out of a master.ly file by copying the master file and retaining one center block of text in each copy. In this way you can reuse the boilerplate before and after the group of text blocks in the master. It can save much typing when there are many short files to put into a LilyPond book. Usage: slyce.py file.ly (or python slyce.py etc...) slyce.py --help (for more detailed help. Page the dump with " | more", redirect " > file", or scroll up.) Slyce will prepend the name of the file, the name of the master, and a date string to each child file in a %{long comment%}. (c)2004 David Raleigh Arnold under GNU 20050430 ''' sys.exit(0) def helpPage(): print ''' ########## slyce.py help ########## Here is an example source modified from one of the templates that comes with LilyPond. For reference, the first chunk is the head: ########## Head: * %{ master.ly %} <-- Lilypond long comments are not copied \\version "2.3.22" except as noted. \\header { \\title "Dy Abetic Koma" \\author "Noah Pancreas" } \\include "english.ly" definition = {etc.} definition = {etc.} definition = {etc.} ########## Middle: "group" ** %:{begin_group <-- This, at the beginning of the line, marks the group. There is no space within it. The rest of the line is not copied. %: cake.ly <-- Target filename preceded by "%:" at melody = \\relative c' { the beginning of a line. Everything a b c d following "%:" is a filename. a b c d a b c d a b c d } text = \\lyricmode { Aaa Bee Cee Dee Aaa Bee Cee Dee Aaa Bee Cee Dee Aaa Bee Cee Dee } harmonies = \\chordmode { a2 c2 a2 c2 a2 c2 a2 c2 } %: sugar.ly <-- This is similar to "cake.ly", preceding. melody <-- The notes are different, but the "melody", text <-- "text" and "harmonies" names are not. harmonies <-- That does _not_ mean that you can reuse file names in a similar way. %: honey.ly melody text harmonies %: candy.ly <-- anything after "%:" at the beginning of a line will melody be the name of a new file. text harmonies %: molasses.ly melody text harmonies end_group%} <-- This line ends the block group. ############ Tail: *** Tail: \\score { << \\context ChordNames { \\set chordChanges = ##t \\harmonies } \\context Voice = one { \\autoBeamOff \\melody } \\lyricsto "one" \\new Lyrics \\text >> \\layout { } \\midi { \\tempo 4=60 } } ############# end of example input file Slyce creates five files: cake.ly, sugar.ly, honey.ly, candy.ly, and molasses.ly. The following outline shows what cake.ly is, ready to "\\lilypondfile" into a lilypond-book. The others are similar. %{ cake.ly from master.ly (date) %} Head-------- contents: melody ... text ... harmonies ... Tail--------- Of course it is easiest to test content by running lilypond on the child file which has the block you are working on. No long comments will be copied to the children. Sly files can be embedded in them. ######### In Conclusion: ######### To read this output, pipe it to a pager: $ slyce.py --help | more or redirect to a file: $ slyce.py --help > help.txt Recap. Run slyce.py on this file: Head %{begin_group %: name1.ly ... %: name2.ly ... %: name3.ly ... end_group%} Tail and these files are generated: name1.ly: name2.ly: name3.ly: Head Head Head ... ... ... Tail Tail Tail See http://www.openguitar.com for more. Enjoy! daveA ''' sys.exit(0) def infile(): '''If there is infile, do it''' if sys.argv[1:]: # if more than argv[0] if os.path.isfile(sys.argv[1]): infile = open(sys.argv[1]) elif sys.argv[1]=='--help': helpPage() else: print "Can't find file " + sys.argv[1] + "." sys.exit(0) else: usage() return infile def striplongcoms(hedge): stripped = "" score = 0 while 1: if hedge=='': break char = hedge[0] hedge = hedge[1:] if char=='%': char = hedge[0] hedge = hedge[1:] if char=='{': score = score + 1 if score==2: print "Two open comments before close. Quitting." sys.exit(0) continue if char=='}': score = score - 1 if score==-1: print "Close comment without open. Quitting." sys.exit(0) continue else: stripped = stripped + '%' + char continue if score==0: stripped = stripped + char if score!=0: print "Long comment not closed. Quitting." sys.exit(0) return stripped def cleanup(fil): ''' Removes blanks including ones with spaces. ''' fil = re.sub('\n\s+\n', '\n\n', fil) # user gets enough white! fil = re.sub('\n\n+', '\n', fil) # user gets enough white! return fil import string, sys, os, time, re try: iter = 0 head = '' group = '' tail = '' stgrp = 1 egrp = 1 for line in infile().readlines(): # split into head, group, and tail iter = iter + 1 line.rstrip() if line.find("%{begin_group")>-1: stgrp = iter print "Start of group: line", iter elif stgrp==1: head = head + line elif line.find("end_group%}")>-1: egrp = iter print "End of group: line", iter elif egrp==1: group = group + line else: tail = tail + line # get long comments out of each section print "Strip and clean up head..." head = striplongcoms(head) head = cleanup(head) print "Strip long comments from group..." group = striplongcoms(group) print "Strip and clean up tail..." tail = striplongcoms(tail) tail = cleanup(tail) print "The following line numbers are of group section only:" ''' problem of single pct starting white line. Writes to file name on the next line if slicing. eliminate the problem by scanning file line by line instead, and giving info to user is a bonus.''' newgroup = '' count = 0 group = (group).split('\n') for line in group: count = count + 1 #print line[:1], line if line[:2]=='%:': print count, line if line[2:].strip()=='': line = '%' print "Error. No filename. Fixing line:", count newgroup = newgroup + line + '\n' group = '\n' + newgroup print "Slicing group..." group = group.split('\n%:')[1:] # cut off stuff before first "\n%:" for index in range(len(group)): #indices of 0123=4 files. file = group[index] # so filename begins first line. chops = file.split('\n', 1) # chop off first line of each filename = chops[0].strip() # jic spaces in filename contents = chops[1].rstrip() # have at least one whiteline? output = open(filename, 'w') timeline = time.asctime(time.localtime(time.time())) + ' %}' + '\n' headline = '%{ ' + filename + " from " + sys.argv[1] + ' ' + timeline str = headline + '\n' + head + contents + '\n' + tail + '\n' print "Writing headline, head, contents, and tail to", filename, "..." output.write(str) print "...Done." except: sys.exit(0) #usage() '''Reachable if Usage commented out: ''' print "python errors:" print sys.exc_type, sys.exc_value