Ruby bindings for the C interface of Clang. Early stage https://github.com/cedlemo/ruby-clangc

cedlemo 6e819d467d Implement Clangc::Index#parse_TU2_full_argv method 8 gadi atpakaļ
.yardoc 52686dc07b Update documentation and README.md 9 gadi atpakaļ
doc 3fefeda490 Update README and rdoc documentation 8 gadi atpakaļ
ext 6e819d467d Implement Clangc::Index#parse_TU2_full_argv method 8 gadi atpakaļ
lib 6e819d467d Implement Clangc::Index#parse_TU2_full_argv method 8 gadi atpakaļ
samples 6d8f8982dc Update sourceparser sample and example in README 8 gadi atpakaļ
test 297ce570f6 Add tests for Clangc::Index#parse_TU2_full_argv method 8 gadi atpakaļ
tools 8155392d8a Add little script that remove status logs 8 gadi atpakaļ
yard_documentation 52686dc07b Update documentation and README.md 9 gadi atpakaļ
.travis.yml 70a00fa4c6 Try to authorize installation of other versions of Clang libs after travis change 8 gadi atpakaļ
ArchLinux_x86_64_Vagrantfile a36d6e22ff Add a debian Vagranfile, symplify the ArchLinux Vagrantfile and update the README 8 gadi atpakaļ
Debian-Jessie-64_Vagrantfile a36d6e22ff Add a debian Vagranfile, symplify the ArchLinux Vagrantfile and update the README 8 gadi atpakaļ
Fedora-23_Vagrantfile 78bbac75fd Add a new vagrantfile 8 gadi atpakaļ
Gemfile 5f03186c5e Try to make it workds again 8 gadi atpakaļ
README.md 3fefeda490 Update README and rdoc documentation 8 gadi atpakaļ
README.rdoc 9e87980d81 Implement Clangc::CompletionResult class 8 gadi atpakaļ
Rakefile f1ab3d74b9 Clean Rake's Yard task 9 gadi atpakaļ
TODO 094d459dfb Update TODO 8 gadi atpakaļ
clangc.gemspec 5fb9482db3 Add License 9 gadi atpakaļ
gpl-3.0.txt 5fb9482db3 Add License 9 gadi atpakaļ

README.md

ruby-clangc

Build Status

ruby bindings to the clang C interface

This is free software shared under the GNU GPL 3. Those bindings have been tested and work with :

  • Clang v3.5 to v3.8.
  • ruby 2.1 to 2.3

Table of Content

Installation

Dependencies

  • llvm/clang v3.5 to v3.8
  • ruby and its development libs
  • usual development tools (gcc, make, autoconf ...)
  • gems:
    • rake
    • rake-compiler
    • minitest (optional)
    • term-ansicolor (optional)

More informations can be found in the Vagrantfiles, in the shell_script variable.

On your system

the ruby-clangc gem has not been released yet. You need to clone this github repository , build the gem and install it.

    git clone https://github.com/cedlemo/ruby-clangc.git
    cd ruby-clangc
    gem build clanc.gemspec
    gem install clangc-x.x.x.gem

With Virtual Box and Vagrant

ArchLinux

    vagrant box add archlinux-x86_64 http://cloud.terry.im/vagrant/archlinux-x86_64.box
    mkdir ruby_clang_test
    cd ruby_clang_test
    wget https://raw.githubusercontent.com/cedlemo/ruby-clangc/master/ArchLinux_x86_64_Vagrantfile
    vagrant up
    vagrant provision

Then clone the github repository, build the gem and install it like on your system.

Fedora

    vagrant add bento/fedora-23
    mkdir ruby_clang_test
    cd ruby_clang_test
    wget https://raw.githubusercontent.com/cedlemo/ruby-clangc/master/Fedora-23_Vagrantfile
    vagrant up
    vagrant provision

Then clone the github repository, build the gem and install it like on your system.

Debian

    vagrant box add debian/jessie64
    mkdir ruby_clang_test
    wget https://raw.githubusercontent.com/cedlemo/ruby-clangc/master/Debian-Jessie-64_Vagrantfile
    vagrant up
    vagrant provision

