RubyToC 1.0.0.4 → 1.0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,167 @@
1
+
2
+ $TESTING = false unless defined? $TESTING
3
+
4
+ begin require 'rubygems' rescue LoadError end
5
+ require 'ruby_to_ansi_c'
6
+
7
+ class RubyToRubyC < RubyToAnsiC
8
+
9
+ ##
10
+ # Lazy initializer for the composite RubytoC translator chain.
11
+
12
+ def self.translator
13
+ # TODO: FIX, but write a test first
14
+ unless defined? @translator then
15
+ @translator = CompositeSexpProcessor.new
16
+ @translator << Rewriter.new
17
+ @translator << TypeChecker.new
18
+ @translator << R2CRewriter.new
19
+ @translator << RubyToRubyC.new
20
+ @translator.on_error_in(:defn) do |processor, exp, err|
21
+ result = processor.expected.new
22
+ case result
23
+ when Array then
24
+ result << :error
25
+ end
26
+ msg = "// ERROR: #{err.class}: #{err}"
27
+ msg += " in #{exp.inspect}" unless exp.nil? or $TESTING
28
+ msg += " from #{caller.join(', ')}" unless $TESTING
29
+ result << msg
30
+ result
31
+ end
32
+ end
33
+ @translator
34
+ end
35
+
36
+ def self.c_type(x)
37
+ "VALUE"
38
+ end
39
+
40
+ def initialize # :nodoc:
41
+ super
42
+ end
43
+
44
+ def process_call(exp)
45
+ receiver = process(exp.shift) || "self"
46
+ name = exp.shift.to_s
47
+ args = [process(exp.shift)].flatten.compact
48
+
49
+ name = '===' if name =~ /^case_equal_/ # undo the evils of TypeChecker
50
+
51
+ if args.empty?
52
+ args = "0"
53
+ else
54
+ args = "#{args.size}, #{args.join(", ")}"
55
+ end
56
+
57
+ "rb_funcall(#{receiver}, rb_intern(#{name.inspect}), #{args})"
58
+ end
59
+
60
+ def process_false(exp)
61
+ "Qfalse"
62
+ end
63
+
64
+ def process_gvar(exp)
65
+ var = exp.shift
66
+ "rb_gv_get(#{var.to_s.inspect})"
67
+ end
68
+
69
+ ##
70
+ # Iterators for loops. After rewriter nearly all iter nodes
71
+ # should be able to be interpreted as a for loop. If not, then you
72
+ # are doing something not supported by C in the first place.
73
+
74
+ def process_iter(exp)
75
+ out = []
76
+ # Only support enums in C-land
77
+ raise UnsupportedNodeError if exp[0][1].nil? # HACK ugly
78
+ @env.scope do
79
+ enum = exp[0][1][1] # HACK ugly t(:iter, t(:call, lhs <-- get lhs
80
+ call = process exp.shift
81
+ var = process(exp.shift).intern # semi-HACK-y
82
+ body = process exp.shift
83
+ index = "index_#{var}"
84
+
85
+ body += ";" unless body =~ /[;}]\Z/
86
+ body.gsub!(/\n\n+/, "\n")
87
+
88
+ out << "unsigned long #{index};"
89
+ out << "unsigned long arrays_max = FIX2LONG(rb_funcall(arrays, rb_intern(\"size\"), 0));"
90
+ out << "for (#{index} = 0; #{index} < arrays_max; ++#{index}) {"
91
+ out << "VALUE x = rb_funcall(arrays, rb_intern(\"at\"), 1, LONG2FIX(index_x));"
92
+ out << body
93
+ out << "}"
94
+ end
95
+
96
+ return out.join("\n")
97
+ end
98
+
99
+ ##
100
+ # Assignment to a local variable.
101
+ #
102
+ # TODO: figure out array issues and clean up.
103
+
104
+ def process_lasgn(exp)
105
+ out = ""
106
+
107
+ var = exp.shift
108
+ value = exp.shift
109
+ # grab the size of the args, if any, before process converts to a string
110
+ arg_count = 0
111
+ arg_count = value.length - 1 if value.first == :array
112
+ args = value
113
+
114
+ exp_type = exp.sexp_type
115
+ @env.add var.to_sym, exp_type
116
+ var_type = self.class.c_type exp_type
117
+
118
+ if exp_type.list? then
119
+ assert_type args, :array
120
+
121
+ raise "array must be of one type" unless args.sexp_type == Type.homo
122
+
123
+ args.shift # :arglist
124
+ out << "#{var} = rb_ary_new2(#{args.length});\n"
125
+ args.each_with_index do |o,i|
126
+ out << "rb_ary_store(#{var}, #{i}, #{process o});\n"
127
+ end
128
+ else
129
+ out << "#{var} = #{process args}"
130
+ end
131
+
132
+ out.sub!(/;\n\Z/, '')
133
+
134
+ return out
135
+ end
136
+
137
+ def process_lit(exp)
138
+ value = exp.shift
139
+
140
+ case value
141
+ when Fixnum then
142
+ "LONG2NUM(#{value})"
143
+ when Float then
144
+ "DBL2NUM(#{value})"
145
+ when Symbol then
146
+ "rb_intern(#{value.to_s.inspect})"
147
+ else
148
+ raise "Bug! no: Unknown literal #{value}:#{value.class}"
149
+ end
150
+ end
151
+
152
+ ##
153
+ # Nil, currently ruby nil, not C NULL (0).
154
+
155
+ def process_nil(exp)
156
+ return "Qnil"
157
+ end
158
+
159
+ def process_str(exp)
160
+ value = exp.shift
161
+ "rb_str_new2(#{value.inspect})"
162
+ end
163
+
164
+ def process_true(exp)
165
+ "Qtrue"
166
+ end
167
+ end
@@ -11,9 +11,9 @@ class Environment
11
11
  @env.length
12
12
  end
13
13
 
14
- def add(id, val)
15
- raise "Adding illegal identifier #{id.inspect}" unless String === id or Symbol === id
16
- current[id] = val
14
+ def add(id, val, depth = 0)
15
+ raise "Adding illegal identifier #{id.inspect}" unless Symbol === id
16
+ @env[depth][id.to_s.sub(/^\*/, '').intern] = val
17
17
  end
18
18
 
19
19
  def extend
@@ -26,6 +26,8 @@ class Environment
26
26
 
27
27
  def lookup(id)
28
28
 
29
+ warn "#{id} is a string from #{caller[0]}" if String === id
30
+
29
31
  # HACK: if id is :self, cheat for now until we have full defn remapping
30
32
  if id == :self then
31
33
  return Type.fucked
@@ -42,10 +44,17 @@ class Environment
42
44
  @env.first
43
45
  end
44
46
 
47
+ def all
48
+ @env.reverse.inject { |env, scope| env.merge scope }
49
+ end
50
+
45
51
  def scope
46
52
  self.extend
47
- yield
48
- self.unextend
53
+ begin
54
+ yield
55
+ ensure
56
+ self.unextend
57
+ end
49
58
  end
50
59
 
51
60
  end
@@ -295,23 +304,15 @@ end
295
304
  # Unique creates unique variable names.
296
305
 
297
306
  class Unique
298
-
299
- ##
300
- # Variable names will be prefixed by +prefix+
301
-
302
- def initialize(prefix)
303
- @prefix = prefix
304
- @curr = 'a'
307
+ def self.reset # mostly for testing
308
+ @@curr = 0
305
309
  end
306
310
 
307
- ##
308
- # Generate a new unique variable name
309
-
310
- def next
311
- var = "#{@prefix}_#{@curr}"
312
- @curr.succ!
313
- return var
311
+ def self.next
312
+ @@curr += 1
313
+ "temp_#{@@curr}".intern
314
314
  end
315
315
 
316
+ reset
316
317
  end
317
318
 
@@ -77,14 +77,6 @@ class TypeChecker < SexpProcessor
77
77
  # Utility method that translates a class and optional method name to
78
78
  # a type checked sexp. Mostly used for testing.
79
79
 
80
- def translate(klass, method = nil)
81
- @@parser = ParseTree.new(false) unless defined? @@parser
82
- @@rewriter = Rewriter.new unless defined? @@rewriter
83
- sexp = @@parser.parse_tree_for_method klass, method
84
- sexp = @@rewriter.process sexp
85
- self.process sexp
86
- end
87
-
88
80
  ##
89
81
  # Utility method that translates a class and optional method name to
90
82
  # a type checked sexp. Mostly used for testing.
@@ -122,6 +114,8 @@ class TypeChecker < SexpProcessor
122
114
  self.strict = true
123
115
  self.expected = TypedSexp
124
116
 
