File: binding.rb

package info (click to toggle)
ruby-rubocop-ast 1.49.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,768 kB
  • sloc: ruby: 17,017; yacc: 90; makefile: 9
file content (78 lines) | stat: -rw-r--r-- 2,628 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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# frozen_string_literal: true

module RuboCop
  module AST
    class NodePattern
      class Compiler
        # Holds the list of bound variable names
        class Binding
          def initialize
            @bound = {}
          end

          # Yields the first time a given name is bound
          #
          # @return [String] bound variable name
          def bind(name)
            var = @bound.fetch(name) do
              yield n = @bound[name] = "unify_#{name.gsub('-', '__')}"
              n
            end

            if var == :forbidden_unification
              raise Invalid, "Wildcard #{name} was first seen in a subset of a " \
                             "union and can't be used outside that union"
            end
            var
          end

          # Yields for each branch of the given union, forbidding unification of
          # bindings which only appear in a subset of the union.
          def union_bind(enum) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
            # We need to reset @bound before each branch is processed.
            # Moreover we need to keep track of newly encountered wildcards.
            # Var `newly_bound_intersection` will hold those that are encountered
            # in all branches; these are not a problem.
            # Var `partially_bound` will hold those encountered in only a subset
            # of the branches; these can't be used outside of the union.

            return to_enum __method__, enum unless block_given?

            newly_bound_intersection = nil
            partially_bound = []
            bound_before = @bound.dup

            result = enum.each do |e|
              @bound = bound_before.dup if newly_bound_intersection
              yield e
              newly_bound = @bound.keys - bound_before.keys
              if newly_bound_intersection.nil?
                # First iteration
                newly_bound_intersection = newly_bound
              else
                union = newly_bound_intersection | newly_bound
                newly_bound_intersection &= newly_bound
                partially_bound |= union - newly_bound_intersection
              end
            end

            # At this point, all members of `newly_bound_intersection` can be used
            # for unification outside of the union, but partially_bound may not

            forbid(partially_bound)

            result
          end

          private

          def forbid(names)
            names.each do |name|
              @bound[name] = :forbidden_unification
            end
          end
        end
      end
    end
  end
end