ParseTree 1.3.0 → 1.3.2

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 CHANGED
@@ -1,3 +1,19 @@
1
+ *** 1.3.2 / 2005-01-04
2
+
3
+ + 1 minor enhancement
4
+ + Added total line to end of ABC metric report.
5
+ + 1 bug fix
6
+ + Updates for ruby 1.8.2 parse tree changes.
7
+
8
+ *** 1.3.1 / 2004-12-09
9
+
10
+ + 1 minor enhancement
11
+ + Added rewrite_<type> processing.
12
+ + 2 bug fixes
13
+ + Renamed SexpProcessor's #exclude to #unsupported.
14
+ + Fixed a bus error when an attrasgn uses self.
15
+ + Added support for cfunc now that we found a repro.
16
+
1
17
  *** 1.3.0 / 2004-12-06
2
18
 
3
19
  + 3 minor enhancements
data/Makefile CHANGED
@@ -1,10 +1,10 @@
1
1
  RUBY?=ruby
2
- RUBY_FLAGS?=-w -Ilib
2
+ RUBY_FLAGS?=-w -Ilib -Ibin
3
3
  RUBY_LIB?=$(shell $(RUBY) -rrbconfig -e 'include Config; print CONFIG["sitelibdir"]')
4
4
  PREFIX?=/usr/local
5
5
 
6
6
  all test: FORCE
7
- $(RUBY) $(RUBY_FLAGS) test/test_all.rb
7
+ GEM_SKIP=ParseTree $(RUBY) $(RUBY_FLAGS) test/test_all.rb
8
8
 
9
9
  # we only install test_sexp_processor.rb to help make ruby_to_c's
10
10
  # subclass tests work.
data/bin/parse_tree_abc CHANGED
@@ -13,6 +13,8 @@ rescue LoadError
13
13
  require 'parse_tree'
14
14
  end
15
15
 
16
+ require 'sexp_processor'
17
+
16
18
  old_classes = []
17
19
  ObjectSpace.each_object(Module) do |klass|
18
20
  old_classes << klass
@@ -31,20 +33,7 @@ score = {}
31
33
 
32
34
  new_classes -= old_classes
33
35
 
34
- def sexp_types(exp)
35
- result = []
36
- if Array === exp.first then
37
- result = sexp_types(exp.first)
38
- else
39
- result << exp.shift
40
- exp.grep(Array).each do |subexp|
41
- result.concat(sexp_types(subexp))
42
- end
43
- end
44
- result
45
- end
46
-
47
- klasses = ParseTree.new.parse_tree(*new_classes)
36
+ klasses = Sexp.from_array(ParseTree.new.parse_tree(*new_classes))
48
37
  klasses.each do |klass|
49
38
  klass.shift # :class
50
39
  klassname = klass.shift
@@ -55,7 +44,7 @@ klasses.each do |klass|
55
44
  a=b=c=0
56
45
  defn.shift
57
46
  methodname = defn.shift
58
- tokens = sexp_types(defn)
47
+ tokens = defn.structure.flatten
59
48
  tokens.each do |token|
60
49
  case token
61
50
  when :attrasgn, :attrset, :dasgn_curr, :iasgn, :lasgn, :masgn then
@@ -79,8 +68,14 @@ end
79
68
  puts "Method = assignments + branches + calls = total"
80
69
  puts
81
70
  count = 1
71
+ ta = tb = tc = tval = 0
82
72
  score.sort_by { |k,v| v }.reverse.each do |key,val|
83
73
  name, a, b, c = *key
74
+ ta += a
75
+ tb += b
76
+ tc += c
77
+ tval += val
84
78
  printf "%3d) %-50s = %2d + %2d + %2d = %3d\n", count, name, a, b, c, val
85
79
  count += 1
86
80
  end
81
+ printf "%3d) %-50s = %2d + %2d + %2d = %3d\n", count, "Total", ta, tb, tc, tval
data/lib/parse_tree.rb CHANGED
@@ -27,7 +27,7 @@ end
27
27
 
