ZenHacks 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *** 1.0.0 / 2005-05-24
2
+
3
+ + 1 major enhancement
4
+ + Birthday!
@@ -0,0 +1,29 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ bin/macgraph
5
+ bin/parse_tree_graph
6
+ bin/test_stats
7
+ fixloops-demo.sh
8
+ lib/OrderedHash.rb
9
+ lib/class-path.rb
10
+ lib/discover.rb
11
+ lib/fixloops.rb
12
+ lib/graph.rb
13
+ lib/muffdaddy.rb
14
+ lib/r2c_hacks.rb
15
+ lib/ruby2ruby.rb
16
+ lib/timezones.rb
17
+ lib/zendebug.rb
18
+ lib/zenoptimize.rb
19
+ lib/zenprofile.rb
20
+ misc/factorial.rb
21
+ misc/find_c_methods
22
+ misc/fixloops-bad.rb
23
+ r2c_hacks-demo.rb
24
+ test/TestOrderedHash.rb
25
+ test/r2ctestcase.rb
26
+ test/test_parse_tree_graph.rb
27
+ zendebug-demo.sh
28
+ zenoptimize-demo.sh
29
+ zenprofile-demo.sh
@@ -0,0 +1,84 @@
1
+ ZenHacks
2
+ http://rubyforge.org/projects/zenhacks/
3
+ ryand-ruby@zenspider.com
4
+
5
+ ** DESCRIPTION:
6
+
7
+ ZenHacks is a semi-random collection of libs and scripts that just
8
+ don't belong in a package of their own. Many, if not most, have
9
+ dependencies on other packages and use, abuse, or extend them in
10
+ interesting ways.
11
+
12
+ This package is not supported in the same sense that my other packages
13
+ are, but since it is such a fun playground, I am very open to
14
+ contributions, suggestions, and bug fixes. I just can't put this
15
+ project at the top of my priority list the way I can/do the others, so
16
+ it may take me longer to get to than normal.
17
+
18
+ ** INCLUDES
19
+
20
+ + RubyInline Hacks
21
+ + zenprofile-demo.sh - demonstrates the use of zenprofile.
22
+ + lib/zenprofile.rb - a code profiler that is fairly readable yet fast.
23
+ + ParseTree Hacks
24
+ + bin/parse_tree_graph - graphs parsetree for code fed to it.
25
+ + test/test_parse_tree_graph.rb - tests for parse_tree_graph.
26
+ + RubyToRuby Hacks
27
+ + fixloops-demo.sh - demonstrates using parsetree to analyze source.
28
+ + lib/fixloops.rb - simple loop analyzer and refactoring tool.
29
+ + misc/fixloops-bad.rb - demo code for fixloops-demo.sh.
30
+ + lib/ruby2ruby.rb - converts ParseTree's sexp back into ruby.
31
+ + RubyToC Hacks
32
+ + r2c_hacks-demo.rb - demonstrates r2c_hacks' methods.
33
+ + lib/r2c_hacks.rb - implements to_sexp, to_ruby, and to_c for methods.
34
+ + zenoptimize-demo.sh - demonstrates dynamic optimization of ruby.
35
+ + lib/zenoptimize.rb - implements a dynamic optimizer using ruby2c.
36
+ + Testing Hacks
37
+ + bin/test_stats - shows ratio of assertions to methods
38
+ + Misc Hacks
39
+ + misc/factorial.rb - not a hack, but a demo file for above toys.
40
+ + misc/find_c_methods - sniffs through ruby's C to find C methods.
41
+ + lib/class-path.rb - returns array of each level of a class' namespace.
42
+ + lib/discover.rb - requires files and returns classes introduced.
43
+ + lib/muffdaddy.rb - allows you to very easily wrap objects and classes.
44
+ + lib/graph.rb - very simple / clean api for building w/ graphviz.
45
+ + bin/macgraph - very stupid / ugly frontend for using graphviz on osx.
46
+ + lib/OrderedHash.rb - a simple Hash with ordered keys.
47
+ + test/TestOrderedHash.rb - (bad) tests for OrderedHash.
48
+ + OrderedHash.rb/TestOrderedHash.rb - Orderered keyed collection.
49
+ + lib/timezones.rb - fixes the fact that you can't get timezones from Time.
50
+
51
+ ** REQUIREMENTS:
52
+
53
+ Lots... RubyInline, Ruby2C, ParseTree. Probably others. Not all
54
+ dependencies are required for all hacks. There may be some tweaking
55
+ needed to run the demo.sh files.
56
+
57
+ ** INSTALL:
58
+
59
+ + No install, this is mostly for playing and/or reading.
60
+
61
+ ** LICENSE:
62
+
63
+ (The MIT License)
64
+
65
+ Copyright (c) 2001-2005 Ryan Davis, Zen Spider Software
66
+
67
+ Permission is hereby granted, free of charge, to any person obtaining
68
+ a copy of this software and associated documentation files (the
69
+ "Software"), to deal in the Software without restriction, including
70
+ without limitation the rights to use, copy, modify, merge, publish,
71
+ distribute, sublicense, and/or sell copies of the Software, and to
72
+ permit persons to whom the Software is furnished to do so, subject to
73
+ the following conditions:
74
+
75
+ The above copyright notice and this permission notice shall be
76
+ included in all copies or substantial portions of the Software.
77
+
78
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
79
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
80
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
81
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
82
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
83
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
84
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,18 @@
1
+ #!/bin/bash
2
+
3
+ # set -x
4
+
5
+ PATH=$PATH:/Applications/Graphviz.app/Contents/MacOS/
6
+
7
+ f1=/tmp/graph.$$.dot
8
+ f2=/tmp/graph.$$.pdf
9
+ if [ $1 == '-f' ]; then
10
+ shift 1
11
+ echo $* | GEM_SKIP=ParseTree ruby -Ilib bin/parse_tree_graph -f -a > $f1
12
+ else
13
+ GEM_SKIP=ParseTree ruby -Ilib bin/parse_tree_graph $* > $f1
14
+ fi
15
+ dot -Tepdf $f1 > $f2
16
+ open $f2
17
+ (sleep 10; rm -f $f1 $f2) &
18
+
@@ -0,0 +1,161 @@
1
+ #!/usr/local/bin/ruby -ws
2
+
3
+ $TESTING = false unless defined? $TESTING
4
+ require 'pp'
5
+ require 'tempfile'
6
+
7
+ def usage(exit_code = 0)
8
+ cmd = File.basename($0)
9
+ puts "#{cmd} [options] files|libs"
10
+ puts " options:"
11
+ puts " -h show help"
12
+ puts " -f fast mode - graph a snippet of code from standard input"
13
+ puts " -a color ABC nodes"
14
+ puts " -s graph basic structure only"
15
+ exit exit_code
16
+ end
17
+
18
+ usage if defined? $h
19
+
20
+
21
+ #begin
22
+ # require 'rubygems'
23
+ # require_gem 'ParseTree'
24
+ #rescue LoadError
25
+ require 'parse_tree'
26
+ #end
27
+
28
+ require 'sexp_processor'
29
+
30
+ def discover_new_classes_from
31
+ old_classes = []
32
+ ObjectSpace.each_object(Module) do |klass|
33
+ old_classes << klass
34
+ end
35
+
36
+ yield
37
+
38
+ new_classes = []
39
+ ObjectSpace.each_object(Module) do |klass|
40
+ new_classes << klass
41
+ end
42
+
43
+ new_classes - old_classes
44
+ end
45
+
46
+ $f = false unless defined? $f
47
+
48
+ new_classes = discover_new_classes_from do
49
+ ARGV.unshift "-" if ARGV.empty?
50
+ ARGV.each do |name|
51
+ if name == "-" then
52
+ code = $stdin.read
53
+ code = "class Example; def example; #{code}; end; end" if $f
54
+ eval code unless code.nil?
55
+ else
56
+ require name
57
+ end
58
+ end
59
+ end unless $TESTING
60
+
61
+ require 'graph'
62
+
63
+ class SexpGrapher < SexpProcessor
64
+
65
+ attr_reader :graph
66
+ def initialize
67
+ super
68
+ self.default_method = :grapher
69
+ self.warn_on_default = false
70
+ @stack = []
71
+ @graph = Graph.new
72
+ @graph.prefix << " node [ shape = box, style = filled ];"
73
+ @abc = defined? $a
74
+ @n = 1
75
+ end
76
+
77
+ ##
78
+ # Create a DOT graph of the parse tree
79
+ #
80
+ # For example:
81
+ #
82
+ # [:call, [:lit, 1], :+, [:array, [:lit, 1]]]
83
+ #
84
+ # at start of exp, push :call onto stack
85
+ # process :call
86
+ # recurse into :lit
87
+ # at start of exp, push :lit onto stack
88
+ # process :lit
89
+ # process 1
90
+ #
91
+
92
+ def convert_node(n)
93
+ node = "n%04d" % @n
94
+ @n += 1
95
+ @graph.attribs[node]<<"label = \"#{n.inspect.gsub('"', '\\"')}\""
96
+ node
97
+ end
98
+
99
+ def color_node(node_type, current)
100
+ color = case node_type
101
+ when :attrasgn, :attrset, :dasgn_curr, :iasgn, :lasgn, :masgn then
102
+ "lightblue"
103
+ when :and, :case, :else, :if, :iter, :or, :rescue, :until, :when, :while then
104
+ "palegreen"
105
+ when :call, :fcall, :super, :vcall, :yield then
106
+ "goldenrod"
107
+ else
108
+ nil
109
+ end
110
+ @graph.attribs[current] << "color = #{color.inspect}" unless color.nil?
111
+ end
112
+
113
+ def grapher(exp)
114
+ current = nil
115
+ previous = @stack.last
116
+
117
+ unless Array === exp.first then
118
+ node_type = exp.shift
119
+ current = convert_node(node_type)
120
+ color_node(node_type, current) if @abc
121
+ @graph[previous] << current unless previous.nil?
122
+ @stack.push current
123
+ end
124
+
125
+ previous = current
126
+
127
+ until exp.empty? do
128
+ current = exp.shift
129
+ if Array === current then
130
+ process(current)
131
+ previous = @stack.last
132
+ else
133
+ node_type = current
134
+ current = convert_node(current)
135
+ @graph[previous] << current unless previous.nil?
136
+ previous = current
137
+ end
138
+ end
139
+ @stack.pop
140
+ s() # junk return
141
+ end
142
+
143
+ def graphstr
144
+ @graph.to_s
145
+ end
146
+ end
147
+
148
+ if __FILE__ == $0 then
149
+ result = ParseTree.new.parse_tree(*new_classes)
150
+ if $f then
151
+ result = result[0][3][2][1] # extracts just the body of the example method
152
+ result.shift
153
+ result.shift
154
+ end
155
+
156
+ result = Sexp.from_array(result)
157
+ grapher = SexpGrapher.new
158
+ result = result.structure if defined? $s
159
+ grapher.process(result)
160
+ puts grapher.graphstr
161
+ end
@@ -0,0 +1,42 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ assert_count = {}
4
+ def_count = {}
5
+ result = []
6
+
7
+ assert_count.default = 0
8
+ def_count.default = 0
9
+
10
+ current_class = "unknown"
11
+
12
+ ARGF.each_line do |l|
13
+
14
+ current_class = $1 if l =~ /^\s*class\s+(\S+)/
15
+ def_count[current_class] += 1 if l =~ /^\s*def/
16
+ assert_count[current_class] += 1 if l =~ /assert_|flunk|fail/
17
+
18
+ end
19
+
20
+ def_count.each_key do |classname|
21
+
22
+ entry = {}
23
+
24
+ next if classname =~ /^Test/
25
+ testclassname = "Test#{classname}"
26
+ a_count = assert_count[testclassname]
27
+ d_count = def_count[classname]
28
+ ratio = a_count.to_f / d_count.to_f * 100.0
29
+
30
+ entry['n'] = classname
31
+ entry['r'] = ratio
32
+ entry['a'] = a_count
33
+ entry['d'] = d_count
34
+
35
+ result.push entry
36
+ end
37
+
38
+ sorted_results = result.sort { |a,b| b['r'] <=> a['r'] }
39
+
40
+ sorted_results.each do |e|
41
+ printf "# %25s: %4d defs %4d = %6.2f%%\n", e['n'], e['d'], e['a'], e['r']
42
+ end
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ GEM_SKIP=ParseTree ruby -Ilib:../../ParseTree/dev/lib:../../ruby_to_c/dev:. -w misc/fixloops-bad.rb
@@ -0,0 +1,37 @@
1
+
2
+ class OrderedHash < Hash
3
+
4
+ def initialize(default=nil)
5
+ super(default)
6
+ @order = []
7
+ end
8
+
9
+ def []=(key,val)
10
+ @order.delete(key)
11
+ @order.push(key)
12
+ super(key,val)
13
+ end
14
+
15
+ def keys
16
+ @order
17
+ end
18
+
19
+ def each
20
+ @order.each do |key|
21
+ yield(key, self[key])
22
+ end
23
+ end
24
+
25
+ def each_key
26
+ @order.each do |key|
27
+ yield(key)
28
+ end
29
+ end
30
+
31
+ def each_value
32
+ @order.each do |key|
33
+ yield(self[key])
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,20 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ class Class
4
+ def path
5
+ result = []
6
+ self.name.split(/::/).inject(Object) do |c, n|
7
+ result << c.const_get(n)
8
+ result.last
9
+ end
10
+ return result
11
+ end
12
+ end
13
+
14
+ class Foo
15
+ class Bar
16
+ end
17
+ end
18
+
19
+ p Foo::Bar.path
20
+ # => [Foo, Foo::Bar]
@@ -0,0 +1,15 @@
1
+ def discover_new_classes_from
2
+ old_classes = []
3
+ ObjectSpace.each_object(Module) do |klass|
4
+ old_classes << klass
5
+ end
6
+
7
+ yield
8
+
9
+ new_classes = []
10
+ ObjectSpace.each_object(Module) do |klass|
11
+ new_classes << klass
12
+ end
13
+
14
+ new_classes - old_classes
15
+ end
@@ -0,0 +1,79 @@
1
+ require 'parse_tree'
2
+ require 'ruby2ruby'
3
+
4
+ class Refax
5
+
6
+ def couldPossiblyRefactor?(p, ind)
7
+ return false unless p[ind].is_a?(Array)
8
+ return false unless p[ind].first == :while
9
+ return false if p[ind][-1] == :post
10
+ return true unless p[ind][2].is_a?(Array)
11
+ p[ind][2].first == :block
12
+ end
13
+
14
+ def howManyInsn(p)
15
+ fail "Must be a while, not a #{p}" unless p.first == :while
16
+ if p[2].is_a?(Array)
17
+ fail unless p[2].first == :block
18
+ p[2].size - 1
19
+ else
20
+ 1
21
+ end
22
+ end
23
+
24
+ def grabInsnArray(p)
25
+ fail "Must be a while, not a #{p}" unless p[0] == :while
26
+ if p[2].is_a?(Array)
27
+ p[2][1..-1]
28
+ else
29
+ [p[2]]
30
+ end
31
+ end
32
+
33
+ def isEquiv(a, b)
34
+ a.to_s == b.to_s
35
+ end
36
+
37
+ def fixcode(p, ind)
38
+ loopsize = howManyInsn(p[ind])
39
+ goodcode = p.clone
40
+ goodcode[ind][-1] = ! goodcode[ind][-1] #true # false
41
+ goodcode.slice!(ind-loopsize..ind-1)
42
+ goodcode # todo : make correcter
43
+ end
44
+
45
+ def recurseOn(p)
46
+ if p.is_a?(Array)
47
+ @lastclass = p[1] if p.first == :class
48
+ @lastfunc = p[1] if p.first == :defn
49
+ p.each { |i| recurseOn(i) }
50
+ p.each_index do |ind|
51
+ if couldPossiblyRefactor?(p,ind)
52
+ loopsize = howManyInsn(p[ind])
53
+ if loopsize < ind
54
+ if isEquiv(p[ind-loopsize,loopsize], grabInsnArray(p[ind]))
55
+ goodstuff = fixcode(p, ind)
56
+ puts "Suggest refactoring #{@lastclass}##{@lastfunc} from:"
57
+ puts
58
+ puts RubyToRuby.translate(eval(@lastclass.to_s), @lastfunc)
59
+ print "\nto:\n\n"
60
+ puts RubyToRuby.new.process(s(:defn, @lastfunc, s(:scope, goodstuff)))
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ def refactor(c)
69
+ fail "Must have class or module" unless c.is_a?(Module)
70
+ p = ParseTree.new.parse_tree(c)
71
+ recurseOn(p)
72
+ end
73
+
74
+ r = Refax.new
75
+ ObjectSpace.each_object(Module) { |c|
76
+ r.refactor(c)
77
+ }
78
+
79
+ end