ParseTree 1.1.1 → 1.2.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 +19 -0
- data/Makefile +24 -7
- data/Manifest.txt +12 -10
- data/ParseTree.gemspec +39 -0
- data/README.txt +1 -1
- data/bin/parse_tree_abc +86 -0
- data/bin/parse_tree_deps +66 -0
- data/bin/parse_tree_show +49 -0
- data/lib/composite_sexp_processor.rb +43 -0
- data/{parse_tree.rb → lib/parse_tree.rb} +104 -32
- data/{sexp_processor.rb → lib/sexp_processor.rb} +89 -19
- data/{something.rb → test/something.rb} +0 -0
- data/test/test_all.rb +13 -0
- data/{test_composite_sexp_processor.rb → test/test_composite_sexp_processor.rb} +0 -0
- data/test/test_parse_tree.rb +275 -0
- data/{test_sexp_processor.rb → test/test_sexp_processor.rb} +0 -9
- metadata +22 -16
- data/composite_sexp_processor.rb +0 -24
- data/parse_tree_abc +0 -62
- data/parse_tree_show +0 -28
- data/test_all.rb +0 -7
- data/test_parse_tree.rb +0 -275
@@ -2,31 +2,53 @@
|
|
2
2
|
$TESTING = false unless defined? $TESTING
|
3
3
|
|
4
4
|
class Object
|
5
|
+
|
6
|
+
##
|
7
|
+
# deep_clone is the usual Marshalling hack to make a deep copy.
|
8
|
+
# It is rather slow, so use it sparingly. Helps with debugging
|
9
|
+
# SexpProcessors since you usually shift off sexps.
|
10
|
+
|
5
11
|
def deep_clone
|
6
12
|
Marshal.load(Marshal.dump(self))
|
7
13
|
end
|
8
14
|
end
|
9
15
|
|
16
|
+
##
|
17
|
+
# Sexps are the basic storage mechanism of SexpProcessor. Sexps have
|
18
|
+
# a +type+ (to be renamed +node_type+) which is the first element of
|
19
|
+
# the Sexp. The type is used by SexpProcessor to determine whom to
|
20
|
+
# dispatch the Sexp to for processing.
|
21
|
+
|
10
22
|
class Sexp < Array # ZenTest FULL
|
11
23
|
|
12
24
|
@@array_types = [ :array, :args, ]
|
13
25
|
|
26
|
+
##
|
27
|
+
# Named positional parameters.
|
28
|
+
# Use with +SexpProcessor.require_empty=false+.
|
14
29
|
attr_accessor :accessors
|
15
|
-
attr_accessor :unpack
|
16
30
|
|
17
|
-
|
31
|
+
##
|
32
|
+
# Create a new Sexp containing +args+.
|
18
33
|
|
19
34
|
def initialize(*args)
|
20
|
-
@unpack = false
|
21
35
|
@accessors = []
|
22
36
|
super(args)
|
23
37
|
end
|
24
38
|
|
39
|
+
##
|
40
|
+
# Returns true if the node_type is +array+ or +args+.
|
41
|
+
#
|
42
|
+
# REFACTOR: to TypedSexp - we only care when we have units.
|
43
|
+
|
25
44
|
def array_type?
|
26
45
|
type = self.first
|
27
46
|
@@array_types.include? type
|
28
47
|
end
|
29
48
|
|
49
|
+
##
|
50
|
+
# Enumeratates the sexp yielding to +b+ when the node_type == +t+.
|
51
|
+
|
30
52
|
def each_of_type(t, &b)
|
31
53
|
each do | elem |
|
32
54
|
if Sexp === elem then
|
@@ -36,7 +58,10 @@ class Sexp < Array # ZenTest FULL
|
|
36
58
|
end
|
37
59
|
end
|
38
60
|
|
39
|
-
|
61
|
+
##
|
62
|
+
# Replaces all elements whose node_type is +from+ with +to+. Used
|
63
|
+
# only for the most trivial of rewrites.
|
64
|
+
|
40
65
|
def find_and_replace_all(from, to)
|
41
66
|
each_with_index do | elem, index |
|
42
67
|
if Sexp === elem then
|
@@ -47,6 +72,30 @@ class Sexp < Array # ZenTest FULL
|
|
47
72
|
end
|
48
73
|
end
|
49
74
|
|
75
|
+
##
|
76
|
+
# Fancy-Schmancy method used to implement named positional accessors
|
77
|
+
# via +accessors+.
|
78
|
+
#
|
79
|
+
# Example:
|
80
|
+
#
|
81
|
+
# class MyProcessor < SexpProcessor
|
82
|
+
# def initialize
|
83
|
+
# super
|
84
|
+
# self.require_empty = false
|
85
|
+
# self.sexp_accessors = {
|
86
|
+
# :call => [:lhs, :name, :rhs]
|
87
|
+
# }
|
88
|
+
# ...
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# def process_call(exp)
|
92
|
+
# lhs = exp.lhs
|
93
|
+
# name = exp.name
|
94
|
+
# rhs = exp.rhs
|
95
|
+
# ...
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
|
50
99
|
def method_missing(meth, *a, &b)
|
51
100
|
super unless @accessors.include? meth
|
52
101
|
|
@@ -54,11 +103,14 @@ class Sexp < Array # ZenTest FULL
|
|
54
103
|
return self.at(index)
|
55
104
|
end
|
56
105
|
|
106
|
+
##
|
107
|
+
# Returns the Sexp without the node_type.
|
108
|
+
|
57
109
|
def sexp_body
|
58
110
|
self[1..-1]
|
59
111
|
end
|
60
112
|
|
61
|
-
def ==(obj)
|
113
|
+
def ==(obj) # :nodoc:
|
62
114
|
case obj
|
63
115
|
when Sexp
|
64
116
|
super
|
@@ -67,25 +119,29 @@ class Sexp < Array # ZenTest FULL
|
|
67
119
|
end
|
68
120
|
end
|
69
121
|
|
70
|
-
def to_a
|
122
|
+
def to_a # :nodoc:
|
71
123
|
self.map { |o| Sexp === o ? o.to_a : o }
|
72
124
|
end
|
73
125
|
|
74
|
-
def inspect
|
126
|
+
def inspect # :nodoc:
|
75
127
|
sexp_str = self.map {|x|x.inspect}.join(', ')
|
76
128
|
return "Sexp.new(#{sexp_str})"
|
77
129
|
end
|
78
130
|
|
79
|
-
def pretty_print(q)
|
131
|
+
def pretty_print(q) # :nodoc:
|
80
132
|
q.group(1, 's(', ')') do
|
81
133
|
q.seplist(self) {|v| q.pp v }
|
82
134
|
end
|
83
135
|
end
|
84
136
|
|
85
|
-
def to_s
|
137
|
+
def to_s # :nodoc:
|
86
138
|
inspect
|
87
139
|
end
|
88
140
|
|
141
|
+
##
|
142
|
+
# If run with debug, Sexp will raise if you shift on an empty
|
143
|
+
# Sexp. Helps with debugging.
|
144
|
+
|
89
145
|
def shift
|
90
146
|
raise "I'm empty" if self.empty?
|
91
147
|
super
|
@@ -93,7 +149,10 @@ class Sexp < Array # ZenTest FULL
|
|
93
149
|
|
94
150
|
end
|
95
151
|
|
96
|
-
|
152
|
+
##
|
153
|
+
# This is just a stupid shortcut to make indentation much cleaner.
|
154
|
+
|
155
|
+
def s(*args)
|
97
156
|
Sexp.new(*args)
|
98
157
|
end
|
99
158
|
|
@@ -114,7 +173,6 @@ end
|
|
114
173
|
# Here is a simple example:
|
115
174
|
#
|
116
175
|
# class MyProcessor < SexpProcessor
|
117
|
-
#
|
118
176
|
# def initialize
|
119
177
|
# super
|
120
178
|
# self.strict = false
|
@@ -124,7 +182,6 @@ end
|
|
124
182
|
# val = exp.shift
|
125
183
|
# return val
|
126
184
|
# end
|
127
|
-
#
|
128
185
|
# end
|
129
186
|
|
130
187
|
class SexpProcessor
|
@@ -263,11 +320,7 @@ class SexpProcessor
|
|
263
320
|
else
|
264
321
|
sub_result = sub_exp
|
265
322
|
end
|
266
|
-
|
267
|
-
result.push(*sub_result)
|
268
|
-
else
|
269
|
-
result << sub_result
|
270
|
-
end
|
323
|
+
result << sub_result
|
271
324
|
end
|
272
325
|
|
273
326
|
# NOTE: this is costly, but we are in the generic processor
|
@@ -293,9 +346,26 @@ class SexpProcessor
|
|
293
346
|
# Raises unless the Sexp type for +list+ matches +typ+
|
294
347
|
|
295
348
|
def assert_type(list, typ)
|
296
|
-
raise TypeError, "Expected type #{typ.inspect} in #{list.inspect}"
|
297
|
-
|
349
|
+
raise TypeError, "Expected type #{typ.inspect} in #{list.inspect}" if
|
350
|
+
list.first != typ
|
298
351
|
end
|
299
352
|
|
353
|
+
##
|
354
|
+
# A fairly generic processor for a dummy node. Dummy nodes are used
|
355
|
+
# when your processor is doing a complicated rewrite that replaces
|
356
|
+
# the current sexp with multiple sexps.
|
357
|
+
#
|
358
|
+
# Bogus Example:
|
359
|
+
#
|
360
|
+
# def process_something(exp)
|
361
|
+
# return s(:dummy, process(exp), s(:extra, 42))
|
362
|
+
|
363
|
+
def process_dummy(exp)
|
364
|
+
result = @expected.new(:dummy)
|
365
|
+
until exp.empty? do
|
366
|
+
result << self.process(exp.shift)
|
367
|
+
end
|
368
|
+
result
|
369
|
+
end
|
300
370
|
end
|
301
371
|
|
File without changes
|
data/test/test_all.rb
ADDED
File without changes
|
@@ -0,0 +1,275 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'parse_tree'
|
5
|
+
require 'something'
|
6
|
+
|
7
|
+
class TestParseTree < Test::Unit::TestCase
|
8
|
+
|
9
|
+
# TODO: need a test of interpolated strings
|
10
|
+
|
11
|
+
@@missing = [nil]
|
12
|
+
@@empty = [:defn, :empty,
|
13
|
+
[:scope,
|
14
|
+
[:args]]]
|
15
|
+
@@stupid = [:defn, :stupid,
|
16
|
+
[:scope,
|
17
|
+
[:block,
|
18
|
+
[:args],
|
19
|
+
[:return, [:nil]]]]]
|
20
|
+
@@simple = [:defn, :simple,
|
21
|
+
[:scope,
|
22
|
+
[:block,
|
23
|
+
[:args, :arg1],
|
24
|
+
[:fcall, :print,
|
25
|
+
[:array, [:lvar, :arg1]]],
|
26
|
+
[:fcall, :puts,
|
27
|
+
[:array,
|
28
|
+
[:call,
|
29
|
+
[:call,
|
30
|
+
[:lit, 4],
|
31
|
+
:+,
|
32
|
+
[:array, [:lit, 2]]],
|
33
|
+
:to_s]]]]]]
|
34
|
+
@@global = [:defn, :global,
|
35
|
+
[:scope,
|
36
|
+
[:block,
|
37
|
+
[:args],
|
38
|
+
[:call,
|
39
|
+
[:gvar, :$stderr],
|
40
|
+
:fputs,
|
41
|
+
[:array, [:str, "blah"]]]]]]
|
42
|
+
@@lasgn_call = [:defn, :lasgn_call,
|
43
|
+
[:scope,
|
44
|
+
[:block,
|
45
|
+
[:args],
|
46
|
+
[:lasgn, :c,
|
47
|
+
[:call,
|
48
|
+
[:lit, 2],
|
49
|
+
:+,
|
50
|
+
[:array, [:lit, 3]]]]]]]
|
51
|
+
@@conditional1 = [:defn, :conditional1,
|
52
|
+
[:scope,
|
53
|
+
[:block,
|
54
|
+
[:args, :arg1],
|
55
|
+
[:if,
|
56
|
+
[:call,
|
57
|
+
[:lvar, :arg1],
|
58
|
+
:==,
|
59
|
+
[:array, [:lit, 0]]],
|
60
|
+
[:return,
|
61
|
+
[:lit, 1]], nil]]]]
|
62
|
+
@@conditional2 = [:defn, :conditional2,
|
63
|
+
[:scope,
|
64
|
+
[:block,
|
65
|
+
[:args, :arg1],
|
66
|
+
[:if,
|
67
|
+
[:call,
|
68
|
+
[:lvar, :arg1],
|
69
|
+
:==,
|
70
|
+
[:array, [:lit, 0]]], nil,
|
71
|
+
[:return,
|
72
|
+
[:lit, 2]]]]]]
|
73
|
+
@@conditional3 = [:defn, :conditional3,
|
74
|
+
[:scope,
|
75
|
+
[:block,
|
76
|
+
[:args, :arg1],
|
77
|
+
[:if,
|
78
|
+
[:call,
|
79
|
+
[:lvar, :arg1],
|
80
|
+
:==,
|
81
|
+
[:array, [:lit, 0]]],
|
82
|
+
[:return,
|
83
|
+
[:lit, 3]],
|
84
|
+
[:return,
|
85
|
+
[:lit, 4]]]]]]
|
86
|
+
@@conditional4 = [:defn, :conditional4,
|
87
|
+
[:scope,
|
88
|
+
[:block,
|
89
|
+
[:args, :arg1],
|
90
|
+
[:if,
|
91
|
+
[:call,
|
92
|
+
[:lvar, :arg1],
|
93
|
+
:==,
|
94
|
+
[:array, [:lit, 0]]],
|
95
|
+
[:return, [:lit, 2]],
|
96
|
+
[:if,
|
97
|
+
[:call,
|
98
|
+
[:lvar, :arg1],
|
99
|
+
:<,
|
100
|
+
[:array, [:lit, 0]]],
|
101
|
+
[:return, [:lit, 3]],
|
102
|
+
[:return, [:lit, 4]]]]]]]
|
103
|
+
@@iteration_body = [:scope,
|
104
|
+
[:block,
|
105
|
+
[:args],
|
106
|
+
[:lasgn, :array,
|
107
|
+
[:array, [:lit, 1], [:lit, 2], [:lit, 3]]],
|
108
|
+
[:iter,
|
109
|
+
[:call,
|
110
|
+
[:lvar, :array], :each],
|
111
|
+
[:dasgn_curr, :x],
|
112
|
+
[:fcall, :puts, [:array, [:call, [:dvar, :x], :to_s]]]]]]
|
113
|
+
@@iteration1 = [:defn, :iteration1, @@iteration_body]
|
114
|
+
@@iteration2 = [:defn, :iteration2, @@iteration_body]
|
115
|
+
@@iteration3 = [:defn, :iteration3,
|
116
|
+
[:scope,
|
117
|
+
[:block,
|
118
|
+
[:args],
|
119
|
+
[:lasgn, :array1,
|
120
|
+
[:array, [:lit, 1], [:lit, 2], [:lit, 3]]],
|
121
|
+
[:lasgn, :array2,
|
122
|
+
[:array, [:lit, 4], [:lit, 5], [:lit, 6], [:lit, 7]]],
|
123
|
+
[:iter,
|
124
|
+
[:call,
|
125
|
+
[:lvar, :array1], :each],
|
126
|
+
[:dasgn_curr, :x],
|
127
|
+
[:iter,
|
128
|
+
[:call,
|
129
|
+
[:lvar, :array2], :each],
|
130
|
+
[:dasgn_curr, :y],
|
131
|
+
[:block,
|
132
|
+
[:fcall, :puts,
|
133
|
+
[:array, [:call, [:dvar, :x], :to_s]]],
|
134
|
+
[:fcall, :puts,
|
135
|
+
[:array, [:call, [:dvar, :y], :to_s]]]]]]]]]
|
136
|
+
@@iteration4 = [:defn,
|
137
|
+
:iteration4,
|
138
|
+
[:scope,
|
139
|
+
[:block,
|
140
|
+
[:args],
|
141
|
+
[:iter,
|
142
|
+
[:call, [:lit, 1], :upto, [:array, [:lit, 3]]],
|
143
|
+
[:dasgn_curr, :n],
|
144
|
+
[:fcall, :puts, [:array, [:call, [:dvar, :n], :to_s]]]]]]]
|
145
|
+
@@iteration5 = [:defn,
|
146
|
+
:iteration5,
|
147
|
+
[:scope,
|
148
|
+
[:block,
|
149
|
+
[:args],
|
150
|
+
[:iter,
|
151
|
+
[:call, [:lit, 3], :downto, [:array, [:lit, 1]]],
|
152
|
+
[:dasgn_curr, :n],
|
153
|
+
[:fcall, :puts, [:array, [:call, [:dvar, :n], :to_s]]]]]]]
|
154
|
+
@@iteration6 = [:defn,
|
155
|
+
:iteration6,
|
156
|
+
[:scope,
|
157
|
+
[:block,
|
158
|
+
[:args],
|
159
|
+
[:iter,
|
160
|
+
[:call, [:lit, 3], :downto, [:array, [:lit, 1]]],
|
161
|
+
nil,
|
162
|
+
[:fcall, :puts, [:array, [:str, "hello"]]]]]]]
|
163
|
+
@@multi_args = [:defn, :multi_args,
|
164
|
+
[:scope,
|
165
|
+
[:block,
|
166
|
+
[:args, :arg1, :arg2],
|
167
|
+
[:lasgn, :arg3,
|
168
|
+
[:call,
|
169
|
+
[:call,
|
170
|
+
[:lvar, :arg1],
|
171
|
+
:*,
|
172
|
+
[:array, [:lvar, :arg2]]],
|
173
|
+
:*,
|
174
|
+
[:array, [:lit, 7]]]],
|
175
|
+
[:fcall, :puts, [:array, [:call, [:lvar, :arg3], :to_s]]],
|
176
|
+
[:return,
|
177
|
+
[:str, "foo"]]]]]
|
178
|
+
@@bools = [:defn, :bools,
|
179
|
+
[:scope,
|
180
|
+
[:block,
|
181
|
+
[:args, :arg1],
|
182
|
+
[:if,
|
183
|
+
[:call,
|
184
|
+
[:lvar, :arg1], :nil?],
|
185
|
+
[:return,
|
186
|
+
[:false]],
|
187
|
+
[:return,
|
188
|
+
[:true]]]]]]
|
189
|
+
@@case_stmt = [:defn, :case_stmt,
|
190
|
+
[:scope,
|
191
|
+
[:block,
|
192
|
+
[:args],
|
193
|
+
[:lasgn, :var, [:lit, 2]],
|
194
|
+
[:lasgn, :result, [:str, ""]],
|
195
|
+
[:case,
|
196
|
+
[:lvar, :var],
|
197
|
+
[:when,
|
198
|
+
[:array, [:lit, 1]],
|
199
|
+
[:block,
|
200
|
+
[:fcall, :puts, [:array, [:str, "something"]]],
|
201
|
+
[:lasgn, :result, [:str, "red"]]]],
|
202
|
+
[:when,
|
203
|
+
[:array, [:lit, 2], [:lit, 3]],
|
204
|
+
[:lasgn, :result, [:str, "yellow"]]],
|
205
|
+
[:when, [:array, [:lit, 4]], nil],
|
206
|
+
[:lasgn, :result, [:str, "green"]]],
|
207
|
+
[:case,
|
208
|
+
[:lvar, :result],
|
209
|
+
[:when, [:array, [:str, "red"]], [:lasgn, :var, [:lit, 1]]],
|
210
|
+
[:when, [:array, [:str, "yellow"]], [:lasgn, :var, [:lit, 2]]],
|
211
|
+
[:when, [:array, [:str, "green"]], [:lasgn, :var, [:lit, 3]]],
|
212
|
+
nil],
|
213
|
+
[:return, [:lvar, :result]]]]]
|
214
|
+
@@eric_is_stubborn = [:defn,
|
215
|
+
:eric_is_stubborn,
|
216
|
+
[:scope,
|
217
|
+
[:block,
|
218
|
+
[:args],
|
219
|
+
[:lasgn, :var, [:lit, 42]],
|
220
|
+
[:lasgn, :var2, [:call, [:lvar, :var], :to_s]],
|
221
|
+
[:call, [:gvar, :$stderr], :fputs, [:array, [:lvar, :var2]]],
|
222
|
+
[:return, [:lvar, :var2]]]]]
|
223
|
+
@@interpolated = [:defn,
|
224
|
+
:interpolated,
|
225
|
+
[:scope,
|
226
|
+
[:block,
|
227
|
+
[:args],
|
228
|
+
[:lasgn, :var, [:lit, 14]],
|
229
|
+
[:lasgn, :var2, [:dstr, "var is ", [:lvar, :var], [:str, ". So there."]]]]]]
|
230
|
+
@@unknown_args = [:defn, :unknown_args,
|
231
|
+
[:scope,
|
232
|
+
[:block,
|
233
|
+
[:args, :arg1, :arg2],
|
234
|
+
[:return, [:lvar, :arg1]]]]]
|
235
|
+
@@determine_args = [:defn, :determine_args,
|
236
|
+
[:scope,
|
237
|
+
[:block,
|
238
|
+
[:args],
|
239
|
+
[:call,
|
240
|
+
[:lit, 5],
|
241
|
+
:==,
|
242
|
+
[:array,
|
243
|
+
[:fcall,
|
244
|
+
:unknown_args,
|
245
|
+
[:array, [:lit, 4], [:str, "known"]]]]]]]]
|
246
|
+
|
247
|
+
@@__all = [:class, :Something, :Object]
|
248
|
+
|
249
|
+
def setup
|
250
|
+
@thing = ParseTree.new
|
251
|
+
end
|
252
|
+
|
253
|
+
Something.instance_methods(false).sort.each do |meth|
|
254
|
+
if class_variables.include?("@@#{meth}") then
|
255
|
+
@@__all << eval("@@#{meth}")
|
256
|
+
eval "def test_#{meth}; assert_equal @@#{meth}, @thing.parse_tree_for_method(Something, :#{meth}); end"
|
257
|
+
else
|
258
|
+
eval "def test_#{meth}; flunk \"You haven't added @@#{meth} yet\"; end"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def test_missing
|
263
|
+
assert_equal(@@missing,
|
264
|
+
@thing.parse_tree_for_method(Something, :missing),
|
265
|
+
"Must return -3 for missing methods")
|
266
|
+
end
|
267
|
+
|
268
|
+
def test_class
|
269
|
+
assert_equal([@@__all],
|
270
|
+
@thing.parse_tree(Something),
|
271
|
+
"Must return a lot of shit")
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
|