ParseTree 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,62 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # ABC metric
4
+ #
5
+ # Assignments, Branches, and Calls
6
+ #
7
+ # A simple way to measure the complexity of a function or method.
8
+
9
+ require 'parse_tree'
10
+
11
+ old_classes = []
12
+ ObjectSpace.each_object(Module) do |klass|
13
+ old_classes << klass
14
+ end
15
+
16
+ ARGV.each do |name|
17
+ require name
18
+ end
19
+
20
+ new_classes = []
21
+ ObjectSpace.each_object(Module) do |klass|
22
+ new_classes << klass
23
+ end
24
+
25
+ score = {}
26
+
27
+ new_classes -= old_classes
28
+
29
+ new_classes.each do |klass|
30
+ ParseTree.new.parse_tree(klass).each do |defn|
31
+ a=b=c=0
32
+ defn.shift
33
+ name = defn.shift
34
+ tokens = defn.flatten.find_all { |t| Symbol === t }
35
+ tokens.each do |token|
36
+ case token
37
+ when :attrasgn, :attrset, :dasgn_curr, :iasgn, :lasgn, :masgn then
38
+ a += 1
39
+ when :and, :case, :else, :if, :iter, :or, :rescue, :until, :when, :while then
40
+ b += 1
41
+ when :call, :fcall, :super, :vcall, :yield then
42
+ c += 1
43
+ when :args, :argscat, :array, :begin, :block, :bool, :cfunc, :colon2, :const, :cvar, :defined, :defn, :dregx, :dstr, :dvar, :dxstr, :ensure, :false, :fbody, :gvar, :hash, :ivar, :lit, :long, :lvar, :match2, :match3, :nil, :not, :nth_ref, :return, :scope, :self, :splat, :str, :to_ary, :true, :unknown, :value, :void, :zarray, :zarray, :zclass, :zsuper then
44
+ # ignore
45
+ else
46
+ puts "unhandled token #{token.inspect}"
47
+ end
48
+ end
49
+ key = ["#{klass}.#{name}", a, b, c]
50
+ val = a+b+c
51
+ score[key] = val
52
+ end
53
+ end
54
+
55
+ puts "Method = assignments + branches + calls = total"
56
+ puts
57
+ count = 1
58
+ score.sort_by { |k,v| v }.reverse.each do |key,val|
59
+ name, a, b, c = *key
60
+ printf "%3d) %-50s = %2d + %2d + %2d = %3d\n", count, name, a, b, c, val
61
+ count += 1
62
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require 'pp'
4
+ require 'parse_tree'
5
+
6
+ old_classes = []
7
+ ObjectSpace.each_object(Module) do |klass|
8
+ old_classes << klass
9
+ end
10
+
11
+ ARGV.each do |name|
12
+ if name == "-" then
13
+ eval $stdin.read
14
+ else
15
+ require name
16
+ end
17
+ end
18
+
19
+ new_classes = []
20
+ ObjectSpace.each_object(Module) do |klass|
21
+ new_classes << klass
22
+ end
23
+
24
+ new_classes -= old_classes
25
+
26
+ new_classes.each do |klass|
27
+ pp ParseTree.new.parse_tree(klass)
28
+ end
@@ -0,0 +1,301 @@
1
+
2
+ $TESTING = false unless defined? $TESTING
3
+
4
+ class Object
5
+ def deep_clone
6
+ Marshal.load(Marshal.dump(self))
7
+ end
8
+ end
9
+
10
+ class Sexp < Array # ZenTest FULL
11
+
12
+ @@array_types = [ :array, :args, ]
13
+
14
+ attr_accessor :accessors
15
+ attr_accessor :unpack
16
+
17
+ alias_method :unpack?, :unpack
18
+
19
+ def initialize(*args)
20
+ @unpack = false
21
+ @accessors = []
22
+ super(args)
23
+ end
24
+
25
+ def array_type?
26
+ type = self.first
27
+ @@array_types.include? type
28
+ end
29
+
30
+ def each_of_type(t, &b)
31
+ each do | elem |
32
+ if Sexp === elem then
33
+ elem.each_of_type(t, &b)
34
+ b.call(elem) if elem.first == t
35
+ end
36
+ end
37
+ end
38
+
39
+ # TODO: need to write test
40
+ def find_and_replace_all(from, to)
41
+ each_with_index do | elem, index |
42
+ if Sexp === elem then
43
+ elem.find_and_replace_all(from, to)
44
+ else
45
+ self[index] = to if elem == from
46
+ end
47
+ end
48
+ end
49
+
50
+ def method_missing(meth, *a, &b)
51
+ super unless @accessors.include? meth
52
+
53
+ index = @accessors.index(meth) + 1 # skip type
54
+ return self.at(index)
55
+ end
56
+
57
+ def sexp_body
58
+ self[1..-1]
59
+ end
60
+
61
+ def ==(obj)
62
+ case obj
63
+ when Sexp
64
+ super
65
+ else
66
+ false
67
+ end
68
+ end
69
+
70
+ def to_a
71
+ self.map { |o| Sexp === o ? o.to_a : o }
72
+ end
73
+
74
+ def inspect
75
+ sexp_str = self.map {|x|x.inspect}.join(', ')
76
+ return "Sexp.new(#{sexp_str})"
77
+ end
78
+
79
+ def pretty_print(q)
80
+ q.group(1, 's(', ')') do
81
+ q.seplist(self) {|v| q.pp v }
82
+ end
83
+ end
84
+
85
+ def to_s
86
+ inspect
87
+ end
88
+
89
+ def shift
90
+ raise "I'm empty" if self.empty?
91
+ super
92
+ end if $DEBUG and $TESTING
93
+
94
+ end
95
+
96
+ def s(*args) # stupid shortcut to make indentation much cleaner
97
+ Sexp.new(*args)
98
+ end
99
+
100
+ ##
101
+ # SexpProcessor provides a uniform interface to process Sexps.
102
+ #
103
+ # In order to create your own SexpProcessor subclass you'll need
104
+ # to call super in the initialize method, then set any of the
105
+ # Sexp flags you want to be different from the defaults.
106
+ #
107
+ # SexpProcessor uses a Sexp's type to determine which process
108
+ # method to call in the subclass. For Sexp <code>s(:lit,
109
+ # 1)</code> SexpProcessor will call #process_lit.
110
+ #
111
+ # You can also provide a default method to call for any Sexp
112
+ # types without a process_ method.
113
+ #
114
+ # Here is a simple example:
115
+ #
116
+ # class MyProcessor < SexpProcessor
117
+ #
118
+ # def initialize
119
+ # super
120
+ # self.strict = false
121
+ # end
122
+ #
123
+ # def process_lit(exp)
124
+ # val = exp.shift
125
+ # return val
126
+ # end
127
+ #
128
+ # end
129
+
130
+ class SexpProcessor
131
+
132
+ ##
133
+ # A default method to call if a process_ method is not found
134
+ # for the Sexp type.
135
+
136
+ attr_accessor :default_method
137
+
138
+ ##
139
+ # Emit a warning when the method in #default_method is called.
140
+
141
+ attr_accessor :warn_on_default
142
+
143
+ ##
144
+ # Automatically shifts off the Sexp type before handing the
145
+ # Sexp to process_
146
+
147
+ attr_accessor :auto_shift_type
148
+
149
+ ##
150
+ # A list of Sexp types. Raises an exception if a Sexp type in
151
+ # this list is encountered.
152
+
153
+ attr_accessor :exclude
154
+
155
+ ##
156
+ # Raise an exception if no process_ method is found for a Sexp.
157
+
158
+ attr_accessor :strict
159
+
160
+ ##
161
+ # A Hash of Sexp types and Regexp.
162
+ #
163
+ # Print a debug message if the Sexp type matches the Hash key
164
+ # and the Sexp's #inspect output matches the Regexp.
165
+
166
+ attr_accessor :debug
167
+
168
+ ##
169
+ # Expected result class
170
+
171
+ attr_accessor :expected
172
+
173
+ ##
174
+ # Raise an exception if the Sexp is not empty after processing
175
+
176
+ attr_accessor :require_empty
177
+
178
+ ##
179
+ # Adds accessor methods to the Sexp
180
+
181
+ attr_accessor :sexp_accessors
182
+
183
+ ##
184
+ # Creates a new SexpProcessor. Use super to invoke this
185
+ # initializer from SexpProcessor subclasses, then use the
186
+ # attributes above to customize the functionality of the
187
+ # SexpProcessor
188
+
189
+ def initialize
190
+ @collection = []
191
+ @default_method = nil
192
+ @warn_on_default = true
193
+ @auto_shift_type = false
194
+ @strict = false
195
+ @exclude = []
196
+ @debug = {}
197
+ @expected = Sexp
198
+ @require_empty = true
199
+ @sexp_accessors = {}
200
+
201
+ # we do this on an instance basis so we can subclass it for
202
+ # different processors.
203
+ @methods = {}
204
+
205
+ public_methods.each do |name|
206
+ next unless name =~ /^process_(.*)/
207
+ @methods[$1.intern] = name.intern
208
+ end
209
+ end
210
+
211
+ ##
212
+ # Default Sexp processor. Invokes process_ methods matching
213
+ # the Sexp type given. Performs additional checks as specified
214
+ # by the initializer.
215
+
216
+ def process(exp)
217
+ return nil if exp.nil?
218
+
219
+ exp_orig = exp.deep_clone if $DEBUG
220
+ result = self.expected.new
221
+
222
+ type = exp.first
223
+
224
+ if @debug.include? type then
225
+ str = exp.inspect
226
+ puts "// DEBUG: #{str}" if str =~ @debug[type]
227
+ end
228
+
229
+ if Sexp === exp then
230
+ if @sexp_accessors.include? type then
231
+ exp.accessors = @sexp_accessors[type]
232
+ else
233
+ exp.accessors = [] # clean out accessor list in case it changed
234
+ end
235
+ end
236
+
237
+ raise SyntaxError, "'#{type}' is not a supported node type." if @exclude.include? type
238
+
239
+ meth = @methods[type] || @default_method
240
+ if meth then
241
+ if @warn_on_default and meth == @default_method then
242
+ $stderr.puts "WARNING: falling back to default method #{meth} for #{exp.first}"
243
+ end
244
+ if @auto_shift_type and meth != @default_method then
245
+ exp.shift
246
+ end
247
+ result = self.send(meth, exp)
248
+ raise TypeError, "Result must be a #{@expected}, was #{result.class}:#{result.inspect}" unless @expected === result
249
+ if $DEBUG then
250
+ raise "exp not empty after #{self.class}.#{meth} on #{exp.inspect} from #{exp_orig.inspect}" if @require_empty and not exp.empty?
251
+ else
252
+ raise "exp not empty after #{self.class}.#{meth} on #{exp.inspect}" if @require_empty and not exp.empty?
253
+ end
254
+ else
255
+ unless @strict then
256
+ until exp.empty? do
257
+ sub_exp = exp.shift
258
+ sub_result = nil
259
+ if Array === sub_exp then
260
+ sub_result = process(sub_exp)
261
+ raise "Result is a bad type" unless Array === sub_exp
262
+ raise "Result does not have a type in front: #{sub_exp.inspect}" unless Symbol === sub_exp.first unless sub_exp.empty?
263
+ else
264
+ sub_result = sub_exp
265
+ end
266
+ if Sexp === sub_result && sub_result.unpack? then
267
+ result.push(*sub_result)
268
+ else
269
+ result << sub_result
270
+ end
271
+ end
272
+
273
+ # NOTE: this is costly, but we are in the generic processor
274
+ # so we shouldn't hit it too much with RubyToC stuff at least.
275
+ #if Sexp === exp and not exp.sexp_type.nil? then
276
+ begin
277
+ result.sexp_type = exp.sexp_type
278
+ rescue Exception
279
+ # nothing to do, on purpose
280
+ end
281
+ else
282
+ raise SyntaxError, "Bug! Unknown type #{type.inspect} to #{self.class}"
283
+ end
284
+ end
285
+ result
286
+ end
287
+
288
+ def generate # :nodoc:
289
+ raise "not implemented yet"
290
+ end
291
+
292
+ ##
293
+ # Raises unless the Sexp type for +list+ matches +typ+
294
+
295
+ def assert_type(list, typ)
296
+ raise TypeError, "Expected type #{typ.inspect} in #{list.inspect}" \
297
+ if list.first != typ
298
+ end
299
+
300
+ end
301
+
@@ -0,0 +1,162 @@
1
+
2
+ class Something
3
+
4
+ # basically: do we work at all?
5
+ def empty
6
+ end
7
+
8
+ # First order transformation: basic language constructs
9
+ def stupid
10
+ return nil
11
+ end
12
+
13
+ def simple(arg1)
14
+ print arg1
15
+ puts((4 + 2).to_s)
16
+ end
17
+
18
+ def global
19
+ $stderr.fputs("blah")
20
+ end
21
+
22
+ def lasgn_call
23
+ c = 2 + 3
24
+ end
25
+
26
+ def conditional1(arg1)
27
+ if arg1 == 0 then
28
+ return 1
29
+ end
30
+ end
31
+
32
+ def conditional2(arg1)
33
+ unless arg1 == 0 then
34
+ return 2
35
+ end
36
+ end
37
+
38
+ def conditional3(arg1)
39
+ if arg1 == 0 then
40
+ return 3
41
+ else
42
+ return 4
43
+ end
44
+ end
45
+
46
+ def conditional4(arg1)
47
+ if arg1 == 0 then
48
+ return 2
49
+ elsif arg1 < 0 then
50
+ return 3
51
+ else
52
+ return 4
53
+ end
54
+ end
55
+
56
+ def iteration1
57
+ array = [1, 2, 3]
58
+ array.each do |x|
59
+ puts(x.to_s)
60
+ end
61
+ end
62
+
63
+ def iteration2
64
+ array = [1, 2, 3]
65
+ array.each { |x| puts(x.to_s) }
66
+ end
67
+
68
+ def iteration3
69
+ array1 = [1, 2, 3]
70
+ array2 = [4, 5, 6, 7]
71
+ array1.each do |x|
72
+ array2.each do |y|
73
+ puts(x.to_s)
74
+ puts(y.to_s)
75
+ end
76
+ end
77
+ end
78
+
79
+ def iteration4
80
+ 1.upto(3) do |n|
81
+ puts n.to_s
82
+ end
83
+ end
84
+
85
+ def iteration5
86
+ 3.downto(1) do |n|
87
+ puts n.to_s
88
+ end
89
+ end
90
+
91
+ def iteration6
92
+ 3.downto(1) do
93
+ puts "hello"
94
+ end
95
+ end
96
+
97
+ def case_stmt
98
+ var = 2
99
+ result = ""
100
+ case var
101
+ when 1 then
102
+ # block
103
+ puts "something"
104
+ result = "red"
105
+ when 2, 3 then
106
+ result = "yellow"
107
+ when 4 then
108
+ # nothing
109
+ else
110
+ result = "green"
111
+ end
112
+
113
+ case result
114
+ when "red" then
115
+ var = 1
116
+ when "yellow" then
117
+ var = 2
118
+ when "green" then
119
+ var = 3
120
+ end
121
+
122
+ return result
123
+ end
124
+
125
+ # Other edge cases:
126
+
127
+ def multi_args(arg1, arg2)
128
+ arg3 = arg1 * arg2 * 7
129
+ puts(arg3.to_s)
130
+ return "foo"
131
+ end
132
+
133
+ def bools(arg1)
134
+ unless arg1.nil? then
135
+ return true
136
+ else
137
+ return false
138
+ end
139
+ end
140
+
141
+ def eric_is_stubborn
142
+ var = 42
143
+ var2 = var.to_s
144
+ $stderr.fputs(var2)
145
+ return var2
146
+ end
147
+
148
+ def interpolated
149
+ var = 14
150
+ var2 = "var is #{var}. So there."
151
+ end
152
+
153
+ def unknown_args(arg1, arg2)
154
+ # does nothing
155
+ return arg1
156
+ end
157
+
158
+ def determine_args
159
+ 5 == unknown_args(4, "known")
160
+ end
161
+
162
+ end