Better error propagation

This commit is contained in:
Matthias Neeracher 2007-10-21 22:06:06 +00:00
parent 2ac1ed067c
commit d530d4ae38

View File

@ -3,45 +3,20 @@
# VLLilypondType.reader - Import lilypond files # VLLilypondType.reader - Import lilypond files
# #
DEBUG = false
require File.dirname($0)+'/plistWriter' require File.dirname($0)+'/plistWriter'
require File.dirname($0)+'/vl' require File.dirname($0)+'/vl'
OUTPUT = {'measures' => []} OUTPUT = {'measures' => []}
CHORDS = []
# NOTES = []
# Lex STANZAS= []
# MEAS = []
tokens = []
INFILE.each do |line|
line.chomp!.sub!(/%.*/, "")
line.scan(%r$\G\s*(\{|\}|\(|\)|\||=|~|<<|>>|--|#'|#\(|##t|##f|\\\w+|\".*?\"|\w[-+\w\d.',:*/]+|.)$) do |token|
tokens.push(token[0])
end
end
#
# Parse
#
nestLevel = 0
block = nil
level = -1
stack = []
repeats = []
chords = []
notes = []
lyrics = []
stanzas = []
lastDur = 1
tied = false
repeat = 0
timeNum = 4
timeDenom = 4
key = 0
mode = 'minor'
lyricFlags= 0
$RELPITCH = 0 $RELPITCH = 0
$timeNum = 4
$timeDenom= 4
$key = 0
$mode = '\major'
PITCH = { PITCH = {
?c => 0, ?c => 0,
@ -202,33 +177,58 @@ def lySteps(steps)
return s return s
end end
while tokens.length > 0 def parseLilypond
token = tokens.shift
# #
# Title, composer, etc. # Lex
# #
if tokens[0] == '=' tokens = []
case token INFILE.each do |line|
when 'title','composer','poet' line.chomp!.sub!(/%.*/, "")
key = token=='poet' ? 'lyricist' : token line.scan(%r$\G\s*(\{|\}|\(|\)|\||=|~|<<|>>|--|#'|#\(|##t|##f|\\\w+|\".*?\"|\w[-+\w\d.',:*/]+|.)$) do |token|
value = tokens[1] tokens.push(token[0])
value.sub!(/"(.*)"/, '\1')
OUTPUT[key] = value
tokens[0..1]= nil
redo
end end
end end
#
# Parse
#
nestLevel = 0
block = nil
level = -1
stack = []
repeats = []
lyrics = []
lastDur = 1
tied = false
repeat = 0
lyricFlags= 0
case block while tokens.length > 0
when '\header', '\paper' token = tokens.shift
# Ignore
when '\chords', '\chordmode'
# #
# Possibly chords # Title, composer, etc.
# #
if token.downcase =~ %r{^ if tokens[0] == '='
case token
when 'title','composer','poet'
key = token=='poet' ? 'lyricist' : token
value = tokens[1]
value.sub!(/"(.*)"/, '\1')
OUTPUT[key] = value
tokens[0..1]= nil
redo
end
end
case block
when '\header', '\paper'
# Ignore
when '\chords', '\chordmode'
#
# Possibly chords
#
if token.downcase =~ %r{^
([rs] | # Rest ([rs] | # Rest
[a-g](?:[ei]?s)? # g, ges, fis, es, as [a-g](?:[ei]?s)? # g, ges, fis, es, as
) )
@ -240,24 +240,24 @@ while tokens.length > 0
[a-g](?:[ei]?s)? # Root: a, bes, fis, as [a-g](?:[ei]?s)? # Root: a, bes, fis, as
))? ))?
$}x $}x
pitch = lyPitch($1, 60) pitch = lyPitch($1, 60)
dur = $2 || lastDur dur = $2 || lastDur
ext = $3 ? lySteps($3) : 0 ext = $3 ? lySteps($3) : 0
root = lyPitch($4, 48) root = lyPitch($4, 48)
lastDur = dur lastDur = dur
d = lyDur(dur) d = lyDur(dur)
chord = {'pitch' => pitch, 'root' => root, 'steps' => ext, chord = {'pitch' => pitch, 'root' => root, 'steps' => ext,
'durNum'=> d[0], 'durDenom' => d[1]} 'durNum'=> d[0], 'durDenom' => d[1]}
p token, chord if DEBUG p token, chord if $DEBUG
chords.push(chord) CHORDS.push(chord)
redo redo
end end
when 'voice' when 'voice'
# #
# Possibly notes # Possibly notes
# #
if token.downcase =~ %r{^ if token.downcase =~ %r{^
([rs] | # Rest ([rs] | # Rest
[a-g](?:[ei]?s)? # g, ges, fis, es, as [a-g](?:[ei]?s)? # g, ges, fis, es, as
[',]* # g''' [',]* # g'''
@ -266,279 +266,288 @@ while tokens.length > 0
(?:\*\d+/\d+)? # *3/4 (?:\*\d+/\d+)? # *3/4
)? )?
$}x $}x
pitch = lyPitch($1) pitch = lyPitch($1)
dur = $2 || lastDur dur = $2 || lastDur
lastDur = dur lastDur = dur
d = lyDur(dur) d = lyDur(dur)
note = {'pitch' => pitch, 'durNum'=> d[0], 'durDenom' => d[1]} note = {'pitch' => pitch, 'durNum'=> d[0], 'durDenom' => d[1]}
note['tied'] = VL::TiedWithPrev if tied note['tied'] = VL::TiedWithPrev if tied
p token, note if DEBUG p token, note if $DEBUG
notes.push(note) NOTES.push(note)
tied = false tied = false
redo redo
elsif token == '~' elsif token == '~'
if note = notes.last if note = NOTES.last
note['tied'] ||= 0 note['tied'] ||= 0
note['tied'] |= VL::TiedWithNext note['tied'] |= VL::TiedWithNext
end
tied = true
elsif token == '\repeat' && (tokens[0] == 'volta' || tokens[0] == fold) &&
tokens[1] =~ /^\d+$/
stack.push([block, level, "repeat"])
level = nestLevel
repeats.push(repeat)
repeat = tokens[1].to_i
NOTES.push({'begin-repeat' => true, 'times' => repeat})
tokens[0..1] = nil
redo
elsif token == '\alternative'
inEndings = true
stack.push([block, level, "endings"])
level = nestLevel+1
voltas = 0
curVoltas = nil
NOTES.push({'begin-ending' => true})
elsif token == '\times' && tokens[0] =~ %r|^(\d+)/(\d+)|
$timesNum = $1.to_i
$timesDen = $2.to_i
stack.push([block, level, "times"])
level = nestLevel
end
when '\lyricmode'
if token == '--'
lyrics.last[1] |= VL::TiedWithNext if lyrics.size > 0
lyricFlags = VL::TiedWithPrev
elsif token == '\skip'
p ["", 0] if $DEBUG
lyrics.push ["", 0]
lyricFlags = 0
if tokens[0] =~ /\d+/
tokens[0..0] = nil
end
elsif token =~ /\\skip\d+/
p ["", 0] if $DEBUG
lyrics.push ["", 0]
lyricFlags = 0
elsif token =~ /"(.*)"/
p [$1, lyricFlags] if $DEBUG
lyrics.push [$1, lyricFlags]
lyricFlags = 0
elsif token =~ /^\w.*/
p [token, lyricFlags] if $DEBUG
lyrics.push [token, lyricFlags]
lyricFlags = 0
end end
tied = true
elsif token == '\repeat' && (tokens[0] == 'volta' || tokens[0] == fold) &&
tokens[1] =~ /^\d+$/
stack.push([block, level, "repeat"])
level = nestLevel
repeats.push(repeat)
repeat = tokens[1].to_i
notes.push({'begin-repeat' => true, 'times' => repeat})
tokens[0..1] = nil
redo
elsif token == '\alternative'
inEndings = true
stack.push([block, level, "endings"])
level = nestLevel+1
voltas = 0
curVoltas = nil
notes.push({'begin-ending' => true})
elsif token == '\times' && tokens[0] =~ %r|^(\d+)/(\d+)|
$timesNum = $1.to_i
$timesDen = $2.to_i
stack.push([block, level, "times"])
level = nestLevel
end end
when '\lyricmode'
if token == '--' #
lyrics.last[1] |= VL::TiedWithNext if lyrics.size > 0 # Nesting levels
lyricFlags = VL::TiedWithPrev #
elsif token == '\skip' case token
p ["", 0] if DEBUG when '{', '<<'
lyrics.push ["", 0] nestLevel += 1
lyricFlags = 0 when '}', '>>'
if tokens[0] =~ /\d+/ nestLevel -= 1
if nestLevel <= level
if lv = stack.pop
block = lv[0]
level = lv[1]
type = lv[2]
else
block = nil
level = -1
end
if type == "repeat"
if tokens[0] != '\alternative'
NOTES.push({'end-repeat' => true})
repeat = repeats.pop
end
elsif type == "endings"
last = tokens[0] == '}'
if last
curVoltas = ((1<<repeat) - 1) & ~voltas
elsif !curVoltas
curVoltas = 1
while (voltas&curVoltas) != 0
curVoltas <<= 1
end
end
NOTES.push({'end-ending' => true, 'volta' => curVoltas,
'last'=>last})
voltas |= curVoltas
curVoltas = 0
if last
repeat = repeats.pop
else
NOTES.push({'begin-ending' => true})
stack.push([block, level, "endings"])
level = nestLevel
end
elsif type == "times"
$timesNum = 1
$timesDen = 1
end
end
when '\chords', '\header', '\paper', '\lyricmode'
stack.push([block, level, ""])
block = token
level = nestLevel
STANZAS.push(lyrics= []) if block == '\lyricmode'
when '\chordmode'
stack.push([block, level, ""])
block = '\chords'
level = nestLevel
when '\lyricsto'
tokens[0..3] = nil
when '\new'
if tokens[0] == "Lyrics"
stack.push([block, level, ""])
block = '\lyricmode'
level = nestLevel
STANZAS.push(lyrics= [])
tokens[0..0] = nil tokens[0..0] = nil
end end
elsif token =~ /\\skip\d+/ when '\relative'
p ["", 0] if DEBUG stack.push([block, level, ""])
lyrics.push ["", 0] if tokens[0] =~ /[a-g](?:[ei]?s)?[',]*/
lyricFlags = 0 $RELPITCH = lyPitch(tokens[0], 48)
elsif token =~ /"(.*)"/ tokens[0..0] = nil
p [$1, lyricFlags] if DEBUG
lyrics.push [$1, lyricFlags]
lyricFlags = 0
elsif token =~ /^\w.*/
p [token, lyricFlags] if DEBUG
lyrics.push [token, lyricFlags]
lyricFlags = 0
end
end
#
# Nesting levels
#
case token
when '{', '<<'
nestLevel += 1
when '}', '>>'
nestLevel -= 1
if nestLevel <= level
if lv = stack.pop
block = lv[0]
level = lv[1]
type = lv[2]
else else
block = nil $RELPITCH = 60
level = -1
end end
if type == "repeat" block = 'voice'
if tokens[0] != '\alternative' level = nestLevel
notes.push({'end-repeat' => true}) when '\time'
repeat = repeats.pop if tokens[0] =~ %r{(\d+)/(\d+)}
end $timeNum = $1.to_i
elsif type == "endings" $timeDenom = $2.to_i
last = tokens[0] == '}' tokens[0..0] = nil
if last
curVoltas = ((1<<repeat) - 1) & ~voltas
elsif !curVoltas
curVoltas = 1
while (voltas&curVoltas) != 0
curVoltas <<= 1
end
end
notes.push({'end-ending' => true, 'volta' => curVoltas,
'last'=>last})
voltas |= curVoltas
curVoltas = 0
if last
repeat = repeats.pop
else
notes.push({'begin-ending' => true})
stack.push([block, level, "endings"])
level = nestLevel
end
elsif type == "times"
$timesNum = 1
$timesDen = 1
end end
if block != 'voice'
stack.push([block, level, ""])
block = 'voice'
level = nestLevel-1
end
when '\key'
p = lyPitch(tokens[0], 0)
$mode = tokens[1]
$key = $mode == '\minor' ? MINORKEY[p] : MAJORKEY[p]
tokens[0..1] = nil
if block != 'voice'
stack.push([block, level, ""])
block = 'voice'
level = nestLevel-1
end
when '\repeat'
tokens[0..1] = nil
when '\alternative'
end end
when '\chords', '\header', '\paper', '\lyricmode'
stack.push([block, level, ""])
block = token
level = nestLevel
stanzas.push(lyrics= []) if block == '\lyricmode'
when '\chordmode'
stack.push([block, level, ""])
block = '\chords'
level = nestLevel
when '\lyricsto'
tokens[0..3] = nil
when '\new'
if tokens[0] == "Lyrics"
stack.push([block, level, ""])
block = '\lyricmode'
level = nestLevel
stanzas.push(lyrics= [])
tokens[0..0] = nil
end
when '\relative'
stack.push([block, level, ""])
if tokens[0] =~ /[a-g](?:[ei]?s)?[',]*/
$RELPITCH = lyPitch(tokens[0], 48)
tokens[0..0] = nil
else
$RELPITCH = 60
end
block = 'voice'
level = nestLevel
when '\time'
if tokens[0] =~ %r{(\d+)/(\d+)}
timeNum = $1.to_i
timeDenom = $2.to_i
tokens[0..0] = nil
end
if block != 'voice'
stack.push([block, level, ""])
block = 'voice'
level = nestLevel-1
end
when '\key'
p = lyPitch(tokens[0], 0)
mode = tokens[1]
key = mode == '\minor' ? MINORKEY[p] : MAJORKEY[p]
tokens[0..1] = nil
if block != 'voice'
stack.push([block, level, ""])
block = 'voice'
level = nestLevel-1
end
when '\repeat'
tokens[0..1] = nil
when '\alternative'
end end
end end
measureLen = VL::Fract.new(timeNum, timeDenom)
#
# Make measures
#
measCount= -1
measures = []
def peek(where, what) def peek(where, what)
return where.first && where.first[what] return where.first && where.first[what]
end end
while notes.size > 0 || chords.size > 0 def makeMeasures
measCount += 1 measureLen = VL::Fract.new($timeNum, $timeDenom)
meas = {}
meas['measure'] = measCount #
meas['properties'] = 0 # Make measures
if peek(notes, 'begin-repeat') #
rep = notes.shift measCount= -1
meas['begin-repeat'] = {'times' => rep['times']}
end while NOTES.size > 0 || CHORDS.size > 0
if peek(notes, 'begin-ending') measCount += 1
notes.shift meas = {}
meas['begin-ending'] = {} meas['measure'] = measCount
end meas['properties'] = 0
if chords.size > 0 if peek(NOTES, 'begin-repeat')
mchords = [] rep = NOTES.shift
len = VL::Fract.new(0, 1) meas['begin-repeat'] = {'times' => rep['times']}
while len < measureLen && chords.size > 0
chord = chords.shift
chordLen = VL::Fract.new(chord['durNum'], chord['durDenom'])
if len+chordLen > measureLen
remLen = len+chordLen-measureLen
chordLen -= remLen
remChord = chord.dup
remChord['durNum'] = remLen.num
remChord['durDenom'] = remLen.denom
chords.unshift(remChord)
end
mchords.push(chord)
len += chordLen
end end
meas['chords'] = mchords if peek(NOTES, 'begin-ending')
end NOTES.shift
if notes.size > 0 meas['begin-ending'] = {}
mnotes = [] end
len = VL::Fract.new(0, 1) if CHORDS.size > 0
while len < measureLen && notes.size > 0 mchords = []
note = notes.shift len = VL::Fract.new(0, 1)
noteLen = VL::Fract.new(note['durNum'], note['durDenom']) while len < measureLen && CHORDS.size > 0
if len+noteLen > measureLen chord = CHORDS.shift
remLen = len+noteLen-measureLen chordLen = VL::Fract.new(chord['durNum'], chord['durDenom'])
noteLen -= remLen if len+chordLen > measureLen
remNote = note.dup remLen = len+chordLen-measureLen
remNote['durNum'] = remLen.num chordLen -= remLen
remNote['durDenom'] = remLen.denom remChord = chord.dup
remNote['tied'] = (remNote['tied'] || 0) | VL::TiedWithPrev remChord['durNum'] = remLen.num
note['tied'] = (note['tied'] || 0) | VL::TiedWithNext remChord['durDenom'] = remLen.denom
notes.unshift(remNote) CHORDS.unshift(remChord)
end
mchords.push(chord)
len += chordLen
end end
if note['pitch'] != VL::NoPitch && meas['chords'] = mchords
(!note['tied'] || (note['tied'] & VL::TiedWithPrev) == 0) end
ly = [] if NOTES.size > 0
stanza = 0 mnotes = []
stanzas.each_index do |i| len = VL::Fract.new(0, 1)
lyrics = stanzas[i] while len < measureLen && NOTES.size > 0
if lyrics.size > 0 note = NOTES.shift
stanza = i+1 noteLen = VL::Fract.new(note['durNum'], note['durDenom'])
syll = lyrics.shift if len+noteLen > measureLen
ly.push({'text' => syll[0].gsub('_', ' '), 'kind' => syll[1]}) remLen = len+noteLen-measureLen
else noteLen -= remLen
ly.push({'text' => '', 'kind' => 0}) remNote = note.dup
remNote['durNum'] = remLen.num
remNote['durDenom'] = remLen.denom
remNote['tied'] = (remNote['tied'] || 0) | VL::TiedWithPrev
note['tied'] = (note['tied'] || 0) | VL::TiedWithNext
NOTES.unshift(remNote)
end
if note['pitch'] != VL::NoPitch &&
(!note['tied'] || (note['tied'] & VL::TiedWithPrev) == 0)
ly = []
stanza = 0
STANZAS.each_index do |i|
lyrics = STANZAS[i]
if lyrics.size > 0
stanza = i+1
syll = lyrics.shift
ly.push({'text' => syll[0].gsub('_', ' '), 'kind' => syll[1]})
else
ly.push({'text' => '', 'kind' => 0})
end
end end
if stanza < ly.size
ly[stanza..-1] = nil
end
note['lyrics'] = ly if stanza > 0
end end
if stanza < ly.size mnotes.push(note)
ly[stanza..-1] = nil len += noteLen
end
note['lyrics'] = ly if stanza > 0
end end
mnotes.push(note) meas['melody'] = mnotes
len += noteLen
end end
meas['melody'] = mnotes if peek(NOTES, 'end-ending')
ending = NOTES.shift
meas['end-ending'] = {'last' => ending['last'], 'volta' => ending['volta']}
end
if peek(NOTES, 'end-repeat')
NOTES.shift
meas['end-repeat'] = {}
end
MEAS.push(meas)
end end
if peek(notes, 'end-ending')
ending = notes.shift
meas['end-ending'] = {'last' => ending['last'], 'volta' => ending['volta']}
end
if peek(notes, 'end-repeat')
notes.shift
meas['end-repeat'] = {}
end
measures.push(meas)
end end
OUTPUT['measures'] = measures begin
OUTPUT['properties'] = [{ parseLilypond
'key' => key, makeMeasures
'mode' => mode == '\minor' ? -1 : 1,
'timeNum' => timeNum,
'timeDenom' => timeDenom
}]
writePlist($stdout, OUTPUT) OUTPUT['measures'] = MEAS
OUTPUT['properties'] = [{
'key' => $key,
'mode' => $mode == '\minor' ? -1 : 1,
'timeNum' => $timeNum,
'timeDenom' => $timeDenom
}]
writePlist($stdout, OUTPUT)
rescue => except
$stderr.print except.message, "\n", except.backtrace.join("\n"), "\n"
end
# Local Variables: # Local Variables:
# mode:ruby # mode:ruby