117
+ 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, :masgn, :match, :match2, :match3, :memo, :method, :module, :newline, :next, :nth_ref, :op_asgn1, :op_asgn2, :op_asgn_and, :opt_n, :postexe, :redo, :retry, :sclass, :svalue, :to_ary, :undef, :until, :valias, :vcall, :when, :xstr, :zarray, :zsuper]
118
+
125
119
  bootstrap
126
120
  end
127
121
 
@@ -649,10 +643,15 @@ class TypeChecker < SexpProcessor
649
643
  body_exp = process exp.shift
650
644
 
651
645
  lhs = call_exp[1] # FIX
652
- Type.unknown_list.unify lhs.sexp_type # force a list type, lhs must be Enum
653
- Type.new(lhs.sexp_type.list_type).unify dargs_exp.sexp_type # pull out type
646
+ if lhs.nil? then
647
+ # We're an fcall getting passed a block.
648
+ return t(:iter, call_exp, dargs_exp, body_exp, call_exp.sexp_type)
649
+ else
650
+ Type.unknown_list.unify lhs.sexp_type # force a list type, lhs must be Enum
651
+ Type.new(lhs.sexp_type.list_type).unify dargs_exp.sexp_type # pull out type
654
652
 
655
- return t(:iter, call_exp, dargs_exp, body_exp, Type.void)
653
+ return t(:iter, call_exp, dargs_exp, body_exp, Type.void)
654
+ end
656
655
  end
657
656
 
658
657
  ##
@@ -917,6 +916,13 @@ class TypeChecker < SexpProcessor
917
916
  return result
918
917
  end
919
918
 
919
+ def translate(klass, method = nil)
920
+ @@parser = ParseTree.new(false) unless defined? @@parser
921
+ @@rewriter = Rewriter.new unless defined? @@rewriter
922
+ sexp = @@parser.parse_tree_for_method klass, method
923
+ sexp = @@rewriter.process sexp
924
+ self.process sexp
925
+ end
920
926
  end
921
927
 
922
928
 
@@ -1,18 +1,47 @@
1
1
 
2
2
  begin require 'rubygems' rescue LoadError end
3
- require 'sexp_processor'
3
+ require 'sexp'
4
4
  require 'support'
5
5
 
6
6
  $TESTING = false unless defined? $TESTING
7
7
 
8
8
  class TypedSexp < Sexp
9
9
 
10
+ def ==(obj)
11
+ case obj
12
+ when TypedSexp
13
+ super && sexp_type == obj.sexp_type
14
+ else
15
+ false
16
+ end
17
+ end
18
+
19
+ def _set_sexp_type(o)
20
+ @sexp_type = o
21
+ end
22
+
10
23
  def initialize(*args)
11
24
  # TODO: should probably be Type.unknown
12
25
  @sexp_type = Type === args.last ? args.pop : nil
13
26
  super(*args)
14
27
  end
15
28
 
29
+ def inspect
30
+ sexp_str = self.map {|x|x.inspect}.join(', ')
31
+ sexp_type_str = (sexp_str.empty? ? "" : ", ") + "#{array_type? ? sexp_types.inspect : sexp_type}" unless sexp_type.nil?
32
+ return "t(#{sexp_str}#{sexp_type_str})"
33
+ end
34
+
35
+ def pretty_print(q)
36
+ q.group(1, 't(', ')') do
37
+ q.seplist(self) {|v| q.pp v }
38
+ unless @sexp_type.nil? then
39
+ q.text ", " unless self.empty?
40
+ q.pp @sexp_type
41
+ end
42
+ end
43
+ end
44
+
16
45
  def sexp_type
17
46
  unless array_type? then
18
47
  @sexp_type
@@ -27,10 +56,6 @@ class TypedSexp < Sexp
27
56
  end
28
57
  end
29
58
 
30
- def _set_sexp_type(o)
31
- @sexp_type = o
32
- end
33
-
34
59
  def sexp_type=(o)
35
60
  raise "You shouldn't call this on an #{first}" if array_type?
36
61
  raise "You shouldn't call this a second time, ever" unless
@@ -43,15 +68,6 @@ class TypedSexp < Sexp
43
68
  self.grep(Sexp).map { |x| x.sexp_type }
44
69
  end
45
70
 
46
- def ==(obj)
47
- case obj
48
- when TypedSexp
49
- super && sexp_type == obj.sexp_type
50
- else
51
- false
52
- end
53
- end
54
-
55
71
  def to_a
56
72
  result = super
57
73
  if defined?(@sexp_type) and not @sexp_type.nil? then
@@ -60,22 +76,6 @@ class TypedSexp < Sexp
60
76
  result
61
77
  end
62
78
 
63
- def inspect
64
- sexp_str = self.map {|x|x.inspect}.join(', ')
65
- sexp_type_str = (sexp_str.empty? ? "" : ", ") + "#{array_type? ? sexp_types.inspect : sexp_type}" unless sexp_type.nil?
66
- return "t(#{sexp_str}#{sexp_type_str})"
67
- end
68
-
69
- def pretty_print(q)
70
- q.group(1, 't(', ')') do
71
- q.seplist(self) {|v| q.pp v }
72
- unless @sexp_type.nil? then
73
- q.text ", " unless self.empty?
74
- q.pp @sexp_type
75
- end
76
- end
77
- end
78
-
79
79
  def to_s
80
80
  inspect
81
81
  end
