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
data/lib/unparser/constants.rb
CHANGED
@@ -4,17 +4,30 @@ module Unparser
|
|
4
4
|
# All unparser constants maybe included in other libraries.
|
5
5
|
module Constants
|
6
6
|
|
7
|
+
# Return frozen symbol set from enumerable
|
8
|
+
#
|
9
|
+
# @param [Enumerable]
|
10
|
+
#
|
11
|
+
# @return [Set<Symbol>]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
#
|
15
|
+
def self.symbol_set(enumerable)
|
16
|
+
enumerable.map(&:to_sym).freeze
|
17
|
+
end
|
18
|
+
private_class_method :symbol_set
|
19
|
+
|
7
20
|
# All unary operators of the ruby language
|
8
|
-
UNARY_OPERATORS = %w(
|
21
|
+
UNARY_OPERATORS = symbol_set %w(
|
9
22
|
! ~ -@ +@
|
10
|
-
)
|
23
|
+
)
|
11
24
|
|
12
25
|
# All binary operators of the ruby language
|
13
|
-
BINARY_OPERATORS = %w(
|
26
|
+
BINARY_OPERATORS = symbol_set %w(
|
14
27
|
+ - * / & | && || << >> ==
|
15
28
|
=== != <= < <=> > >= =~ !~ ^
|
16
29
|
** %
|
17
|
-
)
|
30
|
+
)
|
18
31
|
|
19
32
|
COMMENT = '#'
|
20
33
|
|
@@ -89,12 +102,16 @@ module Unparser
|
|
89
102
|
#
|
90
103
|
# These nodes dont require parentheses to be exactly reproduced in context of a more complex expression.
|
91
104
|
#
|
92
|
-
TERMINATED = %w(
|
105
|
+
TERMINATED = symbol_set %w(
|
93
106
|
int float self kwbegin const regexp args lvar
|
94
107
|
ivar gvar cvar if case module class sclass super
|
95
108
|
yield zsuper break next defined? str block while loop until
|
96
109
|
def defs true false nil array hash sym return
|
97
|
-
)
|
110
|
+
)
|
111
|
+
|
112
|
+
DEFAULT_DELIMITER = ', '.freeze
|
113
|
+
|
114
|
+
CURLY_BRACKETS = IceNine.deep_freeze(%w({ }))
|
98
115
|
|
99
116
|
KEYWORDS = constants.each_with_object([]) do |name, keywords|
|
100
117
|
value = const_get(name).freeze
|
data/lib/unparser/emitter.rb
CHANGED
@@ -7,6 +7,30 @@ module Unparser
|
|
7
7
|
include Adamantium::Flat, AbstractType, Constants, NodeHelpers
|
8
8
|
include Concord.new(:node, :parent)
|
9
9
|
|
10
|
+
module LocalVariableRoot
|
11
|
+
|
12
|
+
# Return local variable root
|
13
|
+
#
|
14
|
+
# @return [Parser::AST::Node]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
#
|
18
|
+
def local_variable_root
|
19
|
+
node
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return local variable root
|
25
|
+
#
|
26
|
+
# @return [Parser::AST::Node]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
#
|
30
|
+
def local_variable_root
|
31
|
+
parent.local_variable_root
|
32
|
+
end
|
33
|
+
|
10
34
|
# Registry for node emitters
|
11
35
|
REGISTRY = {}
|
12
36
|
|
@@ -16,6 +40,14 @@ module Unparser
|
|
16
40
|
|
17
41
|
CURLY_BRACKETS = IceNine.deep_freeze(%w({ }))
|
18
42
|
|
43
|
+
# Return assigned lvars
|
44
|
+
#
|
45
|
+
# @return [Array<Symbol>]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
#
|
49
|
+
abstract_method :local_variables
|
50
|
+
|
19
51
|
# Return node type
|
20
52
|
#
|
21
53
|
# @return [Symbol]
|
@@ -30,6 +30,8 @@ module Unparser
|
|
30
30
|
|
31
31
|
handle :args
|
32
32
|
|
33
|
+
SHADOWARGS = ->(node) { node.type == :shadowarg }.freeze
|
34
|
+
|
33
35
|
private
|
34
36
|
|
35
37
|
# Perform dispatch
|
@@ -39,16 +41,40 @@ module Unparser
|
|
39
41
|
# @api private
|
40
42
|
#
|
41
43
|
def dispatch
|
42
|
-
|
44
|
+
delimited(normal_arguments)
|
45
|
+
unless shadowargs.empty?
|
46
|
+
write('; ')
|
47
|
+
delimited(shadowargs)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Return normal arguments
|
52
|
+
#
|
53
|
+
# @return [Enumerable<Parser::AST::Node>]
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
#
|
57
|
+
def normal_arguments
|
58
|
+
children.reject(&SHADOWARGS).map do |child|
|
43
59
|
if child.type == :mlhs
|
44
60
|
Parser::AST::Node.new(:arg_expr, [child])
|
45
61
|
else
|
46
62
|
child
|
47
63
|
end
|
48
64
|
end
|
49
|
-
delimited(mapped)
|
50
65
|
end
|
51
66
|
|
67
|
+
# Return shadow args
|
68
|
+
#
|
69
|
+
# @return [Enumerable<Parser::AST::Node>]
|
70
|
+
#
|
71
|
+
# @api private
|
72
|
+
#
|
73
|
+
def shadowargs
|
74
|
+
children.select(&SHADOWARGS)
|
75
|
+
end
|
76
|
+
memoize :shadowargs
|
77
|
+
|
52
78
|
end # Arguments
|
53
79
|
|
54
80
|
# Emitter for block arguments
|
@@ -178,7 +204,7 @@ module Unparser
|
|
178
204
|
# Argument emitter
|
179
205
|
class Argument < self
|
180
206
|
|
181
|
-
handle :arg
|
207
|
+
handle :arg, :shadowarg
|
182
208
|
|
183
209
|
children :name
|
184
210
|
|
@@ -213,7 +239,7 @@ module Unparser
|
|
213
239
|
#
|
214
240
|
def dispatch
|
215
241
|
write(T_AMP)
|
216
|
-
|
242
|
+
visit_terminated(name)
|
217
243
|
end
|
218
244
|
|
219
245
|
end # BlockPass
|
@@ -90,6 +90,8 @@ module Unparser
|
|
90
90
|
|
91
91
|
handle :masgn
|
92
92
|
|
93
|
+
PARENS = IceNine.deep_freeze(%w([ ]))
|
94
|
+
|
93
95
|
private
|
94
96
|
|
95
97
|
# Emit left
|
@@ -113,7 +115,10 @@ module Unparser
|
|
113
115
|
right = children.last
|
114
116
|
case right.type
|
115
117
|
when :array
|
116
|
-
|
118
|
+
children = right.children
|
119
|
+
parentheses(*PARENS) do
|
120
|
+
delimited(children)
|
121
|
+
end
|
117
122
|
else
|
118
123
|
visit(right)
|
119
124
|
end
|
@@ -128,6 +133,8 @@ module Unparser
|
|
128
133
|
|
129
134
|
private
|
130
135
|
|
136
|
+
NO_COMMA = [:splat, :restarg, :mlhs]
|
137
|
+
|
131
138
|
# Perform dispatch
|
132
139
|
#
|
133
140
|
# @return [undefined]
|
@@ -138,6 +145,10 @@ module Unparser
|
|
138
145
|
conditional_parentheses(parent_type == :mlhs) do
|
139
146
|
delimited(children)
|
140
147
|
end
|
148
|
+
|
149
|
+
if children.one? && !NO_COMMA.include?(children.first.type)
|
150
|
+
write(',')
|
151
|
+
end
|
141
152
|
end
|
142
153
|
|
143
154
|
end # MLHS
|
@@ -25,6 +25,8 @@ module Unparser
|
|
25
25
|
|
26
26
|
handle :begin
|
27
27
|
|
28
|
+
NON_EMPTY_PARENS = [:root, :dstr, :dyn_str_body].to_set.freeze
|
29
|
+
|
28
30
|
private
|
29
31
|
|
30
32
|
# Perform dispatch
|
@@ -34,7 +36,23 @@ module Unparser
|
|
34
36
|
# @api private
|
35
37
|
#
|
36
38
|
def dispatch
|
37
|
-
|
39
|
+
if children.empty? && !NON_EMPTY_PARENS.include?(parent_type)
|
40
|
+
write('()')
|
41
|
+
else
|
42
|
+
conditional_parentheses(parent_type == :optarg) do
|
43
|
+
emit_inner
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Emit inner delimited by semicolons
|
49
|
+
#
|
50
|
+
# @return [undefined]
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
#
|
54
|
+
def emit_inner_semi
|
55
|
+
delimited(children, ';')
|
38
56
|
end
|
39
57
|
|
40
58
|
end # Implicit
|
@@ -65,7 +83,10 @@ module Unparser
|
|
65
83
|
# @api private
|
66
84
|
#
|
67
85
|
def emit_body
|
68
|
-
|
86
|
+
case
|
87
|
+
when body.nil?
|
88
|
+
nl
|
89
|
+
when NOINDENT.include?(body.type)
|
69
90
|
emit_inner
|
70
91
|
else
|
71
92
|
indented { emit_inner }
|
data/lib/unparser/emitter/def.rb
CHANGED
@@ -4,6 +4,7 @@ module Unparser
|
|
4
4
|
class Emitter
|
5
5
|
# Emitter for def node
|
6
6
|
class Def < self
|
7
|
+
include LocalVariableRoot
|
7
8
|
|
8
9
|
private
|
9
10
|
|
@@ -88,10 +89,36 @@ module Unparser
|
|
88
89
|
# @api private
|
89
90
|
#
|
90
91
|
def emit_name
|
91
|
-
|
92
|
+
conditional_parentheses(!subject_without_parens?) do
|
93
|
+
visit(subject)
|
94
|
+
end
|
92
95
|
write(T_DOT, name.to_s)
|
93
96
|
end
|
94
97
|
|
98
|
+
# Test if subject needs parentheses
|
99
|
+
#
|
100
|
+
# @return [true]
|
101
|
+
# if subject needs parentheses
|
102
|
+
#
|
103
|
+
# @return [false]
|
104
|
+
# otherwise
|
105
|
+
#
|
106
|
+
# @api private
|
107
|
+
#
|
108
|
+
def subject_without_parens?
|
109
|
+
case subject.type
|
110
|
+
when :self
|
111
|
+
true
|
112
|
+
when :const
|
113
|
+
!subject.children.first
|
114
|
+
when :send
|
115
|
+
receiver, _selector, *arguments = *subject
|
116
|
+
!receiver && arguments.empty?
|
117
|
+
else
|
118
|
+
false
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
95
122
|
end # Singleton
|
96
123
|
end # Def
|
97
124
|
end # Emitter
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
class Emitter
|
5
|
+
# Emitter control flow modifiers
|
6
|
+
class FlowModifier < self
|
7
|
+
|
8
|
+
MAP = {
|
9
|
+
return: K_RETURN,
|
10
|
+
next: K_NEXT,
|
11
|
+
break: K_BREAK,
|
12
|
+
}
|
13
|
+
|
14
|
+
handle(*MAP.keys)
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Perform dispatch
|
19
|
+
#
|
20
|
+
# @return [undefined]
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
#
|
24
|
+
def dispatch
|
25
|
+
conditional_parentheses((parent_type == :or || parent_type == :and) && children.any?) do
|
26
|
+
write(MAP.fetch(node.type))
|
27
|
+
emit_arguments if children.any?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Emit break or return arguments
|
32
|
+
#
|
33
|
+
# @return [undefined]
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
#
|
37
|
+
def emit_arguments
|
38
|
+
ws
|
39
|
+
head, *tail = children
|
40
|
+
emit_argument(head)
|
41
|
+
tail.each do |node|
|
42
|
+
write(DEFAULT_DELIMITER)
|
43
|
+
emit_argument(node)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
PARENS = [:if, :case, :begin].to_set.freeze
|
48
|
+
|
49
|
+
# Emit argument
|
50
|
+
#
|
51
|
+
# @param [Parser::AST::Node] node
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
#
|
55
|
+
def emit_argument(node)
|
56
|
+
conditional_parentheses(PARENS.include?(node.type)) do
|
57
|
+
visit(node)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end # Return
|
62
|
+
end # Emitter
|
63
|
+
end # Unparser
|
data/lib/unparser/emitter/if.rb
CHANGED
@@ -18,6 +18,50 @@ module Unparser
|
|
18
18
|
# @api private
|
19
19
|
#
|
20
20
|
def dispatch
|
21
|
+
if postcondition?
|
22
|
+
emit_postcondition
|
23
|
+
else
|
24
|
+
emit_normal
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Test for postcondition
|
29
|
+
#
|
30
|
+
# @return [true]
|
31
|
+
# if node must be emitted in postconditin style
|
32
|
+
#
|
33
|
+
# @return [false]
|
34
|
+
# otherwise
|
35
|
+
#
|
36
|
+
# @api private
|
37
|
+
#
|
38
|
+
def postcondition?
|
39
|
+
return false unless !!if_branch ^ !!else_branch
|
40
|
+
|
41
|
+
body = if_branch || else_branch
|
42
|
+
|
43
|
+
AST.first_assignment_in_body_and_used_in_condition?(local_variable_root, body, condition)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Emit in postcondition style
|
47
|
+
#
|
48
|
+
# @return [undefined]
|
49
|
+
#
|
50
|
+
# @api private
|
51
|
+
#
|
52
|
+
def emit_postcondition
|
53
|
+
visit(if_branch || else_branch)
|
54
|
+
write(WS, keyword, WS)
|
55
|
+
emit_condition
|
56
|
+
end
|
57
|
+
|
58
|
+
# Emit in normal style
|
59
|
+
#
|
60
|
+
# @return [undefined]
|
61
|
+
#
|
62
|
+
# @api private
|
63
|
+
#
|
64
|
+
def emit_normal
|
21
65
|
write(keyword, WS)
|
22
66
|
emit_condition
|
23
67
|
emit_if_branch
|