28
28
  class ParseTree
29
29
 
30
- VERSION = '1.3.0'
30
+ VERSION = '1.3.2'
31
31
 
32
32
  ##
33
33
  # Initializes a ParseTree instance. Includes newline nodes if
@@ -647,7 +647,11 @@ again_no_block:
647
647
 
648
648
  case NODE_ATTRASGN: // literal.meth = y u1 u2 u3
649
649
  // node id node
650
- add_to_parse_tree(current, node->nd_1st, newlines, locals);
650
+ if (node->nd_1st == RNODE(1)) {
651
+ add_to_parse_tree(current, NEW_SELF(), newlines, locals);
652
+ } else {
653
+ add_to_parse_tree(current, node->nd_1st, newlines, locals);
654
+ }
651
655
  rb_ary_push(current, ID2SYM(node->u2.id));
652
656
  add_to_parse_tree(current, node->nd_3rd, newlines, locals);
653
657
  break;
@@ -664,10 +668,14 @@ again_no_block:
664
668
  // Nothing to do here... we are in an iter block
665
669
  break;
666
670
 
671
+ case NODE_CFUNC:
672
+ rb_ary_push(current, INT2FIX(node->nd_cfnc));
673
+ rb_ary_push(current, INT2FIX(node->nd_argc));
674
+ break;
675
+
667
676
  // Nodes we found but have yet to decypher
668
677
  // I think these are all runtime only... not positive but...
669
678
  case NODE_MEMO: // enum.c zip
670
- case NODE_CFUNC:
671
679
  case NODE_CREF:
672
680
  case NODE_IFUNC:
673
681
  // #defines:
@@ -36,6 +36,25 @@ class Sexp < Array # ZenTest FULL
36
36
  super(args)
37
37
  end
38
38
 
39
+ def self.from_array(a)
40
+ ary = Array === a ? a : [a]
41
+
42
+ result = self.new
43
+
44
+ ary.each do |x|
45
+ case x
46
+ when Sexp
47
+ result << x
48
+ when Array
49
+ result << self.from_array(x)
50
+ else
51
+ result << x
52
+ end
53
+ end
54
+
55
+ result
56
+ end
57
+
39
58
  ##
40
59
  # Returns true if the node_type is +array+ or +args+.
41
60
  #
@@ -110,6 +129,23 @@ class Sexp < Array # ZenTest FULL
110
129
  self[1..-1]
111
130
  end
112
131
 
132
+ ##
133
+ # Returnes the bare bones structure of the sexp.
134
+ # s(:a, :b, s(:c, :d), :e) => s(:a, s(:c))
135
+
136
+ def structure
137
+ result = self.class.new
138
+ if Array === self.first then
139
+ result = self.first.structure
140
+ else
141
+ result << self.shift
142
+ self.grep(Array).each do |subexp|
143
+ result << subexp.structure
144
+ end
145
+ end
146
+ result
147
+ end
148
+
113
149
  def ==(obj) # :nodoc:
114
150
  case obj
115
151
  when Sexp
@@ -125,7 +161,7 @@ class Sexp < Array # ZenTest FULL
125
161
 
126
162
  def inspect # :nodoc:
127
163
  sexp_str = self.map {|x|x.inspect}.join(', ')
128
- return "Sexp.new(#{sexp_str})"
164
+ return "s(#{sexp_str})"
129
165
  end
130
166
 
131
167
  def pretty_print(q) # :nodoc:
@@ -145,7 +181,7 @@ class Sexp < Array # ZenTest FULL
145
181
  def shift
146
182
  raise "I'm empty" if self.empty?
147
183
  super
148
- end if $DEBUG and $TESTING
184
+ end if $DEBUG or $TESTING
149
185
 
150
186
  end
151
187
 
@@ -156,6 +192,18 @@ def s(*args)
156
192
  Sexp.new(*args)
157
193
  end
158
194
 
