module Yadriggy::C
C
language embedded in Ruby
Public Class Methods
@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
@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
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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
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
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
@return [Syntax] the syntax.
# File lib/yadriggy/c/c.rb, line 75 def self.syntax @syntax end