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
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
|