195
+ ##
196
+ # Raised by SexpProcessor if it sees a node type listed in its
197
+ # unsupported list.
198
+
199
+ class UnsupportedNodeError < SyntaxError; end
200
+
201
+ ##
202
+ # Raised by SexpProcessor if a processor did not process every node in
203
+ # a sexp and @require_empty is true.
204
+
205
+ class NotEmptyError < SyntaxError; end
206
+
159
207
  ##
160
208
  # SexpProcessor provides a uniform interface to process Sexps.
161
209
  #
@@ -163,12 +211,13 @@ end
163
211
  # to call super in the initialize method, then set any of the
164
212
  # Sexp flags you want to be different from the defaults.
165
213
  #
166
- # SexpProcessor uses a Sexp's type to determine which process
167
- # method to call in the subclass. For Sexp <code>s(:lit,
168
- # 1)</code> SexpProcessor will call #process_lit.
214
+ # SexpProcessor uses a Sexp's type to determine which process method
215
+ # to call in the subclass. For Sexp <code>s(:lit, 1)</code>
216
+ # SexpProcessor will call #process_lit, if it is defined.
169
217
  #
170
- # You can also provide a default method to call for any Sexp
171
- # types without a process_ method.
218
+ # You can also specify a default method to call for any Sexp types
219
+ # without a process_<type> method or use the default processor provided to
220
+ # skip over them.
172
221
  #
173
222
  # Here is a simple example:
174
223
  #
@@ -187,7 +236,7 @@ end
187
236
  class SexpProcessor
188
237
 
189
238
  ##
190
- # A default method to call if a process_ method is not found
239
+ # A default method to call if a process_<type> method is not found
191
240
  # for the Sexp type.
192
241
 
193
242
  attr_accessor :default_method
@@ -199,18 +248,19 @@ class SexpProcessor
199
248
 
200
249
  ##
201
250
  # Automatically shifts off the Sexp type before handing the
202
- # Sexp to process_
251
+ # Sexp to process_<type>
203
252
 
204
253
  attr_accessor :auto_shift_type
205
254
 
206
255
  ##
207
- # A list of Sexp types. Raises an exception if a Sexp type in
208
- # this list is encountered.
256
+ # An array that specifies node types that are unsupported by this
257
+ # processor. SexpProcesor will raise UnsupportedNodeError if you try
258
+ # to process one of those node types.
209
259
 
210
- attr_accessor :exclude
260
+ attr_accessor :unsupported
211
261
 
212
262
  ##
213
- # Raise an exception if no process_ method is found for a Sexp.
263
+ # Raise an exception if no process_<type> method is found for a Sexp.
214
264
 
215
265
  attr_accessor :strict
216
266
 
@@ -249,7 +299,7 @@ class SexpProcessor
249
299
  @warn_on_default = true
250
300
  @auto_shift_type = false
251
301
  @strict = false
252
- @exclude = []
302
+ @unsupported = []
253
303
  @debug = {}
254
304
  @expected = Sexp
255
305
  @require_empty = true
@@ -257,18 +307,23 @@ class SexpProcessor
257
307
 
258
308
  # we do this on an instance basis so we can subclass it for
259
309
  # different processors.
260
- @methods = {}
310
+ @processors = {}
311
+ @rewriters = {}
261
312
 
262
313
  public_methods.each do |name|
263
- next unless name =~ /^process_(.*)/
264
- @methods[$1.intern] = name.intern
314
+ case name
315
+ when /^process_(.*)/ then
316
+ @processors[$1.intern] = name.intern
317
+ when /^rewrite_(.*)/ then
318
+ @rewriters[$1.intern] = name.intern
319
+ end
265
320
  end
266
321
  end
267
322
 
268
323
  ##
269
- # Default Sexp processor. Invokes process_ methods matching
270
- # the Sexp type given. Performs additional checks as specified
271
- # by the initializer.
324
+ # Default Sexp processor. Invokes process_<type> methods matching
325
+ # the Sexp type given. Performs additional checks as specified by
326
+ # the initializer.
272
327
 
273
328
  def process(exp)
274
329
  return nil if exp.nil?
