Class: TypeChecker

Inherits:
SexpProcessor
  • Object
show all
Defined in:
lib/type_checker.rb

Overview

TypeChecker inferences types for sexps using type unification.

TypeChecker expects sexps rewritten with Rewriter, and outputs TypedSexps.

Nodes marked as ‘unsupported’ do not do correct type-checking of all the pieces of the node. They generate possibly incorrect output, that is all.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeTypeChecker

:nodoc:



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/type_checker.rb', line 79

def initialize # :nodoc:
  super
  @env = ::R2CEnvironment.new
  @genv = ::R2CEnvironment.new
  @functions = FunctionTable.new
  self.auto_shift_type = true
  self.expected = TypedSexp

  self.unsupported = [:alias, :alloca, :argscat, :argspush, :attrset,
                      :back_ref, :bmethod, :break, :case, :cdecl, :cfunc,
                      :cref, :cvdecl, :dasgn, :defs, :dmethod, :dot2, :dot3,
                      :dregx, :dregx_once, :dsym, :dxstr, :evstr, :fbody,
                      :fcall, :flip2, :flip3, :for, :ifunc, :last, :match,
                      :match2, :match3, :memo, :method, :module, :newline,
                      :next, :nth_ref, :op_asgn1, :op_asgn2, :op_asgn_and,
                      :opt_n, :postexe, :redo, :retry, :sclass, :svalue,
                      :undef, :until, :valias, :vcall, :when, :xstr, :zarray,
                      :zsuper]

  bootstrap
end

Instance Attribute Details

#envObject (readonly)

Environment containing local variables



67
68
69
# File 'lib/type_checker.rb', line 67

def env
  @env
end

#functionsObject (readonly)

Function table



77
78
79
# File 'lib/type_checker.rb', line 77

def functions
  @functions
end

#genvObject (readonly)

The global environment contains global variables and constants.



72
73
74
# File 'lib/type_checker.rb', line 72

def genv
  @genv
end

Instance Method Details

#bootstrapObject

Runs the bootstrap stage, which runs over $bootstrap and converts each entry into a full fledged method signature registered in the type checker. This is where the basic knowledge for lower level types (in C) comes from.



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/type_checker.rb', line 107

def bootstrap
  # @genv.add :$stdin, Type.file
  # @genv.add :$stdout, Type.file
  # @genv.add :$stderr, Type.file

  $bootstrap.each do |name,signatures|
    # FIX: Using Type.send because it must go through method_missing, not new
    signatures.each do |signature|
      lhs_type = Type.send(signature[0])
      return_type = Type.send(signature[-1])
      arg_types = signature[1..-2].map { |t| Type.send(t) }
      @functions.add_function(name, Type.function(lhs_type, arg_types, return_type))
    end
  end
end

#process_and(exp) ⇒ Object

Logical and unifies its two arguments, then returns a bool sexp.



126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/type_checker.rb', line 126

def process_and(exp)
  rhs = process exp.shift
  lhs = process exp.shift

  rhs_type = rhs.sexp_type
  lhs_type = lhs.sexp_type

  rhs_type.unify lhs_type
  rhs_type.unify Type.bool

  return t(:and, rhs, lhs, Type.bool)
end

#process_arglist(exp) ⇒ Object

Arg list stuff



161
162
163
164
165
166
# File 'lib/type_checker.rb', line 161

def process_arglist(exp)
  args = process_array exp
  args[0] = :arglist

  args
end

#process_args(exp) ⇒ Object

Args list adds each variable to the local variable table with unknown types, then returns an untyped args list of name/type pairs.



143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/type_checker.rb', line 143

def process_args(exp)
  formals = t(:args)
  types = []

  until exp.empty? do
    arg = exp.shift
    type = Type.unknown
    @env.add arg, type
    formals << t(arg, type)
    types << type
  end

  return formals
end

#process_array(exp) ⇒ Object

Array processes each item in the array, then returns an untyped sexp.



