So, I just dived in and started trying to implement duration as a role.
role ABC::Duration { has $.ticks; our multi sub duration-from-parse($top) is export { ABC::Duration.new(:ticks($top.Int || 1)); } our multi sub duration-from-parse($top, $bottom) is export { if +($top // 0) == 0 && +($bottom // 0) == 0 { ABC::Duration.new(:ticks(1/2)); } else { ABC::Duration.new(:ticks(($top.Int || 1) / ($bottom.Int || 1))); } } our method Str() { given $.ticks { when 1 { "---"; } # for debugging, should be "" when 1/2 { "/"; } when Int { .Str; } when Rat { .perl; } die "Duration must be Int or Rat, but it's { .WHAT }"; } } }
The corresponding action for an explicit note length is
method note_length($/) { if $<note_length_denominator> { make duration-from-parse($<top> ?? $<top>[0] !! "", $<note_length_denominator>[0]<bottom>[0]); } else { make duration-from-parse($<top> ?? $<top>[0] !! ""); } }
I messed around a bunch trying to make "e" does Duration
work as a type, but eventually gave up and just coded an ABC::Note type:
class ABC::Note does ABC::Duration { has $.pitch; has $.is-tie; method new($pitch, ABC::Duration $duration, $is-tie) { say :$duration.perl; self.bless(*, :$pitch, :ticks($duration.ticks), :$is-tie); } }
with corresponding action
method mnote($/) { make ABC::Note.new(~$<pitch>, $<note_length> ?? $<note_length>[0].ast !! ABC::Duration.new(1), $<tie> eq '-'); }
So… that seems to work okay so far. But it does raise some issues for me.
1. I’m finding this $<top> ?? $<top>[0] !! ""
pattern to be very repetitive. Surely there must be a better way to do it? (errr… wait a minute, could that be just $<top>[0] // ""
?)
2. I don’t mind the proliferation of small classes in my code, that corresponds nicely to what we are modeling. But I am starting to mind the corresponding proliferation of small source files. Is there a better way to organize things?
… and that’s all I can remember at the moment. I think I’ll wander off and have breakfast.
Leave a comment