Class: RubyToRubyC
- Inherits:
-
RubyToAnsiC
- Object
- SexpProcessor
- RubyToAnsiC
- RubyToRubyC
- Defined in:
- lib/ruby_to_ruby_c.rb
Constant Summary
Constants inherited from RubyToAnsiC
RubyToAnsiC::METHOD_MAP, RubyToAnsiC::VERSION
Instance Attribute Summary
Attributes inherited from RubyToAnsiC
Class Method Summary collapse
- .c_type(x) ⇒ Object
-
.translator ⇒ Object
Lazy initializer for the composite RubytoC translator chain.
Instance Method Summary collapse
-
#check_args(args, add_self = true) ⇒ Object
Checks
args
for unsupported variable types. -
#initialize ⇒ RubyToRubyC
constructor
A new instance of RubyToRubyC.
-
#make_function(exp, register = true) ⇒ Object
Makes a new function from
exp
. -
#map_name(name) ⇒ Object
HACK merge with normal_to_C (?).
-
#normal_to_C(name) ⇒ Object
DOC TODO: get mappings from zentest.
- #process_call(exp) ⇒ Object
-
#process_defn(exp) ⇒ Object
Function definition.
- #process_defx(exp) ⇒ Object
-
#process_dstr(exp) ⇒ Object
String interpolation.
-
#process_dxstr(exp) ⇒ Object
Backtick interpolation.
-
#process_false(exp) ⇒ Object
False.
-
#process_gvar(exp) ⇒ Object
Global variables, evil but necessary.
-
#process_iter(exp) ⇒ Object
Iterators for loops.
-
#process_lasgn(exp) ⇒ Object
Assignment to a local variable.
-
#process_lit(exp) ⇒ Object
Literals, numbers for the most part.
-
#process_nil(exp) ⇒ Object
Nil, currently ruby nil, not C NULL (0).
-
#process_OLD_iter(exp) ⇒ Object
Iterators for loops.
-
#process_str(exp) ⇒ Object
Strings.
-
#process_true(exp) ⇒ Object
Truth…
-
#process_xstr(exp) ⇒ Object
Backtick.
Methods inherited from RubyToAnsiC
#no, #preamble, #process_and, #process_arglist, #process_args, #process_array, #process_block, #process_class, #process_const, #process_cvar, #process_dasgn_curr, #process_dummy, #process_dvar, #process_error, #process_iasgn, #process_if, #process_ivar, #process_lvar, #process_not, #process_or, #process_return, #process_scope, #process_static, #process_while, #with_scope
Constructor Details
#initialize ⇒ RubyToRubyC
Returns a new instance of RubyToRubyC.
40 41 42 43 44 45 46 47 48 49 |
# File 'lib/ruby_to_ruby_c.rb', line 40 def initialize super self.unsupported -= [:dstr, :dxstr, :xstr] @c_klass_name = nil @current_klass = nil @klass_name = nil @methods = {} end |
Class Method Details
.c_type(x) ⇒ Object
36 37 38 |
# File 'lib/ruby_to_ruby_c.rb', line 36 def self.c_type(x) "VALUE" end |
.translator ⇒ Object
Lazy initializer for the composite RubytoC translator chain.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/ruby_to_ruby_c.rb', line 12 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 |
Instance Method Details
#check_args(args, add_self = true) ⇒ Object
Checks args
for unsupported variable types. Adds self when add_self
is true.
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
# File 'lib/ruby_to_ruby_c.rb', line 338 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) ⇒ Object
Makes a new function from exp
. Registers the function in the method list and adds self to the signature when register
is true.
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/ruby_to_ruby_c.rb', line 311 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) ⇒ Object
HACK merge with normal_to_C (?)
361 362 363 364 365 |
# File 'lib/ruby_to_ruby_c.rb', line 361 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) ⇒ Object
DOC TODO: get mappings from zentest
371 372 373 374 375 376 377 378 379 380 |
# File 'lib/ruby_to_ruby_c.rb', line 371 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) ⇒ Object
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/ruby_to_ruby_c.rb', line 51 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) ⇒ Object
Function definition
79 80 81 |
# File 'lib/ruby_to_ruby_c.rb', line 79 def process_defn(exp) make_function exp end |
#process_defx(exp) ⇒ Object
83 84 85 |
# File 'lib/ruby_to_ruby_c.rb', line 83 def process_defx(exp) make_function exp, false end |
#process_dstr(exp) ⇒ Object
String interpolation
90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/ruby_to_ruby_c.rb', line 90 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) ⇒ Object
Backtick interpolation.
106 107 108 109 |
# File 'lib/ruby_to_ruby_c.rb', line 106 def process_dxstr(exp) dstr = process_dstr exp return "rb_funcall(rb_mKernel, rb_intern(\"`\"), 1, #{dstr})" end |
#process_false(exp) ⇒ Object
False. Pretty straightforward.
114 115 116 |
# File 'lib/ruby_to_ruby_c.rb', line 114 def process_false(exp) "Qfalse" end |
#process_gvar(exp) ⇒ Object
Global variables, evil but necessary.
123 124 125 126 |
# File 'lib/ruby_to_ruby_c.rb', line 123 def process_gvar(exp) var = exp.shift "rb_gv_get(#{var.to_s.inspect})" end |
#process_iter(exp) ⇒ Object
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. – TODO have CRewriter handle generating lasgns for statics
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/ruby_to_ruby_c.rb', line 169 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) ⇒ Object
Assignment to a local variable.
TODO: figure out array issues and clean up.
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/ruby_to_ruby_c.rb', line 205 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.sexp_type @env.add var.to_sym, exp_type var_type = self.class.c_type exp_type if exp_type.list? then assert_type args, :array raise "array must be of one type" unless args.sexp_type == Type.homo args.shift # :arglist # REFACTOR: this (here down) is the only diff w/ super out << "#{var} = rb_ary_new2(#{args.length});\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) ⇒ Object
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?
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/ruby_to_ruby_c.rb', line 244 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.})" else raise "Bug! no: Unknown literal #{value}:#{value.class}" end return nil end |
#process_nil(exp) ⇒ Object
Nil, currently ruby nil, not C NULL (0).
278 279 280 |
# File 'lib/ruby_to_ruby_c.rb', line 278 def process_nil(exp) return "Qnil" end |
#process_OLD_iter(exp) ⇒ Object
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.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/ruby_to_ruby_c.rb', line 137 def process_OLD_iter(exp) # TODO/REFACTOR: audit against obfuscator out = [] # Only support enums in C-land raise UnsupportedNodeError if exp[0][1].nil? # HACK ugly @env.scope do enum = exp[0][1][1] # HACK ugly t(:iter, t(:call, lhs <-- get lhs call = process exp.shift var = process(exp.shift).intern # semi-HACK-y body = process exp.shift index = "index_#{var}" body += ";" unless body =~ /[;}]\Z/ body.gsub!(/\n\n+/, "\n") out << "unsigned long #{index};" out << "unsigned long arrays_max = FIX2LONG(rb_funcall(arrays, rb_intern(\"size\"), 0));" out << "for (#{index} = 0; #{index} < arrays_max; ++#{index}) {" out << "VALUE x = rb_funcall(arrays, rb_intern(\"at\"), 1, LONG2FIX(index_x));" out << body out << "}" end return out.join("\n") end |
#process_str(exp) ⇒ Object
Strings. woot.
285 286 287 |
# File 'lib/ruby_to_ruby_c.rb', line 285 def process_str(exp) return "rb_str_new2(#{exp.shift.inspect})" end |
#process_true(exp) ⇒ Object
Truth… what is truth? In this case, Qtrue.
292 293 294 |
# File 'lib/ruby_to_ruby_c.rb', line 292 def process_true(exp) "Qtrue" end |
#process_xstr(exp) ⇒ Object
Backtick. Maps directly to Kernel#‘, no overriding.
299 300 301 302 |
# File 'lib/ruby_to_ruby_c.rb', line 299 def process_xstr(exp) command = exp.shift return "rb_funcall(rb_mKernel, rb_intern(\"`\"), 1, rb_str_new2(#{command.inspect}))" end |