unparser 0.4.7 → 0.6.7

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.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +40 -9
  3. data/bin/unparser +2 -2
  4. data/lib/unparser/abstract_type.rb +121 -0
  5. data/lib/unparser/adamantium/method_builder.rb +111 -0
  6. data/lib/unparser/adamantium.rb +150 -0
  7. data/lib/unparser/anima/attribute.rb +59 -0
  8. data/lib/unparser/anima/error.rb +23 -0
  9. data/lib/unparser/anima.rb +184 -0
  10. data/lib/unparser/ast/local_variable_scope.rb +6 -76
  11. data/lib/unparser/ast.rb +1 -3
  12. data/lib/unparser/buffer.rb +14 -25
  13. data/lib/unparser/cli.rb +85 -77
  14. data/lib/unparser/{cli/color.rb → color.rb} +4 -14
  15. data/lib/unparser/comments.rb +0 -26
  16. data/lib/unparser/concord.rb +114 -0
  17. data/lib/unparser/constants.rb +4 -53
  18. data/lib/unparser/diff.rb +98 -0
  19. data/lib/unparser/dsl.rb +0 -32
  20. data/lib/unparser/either.rb +153 -0
  21. data/lib/unparser/emitter/alias.rb +2 -8
  22. data/lib/unparser/emitter/args.rb +45 -0
  23. data/lib/unparser/emitter/argument.rb +13 -169
  24. data/lib/unparser/emitter/array.rb +27 -0
  25. data/lib/unparser/emitter/array_pattern.rb +29 -0
  26. data/lib/unparser/emitter/assignment.rb +36 -127
  27. data/lib/unparser/emitter/begin.rb +9 -84
  28. data/lib/unparser/emitter/binary.rb +7 -20
  29. data/lib/unparser/emitter/block.rb +57 -41
  30. data/lib/unparser/emitter/case.rb +6 -48
  31. data/lib/unparser/emitter/case_guard.rb +27 -0
  32. data/lib/unparser/emitter/case_match.rb +40 -0
  33. data/lib/unparser/emitter/cbase.rb +1 -3
  34. data/lib/unparser/emitter/class.rb +6 -26
  35. data/lib/unparser/emitter/const_pattern.rb +24 -0
  36. data/lib/unparser/emitter/def.rb +7 -51
  37. data/lib/unparser/emitter/defined.rb +2 -12
  38. data/lib/unparser/emitter/dstr.rb +22 -0
  39. data/lib/unparser/emitter/dsym.rb +41 -0
  40. data/lib/unparser/emitter/find_pattern.rb +18 -0
  41. data/lib/unparser/emitter/flipflop.rb +11 -10
  42. data/lib/unparser/emitter/float.rb +29 -0
  43. data/lib/unparser/emitter/flow_modifier.rb +15 -53
  44. data/lib/unparser/emitter/for.rb +5 -19
  45. data/lib/unparser/emitter/hash.rb +36 -0
  46. data/lib/unparser/emitter/hash_pattern.rb +67 -0
  47. data/lib/unparser/emitter/hookexe.rb +5 -11
  48. data/lib/unparser/emitter/if.rb +15 -71
  49. data/lib/unparser/emitter/in_match.rb +21 -0
  50. data/lib/unparser/emitter/in_pattern.rb +36 -0
  51. data/lib/unparser/emitter/index.rb +22 -89
  52. data/lib/unparser/emitter/kwargs.rb +13 -0
  53. data/lib/unparser/emitter/kwbegin.rb +31 -0
  54. data/lib/unparser/emitter/lambda.rb +0 -8
  55. data/lib/unparser/emitter/masgn.rb +20 -0
  56. data/lib/unparser/emitter/match.rb +3 -17
  57. data/lib/unparser/emitter/match_alt.rb +23 -0
  58. data/lib/unparser/emitter/match_as.rb +21 -0
  59. data/lib/unparser/emitter/match_pattern.rb +30 -0
  60. data/lib/unparser/emitter/match_pattern_p.rb +20 -0
  61. data/lib/unparser/emitter/match_rest.rb +33 -0
  62. data/lib/unparser/emitter/match_var.rb +19 -0
  63. data/lib/unparser/emitter/mlhs.rb +40 -0
  64. data/lib/unparser/emitter/module.rb +3 -9
  65. data/lib/unparser/emitter/op_assign.rb +14 -29
  66. data/lib/unparser/emitter/pair.rb +33 -0
  67. data/lib/unparser/emitter/pin.rb +19 -0
  68. data/lib/unparser/emitter/primitive.rb +93 -0
  69. data/lib/unparser/emitter/range.rb +35 -0
  70. data/lib/unparser/emitter/regexp.rb +35 -0
  71. data/lib/unparser/emitter/repetition.rb +17 -57
  72. data/lib/unparser/emitter/rescue.rb +1 -97
  73. data/lib/unparser/emitter/root.rb +17 -1
  74. data/lib/unparser/emitter/send.rb +10 -219
  75. data/lib/unparser/emitter/simple.rb +33 -0
  76. data/lib/unparser/emitter/splat.rb +13 -19
  77. data/lib/unparser/emitter/super.rb +1 -29
  78. data/lib/unparser/emitter/undef.rb +1 -9
  79. data/lib/unparser/emitter/variable.rb +1 -31
  80. data/lib/unparser/emitter/xstr.rb +72 -0
  81. data/lib/unparser/emitter/yield.rb +1 -9
  82. data/lib/unparser/emitter.rb +24 -425
  83. data/lib/unparser/equalizer.rb +98 -0
  84. data/lib/unparser/generation.rb +252 -0
  85. data/lib/unparser/node_details/send.rb +65 -0
  86. data/lib/unparser/node_details.rb +21 -0
  87. data/lib/unparser/node_helpers.rb +48 -6
  88. data/lib/unparser/validation.rb +172 -0
  89. data/lib/unparser/writer/binary.rb +99 -0
  90. data/lib/unparser/writer/dynamic_string.rb +211 -0
  91. data/lib/unparser/writer/resbody.rb +40 -0
  92. data/lib/unparser/writer/rescue.rb +43 -0
  93. data/lib/unparser/{emitter → writer}/send/attribute_assignment.rb +11 -26
  94. data/lib/unparser/writer/send/binary.rb +27 -0
  95. data/lib/unparser/writer/send/conditional.rb +25 -0
  96. data/lib/unparser/writer/send/regular.rb +33 -0
  97. data/lib/unparser/{emitter → writer}/send/unary.rb +10 -17
  98. data/lib/unparser/writer/send.rb +115 -0
  99. data/lib/unparser/writer.rb +15 -0
  100. data/lib/unparser.rb +161 -77
  101. metadata +100 -157
  102. data/.circleci/config.yml +0 -49
  103. data/.gitignore +0 -37
  104. data/.rspec +0 -4
  105. data/.rubocop.yml +0 -9
  106. data/Changelog.md +0 -156
  107. data/Gemfile +0 -9
  108. data/Gemfile.lock +0 -181
  109. data/LICENSE +0 -20
  110. data/Rakefile +0 -22
  111. data/config/devtools.yml +0 -2
  112. data/config/flay.yml +0 -3
  113. data/config/flog.yml +0 -2
  114. data/config/mutant.yml +0 -6
  115. data/config/reek.yml +0 -98
  116. data/config/rubocop.yml +0 -122
  117. data/config/yardstick.yml +0 -2
  118. data/lib/unparser/cli/differ.rb +0 -152
  119. data/lib/unparser/cli/source.rb +0 -267
  120. data/lib/unparser/emitter/empty.rb +0 -23
  121. data/lib/unparser/emitter/ensure.rb +0 -37
  122. data/lib/unparser/emitter/literal/array.rb +0 -29
  123. data/lib/unparser/emitter/literal/dynamic.rb +0 -53
  124. data/lib/unparser/emitter/literal/dynamic_body.rb +0 -132
  125. data/lib/unparser/emitter/literal/execute_string.rb +0 -38
  126. data/lib/unparser/emitter/literal/hash.rb +0 -156
  127. data/lib/unparser/emitter/literal/primitive.rb +0 -145
  128. data/lib/unparser/emitter/literal/range.rb +0 -36
  129. data/lib/unparser/emitter/literal/regexp.rb +0 -114
  130. data/lib/unparser/emitter/literal/singleton.rb +0 -26
  131. data/lib/unparser/emitter/literal.rb +0 -10
  132. data/lib/unparser/emitter/meta.rb +0 -16
  133. data/lib/unparser/emitter/redo.rb +0 -25
  134. data/lib/unparser/emitter/resbody.rb +0 -76
  135. data/lib/unparser/emitter/retry.rb +0 -25
  136. data/lib/unparser/emitter/send/binary.rb +0 -57
  137. data/lib/unparser/emitter/send/conditional.rb +0 -40
  138. data/lib/unparser/emitter/send/regular.rb +0 -40
  139. data/lib/unparser/preprocessor.rb +0 -159
  140. data/spec/integration/unparser/corpus_spec.rb +0 -111
  141. data/spec/integrations.yml +0 -92
  142. data/spec/spec_helper.rb +0 -20
  143. data/spec/unit/unparser/buffer/append_spec.rb +0 -24
  144. data/spec/unit/unparser/buffer/append_without_prefix_spec.rb +0 -23
  145. data/spec/unit/unparser/buffer/capture_content_spec.rb +0 -17
  146. data/spec/unit/unparser/buffer/content_spec.rb +0 -38
  147. data/spec/unit/unparser/buffer/fresh_line_spec.rb +0 -20
  148. data/spec/unit/unparser/buffer/indent_spec.rb +0 -20
  149. data/spec/unit/unparser/buffer/nl_spec.rb +0 -16
  150. data/spec/unit/unparser/buffer/unindent_spec.rb +0 -20
  151. data/spec/unit/unparser/comments/consume_spec.rb +0 -22
  152. data/spec/unit/unparser/comments/take_all_spec.rb +0 -19
  153. data/spec/unit/unparser/comments/take_before_spec.rb +0 -46
  154. data/spec/unit/unparser/comments/take_eol_comments_spec.rb +0 -32
  155. data/spec/unit/unparser/emitter/class_methods/handle_spec.rb +0 -17
  156. data/spec/unit/unparser_spec.rb +0 -1849
  157. data/unparser.gemspec +0 -32