171
172
173
174
175
176
177
178
179
180
# File 'lib/type_checker.rb', line 171

def process_array(exp)
  types = []
  vars = t(:array)
  until exp.empty? do
    var = process exp.shift
    vars << var
    types << var.sexp_type
  end
  vars
end

#process_attrasgn(exp) ⇒ Object

Attrasgn processes its rhs and lhs, then returns an untyped sexp. – TODO rewrite this in Rewriter echo “self.blah=7” | parse_tree_show -f

> [:attrasgn, [:self], :blah=, [:array, [:lit, 7]]]



189
190
191
192
193
194
195
196
# File 'lib/type_checker.rb', line 189

def process_attrasgn(exp)
  rhs = process exp.shift
  name = exp.shift
  lhs = process exp.shift

  # TODO: since this is an ivar, we need to figger out their var system. :/
  return t(:attrasgn, rhs, name, lhs)
end

#process_begin(exp) ⇒ Object

Begin processes the body, then returns an untyped sexp.



201
202
203
204
205
# File 'lib/type_checker.rb', line 201

def process_begin(exp)
  body = process exp.shift
  # shouldn't be anything to unify
  return t(:begin, body)
end

#process_block(exp) ⇒ Object

Block processes each sexp in the block, then returns an unknown-typed sexp.



211
212
213
214
215
216
217
# File 'lib/type_checker.rb', line 211

def process_block(exp)
  nodes = t(:block, Type.unknown)
  until exp.empty? do
    nodes << process(exp.shift)
  end
  nodes
end

#process_block_arg(exp) ⇒ Object

Block arg is currently unsupported. Returns an unmentionably-typed sexp. – TODO do something more sensible



225
226
227
# File 'lib/type_checker.rb', line 225

def process_block_arg(exp)
  t(:block_arg, exp.shift, Type.fucked)
end

#process_block_pass(exp) ⇒ Object

Block pass is currently unsupported. Returns a typed sexp. – TODO: we might want to look at rewriting this into a call variation. echo “class E; def e(&b); blah(&b); end; end” | parse_tree_show

> [:defn, :e, [:scope, [:block, [:args], [:block_arg, :b], [:block_pass, [:lvar, :b], [:fcall, :blah]]]]]



236
237
238
239
240
# File 'lib/type_checker.rb', line 236

def process_block_pass(exp)
  block = process exp.shift
  call  = process exp.shift
  t(:block_pass, block, call)
end

#process_call(exp) ⇒ Object

Call unifies the actual function paramaters against the formal function paramaters, if a function type signature already exists in the function table. If no type signature for the function name exists, the function is added to the function list.

Returns a sexp returned to the type of the function return value, or unknown if it has not yet been determined.



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/type_checker.rb', line 251

def process_call(exp)
  lhs = process exp.shift     # can be nil
  name = exp.shift
  args = exp.empty? ? nil : process(exp.shift)

  arg_types = if args.nil? then
                []
              else
                if args.first == :arglist then
                  args.sexp_types
                elsif args.first == :splat then
                  [args.sexp_type]
                else
                  raise "That's not a Ruby Sexp you handed me, I'm freaking out on: #{args.inspect}"
                end
              end

  if name == :=== then
    rhs = args[1]
    raise "lhs of === may not be nil" if lhs.nil?
    raise "rhs of === may not be nil" if rhs.nil?
    raise "Help! I can't figure out what kind of #=== comparison to use" if
      lhs.sexp_type.unknown? and rhs.sexp_type.unknown?
    equal_type = lhs.sexp_type.unknown? ? rhs.sexp_type : lhs.sexp_type
    name = "case_equal_#{equal_type.list_type}".intern
  end

  return_type = Type.unknown
  lhs_type = lhs.nil? ? Type.unknown : lhs.sexp_type # TODO: maybe void instead of unknown

  function_type = Type.function(lhs_type, arg_types, return_type)
  @functions.unify(name, function_type) do
    @functions.add_function(name, function_type)
    $stderr.puts "\nWARNING: function #{name} called w/o being defined. Registering #{function_type.inspect}" if $DEBUG
  end
  return_type = function_type.list_type.return_type

  return t(:call, lhs, name, args, return_type)
