ParseTree 1.4.1 → 1.5.0

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.
@@ -1,3 +1,19 @@
1
+ *** 1.5.0 / 2006-09-24
2
+
3
+ + 5 minor enhancements:
4
+ Added parse_tree_audit.
5
+ Added reporting of unsupported nodes that have processors.
6
+ YAY! class method support! generated as :"self.blah"
7
+ Add parse_tree_for_string.
8
+ Converted Rakefile+gemspec to Hoe-based Rakefile.
9
+ + 6 bug fixes:
10
+ Did some preliminary work on 1.9 compatibility.
11
+ Fixed tests for some changes/clarifications.
12
+ Fixed resbody: should have nil exceptions list when no exception rescued.
13
+ Fixed op_asgn1 and op_asgn2.
14
+ Fixed incompatibility with new inline changes.
15
+ Fixed VALUE decl in parse_tree.rb
16
+
1
17
  *** 1.4.1 / 2006-04-10
2
18
 
3
19
  + 4 minor enhancements:
@@ -1,9 +1,9 @@
1
1
  History.txt
2
2
  Rakefile
3
3
  Manifest.txt
4
- ParseTree.gemspec
5
4
  README.txt
6
5
  bin/parse_tree_abc
6
+ bin/parse_tree_audit
7
7
  bin/parse_tree_deps
8
8
  bin/parse_tree_show
9
9
  demo/printer.rb
@@ -11,6 +11,7 @@ lib/composite_sexp_processor.rb
11
11
  lib/parse_tree.rb
12
12
  lib/sexp.rb
13
13
  lib/sexp_processor.rb
14
+ test/pt_testcase.rb
14
15
  test/something.rb
15
16
  test/test_all.rb
16
17
  test/test_composite_sexp_processor.rb
data/Rakefile CHANGED
@@ -1,80 +1,30 @@
1
1
  # -*- ruby -*-
2
2
 
3
- require 'rbconfig'
4
- require 'rake/rdoctask'
3
+ require 'rubygems'
4
+ require 'hoe'
5
5
 
6
- PREFIX = ENV['PREFIX'] || Config::CONFIG['prefix']
7
- RUBYLIB = Config::CONFIG['sitelibdir']
8
- RUBY_DEBUG = ENV['RUBY_DEBUG']
9
- RUBY_FLAGS = ENV['RUBY_FLAGS'] || "-w -Ilib#{File::PATH_SEPARATOR}bin#{File::PATH_SEPARATOR}../../RubyInline/dev"
10
- FILTER = ENV['FILTER']
6
+ $: << "../../RubyInline/dev"
7
+ require './lib/parse_tree.rb'
11
8
 
12
- LIB_FILES = %w(composite_sexp_processor.rb parse_tree.rb sexp.rb sexp_processor.rb)
13
- TEST_FILES = %w(test_sexp_processor.rb)
14
- BIN_FILES = %w(parse_tree_abc parse_tree_show parse_tree_deps)
15
-
16
- task :default => :test
17
-
18
- task :test do
19
- ruby "#{RUBY_FLAGS} test/test_all.rb #{FILTER}"
20
- end
21
-
22
- task :multi do
23
- sh "multiruby #{RUBY_FLAGS} test/test_all.rb #{FILTER}"
24
- end
25
-
26
- # we only install test_sexp_processor.rb to help make ruby_to_c's
27
- # subclass tests work.
28
-
29
- Rake::RDocTask.new(:docs) do |rd|
30
- rd.main = "SexpProcessor"
31
- rd.rdoc_files.include('./**/*').exclude('something.rb').exclude('test_*')
32
- rd.options << '-d'
33
- rd.options << '-Ipng'
9
+ Hoe.new("ParseTree", ParseTree::VERSION) do |p|
10
+ p.summary = "Extract and enumerate ruby parse trees."
11
+ p.description = File.read("README.txt").split(/\n\n+/)[2]
12
+ p.clean_globs << File.expand_path("~/.ruby_inline")
13
+ p.extra_deps << ['RubyInline', '>= 3.2.0']
34
14
  end
35
15
 
36
- task :install do
37
- [
38
- ['lib', LIB_FILES, RUBYLIB, 0444],
39
- ['test', TEST_FILES, RUBYLIB, 0444],
40
- ['bin', BIN_FILES, File.join(PREFIX, 'bin'), 0555]
41
- ].each do |dir, list, dest, mode|
42
- Dir.chdir dir do
43
- list.each do |f|
44
- install f, dest, :mode => mode
45
- end
46
- end
47
- end
16
+ desc 'Run against ruby 1.9 (from a multiruby install) with -d.'
17
+ task :test19 do
18
+ sh "~/.multiruby/install/1_9/bin/ruby -d #{Hoe::RUBY_FLAGS} test/test_all.rb #{Hoe::FILTER}"
48
19
  end
49
20
 
