Skip to content

Instantly share code, notes, and snippets.

@jarib
Created December 28, 2010 01:47
Show Gist options
  • Save jarib/756783 to your computer and use it in GitHub Desktop.
Save jarib/756783 to your computer and use it in GitHub Desktop.
Spike of FFI wrapper for libclang
require 'rubygems'
require 'ffi'
module Clang
module Lib
extend FFI::Library
ffi_lib "clang"
attach_function :create_index, :clang_createIndex, [:int, :int], :pointer
attach_function :dispose_index, :clang_disposeIndex, [:pointer], :void
attach_function :parse_translation_unit, :clang_parseTranslationUnit, [:pointer, :string, :pointer, :int, :pointer, :uint, :uint], :pointer
attach_function :dispose_translation_unit, :clang_disposeTranslationUnit, [:pointer], :void
attach_function :get_num_diagnostics, :clang_getNumDiagnostics, [:pointer], :uint
attach_function :get_diagnostic, :clang_getDiagnostic, [:pointer, :uint], :pointer
attach_function :dispose_diagnostic, :clang_disposeDiagnostic, [:pointer], :void
attach_function :format_diagnostic, :clang_formatDiagnostic, [:pointer, :uint], :pointer
attach_function :default_diagnostic_display_options, :clang_defaultDiagnosticDisplayOptions, [], :uint
attach_function :get_c_string, :clang_getCString, [:pointer], :string
attach_function :dispose_string, :clang_disposeString, [:pointer], :void
attach_function :get_diagnostic_location, :clang_getDiagnosticLocation, [:pointer], :pointer
attach_function :equal_locations, :clang_equalLocations, [:pointer, :pointer], :uint
end
class TranslationUnit
def initialize(ptr)
@ptr = FFI::AutoPointer.new(ptr, Lib.method(:dispose_translation_unit))
end
def diagnostics
n = Lib.get_num_diagnostics(@ptr)
0.upto(n - 1).map do |idx|
Diagnostic.new(Lib.get_diagnostic(@ptr, idx))
end
end
end
class Diagnostic
def initialize(ptr)
@ptr = FFI::AutoPointer.new(ptr, Lib.method(:dispose_diagnostic))
end
def format(opts = {})
raise NotImplementedError, "#{self.class}#format options" unless opts.empty?
cxstring = Lib.format_diagnostic(@ptr, Lib.default_diagnostic_display_options)
result = Lib.get_c_string(cxstring)
Lib.dispose_string(cxstring)
result
end
def severity
raise NotImplementedError
end
def source_location
SourceLocation.new(Lib.get_diagnostic_location(@ptr))
end
def fixits
raise NotImplementedError
# unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag);
# – CXString clang_getDiagnosticFixIt(CXDiagnostic Diag,
# unsigned FixIt,
# CXSourceRange *ReplacementRange);
end
def spelling
raise NotImplementedError
end
end
class SourceLocation
def initialize(ptr)
# no release? should we keep a reference to TU / diagnostic?
@ptr = ptr
end
def ==(other)
return unless other.kind_of? self.class
Lib.equal_locations(@ptr, other.ptr) != 0
end
alias_method :eql?, :==
protected
def ptr
@ptr
end
end
class Index
def initialize(opts = {})
exclude_declarations_from_pch = opts[:exclude_declarations_from_pch] ? 1 : 0
display_diagnostics = opts[:display_diagnostics] ? 1 : 0
@ptr = FFI::AutoPointer.new Lib.create_index(exclude_declarations_from_pch, display_diagnostics),
Lib.method(:dispose_index)
end
def parse_translation_unit(source_file, command_line_args = nil, opts = {})
command_line_args = Array(command_line_args)
args_pointer = FFI::MemoryPointer.new(:pointer)
strings = command_line_args.map do |arg|
FFI::MemoryPointer.from_string(arg.to_s)
end
args_pointer.put_array_of_pointer(strings) unless strings.empty?
raise NotImplementedError, "options for #{self.class}#parse_translation_unit" unless opts.empty?
tu = Lib.parse_translation_unit(@ptr, source_file, args_pointer, command_line_args.size, nil, 0, 0)
raise "error parsing #{source_file.inspect}" if tu.nil? || tu.null?
TranslationUnit.new tu
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment