class RubyToRubyC

Public Class Methods

c_type(x) click to toggle source
# File lib/ruby_to_ruby_c.rb, line 35
def self.c_type(x)
  "VALUE"
end
new() click to toggle source
Calls superclass method
# File lib/ruby_to_ruby_c.rb, line 39
def initialize
  super

  self.unsupported -= [:dstr, :dxstr, :xstr]

  @c_klass_name = nil
  @current_klass = nil
  @klass_name = nil
  @methods = {}
end
translator() click to toggle source

Lazy initializer for the composite RubytoC translator chain.

# File lib/ruby_to_ruby_c.rb, line 11
def self.translator
  # TODO: FIX, but write a test first
  unless defined? @translator then
    @translator = CompositeSexpProcessor.new
    @translator << Rewriter.new
    @translator << TypeChecker.new
    @translator << CRewriter.new
    @translator << RubyToRubyC.new
    @translator.on_error_in(:defn) do |processor, exp, err|
      result = processor.expected.new
      case result
      when Array then
        result << :error
      end
      msg = "// ERROR: #{err.class}: #{err}"
      msg += " in #{exp.inspect}" unless exp.nil? or $TESTING
      msg += " from #{caller.join(', ')}" unless $TESTING
      result << msg
      result
    end
  end
  @translator
end

Public Instance Methods

check_args(args, add_self = true) click to toggle source

Checks args for unsupported variable types. Adds self when add_self is true.

# File lib/ruby_to_ruby_c.rb, line 306
  def check_args(args, add_self = true)
    c_args = process args

# HACK
#     c_args.each do |arg|
#       raise UnsupportedNodeError,
#       "'#{arg}' is not a supported variable type" if arg.to_s =~ /^\*/
#     end

    if add_self then
      if c_args == '()' then
        c_args = '(VALUE self)'
      else
        c_args.sub! '(', '(VALUE self, '
      end
    end

    return c_args
  end
make_function(exp, register = true) click to toggle source

Makes a new function from exp. Registers the function in the method list and adds self to the signature when register is true.

# File lib/ruby_to_ruby_c.rb, line 279
def make_function(exp, register = true)
  name = map_name exp.shift
  args = exp.shift
  ruby_args = args.deep_clone
  ruby_args.shift # :args

  @method_name = name
  @c_method_name = "rrc_c#{@c_klass_name}_#{normal_to_C name}"

  @env.scope do
    c_args = check_args args, register # registered methods get self
    @methods[name] = ruby_args if register

    body = process exp.shift

    if name == :initialize then
      body[-1] = "return self;\n}"
    end

    return "static VALUE\n#{@c_method_name}#{c_args} #{body}"
  end
end
map_name(name) click to toggle source

HACK merge with normal_to_C (?)

# File lib/ruby_to_ruby_c.rb, line 329
def map_name(name)
  # HACK: get from zentest
  name = METHOD_MAP[name] if METHOD_MAP.has_key? name
  name.to_s.sub(/(.*)\?$/, 'is_\1').intern
end
normal_to_C(name) click to toggle source

DOC TODO: get mappings from zentest

# File lib/ruby_to_ruby_c.rb, line 339
def normal_to_C(name)
  name = name.to_s.dup

  name.sub!(/==$/, '_equals2')
  name.sub!(/=$/, '_equals')
  name.sub!(/\?$/, '_p')
  name.sub!(/\!$/, '_bang')

  return name
end
process_call(exp) click to toggle source
# File lib/ruby_to_ruby_c.rb, line 50
def process_call(exp)
  receiver = process(exp.shift) || "self"
  name = exp.shift.to_s
  arg_count = exp.first.size - 1 rescue 0
  args = process(exp.shift) # TODO: we never ever test multiple arguments!

  # TODO: eric is a big boner
  return "NIL_P(#{receiver})" if name == "nil?"

  name = '===' if name =~ /^case_equal_/ # undo the evils of TypeChecker

  if args.empty? || args == "rb_ary_new()" then # HACK
    args = "0"
  else
    args = "#{arg_count}, #{args}"
  end

  "rb_funcall(#{receiver}, rb_intern(#{name.inspect}), #{args})"
end
process_defn(exp) click to toggle source

Function definition

# File lib/ruby_to_ruby_c.rb, line 78
def process_defn(exp)
  make_function exp
end
process_defx(exp) click to toggle source
# File lib/ruby_to_ruby_c.rb, line 82
def process_defx(exp)
  make_function exp, false
end
process_dstr(exp) click to toggle source

String interpolation

