grammar MTK_Grammar
rule root
( space? root:(bare_sequencer | sequencer | timeline) space? ) {
root.value
}
end
rule bare_sequencer
( pattern '' ) {
# it seems at least 2 elements are needed to access the pattern submatch,
# so using an empty string as a workaround
MTK::Sequencers.LegatoSequencer pattern.value
}
end
rule sequencer
( left_curly bare_sequencer right_curly ) {
bare_sequencer.value
}
end
rule timeline
( left_curly timepoint pattern (space timepoint pattern)* right_curly ) {
MTK::Events::Timeline.from_a values_of(:timepoint).zip(values_of :pattern)
}
end
rule pattern
( pattern:(bare_choice | choice) '' ) {
val = first.value
if val.is_a? MTK::Patterns::Pattern then val else MTK::Patterns::Sequence.new [val] end
}
end
rule bare_choice
(
seq:(bare_sequence | sequence) (pipe seq:(bare_sequence | sequence))*
)
{
vals = values_of :seq
if vals.length==1 then vals[0] else MTK::Patterns.Choice(*vals) end
}
end
rule choice
(
left_angle bare_choice right_angle
)
{
bare_choice.value
}
end
rule bare_sequence
(
chain (space chain)*
)
{
vals = values_of :chain
if vals.length==1 then vals[0] else MTK::Patterns.Sequence(*vals) end
}
end
rule sequence
(
left_paren chain (space chain)* right_paren ('*' max_cycles:int)? ('&' element_count:int)?
)
{
chains = values_of(:chain)
options = {}
if element_count
options[:min_elements] = options[:max_elements] = element_count.value
end
options[:max_cycles] = max_cycles.value if max_cycles
if chains.length == 1 and options.empty?
chains[0] # Don't form a chain unnecessarily
else
MTK::Patterns::Sequence.new chains, options
end
}
end
rule foreach
(
sequence ('@' sequence)+
)
{
MTK::Patterns::ForEach.new(values_of :sequence)
}
end
rule chain
( chainable (':' chainable)* ) {
vals = values_of(:chainable)
vals.length == 1 ? vals[0] : MTK::Patterns.Chain(*vals)
}
end
rule chainable
( foreach | choice | sequence | element )
end
rule element
(
elem:( intensity | duration | interval | pitch | pitch_class | variable ) ('*' max_cycles:int)?
)
{
if max_cycles
MTK::Patterns::Sequence.new( [elem.value], max_cycles: max_cycles.value )
else
elem.value
end
}
end
# rule chord # ( left_bracket pitch (space pitch)* right_bracket ) { # MTK::Groups::Chord *values_of(:pitch) # } # end
rule pitch
( pitch_class int ) {
MTK::Core::Pitch[pitch_class.value, int.value]
}
end
rule pitch_class
( diatonic_pitch_class accidental? ) {
MTK::Core::PitchClass[to_s]
}
end
rule diatonic_pitch_class
( [A-G] ) {
MTK::Core::PitchClass[to_s]
}
end
rule accidental
('#'1*2 | 'b'1*2)
end
rule interval
( 'P' [1458] | [Mm] [2367] | 'a' [1-7] | 'd' [2-8] | 'TT' ) {
MTK::Core::Interval.from_s(to_s)
}
end
rule intensity
( ('p'1*3 | 'mp' | 'mf' | 'f'1*3) ('+'|'-')? ) {
MTK::Core::Intensity.from_s(to_s)
}
end
rule duration
( rest:'-'? multiplier:number? [whqesrx] ('.'|'t')* ) {
MTK::Core::Duration.from_s(to_s)
}
end
rule variable
( '$'+ ) {
MTK::Lang::Variable.new(to_s)
}
end
rule timepoint
( number right_arrow ) {
number.value
}
end
rule number
float | rational | int
end
rule float
( '-'? [0-9]+ '.' [0-9]+ ) {
to_f
}
end
rule rational
( numerator:int '/' denominator:[0-9]+ ) {
Rational(numerator.value, denominator.to_i)
}
end
rule int
( '-'? [0-9]+ ) {
to_i
}
end
rule left_paren
( '(' space? ) { nil }
end
rule right_paren
( space? ')' ) { nil }
end
rule left_bracket
( '[' space? ) { nil }
end
rule right_bracket
( space? ']' ) { nil }
end
rule left_curly
( '{' space? ) { nil }
end
rule right_curly
( space? '}' ) { nil }
end
rule left_angle
( '<' space? ) { nil }
end
rule right_angle
( space? '>' ) { nil }
end
rule pipe
( space? '|' space? ) { nil }
end
rule right_arrow
( space? '=>' space? ) { nil }
end
rule space
[\s]+ { nil }
end
end