class Yadriggy::C::CodeGen
C-code generator
Since Checker
implements Visitor pattern, use it for code generation.
Public Class Methods
Obtains the file name of the generated source code. A subclass can redefine this method.
@param [String] dir a directory name. @param [String] lib_name a library name. @return [String] a source file name.
# File lib/yadriggy/c/codegen.rb, line 270 def self.c_src_file(dir, lib_name) "#{dir}#{lib_name}.c" end
printer is a Printer
object. Only main_method is converted into an extern function. Other methods are converted into static functions. @param [Array<ASTree>] public_methods publicly exported methods.
# File lib/yadriggy/c/codegen.rb, line 20 def initialize(printer, typechecker, public_methods) super() @printer = printer @typechecker = typechecker @func_counter = 0 @func_names = {} @public_methods = {} public_methods.each {|m| @public_methods[m.tree] = m.tree } @gvariables = {} end
Public Instance Methods
Obtains compiler command. A subclass can redefine this method.
@return [String] command.
# File lib/yadriggy/c/codegen.rb, line 293 def build_cmd Config::Compiler end
Runs a compiler. A subclass can redefine this method.
@param [String] lib_name a library name. @param [String] dir a directory name. @return [void]
# File lib/yadriggy/c/codegen.rb, line 280 def build_lib(lib_name, dir='./') file_name = self.class.c_src_file(dir, lib_name) lib_file_name = "#{dir}lib#{lib_name}#{Config::LibExtension}" cmd = "#{build_cmd} #{Config::CoptOutput}#{lib_file_name} #{file_name}" system(cmd) status = $?.exitstatus raise BuildError.new(["exit #{status}"]) if status > 0 end
Prints a function implementation.
@param [Def|Block] expr the function. @return [void]
# File lib/yadriggy/c/codegen.rb, line 372 def c_function(expr) check(expr) end
Gets the function name in C
after the translation from a Ruby method into a C
function.
@param [Block|Def|Call] expr an expression. @return [String] the function name for `expr`.
# File lib/yadriggy/c/codegen.rb, line 381 def c_function_name(expr) return expr.name.name if expr.is_a?(Def) && @public_methods.include?(expr) fname_str = @func_names[expr] if fname_str.nil? @func_counter += 1 fname_str = if expr.is_a?(Block) "yadriggy_blk#{@func_counter}" else "#{expr.name.name}_#{@func_counter}" end @func_names[expr] = fname_str end fname_str end
# File lib/yadriggy/c/codegen.rb, line 166 def call_with_block(call_ast) loop_param = call_ast.block.params[0] @printer << 'for (' << c_type(RubyClass::Integer) << ' ' check(loop_param) @printer << ' = (' check(ast.receiver) @printer << ') - 1; ' check(loop_param) @printer << ' >= 0; ' check(loop_param) @printer << '--) {' @printer.down local_var_declarations(ast.block) check(ast.block.body) @printer << ';' unless ast.block.body.is_a?(Exprs) @printer.up @printer << '}' << :nl end
Appends implicitly generated functions. A subclass can override this method. The original implementation does not append any.
@param [Array<String>] func_names the names of the generated
functions.
@param [Array<Type>] func_types the types of the original methods. @return [Array<String>, Array<Type>] the names and types.
# File lib/yadriggy/c/codegen.rb, line 364 def expand_functions(func_names, func_types) return func_names, func_types end
Prints `#include` derectives.
@return [void]
# File lib/yadriggy/c/codegen.rb, line 300 def headers() Config::Headers.each {|h| @printer << h << :nl } @printer << :nl end
Gives a name to each global variable. @return [void]
# File lib/yadriggy/c/codegen.rb, line 307 def name_global_variables() id = 0 @typechecker.instance_variables.each do |obj| @gvariables[obj] = "_gvar_#{id}_" id += 1 end end
Prints a preamble. This method is invoked right after printing function prototypes. A subclass can override this method. The original implementation does not print anything.
# File lib/yadriggy/c/codegen.rb, line 353 def preamble end
Gets the printer given when object construction.
# File lib/yadriggy/c/codegen.rb, line 39 def printer @printer end
Prints a function prototype.
@param [Def|Block] expr the function. @return [void]
# File lib/yadriggy/c/codegen.rb, line 332 def prototype(expr) t = @typechecker.type(expr) return if ForeignMethodType.role(t) @printer << 'static ' if @public_methods[expr].nil? fname_str = c_function_name(expr) mt = MethodType.role(t) if mt parameters(expr, fname_str, mt) @printer << ';' << :nl else error!(expr, "bad method #{fname_str}") end self end
Gets the type checker.
# File lib/yadriggy/c/codegen.rb, line 33 def typechecker @typechecker end
Prints variable declarations. @return [void]
# File lib/yadriggy/c/codegen.rb, line 317 def variable_declarations() @gvariables.each do |obj, name| if obj.is_a?(CType::CArray) @printer << 'static ' << c_type(obj.type) << ' ' << name obj.sizes.each {|s| @printer << '[' << s << ']' } @printer << ';' << :nl end end @printer << :nl end
Private Instance Methods
@param [Type] type
# File lib/yadriggy/c/codegen.rb, line 456 def c_type(type) CFI::c_type_name(type) end
# File lib/yadriggy/c/codegen.rb, line 400 def def_function(expr, fname) t = @typechecker.type(expr) return if ForeignMethodType.role(t) @printer << 'static ' if @public_methods[expr].nil? mt = MethodType.role(t) if mt parameters(expr, fname, mt) else error!(expr, 'not a function') end @printer << ' {' @printer.down native_t = NativeMethodType.role(t) if native_t @printer << native_t.body else local_var_declarations(expr) check(expr.body) @printer << ';' unless expr.body.is_a?(Exprs) end @printer.up @printer << '}' << :nl end
@api private
# File lib/yadriggy/c/codegen.rb, line 461 def error_group 'code generation' end
@param [Def|Block] def_or_block
# File lib/yadriggy/c/codegen.rb, line 444 def local_var_declarations(def_or_block) local_vars = @typechecker.local_vars_table[def_or_block] if local_vars.nil? error!(def_or_block, 'bad function definition or block') else local_vars.each do |name, type| @printer << c_type(type) << ' ' << name.to_s << ';' << :nl end end end
# File lib/yadriggy/c/codegen.rb, line 427 def parameters(expr, fname_str, mtype) ret_type = mtype.result_type @printer << c_type(ret_type) << ' ' @printer << fname_str << '(' param_types = mtype.params if param_types.is_a?(Array) expr.params.each_with_index do |p, i| @printer << ', ' if i > 0 @printer << c_type(param_types[i]) << ' ' << p.name end else error!(expr, 'bad parameter types') end @printer << ')' end