ParseTree 1.7.1 → 2.0.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.
- 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
|
|