zombie-killer 0.3 → 0.4
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.
- checksums.yaml +5 -5
- data/NEWS.md +18 -0
- data/bin/zk +2 -1
- data/lib/zombie_killer/eager_rewriter.rb +56 -0
- data/lib/zombie_killer/killer.rb +22 -2
- data/lib/zombie_killer/rewriter.rb +22 -11
- data/lib/zombie_killer/version.rb +1 -1
- data/spec/spec_helper.rb +20 -2
- metadata +57 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3e180c7784f993ba500fad7e16198be9e6478ba85d7c6817e701fa7b2ba85e7e
|
4
|
+
data.tar.gz: cabad90fad0492478d2ded00b62b1d45435cbc5750fcc1010a36483bac314710
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c27fc0f8f743e91a93dc3c0edd041538c5933015f0c30ede75e537267e3baadce783c8e3d2491f063dad3b1c9d782e8045851e5371344b15e52e3bb4ef6b41d5
|
7
|
+
data.tar.gz: 61a29a2baee22c6190718405e436cc7889c3315cfcb1831864091ead8ae5d551637cc783a42b649167e7f314b926389e559aa00a1490a11f772a5d8715e222bf
|
data/NEWS.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# News
|
2
|
+
|
3
|
+
## unreleased
|
4
|
+
|
5
|
+
## 0.4, 2018-11-02
|
6
|
+
|
7
|
+
- Added zk -e, EagerRewriter (don't care about niceness, replace all)
|
8
|
+
- Recover from parse errors by reverting to the original file
|
9
|
+
- Fixed reporting unhandled node types
|
10
|
+
- Handle keyword arguments, regex match
|
11
|
+
|
12
|
+
## 0.3, 2014-12-02
|
13
|
+
|
14
|
+
- bin/count_method_calls added
|
15
|
+
|
16
|
+
## 0.2, 2014-11-28
|
17
|
+
|
18
|
+
- first release as a gem
|
data/bin/zk
CHANGED
@@ -12,6 +12,7 @@ Usage: zk [options] [FILES...]
|
|
12
12
|
Arguments:
|
13
13
|
FILES Files to operate on, patterns allowed [default: **/*.rb]
|
14
14
|
Options:
|
15
|
+
-e, --eager Translate all zombies regardless of niceness.
|
15
16
|
-u, --unsafe Translate even constructs not known to be safe.
|
16
17
|
-s, --stats Also print statistics about node types.
|
17
18
|
-v, --version Print version information and exit.
|
@@ -21,7 +22,7 @@ EOT
|
|
21
22
|
begin
|
22
23
|
options = Docopt.docopt(doc, help: true, version: ZombieKiller::VERSION)
|
23
24
|
|
24
|
-
killer = ZombieKiller.new
|
25
|
+
killer = ZombieKiller.new(eager: options["--eager"])
|
25
26
|
|
26
27
|
files = options["FILES"]
|
27
28
|
files << "**/*.rb" if files.empty?
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "unparser"
|
2
|
+
require "set"
|
3
|
+
|
4
|
+
# Rewrite Zombies with their idiomatic replacements
|
5
|
+
class EagerRewriter < Parser::TreeRewriter
|
6
|
+
OPS = Parser::AST::Node.new(:const, [nil, :Ops])
|
7
|
+
|
8
|
+
INFIX = {
|
9
|
+
add: :+,
|
10
|
+
subtract: :-,
|
11
|
+
multiply: :*,
|
12
|
+
divide: :/,
|
13
|
+
modulo: :%,
|
14
|
+
bitwise_and: :&,
|
15
|
+
bitwise_or: :|,
|
16
|
+
bitwise_xor: :^,
|
17
|
+
|
18
|
+
less_than: :<,
|
19
|
+
less_or_equal: :<=,
|
20
|
+
greater_than: :>,
|
21
|
+
greater_or_equal: :>=
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
def s(name, *children)
|
25
|
+
Parser::AST::Node.new(name, children)
|
26
|
+
end
|
27
|
+
|
28
|
+
def replace_node(old_node, new_node)
|
29
|
+
source_range = old_node.loc.expression
|
30
|
+
replace(source_range, Unparser.unparse(new_node))
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_send(node)
|
34
|
+
super
|
35
|
+
receiver, name, *args = *node
|
36
|
+
replacement = INFIX[name]
|
37
|
+
if receiver == OPS && replacement && args.size == 2
|
38
|
+
replace_node(node, s(:send, args[0], replacement, args[1]))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
AS_OPS = Set.new [:+, :-]
|
43
|
+
def on_lvasgn(node)
|
44
|
+
super
|
45
|
+
vname1, value = *node
|
46
|
+
return unless value && value.type == :send
|
47
|
+
receiver, oname, *args = *value
|
48
|
+
if vname1 == lvar_vname2(receiver) && AS_OPS.include?(oname)
|
49
|
+
replace_node(node, s(:op_asgn, s(:lvasgn, vname1), oname, args[0]))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def lvar_vname2(receiver)
|
54
|
+
receiver.children.first if receiver && receiver.type == :lvar
|
55
|
+
end
|
56
|
+
end
|
data/lib/zombie_killer/killer.rb
CHANGED
@@ -1,17 +1,34 @@
|
|
1
1
|
require "parser"
|
2
2
|
require "parser/current"
|
3
3
|
|
4
|
+
require_relative "eager_rewriter"
|
4
5
|
require_relative "rewriter"
|
5
6
|
|
7
|
+
# The main class called from the CLI
|
6
8
|
class ZombieKiller
|
9
|
+
# @return [Boolean] use the EagerRewriter
|
10
|
+
attr_reader :eager
|
11
|
+
|
12
|
+
def initialize(eager: false)
|
13
|
+
@eager = eager
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param code [String]
|
17
|
+
# @param filename [String]
|
7
18
|
# @returns new string
|
8
19
|
def kill_string(code, filename = "(inline code)", unsafe: false)
|
9
20
|
fixed_point(code) do |c|
|
10
21
|
parser = Parser::CurrentRuby.new
|
11
|
-
rewriter = ZombieKillerRewriter.new(unsafe: unsafe)
|
22
|
+
rewriter = eager ? EagerRewriter.new : ZombieKillerRewriter.new(unsafe: unsafe)
|
12
23
|
buffer = Parser::Source::Buffer.new(filename)
|
13
24
|
buffer.source = c
|
14
|
-
|
25
|
+
ast = parser.parse(buffer)
|
26
|
+
if ast
|
27
|
+
rewriter.rewrite(buffer, ast)
|
28
|
+
else
|
29
|
+
puts "Parse error for '#{filename}', returning it unchanged"
|
30
|
+
return code
|
31
|
+
end
|
15
32
|
end
|
16
33
|
end
|
17
34
|
alias_method :kill, :kill_string
|
@@ -21,6 +38,9 @@ class ZombieKiller
|
|
21
38
|
new_string = kill_string(File.read(filename), filename, unsafe: unsafe)
|
22
39
|
|
23
40
|
File.write(new_filename, new_string)
|
41
|
+
rescue
|
42
|
+
puts "While processing #{filename}"
|
43
|
+
raise
|
24
44
|
end
|
25
45
|
|
26
46
|
private
|
@@ -8,9 +8,20 @@ require_relative "variable_scope"
|
|
8
8
|
|
9
9
|
# We have encountered code that does satisfy our simplifying assumptions,
|
10
10
|
# translating it would not be correct.
|
11
|
-
class TooComplexToTranslateError <
|
11
|
+
class TooComplexToTranslateError < RuntimeError
|
12
12
|
end
|
13
13
|
|
14
|
+
# An error related to a node
|
15
|
+
class NodeError < RuntimeError
|
16
|
+
attr_reader :node
|
17
|
+
|
18
|
+
def initialize(message, node)
|
19
|
+
@node = node
|
20
|
+
super(message)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# The main rewriter
|
14
25
|
class ZombieKillerRewriter < Parser::Rewriter
|
15
26
|
include Niceness
|
16
27
|
|
@@ -63,10 +74,14 @@ class ZombieKillerRewriter < Parser::Rewriter
|
|
63
74
|
:if, # If and Unless
|
64
75
|
:ivar, # Instance variable value
|
65
76
|
:ivasgn, # Instance variable assignment
|
77
|
+
:kwarg, # Keyword argument, def m(a:)
|
66
78
|
:kwbegin, # A variant of begin; for rescue and while_post
|
67
79
|
:kwoptarg, # Keyword optional argument, def m(a: 1)
|
80
|
+
:kwrestarg, # Rest of keyword arguments, def m(**kwargs)
|
81
|
+
:kwsplat, # Hash **splatting
|
68
82
|
:lvar, # Local variable value
|
69
83
|
:lvasgn, # Local variable assignment
|
84
|
+
:match_with_lvasgn, # /regex/ =~ value
|
70
85
|
:masgn, # Multiple assigment: a, b = c, d
|
71
86
|
:mlhs, # Left-hand side of a multiple assigment: a, b = c, d
|
72
87
|
:module, # Module body
|
@@ -101,8 +116,8 @@ class ZombieKillerRewriter < Parser::Rewriter
|
|
101
116
|
|
102
117
|
def process(node)
|
103
118
|
return if node.nil?
|
104
|
-
|
105
|
-
|
119
|
+
unless @unsafe
|
120
|
+
raise NodeError.new("Unknown node type #{node.type}", node) unless
|
106
121
|
HANDLED_NODE_TYPES.include? node.type
|
107
122
|
end
|
108
123
|
super
|
@@ -117,8 +132,10 @@ class ZombieKillerRewriter < Parser::Rewriter
|
|
117
132
|
scopes.with_new do
|
118
133
|
block.call
|
119
134
|
end
|
120
|
-
rescue => e
|
121
|
-
|
135
|
+
rescue NodeError => e
|
136
|
+
puts e
|
137
|
+
puts "Node exception @ #{e.node.loc.expression}"
|
138
|
+
puts "Offending node: #{e.node.inspect}"
|
122
139
|
end
|
123
140
|
|
124
141
|
def on_def(node)
|
@@ -276,12 +293,6 @@ class ZombieKillerRewriter < Parser::Rewriter
|
|
276
293
|
|
277
294
|
private
|
278
295
|
|
279
|
-
def oops(node, exception)
|
280
|
-
puts "Node exception @ #{node.loc.expression}"
|
281
|
-
puts "Offending node: #{node.inspect}"
|
282
|
-
raise exception
|
283
|
-
end
|
284
|
-
|
285
296
|
def is_call(node, namespace, message)
|
286
297
|
n_receiver, n_message = *node
|
287
298
|
n_receiver && n_receiver.type == :const &&
|
data/spec/spec_helper.rb
CHANGED
@@ -1,7 +1,25 @@
|
|
1
|
-
|
1
|
+
if ENV["COVERAGE"]
|
2
|
+
require "simplecov"
|
3
|
+
SimpleCov.start
|
4
|
+
|
5
|
+
top_location = File.expand_path("../../", __FILE__)
|
6
|
+
# track all ruby files under lib
|
7
|
+
SimpleCov.track_files("#{top_location}/lib/**/*.rb")
|
8
|
+
|
9
|
+
# use coveralls for on-line code coverage reporting at Travis CI
|
10
|
+
if ENV["TRAVIS"]
|
11
|
+
require "coveralls"
|
12
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new [
|
13
|
+
SimpleCov::Formatter::HTMLFormatter,
|
14
|
+
Coveralls::SimpleCov::Formatter
|
15
|
+
]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
2
20
|
require "zombie_killer"
|
3
21
|
|
4
22
|
def cleanup(s)
|
5
23
|
s.split("\n").reject { |l| l =~ /^\s*$/ }.first =~ /^(\s*)/
|
6
|
-
s.gsub(Regexp.new("^#{
|
24
|
+
s.gsub(Regexp.new("^#{Regexp.last_match(1)}"), "")[0..-2]
|
7
25
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zombie-killer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.4'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Vidner
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2018-11-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: docopt
|
@@ -29,9 +29,9 @@ dependencies:
|
|
29
29
|
name: parser
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- - "
|
32
|
+
- - ">="
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: 2.2.0
|
34
|
+
version: 2.2.0
|
35
35
|
- - "<"
|
36
36
|
- !ruby/object:Gem::Version
|
37
37
|
version: '3'
|
@@ -39,9 +39,9 @@ dependencies:
|
|
39
39
|
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
41
|
requirements:
|
42
|
-
- - "
|
42
|
+
- - ">="
|
43
43
|
- !ruby/object:Gem::Version
|
44
|
-
version: 2.2.0
|
44
|
+
version: 2.2.0
|
45
45
|
- - "<"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '3'
|
@@ -59,6 +59,40 @@ dependencies:
|
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: coveralls
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
type: :development
|
70
|
+
prerelease: false
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: rake
|
78
|
+
requirement: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10'
|
83
|
+
- - "<"
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '999'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '10'
|
93
|
+
- - "<"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '999'
|
62
96
|
- !ruby/object:Gem::Dependency
|
63
97
|
name: rspec
|
64
98
|
requirement: !ruby/object:Gem::Requirement
|
@@ -93,6 +127,20 @@ dependencies:
|
|
93
127
|
- - "~>"
|
94
128
|
- !ruby/object:Gem::Version
|
95
129
|
version: '3'
|
130
|
+
- !ruby/object:Gem::Dependency
|
131
|
+
name: simplecov
|
132
|
+
requirement: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - "~>"
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
type: :development
|
138
|
+
prerelease: false
|
139
|
+
version_requirements: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - "~>"
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
96
144
|
description: Translate YCP-like library calls (Ops.*, Builtins.*) to idiomatic Ruby
|
97
145
|
email:
|
98
146
|
- martin@vidner.net
|
@@ -104,11 +152,13 @@ extensions: []
|
|
104
152
|
extra_rdoc_files: []
|
105
153
|
files:
|
106
154
|
- LICENSE
|
155
|
+
- NEWS.md
|
107
156
|
- README.md
|
108
157
|
- bin/count_method_calls
|
109
158
|
- bin/zk
|
110
159
|
- lib/zombie_killer.rb
|
111
160
|
- lib/zombie_killer/code_histogram.rb
|
161
|
+
- lib/zombie_killer/eager_rewriter.rb
|
112
162
|
- lib/zombie_killer/killer.rb
|
113
163
|
- lib/zombie_killer/niceness.rb
|
114
164
|
- lib/zombie_killer/node_type_counter.rb
|
@@ -139,9 +189,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
189
|
version: '0'
|
140
190
|
requirements: []
|
141
191
|
rubyforge_project:
|
142
|
-
rubygems_version: 2.
|
192
|
+
rubygems_version: 2.7.3
|
143
193
|
signing_key:
|
144
194
|
specification_version: 4
|
145
195
|
summary: Resocialize YCP Zombies
|
146
196
|
test_files: []
|
147
|
-
has_rdoc:
|