Skip to content

Commit

Permalink
pitch classes in the grammar must now be upper case
Browse files Browse the repository at this point in the history
  • Loading branch information
adamjmurray committed Aug 4, 2013
1 parent 9c9e84b commit 4a663f9
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 42 deletions.
8 changes: 6 additions & 2 deletions lib/mtk/lang/mtk_grammar.citrus
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,21 @@ grammar MTK_Grammar
end

rule pitch_class
( diatonic_pitch_class ('##' | '#' | 'bb' | 'b')? ) {
( diatonic_pitch_class accidental? ) {
MTK::Core::PitchClass[to_s]
}
end

rule diatonic_pitch_class
( [A-Ga-g] ) {
( [A-G] ) {
MTK::Core::PitchClass[to_s]
}
end

rule accidental
('##' | '#' | 'bb' | 'b')
end

rule interval
( [Pp] [1458] | ('maj'|'min'|[Mm]) [2367] | 'TT' | 'tt' ) {
MTK::Core::Interval.from_s(to_s)
Expand Down
6 changes: 3 additions & 3 deletions lib/mtk/lang/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ class Parser
def self.parse(syntax, root=:root, dump=false)
syntax = syntax.to_s.strip
return nil if syntax.empty?
matcher = ::MTK_Grammar.parse(syntax, :root => root)
puts matcher.dump if dump
matcher.value
match = ::MTK_Grammar.parse(syntax, root: root)
puts match.dump if dump
match.value
end

end
Expand Down
32 changes: 15 additions & 17 deletions lib/mtk/lang/tutorial.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ def initialize
The diatonic pitch classes are the 7 white keys on a piano in a given octave.
They can be used to play, for example, the C major or A natural minor scales.
To play a diatonic pitch class, enter #{'one'.bold.underline} of the following letters
(upper or lower case is allowed):
To play a diatonic pitch class, enter #{'one'.bold.underline} of the following letters:
C D E F G A B c d e f g a b
C D E F G A B
",
validation: :diatonic_pitch_class,
},
Expand All @@ -37,11 +36,9 @@ def initialize
immediately followed by 0, 1, or 2 flats (b) or sharps (#). Each flat (b)
lowers the pitch by a half step and each sharp (#) raises by a half step.
Here are some examples, try entering #{'one'.bold.underline} of the following
(Note, upper or lower case is allowed for the diatonic pitch class but flats
must be lower case):
Here are some examples, try entering #{'one'.bold.underline} of the following:
C# Eb F Gbb A## B c# eb f gbb a## b
C# Bb A## Ebb F
",
validation: :pitch_class,
},
Expand All @@ -61,7 +58,7 @@ def initialize
Here are some examples, try entering #{'one'.bold.underline} of the following:
G3 eb4 F#5 B-1 C##9 dbb6
G3 Eb4 F#5 B-1 C##9 Dbb6
",
validation: :pitch,
},
Expand All @@ -75,7 +72,7 @@ def initialize
Here is an example (Note, unlike previous lessons, enter the #{'entire line'.bold.underline}):
c5 c g5 g a a g
C5 C G5 G A A G
",
validation: :bare_sequence,
},
Expand All @@ -86,11 +83,11 @@ def initialize
number of repetitions. You can also wrap a subsequence of notes with
parentheses and repeat them. Here is an example sequence with repetition:
(e d c)*2 e*3 d c
C*3 D (E D)*2 C
You can also nest repetitions:
You can also nest repetitions (optional whitespace added for readability):
( c (e g)*2 )*2
( C5 (E G)*2 )*2
",
validation: /\*/,
},
Expand All @@ -104,12 +101,13 @@ def run(output)
puts
puts "Welcome to the MTK syntax tutorial!".bold.yellow
puts
puts "MTK is the Music Tool Kit for Ruby."
puts "It has a custom syntax for generating musical patterns."
puts "This tutorial will teach you the basics of the syntax."
puts "MTK is the Music Tool Kit for Ruby, which includes a custom syntax for"
puts "generating musical patterns. This tutorial has a variety of lessons to teach"
puts "you the syntax. It assumes familiarity with music theory."
puts
puts "This is a work in progress. Check back in future versions for more lessons."
puts
puts "#{'Warning!'.bold} This tutorial assumes familiarity with music theory."
puts "This is a work in progress. Check back in the future versions for more lessons."
puts "#{'NOTE:'.bold} MTK syntax is case-sensitive. Upper vs lower-case matters."

output = ensure_output(output)
loop{ select_lesson.run(output) }
Expand Down
26 changes: 6 additions & 20 deletions spec/mtk/lang/parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def parse(*args)

describe ".parse" do
it "can parse a single pitch class and play it" do
sequencer = MTK::Lang::Parser.parse('c')
sequencer = MTK::Lang::Parser.parse('C')
timeline = sequencer.to_timeline
timeline.should == MTK::Events::Timeline.from_h({0 => MTK.Note(C4)})
end
Expand Down Expand Up @@ -68,17 +68,17 @@ def parse(*args)
end

it "parses a chain of choices" do
sequencer = parse("<i|s>:<c|d|e>")
sequencer = parse("<i|s>:<C|D|E>")
sequencer.patterns.should == [ chain( choice(i,s), choice(C,D,E) ) ]
end

it "parses a chain of choices" do
sequencer = parse("(<i|s>:<c|d|e>)&8")
sequencer = parse("(<i|s>:<C|D|E>)&8")
sequencer.patterns.should == [ seq( chain( choice(i,s), choice(C,D,E) ), min_elements:8, max_elements:8 ) ]
end

it "parses the repetition of a basic note property" do
sequencer = parse("c*4")
sequencer = parse("C*4")
sequencer.patterns.should == [ seq(C, max_cycles:4) ]
end
end
Expand Down Expand Up @@ -424,7 +424,7 @@ def parse(*args)

context 'element rule' do
it "parses the repetition of a basic note property as a sequence with a max_cycles option" do
sequence = parse("c*4", :element)
sequence = parse("C*4", :element)
sequence.elements.should == [ C ]
sequence.max_cycles.should == 4
end
Expand Down Expand Up @@ -457,18 +457,10 @@ def parse(*args)
end
end

it "allows for lower case diatonic pitch class names" do
for pitch_class_name in PitchClass::VALID_NAMES
parse(pitch_class_name.downcase, :pitch_class).should == PitchClass[pitch_class_name]
end
end

it "doesn't allow a sharp and flat to be applied to the same diatoonic pitch class" do
it "doesn't allow a sharp and flat to be applied to the same diatonic pitch class" do
for pitch_class_name in %w(A B C D E F G)
lambda{ parse(pitch_class_name + '#b', :pitch_class) }.should raise_error
lambda{ parse(pitch_class_name + 'b#', :pitch_class) }.should raise_error
lambda{ parse(pitch_class_name.downcase + '#b', :pitch_class) }.should raise_error
lambda{ parse(pitch_class_name.downcase + '#b', :pitch_class) }.should raise_error
end
end
end
Expand All @@ -480,12 +472,6 @@ def parse(*args)
parse(diatonic_pitch_class_name, :diatonic_pitch_class).should == PitchClass[diatonic_pitch_class_name]
end
end

it "parses upper case diatonic pitch classes" do
for diatonic_pitch_class_name in %w(a b c d e f g)
parse(diatonic_pitch_class_name, :diatonic_pitch_class).should == PitchClass[diatonic_pitch_class_name]
end
end
end


Expand Down

0 comments on commit 4a663f9

Please sign in to comment.