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