module Yadriggy::C

C language embedded in Ruby

Public Class Methods

attach(module_obj, func_names, method_types, lib_name, dir='./') click to toggle source

@api private Attaches functions to a module. The functions are retrieved from the library generated by compiling Ruby methods.

@param [Module] module_obj the module object. @param [Array<String>] func_names the function names in C. @param [Array<Type>] method_types the types of the original methods. @param [String] lib_name the library name. @param [String] dir the directory where the library is found. @return [Module] the module object `module_obj`.

# File lib/yadriggy/c/ffi.rb, line 143
    def self.attach(module_obj, func_names, method_types, lib_name, dir='./')
      module_obj.module_eval('extend FFI::Library')
      module_obj.ffi_lib("#{dir}lib#{lib_name}#{Config::LibExtension}")
      func_names.each_with_index do |name,i|
        mtype = method_types[i]
        module_obj.attach_function((name + '__org').to_sym,
                                   name.to_sym,
                                   CFI::param_types(mtype),
                                   CFI::return_type(mtype))
        ivk_name = invoker_name(mtype)
        module_obj.module_eval <<-code, __FILE__, __LINE__
          def self.#{name}(*args)
            Yadriggy::C::invoke#{ivk_name}(self, :#{name}__org, args)
          end
        code
      end
      module_obj
    end
attach_funcs(pub_methods, checker, gen, module_name, lib_name, dir) click to toggle source

@api private @return [Pair<Module,Array<String>>] the module where the methods

are attached.  The second element is method names.
# File lib/yadriggy/c/c.rb, line 191
def self.attach_funcs(pub_methods, checker, gen, module_name,
                      lib_name, dir)
  func_names = pub_methods.map { |ast| gen.c_function_name(ast.tree) }
  func_types = pub_methods.map { |ast| checker.type(ast.tree) }

  func_names, func_types = gen.expand_functions(func_names, func_types)

  unless module_name.nil?
    make_attach_file(module_name, func_names, func_types,
                     lib_name, dir)
  end

  [attach(Module.new, func_names, func_types, lib_name, dir),
    func_names]
end
compile(obj, lib_name=nil, dir=Config::WorkDir, module_name=nil) click to toggle source

Compiles methods into binary code.

@param [Proc|Method|UnboundMethod|Object] obj the exposed method

or a block.  If `obj` is neither a method or a block,
all the public methods available on `obj` are exposed.
The methods invoked by the exposed methods are also compiled.

@param [String] lib_name the library name. @param [String] dir the directory name. @param [String] module_name the module name where the exposed methods

are attached when the generated Ruby script is executed.
If `method_name` is nil, no Ruby script is generated.

@return [Module] the module object where the exposed methods

are attached.  It does not have a name.
# File lib/yadriggy/c/c.rb, line 92
def self.compile(obj, lib_name=nil, dir=Config::WorkDir, module_name=nil)
  mod, funcs = compile0(obj, lib_name, dir, module_name,
                        ClangTypeChecker, CodeGen)[0]
  mod
end
compile0(obj, lib_name, dir, module_name, typechecker_class, gen_class) click to toggle source

@api private @return [Pair<Module,Array<String>>]

# File lib/yadriggy/c/c.rb, line 100
def self.compile0(obj, lib_name, dir, module_name,
                  typechecker_class, gen_class)
  begin
    compile1(obj, lib_name, dir, module_name,
             typechecker_class, gen_class)
  rescue SyntaxError, CheckError, BuildError => err
    raise err if Yadriggy.debug > 0
    warn err.message
    nil
  end
end
compile1(obj, lib_name, dir, module_name, typechecker_class, gen_class) click to toggle source

@api private @return [Pair<Module,Array<String>>]

# File lib/yadriggy/c/c.rb, line 114
def self.compile1(obj, lib_name, dir, module_name,
                  typechecker_class, gen_class)
  lib_name0 = obj.class.name
  method_objs = if obj.is_a?(Proc) || obj.is_a?(Method) ||
                    obj.is_a?(UnboundMethod)
                  lib_name0 += obj.object_id.to_s(16)
                  [obj]
                else
                  obj.public_methods(false).map do |name|
                    obj.method(name)
                  end
                end
  lib_name = lib_name0.gsub('::', '_').downcase if lib_name.nil?

  raise BuildError.new('no methods specified') if method_objs.size < 1

  checker = typechecker_class.new(@syntax)
  pub_methods = compiled_methods(checker, method_objs)

  dir += File::Separator unless dir.end_with?(File::Separator)
  FileUtils.mkdir_p(dir)
  printer = Yadriggy::FilePrinter.new(gen_class.c_src_file(dir, lib_name))
  gen = gen_class.new(printer, checker, pub_methods)

  generate_funcs(pub_methods[0], gen, printer)
  gen.build_lib(lib_name, dir)

  attach_funcs(pub_methods, checker, gen, module_name, lib_name, dir)
end
compiled_methods(checker, method_objs) click to toggle source

@api private @return [Array<ASTree>] the ASTs of compiled methods.

# File lib/yadriggy/c/c.rb, line 146
def self.compiled_methods(checker, method_objs)
  ast = nil
  pub_methods = method_objs.map do |mthd|
    if ast.nil?
      ast = Yadriggy::reify(mthd)
    else
      ast = ast.reify(mthd)
    end

    if ast.nil?
      raise SyntaxError.new(
        "cannot locate the source: #{mthd.name.to_s} in #{mthd.receiver.class}")
    end

    @syntax.raise_error unless @syntax.check(ast.tree)
    checker.typecheck(ast.tree)
    ast
  end

  return pub_methods
