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:



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

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



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

def env
  @env
end

#functionsObject (readonly)

Function table



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

def functions
  @functions
end

#genvObject (readonly)

The global environment contains global variables and constants.



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

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.



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

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

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

#process(exp, _src = nil, _timeout = nil) ⇒ Object



122
123
124
# File 'lib/type_checker.rb', line 122

def process exp, _src = nil, _timeout = nil
  super(exp)
end

#process_and(exp) ⇒ Object

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



129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/type_checker.rb', line 129

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

  rhs_type = rhs.c_type
  lhs_type = lhs.c_type

  rhs_type.unify lhs_type
  rhs_type.unify CType.bool

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

#process_arglist(exp) ⇒ Object

Arg list stuff



164
165
166
167
168
# File 'lib/type_checker.rb', line 164

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

#process_arglist_plain(exp) ⇒ Object



622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
# File 'lib/type_checker.rb', line 622

def process_arglist_plain(exp)
  vars = t(:args)
  until exp.empty? do
    var = exp.shift
    case var
    when Symbol then
      vars << process(s(:lasgn, var))
    when Sexp then
      vars << process(var)
    else
      raise "Unknown arglist type: #{var.inspect}"
    end
  end
  vars
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.



146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/type_checker.rb', line 146

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

  until exp.empty? do
    arg = exp.shift
    type = CType.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.



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

def process_array(exp)
  types = []
  vars = t(:array)
  until exp.empty? do
    var = process exp.shift
    vars << var
    types << var.c_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]]]



197
198
199
200
201
202
203
204
# File 'lib/type_checker.rb', line 197

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.



209
210
211
212
213
# File 'lib/type_checker.rb', line 209

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.



219
220
221
222
223
224
225
# File 'lib/type_checker.rb', line 219

def process_block(exp)
  nodes = t(:block, CType.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



233
234
235
# File 'lib/type_checker.rb', line 233

def process_block_arg(exp)
  t(:block_arg, exp.shift, CType.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]]]]]



