ZenHacks 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +29 -0
- data/README.txt +84 -0
- data/bin/macgraph +18 -0
- data/bin/parse_tree_graph +161 -0
- data/bin/test_stats +42 -0
- data/fixloops-demo.sh +3 -0
- data/lib/OrderedHash.rb +37 -0
- data/lib/class-path.rb +20 -0
- data/lib/discover.rb +15 -0
- data/lib/fixloops.rb +79 -0
- data/lib/graph.rb +66 -0
- data/lib/muffdaddy.rb +84 -0
- data/lib/r2c_hacks.rb +36 -0
- data/lib/ruby2ruby.rb +306 -0
- data/lib/timezones.rb +11 -0
- data/lib/zendebug.rb +1037 -0
- data/lib/zenoptimize.rb +149 -0
- data/lib/zenprofile.rb +170 -0
- data/misc/factorial.rb +26 -0
- data/misc/find_c_methods +49 -0
- data/misc/fixloops-bad.rb +62 -0
- data/r2c_hacks-demo.rb +23 -0
- data/test/TestOrderedHash.rb +43 -0
- data/test/r2ctestcase.rb +1076 -0
- data/test/test_parse_tree_graph.rb +47 -0
- data/zendebug-demo.sh +86 -0
- data/zenoptimize-demo.sh +22 -0
- data/zenprofile-demo.sh +29 -0
- metadata +69 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -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
|
data/README.txt
ADDED
@@ -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.
|
data/bin/macgraph
ADDED
@@ -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
|
data/bin/test_stats
ADDED
@@ -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
|
data/fixloops-demo.sh
ADDED
data/lib/OrderedHash.rb
ADDED
@@ -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
|
data/lib/class-path.rb
ADDED
@@ -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]
|
data/lib/discover.rb
ADDED
@@ -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
|
data/lib/fixloops.rb
ADDED
@@ -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
|