class Nokogiri::CSS::XPathVisitor

def visit_function(node)

:stopdoc:
def visit_function(node)
  msg = :"visit_function_#{node.value.first.gsub(/[(]/, "")}"
  return send(msg, node) if respond_to?(msg)
  case node.value.first
  when /^text\(/
    "child::text()"
  when /^self\(/
    "self::#{node.value[1]}"
  when /^eq\(/
    "position()=#{node.value[1]}"
  when /^(nth|nth-of-type)\(/
    if node.value[1].is_a?(Nokogiri::CSS::Node) && (node.value[1].type == :NTH)
      nth(node.value[1])
    else
      "position()=#{node.value[1]}"
    end
  when /^nth-child\(/
    if node.value[1].is_a?(Nokogiri::CSS::Node) && (node.value[1].type == :NTH)
      nth(node.value[1], child: true)
    else
      "count(preceding-sibling::*)=#{node.value[1].to_i - 1}"
    end
  when /^nth-last-of-type\(/
    if node.value[1].is_a?(Nokogiri::CSS::Node) && (node.value[1].type == :NTH)
      nth(node.value[1], last: true)
    else
      index = node.value[1].to_i - 1
      index == 0 ? "position()=last()" : "position()=last()-#{index}"
    end
  when /^nth-last-child\(/
    if node.value[1].is_a?(Nokogiri::CSS::Node) && (node.value[1].type == :NTH)
      nth(node.value[1], last: true, child: true)
    else
      "count(following-sibling::*)=#{node.value[1].to_i - 1}"
    end
  when /^(first|first-of-type)\(/
    "position()=1"
  when /^(last|last-of-type)\(/
    "position()=last()"
  when /^contains\(/
    "contains(.,#{node.value[1]})"
  when /^gt\(/
    "position()>#{node.value[1]}"
  when /^only-child\(/
    "last()=1"
  when /^comment\(/
    "comment()"
  when /^has\(/
    is_direct = node.value[1].value[0].nil? # e.g. "has(> a)", "has(~ a)", "has(+ a)"
    ".#{"//" unless is_direct}#{node.value[1].accept(self)}"
  else
    # xpath function call, let's marshal those arguments
    args = ["."]
    args += node.value[1..-1].map do |n|
      n.is_a?(Nokogiri::CSS::Node) ? n.accept(self) : n
    end
    "nokogiri:#{node.value.first}#{args.join(",")})"
  end
end