File: treewalker.rb

package info (click to toggle)
ruby-pg-query 5.1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 18,248 kB
  • sloc: ansic: 149,767; ruby: 865; makefile: 3
file content (61 lines) | stat: -rw-r--r-- 2,204 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
module PgQuery
  class ParserResult
    # Walks the parse tree and calls the passed block for each contained node
    #
    # If you pass a block with 1 argument, you will get each node.
    # If you pass a block with 4 arguments, you will get each parent_node, parent_field, node and location.
    #
    # Location uniquely identifies a given node within the parse tree. This is a stable identifier across
    # multiple parser runs, assuming the same pg_query release and no modifications to the parse tree.
    def walk!(&block)
      if block.arity == 1
        treewalker!(@tree) do |_, _, node, _|
          yield(node)
        end
      else
        treewalker!(@tree) do |parent_node, parent_field, node, location|
          yield(parent_node, parent_field, node, location)
        end
      end
    end

    private

    def treewalker!(tree) # rubocop:disable Metrics/CyclomaticComplexity
      nodes = [[tree.dup, []]]

      loop do
        parent_node, parent_location = nodes.shift

        case parent_node
        when Google::Protobuf::MessageExts
          parent_node.to_h.keys.each do |parent_field|
            node = parent_node[parent_field.to_s]
            next if node.nil?
            location = parent_location + [parent_field]
            yield(parent_node, parent_field, node, location) if node.is_a?(Google::Protobuf::MessageExts) || node.is_a?(Google::Protobuf::RepeatedField)

            nodes << [node, location] unless node.nil?
          end
        when Google::Protobuf::RepeatedField
          parent_node.each_with_index do |node, parent_field|
            next if node.nil?
            location = parent_location + [parent_field]
            yield(parent_node, parent_field, node, location) if node.is_a?(Google::Protobuf::MessageExts) || node.is_a?(Google::Protobuf::RepeatedField)

            nodes << [node, location] unless node.nil?
          end
        end

        break if nodes.empty?
      end
    end

    def find_tree_location(tree, searched_location)
      treewalker! tree do |parent_node, parent_field, node, location|
        next unless location == searched_location
        yield(parent_node, parent_field, node)
      end
    end
  end
end