@@ -0,0 +1,211 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module Writer
5
+ class DynamicString
6
+ include Writer, Adamantium
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
+ FLAT_INTERPOLATION = %i[ivar cvar gvar nth_ref].to_set.freeze
22
+
23
+ private_constant(*constants(false))
24
+
25
+ def emit_heredoc_reminder
26
+ return unless heredoc?
27
+
28
+ emit_heredoc_body
29
+ emit_heredoc_footer
30
+ end
31
+
32
+ def dispatch
33
+ if heredoc?
34
+ emit_heredoc_header
35
+ else
36
+ emit_dstr
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def heredoc_header
43
+ '<<-HEREDOC'
44
+ end
45
+
46
+ def heredoc?
47
+ !children.empty? && (nl_last_child? && heredoc_pattern?)
48
+ end
49
+
50
+ def emit_heredoc_header
51
+ write(heredoc_header)
52
+ end
53
+
54
+ def emit_heredoc_body
55
+ nl
56
+ emit_normal_heredoc_body
57
+ end
58
+
59
+ def emit_heredoc_footer
60
+ write('HEREDOC')
61
+ end
62
+
63
+ def classify(node)
64
+ if n_str?(node)
65
+ classify_str(node)
66
+ else
67
+ node.type
68
+ end
69
+ end
70
+
71
+ def classify_str(node)
72
+ if str_nl?(node)
73
+ :str_nl
74
+ elsif node.children.first.end_with?("\n")
75
+ :str_nl_eol
76
+ elsif str_ws?(node)
77
+ :str_ws
78
+ elsif str_empty?(node)
79
+ :str_empty
80
+ end
81
+ end
82
+
83
+ def str_nl?(node)
84
+ node.eql?(s(:str, "\n"))
85
+ end
86
+
87
+ def str_empty?(node)
88
+ node.eql?(s(:str, ''))
89
+ end
90
+
91
+ def str_ws?(node)
92
+ /\A( |\t)+\z/.match?(node.children.first)
93
+ end
94
+
95
+ def heredoc_pattern?
96
+ heredoc_pattern_2? || heredoc_pattern_3?
97
+ end
98
+
99
+ def heredoc_pattern_3?
100
+ children.each_cons(3).any? do |group|
101
+ PATTERNS_3.include?(group.map(&method(:classify)))
102
+ end
103
+ end
104
+
105
+ def heredoc_pattern_2?
106
+ children.each_cons(2).any? do |group|
107
+ PATTERNS_2.include?(group.map(&method(:classify)))
108
+ end
109
+ end
110
+
111
+ def nl_last_child?
112
+ last = children.last
113
+ n_str?(last) && last.children.first[-1].eql?("\n")
114
+ end
115
+
116
+ def emit_normal_heredoc_body
117
+ buffer.root_indent do
118
+ children.each do |child|
119
+ if n_str?(child)
120
+ write(escape_dynamic(child.children.first))
121
+ else
122
+ emit_dynamic(child)
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ def escape_dynamic(string)
129
+ string.gsub('#', '\#')
130
+ end
131
+
132
+ def emit_dynamic(child)
133
+ if FLAT_INTERPOLATION.include?(child.type)
134
+ write('#')
135
+ visit(child)
136
+ elsif n_dstr?(child)
137
+ emit_body(child.children)
138
+ else
139
+ write('#{')
140
+ emit_dynamic_component(child.children.first)
141
+ write('}')
142
+ end
143
+ end
144
+
145
+ def emit_dynamic_component(node)
146
+ visit(node) if node
147
+ end
148
+
149
+ def emit_dstr
150
+ if children.empty?
151
+ write('%()')
152
+ else
153
+ segments.each_with_index do |children, index|
154
+ emit_segment(children, index)
155
+ end
156
+ end
157
+ end
158
+
159
+ def breakpoint?(child, current)
160
+ last_type = current.last&.type
161
+
162
+ [
163
+ n_str?(child) && last_type.equal?(:str) && current.none?(&method(:n_begin?)),
164
+ last_type.equal?(:dstr),
165
+ n_dstr?(child) && last_type
166
+ ].any?
167
+ end
168
+
169
+ def segments
170
+ segments = []
171
+
172
+ segments << current = []
173
+
174
+ children.each do |child|
175
+ if breakpoint?(child, current)
176
+ segments << current = []
177
+ end
178
+
179
+ current << child
180
+ end
181
+
182
+ segments
183
+ end
184
+
185
+ def emit_segment(children, index)
186
+ write(' ') unless index.zero?
187
+
188
+ write('"')
189
+ emit_body(children)
190
+ write('"')
191
+ end
192
+
193
+ def emit_body(children)
194
+ buffer.root_indent do
195
+ children.each_with_index do |child, index|
196
+ if n_str?(child)
197
+ string = child.children.first
198
+ if string.eql?("\n") && children.fetch(index.pred).type.equal?(:begin)
199
+ write("\n")
200
+ else
201
+ write(string.inspect[1..-2])
202
+ end
203
+ else
204
+ emit_dynamic(child)
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end # DynamicString
210
+ end # Writer
211
+ end # Unparser
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module Writer
5
+ # Writer for rescue bodies
6
+ class Resbody
7
+ include Writer
8
+
9
+ children :exception, :assignment, :body
10
+
11
+ def emit_postcontrol
12
+ write(' rescue ')
13
+ visit(body)
14
+ end
15
+
16
+ def emit_regular
17
+ write('rescue')
18
+ emit_exception
19
+ emit_assignment
20
+ emit_optional_body(body)
21
+ end
22
+
23
+ private
24
+
25
+ def emit_exception
26
+ return unless exception
27
+
28
+ ws
29
+ delimited(exception.children)
30
+ end
31
+
32
+ def emit_assignment
33
+ return unless assignment
34
+
35
+ write(' => ')
36
+ visit(assignment)
37
+ end
38
+ end # Resbody
39
+ end # Writer
40
+ end # Unparser
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module Writer
5
+ class Rescue
6
+ include Writer, Adamantium
7
+
8
+ children :body, :rescue_body
9
+
10
+ define_group :rescue_bodies, 1..-2
11
+
12
+ def emit_regular
13
+ emit_optional_body(body)
14
+
15
+ rescue_bodies.each(&method(:emit_rescue_body))
16
+
17
+ if else_node
18
+ write('else')
19
+ emit_body(else_node)
20
+ end
21
+ end
22
+
23
+ def emit_heredoc_reminders
24
+ emitter(body).emit_heredoc_reminders
25
+ end
26
+
27
+ def emit_postcontrol
28
+ visit(body)
29
+ writer_with(Resbody, rescue_body).emit_postcontrol
30
+ end
31
+
32
+ private
33
+
34
+ def else_node
35
+ children.last
36
+ end
37
+
38
+ def emit_rescue_body(node)
39
+ writer_with(Resbody, node).emit_regular
40
+ end
41
+ end # Rescue
42
+ end # Writer
43
+ end # Unparser
@@ -1,24 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Unparser
4
- class Emitter
4
+ module Writer
5
5
  class Send
