Class: HeadMusic::Notation::LilyPond::TokenCursor

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

Overview

The readers’ shared place in the token list, and the errors they raise from there. Every ParseError carries the line, column, and lexeme of the token it was raised at, so the cursor builds them rather than each reader that navigates with it.

Instance Method Summary collapse

Constructor Details

#initialize(tokens) ⇒ TokenCursor

Returns a new instance of TokenCursor.



8
9
10
11
# File 'lib/head_music/notation/lily_pond/token_cursor.rb', line 8

def initialize(tokens)
  @tokens = tokens
  @index = 0
end

Instance Method Details

#advance ⇒ Object



17
18
19
20
21
# File 'lib/head_music/notation/lily_pond/token_cursor.rb', line 17

def advance
  token = @tokens[@index]
  @index += 1 if token
  token
end

#eos? ⇒ Boolean

Returns:

  • (Boolean)


23
24
25
# File 'lib/head_music/notation/lily_pond/token_cursor.rb', line 23

def eos?
  @index >= @tokens.length
end

#error(message, token) ⇒ Object



46
47
48
# File 'lib/head_music/notation/lily_pond/token_cursor.rb', line 46

def error(message, token)
  ParseError.new(message, line_number: token&.line, column: token&.column, snippet: token&.lexeme)
end

#expect(type, message, unsupported: false) ⇒ Object



27
28
29
30
31
32
# File 'lib/head_music/notation/lily_pond/token_cursor.rb', line 27

def expect(type, message, unsupported: false)
  token = advance
  return token if token&.type == type

  raise(unsupported ? unsupported(message, token || peek(-1)) : error(message, token || peek(-1)))
end

#peek(offset = 0) ⇒ Object



13
14
15
# File 'lib/head_music/notation/lily_pond/token_cursor.rb', line 13

def peek(offset = 0)
  @tokens[@index + offset]
end

#skip_block ⇒ Object

Consumes a command and the balanced block that follows it.



35
36
37
38
39
40
41
42
43
44
# File 'lib/head_music/notation/lily_pond/token_cursor.rb', line 35

def skip_block
  advance
  expect(:open_brace, "Expected a block")
  depth = 1
  while depth.positive?
    token = advance
    depth += 1 if token.type == :open_brace
    depth -= 1 if token.type == :close_brace
  end
end

#unsupported(message, token) ⇒ Object



50
51
52
# File 'lib/head_music/notation/lily_pond/token_cursor.rb', line 50

def unsupported(message, token)
  UnsupportedFeatureError.new(message, line_number: token&.line, column: token&.column, snippet: token&.lexeme)
end

#unsupported_command(token) ⇒ Object



54
55
56
57
58
59
# File 'lib/head_music/notation/lily_pond/token_cursor.rb', line 54

def unsupported_command(token)
  UnsupportedFeatureError.new(
    %(Unsupported LilyPond feature "\\#{token.lexeme}"),
    line_number: token.line, column: token.column, snippet: "\\#{token.lexeme}"
  )
end

#unsupported_token(token) ⇒ Object

A construct the lexer could name but the model cannot hold. It is reported where the reader meets it, so the same construct inside a block the reader skips costs nothing.



64
65
66
# File 'lib/head_music/notation/lily_pond/token_cursor.rb', line 64

def unsupported_token(token)
  unsupported(%(Unsupported LilyPond feature "#{token.lexeme}"), token)
end