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