ParseTree 1.3.0 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
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