end

#process_class(exp) ⇒ Object

Class adds the class name to the global environment, processes all of the methods in the class. Returns a zclass-typed sexp.



295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/type_checker.rb', line 295

def process_class(exp)
  name = exp.shift
  superclass = exp.shift

  @genv.add name, Type.zclass

  result = t(:class, Type.zclass)
  result << name
  result << superclass
    
  @env.scope do
    # HACK: not sure this is the right place, maybe genv instead?
    klass = eval(name.to_s) # HACK do proper lookup - ugh
    klass.constants.each do |c|
      const_type = case klass.const_get(c)
                   when Fixnum then
                     Type.long
                   when String then
                     Type.str
                   else
                     Type.unknown
                   end
      @env.add c.intern, const_type
    end

    until exp.empty? do
      result << process(exp.shift)
    end
  end

  return result
end

#process_colon2(exp) ⇒ Object

Colon 2 returns a zclass-typed sexp



331
332
333
334
# File 'lib/type_checker.rb', line 331

def process_colon2(exp) # (Module::Class/Module)
  name = process(exp.shift)
  return t(:colon2, name, exp.shift, Type.zclass)
end

#process_colon3(exp) ⇒ Object

Colon 3 returns a zclass-typed sexp



339
340
341
342
# File 'lib/type_checker.rb', line 339

def process_colon3(exp) # (::OUTER_CONST)
  name = exp.shift
  return t(:colon3, name, Type.const)
end

#process_const(exp) ⇒ Object

Const looks up the type of the const in the global environment, then returns a sexp of that type.

Const is partially unsupported. – TODO :const isn’t supported anywhere.



352
353
354
355
356
357
358
359
360
361
362
# File 'lib/type_checker.rb', line 352

def process_const(exp)
  c = exp.shift
  if c.to_s =~ /^[A-Z]/ then
    # TODO: validate that it really is a const?
    type = @genv.lookup(c) rescue @env.lookup(c)
    return t(:const, c, type)
  else
    raise "I don't know what to do with const #{c.inspect}. It doesn't look like a class."
  end
  raise "need to finish process_const in #{self.class}"
end

#process_cvar(exp) ⇒ Object

Class variables are currently unsupported. Returns an unknown-typed sexp. – TODO support class variables



369
370
371
372
373
# File 'lib/type_checker.rb', line 369

def process_cvar(exp)
  # TODO: we should treat these as globals and have them in the top scope
  name = exp.shift
  return t(:cvar, name, Type.unknown)
end

#process_cvasgn(exp) ⇒ Object

Class variable assignment – TODO support class variables



380
381
382
383
384
# File 'lib/type_checker.rb', line 380

def process_cvasgn(exp)
  name = exp.shift
  val = process exp.shift
  return t(:cvasgn, name, val, Type.unknown)
end

#process_dasgn_curr(exp) ⇒ Object

Dynamic variable assignment adds the unknown type to the local environment then returns an unknown-typed sexp.



390
391
392
393
394
395
396
# File 'lib/type_checker.rb', line 390

def process_dasgn_curr(exp)
  name = exp.shift
  type = Type.unknown
  @env.add name, type # HACK lookup before adding like lasgn

  return t(:dasgn_curr, name, type)
end

#process_defined(exp) ⇒ Object

Defined? processes the body, then returns a bool-typed sexp.



401
402
403
404
# File 'lib/type_checker.rb', line 401

def process_defined(exp)
  thing = process exp.shift
  return t(:defined, thing, Type.bool)
end

#process_defn(exp) ⇒ Object

Defn adds the formal argument types to the local environment and attempts to unify itself against the function table. If no function exists in the function table, defn adds itself.

Defn returns a function-typed sexp.