50
- task :uninstall do
51
- Dir.chdir RUBYLIB do
52
- rm_f LIB_FILES
53
- rm_f TEST_FILES
54
- end
55
- Dir.chdir File.join(PREFIX, 'bin') do
56
- rm_f BIN_FILES
57
- end
58
- end
59
-
60
- task :audit do
61
- sh "ZenTest -Ilib#{File::PATH_SEPARATOR}test #{LIB_FILES.collect{|e| File.join('lib', e)}.join(' ')} test/test_all.rb"
62
- # test_composite_sexp_processor.rb test_sexp_processor.rb
63
- end
64
-
65
- task :clean do
66
- inline_dir = File.expand_path("~/.ruby_inline")
67
- rm_rf inline_dir if test ?d, inline_dir
68
- %w(diff diff.txt demo.rb *.gem **/*~).each do |pattern|
69
- files = Dir[pattern]
70
- rm_rf files unless files.empty?
71
- end
21
+ desc 'Run in gdb'
22
+ task :debug do
23
+ puts "RUN: r -d #{Hoe::RUBY_FLAGS} test/test_all.rb #{Hoe::FILTER}"
24
+ sh "gdb ~/.multiruby/install/19/bin/ruby"
72
25
  end
73
26
 
27
+ desc 'Run a very basic demo'
74
28
  task :demo do
75
- verbose(false){sh "echo 1+1 | ruby #{RUBY_FLAGS} ./bin/parse_tree_show -f"}
76
- end
77
-
78
- task :gem do
79
- ruby "ParseTree.gemspec"
29
+ sh "echo 1+1 | ruby #{Hoe::RUBY_FLAGS} ./bin/parse_tree_show -f"
80
30
  end
@@ -0,0 +1,28 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require 'parse_tree'
4
+
5
+ all_nodes = ParseTree::NODE_NAMES
6
+
7
+ ARGV.each do |processor|
8
+ require processor
9
+ end
10
+
11
+ ObjectSpace.each_object(Class) do |klass|
12
+ if klass < SexpProcessor then
13
+
14
+ processor = klass.new
15
+ processors = klass.public_instance_methods(true).grep(/process_/)
16
+
17
+ if processor.strict then
18
+ puts "#{klass.name}:"
19
+ puts
20
+
21
+ # TODO: check unsupported against supported
22
+ processors = processors.map { |m| m[8..-1].intern } + processor.unsupported
23
+ unsupported = all_nodes - processors
24
+ p unsupported.sort_by { |sym| sym.to_s }
25
+ puts
26
+ end
27
+ end
28
+ end
@@ -25,7 +25,7 @@ require 'inline'
25
25
 
26
26
  class ParseTree
27
27
 
28
- VERSION = '1.4.1'
28
+ VERSION = '1.5.0'
29
29
 
30
30
  ##
31
31
  # Initializes a ParseTree instance. Includes newline nodes if
@@ -76,9 +76,13 @@ class ParseTree
76
76
  # protected methods are included in instance_methods, go figure!
77
77
 
78
78
  method_names.sort.each do |m|
79
- $stderr.puts "parse_tree_for_method(#{klass}, #{m}):" if $DEBUG
80
79
  code << parse_tree_for_method(klass, m.to_sym)
81
80
  end
81
+
82
+ klass.singleton_methods.sort.each do |m|
83
+ code << parse_tree_for_method(klass, m.to_sym, true)
84
+ end
85
+
82
86
  result << code
83
87
  end
84
88
  return result
@@ -91,8 +95,25 @@ class ParseTree
91
95
  #
92
96
  # [:defn, :name, :body]
93
97
 
94
- def parse_tree_for_method(klass, method)
95
- parse_tree_for_meth(klass, method.to_sym, @include_newlines)
98
+ def parse_tree_for_method(klass, method, is_cls_meth=false)
99
+ $stderr.puts "** parse_tree_for_method(#{klass}, #{method}):" if $DEBUG
100
+ r = parse_tree_for_meth(klass, method.to_sym, @include_newlines, is_cls_meth)
101
+ r[1] = :"self.#{r[1]}" if is_cls_meth
102
+ r
103
+ end
104
+
105
+ ##
106
+ # Returns the parse tree for a string +source+.
107
+ #
108
+ # Format:
109
+ #
110
+ # [[sexps] ... ]
111
+
112
+ def parse_tree_for_string(source, filename = nil, line = nil,
113
+ newlines = false)
114
+ filename ||= '(string)'
115
+ line ||= 1
116
+ return parse_tree_for_str(source, filename, line, newlines)
96
117
  end
97
118
 
98
119
  if RUBY_VERSION < "1.8.4" then
@@ -149,7 +170,6 @@ class ParseTree
149
170
  :newline, :postexe, :alloca, :dmethod, :bmethod,
150
171
  # 100
151
172
  :memo, :ifunc, :dsym, :attrasgn,
152
- # 104
153
173
  :last
154
174
  ]
155
175
 
@@ -171,11 +191,11 @@ class ParseTree
171
191
 
172
192
  inline do |builder|
173
193
  builder.add_type_converter("bool", '', '')
174
- builder.add_type_converter("VALUE", '', '')
175
194
  builder.add_type_converter("ID *", '', '')
176
195
  builder.add_type_converter("NODE *", '(NODE *)', '(VALUE)')
177
196
  builder.include '"intern.h"'
178
197
  builder.include '"version.h"'
198
+ builder.include '"rubysig.h"'
179
199
  builder.include '"node.h"'
180
200
  builder.include '"st.h"'
181
201
  builder.include '"env.h"'
@@ -238,7 +258,10 @@ class ParseTree
238
258
  struct BLOCK *outer;
239
259
  struct BLOCK *prev;
240
260
  };
241
- } unless RUBY_VERSION >= "1.9"
261
+ } unless RUBY_VERSION >= "1.9" # we got matz to add this to env.h
262
+
263
+ ##
264
+ # add_to_parse_tree(ary, node, include_newlines, local_variables)
242
265
 
243
266
  builder.c_raw %Q@
244
267
  static void add_to_parse_tree(VALUE ary,
@@ -400,7 +423,11 @@ again_no_block:
400
423
  // a = b rescue c
401
424
 
402
425
  case NODE_RESBODY:
403
- add_to_parse_tree(current, node->nd_3rd, newlines, locals);
426
+ if (node->nd_3rd) {
427
+ add_to_parse_tree(current, node->nd_3rd, newlines, locals);
428
+ } else {
429
+ rb_ary_push(current, Qnil);
430
+ }
404
431
  add_to_parse_tree(current, node->nd_2nd, newlines, locals);
405
432
  add_to_parse_tree(current, node->nd_1st, newlines, locals);
406
433
  break;
@@ -484,11 +511,36 @@ again_no_block:
484
511
  case NODE_OP_ASGN1:
485
512
  add_to_parse_tree(current, node->nd_recv, newlines, locals);
486
513
  add_to_parse_tree(current, node->nd_args->nd_next, newlines, locals);
514
+ switch (node->nd_mid) {
515
+ case 0:
516
+ rb_ary_push(current, ID2SYM(rb_intern("||")));
517
+ break;
518
+ case 1:
519
+ rb_ary_push(current, ID2SYM(rb_intern("&&")));
520
+ break;
521
+ default:
522
+ rb_ary_push(current, ID2SYM(node->nd_mid));
523
+ break;
524
+ }
487
525
  add_to_parse_tree(current, node->nd_args->nd_head, newlines, locals);
488
526
  break;
489
527
 
490
528
  case NODE_OP_ASGN2:
491
529
  add_to_parse_tree(current, node->nd_recv, newlines, locals);
530
+ rb_ary_push(current, ID2SYM(node->nd_next->nd_aid));
531
+
532
+ switch (node->nd_next->nd_mid) {
533
+ case 0:
534
+ rb_ary_push(current, ID2SYM(rb_intern("||")));
535
+ break;
536
+ case 1:
537
+ rb_ary_push(current, ID2SYM(rb_intern("&&")));
538
+ break;
539
+ default:
540
+ rb_ary_push(current, ID2SYM(node->nd_next->nd_mid));
541
+ break;
542
+ }
543
+
492
544
  add_to_parse_tree(current, node->nd_value, newlines, locals);
493
545
  break;
494
546
 
@@ -611,9 +663,11 @@ again_no_block:
611
663
  long arg_count = (long)node->nd_rest;
612
664
  if (locals && (node->nd_cnt || node->nd_opt || arg_count != -1)) {
613
665
  int i;
666
+ int max_args;
614
667
  NODE *optnode;
615
668
 
616
- for (i = 0; i < node->nd_cnt; i++) {
669
+ max_args = node->nd_cnt;
670
+ for (i = 0; i < max_args; i++) {
617
671
  // regular arg names
618
672
  rb_ary_push(current, ID2SYM(locals[i + 3]));
619
673
  }
@@ -736,6 +790,15 @@ again_no_block:
736
790
  rb_ary_push(current, INT2FIX(node->nd_argc));
737
791
  break;
738
792
 
793
+ #{if_version :<, "1.9", "#if 0"}
794
+ case NODE_ERRINFO:
795
+ case NODE_VALUES:
796
+ case NODE_PRELUDE:
797
+ case NODE_LAMBDA:
798
+ puts("no worky in 1.9 yet");
799
+ break;
800
+ #{if_version :<, "1.9", "#endif"}
801
+
739
802
  // Nodes we found but have yet to decypher
740
803
  // I think these are all runtime only... not positive but...
741
804
  case NODE_MEMO: // enum.c zip
@@ -768,14 +831,13 @@ again_no_block:
768
831
  @ # end of add_to_parse_tree block
769
832
 
770
833
  builder.c %Q{
771
- static VALUE parse_tree_for_meth(VALUE klass, VALUE method, VALUE newlines) {
834
+ static VALUE parse_tree_for_meth(VALUE klass, VALUE method, VALUE newlines, VALUE is_cls_meth) {
772
835
  VALUE n;
773
836
  NODE *node = NULL;
774
837
  ID id;
775
838
  VALUE result = rb_ary_new();
776
839
 
777
840
  (void) self; // quell warnings
778
- (void) argc; // quell warnings
779
841
 
780
842
  VALUE version = rb_const_get_at(rb_cObject,rb_intern("RUBY_VERSION"));
781
843
  if (strcmp(StringValuePtr(version), #{RUBY_VERSION.inspect})) {
@@ -783,6 +845,9 @@ static VALUE parse_tree_for_meth(VALUE klass, VALUE method, VALUE newlines) {
783
845
  }
784
846
 
785
847
  id = rb_to_id(method);
848
+ if (RTEST(is_cls_meth)) { // singleton method
849
+ klass = CLASS_OF(klass);
850
+ }
786
851
  if (st_lookup(RCLASS(klass)->m_tbl, id, &n)) {
787
852
  node = (NODE*)n;
788
853
  rb_ary_push(result, ID2SYM(rb_intern("defn")));
@@ -795,5 +860,53 @@ static VALUE parse_tree_for_meth(VALUE klass, VALUE method, VALUE newlines) {
795
860
  return result;
796
861
  }
797
862
  }
863
+
864
+ builder.prefix " extern NODE *ruby_eval_tree_begin; " \
865
+ if RUBY_VERSION < '1.9.0'
866
+
867
+ builder.c %Q{
868
+ static VALUE parse_tree_for_str(VALUE source, VALUE filename, VALUE line,
869
+ VALUE newlines) {
870
+ VALUE tmp;
871
+ VALUE result = rb_ary_new();
872
+ NODE *node = NULL;
873
+ int critical;
874
+
875
+ (void) self; // quell warnings
876
+
877
+ tmp = rb_check_string_type(filename);
878
+ if (NIL_P(tmp)) {
879
+ filename = rb_str_new2("(string)");
880
+ }
881
+
882
+ if (NIL_P(line)) {
883
+ line = LONG2FIX(1);
884
+ }
885
+
886
+ newlines = RTEST(newlines);
887
+
888
+ ruby_nerrs = 0;
889
+ StringValue(source);
890
+ critical = rb_thread_critical;
891
+ rb_thread_critical = Qtrue;
892
+ ruby_in_eval++;
893
+ node = rb_compile_string(StringValuePtr(filename), source, NUM2INT(line));
894
+ ruby_in_eval--;
895
+ rb_thread_critical = critical;
896
+
897
+ if (ruby_nerrs > 0) {
898
+ ruby_nerrs = 0;
899
+ #if RUBY_VERSION_CODE < 190
900
+ ruby_eval_tree_begin = 0;
901
+ #endif
902
+ rb_exc_raise(ruby_errinfo);
903
+ }
904
+
905
+ add_to_parse_tree(result, node, newlines, NULL);
906
+
907
+ return result;
908
+ }
909
+ }
910
+
798
911
  end # inline call
799
912
  end # ParseTree class
@@ -138,6 +138,7 @@ class SexpProcessor
138
138
  @auto_shift_type = false
139
139
  @strict = false
140
140
  @unsupported = []
141
+ @unsupported_checked = false
141
142
  @debug = {}
142
143
  @expected = Sexp
143
144
  @require_empty = true
@@ -167,6 +168,15 @@ class SexpProcessor
167
168
  def process(exp)
168
169
  return nil if exp.nil?
169
170
 
171
+ unless @unsupported_checked then
172
+ m = public_methods.grep(/^process_/) { |o| o.sub(/^process_/, '').intern }
173
+ supported = m - (m - @unsupported)
174
+
175
+ raise UnsupportedNodeError, "#{supported.inspect} shouldn't be in @unsupported" unless supported.empty?
176
+
177
+ @unsupported_checked = true
178
+ end
179
+
170
180
  result = self.expected.new
171
181
 
172
182
  type = exp.first
@@ -307,7 +317,7 @@ class SexpProcessor
307
317
  # end
308
318
 
309
319
  def process_dummy(exp)
310
- result = @expected.new(:dummy)
320
+ result = @expected.new(:dummy) rescue @expected.new
311
321
  until exp.empty? do
312
322
  result << self.process(exp.shift)
313
323
  end
@@ -0,0 +1,1378 @@
1
+ require 'test/unit/testcase'
2
+ require 'sexp_processor' # for deep_clone FIX
3
+ require 'typed_sexp'
4
+ require 'unique'
5
+
6
+ # TODO: str -> char * in ansi c
7
+ # TODO: add tests that mix types up to fuck up RubyC type checker
8
+
9
+ class R2CTestCase < Test::Unit::TestCase
10
+
11
+ attr_accessor :processor # to be defined by subclass
12
+
13
+ def self.testcase_order; @@testcase_order; end
14
+ def self.testcases; @@testcases; end
15
+
16
+ def setup
17
+ super
18
+ @processor = nil
19
+ Unique.reset
20
+ end
21
+
22
+ @@testcase_order = %w(Ruby ParseTree Rewriter TypeChecker CRewriter RubyToAnsiC RubyToRubyC)
23
+
24
+ @@testcases = {
25
+
26
+ "accessor" => {
27
+ "Ruby" => "attr_reader :accessor",
28
+ "ParseTree" => [:defn, :accessor, [:ivar, :@accessor]],
29
+ "Rewriter" => s(:defn, :accessor, s(:args),
30
+ s(:scope,
31
+ s(:block, s(:return, s(:ivar, :@accessor))))),
32
+ "TypeChecker" => :skip,
33
+ "CRewriter" => :skip,
34
+ "RubyToAnsiC" => :skip,
35
+ "RubyToRubyC" => :skip,
36
+ },
37
+
38
+ "accessor_equals" => {
39
+ "Ruby" => "attr_writer :accessor",
40
+ "ParseTree" => [:defn, :accessor=, [:attrset, :@accessor]],
41
+ "Rewriter" => s(:defn,
42
+ :accessor=,
43
+ s(:args, :arg),
44
+ s(:scope,
45
+ s(:block,
46
+ s(:return,
47
+ s(:iasgn, :@accessor, s(:lvar, :arg)))))),
48
+ "TypeChecker" => :skip,
49
+ "CRewriter" => :skip,
50
+ "RubyToRubyC" => :skip,
51
+ "RubyToAnsiC" => :skip,
52
+ },
53
+
54
+ "defn_bbegin" => {
55
+ "Ruby" => "def bbegin()
56
+ begin
57
+ 1 + 1
58
+ rescue SyntaxError
59
+ e1 = $!
60
+ 2
61
+ rescue Exception
62
+ e2 = $!
63
+ 3
64
+ else
65
+ 4end
66
+ ensure
67
+ 5
68
+ end",
69
+ "ParseTree" => [:defn, :bbegin,
70
+ [:scope,
71
+ [:block,
72
+ [:args],
73
+ [:begin,
74
+ [:ensure,
75
+ [:rescue,
76
+ [:call, [:lit, 1], :+, [:array, [:lit, 1]]],
77
+ [:resbody,
78
+ [:array, [:const, :SyntaxError]],
79
+ [:block, [:lasgn, :e1, [:gvar, :$!]], [:lit, 2]],
80
+ [:resbody,
81
+ [:array, [:const, :Exception]],
82
+ [:block, [:lasgn, :e2, [:gvar, :$!]], [:lit, 3]]]],
83
+ [:lit, 4]],
84
+ [:lit, 5]]]]]],
85
+ "Rewriter" => s(:defn, :bbegin,
86
+ s(:args),
87
+ s(:scope,
88
+ s(:block,
89
+ s(:begin,
90
+ s(:ensure,
91
+ s(:rescue,
92
+ s(:call, s(:lit, 1), :+, s(:arglist, s(:lit, 1))),
93
+ s(:resbody,
94
+ s(:array, s(:const, :SyntaxError)),
95
+ s(:block, s(:lasgn, :e1, s(:gvar, :$!)),
96
+ s(:lit, 2)),
97
+ s(:resbody,
98
+ s(:array, s(:const, :Exception)),
99
+ s(:block, s(:lasgn, :e2, s(:gvar, :$!)),
100
+ s(:lit, 3)))),
101
+ s(:lit, 4)),
102
+ s(:lit, 5)))))),
103
+ "TypeChecker" => t(:defn, :bbegin,
104
+ t(:args),
105
+ t(:scope,
106
+ t(:block,
107
+ t(:begin,
108
+ t(:ensure,
109
+ t(:rescue,
110
+ t(:call,
111
+ t(:lit, 1, Type.long),
112
+ :+,
113
+ t(:arglist, t(:lit, 1, Type.long)), Type.long),
114
+ t(:resbody,
115
+ t(:array, t(:const, :SyntaxError, Type.fucked)),
116
+ t(:block,
117
+ t(:lasgn, :e1, t(:gvar, :$!, Type.unknown),
118
+ Type.unknown),
119
+ t(:lit, 2, Type.long), Type.unknown),
120
+ t(:resbody,
121
+ t(:array, t(:const, :Exception, Type.fucked)),
122
+ t(:block,
123
+ t(:lasgn, :e2, t(:gvar, :$!, Type.unknown),
124
+ Type.unknown),
125
+ t(:lit, 3, Type.long), Type.unknown),
126
+ Type.unknown), Type.long),
127
+ t(:lit, 4, Type.long), Type.long),
128
+ t(:lit, 5, Type.long))), Type.unknown),
129
+ Type.void),
130
+ Type.function(Type.unknown, [], Type.void)),
131
+ "CRewriter" => :same,
132
+ "RubyToRubyC" => :unsupported,
133
+ "RubyToAnsiC" => :unsupported,
134
+ },
135
+
136
+ "bools" => {
137
+ "Ruby" => "def bools(arg1)
138
+ if (arg1.nil?)
139
+ return false
140
+ else
141
+ return true
142
+ end
143
+ end",
144
+ "ParseTree" => [:defn, :bools,
145
+ [:scope,
146
+ [:block,
147
+ [:args, :arg1],
148
+ [:if,
149
+ [:call, [:lvar, :arg1], "nil?".intern], # emacs is freakin'
150
+ [:return, [:false]],
151
+ [:return, [:true]]]]]],
152
+ "Rewriter" => s(:defn, :bools,
153
+ s(:args, :arg1),
154
+ s(:scope,
155
+ s(:block,
156
+ s(:if,
157
+ s(:call,
158
+ s(:lvar, :arg1),
159
+ :nil?,
160
+ nil),
161
+ s(:return, s(:false)),
162
+ s(:return, s(:true)))))),
163
+ # TODO: why does return false have type void?
164
+ "TypeChecker" => t(:defn, :bools,
165
+ t(:args, t(:arg1, Type.value)),
166
+ t(:scope,
167
+ t(:block,
168
+ t(:if,
169
+ t(:call,
170
+ t(:lvar, :arg1, Type.value),
171
+ :nil?,
172
+ nil,
173
+ Type.bool),
174
+ t(:return,
175
+ t(:false, Type.bool),
176
+ Type.void),
177
+ t(:return,
178
+ t(:true, Type.bool),
179
+ Type.void),
180
+ Type.void),
181
+ Type.unknown),
182
+ Type.void),
183
+ Type.function(Type.unknown, [Type.value], Type.bool)),
184
+ "CRewriter" => :same,
185
+ "RubyToRubyC" => "static VALUE\nrrc_c_bools(VALUE self, VALUE arg1) {\nif (NIL_P(arg1)) {\nreturn Qfalse;\n} else {\nreturn Qtrue;\n}\n}",
186
+ "RubyToAnsiC" => "bool\nbools(void * arg1) {\nif (arg1) {\nreturn 0;\n} else {\nreturn 1;\n}\n}",
187
+ },
188
+
189
+ # TODO: move all call tests here
190
+ "call_arglist" => {
191
+ "Ruby" => "puts(42)",
192
+ "ParseTree" => [:fcall, :puts, [:array, [:lit, 42]]],
193
+ "Rewriter" => s(:call, nil, :puts, s(:arglist, s(:lit, 42))),
194
+ "TypeChecker" => :skip,
195
+ "CRewriter" => :skip,
196
+ "RubyToRubyC" => :skip,
197
+ "RubyToAnsiC" => :skip,
198
+ },
199
+
200
+ "call_attrasgn" => {
201
+ "Ruby" => "42.method=(y)",
202
+ "ParseTree" => [:attrasgn, [:lit, 42], :method=, [:array, [:lvar, :y]]],
203
+ "Rewriter" => s(:call, s(:lit, 42), :method=, s(:arglist, s(:lvar, :y))),
204
+ "TypeChecker" => :skip,
205
+ "CRewriter" => :skip,
206
+ "RubyToRubyC" => :skip,
207
+ "RubyToAnsiC" => :skip,
208
+ },
209
+
210
+ "call_self" => {
211
+ "Ruby" => "self.method",
212
+ "ParseTree" => [:call, [:self], :method],
213
+ "Rewriter" => s(:call, s(:lvar, :self), :method, nil),
214
+ "TypeChecker" => :skip,
215
+ "CRewriter" => :skip,
216
+ "RubyToRubyC" => :skip,
217
+ "RubyToAnsiC" => :skip,
218
+ },
219
+
220
+ "case_stmt" => {
221
+ "Ruby" => "
222
+ def case_stmt()
223
+ var = 2
224
+ result = \"\"
225
+ case var
226
+ when 1
227
+ puts(\"something\")
228
+ result = \"red\"
229
+ when 2, 3
230
+ result = \"yellow\"
231
+ when 4
232
+ else
233
+ result = \"green\"
234
+ end
235
+ case result
236
+ when \"red\"
237
+ var = 1
238
+ when \"yellow\"
239
+ var = 2
240
+ when \"green\"
241
+ var = 3
242
+ else
243
+ end
244
+ return result
245
+ end",
246
+ "ParseTree" => [:defn, :case_stmt,
247
+ [:scope,
248
+ [:block,
249
+ [:args],
250
+ [:lasgn, :var, [:lit, 2]],
251
+ [:lasgn, :result, [:str, ""]],
252
+ [:case,
253
+ [:lvar, :var],
254
+ [:when,
255
+ [:array, [:lit, 1]],
256
+ [:block,
257
+ [:fcall, :puts, [:array, [:str, "something"]]],
258
+ [:lasgn, :result, [:str, "red"]]]],
259
+ [:when,
260
+ [:array, [:lit, 2], [:lit, 3]],
261
+ [:lasgn, :result, [:str, "yellow"]]],
262
+ [:when, [:array, [:lit, 4]], nil],
263
+ [:lasgn, :result, [:str, "green"]]],
264
+ [:case,
265
+ [:lvar, :result],
266
+ [:when, [:array, [:str, "red"]], [:lasgn, :var, [:lit, 1]]],
267
+ [:when, [:array, [:str, "yellow"]], [:lasgn, :var, [:lit, 2]]],
268
+ [:when, [:array, [:str, "green"]], [:lasgn, :var, [:lit, 3]]],
269
+ nil],
270
+ [:return, [:lvar, :result]]]]],
271
+ "Rewriter" => s(:defn, :case_stmt,
272
+ s(:args),
273
+ s(:scope,
274
+ s(:block,
275
+ s(:lasgn, :var, s(:lit, 2)),
276
+ s(:lasgn, :result, s(:str, "")),
277
+ s(:if,
278
+ s(:call,
279
+ s(:lvar, :var),
280
+ :===,
281
+ s(:arglist, s(:lit, 1))),
282
+ s(:block,
283
+ s(:call,
284
+ nil,
285
+ :puts,
286
+ s(:arglist, s(:str, "something"))),
287
+ s(:lasgn, :result, s(:str, "red"))),
288
+ s(:if,
289
+ s(:or,
290
+ s(:call,
291
+ s(:lvar, :var),
292
+ :===,
293
+ s(:arglist, s(:lit, 2))),
294
+ s(:call,
295
+ s(:lvar, :var),
296
+ :===,
297
+ s(:arglist, s(:lit, 3)))),
298
+ s(:lasgn, :result, s(:str, "yellow")),
299
+ s(:if,
300
+ s(:call,
301
+ s(:lvar, :var),
302
+ :===,
303
+ s(:arglist, s(:lit, 4))),
304
+ nil,
305
+ s(:lasgn, :result, s(:str, "green"))))),
306
+ s(:if,
307
+ s(:call,
308
+ s(:lvar, :result),
309
+ :===,
310
+ s(:arglist, s(:str, "red"))),
311
+ s(:lasgn, :var, s(:lit, 1)),
312
+ s(:if,
313
+ s(:call,
314
+ s(:lvar, :result),
315
+ :===,
316
+ s(:arglist, s(:str, "yellow"))),
317
+ s(:lasgn, :var, s(:lit, 2)),
318
+ s(:if,
319
+ s(:call,
320
+ s(:lvar, :result),
321
+ :===,
322
+ s(:arglist, s(:str, "green"))),
323
+ s(:lasgn, :var, s(:lit, 3)),
324
+ nil))),
325
+ s(:return, s(:lvar, :result))))),
326
+ "TypeChecker" => t(:defn, :case_stmt,
327
+ t(:args),
328
+ t(:scope,
329
+ t(:block,
330
+ t(:lasgn,
331
+ :var,
332
+ t(:lit, 2, Type.long),
333
+ Type.long),
334
+ t(:lasgn,
335
+ :result,
336
+ t(:str, "", Type.str),
337
+ Type.str),
338
+ t(:if,
339
+ t(:call,
340
+ t(:lvar, :var, Type.long),
341
+ :case_equal_long,
342
+ t(:arglist, t(:lit, 1, Type.long)),
343
+ Type.bool),
344
+ t(:block,
345
+ t(:call,
346
+ nil,
347
+ :puts,
348
+ t(:arglist,
349
+ t(:str, "something", Type.str)),
350
+ Type.void),
351
+ t(:lasgn,
352
+ :result,
353
+ t(:str, "red", Type.str),
354
+ Type.str),
355
+ Type.str),
356
+ t(:if,
357
+ t(:or,
358
+ t(:call,
359
+ t(:lvar, :var, Type.long),
360
+ :case_equal_long,
361
+ t(:arglist, t(:lit, 2, Type.long)),
362
+ Type.bool),
363
+ t(:call,
364
+ t(:lvar, :var, Type.long),
365
+ :case_equal_long,
366
+ t(:arglist, t(:lit, 3, Type.long)),
367
+ Type.bool),
368
+ Type.bool),
369
+ t(:lasgn,
370
+ :result,
371
+ t(:str, "yellow", Type.str),
372
+ Type.str),
373
+ t(:if,
374
+ t(:call,
375
+ t(:lvar, :var, Type.long),
376
+ :case_equal_long,
377
+ t(:arglist, t(:lit, 4, Type.long)),
378
+ Type.bool),
379
+ nil,
380
+ t(:lasgn,
381
+ :result,
382
+ t(:str, "green", Type.str),
383
+ Type.str),
384
+ Type.str),
385
+ Type.str),
386
+ Type.str),
387
+ t(:if,
388
+ t(:call,
389
+ t(:lvar, :result, Type.str),
390
+ :case_equal_str,
391
+ t(:arglist, t(:str, "red", Type.str)),
392
+ Type.bool),
393
+ t(:lasgn, :var, t(:lit, 1, Type.long), Type.long),
394
+ t(:if,
395
+ t(:call,
396
+ t(:lvar, :result, Type.str),
397
+ :case_equal_str,
398
+ t(:arglist, t(:str, "yellow", Type.str)),
399
+ Type.bool),
400
+ t(:lasgn, :var, t(:lit, 2, Type.long), Type.long),
401
+ t(:if,
402
+ t(:call,
403
+ t(:lvar, :result, Type.str),
404
+ :case_equal_str,
405
+ t(:arglist,
406
+ t(:str, "green", Type.str)),
407
+ Type.bool),
408
+ t(:lasgn,
409
+ :var,
410
+ t(:lit, 3, Type.long),
411
+ Type.long),
412
+ nil,
413
+ Type.long),
414
+ Type.long),
415
+ Type.long),
416
+ t(:return,
417
+ t(:lvar, :result, Type.str),
418
+ Type.void),
419
+ Type.unknown),
420
+ Type.void),
421
+ Type.function(Type.unknown, [], Type.str)),
422
+ "CRewriter" => :same,
423
+ # HACK: I don't like the semis after the if blocks, but it is a compromise right now
424
+ "RubyToRubyC" => "static VALUE
425
+ rrc_c_case_stmt(VALUE self) {
426
+ VALUE result;
427
+ VALUE var;
428
+ var = LONG2NUM(2);
429
+ result = rb_str_new2(\"\");
430
+ if (rb_funcall(var, rb_intern(\"===\"), 1, LONG2NUM(1))) {
431
+ rb_funcall(self, rb_intern(\"puts\"), 1, rb_str_new2(\"something\"));
432
+ result = rb_str_new2(\"red\");
433
+ } else {
434
+ if (rb_funcall(var, rb_intern(\"===\"), 1, LONG2NUM(2)) || rb_funcall(var, rb_intern(\"===\"), 1, LONG2NUM(3))) {
435
+ result = rb_str_new2(\"yellow\");
436
+ } else {
437
+ if (rb_funcall(var, rb_intern(\"===\"), 1, LONG2NUM(4))) {
438
+ ;
439
+ } else {
440
+ result = rb_str_new2(\"green\");
441
+ }
442
+ }
443
+ };
444
+ if (rb_funcall(result, rb_intern(\"===\"), 1, rb_str_new2(\"red\"))) {
445
+ var = LONG2NUM(1);
446
+ } else {
447
+ if (rb_funcall(result, rb_intern(\"===\"), 1, rb_str_new2(\"yellow\"))) {
448
+ var = LONG2NUM(2);
449
+ } else {
450
+ if (rb_funcall(result, rb_intern(\"===\"), 1, rb_str_new2(\"green\"))) {
451
+ var = LONG2NUM(3);
452
+ }
453
+ }
454
+ };
455
+ return result;
456
+ }",
457
+ "RubyToAnsiC" => "str
458
+ case_stmt() {
459
+ str result;
460
+ long var;
461
+ var = 2;
462
+ result = \"\";
463
+ if (case_equal_long(var, 1)) {
464
+ puts(\"something\");
465
+ result = \"red\";
466
+ } else {
467
+ if (case_equal_long(var, 2) || case_equal_long(var, 3)) {
468
+ result = \"yellow\";
469
+ } else {
470
+ if (case_equal_long(var, 4)) {
471
+ ;
472
+ } else {
473
+ result = \"green\";
474
+ }
475
+ }
476
+ };
477
+ if (case_equal_str(result, \"red\")) {
478
+ var = 1;
479
+ } else {
480
+ if (case_equal_str(result, \"yellow\")) {
481
+ var = 2;
482
+ } else {
483
+ if (case_equal_str(result, \"green\")) {
484
+ var = 3;
485
+ }
486
+ }
487
+ };
488
+ return result;
489
+ }",
490
+ },
491
+
492
+ "conditional1" => {
493
+ "Ruby" => "if (42 == 0)\n return 1\n\nend",
494
+ "ParseTree" => [:if, [:call, [:lit, 42], :==, [:array, [:lit, 0]]], [:return, [:lit, 1]], nil],
495
+ "Rewriter" => s(:if, s(:call, s(:lit, 42), :==, s(:arglist, s(:lit, 0))), s(:return, s(:lit, 1)), nil),
496
+ "TypeChecker" => t(:if,
497
+ t(:call, t(:lit, 42, Type.long), :==,
498
+ t(:arglist, t(:lit, 0, Type.long)),
499
+ Type.bool),
500
+ t(:return, t(:lit, 1, Type.long), Type.void),
501
+ nil,
502
+ Type.void),
503
+ "CRewriter" => t(:if,
504
+ t(:call, t(:lit, 42, Type.long), :==,
505
+ t(:arglist, t(:lit, 0, Type.long)),
506
+ Type.bool),
507
+ t(:return, t(:lit, 1, Type.long), Type.void),
508
+ nil,
509
+ Type.void),
510
+ "RubyToRubyC" => "if (rb_funcall(LONG2NUM(42), rb_intern(\"==\"), 1, LONG2NUM(0))) {\nreturn LONG2NUM(1);\n}",
511
+ "RubyToAnsiC" => "if (42 == 0) {\nreturn 1;\n}",
512
+ },
513
+
514
+ "conditional2" => {
515
+ "Ruby" => "unless (42 == 0)\n return 2\nend",
516
+ "ParseTree" => [:if, [:call, [:lit, 42], :==, [:array, [:lit, 0]]], nil, [:return, [:lit, 2]]],
517
+ "Rewriter" => s(:if,
518
+ s(:call, s(:lit, 42),
519
+ :==, s(:arglist, s(:lit, 0))),
520
+ nil,
521
+ s(:return, s(:lit, 2))),
522
+ "TypeChecker" => t(:if,
523
+ t(:call,
524
+ t(:lit, 42, Type.long),
525
+ :==,
526
+ t(:arglist,
527
+ t(:lit, 0, Type.long)),
528
+ Type.bool),
529
+ nil,
530
+ t(:return, t(:lit, 2, Type.long), Type.void),
531
+ Type.void),
532
+ "CRewriter" => :same,
533
+ "RubyToRubyC" => "if (rb_funcall(LONG2NUM(42), rb_intern(\"==\"), 1, LONG2NUM(0))) {\n;\n} else {\nreturn LONG2NUM(2);\n}",
534
+ "RubyToAnsiC" => "if (42 == 0) {\n;\n} else {\nreturn 2;\n}",
535
+ },
536
+
537
+ "conditional3" => {
538
+ "Ruby" => "if (42 == 0)\n return 3\nelse\n return 4\nend",
539
+ "ParseTree" => [:if, [:call, [:lit, 42], :==, [:array, [:lit, 0]]],
540
+ [:return, [:lit, 3]],
541
+ [:return, [:lit, 4]]],
542
+ "Rewriter" => s(:if,
543
+ s(:call,
544
+ s(:lit, 42),
545
+ :==,
546
+ s(:arglist, s(:lit, 0))),
547
+ s(:return, s(:lit, 3)),
548
+ s(:return, s(:lit, 4))),
549
+ "TypeChecker" => t(:if,
550
+ t(:call,
551
+ t(:lit, 42, Type.long),
552
+ :==,
553
+ t(:arglist,
554
+ t(:lit, 0, Type.long)),
555
+ Type.bool),
556
+ t(:return,
557
+ t(:lit, 3, Type.long),
558
+
559
+ Type.void),
560
+ t(:return,
561
+ t(:lit, 4, Type.long),
562
+ Type.void),
563
+ Type.void),
564
+ "CRewriter" => :same,
565
+ "RubyToRubyC" => "if (rb_funcall(LONG2NUM(42), rb_intern(\"==\"), 1, LONG2NUM(0))) {\nreturn LONG2NUM(3);\n} else {\nreturn LONG2NUM(4);\n}",
566
+ "RubyToAnsiC" => "if (42 == 0) {\nreturn 3;\n} else {\nreturn 4;\n}",
567
+ },
568
+
569
+ "conditional4" => {
570
+ "Ruby" => "if (42 == 0)
571
+ return 2
572
+ else
573
+ if (42 < 0)
574
+ return 3
575
+ else
576
+ return 4
577
+ end
578
+ end",
579
+ "ParseTree" => [:if,
580
+ [:call, [:lit, 42], :==, [:array, [:lit, 0]]],
581
+ [:return, [:lit, 2]],
582
+ [:if,
583
+ [:call, [:lit, 42], :<, [:array, [:lit, 0]]],
584
+ [:return, [:lit, 3]],
585
+ [:return, [:lit, 4]]]],
586
+ "Rewriter" => s(:if,
587
+ s(:call,
588
+ s(:lit, 42),
589
+ :==,
590
+ s(:arglist, s(:lit, 0))),
591
+ s(:return, s(:lit, 2)),
592
+ s(:if,
593
+ s(:call,
594
+ s(:lit, 42),
595
+ :<,
596
+ s(:arglist, s(:lit, 0))),
597
+ s(:return, s(:lit, 3)),
598
+ s(:return, s(:lit, 4)))),
599
+ "TypeChecker" => t(:if,
600
+ t(:call,
601
+ t(:lit, 42, Type.long),
602
+ :==,
603
+ t(:arglist,
604
+ t(:lit, 0, Type.long)),
605
+ Type.bool),
606
+ t(:return,
607
+ t(:lit, 2, Type.long),
608
+ Type.void),
609
+ t(:if,
610
+ t(:call,
611
+ t(:lit, 42, Type.long),
612
+ :<,
613
+ t(:arglist,
614
+ t(:lit, 0, Type.long)),
615
+ Type.bool),
616
+ t(:return,
617
+ t(:lit, 3, Type.long),
618
+ Type.void),
619
+ t(:return,
620
+ t(:lit, 4, Type.long),
621
+ Type.void),
622
+ Type.void),
623
+ Type.void),
624
+ "CRewriter" => :same,
625
+ "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}",
626
+ "RubyToAnsiC" => "if (42 == 0) {\nreturn 2;\n} else {\nif (42 < 0) {\nreturn 3;\n} else {\nreturn 4;\n}\n}",
627
+ },
628
+
629
+ "defn_bmethod_added" => {
630
+ "Ruby" => "def bmethod_added(x)\n x + 1\nend",
631
+ "ParseTree" => [:defn, :bmethod_added,
632
+ [:bmethod,
633
+ [:dasgn_curr, :x],
634
+ [:call, [:dvar, :x], :+, [:array, [:lit, 1]]]]],
635
+ "Rewriter" => s(:defn,
636
+ :bmethod_added,
637
+ s(:args, :x),
638
+ s(:scope,
639
+ s(:block,
640
+ s(:call, s(:lvar, :x), :+, s(:arglist, s(:lit, 1)))))),
641
+ "TypeChecker" => :skip,
642
+ "CRewriter" => :skip,
643
+ "RubyToRubyC" => :skip,
644
+ "RubyToAnsiC" => :skip,
645
+ },
646
+
647
+ "defn_empty" => {
648
+ "Ruby" => "def empty()\n nil\nend",
649
+ "ParseTree" => [:defn, :empty, [:scope, [:block, [:args], [:nil]]]],
650
+ "Rewriter" => s(:defn, :empty,
651
+ s(:args), s(:scope, s(:block, s(:nil)))),
652
+ "TypeChecker" => t(:defn, :empty,
653
+ t(:args),
654
+ t(:scope,
655
+ t(:block,
656
+ t(:nil, Type.value),
657
+ Type.unknown),
658
+ Type.void),
659
+ Type.function(Type.unknown, [], Type.void)),
660
+ "CRewriter" => :same,
661
+ "RubyToRubyC" => "static VALUE\nrrc_c_empty(VALUE self) {\nQnil;\n}",
662
+ "RubyToAnsiC" => "void\nempty() {\nNULL;\n}",
663
+ },
664
+
665
+ "defn_zarray" => {
666
+ "Ruby" => "def empty()\n a = []\n return a\nend",
667
+ "ParseTree" => [:defn, :empty, [:scope, [:block, [:args], [:lasgn, :a, [:zarray]], [:return, [:lvar, :a]]]]],
668
+ "Rewriter" => s(:defn,
669
+ :empty,
670
+ s(:args),
671
+ s(:scope, s(:block, s(:lasgn, :a, s(:array)), s(:return, s(:lvar, :a))))),
672
+ "TypeChecker" => t(:defn,
673
+ :empty,
674
+ t(:args),
675
+ t(:scope,
676
+ t(:block,
677
+ t(:lasgn, :a, t(:array), Type.unknown_list),
678
+ t(:return,
679
+ t(:lvar,
680
+ :a, Type.unknown_list), Type.void),
681
+ Type.unknown), Type.void),
682
+ Type.function(Type.unknown, [], Type.unknown_list)),
683
+ "CRewriter" => :same,
684
+ "RubyToRubyC" => "static VALUE\nrrc_c_empty(VALUE self) {\nVALUE a;\na = rb_ary_new2(0);\nreturn a;\n}",
685
+ "RubyToAnsiC" => "void *\nempty() {\nvoid * a;\na = (void *) malloc(sizeof(void *) * 0);\nreturn a;\n}",
686
+ },
687
+
688
+ "defn_or" => {
689
+ "Ruby" => "def |()\n nil\nend",
690
+ "ParseTree" => [:defn, :|, [:scope, [:block, [:args], [:nil]]]],
691
+ "Rewriter" => s(:defn, :|,
692
+ s(:args), s(:scope, s(:block, s(:nil)))),
693
+ "TypeChecker" => t(:defn, :|,
694
+ t(:args),
695
+ t(:scope,
696
+ t(:block,
697
+ t(:nil, Type.value),
698
+ Type.unknown),
699
+ Type.void),
700
+ Type.function(Type.unknown, [], Type.void)),
701
+ "CRewriter" => :same,
702
+ "RubyToRubyC" => "static VALUE\nrrc_c_or(VALUE self) {\nQnil;\n}",
703
+ "RubyToAnsiC" => "void\nor() {\nNULL;\n}",
704
+ },
705
+
706
+ "defn_is_something" => {
707
+ "Ruby" => "def something?()\n nil\nend",
708
+ "ParseTree" => [:defn, :something?, [:scope, [:block, [:args], [:nil]]]],
709
+ "Rewriter" => s(:defn, :something?,
710
+ s(:args), s(:scope, s(:block, s(:nil)))),
711
+ "TypeChecker" => t(:defn, :something?,
712
+ t(:args),
713
+ t(:scope,
714
+ t(:block,
715
+ t(:nil, Type.value),
716
+ Type.unknown),
717
+ Type.void),
718
+ Type.function(Type.unknown, [], Type.void)),
719
+ "CRewriter" => :same,
720
+ "RubyToRubyC" => "static VALUE\nrrc_c_is_something(VALUE self) {\nQnil;\n}",
721
+ "RubyToAnsiC" => "void\nis_something() {\nNULL;\n}",
722
+ },
723
+
724
+ "defn_fbody" => {
725
+ "Ruby" => "def aliased()\n puts(42)\nend",
726
+ "ParseTree" => [:defn, :aliased,
727
+ [:fbody,
728
+ [:scope,
729
+ [:block,
730
+ [:args],
731
+ [:fcall, :puts, [:array, [:lit, 42]]]]]]],
732
+ "Rewriter" => s(:defn, :aliased,
733
+ s(:args),
734
+ s(:scope,
735
+ s(:block,
736
+ s(:call, nil, :puts, s(:arglist, s(:lit, 42)))))),
737
+ "TypeChecker" => :skip,
738
+ "CRewriter" => :skip,
739
+ "RubyToRubyC" => :skip,
740
+ "RubyToAnsiC" => :skip,
741
+ },
742
+
743
+ "defn_optargs" => {
744
+ "Ruby" => "def x(a, *args)\n p(a, args)\nend",
745
+ "ParseTree" => [:defn, :x,
746
+ [:scope,
747
+ [:block,
748
+ [:args, :a, :"*args"],
749
+ [:fcall, :p,
750
+ [:array, [:lvar, :a], [:lvar, :args]]]]]],
751
+ "Rewriter" => s(:defn, :x,
752
+ s(:args, :a, :"*args"),
753
+ s(:scope,
754
+ s(:block,
755
+ s(:call, nil, :p,
756
+ s(:arglist, s(:lvar, :a), s(:lvar, :args)))))),
757
+ "TypeChecker" => :skip,
758
+ "CRewriter" => :skip,
759
+ "RubyToRubyC" => :skip,
760
+ "RubyToAnsiC" => :skip,
761
+ },
762
+
763
+ "dmethod_added" => {
764
+ "Ruby" => "def dmethod_added\n define_method(:bmethod_added)\n x {|(x + 1)|\n }end",
765
+ "ParseTree" => [:defn,
766
+ :dmethod_added,
767
+ [:dmethod,
768
+ :bmethod_maker,
769
+ [:scope,
770
+ [:block,
771
+ [:args],
772
+ [:iter,
773
+ [:fcall, :define_method, [:array, [:lit, :bmethod_added]]],
774
+ [:dasgn_curr, :x],
775
+ [:call, [:dvar, :x], :+, [:array, [:lit, 1]]]]]]]],
776
+ "Rewriter" => s(:defn,
777
+ :dmethod_added,
778
+ s(:args, :x),
779
+ s(:scope,
780
+ s(:block,
781
+ s(:call, s(:lvar, :x), :+,
782
+ s(:arglist, s(:lit, 1)))))),
783
+ "TypeChecker" => :skip,
784
+ "CRewriter" => :skip,
785
+ "RubyToRubyC" => :skip,
786
+ "RubyToAnsiC" => :skip,
787
+ },
788
+
789
+ "global" => {
790
+ "Ruby" => "$stderr",
791
+ "ParseTree" => [:gvar, :$stderr],
792
+ "Rewriter" => s(:gvar, :$stderr),
793
+ # TODO: test s(:gvar, :$stderr) != t(:gvar, $stderr, Type.file)
794
+ "TypeChecker" => t(:gvar, :$stderr, Type.file),
795
+ "CRewriter" => :same,
796
+ "RubyToRubyC" => "rb_gv_get(\"$stderr\")",
797
+ "RubyToAnsiC" => "stderr",
798
+ },
799
+
800
+ "interpolated" => {
801
+ "Ruby" => "\"var is \#{argl}. So there.\"",
802
+ "ParseTree" => [:dstr,
803
+ "var is ", [:lvar, :argl], [:str, ". So there."]],
804
+ "Rewriter" => s(:dstr,
805
+ "var is ", s(:lvar, :argl), s(:str, ". So there.")),
806
+ "TypeChecker" => t(:dstr,
807
+ "var is ",
808
+ t(:lvar, :argl, Type.long),
809
+ t(:str, ". So there.", Type.str),
810
+ Type.str),
811
+ "CRewriter" => :same,
812
+ "RubyToRubyC" => "rb_funcall(rb_mKernel, rb_intern(\"sprintf\"), 4, rb_str_new2(\"%s%s%s\"), rb_str_new2(\"var is \"), argl, rb_str_new2(\". So there.\"))",
813
+ "RubyToAnsiC" => :unsupported,
814
+ },
815
+
816
+ "iter" => {
817
+ "Ruby" => "loop do end",
818
+ "ParseTree" => [:iter, [:fcall, :loop], nil],
819
+ "Rewriter" => s(:iter,
820
+ s(:call, nil, :loop, nil),
821
+ s(:dasgn_curr, :temp_1),
822
+ nil),
823
+ "TypeChecker" => t(:iter,
824
+ t(:call, nil, :loop, nil, Type.unknown),
825
+ t(:dasgn_curr, :temp_1, Type.unknown),
826
+ nil,
827
+ Type.unknown),
828
+ "CRewriter" => [:defx,
829
+ t(:iter,
830
+ t(:call, nil, :loop, nil, Type.unknown),
831
+ t(:args,
832
+ t(:array, t(:dasgn_curr, :temp_1, Type.unknown), Type.void),
833
+ t(:array, Type.void), Type.void),
834
+ t(:call, nil,
835
+ :temp_2,
836
+ s(:arglist, s(:dasgn_curr, :temp_1, Type.unknown),
837
+ t(:nil)))),
838
+ [t(:defx,
839
+ :temp_2,
840
+ t(:args, :temp_2, :temp_3),
841
+ t(:scope, t(:block, nil)), Type.void)]],
842
+ "RubyToRubyC" => "",
843
+ "RubyToAnsiC" => "",
844
+ },
845
+
846
+ "iteration2" => {
847
+ "ParseTree" => [:iter,
848
+ [:call, [:lvar, :arrays], :each],
849
+ [:dasgn_curr, :x],
850
+ [:fcall, :puts, [:arrays, [:dvar, :x]]]],
851
+ "Rewriter" => s(:iter,
852
+ s(:call, s(:lvar, :arrays), :each, nil),
853
+ s(:dasgn_curr, :x),
854
+ s(:call, nil, :puts, s(:arglist, s(:dvar, :x)))),
855
+ "TypeChecker" => t(:iter,
856
+ t(:call,
857
+ t(:lvar, :arrays, Type.str_list),
858
+ :each,
859
+ nil, Type.unknown),
860
+ t(:dasgn_curr, :x, Type.str),
861
+ t(:call, nil, :puts,
862
+ t(:arglist, t(:dvar, :x, Type.str)),
863
+ Type.void),
864
+ Type.void),
865
+ "CRewriter" => [:defx,
866
+ t(:iter,
867
+ t(:call,
868
+ t(:lvar, :arrays, Type.str_list),
869
+ :each,
870
+ nil, Type.unknown),
871
+ t(:args,
872
+ t(:array, t(:dasgn_curr, :x, Type.str), Type.void),
873
+ t(:array, t(:lvar, :arrays, Type.value), Type.void), Type.void),
874
+ t(:call, nil, :temp_1, t(:arglist, t(:dasgn_curr, :x, Type.str), t(:nil)))),
875
+ [t(:defx,
876
+ :temp_1,
877
+ t(:args, :temp_2, :temp_3),
878
+ t(:scope,
879
+ t(:block,
880
+ t(:call,
881
+ nil,
882
+ :puts,
883
+ t(:arglist, t(:dvar, :x, Type.str)), Type.void))), Type.void)]],
884
+ "RubyToRubyC" => 'unsigned long index_temp_1;
885
+ VALUE temp_2 = rb_funcall(arrays, rb_intern("to_a"), 0);
886
+ unsigned long temp_1_max = FIX2LONG(rb_funcall(temp_2, rb_intern("size"), 0));
887
+ for (index_temp_1 = 0; index_temp_1 < temp_1_max; ++index_temp_1) {
888
+ VALUE x;
889
+ x = rb_funcall(temp_2, rb_intern("at"), 1, LONG2FIX(index_temp_1));
890
+ rb_funcall(self, rb_intern("puts"), 1, x);
891
+ }",
892
+ "RubyToAnsiC" => "unsigned long index_x;
893
+ for (index_x = 0; arrays[index_x] != NULL; ++index_x) {
894
+ str x = arrays[index_x];
895
+ puts(x);
896
+ }',
897
+ },
898
+
899
+
900
+ "iteration4" => {
901
+ "Ruby" => "1.upto(3) {|n|\n puts(n.to_s)\n}",
902
+ "ParseTree" => [:iter,
903
+ [:call, [:lit, 1], :upto, [:array, [:lit, 3]]],
904
+ [:dasgn_curr, :n],
905
+ [:fcall, :puts, [:array, [:call, [:dvar, :n], :to_s]]]],
906
+ "Rewriter" => s(:dummy,
907
+ s(:lasgn, :n, s(:lit, 1)),
908
+ s(:while,
909
+ s(:call, s(:lvar, :n), :<=, s(:arglist, s(:lit, 3))),
910
+ s(:block,
911
+ s(:call,
912
+ nil,
913
+ :puts,
914
+ s(:arglist, s(:call, s(:lvar, :n), :to_s, nil))),
915
+ s(:lasgn, :n,
916
+ s(:call, s(:lvar, :n),
917
+ :+,
918
+ s(:arglist, s(:lit, 1))))), true)),
919
+ "TypeChecker" => t(:dummy, t(:lasgn, :n, t(:lit, 1, Type.long), Type.long),
920
+ t(:while,
921
+ t(:call,
922
+ t(:lvar, :n, Type.long),
923
+ :<=,
924
+ t(:arglist, t(:lit, 3, Type.long)), Type.bool),
925
+ t(:block,
926
+ t(:call, nil, :puts,
927
+ t(:arglist,
928
+ t(:call,
929
+ t(:lvar, :n, Type.long),
930
+ :to_s,
931
+ nil, Type.str)), Type.void),
932
+ t(:lasgn, :n,
933
+ t(:call,
934
+ t(:lvar, :n, Type.long),
935
+ :+,
936
+ t(:arglist,
937
+ t(:lit,
938
+ 1, Type.long)),
939
+ Type.long), Type.long), Type.unknown), true)),
940
+ "CRewriter" => :same,
941
+ "RubyToRubyC" => '',
942
+ "RubyToAnsiC" => 'n = 1;
943
+ while (n <= 3) {
944
+ puts(to_s(n));
945
+ n = n + 1;
946
+ }',
947
+ },
948
+
949
+ "iteration5" => {
950
+ "Ruby" => "3.downto(1) {|n|\n puts(n.to_s)\n}",
951
+ "ParseTree" => [:iter,
952
+ [:call, [:lit, 3], :downto, [:array, [:lit, 1]]],
953
+ [:dasgn_curr, :n],
954
+ [:fcall, :puts, [:array, [:call, [:dvar, :n], :to_s]]]],
955
+ "Rewriter" => s(:dummy, s(:lasgn, :n, s(:lit, 3)), s(:while,
956
+ s(:call, s(:lvar, :n), :>=, s(:arglist, s(:lit, 1))),
957
+ s(:block,
958
+ s(:call, nil, :puts,
959
+ s(:arglist, s(:call, s(:lvar, :n), :to_s, nil))),
960
+ s(:lasgn, :n, s(:call, s(:lvar, :n),
961
+ :-, s(:arglist, s(:lit, 1))))), true)),
962
+ "TypeChecker" => t(:dummy,
963
+ t(:lasgn, :n, t(:lit, 3, Type.long), Type.long),
964
+ t(:while,
965
+ t(:call,
966
+ t(:lvar, :n, Type.long),
967
+ :>=,
968
+ t(:arglist, t(:lit, 1, Type.long)), Type.bool),
969
+ t(:block,
970
+ t(:call, nil, :puts,
971
+ t(:arglist,
972
+ t(:call,
973
+ t(:lvar, :n, Type.long),
974
+ :to_s,
975
+ nil, Type.str)), Type.void),
976
+ t(:lasgn, :n,
977
+ t(:call,
978
+ t(:lvar, :n, Type.long),
979
+ :-,
980
+ t(:arglist, t(:lit, 1, Type.long)),
981
+ Type.long),
982
+ Type.long),
983
+ Type.unknown), true)),
984
+ "CRewriter" => :same,
985
+ "RubyToRubyC" => '',
986
+ "RubyToAnsiC" => 'n = 3;
987
+ while (n >= 1) {
988
+ puts(to_s(n));
989
+ n = n - 1;
990
+ }',
991
+ },
992
+
993
+ "iteration6" => {
994
+ "Ruby" => "while ((argl >= (1))) do\nputs((\"hello\"))\nargl = (argl - (1))\n\nend",
995
+ "ParseTree" => [:while, [:call, [:lvar, :argl],
996
+ :>=, [:arglist, [:lit, 1]]], [:block,
997
+ [:call, nil, :puts, [:arglist, [:str, "hello"]]],
998
+ [:lasgn,
999
+ :argl,
1000
+ [:call, [:lvar, :argl],
1001
+ :-, [:arglist, [:lit, 1]]]]], true],
1002
+ "Rewriter" => s(:while,
1003
+ s(:call, s(:lvar, :argl),
1004
+ :>=, s(:arglist, s(:lit, 1))),
1005
+ s(:block,
1006
+ s(:call, nil, :puts, s(:arglist, s(:str, "hello"))),
1007
+ s(:lasgn,
1008
+ :argl,
1009
+ s(:call, s(:lvar, :argl),
1010
+ :-, s(:arglist, s(:lit, 1))))), true),
1011
+ "TypeChecker" => t(:while,
1012
+ t(:call, t(:lvar, :argl, Type.long),
1013
+ :>=,
1014
+ t(:arglist, t(:lit, 1, Type.long)), Type.bool),
1015
+ t(:block,
1016
+ t(:call, nil, :puts,
1017
+ t(:arglist, t(:str, "hello", Type.str)),
1018
+ Type.void),
1019
+ t(:lasgn,
1020
+ :argl,
1021
+ t(:call, t(:lvar, :argl, Type.long),
1022
+ :-,
1023
+ t(:arglist, t(:lit, 1, Type.long)), Type.long),
1024
+ Type.long),
1025
+ Type.unknown), true),
1026
+ "CRewriter" => :same,
1027
+ "RubyToRubyC" => '',
1028
+ "RubyToAnsiC" => 'while (argl >= 1) {
1029
+ puts("hello");
1030
+ argl = argl - 1;
1031
+ }',
1032
+ },
1033
+
1034
+ # TODO: this might still be too much
1035
+ "lasgn_call" => {
1036
+ "Ruby" => "c = 2 + 3",
1037
+ "ParseTree" => [:lasgn, :c, [:call, [:lit, 2], :+, [:arglist, [:lit, 3]]]],
1038
+ "Rewriter" => s(:lasgn, :c, s(:call, s(:lit, 2), :+, s(:arglist, s(:lit, 3)))),
1039
+ "TypeChecker" => t(:lasgn, :c,
1040
+ t(:call,
1041
+ t(:lit, 2, Type.long),
1042
+ :+,
1043
+ t(:arglist,
1044
+ t(:lit, 3, Type.long)),
1045
+ Type.long),
1046
+ Type.long),
1047
+ "CRewriter" => :same,
1048
+ "RubyToRubyC" => 'c = rb_funcall(LONG2NUM(2), rb_intern("+"), 1, LONG2NUM(3))', # FIX: probably not "c ="
1049
+ "RubyToAnsiC" => "c = 2 + 3",
1050
+ },
1051
+
1052
+ "lasgn_array" => {
1053
+ "Ruby" => "var = [\"foo\", \"bar\"]",
1054
+ "ParseTree" => [:lasgn, :var, [:array,
1055
+ [:str, "foo"],
1056
+ [:str, "bar"]]],
1057
+ "Rewriter" => s(:lasgn, :var, s(:array,
1058
+ s(:str, "foo"),
1059
+ s(:str, "bar"))),
1060
+ "TypeChecker" => t(:lasgn,
1061
+ :var,
1062
+ t(:array,
1063
+ t(:str, "foo", Type.str),
1064
+ t(:str, "bar", Type.str)),
1065
+ Type.str_list),
1066
+ "CRewriter" => :same,
1067
+ "RubyToRubyC" => 'var = rb_ary_new2(2);
1068
+ rb_ary_store(var, 0, rb_str_new2("foo"));
1069
+ rb_ary_store(var, 1, rb_str_new2("bar"))',
1070
+ "RubyToAnsiC" => 'var = (str) malloc(sizeof(str) * 2);
1071
+ var[0] = "foo";
1072
+ var[1] = "bar"'
1073
+ },
1074
+
1075
+ "lit_bool_false" => {
1076
+ "Ruby" => "false",
1077
+ "ParseTree" => [:false],
1078
+ "Rewriter" => s(:false),
1079
+ "TypeChecker" => t(:false, Type.bool),
1080
+ "CRewriter" => :same,
1081
+ "RubyToRubyC" => "Qfalse",
1082
+ "RubyToAnsiC" => "0",
1083
+ },
1084
+
1085
+ "lit_bool_true" => {
1086
+ "Ruby" => "true",
1087
+ "ParseTree" => [:true],
1088
+ "Rewriter" => s(:true),
1089
+ "TypeChecker" => t(:true, Type.bool),
1090
+ "CRewriter" => :same,
1091
+ "RubyToRubyC" => "Qtrue",
1092
+ "RubyToAnsiC" => "1",
1093
+ },
1094
+
1095
+ "lit_float" => {
1096
+ "Ruby" => "1.1",
1097
+ "ParseTree" => [:lit, 1.1],
1098
+ "Rewriter" => s(:lit, 1.1),
1099
+ "TypeChecker" => t(:lit, 1.1, Type.float),
1100
+ "CRewriter" => :same,
1101
+ "RubyToRubyC" => "rb_float_new(1.1)",
1102
+ "RubyToAnsiC" => "1.1",
1103
+ },
1104
+
1105
+ "lit_long" => {
1106
+ "Ruby" => "1",
1107
+ "ParseTree" => [:lit, 1],
1108
+ "Rewriter" => s(:lit, 1),
1109
+ "TypeChecker" => t(:lit, 1, Type.long),
1110
+ "CRewriter" => :same,
1111
+ "RubyToRubyC" => "LONG2NUM(1)",
1112
+ "RubyToAnsiC" => "1",
1113
+ },
1114
+
1115
+ "lit_sym" => {
1116
+ "Ruby" => ":x",
1117
+ "ParseTree" => [:lit, :x],
1118
+ "Rewriter" => s(:lit, :x),
1119
+ "TypeChecker" => t(:lit, :x, Type.symbol),
1120
+ "CRewriter" => :same,
1121
+ "RubyToRubyC" => 'ID2SYM(rb_intern("x"))',
1122
+ "RubyToAnsiC" => '"x"', # HACK WRONG! (or... is it?)
1123
+ },
1124
+
1125
+ "lit_str" => {
1126
+ "Ruby" => "\"x\"",
1127
+ "ParseTree" => [:str, "x"],
1128
+ "Rewriter" => s(:str, "x"),
1129
+ "TypeChecker" => t(:str, "x", Type.str),
1130
+ "CRewriter" => :same,
1131
+ "RubyToRubyC" => 'rb_str_new2("x")',
1132
+ "RubyToAnsiC" => '"x"',
1133
+ },
1134
+
1135
+ "multi_args" => {
1136
+ "Ruby" => "def multi_args(arg1, arg2)\n arg3 = ((arg1 * arg2) * 7)\n puts(arg3.to_s)\n return \"foo\"\nend",
1137
+ "ParseTree" => [:defn, :multi_args,
1138
+ [:scope,
1139
+ [:block,
1140
+ [:args, :arg1, :arg2],
1141
+ [:lasgn,
1142
+ :arg3,
1143
+ [:call,
1144
+ [:call, [:lvar, :arg1], :*, [:array, [:lvar, :arg2]]],
1145
+ :*,
1146
+ [:array, [:lit, 7]]]],
1147
+ [:fcall, :puts, [:array, [:call, [:lvar, :arg3], :to_s]]],
1148
+ [:return, [:str, "foo"]]]]],
1149
+ "Rewriter" => s(:defn, :multi_args,
1150
+ s(:args, :arg1, :arg2),
1151
+ s(:scope,
1152
+ s(:block,
1153
+ s(:lasgn, :arg3,
1154
+ s(:call,
1155
+ s(:call,
1156
+ s(:lvar, :arg1),
1157
+ :*,
1158
+ s(:arglist, s(:lvar, :arg2))),
1159
+ :*,
1160
+ s(:arglist, s(:lit, 7)))),
1161
+ s(:call,
1162
+ nil,
1163
+ :puts,
1164
+ s(:arglist,
1165
+ s(:call,
1166
+ s(:lvar, :arg3),
1167
+ :to_s,
1168
+ nil))),
1169
+ s(:return, s(:str, "foo"))))),
1170
+ "TypeChecker" => t(:defn, :multi_args,
1171
+ t(:args,
1172
+ t(:arg1, Type.long),
1173
+ t(:arg2, Type.long)),
1174
+ t(:scope,
1175
+ t(:block,
1176
+ t(:lasgn,
1177
+ :arg3,
1178
+ t(:call,
1179
+ t(:call,
1180
+ t(:lvar, :arg1, Type.long),
1181
+ :*,
1182
+ t(:arglist,
1183
+ t(:lvar,
1184
+ :arg2,
1185
+ Type.long)),
1186
+ Type.long),
1187
+ :*,
1188
+ t(:arglist,
1189
+ t(:lit, 7, Type.long)),
1190
+ Type.long),
1191
+ Type.long),
1192
+ t(:call,
1193
+ nil,
1194
+ :puts,
1195
+ t(:arglist,
1196
+ t(:call,
1197
+ t(:lvar, :arg3, Type.long),
1198
+ :to_s,
1199
+ nil,
1200
+ Type.str)),
1201
+ Type.void),
1202
+ t(:return, t(:str, "foo", Type.str),
1203
+ Type.void),
1204
+ Type.unknown),
1205
+ Type.void),
1206
+ Type.function(Type.unknown,
1207
+ [Type.long, Type.long], Type.str)),
1208
+ "CRewriter" => :same,
1209
+ "RubyToRubyC" => "static VALUE
1210
+ rrc_c_multi_args(VALUE self, VALUE arg1, VALUE arg2) {
1211
+ VALUE arg3;
1212
+ arg3 = rb_funcall(rb_funcall(arg1, rb_intern(\"*\"), 1, arg2), rb_intern(\"*\"), 1, LONG2NUM(7));
1213
+ rb_funcall(self, rb_intern(\"puts\"), 1, rb_funcall(arg3, rb_intern(\"to_s\"), 0));
1214
+ return rb_str_new2(\"foo\");
1215
+ }",
1216
+ "RubyToAnsiC" => "str
1217
+ multi_args(long arg1, long arg2) {
1218
+ long arg3;
1219
+ arg3 = arg1 * arg2 * 7;
1220
+ puts(to_s(arg3));
1221
+ return \"foo\";
1222
+ }",
1223
+ },
1224
+
1225
+ "vcall" => {
1226
+ "Ruby" => "method",
1227
+ "ParseTree" => [:vcall, :method],
1228
+ "Rewriter" => s(:call, nil, :method, nil),
1229
+ "TypeChecker" => t(:call, nil, :method, nil, Type.unknown),
1230
+ "CRewriter" => :same,
1231
+ "RubyToRubyC" => "rb_funcall(self, rb_intern(\"method\"), 0)",
1232
+ "RubyToAnsiC" => "method()",
1233
+ },
1234
+
1235
+ "whiles" => {
1236
+ "Ruby" => "def whiles()\n while (false) do\n puts(\"false\")\n end\n begin\n puts(\"true\")\n end while (false)\nend",
1237
+ "ParseTree" => [:defn,
1238
+ :whiles,
1239
+ [:scope,
1240
+ [:block,
1241
+ [:args],
1242
+ [:while, [:false],
1243
+ [:fcall, :puts, [:array, [:str, "false"]]], true],
1244
+ [:while, [:false],
1245
+ [:fcall, :puts, [:array, [:str, "true"]]], false]]]],
1246
+ "Rewriter" => s(:defn,
1247
+ :whiles,
1248
+ s(:args),
1249
+ s(:scope,
1250
+ s(:block,
1251
+ s(:while,
1252
+ s(:false),
1253
+ s(:call, nil, :puts, s(:arglist, s(:str, "false"))),
1254
+ true),
1255
+ s(:while,
1256
+ s(:false),
1257
+ s(:call, nil, :puts, s(:arglist, s(:str, "true"))),
1258
+ false)))),
1259
+ "TypeChecker" => t(:defn,
1260
+ :whiles,
1261
+ t(:args),
1262
+ t(:scope,
1263
+ t(:block,
1264
+ t(:while,
1265
+ t(:false, Type.bool),
1266
+ t(:call,
1267
+ nil,
1268
+ :puts,
1269
+ t(:arglist, t(:str, "false", Type.str)), Type.void),
1270
+ true),
1271
+ t(:while,
1272
+ t(:false, Type.bool),
1273
+ t(:call,
1274
+ nil,
1275
+ :puts,
1276
+ t(:arglist, t(:str, "true", Type.str)), Type.void),
1277
+ false),
1278
+ Type.unknown),
1279
+ Type.void),
1280
+ Type.function(Type.unknown, [], Type.void)),
1281
+ "CRewriter" => :same,
1282
+ "RubyToRubyC" => "static VALUE
1283
+ rrc_c_whiles(VALUE self) {
1284
+ while (Qfalse) {
1285
+ rb_funcall(self, rb_intern(\"puts\"), 1, rb_str_new2(\"false\"));
1286
+ };
1287
+ {
1288
+ rb_funcall(self, rb_intern(\"puts\"), 1, rb_str_new2(\"true\"));
1289
+ } while (Qfalse);
1290
+ }",
1291
+ "RubyToAnsiC" => "void
1292
+ whiles() {
1293
+ while (0) {
1294
+ puts(\"false\");
1295
+ };
1296
+ {
1297
+ puts(\"true\");
1298
+ } while (0);
1299
+ }",
1300
+ },
1301
+
1302
+ "zarray" => {
1303
+ "Ruby" => "a = []",
1304
+ "ParseTree" => [:lasgn, :a, [:zarray]],
1305
+ "Rewriter" => s(:lasgn, :a, s(:array)),
1306
+ "TypeChecker" => t(:lasgn, :a, t(:array), Type.unknown_list),
1307
+ "CRewriter" => :same,
1308
+ # TODO: need to verify that our variable decl will be correct
1309
+ "RubyToRubyC" => "a = rb_ary_new2(0)",
1310
+ "RubyToAnsiC" => "a = (void *) malloc(sizeof(void *) * 0)",
1311
+ },
1312
+ }
1313
+
1314
+ def self.previous(key)
1315
+ idx = @@testcase_order.index(key)-1
1316
+ case key
1317
+ when "RubyToRubyC" then
1318
+ idx -= 1
1319
+ end
1320
+ @@testcase_order[idx]
1321
+ end
1322
+
1323
+ # lets us used unprocessed :self outside of tests, called when subclassed
1324
+ def self.clone_same
1325
+ @@testcases.each do |node, data|
1326
+ data.each do |key, val|
1327
+ if val == :same then
1328
+ prev_key = self.previous(key)
1329
+ data[key] = data[prev_key].deep_clone
1330
+ end
1331
+ end
1332
+ end
1333
+ end
1334
+
1335
+ def self.inherited(c)
1336
+ self.clone_same
1337
+
1338
+ output_name = c.name.to_s.sub(/^Test/, '')
1339
+ raise "Unknown class #{c}" unless @@testcase_order.include? output_name
1340
+
1341
+ input_name = self.previous(output_name)
1342
+
1343
+ @@testcases.each do |node, data|
1344
+ next if data[input_name] == :skip
1345
+ next if data[output_name] == :skip
1346
+
1347
+ c.send(:define_method, "test_#{node}".intern) do
1348
+ flunk "Processor is nil" if processor.nil?
1349
+ assert data.has_key?(input_name), "Unknown input data"
1350
+ assert data.has_key?(output_name), "Unknown expected data"
1351
+ input = data[input_name].deep_clone
1352
+ expected = data[output_name].deep_clone
1353
+
1354
+ case expected
1355
+ when :unsupported then
1356
+ assert_raises(UnsupportedNodeError) do
1357
+ processor.process(input)
1358
+ end
1359
+ else
1360
+ extra_expected = []
1361
+ extra_input = []
1362
+ _, expected, extra_expected = *expected if Array === expected and expected.first == :defx
1363
+ _, input, extra_input = *input if Array === input and input.first == :defx
1364
+
1365
+ assert_equal expected, processor.process(input)
1366
+ extra_input.each do |input| processor.process(input) end
1367
+ extra = processor.extra_methods rescue []
1368
+ assert_equal extra_expected, extra
1369
+ end
1370
+ end
1371
+ end
1372
+ end
1373
+
1374
+ def test_stoopid
1375
+ # do nothing - shuts up empty test class requirement
1376
+ end
1377
+
1378
+ end