ruby2c 1.0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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