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,920 @@
1
+
2
+ require 'pp'
3
+ begin require 'rubygems'; rescue LoadError; end
4
+ require 'ruby_parser'
5
+ require 'sexp_processor'
6
+ require 'rewriter'
7
+ require 'function_table'
8
+ require 'r2cenvironment'
9
+ require 'type'
10
+ require 'typed_sexp'
11
+
12
+ # TODO: calls to sexp_type should probably be replaced w/ better Sexp API
13
+
14
+ ##
15
+ # TypeChecker bootstrap table.
16
+ #
17
+ # Default type signatures to help the TypeChecker figure out the correct types
18
+ # for methods that it might not otherwise encounter.
19
+ #
20
+ # The format is:
21
+ # :method_name => [[:reciever_type, :args_type_1, ..., :return_type], ...]
22
+
23
+ $bootstrap = {
24
+ :< => [[:long, :long, :bool],],
25
+ :<= => [[:long, :long, :bool],],
26
+ :== => [[:long, :long, :bool],],
27
+ :> => [[:long, :long, :bool],],
28
+ :>= => [[:long, :long, :bool],],
29
+
30
+ :+ => ([
31
+ [:long, :long, :long],
32
+ [:str, :str, :str],
33
+ ]),
34
+ :- => [[:long, :long, :long],],
35
+ :* => [[:long, :long, :long],],
36
+
37
+ # polymorphics:
38
+ :nil? => [[:value, :bool],],
39
+ :to_s => [[:long, :str],], # HACK - should be :value, :str
40
+ :to_i => [[:long, :long],], # HACK - should be :value, :str
41
+ :puts => [[:void, :str, :void],],
42
+ :print => [[:void, :str, :void],],
43
+
44
+ :[] => ([
45
+ [:long_list, :long, :long],
46
+ [:str, :long, :long],
47
+ ]),
48
+
49
+ # TODO: get rid of these
50
+ :case_equal_str => [[:str, :str, :bool],],
51
+ :case_equal_long => [[:long, :long, :bool],],
52
+ }
53
+
54
+ ##
55
+ # TypeChecker inferences types for sexps using type unification.
56
+ #
57
+ # TypeChecker expects sexps rewritten with Rewriter, and outputs TypedSexps.
58
+ #
59
+ # Nodes marked as 'unsupported' do not do correct type-checking of all the
60
+ # pieces of the node. They generate possibly incorrect output, that is all.
61
+
62
+ class TypeChecker < SexpProcessor
63
+
64
+ ##
65
+ # Environment containing local variables
66
+
67
+ attr_reader :env
68
+
69
+ ##
70
+ # The global environment contains global variables and constants.
71
+
72
+ attr_reader :genv
73
+
74
+ ##
75
+ # Function table
76
+
77
+ attr_reader :functions
78
+
79
+ def initialize # :nodoc:
80
+ super
81
+ @env = ::R2CEnvironment.new
82
+ @genv = ::R2CEnvironment.new
83
+ @functions = FunctionTable.new
84
+ self.auto_shift_type = true
85
+ self.expected = TypedSexp
86
+
87
+ self.unsupported = [:alias, :alloca, :argscat, :argspush, :attrset,
88
+ :back_ref, :bmethod, :break, :case, :cdecl, :cfunc,
89
+ :cref, :cvdecl, :dasgn, :defs, :dmethod, :dot2, :dot3,
90
+ :dregx, :dregx_once, :dsym, :dxstr, :evstr, :fbody,
91
+ :fcall, :flip2, :flip3, :for, :ifunc, :last, :match,
92
+ :match2, :match3, :memo, :method, :module, :newline,
93
+ :next, :nth_ref, :op_asgn1, :op_asgn2, :op_asgn_and,
94
+ :opt_n, :postexe, :redo, :retry, :sclass, :svalue,
95
+ :undef, :until, :valias, :vcall, :when, :xstr, :zarray,
96
+ :zsuper]
97
+
98
+ bootstrap
99
+ end
100
+
101
+ ##
102
+ # Runs the bootstrap stage, which runs over +$bootstrap+ and
103
+ # converts each entry into a full fledged method signature
104
+ # registered in the type checker. This is where the basic knowledge
105
+ # for lower level types (in C) comes from.
106
+
107
+ def bootstrap
108
+ # @genv.add :$stdin, Type.file
109
+ # @genv.add :$stdout, Type.file
110
+ # @genv.add :$stderr, Type.file
111
+
112
+ $bootstrap.each do |name,signatures|
113
+ # FIX: Using Type.send because it must go through method_missing, not new
114
+ signatures.each do |signature|
115
+ lhs_type = Type.send(signature[0])
116
+ return_type = Type.send(signature[-1])
117
+ arg_types = signature[1..-2].map { |t| Type.send(t) }
118
+ @functions.add_function(name, Type.function(lhs_type, arg_types, return_type))
119
+ end
120
+ end
121
+ end
122
+
123
+ ##
124
+ # Logical and unifies its two arguments, then returns a bool sexp.
125
+
126
+ def process_and(exp)
127
+ rhs = process exp.shift
128
+ lhs = process exp.shift
129
+
130
+ rhs_type = rhs.sexp_type
131
+ lhs_type = lhs.sexp_type
132
+
133
+ rhs_type.unify lhs_type
134
+ rhs_type.unify Type.bool
135
+
136
+ return t(:and, rhs, lhs, Type.bool)
137
+ end
138
+
139
+ ##
140
+ # Args list adds each variable to the local variable table with unknown
141
+ # types, then returns an untyped args list of name/type pairs.
142
+
143
+ def process_args(exp)
144
+ formals = t(:args)
145
+ types = []
146
+
147
+ until exp.empty? do
148
+ arg = exp.shift
149
+ type = Type.unknown
150
+ @env.add arg, type
151
+ formals << t(arg, type)
152
+ types << type
153
+ end
154
+
155
+ return formals
156
+ end
157
+
158
+ ##
159
+ # Arg list stuff
160
+
161
+ def process_arglist(exp)
162
+ args = process_array exp
163
+ args[0] = :arglist
164
+
165
+ args
166
+ end
167
+
168
+ ##
169
+ # Array processes each item in the array, then returns an untyped sexp.
170
+
171
+ def process_array(exp)
172
+ types = []
173
+ vars = t(:array)
174
+ until exp.empty? do
175
+ var = process exp.shift
176
+ vars << var
177
+ types << var.sexp_type
178
+ end
179
+ vars
180
+ end
181
+
182
+ ##
183
+ # Attrasgn processes its rhs and lhs, then returns an untyped sexp.
184
+ #--
185
+ # TODO rewrite this in Rewriter
186
+ # echo "self.blah=7" | parse_tree_show -f
187
+ # => [:attrasgn, [:self], :blah=, [:array, [:lit, 7]]]
188
+
189
+ def process_attrasgn(exp)
190
+ rhs = process exp.shift
191
+ name = exp.shift
192
+ lhs = process exp.shift
193
+
194
+ # TODO: since this is an ivar, we need to figger out their var system. :/
195
+ return t(:attrasgn, rhs, name, lhs)
196
+ end
197
+
198
+ ##
199
+ # Begin processes the body, then returns an untyped sexp.
200
+
201
+ def process_begin(exp)
202
+ body = process exp.shift
203
+ # shouldn't be anything to unify
204
+ return t(:begin, body)
205
+ end
206
+
207
+ ##
208
+ # Block processes each sexp in the block, then returns an unknown-typed
209
+ # sexp.
210
+
211
+ def process_block(exp)
212
+ nodes = t(:block, Type.unknown)
213
+ until exp.empty? do
214
+ nodes << process(exp.shift)
215
+ end
216
+ nodes
217
+ end
218
+
219
+ ##
220
+ # Block arg is currently unsupported. Returns an unmentionably-typed
221
+ # sexp.
222
+ #--
223
+ # TODO do something more sensible
224
+
225
+ def process_block_arg(exp)
226
+ t(:block_arg, exp.shift, Type.fucked)
227
+ end
228
+
229
+ ##
230
+ # Block pass is currently unsupported. Returns a typed sexp.
231
+ #--
232
+ # TODO: we might want to look at rewriting this into a call variation.
233
+ # echo "class E; def e(&b); blah(&b); end; end" | parse_tree_show
234
+ # => [:defn, :e, [:scope, [:block, [:args], [:block_arg, :b], [:block_pass, [:lvar, :b], [:fcall, :blah]]]]]
235
+
236
+ def process_block_pass(exp)
237
+ block = process exp.shift
238
+ call = process exp.shift
239
+ t(:block_pass, block, call)
240
+ end
241
+
242
+ ##
243
+ # Call unifies the actual function paramaters against the formal function
244
+ # paramaters, if a function type signature already exists in the function
245
+ # table. If no type signature for the function name exists, the function is
246
+ # added to the function list.
247
+ #
248
+ # Returns a sexp returned to the type of the function return value, or
249
+ # unknown if it has not yet been determined.
250
+
251
+ def process_call(exp)
252
+ lhs = process exp.shift # can be nil
253
+ name = exp.shift
254
+ args = exp.empty? ? nil : process(exp.shift)
255
+
256
+ arg_types = if args.nil? then
257
+ []
258
+ else
259
+ if args.first == :arglist then
260
+ args.sexp_types
261
+ elsif args.first == :splat then
262
+ [args.sexp_type]
263
+ else
264
+ raise "That's not a Ruby Sexp you handed me, I'm freaking out on: #{args.inspect}"
265
+ end
266
+ end
267
+
268
+ if name == :=== then
269
+ rhs = args[1]
270
+ raise "lhs of === may not be nil" if lhs.nil?
271
+ raise "rhs of === may not be nil" if rhs.nil?
272
+ raise "Help! I can't figure out what kind of #=== comparison to use" if
273
+ lhs.sexp_type.unknown? and rhs.sexp_type.unknown?
274
+ equal_type = lhs.sexp_type.unknown? ? rhs.sexp_type : lhs.sexp_type
275
+ name = "case_equal_#{equal_type.list_type}".intern
276
+ end
277
+
278
+ return_type = Type.unknown
279
+ lhs_type = lhs.nil? ? Type.unknown : lhs.sexp_type # TODO: maybe void instead of unknown
280
+
281
+ function_type = Type.function(lhs_type, arg_types, return_type)
282
+ @functions.unify(name, function_type) do
283
+ @functions.add_function(name, function_type)
284
+ $stderr.puts "\nWARNING: function #{name} called w/o being defined. Registering #{function_type.inspect}" if $DEBUG
285
+ end
286
+ return_type = function_type.list_type.return_type
287
+
288
+ return t(:call, lhs, name, args, return_type)
289
+ end
290
+
291
+ ##
292
+ # Class adds the class name to the global environment, processes all of the
293
+ # methods in the class. Returns a zclass-typed sexp.
294
+
295
+ def process_class(exp)
296
+ name = exp.shift
297
+ superclass = exp.shift
298
+
299
+ @genv.add name, Type.zclass
300
+
301
+ result = t(:class, Type.zclass)
302
+ result << name
303
+ result << superclass
304
+
305
+ @env.scope do
306
+ # HACK: not sure this is the right place, maybe genv instead?
307
+ klass = eval(name.to_s) # HACK do proper lookup - ugh
308
+ klass.constants.each do |c|
309
+ const_type = case klass.const_get(c)
310
+ when Fixnum then
311
+ Type.long
312
+ when String then
313
+ Type.str
314
+ else
315
+ Type.unknown
316
+ end
317
+ @env.add c.intern, const_type
318
+ end
319
+
320
+ until exp.empty? do
321
+ result << process(exp.shift)
322
+ end
323
+ end
324
+
325
+ return result
326
+ end
327
+
328
+ ##
329
+ # Colon 2 returns a zclass-typed sexp
330
+
331
+ def process_colon2(exp) # (Module::Class/Module)
332
+ name = process(exp.shift)
333
+ return t(:colon2, name, exp.shift, Type.zclass)
334
+ end
335
+
336
+ ##
337
+ # Colon 3 returns a zclass-typed sexp
338
+
339
+ def process_colon3(exp) # (::OUTER_CONST)
340
+ name = exp.shift
341
+ return t(:colon3, name, Type.const)
342
+ end
343
+
344
+ ##
345
+ # Const looks up the type of the const in the global environment, then
346
+ # returns a sexp of that type.
347
+ #
348
+ # Const is partially unsupported.
349
+ #--
350
+ # TODO :const isn't supported anywhere.
351
+
352
+ def process_const(exp)
353
+ c = exp.shift
354
+ if c.to_s =~ /^[A-Z]/ then
355
+ # TODO: validate that it really is a const?
356
+ type = @genv.lookup(c) rescue @env.lookup(c)
357
+ return t(:const, c, type)
358
+ else
359
+ raise "I don't know what to do with const #{c.inspect}. It doesn't look like a class."
360
+ end
361
+ raise "need to finish process_const in #{self.class}"
362
+ end
363
+
364
+ ##
365
+ # Class variables are currently unsupported. Returns an unknown-typed sexp.
366
+ #--
367
+ # TODO support class variables
368
+
369
+ def process_cvar(exp)
370
+ # TODO: we should treat these as globals and have them in the top scope
371
+ name = exp.shift
372
+ return t(:cvar, name, Type.unknown)
373
+ end
374
+
375
+ ##
376
+ # Class variable assignment
377
+ #--
378
+ # TODO support class variables
379
+
380
+ def process_cvasgn(exp)
381
+ name = exp.shift
382
+ val = process exp.shift
383
+ return t(:cvasgn, name, val, Type.unknown)
384
+ end
385
+
386
+ ##
387
+ # Dynamic variable assignment adds the unknown type to the local
388
+ # environment then returns an unknown-typed sexp.
389
+
390
+ def process_dasgn_curr(exp)
391
+ name = exp.shift
392
+ type = Type.unknown
393
+ @env.add name, type # HACK lookup before adding like lasgn
394
+
395
+ return t(:dasgn_curr, name, type)
396
+ end
397
+
398
+ ##
399
+ # Defined? processes the body, then returns a bool-typed sexp.
400
+
401
+ def process_defined(exp)
402
+ thing = process exp.shift
403
+ return t(:defined, thing, Type.bool)
404
+ end
405
+
406
+ ##
407
+ # Defn adds the formal argument types to the local environment and attempts
408
+ # to unify itself against the function table. If no function exists in the
409
+ # function table, defn adds itself.
410
+ #
411
+ # Defn returns a function-typed sexp.
412
+
413
+ def process_defn(exp)
414
+ name = exp.shift
415
+ unprocessed_args = exp.shift
416
+ args = body = function_type = nil
417
+
418
+ @env.scope do
419
+ args = process unprocessed_args
420
+ body = process exp.shift
421
+
422
+ # Function might already have been defined by a :call node.
423
+ # TODO: figure out the receiver type? Is that possible at this stage?
424
+ function_type = Type.function Type.unknown, args.sexp_types, Type.unknown
425
+ @functions.unify(name, function_type) do
426
+ @functions.add_function(name, function_type)
427
+ $stderr.puts "\nWARNING: Registering function #{name}: #{function_type.inspect}" if $DEBUG
428
+
429
+ end
430
+ end
431
+
432
+ return_type = function_type.list_type.return_type
433
+
434
+ # Drill down and find all return calls, unify each one against the
435
+ # registered function return value. That way they all have to
436
+ # return the same type. If we don't end up finding any returns,
437
+ # set the function return type to void.
438
+
439
+ return_count = 0
440
+ body.each_of_type(:return) do |sub_exp|
441
+ return_type.unify sub_exp[1].sexp_type
442
+ return_count += 1
443
+ end
444
+ return_type.unify Type.void if return_count == 0
445
+
446
+ # TODO: bad API, clean
447
+ raise "wrong" if
448
+ args.sexp_types.size != function_type.list_type.formal_types.size
449
+ args.sexp_types.each_with_index do |type, i|
450
+ type.unify function_type.list_type.formal_types[i]
451
+ end
452
+
453
+ return t(:defn, name, args, body, function_type)
454
+ end
455
+
456
+ ##
457
+ # Dynamic string processes all the elements of the body and returns a
458
+ # string-typed sexp.
459
+
460
+ def process_dstr(exp)
461
+ out = t(:dstr, exp.shift, Type.str)
462
+ until exp.empty? do
463
+ result = process exp.shift
464
+ out << result
465
+ end
466
+ return out
467
+ end
468
+
469
+ ##
470
+ # Dynamic variable lookup looks up the variable in the local environment and
471
+ # returns a sexp of that type.
472
+
473
+ def process_dvar(exp)
474
+ name = exp.shift
475
+ type = @env.lookup name
476
+ return t(:dvar, name, type)
477
+ end
478
+
479
+ ##
480
+ # Ensure processes the res and the ensure, and returns an untyped sexp.
481
+
482
+ def process_ensure(exp)
483
+ res = process exp.shift
484
+ ens = process exp.shift
485
+
486
+ t(:ensure, res, ens)
487
+ end
488
+
489
+ ##
490
+ # DOC
491
+
492
+ def process_error(exp) # :nodoc:
493
+ t(:error, exp.shift)
494
+ end
495
+
496
+ ##
497
+ # False returns a bool-typed sexp.
498
+
499
+ def process_false(exp)
500
+ return t(:false, Type.bool)
501
+ end
502
+
503
+ ##
504
+ # Global variable assignment gets stored in the global assignment.
505
+
506
+ def process_gasgn(exp)
507
+ var = exp.shift
508
+ val = process exp.shift
509
+
510
+ var_type = @genv.lookup var rescue nil
511
+ if var_type.nil? then
512
+ @genv.add var, val.sexp_type
513
+ else
514
+ val.sexp_type.unify var_type
515
+ end
516
+
517
+ return t(:gasgn, var, val, val.sexp_type)
518
+ end
519
+
520
+ ##
521
+ # Global variables get looked up in the global environment. If they are
522
+ # found, a sexp of that type is returned, otherwise the unknown type is
523
+ # added to the global environment and an unknown-typed sexp is returned.
524
+
525
+ def process_gvar(exp)
526
+ name = exp.shift
527
+ type = @genv.lookup name rescue nil
528
+ if type.nil? then
529
+ type = Type.unknown
530
+ @genv.add name, type
531
+ end
532
+ return t(:gvar, name, type)
533
+ end
534
+
535
+ ##
536
+ # Hash (inline hashes) are not supported. Returns an unmentionably-typed
537
+ # sexp.
538
+ #--
539
+ # TODO support inline hashes
540
+
541
+ def process_hash(exp)
542
+ result = t(:hash, Type.fucked)
543
+ until exp.empty? do
544
+ result << process(exp.shift)
545
+ end
546
+ return result
547
+ end
548
+
549
+ ##
550
+ # Instance variable assignment is currently unsupported. Does no
551
+ # unification and returns an untyped sexp
552
+
553
+ def process_iasgn(exp)
554
+ var = exp.shift
555
+ val = process exp.shift
556
+
557
+ var_type = @env.lookup var rescue nil
558
+ if var_type.nil? then
559
+ @env.add var, val.sexp_type
560
+ else
561
+ val.sexp_type.unify var_type
562
+ end
563
+
564
+ return t(:iasgn, var, val, val.sexp_type)
565
+ end
566
+
567
+ ##
568
+ # If unifies the condition against the bool type, then unifies the return
569
+ # types of the then and else expressions against each other. Returns a sexp
570
+ # typed the same as the then and else expressions.
571
+
572
+ def process_if(exp)
573
+ cond_exp = process exp.shift
574
+ then_exp = process exp.shift
575
+ else_exp = process exp.shift rescue nil # might be empty
576
+
577
+ cond_exp.sexp_type.unify Type.bool
578
+ begin
579
+ then_exp.sexp_type.unify else_exp.sexp_type unless then_exp.nil? or else_exp.nil?
580
+ rescue TypeError => err
581
+ puts "Error unifying #{then_exp.inspect} with #{else_exp.inspect}"
582
+ raise
583
+ end
584
+
585
+ # FIX: at least document this
586
+ type = then_exp.sexp_type unless then_exp.nil?
587
+ type = else_exp.sexp_type unless else_exp.nil?
588
+
589
+ return t(:if, cond_exp, then_exp, else_exp, type)
590
+ end
591
+
592
+ ##
593
+ # Iter unifies the dynamic variables against the call args (dynamic
594
+ # variables are used in the iter body) and returns a void-typed sexp.
595
+
596
+ def process_iter(exp)
597
+ call_exp = process exp.shift
598
+ dargs_exp = process exp.shift
599
+ body_exp = process exp.shift
600
+
601
+ lhs = call_exp[1] # FIX
602
+ if lhs.nil? then
603
+ # We're an fcall getting passed a block.
604
+ return t(:iter, call_exp, dargs_exp, body_exp, call_exp.sexp_type)
605
+ else
606
+ Type.unknown_list.unify lhs.sexp_type # force a list type, lhs must be Enum
607
+ Type.new(lhs.sexp_type.list_type).unify dargs_exp.sexp_type # pull out type
608
+
609
+ return t(:iter, call_exp, dargs_exp, body_exp, Type.void)
610
+ end
611
+ end
612
+
613
+ ##
614
+ # Instance variables are currently unsupported. Returns an unknown-typed
615
+ # sexp.
616
+ #--
617
+ # TODO support instance variables
618
+
619
+ def process_ivar(exp)
620
+ name = exp.shift
621
+
622
+ var_type = @env.lookup name rescue nil
623
+ if var_type.nil? then
624
+ var_type = Type.unknown
625
+ @env.add name, var_type
626
+ end
627
+
628
+ return t(:ivar, name, var_type)
629
+ end
630
+
631
+ ##
632
+ # Local variable assignment unifies the variable type from the environment
633
+ # with the assignment expression, and returns a sexp of that type. If there
634
+ # is no local variable in the environment, one is added with the type of the
635
+ # assignment expression and a sexp of that type is returned.
636
+ #
637
+ # If an lasgn has no value (inside masgn) the returned sexp has an unknown
638
+ # Type and a nil node is added as the value.
639
+
640
+ def process_lasgn(exp)
641
+ name = exp.shift
642
+ arg_exp = nil
643
+ arg_type = Type.unknown
644
+ var_type = @env.lookup name rescue nil
645
+
646
+ unless exp.empty? then
647
+ sub_exp = exp.shift
648
+ sub_exp_type = sub_exp.first
649
+ arg_exp = process sub_exp
650
+
651
+ # if we've got an array in there, unify everything in it.
652
+ if sub_exp_type == :array then
653
+ arg_type = arg_exp.sexp_types
654
+ arg_type = arg_type.inject(Type.unknown) do |t1, t2|
655
+ t1.unify t2
656
+ end
657
+ arg_type = arg_type.dup # singleton type
658
+ arg_type.list = true
659
+ else
660
+ arg_type = arg_exp.sexp_type
661
+ end
662
+ end
663
+
664
+ if var_type.nil? then
665
+ @env.add name, arg_type
666
+ var_type = arg_type
667
+ else
668
+ var_type.unify arg_type
669
+ end
670
+
671
+ return t(:lasgn, name, arg_exp, var_type)
672
+ end
673
+
674
+ ##
675
+ # Literal values return a sexp typed to match the literal expression.
676
+
677
+ def process_lit(exp)
678
+ value = exp.shift
679
+ type = nil
680
+
681
+ case value
682
+ when Fixnum then
683
+ type = Type.long
684
+ when Float then
685
+ type = Type.float
686
+ when Symbol then
687
+ type = Type.symbol
688
+ when Regexp then
689
+ type = Type.regexp
690
+ when Range then
691
+ type = Type.range
692
+ when Const then
693
+ type = Type.const
694
+ else
695
+ raise "Bug! no: Unknown literal #{value}:#{value.class}"
696
+ end
697
+
698
+ return t(:lit, value, type)
699
+ end
700
+
701
+ ##
702
+ # Local variables get looked up in the local environment and a sexp of that
703
+ # type is returned.
704
+
705
+ def process_lvar(exp)
706
+ name = exp.shift
707
+ t = @env.lookup name
708
+ return t(:lvar, name, t)
709
+ end
710
+
711
+ ##
712
+ # Multiple assignment
713
+
714
+ def process_masgn(exp)
715
+ mlhs = process exp.shift
716
+ mrhs = process exp.shift
717
+
718
+ mlhs_values = mlhs[1..-1]
719
+ mrhs_values = mrhs[1..-1]
720
+
721
+ mlhs_values.zip(mrhs_values) do |lasgn, value|
722
+ if value.nil? then
723
+ lasgn.sexp_type.unify Type.value # nil
724
+ else
725
+ lasgn.sexp_type.unify value.sexp_type
726
+ end
727
+ end
728
+
729
+ if mlhs_values.length < mrhs_values.length then
730
+ last_lasgn = mlhs_values.last
731
+ last_lasgn.sexp_type.list = true
732
+ end
733
+
734
+ return t(:masgn, mlhs, mrhs)
735
+ end
736
+
737
+ ##
738
+ # Nil returns a value-typed sexp.
739
+
740
+ def process_nil(exp)
741
+ # don't do a fucking thing until... we have something to do
742
+ # HACK: wtf to do here? (what type is nil?!?!)
743
+ return t(:nil, Type.value)
744
+ end
745
+
746
+ ##
747
+ # Not unifies the type of its expression against bool, then returns a
748
+ # bool-typed sexp.
749
+
750
+ def process_not(exp)
751
+ thing = process exp.shift
752
+ thing.sexp_type.unify Type.bool
753
+ return t(:not, thing, Type.bool)
754
+ end
755
+
756
+ ##
757
+ # ||= operator is currently unsupported. Returns an untyped sexp.
758
+
759
+ def process_op_asgn_or(exp)
760
+ lhs = exp.shift
761
+ rhs = process(exp.shift)
762
+
763
+ return t(:op_asgn_or, lhs, rhs)
764
+ end
765
+
766
+ ##
767
+ # Or unifies the left and right hand sides with bool, then returns a
768
+ # bool-typed sexp.
769
+
770
+ def process_or(exp)
771
+ rhs = process exp.shift
772
+ lhs = process exp.shift
773
+
774
+ rhs_type = rhs.sexp_type
775
+ lhs_type = lhs.sexp_type
776
+
777
+ rhs_type.unify lhs_type
778
+ rhs_type.unify Type.bool
779
+
780
+ return t(:or, rhs, lhs, Type.bool)
781
+ end
782
+
783
+ ##
784
+ # Rescue body returns an unknown-typed sexp.
785
+
786
+ def process_resbody(exp)
787
+ o1 = process exp.shift
788
+ o2 = exp.empty? ? nil : process(exp.shift)
789
+ o3 = exp.empty? ? nil : process(exp.shift)
790
+
791
+ result = t(:resbody, Type.unknown) # void?
792
+ result << o1
793
+ result << o2 unless o2.nil?
794
+ result << o3 unless o3.nil?
795
+
796
+ return result
797
+ end
798
+
799
+ ##
800
+ # Rescue unifies the begin, rescue and ensure types, and returns an untyped
801
+ # sexp.
802
+
803
+ def process_rescue(exp)
804
+ try_block = process exp.shift
805
+ rescue_block = process exp.shift
806
+ els = exp.empty? ? nil : process(exp.shift)
807
+
808
+ try_type = try_block.sexp_type
809
+ rescue_type = rescue_block.sexp_type
810
+ # ensure_type = els.sexp_type # HACK/FIX: not sure if I should unify
811
+
812
+ try_type.unify rescue_type
813
+ # try_type.unify ensure_type
814
+
815
+ return t(:rescue, try_block, rescue_block, els, try_type)
816
+ end
817
+
818
+ ##
819
+ # Return returns a void typed sexp.
820
+
821
+ def process_return(exp)
822
+ result = t(:return, Type.void) # TODO why void - cuz this is a keyword
823
+ result << process(exp.shift) unless exp.empty?
824
+ return result
825
+ end
826
+
827
+ ##
828
+ # Scope returns a void-typed sexp.
829
+
830
+ def process_scope(exp)
831
+ return t(:scope, Type.void) if exp.empty?
832
+
833
+ body = process exp.shift
834
+
835
+ return t(:scope, body, Type.void)
836
+ end
837
+
838
+ ##
839
+ # Self is currently unsupported. Returns an unknown-typed sexp.
840
+ #--
841
+ # TODO support self
842
+
843
+ def process_self(exp)
844
+ return t(:self, Type.unknown)
845
+ end
846
+
847
+ ##
848
+ # Splat is currently unsupported. Returns an unknown-typed sexp.
849
+ #--
850
+ # TODO support splat, maybe like :array?
851
+
852
+ def process_splat(exp)
853
+ value = process exp.shift
854
+ return t(:splat, value, Type.unknown) # TODO: probably value_list?
855
+ end
856
+
857
+ ##
858
+ # String literal returns a string-typed sexp.
859
+
860
+ def process_str(exp)
861
+ return t(:str, exp.shift, Type.str)
862
+ end
863
+
864
+ ##
865
+ # Super is currently unsupported. Returns an unknown-typed sexp.
866
+ #--
867
+ # TODO support super
868
+
869
+ def process_super(exp)
870
+ args = process exp.shift
871
+ # TODO try to look up the method in our superclass?
872
+ return t(:super, args, Type.unknown)
873
+ end
874
+
875
+ ##
876
+ # Object#to_ary
877
+
878
+ def process_to_ary(exp)
879
+ to_ary = t(:to_ary)
880
+
881
+ until exp.empty?
882
+ to_ary << process(exp.shift)
883
+ end
884
+
885
+ to_ary.sexp_type = to_ary[1].sexp_type.dup
886
+ to_ary.sexp_type.list = true
887
+
888
+ return to_ary
889
+ end
890
+
891
+ ##
892
+ # True returns a bool-typed sexp.
893
+
894
+ def process_true(exp)
895
+ return t(:true, Type.bool)
896
+ end
897
+
898
+ ##
899
+ # While unifies the condition with bool, then returns an untyped sexp.
900
+
901
+ def process_while(exp)
902
+ cond = process exp.shift
903
+ body = process exp.shift
904
+ is_precondition = exp.shift
905
+ Type.bool.unify cond.sexp_type
906
+ return t(:while, cond, body, is_precondition)
907
+ end
908
+
909
+ ##
910
+ # Yield is currently unsupported. Returns a unmentionably-typed sexp.
911
+
912
+ def process_yield(exp)
913
+ result = t(:yield, Type.fucked)
914
+ until exp.empty? do
915
+ result << process(exp.shift)
916
+ end
917
+ return result
918
+ end
919
+ end
920
+