6
- # Emitter for send as attribute assignment
6
+ # Writer for send as attribute assignment
7
7
  class AttributeAssignment < self
8
- include Unterminated
9
-
10
8
  children :receiver, :selector, :first_argument
11
9
 
12
- # Perform regular dispatch
13
- #
14
- # @return [undefined]
15
- #
16
- # @api private
17
- #
18
10
  def dispatch
19
11
  emit_receiver
20
12
  emit_attribute
21
- write(T_ASN)
13
+ write('=')
22
14
 
23
15
  if arguments.one?
24
16
  visit(first_argument)
@@ -27,29 +19,22 @@ module Unparser
27
19
  end
28
20
  end
29
21
 
22
+ def emit_send_mlhs
23
+ emit_receiver
24
+ write(details.non_assignment_selector)
25
+ end
26
+
30
27
  private
31
28
 
32
- # Emit receiver
33
- #
34
- # @return [Parser::AST::Node]
35
- #
36
- # @api private
37
- #
38
29
  def emit_receiver
39
30
  visit(receiver)
40
- write(T_DOT)
31
+ emit_operator
41
32
  end
42
33
 
43
- # Emit attribute
44
- #
45
- # @return [undefined]
46
- #
47
- # @api private
48
- #
49
34
  def emit_attribute