Then clone the github repository, build the gem and install it like on your system.

Examples

See in the samples directory if you want to try those examples.

Code completion

cindex = Clangc::Index.new(false, false)
tu = cindex.create_translation_unit(source: filename,
                                    args: args)

reparse_options = tu.default_reparse_options
tu.reparse(reparse_options)

options = [:include_code_patterns, :include_macros, :include_brief_comments]
complete_results = tu.code_complete_at(filename,
                                       line,
                                       column,
                                       options)
complete_results.sort_results

puts "Diagnostics : "
complete_results.diagnostics.each do |d|
  puts d.spelling
end

puts "Complete :"
complete_results.results.each do |r|
  r.completion_string.chunk_texts.each_with_index do |c, i|
    if r.completion_string.chunk_kind(i) == Clangc::CompletionChunkKind::TYPED_TEXT
      puts c
    end
  end
end

C and C++ parser

require "clangc"
require "fileutils"

PATH = File.expand_path(File.dirname(__FILE__))

class SourceParser
  attr_reader :index, :source_file, :base_dir, 
              :translation_unit, :diagnostics
  def initialize(source_file, base_dir = nil, lang = "c")
    @source_file = source_file
    @base_dir = base_dir
    include_libs = build_default_include_libs
    args = ["-x", lang] + include_libs
    @index = Clangc::Index.new(false, false)
    @translation_unit = @index.create_translation_unit(source: source_file,
                                                       args: args)
    @diagnostics = @translation_unit.diagnostics if @translation_unit
  end
  
  def parse
    cursor = @translation_unit.cursor
    Clangc.visit_children(cursor: cursor) do |cxcursor, parent|
      if block_given?
        yield(@translation_unit, cxcursor, parent)
      else
        puts "Please provide a block"
      end
    end
  end
  
  # Check if the cursor given in argument focus on 
  # the file we want to parse and not on included
  # headers
  def cursor_in_main_file?(cursor)
    cursor_file = cursor.location.spelling[0]
    main_file = @translation_unit.file(@source_file)
    cursor_file.is_equal(main_file)
  end

  private

  # Add the directories where the default headers files
  # for the standards libs can be found
  def build_default_include_libs
    header_paths = []
    if @base_dir && Dir.exist?(@base_dir)
      @base_dir = File.expand_path(@base_dir)
    else
      @base_dir = File.expand_path(File.dirname(@source_file))
    end
    gcc_lib_base='/usr/lib/gcc/' << `llvm-config --host-target`.chomp << "/*"
    last_gcc_lib_base = Dir.glob(gcc_lib_base ).sort.last
    if last_gcc_lib_base
      gcc_lib = last_gcc_lib_base + "/include"
      header_paths << gcc_lib
    end
    header_paths << "/usr/include"
    header_paths << @base_dir
    header_paths.collect {|h| "-I#{h}"}
  end
end

class CSourceParser < SourceParser
  def initialize(source_file, base_dir = nil)
    super(source_file, base_dir, "c")
  end
end

class CPPSourceParser < SourceParser
  def initialize(source_file, base_dir = nil)
    super(source_file, base_dir, "c++")
  end
end

source = "#{PATH}/../tools/clang-3.5/Index.h"

cl35 = CSourceParser.new(source)

unless cl35.translation_unit
  puts "Parsing failed"
end

# This will display the names of all the functions in the header Index.h

cl35.parse do |tu, cursor, parent|
  if cl35.cursor_in_main_file?(cursor)
    puts cursor.spelling if cursor.kind == Clangc::CursorKind::FUNCTION_DECL
  end
  Clangc::ChildVisitResult::RECURSE
end

C simple parsing

#!/usr/bin/env ruby

require "clangc"

source_file = "#{File.expand_path(File.dirname(__FILE__))}/source1.c"

# Get all the necessary headers
clang_headers_path = Dir.glob("/usr/lib/clang/*/include").collect {|x| "-I#{x}"}

