class RuboCop::Cop::Utils::RegexpRanges

Helper to abstract complexity of building range pairs with octal escape reconstruction (needed for regexp_parser < 2.7).

Attributes

compound_token[R]
root[R]

Public Class Methods

new(root) click to toggle source
# File lib/rubocop/cop/utils/regexp_ranges.rb, line 11
def initialize(root)
  @root = root
  @compound_token = []
end

Public Instance Methods

pairs() click to toggle source
# File lib/rubocop/cop/utils/regexp_ranges.rb, line 16
def pairs
  unless @pairs
    @pairs = []
    populate(root)
  end

  # If either bound is a compound the first one is an escape
  # and that's all we need to work with.
  # If there are any cops that wanted to operate on the compound
  # expression we could wrap it with a facade class.
  @pairs.map { |pair| pair.map(&:first) }
end

Private Instance Methods

compose_range(expressions, current) click to toggle source
# File lib/rubocop/cop/utils/regexp_ranges.rb, line 65
def compose_range(expressions, current)
  range_start, range_end = current.expressions
  range_start = if compound_token.size.between?(1, 2) && octal_digit?(range_start.text)
                  compound_token.dup << range_start
                else
                  [range_start]
                end
  range_end = [range_end]
  range_end.concat(pop_octal_digits(expressions)) if escaped_octal?(range_end.first)
  [range_start, range_end]
end
escaped_octal?(expr) click to toggle source
# File lib/rubocop/cop/utils/regexp_ranges.rb, line 77
def escaped_octal?(expr)
  expr.text =~ /^\\[0-7]$/
end
octal_digit?(char) click to toggle source
# File lib/rubocop/cop/utils/regexp_ranges.rb, line 81
def octal_digit?(char)
  ('0'..'7').cover?(char)
end
pop_octal_digits(expressions) click to toggle source
# File lib/rubocop/cop/utils/regexp_ranges.rb, line 85
def pop_octal_digits(expressions)
  digits = []

  2.times do
    next unless (next_child = expressions.first)
    next unless next_child.type == :literal && next_child.text =~ /^[0-7]$/

    digits << expressions.shift
  end

  digits
end
populate(expr) click to toggle source
# File lib/rubocop/cop/utils/regexp_ranges.rb, line 31
def populate(expr)
  expressions = expr.expressions.to_a

  until expressions.empty?
    current = expressions.shift

    if escaped_octal?(current)
      compound_token << current
      compound_token.concat(pop_octal_digits(expressions))
      # If we have all the digits we can discard.
    end

    next unless current.type == :set

    process_set(expressions, current)
    compound_token.clear
  end
end
process_set(expressions, current) click to toggle source
# File lib/rubocop/cop/utils/regexp_ranges.rb, line 50
def process_set(expressions, current)
  case current.token
  when :range
    @pairs << compose_range(expressions, current)
  when :character
    # Child expressions may include the range we are looking for.
    populate(current)
  when :intersection
    # Each child expression could have child expressions that lead to ranges.
    current.expressions.each do |intersected|
      populate(intersected)
    end
  end
end