end
generate_funcs(ast, gen, printer) click to toggle source

@api private

# File lib/yadriggy/c/c.rb, line 169
def self.generate_funcs(ast, gen, printer)
  gen.name_global_variables
  gen.headers
  gen.variable_declarations
  ast.astrees.each do |e|
    gen.prototype(e.tree)
  end
  gen.preamble
  ast.astrees.each do |e|
    printer << :nl
    gen.c_function(e.tree)
  end

  printer.output
  printer.close

  raise BuildError.new(gen.error_messages) if gen.errors?
end
invoke(module_obj, func_name, args) click to toggle source

@api private

# File lib/yadriggy/c/ffi.rb, line 235
def self.invoke(module_obj, func_name, args)
  args2 = args.map do |e|
    if e.is_a?(IntArray) || e.is_a?(FloatArray) || e.is_a?(Float32Array)
      e.memory_pointer
    else
      e
    end
  end
  module_obj.method(func_name).call(*args2)
end
invoke_float(module_obj, func_name, args) click to toggle source

@api private

# File lib/yadriggy/c/ffi.rb, line 223
def self.invoke_float(module_obj, func_name, args)
  r = invoke(module_obj, func_name, args)
  FloatArray.new(0, r)
end
invoke_float32(module_obj, func_name, args) click to toggle source

@api private

# File lib/yadriggy/c/ffi.rb, line 229
def self.invoke_float32(module_obj, func_name, args)
  r = invoke(module_obj, func_name, args)
  Float32Array.new(0, r)
end
invoke_int(module_obj, func_name, args) click to toggle source

@api private

# File lib/yadriggy/c/ffi.rb, line 217
def self.invoke_int(module_obj, func_name, args)
  r = invoke(module_obj, func_name, args)
  IntArray.new(0, r)
end
invoker_name(mtype) click to toggle source

@api private

# File lib/yadriggy/c/ffi.rb, line 203
def self.invoker_name(mtype)
  case ArrayType.role(MethodType.role(mtype)&.result_type)&.element_type
  when RubyClass::Integer
    '_int'
  when RubyClass::Float
    '_float'
  when CType::Float32Type
    '_float32'
  else
    ''
  end
end
make_attach_file(module_name, func_names, method_types, lib_name, dir='./') click to toggle source

@api private Generates a Ruby source file. When the file is executed, it retrieves functions from the library generated by compiling Ruby methods, and then it attaches the functions to a specified module.

@param [String] module_name the module name. @param [Array<String>] func_names the function names in C. @param [Array<Type>] method_types the types of the original methods. @param [String] lib_name the library name. @param [String] dir the directory where the library is found. @return [String] the generated file name.

# File lib/yadriggy/c/ffi.rb, line 174
def self.make_attach_file(module_name, func_names, method_types,
                          lib_name, dir='./')
  file_name0 = module_name.gsub(/::|\./, '_').downcase
  file_name = "#{dir}#{file_name0}.rb"
  printer = Yadriggy::FilePrinter.new(file_name)
  printer << "require 'yadriggy/c/ffi'" << :nl << :nl
  printer << "module #{module_name} extend FFI::Library" << :nl
  printer << "  self.ffi_lib(\"#{dir}lib#{lib_name}#{Config::LibExtension}\")" << :nl
  func_names.each_with_index do |name,i|
    mtype = method_types[i]
    printer << "  self.attach_function(:\"#{name}__org\", "
    printer <<     ":\"#{name}\", "
    printer <<     "#{CFI::param_types(mtype).to_s}, "
    printer <<     ":#{CFI::return_type(mtype)})"
    printer << :nl

    printer << "  def self.#{name}(*args)" << :nl
    printer << "    Yadriggy::C::invoke#{invoker_name(mtype)}(self, "
    printer <<       ":\"#{name}__org\", args)" << :nl
    printer << '  end' << :nl
  end
  printer << 'end' << :nl

  printer.output
  printer.close
  file_name
end
ocl_compile(obj, lib_name=nil, dir=Config::WorkDir, module_name=nil) click to toggle source

Compiles OpenCL methods into binary code.

# File lib/yadriggy/c/opencl.rb, line 13
def self.ocl_compile(obj, lib_name=nil, dir=Config::WorkDir,
                     module_name=nil)
  mod, funcs = compile0(obj, lib_name, dir, module_name,
                        OclTypeChecker, OclCodeGen)
  mod
end
run(lib_name=nil, *args, dir: Config::WorkDir, &block) click to toggle source

Compiles and runs a block.

@param [String] lib_name the library name. @param [String] dir the directory name. @param [Object…] args the arguments to the block. @return [Object] the result of running the given block.

# File lib/yadriggy/c/c.rb, line 213
def self.run(lib_name=nil, *args, dir: Config::WorkDir, &block)
  raise BuildError.new('no block given') if block.nil?
  mod, mths = compile0(block, lib_name, dir, nil,
                       ClangTypeChecker, CodeGen)
  mod.method(mths[0]).call(*args)
end
syntax() click to toggle source

@return [Syntax] the syntax.

# File lib/yadriggy/c/c.rb, line 75
def self.syntax
  @syntax
end