ZenHacks 1.0.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,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