Class: PatchYAML::QueryParser

Inherits:
Object
  • Object
show all
Defined in:
lib/patchyaml/query_parser.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(query) ⇒ QueryParser

Returns a new instance of QueryParser.



7
8
9
10
11
12
13
# File 'lib/patchyaml/query_parser.rb', line 7

def initialize(query)
  @path = []
  @pos = 0
  @start = 0
  @query = query.each_char.to_a
  @current_path = nil
end

Class Method Details

.parse(query) ⇒ Object



5
# File 'lib/patchyaml/query_parser.rb', line 5

def self.parse(query) = new(query).parse

Instance Method Details

#advanceObject



16
# File 'lib/patchyaml/query_parser.rb', line 16

def advance = @query[@pos].tap { @pos += 1 }

#digit?(value = nil) ⇒ Boolean

Returns:



22
23
24
25
# File 'lib/patchyaml/query_parser.rb', line 22

def digit?(value = nil)
  value = peek if value.nil?
  !value.nil? && value >= "0" && value <= "9"
end

#eof?Boolean

Returns:



17
# File 'lib/patchyaml/query_parser.rb', line 17

def eof? = @pos >= @query.length

#markObject



19
# File 'lib/patchyaml/query_parser.rb', line 19

def mark = @start = @pos

#matches?(char) ⇒ Boolean

Returns:



18
# File 'lib/patchyaml/query_parser.rb', line 18

def matches?(char) = peek == char

#non_separator?Boolean

Returns:



27
28
29
# File 'lib/patchyaml/query_parser.rb', line 27

def non_separator?
  peek != "[" && peek != "."
end

#parseObject



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/patchyaml/query_parser.rb', line 41

def parse
  until eof?
    next parse_index if digit?

    case peek
    when "["
      parse_expression_path
    when "."
      push_path
    else
      parse_simple_path
    end
  end

  @path
end

#parse_expression_pathObject

Raises:



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/patchyaml/query_parser.rb', line 65

def parse_expression_path
  raise ArgumentError, "Expected '[', found #{peek} at index #{@pos}" unless peek == "["

  advance # Consume [
  mark
  advance until peek == "]" || eof?

  raise ArgumentError, "Unterminated expression path at index #{@pos}" unless peek == "]"

  v = value
  advance # Consume ]

  unless v.include? "="
    raise ArgumentError, "Invalid expression path at index #{@start}: Expected expression to be in the format " \
                         "key=value"
  end

  key, value = v.split("=", 2).map(&:strip)

  value = case
  when digit?(value[0])
    value.to_i
  when ['"', "'"].include?(value[0])
    parse_quoted(value)
  when "true"
    true
  when "false"
    false
  else
    value
  end

  @current_path = { kind: :expression, key:, value: }
  push_path
end

#parse_indexObject



58
59
60
61
62
63
# File 'lib/patchyaml/query_parser.rb', line 58

def parse_index
  mark
  advance while digit?
  @current_path = { kind: :index, value: value.to_i }
  push_path
end

#parse_quoted(value) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/patchyaml/query_parser.rb', line 108

def parse_quoted(value)
  quote = value[0]
  escaping = false
  v = []
  chars = value[1...].each_char.to_a

  until chars.empty?
    ch = chars.shift

    if ch == "\\"
      escaping = true
      next
    end

    if escaping
      escaping = false

      case ch
      when quote
        v << quote
      else
        v << "\\"
        v << ch
      end
      next
    end

    break if ch == quote

    v << ch
  end

  unless chars.empty?
    raise ArgumentError, "Invalid expression #{value}: Stray #{chars.shift} after closing #{quote}"
  end

  v.join
end

#parse_simple_pathObject



101
102
103
104
105
106
# File 'lib/patchyaml/query_parser.rb', line 101

def parse_simple_path
  mark
  advance until !non_separator? || eof?
  @current_path = { kind: :simple, value: value }
  push_path
end

#peekObject



15
# File 'lib/patchyaml/query_parser.rb', line 15

def peek = @query[@pos]

#push_pathObject



31
32
33
34
35
36
37
38
39
# File 'lib/patchyaml/query_parser.rb', line 31

def push_path
  if @current_path
    @path << @current_path
    @current_path = nil
  end

  advance if peek == "."
  mark
end

#valueObject



20
# File 'lib/patchyaml/query_parser.rb', line 20

def value = @query[@start...@pos].join