index = Clangc::Index.new(false, false)

tu = index.create_translation_unit_from_source_file(source_file, 
                                                    clang_headers_path)
exit unless tu
cursor = tu.cursor

def pretty_print(cursor_kind, cursor)
  printf("%s %s line %d, char %d\n",
         cursor_kind,                 
         cursor.spelling,
         cursor.location.spelling[1],
         cursor.location.spelling[2])
end

Clangc.visit_children(cursor: cursor) do |cursor, parent| 
  if cursor.location.spelling[0].name == source_file
    case cursor.kind 
    when Clangc::CursorKind::TYPEDEF_DECL
      pretty_print("typedef     ", cursor)
    when Clangc::CursorKind::STRUCT_DECL
      pretty_print("structure   ", cursor)
    when Clangc::CursorKind::ENUM_DECL
      pretty_print("Enumeration ", cursor)
    when Clangc::CursorKind::UNION_DECL
      pretty_print("Union       ", cursor)
    when Clangc::CursorKind::FUNCTION_DECL
      pretty_print("Function    ", cursor)
      arguments = cursor.type.arg_types
      puts "\t#{arguments.size} argument(s)"
        arguments.each do |a|
        puts "\t-\t" + a.spelling 
      end
    end
  end
  Clangc::ChildVisitResult::RECURSE
end

Displaying code diagnostics

#!/usr/bin/env ruby
require "clangc"

# excludeDeclsFromPCH = 0, displayDiagnostics=0
cindex = Clangc::Index.new(false, false)

clang_headers_path = Dir.glob("/usr/lib/clang/*/include").collect {|x| "-I#{x}"}
source = "#{File.expand_path(File.dirname(__FILE__))}/list.c"

tu = cindex.parse_translation_unit(source: source, 
                                   args: clang_headers_path, 
                                   flags: :none)

exit unless tu

tu.diagnostics.each_with_index do |diagnostic, index|
  puts "################### Diagnostic N° #{index + 1 } #####################"
  puts "Default display options:"
  puts  "\t #{diagnostic.format(Clangc::default_diagnostic_display_options)}"
  puts "None:"
  puts  "\t #{diagnostic.format(0)}"
  puts "None + Source Location:"
  # Clangc::DiagnosticDisplayOptions::DISPLAY_SOURCE_LOCATION
  puts  "\t #{diagnostic.format(:display_source_location)}"
  puts "None + Source Location + Column:"
  # Clangc::DiagnosticDisplayOptions::DISPLAY_SOURCE_LOCATION |
  # Clangc::DiagnosticDisplayOptions::DISPLAY_COLUMN
  puts "\t #{diagnostic.format([:display_source_location, :display_column])}"
  puts "None + Source Location + Column + Category Name:"
  # Clangc::DiagnosticDisplayOptions::DISPLAY_SOURCE_LOCATION |
  # Clangc::DiagnosticDisplayOptions::DISPLAY_COLUMN |
  # Clangc::DiagnosticDisplayOptions::DISPLAY_CATEGORY_NAME
  puts "\t #{diagnostic.format([:display_source_location,
                                :display_column,
                                :display_category_name])}"
end

Documentation

All the functions are documented with rdoc (maybe yard when I will have the time) in the doc directory. If you want to update it, or re-generate it just do:

rake rdoc

Status

When ruby-clangc is installed, you can try the tools/status.rb script in order to see the current status and to see what we can do with ruby-clangc. You will need the gem term-ansicolor

functions wrapped:

  • 185/257 functions wrapped => 71.98443579766537%

classes wrapped:

  • CXIndex
  • CXTranslationUnit
  • CXDiagnostic
  • CXFile
  • CXSourceRange
  • CXSourceLocation
  • CXCursor
  • CXType
  • CXCursorSet
  • CXModule
  • CXCompletionString
  • CXCodeCompleteResults
  • CXCompletionResult

ruby-clangc ruby bindings for the C interface of Clang Copyright (C) 2015-2016 Cédric Le Moigne cedlemo cedlemo@gmx.com