50
- write(non_assignment_selector)
35
+ write(details.non_assignment_selector)
51
36
  end
52
37
  end # AttributeAssignment
53
38
  end # Send
54
- end # Emitter
39
+ end # Writer
55
40
  end # Unparser
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module Writer
5
+ class Send
6
+ # Writer for binary sends
7
+ class Binary < self
8
+ def dispatch
9
+ visit(receiver)
10
+ emit_operator
11
+ emit_right
12
+ end
13
+
14
+ private
15
+
16
+ def emit_operator
17
+ write(' ', details.string_selector, ' ')
18
+ end
19
+
20
+ def emit_right
21
+ emit_send_regular(children.fetch(2))
22
+ end
23
+
24
+ end # Binary
25
+ end # Send
26
+ end # Writer
27
+ end # Unparser
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module Writer
5
+ class Send
6
+ # Writer for "conditional" receiver&.selector(arguments...) case
7
+ class Conditional < self
8
+
9
+ private
10
+
11
+ def dispatch
12
+ emit_receiver
13
+ emit_selector
14
+ emit_arguments
15
+ end
16
+
17
+ def emit_receiver
18
+ visit(receiver)
19
+ write('&.')
20
+ end
21
+
22
+ end # Regular
23
+ end # Send
24
+ end # Writer
25
+ end # Unparser
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module Writer
5
+ class Send
6
+ # Writer for "regular" receiver.selector(arguments...) case
7
+ class Regular < self
8
+ def dispatch
9
+ emit_receiver
10
+ emit_selector
11
+ emit_arguments
12
+ end
13
+
14
+ def emit_send_mlhs
15
+ dispatch
16
+ end
17
+
18
+ def emit_arguments_without_heredoc_body
19
+ emit_normal_arguments if arguments.any?
20
+ end
21
+
22
+ def emit_receiver
23
+ return unless receiver
24
+
25
+ emit_send_regular(receiver)
26
+
27
+ emit_operator
28
+ end
29
+
30
+ end # Regular
31
+ end # Send
32
+ end # Writer
33
+ end # Unparser
@@ -1,36 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Unparser
4
- class Emitter
4
+ module Writer
5
5
  class Send
