unparser 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Dependency Status](https://gemnasium.com/mbj/unparser.png)](https://gemnasium.com/mbj/unparser)
|
6
6
|
[![Code Climate](https://codeclimate.com/github/mbj/unparser.png)](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
|