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.
@@ -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
+