413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
# File 'lib/type_checker.rb', line 413

def process_defn(exp)
  name = exp.shift
  unprocessed_args = exp.shift
  args = body = function_type = nil

  @env.scope do
    args = process unprocessed_args
    body = process exp.shift

    # Function might already have been defined by a :call node.
    # TODO: figure out the receiver type? Is that possible at this stage?
    function_type = Type.function Type.unknown, args.sexp_types, Type.unknown
    @functions.unify(name, function_type) do
      @functions.add_function(name, function_type)
      $stderr.puts "\nWARNING: Registering function #{name}: #{function_type.inspect}" if $DEBUG

    end
  end

  return_type = function_type.list_type.return_type

  # Drill down and find all return calls, unify each one against the
  # registered function return value. That way they all have to
  # return the same type. If we don't end up finding any returns,
  # set the function return type to void.

  return_count = 0
  body.each_of_type(:return) do |sub_exp|
    return_type.unify sub_exp[1].sexp_type
    return_count += 1
  end
  return_type.unify Type.void if return_count == 0

  # TODO: bad API, clean
  raise "wrong" if
    args.sexp_types.size != function_type.list_type.formal_types.size
  args.sexp_types.each_with_index do |type, i|
    type.unify function_type.list_type.formal_types[i]
  end

  return t(:defn, name, args, body, function_type)
end

#process_dstr(exp) ⇒ Object

Dynamic string processes all the elements of the body and returns a string-typed sexp.



460
461
462
463
464
465
466
467
# File 'lib/type_checker.rb', line 460

def process_dstr(exp)
  out = t(:dstr, exp.shift, Type.str)
  until exp.empty? do
    result = process exp.shift
    out << result
  end
  return out
end

#process_dvar(exp) ⇒ Object

Dynamic variable lookup looks up the variable in the local environment and returns a sexp of that type.



473
474
475
476
477
# File 'lib/type_checker.rb', line 473

def process_dvar(exp)
  name = exp.shift
  type = @env.lookup name
  return t(:dvar, name, type)
end

#process_ensure(exp) ⇒ Object

Ensure processes the res and the ensure, and returns an untyped sexp.



482
483
484
485
486
487
# File 'lib/type_checker.rb', line 482

def process_ensure(exp)
  res = process exp.shift
  ens = process exp.shift

  t(:ensure, res, ens)
end

#process_error(exp) ⇒ Object

DOC



492
493
494
# File 'lib/type_checker.rb', line 492

def process_error(exp) # :nodoc:
  t(:error, exp.shift)
end

#process_false(exp) ⇒ Object

False returns a bool-typed sexp.



499
500
501
# File 'lib/type_checker.rb', line 499

def process_false(exp)
  return t(:false, Type.bool)
end

#process_gasgn(exp) ⇒ Object

Global variable assignment gets stored in the global assignment.



506
507
508
509
510
511
512
513
514
515
516
517
518
# File 'lib/type_checker.rb', line 506

def process_gasgn(exp)
  var = exp.shift
  val = process exp.shift

  var_type = @genv.lookup var rescue nil
  if var_type.nil? then
    @genv.add var, val.sexp_type
  else
    val.sexp_type.unify var_type
  end

  return t(:gasgn, var, val, val.sexp_type)
end

#process_gvar(exp) ⇒ Object

Global variables get looked up in the global environment. If they are found, a sexp of that type is returned, otherwise the unknown type is added to the global environment and an unknown-typed sexp is returned.



525
526
527
528
529
530
531
532
533
# File 'lib/type_checker.rb', line 525

def process_gvar(exp)
  name = exp.shift
  type = @genv.lookup name rescue nil
  if type.nil? then
    type = Type.unknown
    @genv.add name, type
  end
  return t(:gvar, name, type)
end

#process_hash(exp) ⇒ Object

Hash (inline hashes) are not supported. Returns an unmentionably-typed sexp. – TODO support inline hashes



