zombie-killer 0.3 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|