ParseTree 1.1.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.
@@ -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