class Yadriggy::C::CodeGen

C-code generator

Since Checker implements Visitor pattern, use it for code generation.

Public Class Methods

c_src_file(dir, lib_name) click to toggle source

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
new(printer, typechecker, public_methods) click to toggle source

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.

Calls superclass method
# 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

build_cmd() click to toggle source

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
build_lib(lib_name, dir='./') click to toggle source

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
c_function(expr) click to toggle source

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
c_function_name(expr) click to toggle source

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
call_with_block(call_ast) click to toggle source
# 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
expand_functions(func_names, func_types) click to toggle source

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
headers() click to toggle source

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
name_global_variables() click to toggle source

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
preamble() click to toggle source

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
printer() click to toggle source

Gets the printer given when object construction.

# File lib/yadriggy/c/codegen.rb, line 39
def printer
  @printer
end
prototype(expr) click to toggle source

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
typechecker() click to toggle source

Gets the type checker.

# File lib/yadriggy/c/codegen.rb, line 33
def typechecker
  @typechecker
end
variable_declarations() click to toggle source

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

c_type(type) click to toggle source

@param [Type] type

# File lib/yadriggy/c/codegen.rb, line 456
def c_type(type)
  CFI::c_type_name(type)
end
def_function(expr, fname) click to toggle source
# 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
error_group() click to toggle source

@api private

# File lib/yadriggy/c/codegen.rb, line 461
def error_group
  'code generation'
end
local_var_declarations(def_or_block) click to toggle source

@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
parameters(expr, fname_str, mtype) click to toggle source
# 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