Class: HeadMusic::Notation::LilyPond::ContextReader
- Inherits:
-
Object
- Object
- HeadMusic::Notation::LilyPond::ContextReader
- Defined in:
- lib/head_music/notation/lily_pond/context_reader.rb
Overview
Reads the declaration of a Staff or Voice context — \new, the optional context name, and the \with block that can name the instrument — and the rules about where a context may appear. The music inside it is read by the MusicReader that opened this one.
Constant Summary collapse
- CONTEXT_TYPES =
%w[Staff Voice].freeze
- WITH_FIELDS =
%w[instrumentName].freeze
Instance Attribute Summary collapse
-
#cursor ⇒ Object
readonly
private
Returns the value of attribute cursor.
-
#document ⇒ Object
readonly
private
Returns the value of attribute document.
-
#music ⇒ Object
readonly
private
Returns the value of attribute music.
Instance Method Summary collapse
-
#initialize(cursor, document, music) ⇒ ContextReader
constructor
A new instance of ContextReader.
- #read_new(context) ⇒ Object
-
#read_parallel_item(context) ⇒ Object
Each parallel item must open a context; parallel bare music is voice separation the model cannot represent.
-
#read_sequential_new(context) ⇒ Object
LilyPond starts a context where the sequence around it has reached, but a stream always starts at bar one, so a context inside a sequence is only placed correctly when it is the whole of that sequence.
-
#role(context, type) ⇒ Object
private
A Voice with no name of its own belongs to the staff that holds it.
- #skip_context_name ⇒ Object private
- #with_role ⇒ Object private
Constructor Details
#initialize(cursor, document, music) ⇒ ContextReader
Returns a new instance of ContextReader.
11 12 13 14 15 |
# File 'lib/head_music/notation/lily_pond/context_reader.rb', line 11 def initialize(cursor, document, music) @cursor = cursor @document = document @music = music end |
Instance Attribute Details
#cursor ⇒ Object (readonly, private)
Returns the value of attribute cursor.
59 60 61 |
# File 'lib/head_music/notation/lily_pond/context_reader.rb', line 59 def cursor @cursor end |
#document ⇒ Object (readonly, private)
Returns the value of attribute document.
59 60 61 |
# File 'lib/head_music/notation/lily_pond/context_reader.rb', line 59 def document @document end |
#music ⇒ Object (readonly, private)
Returns the value of attribute music.
59 60 61 |
# File 'lib/head_music/notation/lily_pond/context_reader.rb', line 59 def music @music end |
Instance Method Details
#read_new(context) ⇒ Object
17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/head_music/notation/lily_pond/context_reader.rb', line 17 def read_new(context) opener = cursor.advance type = cursor.expect(:word, "\\new expects a context type") unless CONTEXT_TYPES.include?(type.lexeme) raise cursor.unsupported(%(\\new #{type.lexeme} contexts are not supported), type) end skip_context_name child = VoiceContext.new(document, role(context, type), explicit: true) context.children += 1 music.nested(opener) { music.read_expression(child) } child.close end |
#read_parallel_item(context) ⇒ Object
Each parallel item must open a context; parallel bare music is voice separation the model cannot represent.
33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/head_music/notation/lily_pond/context_reader.rb', line 33 def read_parallel_item(context) token = cursor.peek raise cursor.unsupported_token(token) if token.type == :unsupported case token.type == :command && token.lexeme when "new" then read_new(context) when "relative" then music.read_relative { read_parallel_item(context) } when "absolute" then music.read_absolute { read_parallel_item(context) } else raise cursor.unsupported("Simultaneous music without \\new contexts is not supported", token) end end |
#read_sequential_new(context) ⇒ Object
LilyPond starts a context where the sequence around it has reached, but a stream always starts at bar one, so a context inside a sequence is only placed correctly when it is the whole of that sequence.
48 49 50 51 52 53 54 55 |
# File 'lib/head_music/notation/lily_pond/context_reader.rb', line 48 def read_sequential_new(context) raise cursor.unsupported("A \\new context that follows music in a sequence is not supported", cursor.peek) if context.stream.music? read_new(context) return if cursor.peek&.type == :close_brace raise cursor.unsupported("Music that follows a \\new context in a sequence is not supported", cursor.peek) end |
#role(context, type) ⇒ Object (private)
A Voice with no name of its own belongs to the staff that holds it.
62 63 64 |
# File 'lib/head_music/notation/lily_pond/context_reader.rb', line 62 def role(context, type) with_role || (context.role if type.lexeme == "Voice") end |
#skip_context_name ⇒ Object (private)
66 67 68 69 70 71 |
# File 'lib/head_music/notation/lily_pond/context_reader.rb', line 66 def skip_context_name return unless cursor.peek&.type == :equals cursor.advance cursor.expect(:string, "A context name must be a quoted string") end |
#with_role ⇒ Object (private)
73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/head_music/notation/lily_pond/context_reader.rb', line 73 def with_role return unless cursor.peek&.type == :command && cursor.peek.lexeme == "with" cursor.advance cursor.expect(:open_brace, "\\with expects a block") role = nil AssignmentReader.new(cursor, "\\with", WITH_FIELDS).read do |name, value| role = value if name == "instrumentName" end role end |