plugins/crubybot.nb
# -*-ruby-*-
require 'ffi/clang'
require 'tempfile'
# dependency:
# * ffi-clang.gem
# * libclang
# * lang/clang34 in FreeBSD ports
# * llvm of homebrew
#
# example setting:
# {
# :name => :CRubyBot,
# :channels => [ "#ruby-ja" ],
# :ruby_srcdir => '/path/of/ruby/src',
# }
#
class CRubyBot < Nadoka::NDK_Bot
def bot_initialize
if @bot_config.key?(:channels)
channels = '\A(?:' + @bot_config[:channels].collect{|ch|
Regexp.quote(ch)
}.join('|') + ')\z'
@available_channel = Regexp.compile(channels)
else
@available_channel = @bot_config.fetch(:ch, //)
end
unless @ruby_srcdir = @bot_config[:ruby_srcdir]
raise "ruby_srcdir is not specified"
end
unless Dir.exist?(@ruby_srcdir)
raise "ruby_srcdir(#{@ruby_srcdir}) does not exist"
end
@ruby_srcdir << '/' if @ruby_srcdir[-1] != '/'
@translation_unit = nil
@translation_unit_rev = nil
end
def bot_state
"<#{self.class.to_s}>"
end
def translation_unit
rev = IO.read("#@ruby_srcdir/revision.h").to_s[/\d+/].to_i
return @translation_unit if @translation_unit_rev == rev
ary = Dir[File.join @ruby_srcdir, "*.c"]
f = Tempfile.open(["crubybot-ruby", ".c"])
ary = Dir[File.join @ruby_srcdir, "*.c"]
ary << File.join(@ruby_srcdir, "win32/win32.c")
ary.each do |fn|
f.puts %[#include "#{fn}"]
end
f.flush
index = FFI::Clang::Index.new
@translation_unit = index.parse_translation_unit(f.path, "-I#{@ruby_srcdir}/include")
@translation_unit_rev = rev
f.close(true)
@translation_unit
end
def find_def(name, kind)
translation_unit.cursor.visit_children do |cursor, parent|
if cursor.kind == kind && cursor.definition? && cursor.spelling == name
loc = cursor.location
path = loc.file
if path.start_with?(@ruby_srcdir)
return [path[@ruby_srcdir.size..-1], loc.line]
end
end
next :recurse
end
return nil
end
def on_privmsg(client, ch, message)
return unless @available_channel === ch
case message
when /\A\S*(struct|fun\w*)\s+(\w+)/
kind1 = $1
name = $2
kind = case kind1
when "struct"
:cursor_struct
when /\Afun/
:cursor_function
end
path, line = find_def(name, kind)
return unless path
send_notice(ch, "crubybot: #{kind1} #{name} at " \
"https://github.com/ruby/ruby/blob/trunk/#{path}#L#{line}")
end
end
end