541
542
543
544
545
546
547
# File 'lib/type_checker.rb', line 541

def process_hash(exp)
  result = t(:hash, Type.fucked)
  until exp.empty? do
    result << process(exp.shift)
  end
  return result
end

#process_iasgn(exp) ⇒ Object

Instance variable assignment is currently unsupported. Does no unification and returns an untyped sexp



553
554
555
556
557
558
559
560
561
562
563
564
565
# File 'lib/type_checker.rb', line 553

def process_iasgn(exp)
  var = exp.shift
  val = process exp.shift

  var_type = @env.lookup var rescue nil
  if var_type.nil? then
    @env.add var, val.sexp_type
  else
    val.sexp_type.unify var_type
  end

  return t(:iasgn, var, val, val.sexp_type)
end

#process_if(exp) ⇒ Object

If unifies the condition against the bool type, then unifies the return types of the then and else expressions against each other. Returns a sexp typed the same as the then and else expressions.



572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
# File 'lib/type_checker.rb', line 572

def process_if(exp)
  cond_exp = process exp.shift
  then_exp = process exp.shift
  else_exp = process exp.shift rescue nil # might be empty

  cond_exp.sexp_type.unify Type.bool
  begin
    then_exp.sexp_type.unify else_exp.sexp_type unless then_exp.nil? or else_exp.nil?
  rescue TypeError => err
    puts "Error unifying #{then_exp.inspect} with #{else_exp.inspect}"
    raise
  end

  # FIX: at least document this
  type = then_exp.sexp_type unless then_exp.nil?
  type = else_exp.sexp_type unless else_exp.nil?

  return t(:if, cond_exp, then_exp, else_exp, type)
end

#process_iter(exp) ⇒ Object

Iter unifies the dynamic variables against the call args (dynamic variables are used in the iter body) and returns a void-typed sexp.



596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
# File 'lib/type_checker.rb', line 596

def process_iter(exp)
  call_exp = process exp.shift
  dargs_exp = process exp.shift
  body_exp = process exp.shift

  lhs = call_exp[1] # FIX
  if lhs.nil? then
    # We're an fcall getting passed a block.
    return t(:iter, call_exp, dargs_exp, body_exp, call_exp.sexp_type)
  else
    Type.unknown_list.unify lhs.sexp_type # force a list type, lhs must be Enum
    Type.new(lhs.sexp_type.list_type).unify dargs_exp.sexp_type # pull out type

    return t(:iter, call_exp, dargs_exp, body_exp, Type.void)
  end
end

#process_ivar(exp) ⇒ Object

Instance variables are currently unsupported. Returns an unknown-typed sexp. – TODO support instance variables



619
620
621
622
623
624
625
626
627
628
629
# File 'lib/type_checker.rb', line 619

def process_ivar(exp)
  name = exp.shift

  var_type = @env.lookup name rescue nil
  if var_type.nil? then
    var_type = Type.unknown
    @env.add name, var_type
  end

  return t(:ivar, name, var_type)
end

#process_lasgn(exp) ⇒ Object

Local variable assignment unifies the variable type from the environment with the assignment expression, and returns a sexp of that type. If there is no local variable in the environment, one is added with the type of the assignment expression and a sexp of that type is returned.

If an lasgn has no value (inside masgn) the returned sexp has an unknown Type and a nil node is added as the value.



640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
# File 'lib/type_checker.rb', line 640

def process_lasgn(exp)
  name = exp.shift
  arg_exp = nil
  arg_type = Type.unknown
  var_type = @env.lookup name rescue nil

  unless exp.empty? then
    sub_exp = exp.shift
    sub_exp_type = sub_exp.first
    arg_exp = process sub_exp

    # if we've got an array in there, unify everything in it.
    if sub_exp_type == :array then
      arg_type = arg_exp.sexp_types
      arg_type = arg_type.inject(Type.unknown) do |t1, t2|
        t1.unify t2
      end
      arg_type = arg_type.dup # singleton type
      arg_type.list = true
    else
      arg_type = arg_exp.sexp_type
    end
  end

  if var_type.nil? then
    @env.add name, arg_type
    var_type = arg_type
  else
    var_type.unify arg_type
  end

  return t(:lasgn, name, arg_exp, var_type)
