class InevitableCacophony::Parser::Rhythms
Constants
- COMBINATION_TYPE_SENTENCE
Used to recognise how multiple rhythms are to be combined
- COMPOSITE_RHYTHM_SENTENCE
- IS_PRIMARY_COMMENT
- POLYRHYTHM_TYPE_SUMMARY
- SIMPLE_RHYTHM_SENTENCE
Regular expressions used in parsing
- THE_RHYTHM
“the <rhythm>”. Used to match individual components in
COMPOSITE_RHYTHM_SENTENCE
Public Instance Methods
Parses the rhythms from the given form text.
@param form_text [String] @return [Hash{Symbol,Rhythm}]
# File lib/inevitable_cacophony/parser/rhythms.rb, line 50 def parse(form_text) parser = Parser::SectionedText.new(form_text) simple_rhythms = parse_simple_rhythms(parser) composite_rhythms = parse_composite_rhythms(parser, simple_rhythms) simple_rhythms.merge(composite_rhythms) end
Private Instance Methods
@param parser [Parser::SectionedText] @param base_rhythms [Hash{Symbol,Rhythm}] Simpler rhythms that can be used by the composite forms we're parsing. @return [Hash{Symbol,Rhythm}]
# File lib/inevitable_cacophony/parser/rhythms.rb, line 83 def parse_composite_rhythms(parser, base_rhythms) composite_rhythms = {} parser.find_all_paragraphs(COMPOSITE_RHYTHM_SENTENCE).each do |paragraph| # TODO: write something that handles named matches a bit better intro_sentence = paragraph.find(COMPOSITE_RHYTHM_SENTENCE).match(COMPOSITE_RHYTHM_SENTENCE) polyrhythm_name = intro_sentence[:name].to_sym primary, *secondaries = parse_polyrhythm_components(intro_sentence[:patterns], base_rhythms) combination_type = paragraph.find(COMBINATION_TYPE_SENTENCE).match(COMBINATION_TYPE_SENTENCE)[:type_summary] unless combination_type == POLYRHYTHM_TYPE_SUMMARY raise UnrecognisedFormSyntax.new("Unrecognised polyrhythm type #{combination_type}") end composite_rhythms[polyrhythm_name] = Polyrhythm.new(primary, secondaries) end composite_rhythms end
@param reference_string [String] The list of rhythms used, like “the anto (considered the primary), the tak, …” @param base_rhythms [Hash{Symbol, Rhythm}] @return [Array<Rhythm>] The matched rhythms, with the primary one as first element/.
# File lib/inevitable_cacophony/parser/rhythms.rb, line 108 def parse_polyrhythm_components(reference_string, base_rhythms) primary = nil secondaries = [] rhythms_with_comments = reference_string.scan(THE_RHYTHM).map do |rhythm_name, comment| component = base_rhythms[rhythm_name.to_sym] raise(UnknownBaseRhythm.new(rhythm_name)) unless component [component, comment] end primary_rhythms = rhythms_with_comments.select { |_, comment| comment == IS_PRIMARY_COMMENT } if primary_rhythms.length != 1 raise "Unexpected number of primary rhythms in #{primary_rhythms.inspect}; expected exactly 1" end primary = primary_rhythms.first.first remaining_rhythms = rhythms_with_comments - primary_rhythms remaining_comments = remaining_rhythms.map(&:last).compact.uniq if remaining_comments.any? raise "Unrecognised rhythm comment(s) #{remaining_comments.inspect}" end secondaries = remaining_rhythms.select {|_, comment| comment.nil? }.map(&:first) [primary, *secondaries] end
@param parser [Parser::SectionedText] @return [Hash{Symbol,Rhythm}]
# File lib/inevitable_cacophony/parser/rhythms.rb, line 63 def parse_simple_rhythms(parser) rhythms = {} # Find the rhythm description and the following paragraph with the score. parser.sections.each_cons(2) do |rhythm, score| match = SIMPLE_RHYTHM_SENTENCE.match(rhythm) # Make sure we're actually dealing with a rhythm, not some other form element. next unless match rhythms[match[:name].to_sym] = RhythmLine.parse(score) end rhythms end