ParseTree 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- alias_method :unpack?, :unpack
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
- # TODO: need to write test
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
- def s(*args) # stupid shortcut to make indentation much cleaner
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
- if Sexp === sub_result && sub_result.unpack? then
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
- if list.first != typ
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
@@ -0,0 +1,13 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ pat = "test_*.rb"
4
+ if File.basename(Dir.pwd) != "test" then
5
+ $: << "test"
6
+ pat = File.join("test", pat)
7
+ end
8
+
9
+ Dir.glob(pat).each do |f|
10
+ require f
11
+ end
12
+
13
+ require 'test/unit'
@@ -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
+