244
245
246
247
248
# File 'lib/type_checker.rb', line 244

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.



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/type_checker.rb', line 272

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.c_types
                elsif args.first == :splat then
                  [args.c_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.c_type.unknown? and rhs.c_type.unknown?
    equal_type = lhs.c_type.unknown? ? rhs.c_type : lhs.c_type
    name = "case_equal_#{equal_type.list_type}".intern
  end

  return_type = CType.unknown
  lhs_type = lhs.nil? ? CType.unknown : lhs.c_type # TODO: maybe void instead of unknown

  function_type = CType.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.



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/type_checker.rb', line 316

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

  @genv.add name, CType.zclass

  result = t(:class, CType.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 Integer then
                     CType.long
                   when String then
                     CType.str
                   else
                     CType.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



352
353
354
355
# File 'lib/type_checker.rb', line 352

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

#process_colon3(exp) ⇒ Object

Colon 3 returns a zclass-typed sexp



360
361
362
363
# File 'lib/type_checker.rb', line 360

def process_colon3(exp) # (::OUTER_CONST)
  name = exp.shift
  return t(:colon3, name, CType.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.



373
374
375
376
377
378
379
380
381
382
383
# File 'lib/type_checker.rb', line 373

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



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

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, CType.unknown)
end

#process_cvasgn(exp) ⇒ Object

Class variable assignment – TODO support class variables



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

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

#process_dasgn_curr(exp) ⇒ Object

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



411
412
413
414
415
416
417
# File 'lib/type_checker.rb', line 411

def process_dasgn_curr(exp)
  name = exp.shift
  type = CType.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.



422
423
424
425
# File 'lib/type_checker.rb', line 422

def process_defined(exp)
  thing = process exp.shift
  return t(:defined, thing, CType.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.



443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
# File 'lib/type_checker.rb', line 443

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 = CType.function CType.unknown, args.c_types, CType.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].c_type
    return_count += 1
  end
  return_type.unify CType.void if return_count == 0

  # TODO: bad API, clean
  raise "wrong" if
    args.c_types.size != function_type.list_type.formal_types.size
  args.c_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.



490
491
492
493
494
495
496
497
# File 'lib/type_checker.rb', line 490

def process_dstr(exp)
  out = t(:dstr, exp.shift, CType.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.



503
504
505
506
507
# File 'lib/type_checker.rb', line 503

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.



512
513
514
515
516
517
# File 'lib/type_checker.rb', line 512

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

  t(:ensure, res, ens)
end

#process_error(exp) ⇒ Object

DOC



522
523
524
# File 'lib/type_checker.rb', line 522

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

#process_false(exp) ⇒ Object

False returns a bool-typed sexp.



529
530
531
# File 'lib/type_checker.rb', line 529

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

#process_gasgn(exp) ⇒ Object

Global variable assignment gets stored in the global assignment.



536
537
538
539
540
541
542
543
544
545
546
547
548
# File 'lib/type_checker.rb', line 536

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.c_type
  else
    val.c_type.unify var_type
  end

  return t(:gasgn, var, val, val.c_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.



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

def process_gvar(exp)
  name = exp.shift
  type = @genv.lookup name rescue nil
  if type.nil? then
    type = CType.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



571
572
573
574
575
576
577
# File 'lib/type_checker.rb', line 571

def process_hash(exp)
  result = t(:hash, CType.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



583
584
585
586
587
588
589
590
591
592
593
594
595
# File 'lib/type_checker.rb', line 583

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.c_type
  else
    val.c_type.unify var_type
  end

  return t(:iasgn, var, val, val.c_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.



602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
# File 'lib/type_checker.rb', line 602

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.c_type.unify CType.bool
  begin
    then_exp.c_type.unify else_exp.c_type unless then_exp.nil? or else_exp.nil?
  rescue TypeError
    puts "Error unifying #{then_exp.inspect} with #{else_exp.inspect}"
    raise
  end

  # FIX: at least document this
  type = then_exp.c_type unless then_exp.nil?
  type = else_exp.c_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.



642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
# File 'lib/type_checker.rb', line 642

def process_iter(exp)
  call_exp = process exp.shift

  dargs_exp = exp.shift
  dargs_exp[0] = :arglist_plain
  dargs_exp = process dargs_exp

  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.c_type)
  else
    CType.unknown_list.unify lhs.c_type # force a list type, lhs must be Enum

    dargs_exp.sexp_body.each do |subexp|
      CType.new(lhs.c_type.list_type).unify subexp.c_type
    end

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

#process_ivar(exp) ⇒ Object

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



672
673
674
675
676
677
678
679
680
681
682
# File 'lib/type_checker.rb', line 672

def process_ivar(exp)
  name = exp.shift

  var_type = @env.lookup name rescue nil
  if var_type.nil? then
    var_type = CType.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.



693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
# File 'lib/type_checker.rb', line 693

def process_lasgn(exp)
  name = exp.shift
  arg_exp = nil
  arg_type = CType.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.c_types
      arg_type = arg_type.inject(CType.unknown) do |t1, t2|
        t1.unify t2
      end
      arg_type = arg_type.dup # singleton type
      arg_type.list = true
    else
      arg_type = arg_exp.c_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.



730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
# File 'lib/type_checker.rb', line 730

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

  case value
  when Integer then
    type = CType.long
  when Float then
    type = CType.float
  when Symbol then
    type = CType.symbol
  when Regexp then
    type = CType.regexp
  when Range then
    type = CType.range
  when Const then
    type = CType.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.



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

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

#process_masgn(exp) ⇒ Object

Multiple assignment



767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
# File 'lib/type_checker.rb', line 767

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.c_type.unify CType.value # nil
    else
      lasgn.c_type.unify value.c_type
    end
  end

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

  return t(:masgn, mlhs, mrhs)
end

#process_nil(exp) ⇒ Object

Nil returns a value-typed sexp.



793
794
795
796
797
# File 'lib/type_checker.rb', line 793

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, CType.value)
end

#process_not(exp) ⇒ Object

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



803
804
805
806
807
# File 'lib/type_checker.rb', line 803

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

#process_op_asgn_or(exp) ⇒ Object

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



812
813
814
815
816
817
# File 'lib/type_checker.rb', line 812

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.



823
824
825
826
827
828
829
830
831
832
833
834
# File 'lib/type_checker.rb', line 823

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

  rhs_type = rhs.c_type
  lhs_type = lhs.c_type

  rhs_type.unify lhs_type
  rhs_type.unify CType.bool

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

#process_resbody(exp) ⇒ Object

Rescue body returns an unknown-typed sexp.



839
840
841
842
843
844
845
846
847
848
849
850
# File 'lib/type_checker.rb', line 839

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, CType.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.



856
857
858
859
860
861
862
863
864
865
866
867
868
869
# File 'lib/type_checker.rb', line 856

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.c_type
  rescue_type = rescue_block.c_type
#    ensure_type = els.c_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.



874
875
876
877
878
# File 'lib/type_checker.rb', line 874

def process_return(exp)
  result = t(:return, CType.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.



883
884
885
886
887
888
889
# File 'lib/type_checker.rb', line 883

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

  body = process exp.shift

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

#process_self(exp) ⇒ Object

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



896
897
898
# File 'lib/type_checker.rb', line 896

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

#process_splat(exp) ⇒ Object

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



905
906
907
908
# File 'lib/type_checker.rb', line 905

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

#process_str(exp) ⇒ Object

String literal returns a string-typed sexp.



913
914
915
# File 'lib/type_checker.rb', line 913

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

#process_super(exp) ⇒ Object

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



922
923
924
925
926
# File 'lib/type_checker.rb', line 922

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

#process_to_ary(exp) ⇒ Object

Object#to_ary



931
932
933
934
935
936
937
938
939
940
941
942
# File 'lib/type_checker.rb', line 931

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

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

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

  return to_ary
end

#process_true(exp) ⇒ Object

True returns a bool-typed sexp.



947
948
949
# File 'lib/type_checker.rb', line 947

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

#process_while(exp) ⇒ Object

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



954
955
956
957
958
959
960
# File 'lib/type_checker.rb', line 954

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

#process_yield(exp) ⇒ Object

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



965
966
967
968
969
970
971
# File 'lib/type_checker.rb', line 965

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

#rewrite_attrasgn(exp) ⇒ Object



184
185
186
187
188
# File 'lib/type_checker.rb', line 184

def rewrite_attrasgn exp
  t, lhs, name, *rhs = exp

  s(t, lhs, name, s(:arglist, *rhs))
end

#rewrite_call(exp) ⇒ Object



250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/type_checker.rb', line 250

def rewrite_call(exp)
  t, recv, meth, *args = exp

  args = args.compact
  args = [s(:arglist)] if args == []

  unless args.first and args.first.first == :arglist then
    args = [s(:arglist, *args)]
  end

  s(t, recv, meth, *args)
end

#rewrite_defn(exp) ⇒ Object



427
428
429
430
431
432
433
434
# File 'lib/type_checker.rb', line 427

def rewrite_defn(exp)
  t, name, args, *body = exp

  body = [s(:scope, s(:block, *body))] unless
    body and body.first.first == :scope

  s(t, name, args, *body)
end