@@ -291,22 +346,42 @@ class SexpProcessor
291
346
  end
292
347
  end
293
348
 
294
- raise SyntaxError, "'#{type}' is not a supported node type." if @exclude.include? type
349
+ raise UnsupportedNodeError, "'#{type}' is not a supported node type." if @unsupported.include? type
295
350
 
296
- meth = @methods[type] || @default_method
351
+ # do a pass through the rewriter first, if any, reassign back to exp
352
+ meth = @rewriters[type]
297
353
  if meth then
298
- if @warn_on_default and meth == @default_method then
299
- $stderr.puts "WARNING: falling back to default method #{meth} for #{exp.first}"
354
+ new_exp = self.send(meth, exp)
355
+ # REFACTOR: duplicated from below
356
+ if @require_empty and not exp.empty? then
357
+ msg = "exp not empty after #{self.class}.#{meth} on #{exp.inspect}"
358
+ if $DEBUG then
359
+ msg += " from #{exp_orig.inspect}"
360
+ end
361
+ raise NotEmptyError, msg
300
362
  end
301
- if @auto_shift_type and meth != @default_method then
302
- exp.shift
363
+ exp = new_exp
364
+ end
365
+
366
+ # now do a pass with the real processor (or generic
367
+ meth = @processors[type] || @default_method
368
+ if meth then
369
+
370
+ if @warn_on_default and meth == @default_method then
371
+ $stderr.puts "WARNING: Using default method #{meth} for #{type}"
303
372
  end
373
+
374
+ exp.shift if @auto_shift_type and meth != @default_method
375
+
304
376
  result = self.send(meth, exp)
305
377
  raise TypeError, "Result must be a #{@expected}, was #{result.class}:#{result.inspect}" unless @expected === result
306
- if $DEBUG then
307
- raise "exp not empty after #{self.class}.#{meth} on #{exp.inspect} from #{exp_orig.inspect}" if @require_empty and not exp.empty?
308
- else
309
- raise "exp not empty after #{self.class}.#{meth} on #{exp.inspect}" if @require_empty and not exp.empty?
378
+
379
+ if @require_empty and not exp.empty? then
380
+ msg = "exp not empty after #{self.class}.#{meth} on #{exp.inspect}"
381
+ if $DEBUG then
382
+ msg += " from #{exp_orig.inspect}"
383
+ end
384
+ raise NotEmptyError, msg
310
385
  end
311
386
  else
312
387
  unless @strict then
data/test/something.rb CHANGED
@@ -159,6 +159,11 @@ class Something
159
159
  5 == unknown_args(4, "known")
160
160
  end
161
161
 
162
+ def attrasgn
163
+ 42.method = y
164
+ self.type = other.type
165
+ end
166
+
162
167
  def self.bmethod_maker
163
168
  define_method(:bmethod_added) do |x|
164
169
  x + 1
@@ -11,7 +11,9 @@ class TestParseTree < Test::Unit::TestCase
11
11
  @@missing = [nil]
12
12
  @@empty = [:defn, :empty,
13
13
  [:scope,
14
- [:args]]]
14
+ [:block,
15
+ [:args],
16
+ [:nil]]]]
15
17
  @@stupid = [:defn, :stupid,
16
18
  [:scope,
17
19
  [:block,
@@ -262,8 +264,19 @@ class TestParseTree < Test::Unit::TestCase
262
264
  [:dasgn_curr, :x],
263
265
  [:call, [:dvar, :x], :+, [:array, [:lit, 1]]]]]]]]
264
266
 
267
+ @@attrasgn = [:defn,
268
+ :attrasgn,
269
+ [:scope,
270
+ [:block,
271
+ [:args],
272
+ [:attrasgn, [:lit, 42], :method=, [:array, [:vcall, :y]]],
273
+ [:attrasgn,
274
+ [:self],
275
+ :type=,
276
+ [:array, [:call, [:vcall, :other], :type]]]]]]
277
+
265
278
  @@__all = [:class, :Something, :Object]
266
-
279
+
267
280
  def setup