# File lib/ruby_to_ruby_c.rb, line 89
def process_dstr(exp)
  parts = []
  parts << process(s(:str, exp.shift))
  until exp.empty? do
    parts << process(exp.shift)
  end

  pattern = process(s(:str, "%s" * parts.length))
  parts.unshift pattern
                   
  return %{rb_funcall(rb_mKernel, rb_intern("sprintf"), #{parts.length}, #{parts.join(", ")})}
end
process_dxstr(exp) click to toggle source

Backtick interpolation.

# File lib/ruby_to_ruby_c.rb, line 105
def process_dxstr(exp)
  dstr = process_dstr exp
  return "rb_funcall(rb_mKernel, rb_intern(\"`\"), 1, #{dstr})"
end
process_false(exp) click to toggle source

False. Pretty straightforward.

# File lib/ruby_to_ruby_c.rb, line 113
def process_false(exp)
  "Qfalse"
end
process_gvar(exp) click to toggle source

Global variables, evil but necessary.

# File lib/ruby_to_ruby_c.rb, line 122
def process_gvar(exp)
  var = exp.shift
  "rb_gv_get(#{var.to_s.inspect})"
end
process_iter(exp) click to toggle source

Iterators for loops. After rewriter nearly all iter nodes should be able to be interpreted as a for loop. If not, then you are doing something not supported by C in the first place.

# File lib/ruby_to_ruby_c.rb, line 138
def process_iter(exp)
  call = exp.shift
  args = exp.shift
  block_method = exp.shift

  iterable = process call[1] # t(:call, lhs, :iterable, rhs)

  # t(:args, t(:array, of frees), t(:array, of statics))
  free_arg_exps = args[1]
  static_arg_exps = args[2]
  free_arg_exps.shift # :array
  static_arg_exps.shift # :array

  free_args = free_arg_exps.zip(static_arg_exps).map { |f,s| [process(f), process(s)] }

  out = []

  # save
  out.push(*free_args.map { |free,static| "#{static} = #{free};" })

  out << "rb_iterate(rb_each, #{iterable}, #{block_method}, Qnil);"

  # restore
  free_args.each do |free, static|
    out << "#{free} = #{static};"
    statics << "static VALUE #{static};"
  end

  return out.join("\n")
end
process_lasgn(exp) click to toggle source

Assignment to a local variable.

TODO: figure out array issues and clean up.

# File lib/ruby_to_ruby_c.rb, line 174
def process_lasgn(exp) # TODO: audit against obfuscator
  out = ""

  var = exp.shift
  value = exp.shift
  # grab the size of the args, if any, before process converts to a string
  arg_count = 0
  arg_count = value.length - 1 if value.first == :array
  args = value

  exp_type = exp.c_type
  @env.add var.to_sym, exp_type

  if exp_type.list? then
    assert_type args, :array

    raise "array must be of one type" unless args.c_type == CType.homo

    args.shift # :arglist
    # REFACTOR: this (here down) is the only diff w/ super
    out << "#{var} = rb_ary_new2(#{arg_count});\n"
    args.each_with_index do |o,i|
      out << "rb_ary_store(#{var}, #{i}, #{process o});\n"
    end
  else
    out << "#{var} = #{process args}"
  end

  out.sub!(/;\n\Z/, '')

  return out
end
process_lit(exp) click to toggle source

Literals, numbers for the most part. Will probably cause compilation errors if you try to translate bignums and other values that don't have analogs in the C world. Sensing a pattern?

# File lib/ruby_to_ruby_c.rb, line 212
def process_lit(exp)
  # TODO: audit against obfuscator
  value = exp.shift
  case value
  when Integer then
    return "LONG2NUM(#{value})"
  when Float then
    return "rb_float_new(#{value})"
  when Symbol
    return "ID2SYM(rb_intern(#{value.to_s.inspect}))"
  when Range
    f = process_lit [ value.first ]
    l = process_lit [ value.last ]
    x = 0
    x = 1 if value.exclude_end?

    return "rb_range_new(#{f}, #{l}, #{x})"
  when Regexp
    src = value.source
    return "rb_reg_new(#{src.inspect}, #{src.size}, #{value.options})"
  else
    raise "Bug! no: Unknown literal #{value}:#{value.class}"
  end
  return nil
end
process_nil(exp) click to toggle source

Nil, currently ruby nil, not C NULL (0).

# File lib/ruby_to_ruby_c.rb, line 246
def process_nil(exp)
  return "Qnil"
end
process_str(exp) click to toggle source

Strings. woot.

# File lib/ruby_to_ruby_c.rb, line 253
def process_str(exp)
  return "rb_str_new2(#{exp.shift.inspect})"
end
process_true(exp) click to toggle source

Truth… what is truth? In this case, Qtrue.

# File lib/ruby_to_ruby_c.rb, line 260
def process_true(exp)
  "Qtrue"
end
process_xstr(exp) click to toggle source

Backtick. Maps directly to Kernel#`, no overriding.

# File lib/ruby_to_ruby_c.rb, line 267
def process_xstr(exp)
  command = exp.shift
  return "rb_funcall(rb_mKernel, rb_intern(\"`\"), 1, rb_str_new2(#{command.inspect}))"
end