ruby2c 1.0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,382 @@
1
+
2
+ $TESTING = false unless defined? $TESTING
3
+
4
+ begin require 'rubygems'; rescue LoadError; end
5
+ require 'ruby_to_ansi_c'
6
+
7
+ class RubyToRubyC < RubyToAnsiC
8
+
9
+ ##
10
+ # Lazy initializer for the composite RubytoC translator chain.
11
+
12
+ def self.translator
13
+ # TODO: FIX, but write a test first
14
+ unless defined? @translator then
15
+ @translator = CompositeSexpProcessor.new
16
+ @translator << Rewriter.new
17
+ @translator << TypeChecker.new
18
+ @translator << CRewriter.new
19
+ @translator << RubyToRubyC.new
20
+ @translator.on_error_in(:defn) do |processor, exp, err|
21
+ result = processor.expected.new
22
+ case result
23
+ when Array then
24
+ result << :error
25
+ end
26
+ msg = "// ERROR: #{err.class}: #{err}"
27
+ msg += " in #{exp.inspect}" unless exp.nil? or $TESTING
28
+ msg += " from #{caller.join(', ')}" unless $TESTING
29
+ result << msg
30
+ result
31
+ end
32
+ end
33
+ @translator
34
+ end
35
+
36
+ def self.c_type(x)
37
+ "VALUE"
38
+ end
39
+
40
+ def initialize
41
+ super
42
+
43
+ self.unsupported -= [:dstr, :dxstr, :xstr]
44
+
45
+ @c_klass_name = nil
46
+ @current_klass = nil
47
+ @klass_name = nil
48
+ @methods = {}
49
+ end
50
+
51
+ def process_call(exp)
52
+ receiver = process(exp.shift) || "self"
53
+ name = exp.shift.to_s
54
+ arg_count = exp.first.size - 1 rescue 0
55
+ args = process(exp.shift) # TODO: we never ever test multiple arguments!
56
+
57
+ # TODO: eric is a big boner
58
+ return "NIL_P(#{receiver})" if name == "nil?"
59
+
60
+ name = '===' if name =~ /^case_equal_/ # undo the evils of TypeChecker
61
+
62
+ if args.empty? || args == "rb_ary_new()" then # HACK
63
+ args = "0"
64
+ else
65
+ args = "#{arg_count}, #{args}"
66
+ end
67
+
68
+ "rb_funcall(#{receiver}, rb_intern(#{name.inspect}), #{args})"
69
+ end
70
+
71
+ # TODO: pull process_const from obfuscator
72
+ # TODO: pull process_colon2 from obfuscator
73
+ # TODO: pull process_cvar from obfuscator
74
+ # TODO: pull process_dasgn_curr from obfuscator
75
+
76
+ ##
77
+ # Function definition
78
+
79
+ def process_defn(exp)
80
+ make_function exp
81
+ end
82
+
83
+ def process_defx(exp)
84
+ make_function exp, false
85
+ end
86
+
87
+ ##
88
+ # String interpolation
89
+
90
+ def process_dstr(exp)
91
+ parts = []
92
+ parts << process(s(:str, exp.shift))
93
+ until exp.empty? do
94
+ parts << process(exp.shift)
95
+ end
96
+
97
+ pattern = process(s(:str, "%s" * parts.length))
98
+ parts.unshift pattern
99
+
100
+ return %{rb_funcall(rb_mKernel, rb_intern("sprintf"), #{parts.length}, #{parts.join(", ")})}
101
+ end
102
+
103
+ ##
104
+ # Backtick interpolation.
105
+
106
+ def process_dxstr(exp)
107
+ dstr = process_dstr exp
108
+ return "rb_funcall(rb_mKernel, rb_intern(\"`\"), 1, #{dstr})"
109
+ end
110
+
111
+ ##
112
+ # False. Pretty straightforward.
113
+
114
+ def process_false(exp)
115
+ "Qfalse"
116
+ end
117
+
118
+ # TODO: pull up process_gasgn from obfuscator
119
+
120
+ ##
121
+ # Global variables, evil but necessary.
122
+
123
+ def process_gvar(exp)
124
+ var = exp.shift
125
+ "rb_gv_get(#{var.to_s.inspect})"
126
+ end
127
+
128
+ # TODO: pull hash from obfuscator
129
+ # TODO: pull iasgn from obfuscator
130
+ # TODO: pull ivar from obfuscator
131
+
132
+ ##
133
+ # Iterators for loops. After rewriter nearly all iter nodes
134
+ # should be able to be interpreted as a for loop. If not, then you
135
+ # are doing something not supported by C in the first place.
136
+
137
+ def process_OLD_iter(exp) # TODO/REFACTOR: audit against obfuscator
138
+ out = []
139
+ # Only support enums in C-land
140
+ raise UnsupportedNodeError if exp[0][1].nil? # HACK ugly
141
+ @env.scope do
142
+ enum = exp[0][1][1] # HACK ugly t(:iter, t(:call, lhs <-- get lhs
143
+ call = process exp.shift
144
+ var = process(exp.shift).intern # semi-HACK-y
145
+ body = process exp.shift
146
+ index = "index_#{var}"
147
+
148
+ body += ";" unless body =~ /[;}]\Z/
149
+ body.gsub!(/\n\n+/, "\n")
150
+
151
+ out << "unsigned long #{index};"
152
+ out << "unsigned long arrays_max = FIX2LONG(rb_funcall(arrays, rb_intern(\"size\"), 0));"
153
+ out << "for (#{index} = 0; #{index} < arrays_max; ++#{index}) {"
154
+ out << "VALUE x = rb_funcall(arrays, rb_intern(\"at\"), 1, LONG2FIX(index_x));"
155
+ out << body
156
+ out << "}"
157
+ end
158
+
159
+ return out.join("\n")
160
+ end
161
+
162
+ ##
163
+ # Iterators for loops. After rewriter nearly all iter nodes
164
+ # should be able to be interpreted as a for loop. If not, then you
165
+ # are doing something not supported by C in the first place.
166
+ #--
167
+ # TODO have CRewriter handle generating lasgns for statics
168
+
169
+ def process_iter(exp)
170
+ call = exp.shift
171
+ args = exp.shift
172
+ block_method = exp.shift
173
+
174
+ iterable = process call[1] # t(:call, lhs, :iterable, rhs)
175
+
176
+ # t(:args, t(:array, of frees), t(:array, of statics))
177
+ free_arg_exps = args[1]
178
+ static_arg_exps = args[2]
179
+ free_arg_exps.shift # :array
180
+ static_arg_exps.shift # :array
181
+
182
+ free_args = free_arg_exps.zip(static_arg_exps).map { |f,s| [process(f), process(s)] }
183
+
184
+ out = []
185
+
186
+ # save
187
+ out.push(*free_args.map { |free,static| "#{static} = #{free};" })
188
+
189
+ out << "rb_iterate(rb_each, #{iterable}, #{block_method}, Qnil);"
190
+
191
+ # restore
192
+ free_args.each do |free, static|
193
+ out << "#{free} = #{static};"
194
+ statics << "static VALUE #{static};"
195
+ end
196
+
197
+ return out.join("\n")
198
+ end
199
+
200
+ ##
201
+ # Assignment to a local variable.
202
+ #
203
+ # TODO: figure out array issues and clean up.
204
+
205
+ def process_lasgn(exp) # TODO: audit against obfuscator
206
+ out = ""
207
+
208
+ var = exp.shift
209
+ value = exp.shift
210
+ # grab the size of the args, if any, before process converts to a string
211
+ arg_count = 0
212
+ arg_count = value.length - 1 if value.first == :array
213
+ args = value
214
+
215
+ exp_type = exp.sexp_type
216
+ @env.add var.to_sym, exp_type
217
+ var_type = self.class.c_type exp_type
218
+
219
+ if exp_type.list? then
220
+ assert_type args, :array
221
+
222
+ raise "array must be of one type" unless args.sexp_type == Type.homo
223
+
224
+ args.shift # :arglist
225
+ # REFACTOR: this (here down) is the only diff w/ super
226
+ out << "#{var} = rb_ary_new2(#{args.length});\n"
227
+ args.each_with_index do |o,i|
228
+ out << "rb_ary_store(#{var}, #{i}, #{process o});\n"
229
+ end
230
+ else
231
+ out << "#{var} = #{process args}"
232
+ end
233
+
234
+ out.sub!(/;\n\Z/, '')
235
+
236
+ return out
237
+ end
238
+
239
+ ##
240
+ # Literals, numbers for the most part. Will probably cause
241
+ # compilation errors if you try to translate bignums and other
242
+ # values that don't have analogs in the C world. Sensing a pattern?
243
+
244
+ def process_lit(exp)
245
+ # TODO: audit against obfuscator
246
+ value = exp.shift
247
+ case value
248
+ when Integer then
249
+ return "LONG2NUM(#{value})"
250
+ when Float then
251
+ return "rb_float_new(#{value})"
252
+ when Symbol
253
+ return "ID2SYM(rb_intern(#{value.to_s.inspect}))"
254
+ when Range
255
+ f = process_lit [ value.first ]
256
+ l = process_lit [ value.last ]
257
+ x = 0
258
+ x = 1 if value.exclude_end?
259
+
260
+ return "rb_range_new(#{f}, #{l}, #{x})"
261
+ when Regexp
262
+ src = value.source
263
+ return "rb_reg_new(#{src.inspect}, #{src.size}, #{value.options})"
264
+ else
265
+ raise "Bug! no: Unknown literal #{value}:#{value.class}"
266
+ end
267
+ return nil
268
+ end
269
+
270
+ # TODO: pull match/2/3 from obfuscator
271
+ # TODO: pull next from obfuscator (and modify for iters)
272
+
273
+ # TODO: process_not?!? wtf? I don't think the ansi not works
274
+
275
+ ##
276
+ # Nil, currently ruby nil, not C NULL (0).
277
+
278
+ def process_nil(exp)
279
+ return "Qnil"
280
+ end
281
+
282
+ ##
283
+ # Strings. woot.
284
+
285
+ def process_str(exp)
286
+ return "rb_str_new2(#{exp.shift.inspect})"
287
+ end
288
+
289
+ ##
290
+ # Truth... what is truth? In this case, Qtrue.
291
+
292
+ def process_true(exp)
293
+ "Qtrue"
294
+ end
295
+
296
+ ##
297
+ # Backtick. Maps directly to Kernel#`, no overriding.
298
+
299
+ def process_xstr(exp)
300
+ command = exp.shift
301
+ return "rb_funcall(rb_mKernel, rb_intern(\"`\"), 1, rb_str_new2(#{command.inspect}))"
302
+ end
303
+
304
+ # TODO: pull while from obfuscator
305
+ # TODO: pull zsuper from obfuscator
306
+
307
+ ##
308
+ # Makes a new function from +exp+. Registers the function in the method
309
+ # list and adds self to the signature when +register+ is true.
310
+
311
+ def make_function(exp, register = true)
312
+ name = map_name exp.shift
313
+ args = exp.shift
314
+ ruby_args = args.deep_clone
315
+ ruby_args.shift # :args
316
+
317
+ @method_name = name
318
+ @c_method_name = "rrc_c#{@c_klass_name}_#{normal_to_C name}"
319
+
320
+ @env.scope do
321
+ c_args = check_args args, register # registered methods get self
322
+ @methods[name] = ruby_args if register
323
+
324
+ body = process exp.shift
325
+
326
+ if name == :initialize then
327
+ body[-1] = "return self;\n}"
328
+ end
329
+
330
+ return "static VALUE\n#{@c_method_name}#{c_args} #{body}"
331
+ end
332
+ end
333
+
334
+ ##
335
+ # Checks +args+ for unsupported variable types. Adds self when +add_self+
336
+ # is true.
337
+
338
+ def check_args(args, add_self = true)
339
+ c_args = process args
340
+
341
+ # HACK
342
+ # c_args.each do |arg|
343
+ # raise UnsupportedNodeError,
344
+ # "'#{arg}' is not a supported variable type" if arg.to_s =~ /^\*/
345
+ # end
346
+
347
+ if add_self then
348
+ if c_args == '()' then
349
+ c_args = '(VALUE self)'
350
+ else
351
+ c_args.sub! '(', '(VALUE self, '
352
+ end
353
+ end
354
+
355
+ return c_args
356
+ end
357
+
358
+ ##
359
+ # HACK merge with normal_to_C (?)
360
+
361
+ def map_name(name)
362
+ # HACK: get from zentest
363
+ name = METHOD_MAP[name] if METHOD_MAP.has_key? name
364
+ name.to_s.sub(/(.*)\?$/, 'is_\1').intern
365
+ end
366
+
367
+ ##
368
+ # DOC
369
+ # TODO: get mappings from zentest
370
+
371
+ def normal_to_C(name)
372
+ name = name.to_s.dup
373
+
374
+ name.sub!(/==$/, '_equals2')
375
+ name.sub!(/=$/, '_equals')
376
+ name.sub!(/\?$/, '_p')
377
+ name.sub!(/\!$/, '_bang')
378
+
379
+ return name
380
+ end
381
+
382
+ end
@@ -0,0 +1,148 @@
1
+
2
+ require 'handle'
3
+ require 'function_type'
4
+
5
+ class Type
6
+
7
+ # REFACTOR: nuke this
8
+ KNOWN_TYPES = {
9
+ :bool => "Bool",
10
+ :bool_list => "Bool list",
11
+ :const => "Const",
12
+ :file => "File",
13
+ :float => "Float",
14
+ :float_list => "Float list",
15
+ :function => "Function",
16
+ :long => "Integer",
17
+ :long_list => "Integer list",
18
+ :range => "Range",
19
+ :regexp => "Regular Expression",
20
+ :str => "String",
21
+ :str_list => "String list",
22
+ :symbol => "Symbol",
23
+ :value => "Value",
24
+ :value_list => "Value list",
25
+ :void => "Void",
26
+ :zclass => "Class",
27
+
28
+ :fucked => "Untranslatable type",
29
+ :hetero => "Heterogenous",
30
+ :homo => "Homogenous",
31
+ :unknown => "Unknown",
32
+ :unknown_list => "Unknown list",
33
+ }
34
+
35
+ TYPES = {}
36
+
37
+ def self.function lhs_type, arg_types, return_type = nil
38
+ unless return_type then
39
+ $stderr.puts "\nWARNING: adding Type.unknown for #{caller[0]}" if $DEBUG
40
+ # TODO: gross, maybe go back to the *args version from method_missing
41
+ return_type = arg_types
42
+ arg_types = lhs_type
43
+ lhs_type = Type.unknown
44
+ end
45
+
46
+ self.new FunctionType.new(lhs_type, arg_types, return_type)
47
+ end
48
+
49
+ def self.unknown
50
+ self.new :unknown
51
+ end
52
+
53
+ def self.method_missing(type, *args)
54
+ raise "Unknown type Type.#{type} (#{type.inspect})" unless
55
+ Type::KNOWN_TYPES.has_key?(type)
56
+
57
+ if type.to_s =~ /(.*)_list$/ then
58
+ TYPES[type] = self.new($1.intern, true) unless TYPES.has_key?(type)
59
+ return TYPES[type]
60
+ else
61
+ TYPES[type] = self.new(type) unless TYPES.has_key?(type)
62
+ return TYPES[type]
63
+ end
64
+ end
65
+
66
+ def self.unknown_list
67
+ self.new(:unknown, true)
68
+ end
69
+
70
+ attr_accessor :type
71
+ attr_accessor :list
72
+
73
+ def initialize(type, list=false)
74
+ # HACK
75
+ unless KNOWN_TYPES.has_key? type or type.class.name =~ /Type$/ then
76
+ raise "Unknown type Type.new(#{type.inspect})"
77
+ end
78
+ @type = Handle.new type
79
+ @list = list
80
+ end
81
+
82
+ def function?
83
+ not KNOWN_TYPES.has_key? self.type.contents
84
+ end
85
+
86
+ def unknown?
87
+ self.type.contents == :unknown
88
+ end
89
+
90
+ def list?
91
+ @list
92
+ end
93
+
94
+ # REFACTOR: this should be named type, but that'll break code at the moment
95
+ def list_type
96
+ @type.contents
97
+ end
98
+
99
+ def eql?(other)
100
+ return nil unless other.class == self.class
101
+
102
+ other.type == self.type && other.list? == self.list?
103
+ end
104
+
105
+ alias :== :eql?
106
+
107
+ def hash
108
+ type.contents.hash ^ @list.hash
109
+ end
110
+
111
+ def unify(other)
112
+ return other.unify(self) if Array === other
113
+ return self if other == self and (not self.unknown?)
114
+ return self if other.nil?
115
+ if self.unknown? and other.unknown? then
116
+ # link types between unknowns
117
+ self.type = other.type
118
+ self.list = other.list? or self.list? # HACK may need to be tri-state
119
+ elsif self.unknown? then
120
+ # other's type is now my type
121
+ self.type.contents = other.type.contents
122
+ self.list = other.list?
123
+ elsif other.unknown? then
124
+ # my type is now other's type
125
+ other.type.contents = self.type.contents
126
+ other.list = self.list?
127
+ elsif self.function? and other.function? then
128
+ self_fun = self.type.contents
129
+ other_fun = other.type.contents
130
+
131
+ self_fun.unify_components other_fun
132
+ else
133
+ raise TypeError, "Unable to unify #{self.inspect} with #{other.inspect}"
134
+ end
135
+ return self
136
+ end
137
+
138
+ def to_s
139
+ str = "Type.#{self.type.contents}"
140
+ str << "_list" if self.list?
141
+ str
142
+ end
143
+
144
+ def inspect
145
+ to_s
146
+ end unless $DEBUG
147
+
148
+ end