module SimpleCov::SourceFile::RubyDataParser

def call(structure)

so no need to put them through here.
Tests use the real data structures (except for integration tests)
def call(structure)
  return structure if structure.is_a?(Array)
  parse_array_string(structure.to_s)
end

def parse_array_string(str)

'["ClassName", :method1, 2, 2, 5, 5]' back into a Ruby array.
Parse a string like '[:if, 0, 3, 4, 3, 21]' or
def parse_array_string(str)
  # Try plain Ripper first; only pre-quote `#<...>` inspect segments
  # if the input isn't already valid Ruby (otherwise we corrupt
  # `"#<Class:Foo>"` strings that *are* valid Ruby literals — exactly
  # the shape simplecov-on-simplecov method-coverage keys take).
  sexp = Ripper.sexp(str) || Ripper.sexp(quote_inspected_class_segments(str))
  # simplecov:disable — defensive: Ripper.sexp returning nil from both passes requires malformed input
  array_node = sexp&.dig(1, 0)
  # simplecov:enable
  raise ArgumentError, "expected array literal: #{str.inspect}" unless array_node && array_node[0] == :array
  Array(array_node[1]).map { |element| parse_element(element) }
end

def parse_element(node)

def parse_element(node)
  case node[0]
  when :@int, :unary                 then parse_integer_node(node)
  when :symbol_literal, :dyna_symbol then parse_symbol_node(node)
  when :string_literal               then unescape_ruby(string_literal_text(node[1]))
  when :var_ref                      then node.dig(1, 1) # `Foo`
  when :const_path_ref               then "#{parse_element(node[1])}::#{node[2][1]}" # `Foo::Bar`
  else
    # simplecov:disable — defensive fallback for unexpected Ripper node shapes
    raise ArgumentError, "unexpected element: #{node.inspect}"
    # simplecov:enable
  end
end

def parse_integer_node(node)

def parse_integer_node(node)
  node[0] == :@int ? node[1].to_i : -node[2][1].to_i
end

def parse_symbol_node(node)

def parse_symbol_node(node)
  if node[0] == :symbol_literal
    node.dig(1, 1, 1).to_sym
  else
    unescape_ruby(string_literal_text(node[1])).to_sym
  end
end

def quote_inspected_class_segments(str)

array literal; downstream we treat them as opaque strings.
syntax. Wrap them in quotes so Ripper can parse the surrounding
like `#` or `#`, which aren't valid Ruby
Method coverage keys can contain inspect-format class references
def quote_inspected_class_segments(str)
  str.gsub(/#<[^>]*>/) { |segment| %("#{segment.gsub('"', '\\"')}") }
end

def string_literal_text(string_content)

on the literal.
may emit zero, one, or many `:@tstring_content` children depending
Concatenate the text fragments of a `:string_content` node. Ripper
def string_literal_text(string_content)
  Array(string_content[1..]).map { |child| child[1] }.join
end

def unescape_ruby(raw)

parser undid: `\X` → `X` for any X.
Undo the same backslash-prefix escapes the previous hand-rolled
def unescape_ruby(raw)
  raw.gsub(/\\(.)/) { ::Regexp.last_match(1) }
end