Class: HeadMusic::Notation::LilyPond::AssignmentReader

Inherits:
Object
  • Object
show all
Defined in:
lib/head_music/notation/lily_pond/assignment_reader.rb

Overview

Reads a block of name = value assignments — a \header or a \with — up to its closing brace, yielding the fields the caller named. A field the caller did not name carries no musical meaning and is commonly Scheme (tagline = ##f), so its value is skipped by balance; a field the caller reads must be a quoted string.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cursor, block_name, fields) ⇒ AssignmentReader

Returns a new instance of AssignmentReader.



9
10
11
12
13
# File 'lib/head_music/notation/lily_pond/assignment_reader.rb', line 9

def initialize(cursor, block_name, fields)
  @cursor = cursor
  @block_name = block_name
  @fields = fields
end

Instance Attribute Details

#block_name ⇒ Object (readonly, private)

Returns the value of attribute block_name.



30
31
32
# File 'lib/head_music/notation/lily_pond/assignment_reader.rb', line 30

def block_name
  @block_name
end

#cursor ⇒ Object (readonly, private)

Returns the value of attribute cursor.



30
31
32
# File 'lib/head_music/notation/lily_pond/assignment_reader.rb', line 30

def cursor
  @cursor
end

#fields ⇒ Object (readonly, private)

Returns the value of attribute fields.



30
31
32
# File 'lib/head_music/notation/lily_pond/assignment_reader.rb', line 30

def fields
  @fields
end

Instance Method Details

#message ⇒ Object (private)



32
33
34
# File 'lib/head_music/notation/lily_pond/assignment_reader.rb', line 32

def message
  "#{block_name} expects name = \"value\" assignments"
end

#next_assignment? ⇒ Boolean (private)

Returns:

  • (Boolean)


61
62
63
# File 'lib/head_music/notation/lily_pond/assignment_reader.rb', line 61

def next_assignment?
  cursor.peek&.type == :word && cursor.peek(1)&.type == :equals
end

#read ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/head_music/notation/lily_pond/assignment_reader.rb', line 15

def read
  until cursor.peek.type == :close_brace
    name = cursor.expect(:word, message, unsupported: true)
    cursor.expect(:equals, message, unsupported: true)
    if fields.include?(name.lexeme)
      yield name.lexeme, string_value(name)
    else
      skip_value(name)
    end
  end
  cursor.advance
end

#skip_value(name) ⇒ Object (private)

The value runs to the next assignment or the closing brace, whichever comes first at the outermost level. A value of no tokens is a malformed assignment rather than an empty one.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/head_music/notation/lily_pond/assignment_reader.rb', line 46

def skip_value(name)
  depth = 0
  consumed = 0
  until cursor.eos?
    token = cursor.peek
    break if depth.zero? && (next_assignment? || token.type == :close_brace)

    depth += 1 if token.type == :open_brace
    depth -= 1 if token.type == :close_brace
    cursor.advance
    consumed += 1
  end
  raise cursor.unsupported(message, name) if consumed.zero?
end

#string_value(name) ⇒ Object (private)



36
37
38
39
40
41
# File 'lib/head_music/notation/lily_pond/assignment_reader.rb', line 36

def string_value(name)
  value = cursor.advance
  return value.lexeme if value&.type == :string

  raise cursor.unsupported("#{block_name} values other than quoted strings are not supported", value || name)
end