unparser 0.1.7 → 0.1.8
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 +4 -4
- data/.rubocop.yml +7 -0
- data/.travis.yml +3 -0
- data/Changelog.md +4 -0
- data/README.md +4 -2
- data/config/flay.yml +1 -1
- data/config/flog.yml +1 -1
- data/config/reek.yml +24 -19
- data/config/rubocop.yml +2 -3
- data/lib/unparser.rb +8 -22
- data/lib/unparser/ast.rb +232 -0
- data/lib/unparser/ast/local_variable_scope.rb +198 -0
- data/lib/unparser/cli.rb +41 -24
- data/lib/unparser/cli/differ.rb +38 -16
- data/lib/unparser/cli/source.rb +46 -17
- data/lib/unparser/constants.rb +23 -6
- data/lib/unparser/emitter.rb +32 -0
- data/lib/unparser/emitter/argument.rb +30 -4
- data/lib/unparser/emitter/assignment.rb +12 -1
- data/lib/unparser/emitter/begin.rb +23 -2
- data/lib/unparser/emitter/case.rb +1 -1
- data/lib/unparser/emitter/class.rb +1 -0
- data/lib/unparser/emitter/def.rb +28 -1
- data/lib/unparser/emitter/defined.rb +3 -1
- data/lib/unparser/emitter/flow_modifier.rb +63 -0
- data/lib/unparser/emitter/if.rb +44 -0
- data/lib/unparser/emitter/literal/dynamic.rb +25 -1
- data/lib/unparser/emitter/literal/hash.rb +3 -3
- data/lib/unparser/emitter/literal/primitive.rb +9 -47
- data/lib/unparser/emitter/literal/regexp.rb +5 -16
- data/lib/unparser/emitter/module.rb +1 -0
- data/lib/unparser/emitter/repetition.rb +52 -0
- data/lib/unparser/emitter/resbody.rb +4 -2
- data/lib/unparser/emitter/rescue.rb +12 -2
- data/lib/unparser/emitter/root.rb +2 -11
- data/lib/unparser/emitter/send.rb +19 -2
- data/lib/unparser/emitter/send/index.rb +42 -4
- data/lib/unparser/emitter/send/unary.rb +4 -0
- data/lib/unparser/emitter/undef.rb +1 -3
- data/lib/unparser/node_helpers.rb +13 -1
- data/lib/unparser/preprocessor.rb +226 -0
- data/lib/unparser/strip_helper.rb +23 -0
- data/rubyspec.sh +20 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/unparser_spec.rb +390 -151
- data/unparser.gemspec +1 -1
- metadata +27 -24
- data/lib/unparser/cli/preprocessor.rb +0 -197
- data/lib/unparser/emitter/break.rb +0 -27
- data/lib/unparser/emitter/next.rb +0 -28
- data/lib/unparser/emitter/return.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d247e782f83bba8d99fdba92eb8cf9fb010ea6cf
|
4
|
+
data.tar.gz: 51ead40ddaae1b6424a80d9e9ade1617b3189209
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a5d334a76e0cab7091514c88ad8abcb12b56c2a7553eb22e4f74f79b893353a6bbbbf68fd04f3fd36528320731a70bb1517b0f8116c994d69a0643881d79fe4
|
7
|
+
data.tar.gz: 7549efa3f744f4190aef927f38f1467667d911a0b123ff4d37e014506d7e93c854a892863a22f8b26c46a4e4f8f36bfe384568a2355bcf485e316f350508b00e
|
data/.rubocop.yml
ADDED
data/.travis.yml
CHANGED
data/Changelog.md
CHANGED
data/README.md
CHANGED
@@ -5,12 +5,14 @@ unparser
|
|
5
5
|
[](https://gemnasium.com/mbj/unparser)
|
6
6
|
[](https://codeclimate.com/github/mbj/unparser)
|
7
7
|
|
8
|
-
Generate equivalent source for ASTs from whitequarks
|
8
|
+
Generate equivalent source for ASTs from whitequarks [parser](https://github.com/whitequark/parser).
|
9
9
|
|
10
10
|
This library is able to reproduce 100% of ruby 1.9 and 2.0 syntax. Including its own source code.
|
11
|
+
|
11
12
|
It serves well for [mutant](https://github.cm/mbj/mutant) mutators and the in-memory vendoring for self hosting.
|
12
13
|
|
13
|
-
This library dropped the reproduction of 1.8 syntax in the 0.1.0 release.
|
14
|
+
This library dropped the reproduction of 1.8 syntax in the 0.1.0 release.
|
15
|
+
It currently does not have support for 2.1 specific syntax.
|
14
16
|
|
15
17
|
Usage
|
16
18
|
-----
|
data/config/flay.yml
CHANGED
data/config/flog.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
2
|
+
threshold: 20.7
|
data/config/reek.yml
CHANGED
@@ -10,7 +10,8 @@ UncommunicativeParameterName:
|
|
10
10
|
- !ruby/regexp /[0-9]$/
|
11
11
|
- !ruby/regexp /[A-Z]/
|
12
12
|
TooManyInstanceVariables:
|
13
|
-
exclude:
|
13
|
+
exclude:
|
14
|
+
- Unparser::CLI
|
14
15
|
enabled: true
|
15
16
|
max_instance_variables: 3
|
16
17
|
TooManyMethods:
|
@@ -21,6 +22,7 @@ TooManyMethods:
|
|
21
22
|
UncommunicativeMethodName:
|
22
23
|
accept:
|
23
24
|
- s
|
25
|
+
- n
|
24
26
|
exclude: []
|
25
27
|
enabled: true
|
26
28
|
reject:
|
@@ -35,14 +37,12 @@ LongParameterList:
|
|
35
37
|
overrides: {}
|
36
38
|
FeatureEnvy:
|
37
39
|
exclude:
|
38
|
-
|
39
|
-
- Unparser::
|
40
|
-
- Unparser::Emitter::Send#
|
41
|
-
|
42
|
-
- Unparser::
|
43
|
-
- Unparser::CLI
|
44
|
-
- Unparser::CLI::Preprocessor::Dstr#collapsed_children
|
45
|
-
- Unparser::Comments#take_before
|
40
|
+
# False positives
|
41
|
+
- Unparser::CLI::Differ#collapsed_hunks
|
42
|
+
- Unparser::Emitter::Send::Arguments#effective_arguments
|
43
|
+
# Helper methods, false positive
|
44
|
+
- Unparser::StripHelper#strip
|
45
|
+
- Unparser::CLI#sources
|
46
46
|
enabled: true
|
47
47
|
ClassVariable:
|
48
48
|
exclude: []
|
@@ -63,10 +63,10 @@ UncommunicativeModuleName:
|
|
63
63
|
NestedIterators:
|
64
64
|
ignore_iterators: []
|
65
65
|
exclude:
|
66
|
-
|
66
|
+
# Acceptable cases:
|
67
67
|
- Unparser::Emitter::Literal::Regexp#dispatch
|
68
|
-
- Unparser::Emitter::Return#emit_break_return_arguments
|
69
68
|
- Unparser::CLI::Preprocessor::Dstr#collapsed_children
|
69
|
+
- Unparser::Preprocessor::CollapseStrChildren#collapsed_children
|
70
70
|
enabled: true
|
71
71
|
max_allowed_nesting: 1
|
72
72
|
TooManyStatements:
|
@@ -81,7 +81,10 @@ DuplicateMethodCall:
|
|
81
81
|
max_calls: 1
|
82
82
|
UtilityFunction:
|
83
83
|
max_helper_calls: 1
|
84
|
-
exclude:
|
84
|
+
exclude:
|
85
|
+
# Intent to be helper methods
|
86
|
+
- Unparser::StripHelper#strip
|
87
|
+
- Unparser::CLI#sources
|
85
88
|
enabled: true
|
86
89
|
Attribute:
|
87
90
|
exclude: []
|
@@ -96,11 +99,11 @@ UncommunicativeVariableName:
|
|
96
99
|
- !ruby/regexp /[A-Z]/
|
97
100
|
RepeatedConditional:
|
98
101
|
exclude:
|
99
|
-
|
100
|
-
- Unparser::Comments
|
101
|
-
|
102
|
+
# TODO:
|
103
|
+
- Unparser::Comments
|
104
|
+
# False positives
|
102
105
|
- Unparser::Emitter::If
|
103
|
-
- Unparser::CLI
|
106
|
+
- Unparser::CLI
|
104
107
|
enabled: true
|
105
108
|
max_ifs: 1
|
106
109
|
DataClump:
|
@@ -110,12 +113,14 @@ DataClump:
|
|
110
113
|
min_clump_size: 3
|
111
114
|
ControlParameter:
|
112
115
|
exclude:
|
113
|
-
|
114
|
-
- Unparser::Emitter#
|
116
|
+
# False positive:
|
117
|
+
- Unparser::Emitter#emit_body
|
118
|
+
- Unparser::Emitter#conditional_parentheses
|
115
119
|
enabled: true
|
116
120
|
NilCheck:
|
117
121
|
enabled: false
|
118
122
|
LongYieldList:
|
119
123
|
max_params: 1
|
120
|
-
exclude:
|
124
|
+
exclude:
|
125
|
+
- Unparser::AST::LocalVariableScope#visit
|
121
126
|
enabled: true
|
data/config/rubocop.yml
CHANGED
data/lib/unparser.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require 'set'
|
3
4
|
require 'abstract_type'
|
4
5
|
require 'procto'
|
5
6
|
require 'concord'
|
@@ -23,37 +24,24 @@ module Unparser
|
|
23
24
|
# @api private
|
24
25
|
#
|
25
26
|
def self.unparse(node, comment_array = [])
|
26
|
-
|
27
|
-
node = Parser::AST::Node.new(:empty)
|
28
|
-
end
|
27
|
+
node = Preprocessor.run(node)
|
29
28
|
buffer = Buffer.new
|
30
29
|
comments = Comments.new(comment_array)
|
31
|
-
root = Emitter::Root.new(buffer, comments)
|
30
|
+
root = Emitter::Root.new(Parser::AST::Node.new(:root, [node]), buffer, comments)
|
32
31
|
Emitter.emitter(node, root).write_to_buffer
|
33
32
|
buffer.content
|
34
33
|
end
|
35
34
|
|
36
|
-
# Transquote string
|
37
|
-
#
|
38
|
-
# @param [String] raw_quoted
|
39
|
-
# @param [String] current_delimiter
|
40
|
-
# @param [String] target_delimiter
|
41
|
-
#
|
42
|
-
# @return [String]
|
43
|
-
#
|
44
|
-
# @api private
|
45
|
-
#
|
46
|
-
def self.transquote(raw_quoted, current_delimiter, target_delimiter)
|
47
|
-
raw = raw_quoted.gsub("\\#{current_delimiter}", current_delimiter)
|
48
|
-
raw.gsub(target_delimiter, "\\#{target_delimiter}").freeze
|
49
|
-
end
|
50
|
-
|
51
35
|
end # Unparser
|
52
36
|
|
37
|
+
require 'unparser/strip_helper'
|
53
38
|
require 'unparser/buffer'
|
54
39
|
require 'unparser/node_helpers'
|
40
|
+
require 'unparser/preprocessor'
|
55
41
|
require 'unparser/comments'
|
56
42
|
require 'unparser/constants'
|
43
|
+
require 'unparser/ast'
|
44
|
+
require 'unparser/ast/local_variable_scope'
|
57
45
|
require 'unparser/emitter'
|
58
46
|
require 'unparser/emitter/literal'
|
59
47
|
require 'unparser/emitter/literal/primitive'
|
@@ -79,7 +67,7 @@ require 'unparser/emitter/splat'
|
|
79
67
|
require 'unparser/emitter/cbase'
|
80
68
|
require 'unparser/emitter/argument'
|
81
69
|
require 'unparser/emitter/begin'
|
82
|
-
require 'unparser/emitter/
|
70
|
+
require 'unparser/emitter/flow_modifier'
|
83
71
|
require 'unparser/emitter/undef'
|
84
72
|
require 'unparser/emitter/def'
|
85
73
|
require 'unparser/emitter/class'
|
@@ -88,10 +76,8 @@ require 'unparser/emitter/op_assign'
|
|
88
76
|
require 'unparser/emitter/defined'
|
89
77
|
require 'unparser/emitter/hookexe'
|
90
78
|
require 'unparser/emitter/super'
|
91
|
-
require 'unparser/emitter/break'
|
92
79
|
require 'unparser/emitter/retry'
|
93
80
|
require 'unparser/emitter/redo'
|
94
|
-
require 'unparser/emitter/next'
|
95
81
|
require 'unparser/emitter/if'
|
96
82
|
require 'unparser/emitter/alias'
|
97
83
|
require 'unparser/emitter/yield'
|
data/lib/unparser/ast.rb
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
# Namespace for AST processing tools
|
5
|
+
module AST
|
6
|
+
|
7
|
+
FIRST_CHILD = ->(node) { node.children.first }.freeze
|
8
|
+
TAUTOLOGY = ->(node) { true }.freeze
|
9
|
+
|
10
|
+
# Test if local variable was first at given assignment
|
11
|
+
#
|
12
|
+
# @param [Parser::AST::Node] root
|
13
|
+
# @param [Parser::AST::Node] assignment
|
14
|
+
#
|
15
|
+
# @return [true]
|
16
|
+
# if local variable was firstly introduced in body
|
17
|
+
#
|
18
|
+
# @return [false]
|
19
|
+
# otherwise
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
#
|
23
|
+
def self.first_assignment?(root, assignment)
|
24
|
+
name = assignment.children.first
|
25
|
+
AST::LocalVariableScope.each(root) do |node, current, before|
|
26
|
+
if node.equal?(assignment) && current.include?(name) && !before.include?(name)
|
27
|
+
return true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
# Test if local variable is defined for given node
|
35
|
+
#
|
36
|
+
# @param [Parser::AST::Node] root
|
37
|
+
# @param [Parser::AST::Node] node
|
38
|
+
# @param [Symbol] name
|
39
|
+
#
|
40
|
+
# @return [true]
|
41
|
+
# if local variable is defined
|
42
|
+
#
|
43
|
+
# @return [false]
|
44
|
+
# otherwise
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
#
|
48
|
+
def self.local_variable_defined_for_node?(root, node, name)
|
49
|
+
AST::LocalVariableScope.each(root) do |child, current, before|
|
50
|
+
if child.equal?(node) && current.include?(name)
|
51
|
+
return true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
# Test if local variables where first assigned in body and read by conditional
|
59
|
+
#
|
60
|
+
# @param [Parser::AST::Node] root
|
61
|
+
# @param [Parser::AST::Node] conditional
|
62
|
+
# @param [Parser::AST::Node] body
|
63
|
+
#
|
64
|
+
# @api private
|
65
|
+
#
|
66
|
+
def self.first_assignment_in_body_and_used_in_condition?(root, body, condition)
|
67
|
+
condition_reads = local_variables_read_in_scope(condition)
|
68
|
+
|
69
|
+
candidates = AST.local_variable_assignments_in_scope(body).select do |node|
|
70
|
+
name = node.children.first
|
71
|
+
condition_reads.include?(name)
|
72
|
+
end
|
73
|
+
|
74
|
+
candidates.any? do |node|
|
75
|
+
first_assignment?(root, node)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return local variables that get assigned in scope
|
80
|
+
#
|
81
|
+
# @param [Parser::AST::Node]
|
82
|
+
#
|
83
|
+
# @return [Set<Symbol>]
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
#
|
87
|
+
def self.local_variable_assignments_in_scope(node)
|
88
|
+
Enumerator.new(
|
89
|
+
node,
|
90
|
+
LocalVariableScope.method(:not_reset_scope?),
|
91
|
+
).types(LocalVariableScope::ASSIGN_NODES)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Return local variables read
|
95
|
+
#
|
96
|
+
# @param [Parser::AST::Node] node
|
97
|
+
#
|
98
|
+
# @return [Set<Symbol>]
|
99
|
+
#
|
100
|
+
# @api private
|
101
|
+
#
|
102
|
+
def self.local_variables_read_in_scope(node)
|
103
|
+
Enumerator.new(
|
104
|
+
node,
|
105
|
+
LocalVariableScope.method(:not_close_scope?),
|
106
|
+
).type(:lvar).map(&FIRST_CHILD).to_set
|
107
|
+
end
|
108
|
+
|
109
|
+
# AST enumerator
|
110
|
+
class Enumerator
|
111
|
+
include Adamantium::Flat, Concord.new(:node, :controller), Enumerable
|
112
|
+
|
113
|
+
# Return new instance
|
114
|
+
#
|
115
|
+
# @param [Parser::AST::Node] node
|
116
|
+
# @param [#call(node)] controller
|
117
|
+
#
|
118
|
+
# @return [Enumerator]
|
119
|
+
#
|
120
|
+
# @api private
|
121
|
+
#
|
122
|
+
def self.new(node, controller = TAUTOLOGY)
|
123
|
+
super
|
124
|
+
end
|
125
|
+
|
126
|
+
# Return each node
|
127
|
+
#
|
128
|
+
# @return [Enumerator<Parser::AST::Node>]
|
129
|
+
# if no block given
|
130
|
+
#
|
131
|
+
# @return [self]
|
132
|
+
# otherwise
|
133
|
+
#
|
134
|
+
# @api private
|
135
|
+
#
|
136
|
+
def each(&block)
|
137
|
+
return to_enum unless block_given?
|
138
|
+
Walker.call(node, controller, &block)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Return nodes selected by types
|
142
|
+
#
|
143
|
+
# @param [Enumerable<Symbol>] types
|
144
|
+
#
|
145
|
+
# @return [Enumerable<Parser::AST::Node>]
|
146
|
+
#
|
147
|
+
# @api private
|
148
|
+
#
|
149
|
+
def types(types)
|
150
|
+
select { |node| types.include?(node.type) }
|
151
|
+
end
|
152
|
+
|
153
|
+
# Return nodes selected by type
|
154
|
+
#
|
155
|
+
# @param [Symbol] type
|
156
|
+
#
|
157
|
+
# @return [Enumerable<Parser::AST::Node>]
|
158
|
+
#
|
159
|
+
# @api private
|
160
|
+
#
|
161
|
+
def type(type)
|
162
|
+
select { |node| node.type == type }
|
163
|
+
end
|
164
|
+
|
165
|
+
# Return frozne set of objects
|
166
|
+
#
|
167
|
+
# @param [Enumerable] enumerable
|
168
|
+
#
|
169
|
+
# @return [Set]
|
170
|
+
#
|
171
|
+
# @api private
|
172
|
+
#
|
173
|
+
def self.set(enumerable)
|
174
|
+
enumerable.to_set.freeze
|
175
|
+
end
|
176
|
+
private_class_method :set
|
177
|
+
|
178
|
+
# Return nodes of type
|
179
|
+
#
|
180
|
+
# @param [Parser::AST::Node] node
|
181
|
+
# @param [Symbol] type
|
182
|
+
#
|
183
|
+
# @return [Enumerable<Parser::AST::Node]
|
184
|
+
#
|
185
|
+
# @api private
|
186
|
+
#
|
187
|
+
def self.type(node, type)
|
188
|
+
new(node).type(type)
|
189
|
+
end
|
190
|
+
private_class_method :type
|
191
|
+
|
192
|
+
end # Enumerator
|
193
|
+
|
194
|
+
# Controlled AST walker walking the AST in deeth first search with pre order
|
195
|
+
class Walker
|
196
|
+
include Concord.new(:block, :controller)
|
197
|
+
|
198
|
+
# Call ast walker
|
199
|
+
#
|
200
|
+
# @param [Parser::AST::Node] node
|
201
|
+
#
|
202
|
+
# @return [self]
|
203
|
+
#
|
204
|
+
# @api private
|
205
|
+
#
|
206
|
+
def self.call(node, controller = TAUTOLOGY, &block)
|
207
|
+
new(block, controller).call(node)
|
208
|
+
self
|
209
|
+
end
|
210
|
+
|
211
|
+
# Call walker with node
|
212
|
+
#
|
213
|
+
# @param [Parser::AST::Node] node
|
214
|
+
#
|
215
|
+
# @return [self]
|
216
|
+
#
|
217
|
+
# @api private
|
218
|
+
#
|
219
|
+
def call(node)
|
220
|
+
return unless controller.call(node)
|
221
|
+
block.call(node)
|
222
|
+
node.children.each do |child|
|
223
|
+
if child.kind_of?(Parser::AST::Node)
|
224
|
+
call(child)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
self
|
228
|
+
end
|
229
|
+
|
230
|
+
end # Walker
|
231
|
+
end # AST
|
232
|
+
end # Unparser
|