@@ -0,0 +1,1261 @@
1
+ require 'test/unit/testcase'
2
+
3
+ require 'sexp_processor'
4
+ require 'typed_sexp_processor'
5
+ require 'support'
6
+
7
+ # TODO: str -> char * in ansi c
8
+ # TODO: add tests that mix types up to fuck up RubyC type checker
9
+
10
+ class R2CTestCase < Test::Unit::TestCase
11
+
12
+ attr_accessor :processor # to be defined by subclass
13
+
14
+ def setup
15
+ super
16
+ @processor = nil
17
+ end
18
+
19
+ @@testcase_order = [
20
+ "ParseTree",
21
+ "Rewriter",
22
+ "TypeChecker",
23
+ "R2CRewriter",
24
+ "RubyToAnsiC",
25
+ "RubyToRubyC",
26
+ ]
27
+
28
+ @@testcases = {
29
+
30
+ "accessor" => {
31
+ "ParseTree" => [:defn, :accessor, [:ivar, :@accessor]],
32
+ "Rewriter" => s(:defn, :accessor, s(:args),
33
+ s(:scope,
34
+ s(:block, s(:return, s(:ivar, :@accessor))))),
35
+ "TypeChecker" => :skip,
36
+ "R2CRewriter" => :skip,
37
+ "RubyToAnsiC" => :skip,
38
+ "RubyToRubyC" => :skip,
39
+ },
40
+
41
+ "accessor_equals" => {
42
+ "ParseTree" => [:defn, :accessor=, [:attrset, :@accessor]],
43
+ "Rewriter" => s(:defn,
44
+ :accessor=,
45
+ s(:args, :arg),
46
+ s(:scope,
47
+ s(:block,
48
+ s(:return,
49
+ s(:iasgn, :@accessor, s(:lvar, :arg)))))),
50
+ "TypeChecker" => :skip,
51
+ "R2CRewriter" => :skip,
52
+ "RubyToRubyC" => :skip,
53
+ "RubyToAnsiC" => :skip,
54
+ },
55
+
56
+ "defn_bbegin" => {
57
+ "ParseTree" => [:defn, :bbegin,
58
+ [:scope,
59
+ [:block,
60
+ [:args],
61
+ [:begin,
62
+ [:ensure,
63
+ [:rescue,
64
+ [:call, [:lit, 1], :+, [:array, [:lit, 1]]],
65
+ [:resbody,
66
+ [:array, [:const, :SyntaxError]],
67
+ [:block, [:lasgn, :e1, [:gvar, :$!]], [:lit, 2]],
68
+ [:resbody,
69
+ [:array, [:const, :Exception]],
70
+ [:block, [:lasgn, :e2, [:gvar, :$!]], [:lit, 3]]]],
71
+ [:lit, 4]],
72
+ [:lit, 5]]]]]],
73
+ "Rewriter" => s(:defn, :bbegin,
74
+ s(:args),
75
+ s(:scope,
76
+ s(:block,
77
+ s(:begin,
78
+ s(:ensure,
79
+ s(:rescue,
80
+ s(:call, s(:lit, 1), :+, s(:arglist, s(:lit, 1))),
81
+ s(:resbody,
82
+ s(:array, s(:const, :SyntaxError)),
83
+ s(:block, s(:lasgn, :e1, s(:gvar, :$!)),
84
+ s(:lit, 2)),
85
+ s(:resbody,
86
+ s(:array, s(:const, :Exception)),
87
+ s(:block, s(:lasgn, :e2, s(:gvar, :$!)),
88
+ s(:lit, 3)))),
89
+ s(:lit, 4)),
90
+ s(:lit, 5)))))),
91
+ "TypeChecker" => t(:defn, :bbegin,
92
+ t(:args),
93
+ t(:scope,
94
+ t(:block,
95
+ t(:begin,
96
+ t(:ensure,
97
+ t(:rescue,
98
+ t(:call,
99
+ t(:lit, 1, Type.long),
100
+ :+,
101
+ t(:arglist, t(:lit, 1, Type.long)), Type.long),
102
+ t(:resbody,
103
+ t(:array, t(:const, :SyntaxError, Type.fucked)),
104
+ t(:block,
105
+ t(:lasgn, :e1, t(:gvar, :$!, Type.unknown),
106
+ Type.unknown),
107
+ t(:lit, 2, Type.long), Type.unknown),
108
+ t(:resbody,
109
+ t(:array, t(:const, :Exception, Type.fucked)),
110
+ t(:block,
111
+ t(:lasgn, :e2, t(:gvar, :$!, Type.unknown),
112
+ Type.unknown),
113
+ t(:lit, 3, Type.long), Type.unknown),
114
+ Type.unknown), Type.long),
115
+ t(:lit, 4, Type.long), Type.long),
116
+ t(:lit, 5, Type.long))), Type.unknown),
117
+ Type.void),
118
+ Type.function(Type.unknown, [], Type.void)),
119
+ "R2CRewriter" => :same,
120
+ "RubyToRubyC" => :unsupported,
121
+ "RubyToAnsiC" => :unsupported,
122
+ },
123
+
124
+ "defn_bmethod_added" => {
125
+ "ParseTree" => [:defn, :bmethod_added,
126
+ [:bmethod,
127
+ [:dasgn_curr, :x],
128
+ [:call, [:dvar, :x], :+, [:array, [:lit, 1]]]]],
129
+ "Rewriter" => s(:defn,
130
+ :bmethod_added,
131
+ s(:args, :x),
132
+ s(:scope,
133
+ s(:block,
134
+ s(:call, s(:lvar, :x), :+, s(:arglist, s(:lit, 1)))))),
135
+ "TypeChecker" => :skip,
136
+ "R2CRewriter" => :skip,
137
+ "RubyToRubyC" => :skip,
138
+ "RubyToAnsiC" => :skip,
139
+ },
140
+
141
+ "bools" => {
142
+ "ParseTree" => [:defn, :bools,
143
+ [:scope,
144
+ [:block,
145
+ [:args, :arg1],
146
+ [:if,
147
+ [:call, [:lvar, :arg1], "nil?".intern], # emacs is freakin'
148
+ [:return, [:false]],
149
+ [:return, [:true]]]]]],
150
+ "Rewriter" => s(:defn, :bools,
151
+ s(:args, :arg1),
152
+ s(:scope,
153
+ s(:block,
154
+ s(:if,
155
+ s(:call,
156
+ s(:lvar, :arg1),
157
+ :nil?,
158
+ nil),
159
+ s(:return, s(:false)),
160
+ s(:return, s(:true)))))),
161
+ # TODO: why does return false have type void?
162
+ "TypeChecker" => t(:defn, :bools,
163
+ t(:args, t(:arg1, Type.value)),
164
+ t(:scope,
165
+ t(:block,
166
+ t(:if,
167
+ t(:call,
168
+ t(:lvar, :arg1, Type.value),
169
+ :nil?,
170
+ nil,
171
+ Type.bool),
172
+ t(:return,
173
+ t(:false, Type.bool),
174
+ Type.void),
175
+ t(:return,
176
+ t(:true, Type.bool),
177
+ Type.void),
178
+ Type.void),
179
+ Type.unknown),
180
+ Type.void),
181
+ Type.function(Type.unknown, [Type.value], Type.bool)),
182
+ "R2CRewriter" => :same,
183
+ "RubyToRubyC" => "VALUE\nbools(VALUE arg1) {\nif (rb_funcall(arg1, rb_intern(\"nil?\"), 0)) {\nreturn Qfalse;\n} else {\nreturn Qtrue;\n}\n}",
184
+ "RubyToAnsiC" => "bool\nbools(void * arg1) {\nif (arg1) {\nreturn 0;\n} else {\nreturn 1;\n}\n}",
185
+ },
186
+
187
+ # TODO: move all call tests here
188
+ "call_arglist" => {
189
+ "ParseTree" => [:fcall, :puts, [:array, [:lit, 42]]],
190
+ "Rewriter" => s(:call, nil, :puts, s(:arglist, s(:lit, 42))),
191
+ "TypeChecker" => :skip,
192
+ "R2CRewriter" => :skip,
193
+ "RubyToRubyC" => :skip,
194
+ "RubyToAnsiC" => :skip,
195
+ },
196
+
197
+ "call_attrasgn" => {
198
+ "ParseTree" => [:attrasgn, [:lit, 42], :method=, [:array, [:lvar, :y]]],
199
+ "Rewriter" => s(:call, s(:lit, 42), :method=, s(:arglist, s(:lvar, :y))),
200
+ "TypeChecker" => :skip,
201
+ "R2CRewriter" => :skip,
202
+ "RubyToRubyC" => :skip,
203
+ "RubyToAnsiC" => :skip,
204
+ },
205
+
206
+ "call_self" => {
207
+ "ParseTree" => [:call, [:self], :method],
208
+ "Rewriter" => s(:call, s(:lvar, :self), :method, nil),
209
+ "TypeChecker" => :skip,
210
+ "R2CRewriter" => :skip,
211
+ "RubyToRubyC" => :skip,
212
+ "RubyToAnsiC" => :skip,
213
+ },
214
+
215
+ "case_stmt" => {
216
+ "ParseTree" => [:defn, :case_stmt,
217
+ [:scope,
218
+ [:block,
219
+ [:args],
220
+ [:lasgn, :var, [:lit, 2]],
221
+ [:lasgn, :result, [:str, ""]],
222
+ [:case,
223
+ [:lvar, :var],
224
+ [:when,
225
+ [:array, [:lit, 1]],
226
+ [:block,
227
+ [:fcall, :puts, [:array, [:str, "something"]]],
228
+ [:lasgn, :result, [:str, "red"]]]],
229
+ [:when,
230
+ [:array, [:lit, 2], [:lit, 3]],
231
+ [:lasgn, :result, [:str, "yellow"]]],
232
+ [:when, [:array, [:lit, 4]], nil],
233
+ [:lasgn, :result, [:str, "green"]]],
234
+ [:case,
235
+ [:lvar, :result],
236
+ [:when, [:array, [:str, "red"]], [:lasgn, :var, [:lit, 1]]],
237
+ [:when, [:array, [:str, "yellow"]], [:lasgn, :var, [:lit, 2]]],
238
+ [:when, [:array, [:str, "green"]], [:lasgn, :var, [:lit, 3]]],
239
+ nil],
240
+ [:return, [:lvar, :result]]]]],
241
+ "Rewriter" => s(:defn, :case_stmt,
242
+ s(:args),
243
+ s(:scope,
244
+ s(:block,
245
+ s(:lasgn, :var, s(:lit, 2)),
246
+ s(:lasgn, :result, s(:str, "")),
247
+ s(:if,
248
+ s(:call,
249
+ s(:lvar, :var),
250
+ :===,
251
+ s(:arglist, s(:lit, 1))),
252
+ s(:block,
253
+ s(:call,
254
+ nil,
255
+ :puts,
256
+ s(:arglist, s(:str, "something"))),
257
+ s(:lasgn, :result, s(:str, "red"))),
258
+ s(:if,
259
+ s(:or,
260
+ s(:call,
261
+ s(:lvar, :var),
262
+ :===,
263
+ s(:arglist, s(:lit, 2))),
264
+ s(:call,
265
+ s(:lvar, :var),
266
+ :===,
267
+ s(:arglist, s(:lit, 3)))),
268
+ s(:lasgn, :result, s(:str, "yellow")),
269
+ s(:if,
270
+ s(:call,
271
+ s(:lvar, :var),
272
+ :===,
273
+ s(:arglist, s(:lit, 4))),
274
+ nil,
275
+ s(:lasgn, :result, s(:str, "green"))))),
276
+ s(:if,
277
+ s(:call,
278
+ s(:lvar, :result),
279
+ :===,
280
+ s(:arglist, s(:str, "red"))),
281
+ s(:lasgn, :var, s(:lit, 1)),
282
+ s(:if,
283
+ s(:call,
284
+ s(:lvar, :result),
285
+ :===,
286
+ s(:arglist, s(:str, "yellow"))),
287
+ s(:lasgn, :var, s(:lit, 2)),
288
+ s(:if,
289
+ s(:call,
290
+ s(:lvar, :result),
291
+ :===,
292
+ s(:arglist, s(:str, "green"))),
293
+ s(:lasgn, :var, s(:lit, 3)),
294
+ nil))),
295
+ s(:return, s(:lvar, :result))))),
296
+ "TypeChecker" => t(:defn, :case_stmt,
297
+ t(:args),
298
+ t(:scope,
299
+ t(:block,
300
+ t(:lasgn,
301
+ :var,
302
+ t(:lit, 2, Type.long),
303
+ Type.long),
304
+ t(:lasgn,
305
+ :result,
306
+ t(:str, "", Type.str),
307
+ Type.str),
308
+ t(:if,
309
+ t(:call,
310
+ t(:lvar, :var, Type.long),
311
+ :case_equal_long,
312
+ t(:arglist, t(:lit, 1, Type.long)),
313
+ Type.bool),
314
+ t(:block,
315
+ t(:call,
316
+ nil,
317
+ :puts,
318
+ t(:arglist,
319
+ t(:str, "something", Type.str)),
320
+ Type.void),
321
+ t(:lasgn,
322
+ :result,
323
+ t(:str, "red", Type.str),
324
+ Type.str),
325
+ Type.str),
326
+ t(:if,
327
+ t(:or,
328
+ t(:call,
329
+ t(:lvar, :var, Type.long),
330
+ :case_equal_long,
331
+ t(:arglist, t(:lit, 2, Type.long)),
332
+ Type.bool),
333
+ t(:call,
334
+ t(:lvar, :var, Type.long),
335
+ :case_equal_long,
336
+ t(:arglist, t(:lit, 3, Type.long)),
337
+ Type.bool),
338
+ Type.bool),
339
+ t(:lasgn,
340
+ :result,
341
+ t(:str, "yellow", Type.str),
342
+ Type.str),
343
+ t(:if,
344
+ t(:call,
345
+ t(:lvar, :var, Type.long),
346
+ :case_equal_long,
347
+ t(:arglist, t(:lit, 4, Type.long)),
348
+ Type.bool),
349
+ nil,
350
+ t(:lasgn,
351
+ :result,
352
+ t(:str, "green", Type.str),
353
+ Type.str),
354
+ Type.str),
355
+ Type.str),
356
+ Type.str),
357
+ t(:if,
358
+ t(:call,
359
+ t(:lvar, :result, Type.str),
360
+ :case_equal_str,
361
+ t(:arglist, t(:str, "red", Type.str)),
362
+ Type.bool),
363
+ t(:lasgn, :var, t(:lit, 1, Type.long), Type.long),
364
+ t(:if,
365
+ t(:call,
366
+ t(:lvar, :result, Type.str),
367
+ :case_equal_str,
368
+ t(:arglist, t(:str, "yellow", Type.str)),
369
+ Type.bool),
370
+ t(:lasgn, :var, t(:lit, 2, Type.long), Type.long),
371
+ t(:if,
372
+ t(:call,
373
+ t(:lvar, :result, Type.str),
374
+ :case_equal_str,
375
+ t(:arglist,
376
+ t(:str, "green", Type.str)),
377
+ Type.bool),
378
+ t(:lasgn,
379
+ :var,
380
+ t(:lit, 3, Type.long),
381
+ Type.long),
382
+ nil,
383
+ Type.long),
384
+ Type.long),
385
+ Type.long),
386
+ t(:return,
387
+ t(:lvar, :result, Type.str),
388
+ Type.void),
389
+ Type.unknown),
390
+ Type.void),
391
+ Type.function(Type.unknown, [], Type.str)),
392
+ "R2CRewriter" => :same,
393
+ # HACK: I don't like the semis after the if blocks, but it is a compromise right now
394
+ "RubyToRubyC" => "VALUE
395
+ case_stmt() {
396
+ VALUE result;
397
+ VALUE var;
398
+ var = LONG2NUM(2);
399
+ result = rb_str_new2(\"\");
400
+ if (rb_funcall(var, rb_intern(\"===\"), 1, LONG2NUM(1))) {
401
+ rb_funcall(self, rb_intern(\"puts\"), 1, rb_str_new2(\"something\"));
402
+ result = rb_str_new2(\"red\");
403
+ } else {
404
+ if (rb_funcall(var, rb_intern(\"===\"), 1, LONG2NUM(2)) || rb_funcall(var, rb_intern(\"===\"), 1, LONG2NUM(3))) {
405
+ result = rb_str_new2(\"yellow\");
406
+ } else {
407
+ if (rb_funcall(var, rb_intern(\"===\"), 1, LONG2NUM(4))) {
408
+ ;
409
+ } else {
410
+ result = rb_str_new2(\"green\");
411
+ }
412
+ }
413
+ };
414
+ if (rb_funcall(result, rb_intern(\"===\"), 1, rb_str_new2(\"red\"))) {
415
+ var = LONG2NUM(1);
416
+ } else {
417
+ if (rb_funcall(result, rb_intern(\"===\"), 1, rb_str_new2(\"yellow\"))) {
418
+ var = LONG2NUM(2);
419
+ } else {
420
+ if (rb_funcall(result, rb_intern(\"===\"), 1, rb_str_new2(\"green\"))) {
421
+ var = LONG2NUM(3);
422
+ }
423
+ }
424
+ };
425
+ return result;
426
+ }",
427
+ "RubyToAnsiC" => "str
428
+ case_stmt() {
429
+ str result;
430
+ long var;
431
+ var = 2;
432
+ result = \"\";
433
+ if (case_equal_long(var, 1)) {
434
+ puts(\"something\");
435
+ result = \"red\";
436
+ } else {
437
+ if (case_equal_long(var, 2) || case_equal_long(var, 3)) {
438
+ result = \"yellow\";
439
+ } else {
440
+ if (case_equal_long(var, 4)) {
441
+ ;
442
+ } else {
443
+ result = \"green\";
444
+ }
445
+ }
446
+ };
447
+ if (case_equal_str(result, \"red\")) {
448
+ var = 1;
449
+ } else {
450
+ if (case_equal_str(result, \"yellow\")) {
451
+ var = 2;
452
+ } else {
453
+ if (case_equal_str(result, \"green\")) {
454
+ var = 3;
455
+ }
456
+ }
457
+ };
458
+ return result;
459
+ }",
460
+ },
461
+
462
+ "conditional1" => {
463
+ "ParseTree" => [:if, [:call, [:lit, 42], :==, [:array, [:lit, 0]]], [:return, [:lit, 1]], nil],
464
+ "Rewriter" => s(:if, s(:call, s(:lit, 42), :==, s(:arglist, s(:lit, 0))), s(:return, s(:lit, 1)), nil),
465
+ "TypeChecker" => t(:if,
466
+ t(:call, t(:lit, 42, Type.long), :==,
467
+ t(:arglist, t(:lit, 0, Type.long)),
468
+ Type.bool),
469
+ t(:return, t(:lit, 1, Type.long), Type.void),
470
+ nil,
471
+ Type.void),
472
+ "R2CRewriter" => t(:if,
473
+ t(:call, t(:lit, 42, Type.long), :==,
474
+ t(:arglist, t(:lit, 0, Type.long)),
475
+ Type.bool),
476
+ t(:return, t(:lit, 1, Type.long), Type.void),
477
+ nil,
478
+ Type.void),
479
+ "RubyToRubyC" => "if (rb_funcall(LONG2NUM(42), rb_intern(\"==\"), 1, LONG2NUM(0))) {\nreturn LONG2NUM(1);\n}",
480
+ "RubyToAnsiC" => "if (42 == 0) {\nreturn 1;\n}",
481
+ },
482
+
483
+ "conditional2" => {
484
+ "ParseTree" => [:if, [:call, [:lit, 42], :==, [:array, [:lit, 0]]], nil, [:return, [:lit, 2]]],
485
+ "Rewriter" => s(:if,
486
+ s(:call, s(:lit, 42),
487
+ :==, s(:arglist, s(:lit, 0))),
488
+ nil,
489
+ s(:return, s(:lit, 2))),
490
+ "TypeChecker" => t(:if,
491
+ t(:call,
492
+ t(:lit, 42, Type.long),
493
+ :==,
494
+ t(:arglist,
495
+ t(:lit, 0, Type.long)),
496
+ Type.bool),
497
+ nil,
498
+ t(:return, t(:lit, 2, Type.long), Type.void),
499
+ Type.void),
500
+ "R2CRewriter" => :same,
501
+ "RubyToRubyC" => "if (rb_funcall(LONG2NUM(42), rb_intern(\"==\"), 1, LONG2NUM(0))) {\n;\n} else {\nreturn LONG2NUM(2);\n}",
502
+ "RubyToAnsiC" => "if (42 == 0) {\n;\n} else {\nreturn 2;\n}",
503
+ },
504
+
505
+ "conditional3" => {
506
+ "ParseTree" => [:if, [:call, [:lit, 42], :==, [:array, [:lit, 0]]],
507
+ [:return, [:lit, 3]],
508
+ [:return, [:lit, 4]]],
509
+ "Rewriter" => s(:if,
510
+ s(:call,
511
+ s(:lit, 42),
512
+ :==,
513
+ s(:arglist, s(:lit, 0))),
514
+ s(:return, s(:lit, 3)),
515
+ s(:return, s(:lit, 4))),
516
+ "TypeChecker" => t(:if,
517
+ t(:call,
518
+ t(:lit, 42, Type.long),
519
+ :==,
520
+ t(:arglist,
521
+ t(:lit, 0, Type.long)),
522
+ Type.bool),
523
+ t(:return,
524
+ t(:lit, 3, Type.long),
525
+
526
+ Type.void),
527
+ t(:return,
528
+ t(:lit, 4, Type.long),
529
+ Type.void),
530
+ Type.void),
531
+ "R2CRewriter" => :same,
532
+ "RubyToRubyC" => "if (rb_funcall(LONG2NUM(42), rb_intern(\"==\"), 1, LONG2NUM(0))) {\nreturn LONG2NUM(3);\n} else {\nreturn LONG2NUM(4);\n}",
533
+ "RubyToAnsiC" => "if (42 == 0) {\nreturn 3;\n} else {\nreturn 4;\n}",
534
+ },
535
+
536
+ "conditional4" => {
537
+ "ParseTree" => [:if,
538
+ [:call, [:lit, 42], :==, [:array, [:lit, 0]]],
539
+ [:return, [:lit, 2]],
540
+ [:if,
541
+ [:call, [:lit, 42], :<, [:array, [:lit, 0]]],
542
+ [:return, [:lit, 3]],
543
+ [:return, [:lit, 4]]]],
544
+ "Rewriter" => s(:if,
545
+ s(:call,
546
+ s(:lit, 42),
547
+ :==,
548
+ s(:arglist, s(:lit, 0))),
549
+ s(:return, s(:lit, 2)),
550
+ s(:if,
551
+ s(:call,
552
+ s(:lit, 42),
553
+ :<,
554
+ s(:arglist, s(:lit, 0))),
555
+ s(:return, s(:lit, 3)),
556
+ s(:return, s(:lit, 4)))),
557
+ "TypeChecker" => t(:if,
558
+ t(:call,
559
+ t(:lit, 42, Type.long),
560
+ :==,
561
+ t(:arglist,
562
+ t(:lit, 0, Type.long)),
563
+ Type.bool),
564
+ t(:return,
565
+ t(:lit, 2, Type.long),
566
+ Type.void),
567
+ t(:if,
568
+ t(:call,
569
+ t(:lit, 42, Type.long),
570
+ :<,
571
+ t(:arglist,
572
+ t(:lit, 0, Type.long)),
573
+ Type.bool),
574
+ t(:return,
575
+ t(:lit, 3, Type.long),
576
+ Type.void),
577
+ t(:return,
578
+ t(:lit, 4, Type.long),
579
+ Type.void),
580
+ Type.void),
581
+ Type.void),
582
+ "R2CRewriter" => :same,
583
+ "RubyToRubyC" => "if (rb_funcall(LONG2NUM(42), rb_intern(\"==\"), 1, LONG2NUM(0))) {\nreturn LONG2NUM(2);\n} else {\nif (rb_funcall(LONG2NUM(42), rb_intern(\"<\"), 1, LONG2NUM(0))) {\nreturn LONG2NUM(3);\n} else {\nreturn LONG2NUM(4);\n}\n}",
584
+ "RubyToAnsiC" => "if (42 == 0) {\nreturn 2;\n} else {\nif (42 < 0) {\nreturn 3;\n} else {\nreturn 4;\n}\n}",
585
+ },
586
+
587
+ "defn_empty" => {
588
+ "ParseTree" => [:defn, :empty, [:scope, [:block, [:args], [:nil]]]],
589
+ "Rewriter" => s(:defn, :empty,
590
+ s(:args), s(:scope, s(:block, s(:nil)))),
591
+ "TypeChecker" => t(:defn, :empty,
592
+ t(:args),
593
+ t(:scope,
594
+ t(:block,
595
+ t(:nil, Type.value),
596
+ Type.unknown),
597
+ Type.void),
598
+ Type.function(Type.unknown, [], Type.void)),
599
+ "R2CRewriter" => :same,
600
+ "RubyToRubyC" => "VALUE\nempty() {\nQnil;\n}",
601
+ "RubyToAnsiC" => "void\nempty() {\nNULL;\n}",
602
+ },
603
+
604
+ "defn_zarray" => {
605
+ "ParseTree" => [:defn, :empty, [:scope, [:block, [:args], [:lasgn, :a, [:zarray]], [:return, [:lvar, :a]]]]],
606
+ "Rewriter" => s(:defn,
607
+ :empty,
608
+ s(:args),
609
+ s(:scope, s(:block, s(:lasgn, :a, s(:array)), s(:return, s(:lvar, :a))))),
610
+ "TypeChecker" => t(:defn,
611
+ :empty,
612
+ t(:args),
613
+ t(:scope,
614
+ t(:block,
615
+ t(:lasgn, :a, t(:array), Type.unknown_list),
616
+ t(:return,
617
+ t(:lvar,
618
+ :a, Type.unknown_list), Type.void),
619
+ Type.unknown), Type.void),
620
+ Type.function(Type.unknown, [], Type.unknown_list)),
621
+ "R2CRewriter" => :same,
622
+ "RubyToRubyC" => "VALUE\nempty() {\nVALUE a;\na = rb_ary_new2(0);\nreturn a;\n}",
623
+ "RubyToAnsiC" => "void *\nempty() {\nvoid * a;\na = (void *) malloc(sizeof(void *) * 0);\nreturn a;\n}",
624
+ },
625
+
626
+ "defn_or" => {
627
+ "ParseTree" => [:defn, :|, [:scope, [:block, [:args], [:nil]]]],
628
+ "Rewriter" => s(:defn, :|,
629
+ s(:args), s(:scope, s(:block, s(:nil)))),
630
+ "TypeChecker" => t(:defn, :|,
631
+ t(:args),
632
+ t(:scope,
633
+ t(:block,
634
+ t(:nil, Type.value),
635
+ Type.unknown),
636
+ Type.void),
637
+ Type.function(Type.unknown, [], Type.void)),
638
+ "R2CRewriter" => :same,
639
+ "RubyToRubyC" => "VALUE\nor() {\nQnil;\n}",
640
+ "RubyToAnsiC" => "void\nor() {\nNULL;\n}",
641
+ },
642
+
643
+ "defn_is_something" => {
644
+ "ParseTree" => [:defn, :something?, [:scope, [:block, [:args], [:nil]]]],
645
+ "Rewriter" => s(:defn, :something?,
646
+ s(:args), s(:scope, s(:block, s(:nil)))),
647
+ "TypeChecker" => t(:defn, :something?,
648
+ t(:args),
649
+ t(:scope,
650
+ t(:block,
651
+ t(:nil, Type.value),
652
+ Type.unknown),
653
+ Type.void),
654
+ Type.function(Type.unknown, [], Type.void)),
655
+ "R2CRewriter" => :same,
656
+ "RubyToRubyC" => "VALUE\nis_something() {\nQnil;\n}",
657
+ "RubyToAnsiC" => "void\nis_something() {\nNULL;\n}",
658
+ },
659
+
660
+ "defn_fbody" => {
661
+ "ParseTree" => [:defn, :aliased,
662
+ [:fbody,
663
+ [:scope,
664
+ [:block,
665
+ [:args],
666
+ [:fcall, :puts, [:array, [:lit, 42]]]]]]],
667
+ "Rewriter" => s(:defn, :aliased,
668
+ s(:args),
669
+ s(:scope,
670
+ s(:block,
671
+ s(:call, nil, :puts, s(:arglist, s(:lit, 42)))))),
672
+ "TypeChecker" => :skip,
673
+ "R2CRewriter" => :skip,
674
+ "RubyToRubyC" => :skip,
675
+ "RubyToAnsiC" => :skip,
676
+ },
677
+
678
+ "defn_optargs" => {
679
+ "ParseTree" => [:defn, :x,
680
+ [:scope,
681
+ [:block,
682
+ [:args, :a, :"*args"],
683
+ [:fcall, :p,
684
+ [:array, [:lvar, :a], [:lvar, :args]]]]]],
685
+ "Rewriter" => s(:defn, :x,
686
+ s(:args, :a, :"*args"),
687
+ s(:scope,
688
+ s(:block,
689
+ s(:call, nil, :p,
690
+ s(:arglist, s(:lvar, :a), s(:lvar, :args)))))),
691
+ "TypeChecker" => :skip,
692
+ "R2CRewriter" => :skip,
693
+ "RubyToRubyC" => :skip,
694
+ "RubyToAnsiC" => :skip,
695
+ },
696
+
697
+ "dmethod_added" => {
698
+ "ParseTree" => [:defn,
699
+ :dmethod_added,
700
+ [:dmethod,
701
+ :bmethod_maker,
702
+ [:scope,
703
+ [:block,
704
+ [:args],
705
+ [:iter,
706
+ [:fcall, :define_method, [:array, [:lit, :bmethod_added]]],
707
+ [:dasgn_curr, :x],
708
+ [:call, [:dvar, :x], :+, [:array, [:lit, 1]]]]]]]],
709
+ "Rewriter" => s(:defn,
710
+ :dmethod_added,
711
+ s(:args, :x),
712
+ s(:scope,
713
+ s(:block,
714
+ s(:call, s(:lvar, :x), :+,
715
+ s(:arglist, s(:lit, 1)))))),
716
+ "TypeChecker" => :skip,
717
+ "R2CRewriter" => :skip,
718
+ "RubyToRubyC" => :skip,
719
+ "RubyToAnsiC" => :skip,
720
+ },
721
+
722
+ "global" => {
723
+ "ParseTree" => [:gvar, :$stderr],
724
+ "Rewriter" => s(:gvar, :$stderr),
725
+ # TODO: test s(:gvar, :$stderr) != t(:gvar, $stderr, Type.file)
726
+ "TypeChecker" => t(:gvar, :$stderr, Type.file),
727
+ "R2CRewriter" => :same,
728
+ "RubyToRubyC" => "rb_gv_get(\"$stderr\")",
729
+ "RubyToAnsiC" => "stderr",
730
+ },
731
+
732
+ "interpolated" => {
733
+ "ParseTree" => [:dstr,
734
+ "var is ", [:lvar, :argl], [:str, ". So there."]],
735
+ "Rewriter" => s(:dstr,
736
+ "var is ", s(:lvar, :argl), s(:str, ". So there.")),
737
+ "TypeChecker" => t(:dstr,
738
+ "var is ",
739
+ t(:lvar, :argl, Type.long),
740
+ t(:str, ". So there.", Type.str),
741
+ Type.str),
742
+ "R2CRewriter" => :same,
743
+ "RubyToRubyC" => :unsupported,
744
+ "RubyToAnsiC" => :unsupported,
745
+ },
746
+
747
+ "iter" => {
748
+ "ParseTree" => [:iter, [:fcall, :loop], nil],
749
+ "Rewriter" => s(:iter,
750
+ s(:call, nil, :loop, nil),
751
+ s(:dasgn_curr, :temp_1),
752
+ nil),
753
+ "TypeChecker" => t(:iter,
754
+ t(:call, nil, :loop, nil, Type.unknown),
755
+ t(:dasgn_curr, :temp_1, Type.unknown),
756
+ nil,
757
+ Type.unknown),
758
+ "R2CRewriter" => :same,
759
+ "RubyToRubyC" => :unsupported,
760
+ "RubyToAnsiC" => :unsupported,
761
+ },
762
+
763
+ "iteration2" => {
764
+ "ParseTree" => [:iter,
765
+ [:call, [:lvar, :arrays], :each],
766
+ [:dasgn_curr, :x],
767
+ [:fcall, :puts, [:arrays, [:dvar, :x]]]],
768
+ "Rewriter" => s(:iter,
769
+ s(:call, s(:lvar, :arrays), :each, nil),
770
+ s(:dasgn_curr, :x),
771
+ s(:call, nil, :puts, s(:arglist, s(:dvar, :x)))),
772
+ "TypeChecker" => t(:iter,
773
+ t(:call,
774
+ t(:lvar, :arrays, Type.str_list),
775
+ :each,
776
+ nil, Type.unknown),
777
+ t(:dasgn_curr, :x, Type.str),
778
+ t(:call, nil, :puts,
779
+ t(:arglist, t(:dvar, :x, Type.str)),
780
+ Type.void),
781
+ Type.void),
782
+ "R2CRewriter" => :same,
783
+ "RubyToRubyC" => "unsigned long index_x;
784
+ unsigned long arrays_max = FIX2LONG(rb_funcall(arrays, rb_intern(\"size\"), 0));
785
+ for (index_x = 0; index_x < arrays_max; ++index_x) {
786
+ VALUE x = rb_funcall(arrays, rb_intern(\"at\"), 1, LONG2FIX(index_x));
787
+ rb_funcall(self, rb_intern(\"puts\"), 1, x);
788
+ }",
789
+ "RubyToAnsiC" => "unsigned long index_x;
790
+ for (index_x = 0; arrays[index_x] != NULL; ++index_x) {
791
+ str x = arrays[index_x];
792
+ puts(x);
793
+ }",
794
+ },
795
+
796
+
797
+ "iteration4" => {
798
+ "ParseTree" => [:iter,
799
+ [:call, [:lit, 1], :upto, [:array, [:lit, 3]]],
800
+ [:dasgn_curr, :n],
801
+ [:fcall, :puts, [:array, [:call, [:dvar, :n], :to_s]]]],
802
+ "Rewriter" => s(:dummy,
803
+ s(:lasgn, :n, s(:lit, 1)),
804
+ s(:while,
805
+ s(:call, s(:lvar, :n), :<=, s(:arglist, s(:lit, 3))),
806
+ s(:block,
807
+ s(:call,
808
+ nil,
809
+ :puts,
810
+ s(:arglist, s(:call, s(:lvar, :n), :to_s, nil))),
811
+ s(:lasgn, :n,
812
+ s(:call, s(:lvar, :n),
813
+ :+,
814
+ s(:arglist, s(:lit, 1))))), true)),
815
+ "TypeChecker" => t(:dummy, t(:lasgn, :n, t(:lit, 1, Type.long), Type.long),
816
+ t(:while,
817
+ t(:call,
818
+ t(:lvar, :n, Type.long),
819
+ :<=,
820
+ t(:arglist, t(:lit, 3, Type.long)), Type.bool),
821
+ t(:block,
822
+ t(:call, nil, :puts,
823
+ t(:arglist,
824
+ t(:call,
825
+ t(:lvar, :n, Type.long),
826
+ :to_s,
827
+ nil, Type.str)), Type.void),
828
+ t(:lasgn, :n,
829
+ t(:call,
830
+ t(:lvar, :n, Type.long),
831
+ :+,
832
+ t(:arglist,
833
+ t(:lit,
834
+ 1, Type.long)),
835
+ Type.long), Type.long), Type.unknown), true)),
836
+ "R2CRewriter" => :same,
837
+ "RubyToRubyC" => "n = LONG2NUM(1);
838
+ while (rb_funcall(n, rb_intern(\"<=\"), 1, LONG2NUM(3))) {
839
+ rb_funcall(self, rb_intern(\"puts\"), 1, rb_funcall(n, rb_intern(\"to_s\"), 0));
840
+ n = rb_funcall(n, rb_intern(\"+\"), 1, LONG2NUM(1));
841
+ }",
842
+ "RubyToAnsiC" => "n = 1;
843
+ while (n <= 3) {
844
+ puts(to_s(n));
845
+ n = n + 1;
846
+ }",
847
+ },
848
+
849
+ "iteration5" => {
850
+ "ParseTree" => [:iter,
851
+ [:call, [:lit, 3], :downto, [:array, [:lit, 1]]],
852
+ [:dasgn_curr, :n],
853
+ [:fcall, :puts, [:array, [:call, [:dvar, :n], :to_s]]]],
854
+ "Rewriter" => s(:dummy, s(:lasgn, :n, s(:lit, 3)), s(:while,
855
+ s(:call, s(:lvar, :n), :>=, s(:arglist, s(:lit, 1))),
856
+ s(:block,
857
+ s(:call, nil, :puts,
858
+ s(:arglist, s(:call, s(:lvar, :n), :to_s, nil))),
859
+ s(:lasgn, :n, s(:call, s(:lvar, :n),
860
+ :-, s(:arglist, s(:lit, 1))))), true)),
861
+ "TypeChecker" => t(:dummy,
862
+ t(:lasgn, :n, t(:lit, 3, Type.long), Type.long),
863
+ t(:while,
864
+ t(:call,
865
+ t(:lvar, :n, Type.long),
866
+ :>=,
867
+ t(:arglist, t(:lit, 1, Type.long)), Type.bool),
868
+ t(:block,
869
+ t(:call, nil, :puts,
870
+ t(:arglist,
871
+ t(:call,
872
+ t(:lvar, :n, Type.long),
873
+ :to_s,
874
+ nil, Type.str)), Type.void),
875
+ t(:lasgn, :n,
876
+ t(:call,
877
+ t(:lvar, :n, Type.long),
878
+ :-,
879
+ t(:arglist, t(:lit, 1, Type.long)),
880
+ Type.long),
881
+ Type.long),
882
+ Type.unknown), true)),
883
+ "R2CRewriter" => :same,
884
+ "RubyToRubyC" => "n = LONG2NUM(3);
885
+ while (rb_funcall(n, rb_intern(\">=\"), 1, LONG2NUM(1))) {
886
+ rb_funcall(self, rb_intern(\"puts\"), 1, rb_funcall(n, rb_intern(\"to_s\"), 0));
887
+ n = rb_funcall(n, rb_intern(\"-\"), 1, LONG2NUM(1));
888
+ }",
889
+ "RubyToAnsiC" => "n = 3;
890
+ while (n >= 1) {
891
+ puts(to_s(n));
892
+ n = n - 1;
893
+ }",
894
+ },
895
+
896
+ "iteration6" => {
897
+ "ParseTree" => [:while, [:call, [:lvar, :argl],
898
+ :>=, [:arglist, [:lit, 1]]], [:block,
899
+ [:call, nil, :puts, [:arglist, [:str, "hello"]]],
900
+ [:lasgn,
901
+ :argl,
902
+ [:call, [:lvar, :argl],
903
+ :-, [:arglist, [:lit, 1]]]]], true],
904
+ "Rewriter" => s(:while,
905
+ s(:call, s(:lvar, :argl),
906
+ :>=, s(:arglist, s(:lit, 1))),
907
+ s(:block,
908
+ s(:call, nil, :puts, s(:arglist, s(:str, "hello"))),
909
+ s(:lasgn,
910
+ :argl,
911
+ s(:call, s(:lvar, :argl),
912
+ :-, s(:arglist, s(:lit, 1))))), true),
913
+ "TypeChecker" => t(:while,
914
+ t(:call, t(:lvar, :argl, Type.long),
915
+ :>=,
916
+ t(:arglist, t(:lit, 1, Type.long)), Type.bool),
917
+ t(:block,
918
+ t(:call, nil, :puts,
919
+ t(:arglist, t(:str, "hello", Type.str)),
920
+ Type.void),
921
+ t(:lasgn,
922
+ :argl,
923
+ t(:call, t(:lvar, :argl, Type.long),
924
+ :-,
925
+ t(:arglist, t(:lit, 1, Type.long)), Type.long),
926
+ Type.long),
927
+ Type.unknown), true),
928
+ "R2CRewriter" => :same,
929
+ "RubyToRubyC" => "while (rb_funcall(argl, rb_intern(\">=\"), 1, LONG2NUM(1))) {
930
+ rb_funcall(self, rb_intern(\"puts\"), 1, rb_str_new2(\"hello\"));
931
+ argl = rb_funcall(argl, rb_intern(\"-\"), 1, LONG2NUM(1));
932
+ }",
933
+ "RubyToAnsiC" => "while (argl >= 1) {
934
+ puts(\"hello\");
935
+ argl = argl - 1;
936
+ }",
937
+ },
938
+
939
+ # TODO: this might still be too much
940
+ "lasgn_call" => {
941
+ "ParseTree" => [:lasgn, :c, [:call, [:lit, 2], :+, [:arglist, [:lit, 3]]]],
942
+ "Rewriter" => s(:lasgn, :c, s(:call, s(:lit, 2), :+, s(:arglist, s(:lit, 3)))),
943
+ "TypeChecker" => t(:lasgn, :c,
944
+ t(:call,
945
+ t(:lit, 2, Type.long),
946
+ :+,
947
+ t(:arglist,
948
+ t(:lit, 3, Type.long)),
949
+ Type.long),
950
+ Type.long),
951
+ "R2CRewriter" => :same,
952
+ "RubyToRubyC" => "c = rb_funcall(LONG2NUM(2), rb_intern(\"+\"), 1, LONG2NUM(3))", # FIX: probably not "c ="
953
+ "RubyToAnsiC" => "c = 2 + 3",
954
+ },
955
+
956
+ "lasgn_array" => {
957
+ "ParseTree" => [:lasgn, :var, [:array,
958
+ [:str, "foo"],
959
+ [:str, "bar"]]],
960
+ "Rewriter" => s(:lasgn, :var, s(:array,
961
+ s(:str, "foo"),
962
+ s(:str, "bar"))),
963
+ "TypeChecker" => t(:lasgn,
964
+ :var,
965
+ t(:array,
966
+ t(:str, "foo", Type.str),
967
+ t(:str, "bar", Type.str)),
968
+ Type.str_list),
969
+ "R2CRewriter" => :same,
970
+ "RubyToRubyC" => "var = rb_ary_new2(2);\nrb_ary_store(var, 0, rb_str_new2(\"foo\"));\nrb_ary_store(var, 1, rb_str_new2(\"bar\"))",
971
+ "RubyToAnsiC" => "var = (str) malloc(sizeof(str) * 2);\nvar[0] = \"foo\";\nvar[1] = \"bar\""
972
+ },
973
+
974
+ "lit_bool_false" => {
975
+ "ParseTree" => [:false],
976
+ "Rewriter" => s(:false),
977
+ "TypeChecker" => t(:false, Type.bool),
978
+ "R2CRewriter" => :same,
979
+ "RubyToRubyC" => "Qfalse",
980
+ "RubyToAnsiC" => "0",
981
+ },
982
+
983
+ "lit_bool_true" => {
984
+ "ParseTree" => [:true],
985
+ "Rewriter" => s(:true),
986
+ "TypeChecker" => t(:true, Type.bool),
987
+ "R2CRewriter" => :same,
988
+ "RubyToRubyC" => "Qtrue",
989
+ "RubyToAnsiC" => "1",
990
+ },
991
+
992
+ "lit_float" => {
993
+ "ParseTree" => [:lit, 1.1],
994
+ "Rewriter" => s(:lit, 1.1),
995
+ "TypeChecker" => t(:lit, 1.1, Type.float),
996
+ "R2CRewriter" => :same,
997
+ "RubyToRubyC" => "DBL2NUM(1.1)",
998
+ "RubyToAnsiC" => "1.1",
999
+ },
1000
+
1001
+ "lit_long" => {
1002
+ "ParseTree" => [:lit, 1],
1003
+ "Rewriter" => s(:lit, 1),
1004
+ "TypeChecker" => t(:lit, 1, Type.long),
1005
+ "R2CRewriter" => :same,
1006
+ "RubyToRubyC" => "LONG2NUM(1)",
1007
+ "RubyToAnsiC" => "1",
1008
+ },
1009
+
1010
+ "lit_sym" => {
1011
+ "ParseTree" => [:lit, :x],
1012
+ "Rewriter" => s(:lit, :x),
1013
+ "TypeChecker" => t(:lit, :x, Type.symbol),
1014
+ "R2CRewriter" => :same,
1015
+ "RubyToRubyC" => "rb_intern(\"x\")",
1016
+ "RubyToAnsiC" => "\"x\"",
1017
+ },
1018
+
1019
+ "lit_str" => {
1020
+ "ParseTree" => [:str, "x"],
1021
+ "Rewriter" => s(:str, "x"),
1022
+ "TypeChecker" => t(:str, "x", Type.str),
1023
+ "R2CRewriter" => :same,
1024
+ "RubyToRubyC" => "rb_str_new2(\"x\")",
1025
+ "RubyToAnsiC" => "\"x\"",
1026
+ },
1027
+
1028
+ "multi_args" => {
1029
+ "ParseTree" => [:defn, :multi_args,
1030
+ [:scope,
1031
+ [:block,
1032
+ [:args, :arg1, :arg2],
1033
+ [:lasgn,
1034
+ :arg3,
1035
+ [:call,
1036
+ [:call, [:lvar, :arg1], :*, [:array, [:lvar, :arg2]]],
1037
+ :*,
1038
+ [:array, [:lit, 7]]]],
1039
+ [:fcall, :puts, [:array, [:call, [:lvar, :arg3], :to_s]]],
1040
+ [:return, [:str, "foo"]]]]],
1041
+ "Rewriter" => s(:defn, :multi_args,
1042
+ s(:args, :arg1, :arg2),
1043
+ s(:scope,
1044
+ s(:block,
1045
+ s(:lasgn, :arg3,
1046
+ s(:call,
1047
+ s(:call,
1048
+ s(:lvar, :arg1),
1049
+ :*,
1050
+ s(:arglist, s(:lvar, :arg2))),
1051
+ :*,
1052
+ s(:arglist, s(:lit, 7)))),
1053
+ s(:call,
1054
+ nil,
1055
+ :puts,
1056
+ s(:arglist,
1057
+ s(:call,
1058
+ s(:lvar, :arg3),
1059
+ :to_s,
1060
+ nil))),
1061
+ s(:return, s(:str, "foo"))))),
1062
+ "TypeChecker" => t(:defn, :multi_args,
1063
+ t(:args,
1064
+ t(:arg1, Type.long),
1065
+ t(:arg2, Type.long)),
1066
+ t(:scope,
1067
+ t(:block,
1068
+ t(:lasgn,
1069
+ :arg3,
1070
+ t(:call,
1071
+ t(:call,
1072
+ t(:lvar, :arg1, Type.long),
1073
+ :*,
1074
+ t(:arglist,
1075
+ t(:lvar,
1076
+ :arg2,
1077
+ Type.long)),
1078
+ Type.long),
1079
+ :*,
1080
+ t(:arglist,
1081
+ t(:lit, 7, Type.long)),
1082
+ Type.long),
1083
+ Type.long),
1084
+ t(:call,
1085
+ nil,
1086
+ :puts,
1087
+ t(:arglist,
1088
+ t(:call,
1089
+ t(:lvar, :arg3, Type.long),
1090
+ :to_s,
1091
+ nil,
1092
+ Type.str)),
1093
+ Type.void),
1094
+ t(:return, t(:str, "foo", Type.str),
1095
+ Type.void),
1096
+ Type.unknown),
1097
+ Type.void),
1098
+ Type.function(Type.unknown,
1099
+ [Type.long, Type.long], Type.str)),
1100
+ "R2CRewriter" => :same,
1101
+ "RubyToRubyC" => "VALUE
1102
+ multi_args(VALUE arg1, VALUE arg2) {
1103
+ VALUE arg3;
1104
+ arg3 = rb_funcall(rb_funcall(arg1, rb_intern(\"*\"), 1, arg2), rb_intern(\"*\"), 1, LONG2NUM(7));
1105
+ rb_funcall(self, rb_intern(\"puts\"), 1, rb_funcall(arg3, rb_intern(\"to_s\"), 0));
1106
+ return rb_str_new2(\"foo\");
1107
+ }",
1108
+ "RubyToAnsiC" => "str
1109
+ multi_args(long arg1, long arg2) {
1110
+ long arg3;
1111
+ arg3 = arg1 * arg2 * 7;
1112
+ puts(to_s(arg3));
1113
+ return \"foo\";
1114
+ }",
1115
+ },
1116
+
1117
+ "vcall" => {
1118
+ "ParseTree" => [:vcall, :method],
1119
+ "Rewriter" => s(:call, nil, :method, nil),
1120
+ "TypeChecker" => t(:call, nil, :method, nil, Type.unknown),
1121
+ "R2CRewriter" => :same,
1122
+ "RubyToRubyC" => "rb_funcall(self, rb_intern(\"method\"), 0)",
1123
+ "RubyToAnsiC" => "method()",
1124
+ },
1125
+
1126
+ "whiles" => {
1127
+ "ParseTree" => [:defn,
1128
+ :whiles,
1129
+ [:scope,
1130
+ [:block,
1131
+ [:args],
1132
+ [:while, [:false],
1133
+ [:fcall, :puts, [:array, [:str, "false"]]], true],
1134
+ [:while, [:false],
1135
+ [:fcall, :puts, [:array, [:str, "true"]]], false]]]],
1136
+ "Rewriter" => s(:defn,
1137
+ :whiles,
1138
+ s(:args),
1139
+ s(:scope,
1140
+ s(:block,
1141
+ s(:while,
1142
+ s(:false),
1143
+ s(:call, nil, :puts, s(:arglist, s(:str, "false"))),
1144
+ true),
1145
+ s(:while,
1146
+ s(:false),
1147
+ s(:call, nil, :puts, s(:arglist, s(:str, "true"))),
1148
+ false)))),
1149
+ "TypeChecker" => t(:defn,
1150
+ :whiles,
1151
+ t(:args),
1152
+ t(:scope,
1153
+ t(:block,
1154
+ t(:while,
1155
+ t(:false, Type.bool),
1156
+ t(:call,
1157
+ nil,
1158
+ :puts,
1159
+ t(:arglist, t(:str, "false", Type.str)), Type.void),
1160
+ true),
1161
+ t(:while,
1162
+ t(:false, Type.bool),
1163
+ t(:call,
1164
+ nil,
1165
+ :puts,
1166
+ t(:arglist, t(:str, "true", Type.str)), Type.void),
1167
+ false),
1168
+ Type.unknown),
1169
+ Type.void),
1170
+ Type.function(Type.unknown, [], Type.void)),
1171
+ "R2CRewriter" => :same,
1172
+ "RubyToRubyC" => "VALUE
1173
+ whiles() {
1174
+ while (Qfalse) {
1175
+ rb_funcall(self, rb_intern(\"puts\"), 1, rb_str_new2(\"false\"));
1176
+ };
1177
+ {
1178
+ rb_funcall(self, rb_intern(\"puts\"), 1, rb_str_new2(\"true\"));
1179
+ } while (Qfalse);
1180
+ }",
1181
+ "RubyToAnsiC" => "void
1182
+ whiles() {
1183
+ while (0) {
1184
+ puts(\"false\");
1185
+ };
1186
+ {
1187
+ puts(\"true\");
1188
+ } while (0);
1189
+ }",
1190
+ },
1191
+
1192
+ "zarray" => {
1193
+ "ParseTree" => [:lasgn, :a, [:zarray]],
1194
+ "Rewriter" => s(:lasgn, :a, s(:array)),
1195
+ "TypeChecker" => t(:lasgn, :a, t(:array), Type.unknown_list),
1196
+ "R2CRewriter" => :same,
1197
+ # TODO: need to verify that our variable decl will be correct
1198
+ "RubyToRubyC" => "a = rb_ary_new2(0)",
1199
+ "RubyToAnsiC" => "a = (void *) malloc(sizeof(void *) * 0)",
1200
+ },
1201
+ }
1202
+
1203
+ def self.previous(key)
1204
+
1205
+ # for now, RubyToC will mean RubyToAnsiC, since that is closest
1206
+ # "RubyToAnsiC" ,
1207
+ # "RubyToRubyC",
1208
+
1209
+ idx = @@testcase_order.index(key)-1
1210
+ case key
1211
+ when "RubyToC" then
1212
+ raise "RubyToC is dead, use RubyToAnsiC."
1213
+ when "RubyToRubyC" then
1214
+ idx -= 1
1215
+ end
1216
+ @@testcase_order[idx]
1217
+ end
1218
+
1219
+ @@testcases.each do |node, data|
1220
+ data.each do |key, val|
1221
+ if val == :same then
1222
+ prev_key = self.previous(key)
1223
+ data[key] = data[prev_key].deep_clone
1224
+ end
1225
+ end
1226
+ end
1227
+
1228
+ def self.inherited(c)
1229
+ output_name = c.name.to_s.sub(/^Test/, '')
1230
+ raise "Unknown class #{c}" unless @@testcase_order.include? output_name
1231
+
1232
+ input_name = self.previous(output_name)
1233
+
1234
+ @@testcases.each do |node, data|
1235
+ next if data[input_name] == :skip
1236
+ next if data[output_name] == :skip
1237
+
1238
+ c.send(:define_method, "test_#{node}".intern) do
1239
+ flunk "Processor is nil" if processor.nil?
1240
+ assert data.has_key?(input_name), "Unknown input data"
1241
+ assert data.has_key?(output_name), "Unknown expected data"
1242
+ input = data[input_name].deep_clone
1243
+ expected = data[output_name].deep_clone
1244
+
1245
+ case expected
1246
+ when :unsupported then
1247
+ assert_raises(UnsupportedNodeError) do
1248
+ processor.process(input)
1249
+ end
1250
+ else
1251
+ assert_equal expected, processor.process(input)
1252
+ end
1253
+ end
1254
+ end
1255
+ end
1256
+
1257
+ def test_stoopid
1258
+ # do nothing - shuts up empty test class requirement
1259
+ end
1260
+
1261
+ end