268
281
  @thing = ParseTree.new(false)
269
282
  end
@@ -36,7 +36,7 @@ class TestProcessor < SexpProcessor # ZenTest SKIP
36
36
  end
37
37
 
38
38
  def process_nonempty(exp)
39
- exp
39
+ s(*exp)
40
40
  end
41
41
 
42
42
  def process_broken(exp)
@@ -54,6 +54,14 @@ class TestProcessor < SexpProcessor # ZenTest SKIP
54
54
  return exp.shift
55
55
  end
56
56
 
57
+ def rewrite_rewritable(exp)
58
+ return s(exp.shift, exp.pop, exp.shift)
59
+ end
60
+
61
+ def process_rewritable(exp)
62
+ return s(exp.shift, exp.shift == 2, exp.shift == 1)
63
+ end
64
+
57
65
  end
58
66
 
59
67
  class TestProcessorDefault < SexpProcessor # ZenTest SKIP
@@ -72,19 +80,15 @@ end
72
80
 
73
81
  class TestSexp < Test::Unit::TestCase # ZenTest FULL
74
82
 
75
- def util_sexp_class
76
- Object.const_get(self.class.name[4..-1])
77
- end
78
-
79
83
  def setup
80
- @sexp_class = util_sexp_class
84
+ @sexp_class = Object.const_get(self.class.name[4..-1])
81
85
  @processor = SexpProcessor.new
82
86
  @sexp = @sexp_class.new(1, 2, 3)
83
87
  end
84
88
 
85
89
  def test_new_nested
