Module: LazyGraph::PathParser

Defined in:
lib/lazy_graph/path_parser.rb,
lib/lazy_graph/path_parser/path.rb,
lib/lazy_graph/path_parser/path_part.rb,
lib/lazy_graph/path_parser/path_group.rb

Defined Under Namespace

Classes: Path, PathGroup, PathPart

Constant Summary collapse

INDEX_REGEXP =
/\A-?\d+\z/

Class Method Summary collapse

Class Method Details

.append_parsed(parsed, parts) ⇒ Object


96
97
98
99
100
101
102
103
104
# File 'lib/lazy_graph/path_parser.rb', line 96

def append_parsed(parsed, parts)
  if parsed.is_a?(Array)
    parsed.each { |p| parts << p }
  elsif parsed.is_a?(Range)
    parts << PathGroup.new(options: parsed.map { |p| Path.new(parts: [PathPart.new(part: p.to_sym)]) })
  else
    parts << parsed
  end
end

.find_matching_bracket(path, start) ⇒ Object


148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/lazy_graph/path_parser.rb', line 148

def find_matching_bracket(path, start)
  depth = 1
  i = start + 1
  while i < path.length
    if path[i] == '['
      depth += 1
    elsif path[i] == ']'
      depth -= 1
      return i if depth.zero?
    end
    i += 1
  end
  -1 # No matching closing bracket found
end

.handle_char(char, path, structure) ⇒ Object


52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/lazy_graph/path_parser.rb', line 52

def handle_char(char, path, structure)
  case char
  when '.'
    handle_dot(path, structure)
  when '['
    handle_open_bracket(path, structure)
  when ','
    handle_comma(structure)
  when ']'
    handle_close_bracket(structure)
  else
    structure[:buffer] += char
    structure[:i] += 1
  end
end

.handle_close_bracket(structure) ⇒ Object


133
134
135
136
137
138
139
# File 'lib/lazy_graph/path_parser.rb', line 133

def handle_close_bracket(structure)
  raise 'Unbalanced closing bracket in path.' if structure[:buffer].strip.empty?

  parsed = parse_buffer(structure[:buffer].strip)
  append_parsed(parsed, structure[:parts])
  structure[:buffer] = ''.dup
end

.handle_comma(structure) ⇒ Object


124
125
126
127
128
129
130
131
# File 'lib/lazy_graph/path_parser.rb', line 124

def handle_comma(structure)
  unless structure[:buffer].strip.empty?
    parsed = parse_buffer(structure[:buffer].strip)
    append_parsed(parsed, structure[:parts])
    structure[:buffer] = ''.dup
  end
  structure[:i] += 1
end

.handle_dot(path, structure) ⇒ Object


68
69
70
71
72
73
74
75
# File 'lib/lazy_graph/path_parser.rb', line 68

def handle_dot(path, structure)
  # Check if it's part of a range ('..' or '...')
  if path[structure[:i] + 1] == '.'
    handle_range_dot(path, structure)
  else
    handle_single_dot(structure)
  end
end

.handle_open_bracket(path, structure) ⇒ Object


106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/lazy_graph/path_parser.rb', line 106

def handle_open_bracket(path, structure)
  unless structure[:buffer].strip.empty?
    paths = structure[:buffer].strip.split('.').map(&:strip)
    paths.each { |p| structure[:parts] << PathPart.new(part: p.to_sym) }
    structure[:buffer] = ''.dup
  end

  closing_bracket = find_matching_bracket(path, structure[:i])
  raise 'Unbalanced brackets in path.' if closing_bracket == -1

  inside = path[(structure[:i] + 1)...closing_bracket]
  elements = split_by_comma(inside)
  parsed_elements = elements.map { |el| parse_recursive(el, 0) }
  path_group = PathGroup.new(options: parsed_elements)
  structure[:parts] << path_group
  structure[:i] = closing_bracket + 1
end

.handle_range_dot(path, structure) ⇒ Object


77
78
79
80
81
82
83
84
85
# File 'lib/lazy_graph/path_parser.rb', line 77

def handle_range_dot(path, structure)
  if path[structure[:i] + 2] == '.'
    structure[:buffer] += '...'
    structure[:i] += 3
  else
    structure[:buffer] += '..'
    structure[:i] += 2
  end
end

.handle_single_dot(structure) ⇒ Object


87
88
89
90
91
92
93
94
# File 'lib/lazy_graph/path_parser.rb', line 87

def handle_single_dot(structure)
  unless structure[:buffer].strip.empty?
    parsed = parse_buffer(structure[:buffer].strip)
    append_parsed(parsed, structure[:parts])
    structure[:buffer] = ''.dup
  end
  structure[:i] += 1
end

.parse(path, strip_root: false) ⇒ Object

This module is responsible for parsing complex path strings into structured components. Public class method to parse the path string


22
23
24
25
26
27
# File 'lib/lazy_graph/path_parser.rb', line 22

def self.parse(path, strip_root: false)
  return Path::BLANK if path.nil? || path.empty?

  start = strip_root && path.to_s.start_with?('$.') ? 2 : 0
  parse_recursive(path, start)
end

.parse_buffer(buffer) ⇒ Object


190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/lazy_graph/path_parser.rb', line 190

def parse_buffer(buffer)
  if buffer.include?('...')
    parse_range(buffer, '...', true)
  elsif buffer.include?('..')
    parse_range(buffer, '..', false)
  elsif buffer.include?('.')
    paths = buffer.split('.').map(&:strip)
    paths.map { |p| PathPart.new(part: p.to_sym) }
  else
    PathPart.new(part: buffer.to_sym)
  end
end

.parse_finalize(structure) ⇒ Object


141
142
143
144
145
146
# File 'lib/lazy_graph/path_parser.rb', line 141

def parse_finalize(structure)
  return if structure[:buffer].strip.empty?

  parsed = parse_buffer(structure[:buffer].strip)
  append_parsed(parsed, structure[:parts])
end

.parse_main_loop(path, parse_structure) ⇒ Object


45
46
47
48
49
50
# File 'lib/lazy_graph/path_parser.rb', line 45

def parse_main_loop(path, parse_structure)
  while parse_structure[:i] < path.length
    char = path[parse_structure[:i]]
    handle_char(char, path, parse_structure)
  end
end

.parse_range(buffer, delimiter, exclude_end) ⇒ Object


203
204
205
206
207
208
# File 'lib/lazy_graph/path_parser.rb', line 203

def parse_range(buffer, delimiter, exclude_end)
  parts = buffer.split(delimiter)
  return buffer unless parts.size == 2

  Range.new(parts[0].strip, parts[1].strip, exclude_end)
end

.parse_recursive(path, start) ⇒ Object

Recursively parses the path starting from index ‘start’ Returns [Path object, new_index]


32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/lazy_graph/path_parser.rb', line 32

def parse_recursive(path, start)
  parse_structure = {
    parts: [],
    buffer: ''.dup,
    i: start
  }

  parse_main_loop(path, parse_structure)
  parse_finalize(parse_structure)

  Path.new(parts: parse_structure[:parts])
end

.split_by_comma(str) ⇒ Object


163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/lazy_graph/path_parser.rb', line 163

def split_by_comma(str)
  elements = []
  buffer = ''.dup
  depth = 0
  str.each_char do |c|
    case c
    when '['
      depth += 1
      buffer << c
    when ']'
      depth -= 1
      buffer << c
    when ','
      if depth.zero?
        elements << buffer.strip
        buffer = ''.dup
      else
        buffer << c
      end
    else
      buffer << c
    end
  end
  elements << buffer.strip unless buffer.strip.empty?
  elements
end