ParseTree 1.7.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +32 -0
- data/Manifest.txt +2 -0
- data/README.txt +28 -24
- data/Rakefile +14 -7
- data/lib/parse_tree.rb +47 -14
- data/lib/sexp.rb +46 -3
- data/lib/sexp_processor.rb +15 -19
- data/lib/unified_ruby.rb +147 -0
- data/test/pt_testcase.rb +158 -53
- data/test/test_parse_tree.rb +2 -1
- data/test/test_sexp.rb +28 -3
- data/test/test_sexp_processor.rb +74 -17
- data/test/test_unified_ruby.rb +229 -0
- metadata +8 -6
data/lib/unified_ruby.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
|
2
|
+
module UnifiedRuby
|
3
|
+
def rewrite_bmethod(exp)
|
4
|
+
exp[0] = :scope
|
5
|
+
|
6
|
+
args =
|
7
|
+
if exp.masgn and exp.masgn.dasgn_curr then
|
8
|
+
arg = exp.masgn(true).dasgn_curr(true).sexp_body
|
9
|
+
raise "nope: #{arg.size}" unless arg.size == 1
|
10
|
+
s(:args, :"*#{arg.last}")
|
11
|
+
else
|
12
|
+
args = exp.dasgn_curr(true)
|
13
|
+
if args then
|
14
|
+
s(:args, *args.sexp_body)
|
15
|
+
else
|
16
|
+
exp.delete_at 1 # nil
|
17
|
+
s(:args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
exp = s(:scope, s(:block, *exp.sexp_body)) unless exp.block
|
22
|
+
exp.block.insert 1, args
|
23
|
+
exp.find_and_replace_all(:dvar, :lvar)
|
24
|
+
|
25
|
+
exp
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# :defn is one of the most complex of all the ASTs in ruby. We do
|
30
|
+
# one of 3 different translations:
|
31
|
+
#
|
32
|
+
# 1) From:
|
33
|
+
#
|
34
|
+
# s(:defn, :name, s(:scope, s(:block, s(:args, ...), ...)))
|
35
|
+
# s(:defn, :name, s(:bmethod, s(:masgn, s(:dasgn_curr, :args)), s(:block, ...)))
|
36
|
+
# s(:defn, :name, s(:fbody, s(:bmethod, s(:masgn, s(:dasgn_curr, :splat)), s(:block, ...))))
|
37
|
+
#
|
38
|
+
# to:
|
39
|
+
#
|
40
|
+
# s(:defn, :name, s(:args, ...), s(:scope, s:(block, ...)))
|
41
|
+
#
|
42
|
+
# 2) From:
|
43
|
+
#
|
44
|
+
# s(:defn, :writer=, s(:attrset, :@name))
|
45
|
+
#
|
46
|
+
# to:
|
47
|
+
#
|
48
|
+
# s(:defn, :writer=, s(:args), s(:attrset, :@name))
|
49
|
+
#
|
50
|
+
# 3) From:
|
51
|
+
#
|
52
|
+
# s(:defn, :reader, s(:ivar, :@name))
|
53
|
+
#
|
54
|
+
# to:
|
55
|
+
#
|
56
|
+
# s(:defn, :reader, s(:args), s(:ivar, :@name))
|
57
|
+
#
|
58
|
+
#
|
59
|
+
|
60
|
+
def rewrite_defn(exp)
|
61
|
+
weirdo = exp.ivar || exp.attrset
|
62
|
+
|
63
|
+
# move args up
|
64
|
+
args = exp.scope.block.args(true) unless weirdo
|
65
|
+
exp.insert 2, args if args
|
66
|
+
|
67
|
+
# move block_arg up and in
|
68
|
+
block_arg = exp.scope.block.block_arg(true) rescue nil
|
69
|
+
exp.args << block_arg if block_arg
|
70
|
+
|
71
|
+
# patch up attr_accessor methods
|
72
|
+
exp.insert 2, s(:args) if weirdo
|
73
|
+
|
74
|
+
exp
|
75
|
+
end
|
76
|
+
|
77
|
+
def rewrite_dmethod(exp)
|
78
|
+
exp.shift # type
|
79
|
+
exp.shift # dmethod name
|
80
|
+
exp.shift # scope / block / body
|
81
|
+
end
|
82
|
+
|
83
|
+
def rewrite_fbody(exp)
|
84
|
+
return *exp.sexp_body
|
85
|
+
end
|
86
|
+
|
87
|
+
def rewrite_fcall(exp)
|
88
|
+
exp[0] = :call
|
89
|
+
exp.insert 1, nil
|
90
|
+
exp.push nil if exp.size <= 3
|
91
|
+
|
92
|
+
args = exp[-1]
|
93
|
+
if Array === args and args.first == :array then
|
94
|
+
args[0] = :arglist
|
95
|
+
elsif args.nil? then
|
96
|
+
exp[-1] = s(:arglist)
|
97
|
+
else
|
98
|
+
exp[-1] = s(:arglist, args) unless args.nil?
|
99
|
+
end
|
100
|
+
|
101
|
+
exp
|
102
|
+
end
|
103
|
+
|
104
|
+
def rewrite_resbody(exp) # TODO: clean up and move to unified
|
105
|
+
result = s()
|
106
|
+
|
107
|
+
code = result
|
108
|
+
while exp and exp.first == :resbody do
|
109
|
+
code << exp.shift
|
110
|
+
list = exp.shift || s(:array)
|
111
|
+
body = exp.shift rescue nil
|
112
|
+
exp = exp.shift rescue nil
|
113
|
+
|
114
|
+
# code may be nil, :lasgn, or :block
|
115
|
+
case body.first
|
116
|
+
when nil then
|
117
|
+
# do nothing
|
118
|
+
when :lasgn then
|
119
|
+
# TODO: check that it is assigning $!
|
120
|
+
list << body
|
121
|
+
body = nil
|
122
|
+
when :block then
|
123
|
+
# TODO: check that it is assigning $!
|
124
|
+
list << body.delete_at(1) if body[1].first == :lasgn
|
125
|
+
else
|
126
|
+
# do nothing (expression form)
|
127
|
+
end if body
|
128
|
+
|
129
|
+
code << list << body
|
130
|
+
if exp then
|
131
|
+
code = s()
|
132
|
+
result << code
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
raise unless result.first == :resbody
|
137
|
+
raise unless Sexp === result[1] and result[1].first == :array
|
138
|
+
raise unless result[2].nil? or (Sexp === result[2] and ! result[2].empty?)
|
139
|
+
|
140
|
+
result
|
141
|
+
end
|
142
|
+
|
143
|
+
def rewrite_vcall(exp)
|
144
|
+
exp.push nil
|
145
|
+
rewrite_fcall(exp)
|
146
|
+
end
|
147
|
+
end
|
data/test/pt_testcase.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
$TESTING = true
|
2
|
+
|
1
3
|
require 'test/unit/testcase'
|
2
4
|
require 'sexp_processor' # for deep_clone
|
3
5
|
require 'unique'
|
@@ -9,6 +11,10 @@ class Examples
|
|
9
11
|
def a_method(x); x+1; end
|
10
12
|
alias an_alias a_method
|
11
13
|
|
14
|
+
define_method(:bmethod_noargs) do
|
15
|
+
x + 1
|
16
|
+
end
|
17
|
+
|
12
18
|
define_method(:unsplatted) do |x|
|
13
19
|
x + 1
|
14
20
|
end
|
@@ -133,11 +139,32 @@ class ParseTreeTestCase < Test::Unit::TestCase
|
|
133
139
|
[:nil]]]
|
134
140
|
},
|
135
141
|
|
142
|
+
"block_lasgn" => {
|
143
|
+
"Ruby" => "x = (y = 1\n(y + 2))",
|
144
|
+
"ParseTree" => [:lasgn, :x,
|
145
|
+
[:block,
|
146
|
+
[:lasgn, :y, [:lit, 1]],
|
147
|
+
[:call, [:lvar, :y], :+, [:array, [:lit, 2]]]]],
|
148
|
+
},
|
149
|
+
|
136
150
|
"block_pass" => {
|
137
151
|
"Ruby" => "a(&b)",
|
138
152
|
"ParseTree" => [:block_pass, [:vcall, :b], [:fcall, :a]],
|
139
153
|
},
|
140
154
|
|
155
|
+
"block_pass_args_and_splat" => {
|
156
|
+
"Ruby" => "def blah(*args, &block)\n other(42, *args, &block)\nend",
|
157
|
+
"ParseTree" => [:defn, :blah,
|
158
|
+
[:scope,
|
159
|
+
[:block,
|
160
|
+
[:args, "*args".intern],
|
161
|
+
[:block_arg, :block],
|
162
|
+
[:block_pass,
|
163
|
+
[:lvar, :block],
|
164
|
+
[:fcall, :other,
|
165
|
+
[:argscat, [:array, [:lit, 42]], [:lvar, :args]]]]]]],
|
166
|
+
},
|
167
|
+
|
141
168
|
"block_pass_omgwtf" => {
|
142
169
|
"Ruby" => "define_attr_method(:x, :sequence_name, &Proc.new { |*args| nil })",
|
143
170
|
"ParseTree" => [:block_pass,
|
@@ -161,29 +188,26 @@ class ParseTreeTestCase < Test::Unit::TestCase
|
|
161
188
|
[:fcall, :other, [:splat, [:lvar, :args]]]]]]],
|
162
189
|
},
|
163
190
|
|
164
|
-
"block_pass_args_and_splat" => {
|
165
|
-
"Ruby" => "def blah(*args, &block)\n other(42, *args, &block)\nend",
|
166
|
-
"ParseTree" => [:defn, :blah,
|
167
|
-
[:scope,
|
168
|
-
[:block,
|
169
|
-
[:args, "*args".intern],
|
170
|
-
[:block_arg, :block],
|
171
|
-
[:block_pass,
|
172
|
-
[:lvar, :block],
|
173
|
-
[:fcall, :other,
|
174
|
-
[:argscat, [:array, [:lit, 42]], [:lvar, :args]]]]]]],
|
175
|
-
},
|
176
|
-
|
177
191
|
"bmethod" => {
|
178
192
|
"Ruby" => [Examples, :unsplatted],
|
179
193
|
"ParseTree" => [:defn,
|
180
194
|
:unsplatted,
|
181
195
|
[:bmethod,
|
182
196
|
[:dasgn_curr, :x],
|
183
|
-
[:call, [:dvar, :x],
|
197
|
+
[:call, [:dvar, :x], :+, [:array, [:lit, 1]]]]],
|
184
198
|
"Ruby2Ruby" => "def unsplatted(x)\n (x + 1)\nend"
|
185
199
|
},
|
186
200
|
|
201
|
+
"bmethod_noargs" => {
|
202
|
+
"Ruby" => [Examples, :bmethod_noargs],
|
203
|
+
"ParseTree" => [:defn,
|
204
|
+
:bmethod_noargs,
|
205
|
+
[:bmethod,
|
206
|
+
nil,
|
207
|
+
[:call, [:vcall, :x], "+".intern, [:array, [:lit, 1]]]]],
|
208
|
+
"Ruby2Ruby" => "def bmethod_noargs\n (x + 1)\nend"
|
209
|
+
},
|
210
|
+
|
187
211
|
"bmethod_splat" => {
|
188
212
|
"Ruby" => [Examples, :splatted],
|
189
213
|
"ParseTree" => [:defn, :splatted,
|
@@ -218,6 +242,14 @@ class ParseTreeTestCase < Test::Unit::TestCase
|
|
218
242
|
"ParseTree" => [:fcall, :puts, [:array, [:lit, 42]]],
|
219
243
|
},
|
220
244
|
|
245
|
+
"call_expr" => {
|
246
|
+
"Ruby" => "(v = (1 + 1)).zero?",
|
247
|
+
"ParseTree" => [:call,
|
248
|
+
[:lasgn, :v,
|
249
|
+
[:call, [:lit, 1], :+, [:array, [:lit, 1]]]],
|
250
|
+
:zero?],
|
251
|
+
},
|
252
|
+
|
221
253
|
"call_index" => { # see attrasgn_index_equals for opposite
|
222
254
|
"Ruby" => "a[42]",
|
223
255
|
"ParseTree" => [:call, [:vcall, :a], :[], [:array, [:lit, 42]]],
|
@@ -310,14 +342,6 @@ class ParseTreeTestCase < Test::Unit::TestCase
|
|
310
342
|
[:fcall, :puts, [:array, [:str, "hello"]]]]]]]]],
|
311
343
|
},
|
312
344
|
|
313
|
-
"class_super_object" => {
|
314
|
-
"Ruby" => "class X < Object\nend",
|
315
|
-
"ParseTree" => [:class,
|
316
|
-
:X,
|
317
|
-
[:const, :Object],
|
318
|
-
[:scope]],
|
319
|
-
},
|
320
|
-
|
321
345
|
"class_super_array" => {
|
322
346
|
"Ruby" => "class X < Array\nend",
|
323
347
|
"ParseTree" => [:class,
|
@@ -334,6 +358,14 @@ class ParseTreeTestCase < Test::Unit::TestCase
|
|
334
358
|
[:scope]],
|
335
359
|
},
|
336
360
|
|
361
|
+
"class_super_object" => {
|
362
|
+
"Ruby" => "class X < Object\nend",
|
363
|
+
"ParseTree" => [:class,
|
364
|
+
:X,
|
365
|
+
[:const, :Object],
|
366
|
+
[:scope]],
|
367
|
+
},
|
368
|
+
|
337
369
|
"colon2" => {
|
338
370
|
"Ruby" => "X::Y",
|
339
371
|
"ParseTree" => [:colon2, [:const, :X], :Y],
|
@@ -458,14 +490,15 @@ class ParseTreeTestCase < Test::Unit::TestCase
|
|
458
490
|
"ParseTree" => [:defn, :something?, [:scope, [:block, [:args], [:nil]]]],
|
459
491
|
},
|
460
492
|
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
493
|
+
"defn_optargs" => {
|
494
|
+
"Ruby" => "def x(a, *args)\n p(a, args)\nend",
|
495
|
+
"ParseTree" => [:defn, :x,
|
496
|
+
[:scope,
|
497
|
+
[:block,
|
498
|
+
[:args, :a, "*args".intern],
|
499
|
+
[:fcall, :p,
|
500
|
+
[:array, [:lvar, :a], [:lvar, :args]]]]]],
|
501
|
+
},
|
469
502
|
|
470
503
|
"defn_or" => {
|
471
504
|
"Ruby" => "def |(o)\n # do nothing\nend",
|
@@ -475,15 +508,25 @@ class ParseTreeTestCase < Test::Unit::TestCase
|
|
475
508
|
"defn_rescue" => {
|
476
509
|
"Ruby" => "def eql?(resource)\n (self.uuid == resource.uuid) rescue false\nend",
|
477
510
|
"ParseTree" => [:defn, :eql?,
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
511
|
+
[:scope,
|
512
|
+
[:block,
|
513
|
+
[:args, :resource],
|
514
|
+
[:rescue,
|
515
|
+
[:call,
|
516
|
+
[:call, [:self], :uuid],
|
517
|
+
:==,
|
518
|
+
[:array, [:call, [:lvar, :resource], :uuid]]],
|
519
|
+
[:resbody, nil, [:false]]]]]],
|
520
|
+
},
|
521
|
+
|
522
|
+
"defn_splat_no_name" => {
|
523
|
+
"Ruby" => "def x(a, *)\n p(a)\nend",
|
524
|
+
"ParseTree" => [:defn, :x,
|
525
|
+
[:scope,
|
526
|
+
[:block,
|
527
|
+
[:args, :a, "*".intern],
|
528
|
+
[:fcall, :p,
|
529
|
+
[:array, [:lvar, :a]]]]]],
|
487
530
|
},
|
488
531
|
|
489
532
|
"defn_zarray" => { # tests memory allocation for returns
|
@@ -668,11 +711,41 @@ end",
|
|
668
711
|
"ParseTree" => [:hash, [:lit, 1], [:lit, 2], [:lit, 3], [:lit, 4]],
|
669
712
|
},
|
670
713
|
|
714
|
+
"hash_rescue" => {
|
715
|
+
"Ruby" => "{ 1 => (2 rescue 3) }",
|
716
|
+
"ParseTree" => [:hash,
|
717
|
+
[:lit, 1],
|
718
|
+
[:rescue, [:lit, 2], [:resbody, nil, [:lit, 3]]]],
|
719
|
+
},
|
720
|
+
|
671
721
|
"iasgn" => {
|
672
722
|
"Ruby" => "@a = 4",
|
673
723
|
"ParseTree" => [:iasgn, :@a, [:lit, 4]],
|
674
724
|
},
|
675
725
|
|
726
|
+
"if_block_condition" => {
|
727
|
+
"Ruby" => "if (x = 5\n(x + 1)) then\n nil\nend",
|
728
|
+
"ParseTree" => [:if,
|
729
|
+
[:block,
|
730
|
+
[:lasgn, :x, [:lit, 5]],
|
731
|
+
[:call,
|
732
|
+
[:lvar, :x],
|
733
|
+
:+,
|
734
|
+
[:array, [:lit, 1]]]],
|
735
|
+
[:nil],
|
736
|
+
nil],
|
737
|
+
},
|
738
|
+
|
739
|
+
"if_lasgn_short" => {
|
740
|
+
"Ruby" => "if x = obj.x then\n x.do_it\nend",
|
741
|
+
"ParseTree" => [:if,
|
742
|
+
[:lasgn, :x,
|
743
|
+
[:call, [:vcall, :obj], :x]],
|
744
|
+
[:call,
|
745
|
+
[:lvar, :x], :do_it],
|
746
|
+
nil],
|
747
|
+
},
|
748
|
+
|
676
749
|
"iteration1" => {
|
677
750
|
"Ruby" => "loop { }",
|
678
751
|
"ParseTree" => [:iter, [:fcall, :loop], nil],
|
@@ -811,11 +884,14 @@ end",
|
|
811
884
|
[:array, [:vcall, :c], [:vcall, :d]]],
|
812
885
|
},
|
813
886
|
|
814
|
-
"
|
815
|
-
"Ruby" => "a,
|
887
|
+
"masgn_argscat" => {
|
888
|
+
"Ruby" => "a, b, *c = 1, 2, *[3, 4]",
|
816
889
|
"ParseTree" => [:masgn,
|
817
|
-
[:array, [:lasgn, :a], [:
|
818
|
-
[:
|
890
|
+
[:array, [:lasgn, :a], [:lasgn, :b]],
|
891
|
+
[:lasgn, :c],
|
892
|
+
[:argscat,
|
893
|
+
[:array, [:lit, 1], [:lit, 2]],
|
894
|
+
[:array, [:lit, 3], [:lit, 4]]]]
|
819
895
|
},
|
820
896
|
|
821
897
|
"masgn_attrasgn" => {
|
@@ -825,6 +901,31 @@ end",
|
|
825
901
|
[:array, [:vcall, :d], [:vcall, :e]]],
|
826
902
|
},
|
827
903
|
|
904
|
+
"masgn_iasgn" => {
|
905
|
+
"Ruby" => "a, @b = c, d",
|
906
|
+
"ParseTree" => [:masgn,
|
907
|
+
[:array, [:lasgn, :a], [:iasgn, "@b".intern]],
|
908
|
+
[:array, [:vcall, :c], [:vcall, :d]]],
|
909
|
+
},
|
910
|
+
|
911
|
+
"masgn_masgn" => {
|
912
|
+
"Ruby" => "a, (b, c) = [1, [2, 3]]",
|
913
|
+
"ParseTree" => [:masgn,
|
914
|
+
[:array,
|
915
|
+
[:lasgn, :a],
|
916
|
+
[:masgn,
|
917
|
+
[:array,
|
918
|
+
[:lasgn, :b],
|
919
|
+
[:lasgn, :c]]]],
|
920
|
+
[:to_ary,
|
921
|
+
[:array,
|
922
|
+
[:lit, 1],
|
923
|
+
[:array,
|
924
|
+
[:lit, 2],
|
925
|
+
[:lit, 3]]]]]
|
926
|
+
|
927
|
+
},
|
928
|
+
|
828
929
|
"masgn_splat" => {
|
829
930
|
"Ruby" => "a, b, *c = d, e, f, g",
|
830
931
|
"ParseTree" => [:masgn,
|
@@ -835,7 +936,6 @@ end",
|
|
835
936
|
[:vcall, :f], [:vcall, :g]]]
|
836
937
|
},
|
837
938
|
|
838
|
-
|
839
939
|
"match" => {
|
840
940
|
"Ruby" => "1 if /x/",
|
841
941
|
"ParseTree" => [:if, [:match, [:lit, /x/]], [:lit, 1], nil],
|
@@ -994,8 +1094,12 @@ end",
|
|
994
1094
|
},
|
995
1095
|
|
996
1096
|
"splat" => {
|
997
|
-
"Ruby" => "a(*b)",
|
998
|
-
"ParseTree" => [:
|
1097
|
+
"Ruby" => "def x(*b)\n a(*b)\nend",
|
1098
|
+
"ParseTree" => [:defn, :x,
|
1099
|
+
[:scope,
|
1100
|
+
[:block,
|
1101
|
+
[:args, :"*b"],
|
1102
|
+
[:fcall, :a, [:splat, [:lvar, :b]]]]]],
|
999
1103
|
},
|
1000
1104
|
|
1001
1105
|
# TODO: all supers need to pass args
|
@@ -1070,6 +1174,12 @@ end",
|
|
1070
1174
|
"ParseTree" => [:vcall, :method],
|
1071
1175
|
},
|
1072
1176
|
|
1177
|
+
"while_post" => {
|
1178
|
+
"Ruby" => "begin\n (1 + 1)\nend while false",
|
1179
|
+
"ParseTree" => [:while, [:false],
|
1180
|
+
[:call, [:lit, 1], :+, [:array, [:lit, 1]]], false],
|
1181
|
+
},
|
1182
|
+
|
1073
1183
|
"while_pre" => {
|
1074
1184
|
"Ruby" => "while false do\n (1 + 1)\nend",
|
1075
1185
|
"ParseTree" => [:while, [:false],
|
@@ -1081,12 +1191,6 @@ end",
|
|
1081
1191
|
"ParseTree" => [:while, [:false], nil, true],
|
1082
1192
|
},
|
1083
1193
|
|
1084
|
-
"while_post" => {
|
1085
|
-
"Ruby" => "begin\n (1 + 1)\nend while false",
|
1086
|
-
"ParseTree" => [:while, [:false],
|
1087
|
-
[:call, [:lit, 1], :+, [:array, [:lit, 1]]], false],
|
1088
|
-
},
|
1089
|
-
|
1090
1194
|
"xstr" => {
|
1091
1195
|
"Ruby" => "`touch 5`",
|
1092
1196
|
"ParseTree" => [:xstr, 'touch 5'],
|
@@ -1170,7 +1274,8 @@ end",
|
|
1170
1274
|
|
1171
1275
|
def self.inherited(c)
|
1172
1276
|
output_name = c.name.to_s.sub(/^Test/, '')
|
1173
|
-
raise "Unknown class #{c}
|
1277
|
+
raise "Unknown class #{c} in @@testcase_order" unless
|
1278
|
+
@@testcase_order.include? output_name
|
1174
1279
|
|
1175
1280
|
input_name = self.previous(output_name)
|
1176
1281
|
|