6
- # Emitter for unary sends
6
+ # Writer for unary sends
7
7
  class Unary < self
8
- include Unterminated
9
-
10
- private
11
-
12
- MAP = IceNine.deep_freeze(
8
+ MAP = {
13
9
  '-@': '-',
14
10
  '+@': '+'
15
- )
11
+ }.freeze
12
+
13
+ private_constant(*constants(false))
16
14
 
17
- # Perform dispatch
18
- #
19
- # @return [undefined]
20
- #
21
- # @api private
22
- #
23
15
  def dispatch
24
16
  name = selector
17
+
25
18
  write(MAP.fetch(name, name).to_s)
26
- if receiver.type.equal?(:int) && selector.equal?(:'+@')
19
+
20
+ if n_int?(receiver) && selector.equal?(:+@)
27
21
  write('+')
28
22
  end
29
23
 
30
24
  visit(receiver)
31
25
  end
32
-
33
26
  end # Unary
34
27
  end # Send
35
- end # Emitter
28
+ end # Writer
36
29
  end # Unparser
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module Writer
5
+ # Writer for send
6
+ class Send
7
+ include Writer, Adamantium, Constants, Generation
8
+
9
+ INDEX_ASSIGN = :[]=
10
+ INDEX_REFERENCE = :[]
11
+
12
+ OPERATORS = {
13
+ csend: '&.',
14
+ send: '.'
15
+ }.freeze
16
+
17
+ private_constant(*constants(false))
18
+
19
+ children :receiver, :selector
20
+
21
+ def dispatch
22
+ effective_writer.dispatch
23
+ end
24
+
25
+ def emit_mlhs
26
+ effective_writer.emit_send_mlhs
27
+ end
28
+
29
+ def emit_selector
30
+ write(details.string_selector)
31
+ end
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
+ private
39
+
40
+ def effective_writer
41
+ writer_with(effective_writer_class, node)
42
+ end
43
+ memoize :effective_writer
44
+
45
+ def effective_writer_class
46
+ if details.binary_syntax_allowed?
47
+ Binary
48
+ elsif details.selector_unary_operator? && n_send?(node) && arguments.empty?
49
+ Unary
50
+ elsif write_as_attribute_assignment?
51
+ AttributeAssignment
52
+ else
53
+ Regular
54
+ end
55
+ end
56
+
57
+ def write_as_attribute_assignment?
58
+ details.assignment_operator?
59
+ end
60
+
61
+ def emit_operator
62
+ write(OPERATORS.fetch(node.type))
63
+ end
64
+
65
+ def emit_arguments
66
+ if arguments.empty?
67
+ write('()') if receiver.nil? && avoid_clash?
68
+ else
69
+ emit_normal_arguments
70
+ end
71
+ end
72
+
73
+ def arguments
74
+ details.arguments
75
+ end
76
+
77
+ def emit_normal_arguments
78
+ parentheses { delimited(arguments) }
79
+ end
80
+
81
+ def emit_heredoc_reminder(argument)
82
+ emitter(argument).emit_heredoc_reminders
83
+ end
84
+
85
+ def avoid_clash?
86
+ local_variable_clash? || parses_as_constant?
87
+ end
88
+
89
+ def local_variable_clash?
90
+ local_variable_scope.local_variable_defined_for_node?(node, selector)
91
+ end
92
+
93
+ def parses_as_constant?
94
+ test = Unparser.parse_either(selector.to_s).from_right do
95
+ fail InvalidNodeError.new("Invalid selector for send node: #{selector.inspect}", node)
96
+ end
97
+
98
+ n_const?(test)
99
+ end
100
+
101
+ def details
102
+ NodeDetails::Send.new(node)
103
+ end
104
+ memoize :details
105
+
106
+ def emit_send_regular(node)
107
+ if n_send?(node)
108
+ writer_with(Regular, node).dispatch
109
+ else
110
+ visit(node)
111
+ end
112
+ end
113
+ end # Send
114
+ end # Writer
115
+ end # Unparser
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module Writer
5
+ include Generation, NodeHelpers
6
+
7
+ def self.included(descendant)
8
+ descendant.class_eval do
9
+ include Anima.new(:buffer, :comments, :node, :local_variable_scope)
10
+
11
+ extend DSL
12
+ end
13
+ end
14
+ end # Writer
15
+ end # Unparser