unparser 0.6.5 → 0.8.0
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/README.md +11 -6
- data/bin/unparser +1 -1
- data/lib/unparser/adamantium.rb +3 -1
- data/lib/unparser/anima.rb +11 -0
- data/lib/unparser/ast/local_variable_scope.rb +28 -25
- data/lib/unparser/ast.rb +18 -22
- data/lib/unparser/buffer.rb +43 -15
- data/lib/unparser/cli.rb +30 -7
- data/lib/unparser/color.rb +5 -0
- data/lib/unparser/either.rb +6 -6
- data/lib/unparser/emitter/args.rb +5 -1
- data/lib/unparser/emitter/argument.rb +6 -4
- data/lib/unparser/emitter/array.rb +0 -4
- data/lib/unparser/emitter/array_pattern.rb +1 -9
- data/lib/unparser/emitter/assignment.rb +17 -8
- data/lib/unparser/emitter/begin.rb +0 -6
- data/lib/unparser/emitter/binary.rb +1 -1
- data/lib/unparser/emitter/block.rb +13 -6
- data/lib/unparser/emitter/def.rb +1 -1
- data/lib/unparser/emitter/dstr.rb +6 -5
- data/lib/unparser/emitter/dsym.rb +1 -1
- data/lib/unparser/emitter/ensure.rb +16 -0
- data/lib/unparser/emitter/flipflop.rb +7 -2
- data/lib/unparser/emitter/flow_modifier.rb +1 -7
- data/lib/unparser/emitter/for.rb +1 -1
- data/lib/unparser/emitter/hash.rb +0 -16
- data/lib/unparser/emitter/hash_pattern.rb +1 -1
- data/lib/unparser/emitter/in_pattern.rb +9 -1
- data/lib/unparser/emitter/index.rb +0 -4
- data/lib/unparser/emitter/kwbegin.rb +1 -1
- data/lib/unparser/emitter/match_pattern.rb +7 -11
- data/lib/unparser/emitter/match_pattern_p.rb +6 -1
- data/lib/unparser/emitter/mlhs.rb +7 -1
- data/lib/unparser/emitter/op_assign.rb +0 -5
- data/lib/unparser/emitter/pair.rb +31 -5
- data/lib/unparser/emitter/primitive.rb +19 -6
- data/lib/unparser/emitter/range.rb +23 -2
- data/lib/unparser/emitter/regexp.rb +5 -17
- data/lib/unparser/emitter/rescue.rb +7 -1
- data/lib/unparser/emitter/root.rb +2 -9
- data/lib/unparser/emitter/send.rb +1 -5
- data/lib/unparser/emitter/string.rb +31 -0
- data/lib/unparser/emitter/xstr.rb +8 -1
- data/lib/unparser/emitter.rb +9 -10
- data/lib/unparser/generation.rb +33 -29
- data/lib/unparser/node_details/send.rb +4 -3
- data/lib/unparser/node_details.rb +1 -0
- data/lib/unparser/node_helpers.rb +19 -9
- data/lib/unparser/util.rb +23 -0
- data/lib/unparser/validation.rb +70 -28
- data/lib/unparser/writer/array.rb +51 -0
- data/lib/unparser/writer/binary.rb +8 -4
- data/lib/unparser/writer/dynamic_string.rb +127 -146
- data/lib/unparser/writer/regexp.rb +101 -0
- data/lib/unparser/writer/resbody.rb +37 -3
- data/lib/unparser/writer/rescue.rb +3 -7
- data/lib/unparser/writer/send/unary.rb +9 -4
- data/lib/unparser/writer/send.rb +8 -14
- data/lib/unparser/writer.rb +31 -1
- data/lib/unparser.rb +149 -38
- metadata +38 -20
|
@@ -5,128 +5,123 @@ module Unparser
|
|
|
5
5
|
class DynamicString
|
|
6
6
|
include Writer, Adamantium
|
|
7
7
|
|
|
8
|
-
PATTERNS_2 =
|
|
9
|
-
[
|
|
10
|
-
%i[str_empty begin].freeze,
|
|
11
|
-
%i[begin str_nl].freeze
|
|
12
|
-
].freeze
|
|
13
|
-
|
|
14
|
-
PATTERNS_3 =
|
|
15
|
-
[
|
|
16
|
-
%i[begin str_nl_eol str_nl_eol].freeze,
|
|
17
|
-
%i[str_nl_eol begin str_nl_eol].freeze,
|
|
18
|
-
%i[str_ws begin str_nl_eol].freeze
|
|
19
|
-
].freeze
|
|
20
|
-
|
|
21
8
|
FLAT_INTERPOLATION = %i[ivar cvar gvar nth_ref].to_set.freeze
|
|
22
9
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
10
|
+
# Amount of dstr children at which heredoc emitting is
|
|
11
|
+
# preferred, but not guaranteed.
|
|
12
|
+
HEREDOC_THRESHOLD = 8
|
|
13
|
+
HEREDOC_DELIMITER = 'HEREDOC'
|
|
14
|
+
HEREDOC_HEADER = "<<-#{HEREDOC_DELIMITER}".freeze
|
|
15
|
+
HEREDOC_FOOTER = "#{HEREDOC_DELIMITER}\n".freeze
|
|
27
16
|
|
|
28
|
-
|
|
29
|
-
emit_heredoc_footer
|
|
30
|
-
end
|
|
17
|
+
private_constant(*constants(false))
|
|
31
18
|
|
|
19
|
+
# The raise below is not reachable if unparser is correctly implemented
|
|
20
|
+
# but has to exist as I have to assume unparser still has bugs.
|
|
21
|
+
#
|
|
22
|
+
# But unless I had such a bug in my test corpus: I cannot enable mutant, and if I
|
|
23
|
+
# knew about such a bug: I'd fix it so would be back at the start.
|
|
24
|
+
#
|
|
25
|
+
# TLDR: Good case for a mutant disable.
|
|
26
|
+
#
|
|
27
|
+
# mutant:disable
|
|
32
28
|
def dispatch
|
|
33
29
|
if heredoc?
|
|
34
|
-
|
|
30
|
+
write(HEREDOC_HEADER)
|
|
31
|
+
buffer.push_heredoc(heredoc_body)
|
|
32
|
+
elsif round_tripping_segmented_source
|
|
33
|
+
write(round_tripping_segmented_source)
|
|
35
34
|
else
|
|
36
|
-
|
|
35
|
+
fail UnsupportedNodeError, "Unparser cannot round trip this node: #{node.inspect}"
|
|
37
36
|
end
|
|
38
37
|
end
|
|
39
38
|
|
|
40
39
|
private
|
|
41
40
|
|
|
42
|
-
def heredoc_header
|
|
43
|
-
'<<-HEREDOC'
|
|
44
|
-
end
|
|
45
|
-
|
|
46
41
|
def heredoc?
|
|
47
|
-
|
|
42
|
+
if children.length >= HEREDOC_THRESHOLD
|
|
43
|
+
round_trips_heredoc?
|
|
44
|
+
else
|
|
45
|
+
round_tripping_segmented_source.nil? # && round_trips_heredoc?
|
|
46
|
+
end
|
|
48
47
|
end
|
|
48
|
+
memoize :heredoc?
|
|
49
49
|
|
|
50
|
-
def
|
|
51
|
-
|
|
50
|
+
def round_trips_heredoc?
|
|
51
|
+
round_trips?(source: heredoc_source)
|
|
52
52
|
end
|
|
53
|
+
memoize :round_trips_heredoc?
|
|
53
54
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
emit_normal_heredoc_body
|
|
57
|
-
end
|
|
55
|
+
def round_tripping_segmented_source
|
|
56
|
+
each_segments(children) do |segments|
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
write('HEREDOC')
|
|
61
|
-
end
|
|
58
|
+
source = segmented_source(segments: segments)
|
|
62
59
|
|
|
63
|
-
|
|
64
|
-
if n_str?(node)
|
|
65
|
-
classify_str(node)
|
|
66
|
-
else
|
|
67
|
-
node.type
|
|
60
|
+
return source if round_trips?(source: source)
|
|
68
61
|
end
|
|
62
|
+
nil
|
|
69
63
|
end
|
|
64
|
+
memoize :round_tripping_segmented_source
|
|
65
|
+
|
|
66
|
+
def each_segments(array)
|
|
67
|
+
yield [array]
|
|
70
68
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
:str_ws
|
|
78
|
-
elsif str_empty?(node)
|
|
79
|
-
:str_empty
|
|
69
|
+
1.upto(array.length) do |take|
|
|
70
|
+
prefix = [array.take(take)]
|
|
71
|
+
suffix = array.drop(take)
|
|
72
|
+
each_segments(suffix) do |items|
|
|
73
|
+
yield(prefix + items)
|
|
74
|
+
end
|
|
80
75
|
end
|
|
81
76
|
end
|
|
82
77
|
|
|
83
|
-
def
|
|
84
|
-
|
|
85
|
-
end
|
|
78
|
+
def segmented_source(segments:)
|
|
79
|
+
buffer = Buffer.new
|
|
86
80
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
81
|
+
Segmented.new(
|
|
82
|
+
buffer:,
|
|
83
|
+
comments:,
|
|
84
|
+
explicit_encoding: nil,
|
|
85
|
+
local_variable_scope:,
|
|
86
|
+
node:,
|
|
87
|
+
segments:
|
|
88
|
+
).dispatch
|
|
90
89
|
|
|
91
|
-
|
|
92
|
-
/\A( |\t)+\z/.match?(node.children.first)
|
|
90
|
+
buffer.content
|
|
93
91
|
end
|
|
94
92
|
|
|
95
|
-
def
|
|
96
|
-
|
|
97
|
-
end
|
|
93
|
+
def heredoc_body
|
|
94
|
+
buffer = Buffer.new
|
|
98
95
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
96
|
+
writer = Heredoc.new(
|
|
97
|
+
buffer:,
|
|
98
|
+
comments:,
|
|
99
|
+
explicit_encoding: nil,
|
|
100
|
+
local_variable_scope:,
|
|
101
|
+
node:
|
|
102
|
+
)
|
|
104
103
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
PATTERNS_2.include?(group.map(&method(:classify)))
|
|
108
|
-
end
|
|
104
|
+
writer.emit
|
|
105
|
+
buffer.content
|
|
109
106
|
end
|
|
107
|
+
memoize :heredoc_body
|
|
110
108
|
|
|
111
|
-
def
|
|
112
|
-
|
|
113
|
-
n_str?(last) && last.children.first[-1].eql?("\n")
|
|
109
|
+
def heredoc_source
|
|
110
|
+
"#{HEREDOC_HEADER}\n#{heredoc_body}"
|
|
114
111
|
end
|
|
112
|
+
memoize :heredoc_source
|
|
115
113
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
emit_dynamic(child)
|
|
123
|
-
end
|
|
114
|
+
class Heredoc
|
|
115
|
+
include Writer, Adamantium
|
|
116
|
+
|
|
117
|
+
def emit
|
|
118
|
+
emit_heredoc_body
|
|
119
|
+
write(HEREDOC_FOOTER)
|
|
124
120
|
end
|
|
125
|
-
buffer.unindent
|
|
126
|
-
end
|
|
127
121
|
|
|
128
|
-
|
|
129
|
-
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
def emit_heredoc_body
|
|
130
125
|
children.each do |child|
|
|
131
126
|
if n_str?(child)
|
|
132
127
|
write(escape_dynamic(child.children.first))
|
|
@@ -135,88 +130,74 @@ module Unparser
|
|
|
135
130
|
end
|
|
136
131
|
end
|
|
137
132
|
end
|
|
138
|
-
end
|
|
139
133
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
134
|
+
def escape_dynamic(string)
|
|
135
|
+
string.gsub('#', '\#')
|
|
136
|
+
end
|
|
143
137
|
|
|
144
|
-
|
|
145
|
-
if FLAT_INTERPOLATION.include?(child.type)
|
|
146
|
-
write('#')
|
|
147
|
-
visit(child)
|
|
148
|
-
elsif n_dstr?(child)
|
|
149
|
-
emit_body(child.children)
|
|
150
|
-
else
|
|
138
|
+
def emit_dynamic(child)
|
|
151
139
|
write('#{')
|
|
152
140
|
emit_dynamic_component(child.children.first)
|
|
153
141
|
write('}')
|
|
154
142
|
end
|
|
155
|
-
end
|
|
156
143
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
def emit_dstr
|
|
162
|
-
if children.empty?
|
|
163
|
-
write('%()')
|
|
164
|
-
else
|
|
165
|
-
segments.each_with_index do |children, index|
|
|
166
|
-
emit_segment(children, index)
|
|
167
|
-
end
|
|
144
|
+
def emit_dynamic_component(node)
|
|
145
|
+
visit(node) if node
|
|
168
146
|
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
def breakpoint?(child, current)
|
|
172
|
-
last_type = current.last&.type
|
|
147
|
+
end # Heredoc
|
|
173
148
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
last_type.equal?(:dstr),
|
|
177
|
-
n_dstr?(child) && last_type
|
|
178
|
-
].any?
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def segments
|
|
182
|
-
segments = []
|
|
149
|
+
class Segmented
|
|
150
|
+
include Writer, Adamantium
|
|
183
151
|
|
|
184
|
-
segments
|
|
152
|
+
include anima.add(:segments)
|
|
185
153
|
|
|
186
|
-
|
|
187
|
-
if
|
|
188
|
-
|
|
154
|
+
def dispatch
|
|
155
|
+
if children.empty?
|
|
156
|
+
write('%()')
|
|
157
|
+
else
|
|
158
|
+
segments.each_with_index { |segment, index| emit_segment(segment, index) }
|
|
189
159
|
end
|
|
190
|
-
|
|
191
|
-
current << child
|
|
192
160
|
end
|
|
193
161
|
|
|
194
|
-
|
|
195
|
-
end
|
|
162
|
+
private
|
|
196
163
|
|
|
197
|
-
|
|
198
|
-
|
|
164
|
+
def emit_segment(children, index)
|
|
165
|
+
write(' ') unless index.zero?
|
|
199
166
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
167
|
+
write('"')
|
|
168
|
+
emit_segment_body(children)
|
|
169
|
+
write('"')
|
|
170
|
+
end
|
|
204
171
|
|
|
205
|
-
|
|
206
|
-
buffer.root_indent do
|
|
172
|
+
def emit_segment_body(children)
|
|
207
173
|
children.each_with_index do |child, index|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
174
|
+
case child.type
|
|
175
|
+
when :begin
|
|
176
|
+
write('#{')
|
|
177
|
+
visit(child.children.first) if child.children.first
|
|
178
|
+
write('}')
|
|
179
|
+
when FLAT_INTERPOLATION
|
|
180
|
+
write('#')
|
|
181
|
+
visit(child)
|
|
182
|
+
when :str
|
|
183
|
+
visit_str(children, child, index)
|
|
184
|
+
when :dstr
|
|
185
|
+
emit_segment_body(child.children)
|
|
217
186
|
end
|
|
218
187
|
end
|
|
219
188
|
end
|
|
189
|
+
|
|
190
|
+
def visit_str(children, child, index)
|
|
191
|
+
string = child.children.first
|
|
192
|
+
|
|
193
|
+
next_child = children.at(index.succ)
|
|
194
|
+
|
|
195
|
+
if next_child && next_child.type.equal?(:str)
|
|
196
|
+
write(string.gsub('"', '\\"'))
|
|
197
|
+
else
|
|
198
|
+
write(child.children.first.inspect[1..-2])
|
|
199
|
+
end
|
|
200
|
+
end
|
|
220
201
|
end
|
|
221
202
|
end # DynamicString
|
|
222
203
|
end # Writer
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Unparser
|
|
4
|
+
module Writer
|
|
5
|
+
# Writer for regexp literals
|
|
6
|
+
class Regexp
|
|
7
|
+
include Writer, Adamantium
|
|
8
|
+
|
|
9
|
+
CANDIDATES = [
|
|
10
|
+
['/', '/'].freeze,
|
|
11
|
+
['%r{', '}'].freeze
|
|
12
|
+
].freeze
|
|
13
|
+
|
|
14
|
+
define_group(:body, 0..-2)
|
|
15
|
+
|
|
16
|
+
def dispatch
|
|
17
|
+
effective_writer.write_to_buffer
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
# mutant:disable
|
|
23
|
+
def effective_writer
|
|
24
|
+
CANDIDATES.each do |token_open, token_close|
|
|
25
|
+
source = render_with_delimiter(token_close:, token_open:)
|
|
26
|
+
|
|
27
|
+
next unless round_trips?(source:)
|
|
28
|
+
|
|
29
|
+
return writer_with(Effective, node:, token_close:, token_open:)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
fail 'Could not find a round tripping solution for regexp'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class Effective
|
|
36
|
+
include Writer, Adamantium
|
|
37
|
+
|
|
38
|
+
include anima.add(:token_close, :token_open)
|
|
39
|
+
|
|
40
|
+
define_group(:body, 0..-2)
|
|
41
|
+
|
|
42
|
+
def dispatch
|
|
43
|
+
buffer.root_indent do
|
|
44
|
+
write(token_open)
|
|
45
|
+
body.each(&method(:emit_body))
|
|
46
|
+
write(token_close)
|
|
47
|
+
emit_options
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def emit_body(node)
|
|
54
|
+
if n_begin?(node)
|
|
55
|
+
write('#{')
|
|
56
|
+
node.children.each(&method(:visit))
|
|
57
|
+
write('}')
|
|
58
|
+
elsif n_gvar?(node)
|
|
59
|
+
write('#')
|
|
60
|
+
write_regular(node.children.first.to_s)
|
|
61
|
+
else
|
|
62
|
+
write_regular(node.children.first)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def write_regular(string)
|
|
67
|
+
if string.length > 1 && string.start_with?("\n")
|
|
68
|
+
string.each_char do |char|
|
|
69
|
+
buffer.append_without_prefix(char.eql?("\n") ? '\c*' : char)
|
|
70
|
+
end
|
|
71
|
+
else
|
|
72
|
+
buffer.append_without_prefix(string)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def emit_options
|
|
77
|
+
write(children.last.children.join)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# mutant:disable
|
|
82
|
+
def render_with_delimiter(token_close:, token_open:)
|
|
83
|
+
buffer = Buffer.new
|
|
84
|
+
|
|
85
|
+
writer = Effective.new(
|
|
86
|
+
buffer:,
|
|
87
|
+
comments:,
|
|
88
|
+
explicit_encoding:,
|
|
89
|
+
local_variable_scope:,
|
|
90
|
+
node:,
|
|
91
|
+
token_close:,
|
|
92
|
+
token_open:
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
writer.dispatch
|
|
96
|
+
buffer.nl_flush_heredocs
|
|
97
|
+
buffer.content
|
|
98
|
+
end
|
|
99
|
+
end # Regexp
|
|
100
|
+
end # Emitter
|
|
101
|
+
end # Unparser
|
|
@@ -6,11 +6,21 @@ module Unparser
|
|
|
6
6
|
class Resbody
|
|
7
7
|
include Writer
|
|
8
8
|
|
|
9
|
+
OPERATORS = {
|
|
10
|
+
csend: '&.',
|
|
11
|
+
send: '.'
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
9
14
|
children :exception, :assignment, :body
|
|
10
15
|
|
|
11
16
|
def emit_postcontrol
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
if body
|
|
18
|
+
write(' rescue ')
|
|
19
|
+
visit(body)
|
|
20
|
+
else
|
|
21
|
+
nl
|
|
22
|
+
write('rescue')
|
|
23
|
+
end
|
|
14
24
|
end
|
|
15
25
|
|
|
16
26
|
def emit_regular
|
|
@@ -33,7 +43,31 @@ module Unparser
|
|
|
33
43
|
return unless assignment
|
|
34
44
|
|
|
35
45
|
write(' => ')
|
|
36
|
-
|
|
46
|
+
|
|
47
|
+
case assignment.type
|
|
48
|
+
when :send, :csend
|
|
49
|
+
write_send_assignment
|
|
50
|
+
when :indexasgn
|
|
51
|
+
write_index_assignment
|
|
52
|
+
else
|
|
53
|
+
visit(assignment)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def write_send_assignment
|
|
58
|
+
details = NodeDetails::Send.new(assignment)
|
|
59
|
+
|
|
60
|
+
visit(details.receiver)
|
|
61
|
+
write(OPERATORS.fetch(assignment.type))
|
|
62
|
+
write(details.non_assignment_selector)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def write_index_assignment
|
|
66
|
+
receiver, *indexes = assignment.children
|
|
67
|
+
visit(receiver)
|
|
68
|
+
write('[')
|
|
69
|
+
delimited(indexes)
|
|
70
|
+
write(']')
|
|
37
71
|
end
|
|
38
72
|
end # Resbody
|
|
39
73
|
end # Writer
|
|
@@ -20,13 +20,9 @@ module Unparser
|
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def emit_heredoc_reminders
|
|
24
|
-
emitter(body).emit_heredoc_reminders
|
|
25
|
-
end
|
|
26
|
-
|
|
27
23
|
def emit_postcontrol
|
|
28
|
-
visit(body)
|
|
29
|
-
writer_with(Resbody, rescue_body).emit_postcontrol
|
|
24
|
+
visit(body) if body
|
|
25
|
+
writer_with(Resbody, node: rescue_body).emit_postcontrol
|
|
30
26
|
end
|
|
31
27
|
|
|
32
28
|
private
|
|
@@ -36,7 +32,7 @@ module Unparser
|
|
|
36
32
|
end
|
|
37
33
|
|
|
38
34
|
def emit_rescue_body(node)
|
|
39
|
-
writer_with(Resbody, node).emit_regular
|
|
35
|
+
writer_with(Resbody, node:).emit_regular
|
|
40
36
|
end
|
|
41
37
|
end # Rescue
|
|
42
38
|
end # Writer
|
|
@@ -12,13 +12,18 @@ module Unparser
|
|
|
12
12
|
|
|
13
13
|
private_constant(*constants(false))
|
|
14
14
|
|
|
15
|
-
def dispatch
|
|
15
|
+
def dispatch # rubocop:disable Metrics/AbcSize
|
|
16
16
|
name = selector
|
|
17
|
+
first_child = children.fetch(0)
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
if n_flipflop?(first_child) || n_and?(first_child) || n_or?(first_child)
|
|
20
|
+
write 'not '
|
|
21
|
+
else
|
|
22
|
+
write(MAP.fetch(name, name).to_s)
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
if n_int?(receiver) && selector.equal?(:+@)
|
|
25
|
+
write('+')
|
|
26
|
+
end
|
|
22
27
|
end
|
|
23
28
|
|
|
24
29
|
visit(receiver)
|
data/lib/unparser/writer/send.rb
CHANGED
|
@@ -30,15 +30,10 @@ module Unparser
|
|
|
30
30
|
write(details.string_selector)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
def emit_heredoc_reminders
|
|
34
|
-
emitter(receiver).emit_heredoc_reminders if receiver
|
|
35
|
-
arguments.each(&method(:emit_heredoc_reminder))
|
|
36
|
-
end
|
|
37
|
-
|
|
38
33
|
private
|
|
39
34
|
|
|
40
35
|
def effective_writer
|
|
41
|
-
writer_with(effective_writer_class, node)
|
|
36
|
+
writer_with(effective_writer_class, node:)
|
|
42
37
|
end
|
|
43
38
|
memoize :effective_writer
|
|
44
39
|
|
|
@@ -78,10 +73,6 @@ module Unparser
|
|
|
78
73
|
parentheses { delimited(arguments) }
|
|
79
74
|
end
|
|
80
75
|
|
|
81
|
-
def emit_heredoc_reminder(argument)
|
|
82
|
-
emitter(argument).emit_heredoc_reminders
|
|
83
|
-
end
|
|
84
|
-
|
|
85
76
|
def avoid_clash?
|
|
86
77
|
local_variable_clash? || parses_as_constant?
|
|
87
78
|
end
|
|
@@ -91,9 +82,12 @@ module Unparser
|
|
|
91
82
|
end
|
|
92
83
|
|
|
93
84
|
def parses_as_constant?
|
|
94
|
-
test = Unparser
|
|
95
|
-
|
|
96
|
-
|
|
85
|
+
test = Unparser
|
|
86
|
+
.parse_ast_either(selector.to_s)
|
|
87
|
+
.fmap(&:node)
|
|
88
|
+
.from_right do
|
|
89
|
+
fail InvalidNodeError.new("Invalid selector for send node: #{selector.inspect}", node)
|
|
90
|
+
end
|
|
97
91
|
|
|
98
92
|
n_const?(test)
|
|
99
93
|
end
|
|
@@ -105,7 +99,7 @@ module Unparser
|
|
|
105
99
|
|
|
106
100
|
def emit_send_regular(node)
|
|
107
101
|
if n_send?(node)
|
|
108
|
-
writer_with(Regular, node).dispatch
|
|
102
|
+
writer_with(Regular, node:).dispatch
|
|
109
103
|
else
|
|
110
104
|
visit(node)
|
|
111
105
|
end
|
data/lib/unparser/writer.rb
CHANGED
|
@@ -4,12 +4,42 @@ module Unparser
|
|
|
4
4
|
module Writer
|
|
5
5
|
include Generation, NodeHelpers
|
|
6
6
|
|
|
7
|
+
# mutant:disable
|
|
7
8
|
def self.included(descendant)
|
|
8
9
|
descendant.class_eval do
|
|
9
|
-
include Anima.new(:buffer, :comments, :node, :local_variable_scope)
|
|
10
|
+
include Adamantium, Anima.new(:buffer, :comments, :explicit_encoding, :node, :local_variable_scope)
|
|
10
11
|
|
|
11
12
|
extend DSL
|
|
12
13
|
end
|
|
13
14
|
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
# mutant:disable
|
|
19
|
+
def emitter(node)
|
|
20
|
+
Emitter.emitter(
|
|
21
|
+
buffer: buffer,
|
|
22
|
+
comments: comments,
|
|
23
|
+
explicit_encoding: explicit_encoding,
|
|
24
|
+
local_variable_scope: local_variable_scope,
|
|
25
|
+
node: node
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# mutant:disable
|
|
30
|
+
def round_trips?(source:)
|
|
31
|
+
parser = Unparser.parser
|
|
32
|
+
local_variable_scope.local_variables_for_node(node).each do |local_variable|
|
|
33
|
+
parser.declare_local_variable(local_variable)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
buffer = Buffer.new
|
|
37
|
+
buffer.write_encoding(explicit_encoding) if explicit_encoding
|
|
38
|
+
buffer.write(source)
|
|
39
|
+
|
|
40
|
+
node.eql?(parser.parse(Unparser.buffer(buffer.content)))
|
|
41
|
+
rescue Parser::SyntaxError
|
|
42
|
+
false
|
|
43
|
+
end
|
|
14
44
|
end # Writer
|
|
15
45
|
end # Unparser
|