end

#process_lit(exp) ⇒ Object

Literal values return a sexp typed to match the literal expression.



677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
# File 'lib/type_checker.rb', line 677

def process_lit(exp)
  value = exp.shift
  type = nil

  case value
  when Fixnum then
    type = Type.long
  when Float then
    type = Type.float
  when Symbol then
    type = Type.symbol
  when Regexp then
    type = Type.regexp
  when Range then
    type = Type.range
  when Const then
    type = Type.const
  else
    raise "Bug! no: Unknown literal #{value}:#{value.class}"
  end

  return t(:lit, value, type)
end

#process_lvar(exp) ⇒ Object

Local variables get looked up in the local environment and a sexp of that type is returned.



705
706
707
708
709
# File 'lib/type_checker.rb', line 705

def process_lvar(exp)
  name = exp.shift
  t = @env.lookup name
  return t(:lvar, name, t)
end

#process_masgn(exp) ⇒ Object

Multiple assignment



714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
# File 'lib/type_checker.rb', line 714

def process_masgn(exp)
  mlhs = process exp.shift
  mrhs = process exp.shift

  mlhs_values = mlhs[1..-1]
  mrhs_values = mrhs[1..-1]

  mlhs_values.zip(mrhs_values) do |lasgn, value|
    if value.nil? then
      lasgn.sexp_type.unify Type.value # nil
    else
      lasgn.sexp_type.unify value.sexp_type
    end
  end

  if mlhs_values.length < mrhs_values.length then
    last_lasgn = mlhs_values.last
    last_lasgn.sexp_type.list = true
  end

  return t(:masgn, mlhs, mrhs)
end

#process_nil(exp) ⇒ Object

Nil returns a value-typed sexp.



740
741
742
743
744
# File 'lib/type_checker.rb', line 740

def process_nil(exp)
  # don't do a fucking thing until... we have something to do
  # HACK: wtf to do here? (what type is nil?!?!)
  return t(:nil, Type.value)
end

#process_not(exp) ⇒ Object

Not unifies the type of its expression against bool, then returns a bool-typed sexp.



750
751
752
753
754
# File 'lib/type_checker.rb', line 750

def process_not(exp)
  thing = process exp.shift
  thing.sexp_type.unify Type.bool
  return t(:not, thing, Type.bool)
end

#process_op_asgn_or(exp) ⇒ Object

||= operator is currently unsupported. Returns an untyped sexp.



759
760
761
762
763
764
# File 'lib/type_checker.rb', line 759

def process_op_asgn_or(exp)
  lhs = exp.shift
  rhs = process(exp.shift)

  return t(:op_asgn_or, lhs, rhs)
end

#process_or(exp) ⇒ Object

Or unifies the left and right hand sides with bool, then returns a bool-typed sexp.



770
771
772
773
774
775
776
777
778
779
780
781
# File 'lib/type_checker.rb', line 770

def process_or(exp)
  rhs = process exp.shift
  lhs = process exp.shift

  rhs_type = rhs.sexp_type
  lhs_type = lhs.sexp_type

  rhs_type.unify lhs_type
  rhs_type.unify Type.bool

  return t(:or, rhs, lhs, Type.bool)
end

#process_resbody(exp) ⇒ Object

Rescue body returns an unknown-typed sexp.



786
787
788
789
790
791
792
793
794
795
796
797
# File 'lib/type_checker.rb', line 786

def process_resbody(exp)
  o1 = process exp.shift
  o2 = exp.empty? ? nil : process(exp.shift)
  o3 = exp.empty? ? nil : process(exp.shift)

  result = t(:resbody, Type.unknown) # void?
  result << o1
  result << o2 unless o2.nil?
  result << o3 unless o3.nil?
  
  return result
