RubyToC 1.0.0.4 → 1.0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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