123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Utils::RegexpRanges

Relationships & Source Files
Inherits: Object
Defined in: lib/rubocop/cop/utils/regexp_ranges.rb

Overview

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

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(root) ⇒ RegexpRanges

[ GitHub ]

  
# File 'lib/rubocop/cop/utils/regexp_ranges.rb', line 11

def initialize(root)
  @root = root
  @compound_token = []
  @pairs = []
  @populated = false
end

Instance Attribute Details

#root (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/utils/regexp_ranges.rb', line 9

attr_reader :root

Instance Method Details

#compose_range(expressions, current) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/utils/regexp_ranges.rb', line 78

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

#compound_token

[ GitHub ]

  
# File 'lib/rubocop/cop/utils/regexp_ranges.rb', line 18

def compound_token
  populate_all unless @populated

  @compound_token
end

#escaped_octal?(expr) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/utils/regexp_ranges.rb', line 90

def escaped_octal?(expr)
  expr.text.valid_encoding? && expr.text =~ /^\\[0-7]$/
end

#octal_digit?(char) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/utils/regexp_ranges.rb', line 94

def octal_digit?(char)
  ('0'..'7').cover?(char)
end

#pairs

[ GitHub ]

  
# File 'lib/rubocop/cop/utils/regexp_ranges.rb', line 24

def pairs
  populate_all unless @populated

  @pairs
end

#pop_octal_digits(expressions) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/utils/regexp_ranges.rb', line 98

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) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/utils/regexp_ranges.rb', line 44

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

#populate_all (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/utils/regexp_ranges.rb', line 32

def populate_all
  populate(@root)

  # 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) }

  @populated = true
end

#process_set(expressions, current) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/utils/regexp_ranges.rb', line 63

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