end

#process_rescue(exp) ⇒ Object

Rescue unifies the begin, rescue and ensure types, and returns an untyped sexp.



803
804
805
806
807
808
809
810
811
812
813
814
815
816
# File 'lib/type_checker.rb', line 803

def process_rescue(exp)
  try_block = process exp.shift
  rescue_block = process exp.shift
  els = exp.empty? ? nil : process(exp.shift)

  try_type = try_block.sexp_type
  rescue_type = rescue_block.sexp_type
#    ensure_type = els.sexp_type # HACK/FIX: not sure if I should unify

  try_type.unify rescue_type
#    try_type.unify ensure_type 

  return t(:rescue, try_block, rescue_block, els, try_type)
end

#process_return(exp) ⇒ Object

Return returns a void typed sexp.



821
822
823
824
825
# File 'lib/type_checker.rb', line 821

def process_return(exp)
  result = t(:return, Type.void) # TODO why void - cuz this is a keyword
  result << process(exp.shift) unless exp.empty?
  return result
end

#process_scope(exp) ⇒ Object

Scope returns a void-typed sexp.



830
831
832
833
834
835
836
# File 'lib/type_checker.rb', line 830

def process_scope(exp)
  return t(:scope, Type.void) if exp.empty?

  body = process exp.shift

  return t(:scope, body, Type.void)
end

#process_self(exp) ⇒ Object

Self is currently unsupported. Returns an unknown-typed sexp. – TODO support self



843
844
845
# File 'lib/type_checker.rb', line 843

def process_self(exp)
  return t(:self, Type.unknown)
end

#process_splat(exp) ⇒ Object

Splat is currently unsupported. Returns an unknown-typed sexp. – TODO support splat, maybe like :array?



852
853
854
855
# File 'lib/type_checker.rb', line 852

def process_splat(exp)
  value = process exp.shift
  return t(:splat, value, Type.unknown) # TODO: probably value_list?
end

#process_str(exp) ⇒ Object

String literal returns a string-typed sexp.



860
861
862
# File 'lib/type_checker.rb', line 860

def process_str(exp)
  return t(:str, exp.shift, Type.str)
end

#process_super(exp) ⇒ Object

Super is currently unsupported. Returns an unknown-typed sexp. – TODO support super



869
870
871
872
873
# File 'lib/type_checker.rb', line 869

def process_super(exp)
  args = process exp.shift
  # TODO try to look up the method in our superclass?
  return t(:super, args, Type.unknown)
end

#process_to_ary(exp) ⇒ Object

Object#to_ary



878
879
880
881
882
883
884
885
886
887
888
889
# File 'lib/type_checker.rb', line 878

def process_to_ary(exp)
  to_ary = t(:to_ary)

  until exp.empty?
    to_ary << process(exp.shift)
  end

  to_ary.sexp_type = to_ary[1].sexp_type.dup
  to_ary.sexp_type.list = true

  return to_ary
end

#process_true(exp) ⇒ Object

True returns a bool-typed sexp.



894
895
896
# File 'lib/type_checker.rb', line 894

def process_true(exp)
  return t(:true, Type.bool)
end

#process_while(exp) ⇒ Object

While unifies the condition with bool, then returns an untyped sexp.



901
902
903
904
905
906
907
# File 'lib/type_checker.rb', line 901

def process_while(exp)
  cond = process exp.shift
  body = process exp.shift
  is_precondition = exp.shift
  Type.bool.unify cond.sexp_type
  return t(:while, cond, body, is_precondition)
end

#process_yield(exp) ⇒ Object

Yield is currently unsupported. Returns a unmentionably-typed sexp.



912
913
914
915
916
917
918
# File 'lib/type_checker.rb', line 912

def process_yield(exp)
  result = t(:yield, Type.fucked)
  until exp.empty? do
    result << process(exp.shift)
  end
  return result
end