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,45 @@
1
+ class FunctionTable
2
+
3
+ def initialize
4
+ @functions = Hash.new do |h,k|
5
+ h[k] = []
6
+ end
7
+ end
8
+
9
+ def cheat(name) # HACK: just here for debugging
10
+ puts "\n# WARNING: FunctionTable.cheat called from #{caller[0]}" if $DEBUG
11
+ @functions[name]
12
+ end
13
+
14
+ def [](name) # HACK: just here for transition
15
+ puts "\n# WARNING: FunctionTable.[] called from #{caller[0]}" if $DEBUG
16
+ @functions[name].first
17
+ end
18
+
19
+ def has_key?(name) # HACK: just here for transition
20
+ puts "\n# WARNING: FunctionTable.has_key? called from #{caller[0]}" if $DEBUG
21
+ @functions.has_key?(name)
22
+ end
23
+
24
+ def add_function(name, type)
25
+ @functions[name] << type
26
+ type
27
+ end
28
+
29
+ def unify(name, type)
30
+ success = false
31
+ @functions[name].each do |o| # unify(type)
32
+ begin
33
+ o.unify type
34
+ success = true
35
+ rescue
36
+ # ignore
37
+ end
38
+ end
39
+ unless success then
40
+ yield(name, type) if block_given?
41
+ end
42
+ type
43
+ end
44
+
45
+ end
@@ -0,0 +1,46 @@
1
+
2
+ class FunctionType
3
+
4
+ attr_accessor :receiver_type
5
+ attr_accessor :formal_types
6
+ attr_accessor :return_type
7
+
8
+ def initialize(receiver_type, formal_types, return_type)
9
+ raise "nil not allowed" if formal_types.nil? or return_type.nil?
10
+ @receiver_type = receiver_type
11
+ @formal_types = formal_types
12
+ @return_type = return_type
13
+ end
14
+
15
+ def ==(other)
16
+ return nil unless other.class == self.class
17
+
18
+ return false unless other.receiver_type == self.receiver_type
19
+ return false unless other.return_type == self.return_type
20
+ return false unless other.formal_types == self.formal_types
21
+ return true
22
+ end
23
+
24
+ def unify_components(other)
25
+ raise TypeError, "Unable to unify: different number of args #{self.inspect} vs #{other.inspect}" unless
26
+ @formal_types.length == other.formal_types.length
27
+
28
+ @formal_types.each_with_index do |t, i|
29
+ t.unify other.formal_types[i]
30
+ end
31
+
32
+ @receiver_type.unify other.receiver_type
33
+ @return_type.unify other.return_type
34
+ # rescue RuntimeError # print more complete warning message
35
+ # raise "Unable to unify\n#{self}\nwith\n#{other}"
36
+ end
37
+
38
+ def to_s
39
+ formals = formal_types.map do |t|
40
+ t.inspect
41
+ end
42
+
43
+ "function(#{receiver_type.inspect}, [#{formals.join ', '}], #{return_type.inspect})"
44
+ end
45
+
46
+ end
@@ -0,0 +1,14 @@
1
+ class Handle
2
+
3
+ attr_accessor :contents
4
+
5
+ def initialize(contents)
6
+ @contents = contents
7
+ end
8
+
9
+ def ==(other)
10
+ return nil unless other.class == self.class
11
+ return other.contents == self.contents
12
+ end
13
+
14
+ end
@@ -0,0 +1,59 @@
1
+ require 'ruby_parser_extras' # TODO: split out to environment.rb?
2
+
3
+ class R2CEnvironment < Environment
4
+
5
+ TYPE = 0
6
+ VALUE = 1
7
+
8
+ attr_reader :env
9
+
10
+ def add(id, type, depth = 0)
11
+ raise "Adding illegal identifier #{id.inspect}" unless
12
+ Symbol === id
13
+ raise ArgumentError, "type must be a valid Type instance" unless
14
+ Type === type
15
+
16
+ @env[depth][id.to_s.sub(/^\*/, '').intern][TYPE] = type
17
+ end
18
+
19
+ def depth
20
+ @env.length
21
+ end
22
+
23
+ alias :old_extend :extend
24
+ def extend # override
25
+ @env.unshift(Hash.new { |h,k| h[k] = [] })
26
+ end
27
+
28
+ def get_val(name)
29
+ self._get(name)[VALUE]
30
+ end
31
+
32
+ def lookup(name)
33
+ # HACK: if name is :self, cheat for now until we have full defn remapping
34
+ return Type.fucked if name == :self
35
+
36
+ return self._get(name)[TYPE]
37
+ end
38
+
39
+ def set_val(name, val)
40
+ self._get(name)[VALUE] = val
41
+ end
42
+
43
+ def scope
44
+ self.extend
45
+ begin
46
+ yield
47
+ ensure
48
+ self.unextend
49
+ end
50
+ end
51
+
52
+ def _get(name)
53
+ @env.each do |closure|
54
+ return closure[name] if closure.has_key? name
55
+ end
56
+
57
+ raise NameError, "Unbound var: #{name.inspect} in #{@env.inspect}"
58
+ end
59
+ end
@@ -0,0 +1,35 @@
1
+
2
+ begin require 'rubygems'; rescue LoadError; end
3
+ require 'sexp'
4
+ require 'sexp_processor'
5
+ require 'unique'
6
+ require 'unified_ruby'
7
+
8
+ class Sexp
9
+ # add arglist because we introduce the new array type in this file
10
+ @@array_types << :arglist
11
+ end
12
+
13
+ ##
14
+ # Rewriter (probably should be renamed) is a first-pass filter that
15
+ # normalizes some of ruby's ASTs to make them more processable later
16
+ # in the pipeline. It only has processors for what it is interested
17
+ # in, so real the individual methods for a better understanding of
18
+ # what it does.
19
+
20
+ class Rewriter < SexpProcessor
21
+ def rewrite_defn(exp)
22
+ case exp.last[0]
23
+ when :ivar then
24
+ ivar = exp.pop
25
+ exp.push s(:scope, s(:block, s(:return, ivar)))
26
+ when :attrset then
27
+ var = exp.pop
28
+ exp.push s(:scope,
29
+ s(:block,
30
+ s(:return, s(:iasgn, var.last, s(:lvar, :arg)))))
31
+ end
32
+ exp
33
+ end
34
+ end
35
+
@@ -0,0 +1,673 @@
1
+
2
+ $TESTING = false unless defined? $TESTING
3
+
4
+ require 'pp'
5
+ begin require 'rubygems'; rescue LoadError; end
6
+ require 'ruby_parser'
7
+ require 'sexp_processor'
8
+ require 'composite_sexp_processor'
9
+
10
+ require 'type_checker'
11
+ require 'rewriter'
12
+ require 'crewriter'
13
+ require 'r2cenvironment'
14
+
15
+ ##
16
+ # The whole point of this project! RubyToC is an actually very simple
17
+ # SexpProcessor that does the final conversion from Sexp to C code.
18
+ # This class has more unsupported nodes than any other (on
19
+ # purpose--we'd like TypeChecker and friends to be as generally useful
20
+ # as possible), and as a result, supports a very small subset of ruby.
21
+
22
+ class RubyToAnsiC < SexpProcessor
23
+
24
+ VERSION = '1.0.0.6' # HACK version should be 1.0.0.beta.6, but rubygems sucks
25
+
26
+ # TODO: remove me
27
+ def no(exp) # :nodoc:
28
+ raise "no: #{caller[0].split[1]} #{exp.inspect}"
29
+ end
30
+
31
+ ##
32
+ # Returns a textual version of a C type that corresponds to a sexp
33
+ # type.
34
+
35
+ def self.c_type(typ)
36
+ base_type =
37
+ case typ.type.contents # HACK this is breaking demeter
38
+ when :float then
39
+ "double"
40
+ when :long then
41
+ "long"
42
+ when :str then
43
+ "str"
44
+ when :symbol then
45
+ "symbol"
46
+ when :bool then # TODO: subject to change
47
+ "bool"
48
+ when :void then
49
+ "void"
50
+ when :homo then
51
+ "void *" # HACK
52
+ when :value, :unknown then
53
+ "void *" # HACK
54
+ # HACK: uncomment this and fix the above when you want to have good tests
55
+ # when :unknown then
56
+ # raise "You should not have unknown types by now!"
57
+ else
58
+ raise "Bug! Unknown type #{typ.inspect} in c_type"
59
+ end
60
+
61
+ base_type += " *" if typ.list? unless typ.unknown?
62
+
63
+ base_type
64
+ end
65
+
66
+ ##
67
+ # Provides a place to put things at the file scope.
68
+ # Be smart, make them static (hence the name).
69
+
70
+ attr_reader :statics
71
+
72
+ ##
73
+ # Provides access to the variable scope.
74
+
75
+ attr_reader :env
76
+
77
+ ##
78
+ # Provides access to the method signature prototypes that are needed
79
+ # at the top of the C file.
80
+
81
+ attr_reader :prototypes
82
+
83
+ ##
84
+ # Provides a (rather bogus) preamble. Put your includes and defines
85
+ # here. It really should be made to be much more clean and
86
+ # extendable.
87
+
88
+ def preamble
89
+ "// BEGIN METARUBY PREAMBLE
90
+ #include <ruby.h>
91
+ #define RB_COMPARE(x, y) (x) == (y) ? 0 : (x) < (y) ? -1 : 1
92
+ typedef char * str;
93
+ #define case_equal_long(x, y) ((x) == (y))
94
+ // END METARUBY PREAMBLE
95
+ " + self.prototypes.join('')
96
+ end
97
+
98
+ ##
99
+ # Lazy initializer for the composite RubytoC translator chain.
100
+
101
+ def self.translator
102
+ unless defined? @translator then
103
+ @translator = CompositeSexpProcessor.new
104
+ @translator << Rewriter.new
105
+ @translator << TypeChecker.new
106
+ # @translator << CRewriter.new
107
+ @translator << RubyToAnsiC.new
108
+ @translator.on_error_in(:defn) do |processor, exp, err|
109
+ result = processor.expected.new
110
+ case result
111
+ when Array then
112
+ result << :error
113
+ end
114
+ msg = "// ERROR: #{err.class}: #{err}"
115
+ msg += " in #{exp.inspect}" unless exp.nil? or $TESTING
116
+ msg += " from #{caller.join(', ')}" unless $TESTING
117
+ result << msg
118
+ result
119
+ end
120
+ end
121
+ @translator
122
+ end
123
+
124
+ def initialize # :nodoc:
125
+ super
126
+ @env = ::R2CEnvironment.new
127
+ self.auto_shift_type = true
128
+ self.unsupported = [:alias, :alloca, :argscat, :argspush, :attrasgn, :attrset, :back_ref, :begin, :block_arg, :block_pass, :bmethod, :break, :case, :cdecl, :cfunc, :colon2, :colon3, :cref, :cvasgn, :cvdecl, :dasgn, :defined, :defs, :dmethod, :dot2, :dot3, :dregx, :dregx_once, :dstr, :dsym, :dxstr, :ensure, :evstr, :fbody, :fcall, :flip2, :flip3, :for, :gasgn, :hash, :ifunc, :last, :masgn, :match, :match2, :match3, :memo, :method, :module, :newline, :next, :nth_ref, :op_asgn_or, :op_asgn1, :op_asgn2, :op_asgn_and, :opt_n, :postexe, :redo, :resbody, :rescue, :retry, :sclass, :self, :splat, :super, :svalue, :to_ary, :undef, :until, :valias, :vcall, :when, :xstr, :yield, :zarray, :zsuper]
129
+
130
+ self.strict = true
131
+ self.expected = String
132
+
133
+ @statics = []
134
+ @prototypes = []
135
+ end
136
+
137
+ ##
138
+ # Logical And. Nothing exciting here
139
+
140
+ def process_and(exp)
141
+ lhs = process exp.shift
142
+ rhs = process exp.shift
143
+
144
+ return "#{lhs} && #{rhs}"
145
+ end
146
+
147
+ ##
148
+ # Arglist is used by call arg lists.
149
+
150
+ def process_arglist(exp)
151
+ return '' if exp.empty?
152
+ return process_array(exp)
153
+ end
154
+
155
+ ##
156
+ # Argument List including variable types.
157
+
158
+ def process_args(exp)
159
+ args = []
160
+
161
+ until exp.empty? do
162
+ arg = exp.shift
163
+ name = arg.first.to_s.sub(/^\*/, '').intern
164
+ type = arg.sexp_type
165
+ @env.add name, type
166
+ args << "#{self.class.c_type(type)} #{name}"
167
+ end
168
+
169
+ return "(#{args.join ', '})"
170
+ end
171
+
172
+ ##
173
+ # Array is used as call arg lists and as initializers for variables.
174
+
175
+ def process_array(exp)
176
+ return "rb_ary_new()" if exp.empty? # HACK FIX! not ansi c!
177
+
178
+ code = []
179
+ until exp.empty? do
180
+ code << process(exp.shift)
181
+ end
182
+
183
+ s = code.join ', '
184
+
185
+ return s
186
+ end
187
+
188
+ ##
189
+ # Block doesn't have an analog in C, except maybe as a functions's
190
+ # outer braces.
191
+
192
+ def process_block(exp)
193
+ code = []
194
+ until exp.empty? do
195
+ code << process(exp.shift)
196
+ end
197
+
198
+ body = code.join(";\n")
199
+ body += ";" unless body =~ /[;}]\Z/
200
+ body += "\n"
201
+
202
+ return body
203
+ end
204
+
205
+ ##
206
+ # Call, both unary and binary operators and regular function calls.
207
+ #
208
+ # TODO: This needs a lot of work. We've cheated with the case
209
+ # statement below. We need a real function signature lookup like we
210
+ # have in R2CRewriter.
211
+
212
+ def process_call(exp)
213
+ receiver = exp.shift
214
+ name = exp.shift
215
+
216
+ receiver_type = Type.unknown
217
+ unless receiver.nil? then
218
+ receiver_type = receiver.sexp_type
219
+ end
220
+ receiver = process receiver
221
+
222
+ case name
223
+ # TODO: these need to be numerics
224
+ # emacs gets confused by :/ below, need quotes to fix indentation
225
+ when :==, :<, :>, :<=, :>=, :-, :+, :*, :"/", :% then
226
+ args = process exp.shift[1]
227
+ return "#{receiver} #{name} #{args}"
228
+ when :<=>
229
+ args = process exp.shift[1]
230
+ return "RB_COMPARE(#{receiver}, #{args})"
231
+ when :equal?
232
+ args = process exp.shift
233
+ return "#{receiver} == #{args}" # equal? == address equality
234
+ when :[]
235
+ args = process exp.shift
236
+ return "#{receiver}[#{args}]"
237
+ when :nil?
238
+ exp.clear
239
+ return receiver.to_s
240
+ else
241
+ args = process exp.shift
242
+
243
+ if receiver.nil? and args.nil? then
244
+ args = ""
245
+ elsif receiver.nil? then
246
+ # nothing to do
247
+ elsif args.nil? or args.empty? then
248
+ args = receiver
249
+ else
250
+ args = "#{receiver}, #{args}"
251
+ end
252
+
253
+ args = '' if args == 'rb_ary_new()' # HACK
254
+
255
+ return "#{name}(#{args})"
256
+ end
257
+ end
258
+
259
+ ##
260
+ # DOC
261
+
262
+ def process_class(exp)
263
+ name = exp.shift
264
+ superklass = exp.shift
265
+
266
+ result = []
267
+
268
+ until exp.empty? do
269
+ # HACK: cheating!
270
+ result << process(exp.shift)
271
+ end
272
+
273
+ result.unshift(*statics)
274
+ result.unshift "// class #{name} < #{superklass}"
275
+
276
+ return result.join("\n\n")
277
+ end
278
+
279
+ ##
280
+ # Constants, must be pre-defined in the global env for ansi c.
281
+
282
+ def process_const(exp)
283
+ name = exp.shift
284
+ return name.to_s
285
+ end
286
+
287
+ ##
288
+ # Constants, must be defined in the global env.
289
+ #
290
+ # TODO: This will cause a lot of errors with the built in classes
291
+ # until we add them to the bootstrap phase.
292
+ # HACK: what is going on here??? We have NO tests for this node
293
+
294
+ def process_cvar(exp)
295
+ # TODO: we should treat these as globals and have them in the top scope
296
+ name = exp.shift
297
+ return name.to_s
298
+ end
299
+
300
+ ##
301
+ # Iterator variables.
302
+ #
303
+ # TODO: check to see if this is the least bit relevant anymore. We
304
+ # might have rewritten them all.
305
+
306
+ def process_dasgn_curr(exp) # TODO: audit against obfuscator
307
+ var = exp.shift
308
+ @env.add var.to_sym, exp.sexp_type
309
+ return var.to_s
310
+ end
311
+
312
+ ##
313
+ # Function definition
314
+
315
+ METHOD_MAP = { # TODO: steal map from ZenTest
316
+ :| => "or",
317
+ :& => "and",
318
+ :^ => "xor",
319
+ }
320
+
321
+ def process_defn(exp) # TODO: audit against obfuscator
322
+ name = exp.shift
323
+ name = METHOD_MAP[name] if METHOD_MAP.has_key? name
324
+ name = name.to_s.sub(/(.*)\?$/, 'is_\1').intern
325
+ args = process exp.shift
326
+ body = process exp.shift
327
+ function_type = exp.sexp_type
328
+
329
+ ret_type = self.class.c_type function_type.list_type.return_type
330
+
331
+ @prototypes << "#{ret_type} #{name}#{args};\n"
332
+ "#{ret_type}\n#{name}#{args} #{body}"
333
+ end
334
+
335
+ def process_defx(exp) # TODO: audit against obfuscator
336
+ return process_defn(exp)
337
+ end
338
+
339
+ ##
340
+ # Generic handler. Ignore me, I'm not here.
341
+ #
342
+ # TODO: nuke dummy nodes by using new SexpProcessor rewrite rules.
343
+
344
+ def process_dummy(exp)
345
+ process_block(exp).chomp
346
+ end
347
+
348
+ ##
349
+ # Dynamic variables, should be the same as lvar at this stage.
350
+ #
351
+ # TODO: remove / rewrite?
352
+
353
+ def process_dvar(exp)
354
+ var = exp.shift
355
+ @env.add var.to_sym, exp.sexp_type
356
+ return var.to_s
357
+ end
358
+
359
+ ##
360
+ # DOC - TODO: what is this?!?
361
+
362
+ def process_error(exp)
363
+ return exp.shift
364
+ end
365
+
366
+ ##
367
+ # False. Pretty straightforward.
368
+
369
+ def process_false(exp)
370
+ return "0"
371
+ end
372
+
373
+ # TODO: process_gasgn
374
+
375
+ ##
376
+ # Global variables, evil but necessary.
377
+ #
378
+ # TODO: get the case statement out by using proper bootstrap in genv.
379
+
380
+ def process_gvar(exp)
381
+ name = exp.shift
382
+ type = exp.sexp_type
383
+ case name
384
+ when :$stderr then
385
+ "stderr"
386
+ else
387
+ raise "Bug! Unhandled gvar #{name.inspect} (type = #{type})"
388
+ end
389
+ end
390
+
391
+ ##
392
+ # Instance Variable Assignment
393
+
394
+ def process_iasgn(exp)
395
+ name = exp.shift
396
+ val = process exp.shift
397
+ "self->#{name.to_s.sub(/^@/, '')} = #{val}"
398
+ end
399
+
400
+ ##
401
+ # Conditional statements
402
+ #
403
+ # TODO: implementation is ugly as hell... PLEASE try to clean
404
+
405
+ def process_if(exp)
406
+ cond_part = process exp.shift
407
+
408
+ result = "if (#{cond_part})"
409
+
410
+ then_block = ! exp.first.nil? && exp.first.first == :block
411
+ then_part = process exp.shift
412
+ else_block = ! exp.first.nil? && exp.first.first == :block
413
+ else_part = process exp.shift
414
+
415
+ then_part = "" if then_part.nil?
416
+ else_part = "" if else_part.nil?
417
+
418
+ result += " {\n"
419
+
420
+ then_part = then_part.join(";\n") if Array === then_part
421
+ then_part += ";" unless then_part =~ /[;}]\Z/
422
+ # HACK: um... deal with nil correctly (see unless support)
423
+ result += then_part.to_s # + ";"
424
+ result += ";" if then_part.nil?
425
+ result += "\n" unless result =~ /\n\Z/
426
+ result += "}"
427
+
428
+ if else_part != "" then
429
+ result += " else {\n"
430
+ else_part = else_part.join(";\n") if Array === else_part
431
+ else_part += ";" unless else_part =~ /[;}]\Z/
432
+ result += else_part
433
+ result += "\n}"
434
+ end
435
+
436
+ result
437
+ end
438
+
439
+ ##
440
+ # Iterators for loops. After rewriter nearly all iter nodes
441
+ # should be able to be interpreted as a for loop. If not, then you
442
+ # are doing something not supported by C in the first place.
443
+
444
+ def process_iter(exp) # TODO: audit against obfuscator
445
+ out = []
446
+ # Only support enums in C-land
447
+ raise UnsupportedNodeError if exp[0][1].nil? # HACK ugly
448
+ @env.scope do
449
+ enum = exp[0][1][1] # HACK ugly t(:iter, t(:call, lhs <-- get lhs
450
+
451
+ p exp
452
+
453
+ call = process exp.shift
454
+ var = process(exp.shift).intern # semi-HACK-y
455
+ body = process exp.shift
456
+ index = "index_#{var}"
457
+
458
+ body += ";" unless body =~ /[;}]\Z/
459
+ body.gsub!(/\n\n+/, "\n")
460
+
461
+ out << "unsigned long #{index};"
462
+ out << "for (#{index} = 0; #{enum}[#{index}] != NULL; ++#{index}) {"
463
+ out << "#{self.class.c_type @env.lookup(var)} #{var} = #{enum}[#{index}];"
464
+ out << body
465
+ out << "}"
466
+ end
467
+
468
+ return out.join("\n")
469
+ end
470
+
471
+ ##
472
+ # Instance Variable Access
473
+
474
+ def process_ivar(exp)
475
+ name = exp.shift
476
+ "self->#{name.to_s.sub(/^@/, '')}"
477
+ end
478
+
479
+ ##
480
+ # Assignment to a local variable.
481
+ #
482
+ # TODO: figure out array issues and clean up.
483
+
484
+ def process_lasgn(exp) # TODO: audit against obfuscator
485
+ out = ""
486
+
487
+ var = exp.shift
488
+ value = exp.shift
489
+ # grab the size of the args, if any, before process converts to a string
490
+ arg_count = 0
491
+ arg_count = value.length - 1 if value.first == :array
492
+ args = value
493
+
494
+ exp_type = exp.sexp_type
495
+ @env.add var.to_sym, exp_type
496
+ var_type = self.class.c_type exp_type
497
+
498
+ if exp_type.list? then
499
+ assert_type args, :array
500
+
501
+ raise "array must be of one type" unless args.sexp_type == Type.homo
502
+
503
+ # HACK: until we figure out properly what to do w/ zarray
504
+ # before we know what its type is, we will default to long.
505
+ array_type = args.sexp_types.empty? ? 'void *' : self.class.c_type(args.sexp_types.first)
506
+
507
+ args.shift # :arglist
508
+ # TODO: look into alloca
509
+ out << "#{var} = (#{array_type}) malloc(sizeof(#{array_type}) * #{args.length});\n"
510
+ args.each_with_index do |o,i|
511
+ out << "#{var}[#{i}] = #{process o};\n"
512
+ end
513
+ else
514
+ out << "#{var} = #{process args}"
515
+ end
516
+
517
+ out.sub!(/;\n\Z/, '')
518
+
519
+ return out
520
+ end
521
+
522
+ ##
523
+ # Literals, numbers for the most part. Will probably cause
524
+ # compilation errors if you try to translate bignums and other
525
+ # values that don't have analogs in the C world. Sensing a pattern?
526
+
527
+ def process_lit(exp)
528
+ # TODO what about floats and big numbers?
529
+
530
+ value = exp.shift
531
+ sexp_type = exp.sexp_type
532
+ case sexp_type
533
+ when Type.long, Type.float then
534
+ return value.to_s
535
+ when Type.symbol then
536
+ return value.to_s.inspect # HACK wrong! write test!
537
+ else
538
+ raise "Bug! no: Unknown literal #{value}:#{value.class}"
539
+ end
540
+ end
541
+
542
+ ##
543
+ # Local variable
544
+
545
+ def process_lvar(exp)
546
+ name = exp.shift
547
+ # do nothing at this stage, var should have been checked for
548
+ # existance already.
549
+ return name.to_s
550
+ end
551
+
552
+ # TODO: pull masgn from obfuscator
553
+ # TODO: pull module from obfuscator
554
+ # TODO: pull next from obfuscator
555
+
556
+ ##
557
+ # Nil, currently ruby nil, not C NULL (0).
558
+
559
+ def process_nil(exp)
560
+ return "NULL"
561
+ end
562
+
563
+ ##
564
+ # Nil, currently ruby nil, not C NULL (0).
565
+
566
+ def process_not(exp)
567
+ term = process exp.shift
568
+ return "!(#{term})"
569
+ end
570
+
571
+ ##
572
+ # Logical or. Nothing exciting here
573
+
574
+ def process_or(exp)
575
+ lhs = process exp.shift
576
+ rhs = process exp.shift
577
+
578
+ return "#{lhs} || #{rhs}"
579
+ end
580
+
581
+ ##
582
+ # Return statement. Nothing exciting here
583
+
584
+ def process_return(exp)
585
+ return "return #{process exp.shift}"
586
+ end
587
+
588
+ ##
589
+ # Scope has no real equivalent in C-land, except that like
590
+ # process_block above. We put variable declarations here before the
591
+ # body and use this as our opportunity to open a variable
592
+ # scope. Crafty, no?
593
+
594
+ def process_scope(exp)
595
+ declarations, body = with_scope do
596
+ process exp.shift unless exp.empty?
597
+ end
598
+
599
+ declarations = declarations.reject { |d| d =~ / static_/ }
600
+
601
+ result = []
602
+ result << "{"
603
+ result << declarations.join("\n") unless declarations.empty?
604
+ result << body.chomp if body
605
+ result << "}"
606
+
607
+ return result.join("\n")
608
+ end
609
+
610
+ ##
611
+ # A bogus ruby sexp type for generating static variable declarations
612
+
613
+ def process_static(exp)
614
+ return exp.shift
615
+ end
616
+
617
+ ##
618
+ # Strings. woot.
619
+
620
+ def process_str(exp)
621
+ return exp.shift.inspect
622
+ end
623
+
624
+ # TODO: pull scope from obfuscator
625
+
626
+ ##
627
+ # Truth... what is truth?
628
+
629
+ def process_true(exp)
630
+ return "1"
631
+ end
632
+
633
+ ##
634
+ # While block. Nothing exciting here.
635
+
636
+ def process_while(exp)
637
+ cond = process exp.shift
638
+ body = process exp.shift
639
+ body += ";" unless body =~ /;/
640
+ is_precondition = exp.shift
641
+ if is_precondition then
642
+ return "while (#{cond}) {\n#{body.strip}\n}"
643
+ else
644
+ return "{\n#{body.strip}\n} while (#{cond})"
645
+ end
646
+ end
647
+
648
+ def with_scope
649
+ declarations = []
650
+ result = nil
651
+ outer_scope = @env.all.keys
652
+
653
+ @env.scope do
654
+ result = yield
655
+ @env.current.sort_by { |v,_| v.to_s }.each do |var, (type,val)|
656
+ next if outer_scope.include? var
657
+ decl = "#{self.class.c_type type} #{var}"
658
+ case val
659
+ when nil then
660
+ # do nothing
661
+ when /^\[/ then
662
+ decl << "#{val}"
663
+ else
664
+ decl << " = #{val}"
665
+ end
666
+ decl << ';'
667
+ declarations << decl
668
+ end
669
+ end
670
+
671
+ return declarations, result
672
+ end
673
+ end