86
- @sexp = Sexp.new(:lasgn, "var", Sexp.new(:str, "foo"))
87
- assert_equal('Sexp.new(:lasgn, "var", Sexp.new(:str, "foo"))',
90
+ @sexp = s(:lasgn, "var", s(:str, "foo"))
91
+ assert_equal('s(:lasgn, "var", s(:str, "foo"))',
88
92
  @sexp.inspect)
89
93
  end
90
94
 
@@ -99,12 +103,12 @@ class TestSexp < Test::Unit::TestCase # ZenTest FULL
99
103
  end
100
104
 
101
105
  def test_equals_sexp
102
- sexp2 = Sexp.new(1, 2, 3)
106
+ sexp2 = s(1, 2, 3)
103
107
  assert_equal(@sexp, sexp2)
104
108
  end
105
109
 
106
110
  def test_equals_not_body
107
- sexp2 = Sexp.new(1, 2, 5)
111
+ sexp2 = s(1, 2, 5)
108
112
  assert_not_equal(@sexp, sexp2)
109
113
  end
110
114
 
@@ -159,20 +163,11 @@ class TestSexp < Test::Unit::TestCase # ZenTest FULL
159
163
  assert_equal(expected, @sexp)
160
164
  end
161
165
 
162
- def test_inspect
163
- k = @sexp_class
164
- assert_equal("#{k}.new()",
165
- k.new().inspect)
166
- assert_equal("#{k}.new(:a)",
167
- k.new(:a).inspect)
168
- assert_equal("#{k}.new(:a, :b)",
169
- k.new(:a, :b).inspect)
170
- assert_equal("#{k}.new(:a, #{k}.new(:b))",
171
- k.new(:a, k.new(:b)).inspect)
172
- end
166
+ def test_structure
167
+ @sexp = s(:a, 1, 2, s(:b, 3, 4), 5, 6)
168
+ expected = s(:a, s(:b))
173
169
 
174
- def test_to_s
175
- test_inspect
170
+ assert_equal(expected, @sexp.structure)
176
171
  end
177
172
 
178
173
  def test_method_missing
@@ -187,6 +182,23 @@ class TestSexp < Test::Unit::TestCase # ZenTest FULL
187
182
  end
188
183
  end
189
184
 
185
+ def test_inspect
186
+ k = @sexp_class
187
+ n = k.name[0].chr.downcase
188
+ assert_equal("#{n}()",
189
+ k.new().inspect)
190
+ assert_equal("#{n}(:a)",
191
+ k.new(:a).inspect)
192
+ assert_equal("#{n}(:a, :b)",
193
+ k.new(:a, :b).inspect)
194
+ assert_equal("#{n}(:a, #{n}(:b))",
195
+ k.new(:a, k.new(:b)).inspect)
196
+ end
197
+
198
+ def test_to_s
199
+ test_inspect
200
+ end
201
+
190
202
  def util_pretty_print(expect, input)
191
203
  io = StringIO.new
192
204
  PP.pp(input, io)
@@ -209,9 +221,11 @@ class TestSexp < Test::Unit::TestCase # ZenTest FULL
209
221
  assert_equal(1, @sexp.shift)
210
222
  assert_equal(2, @sexp.shift)
211
223
  assert_equal(3, @sexp.shift)
212
- assert_nil(@sexp.shift)
213
- end
214
224
 
225
+ assert_raise(RuntimeError) do
226
+ @sexp.shift
227
+ end
228
+ end
215
229
  end
216
230
 
217
231
  class TestSexpProcessor < Test::Unit::TestCase
@@ -273,15 +287,14 @@ class TestSexpProcessor < Test::Unit::TestCase
273
287
  end
274
288
  end
275
289
 
276
- def test_exclude
277
- @processor.exclude = [ :blah ]
278
- assert_raise(SyntaxError) do
279
- @processor.process([:blah, 1, 2, 3])
290
+ def test_unsupported_equal
291
+ @processor.strict = true
292
+ @processor.unsupported = [ :unsupported ]
293
+ assert_raises(UnsupportedNodeError) do
294
+ @processor.process([:unsupported, 42])
280
295
  end
281
296
  end
282
297
 
283
- def test_exclude=; end # Handled
284
-
285
298
  def test_strict
286
299
  @processor.strict = true
287
300
  assert_raise(SyntaxError) do
@@ -300,7 +313,7 @@ class TestSexpProcessor < Test::Unit::TestCase
300
313
  end
301
314
 
302
315
  def test_require_empty_true
303
- assert_raise(TypeError) do
316
+ assert_raise(NotEmptyError) do
304
317
  @processor.process([:nonempty, 1, 2, 3])
305
318
  end
306
319
  end
@@ -311,6 +324,17 @@ class TestSexpProcessor < Test::Unit::TestCase
311
324
  assert_equal([1, 2, 3], @processor.process(s(:strip, 1, 2, 3)))
312
325
  end
313
326
 
327
+ def test_process_rewrite
328
+ assert_equal(s(:rewritable, true, true),
329
+ @processor.process(s(:rewritable, 1, 2)))
330
+ end
331
+
332
+ def test_process_rewrite_not_empty
333
+ assert_raise(NotEmptyError) do
334
+ @processor.process(s(:rewritable, 1, 2, 2))
335
+ end
336
+ end
337
+
314
338
  def test_assert_type_hit
315
339
  assert_nothing_raised do
316
340
  @processor.assert_type([:blah, 1, 2, 3], :blah)
@@ -353,7 +377,7 @@ class TestSexpProcessor < Test::Unit::TestCase
353
377
 
354
378
  @processor.expected = Hash
355
379
  assert_equal Hash, @processor.expected
356
- assert !(Hash === Sexp.new()), "Hash === Sexp.new should not be true"
380
+ assert !(Hash === s()), "Hash === s() should not be true"
357
381
 
358
382
  assert_raises(TypeError) do
359
383
  @processor.process(s(:string, "string")) # should raise
@@ -370,4 +394,3 @@ class TestSexpProcessor < Test::Unit::TestCase
370
394
  def test_warn_on_default=; end
371
395
 
372
396
  end
373
-
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.1
3
3
  specification_version: 1
4
4
  name: ParseTree
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.3.0
7
- date: 2004-12-07
6
+ version: 1.3.2
7
+ date: 2005-01-04
8
8
  summary: Extract and enumerate ruby parse trees.
9
9
  require_paths:
10
10
  - lib