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,252 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ # rubocop:disable Metrics/ModuleLength
5
+ module Generation
6
+ EXTRA_NL = %i[kwbegin def defs module class sclass].freeze
7
+
8
+ private_constant(*constants(false))
9
+
10
+ def emit_heredoc_reminders; end
11
+
12
+ def symbol_name; end
13
+
14
+ def write_to_buffer
15
+ with_comments { dispatch }
16
+ self
17
+ end
18
+
19
+ private
20
+
21
+ def delimited(nodes, delimiter = ', ', &block)
22
+ return if nodes.empty?
23
+
24
+ emit_join(nodes, block || method(:visit), -> { write(delimiter) })
25
+ end
26
+
27
+ def emit_join(nodes, emit_node, emit_delimiter)
28
+ return if nodes.empty?
29
+
30
+ head, *tail = nodes
31
+ emit_node.call(head)
32
+
33
+ tail.each do |node|
34
+ emit_delimiter.call
35
+ emit_node.call(node)
36
+ end
37
+ end
38
+
39
+ def nl
40
+ emit_eol_comments
41
+ buffer.nl
42
+ end
43
+
44
+ def with_comments
45
+ emit_comments_before if buffer.fresh_line?
46
+ yield
47
+ comments.consume(node)
48
+ end
49
+
50
+ def ws
51
+ write(' ')
52
+ end
53
+
54
+ def emit_eol_comments
55
+ comments.take_eol_comments.each do |comment|
56
+ write(' ', comment.text)
57
+ end
58
+ end
59
+
60
+ def emit_eof_comments
61
+ emit_eol_comments
62
+ comments_left = comments.take_all
63
+ return if comments_left.empty?
64
+
65
+ buffer.nl
66
+ emit_comments(comments_left)
67
+ end
68
+
69
+ def emit_comments_before(source_part = :expression)
70
+ comments_before = comments.take_before(node, source_part)
71
+ return if comments_before.empty?
72
+
73
+ emit_comments(comments_before)
74
+ buffer.nl
75
+ end
76
+
77
+ def emit_comments(comments)
78
+ max = comments.size - 1
79
+ comments.each_with_index do |comment, index|
80
+ if comment.type.equal?(:document)
81
+ buffer.append_without_prefix(comment.text.chomp)
82
+ else
83
+ write(comment.text)
84
+ end
85
+ buffer.nl if index < max
86
+ end
87
+ end
88
+
89
+ def write(*strings)
90
+ strings.each(&buffer.method(:append))
91
+ end
92
+
93
+ def k_end
94
+ buffer.indent
95
+ emit_comments_before(:end)
96
+ buffer.unindent
97
+ write('end')
98
+ end
99
+
100
+ def parentheses(open = '(', close = ')')
101
+ write(open)
102
+ yield
103
+ write(close)
104
+ end
105
+
106
+ def indented
107
+ buffer = buffer()
108
+ buffer.indent
109
+ nl
110
+ yield
111
+ nl
112
+ buffer.unindent
113
+ end
114
+
115
+ def emit_optional_body(node, indent: true)
116
+ if node
117
+ emit_body(node, indent: indent)
118
+ else
119
+ nl
120
+ end
121
+ end
122
+
123
+ def emit_body(node, indent: true)
124
+ if indent
125
+ buffer.indent
126
+ nl
127
+ end
128
+
129
+ if n_begin?(node)
130
+ if node.children.one?
131
+ visit_deep(node)
132
+ else
133
+ emit_body_inner(node)
134
+ end
135
+ else
136
+ visit_deep(node)
137
+ end
138
+
139
+ if indent
140
+ buffer.unindent
141
+ nl
142
+ end
143
+ end
144
+
145
+ def emit_body_inner(node)
146
+ head, *tail = node.children
147
+ emit_body_member(head)
148
+
149
+ tail.each do |child|
150
+ nl
151
+
152
+ nl if EXTRA_NL.include?(child.type)
153
+
154
+ emit_body_member(child)
155
+ end
156
+ end
157
+
158
+ def emit_body_member(node)
159
+ if n_rescue?(node)
160
+ emit_rescue_postcontrol(node)
161
+ else
162
+ visit_deep(node)
163
+ end
164
+ end
165
+
166
+ def emit_ensure(node)
167
+ body, ensure_body = node.children
168
+
169
+ if body
170
+ emit_body_rescue(body)
171
+ else
172
+ nl
173
+ end
174
+
175
+ write('ensure')
176
+
177
+ emit_optional_body(ensure_body)
178
+ end
179
+
180
+ def emit_body_rescue(node)
181
+ if n_rescue?(node)
182
+ emit_rescue_regular(node)
183
+ else
184
+ emit_body(node)
185
+ end
186
+ end
187
+
188
+ def emit_optional_body_ensure_rescue(node)
189
+ if node
190
+ emit_body_ensure_rescue(node)
191
+ else
192
+ nl
193
+ end
194
+ end
195
+
196
+ def emit_body_ensure_rescue(node)
197
+ if n_ensure?(node)
198
+ emit_ensure(node)
199
+ elsif n_rescue?(node)
200
+ emit_rescue_regular(node)
201
+ else
202
+ emit_body(node)
203
+ end
204
+ end
205
+
206
+ def emit_rescue_postcontrol(node)
207
+ writer = writer_with(Writer::Rescue, node)
208
+ writer.emit_postcontrol
209
+ writer.emit_heredoc_reminders
210
+ end
211
+
212
+ def emit_rescue_regular(node)
213
+ writer_with(Writer::Rescue, node).emit_regular
214
+ end
215
+
216
+ def writer_with(klass, node)
217
+ klass.new(to_h.merge(node: node))
218
+ end
219
+
220
+ def emitter(node)
221
+ Emitter.emitter(**to_h.merge(node: node))
222
+ end
223
+
224
+ def visit(node)
225
+ emitter(node).write_to_buffer
226
+ end
227
+
228
+ def visit_deep(node)
229
+ emitter(node).tap do |emitter|
230
+ emitter.write_to_buffer
231
+ emitter.emit_heredoc_reminders
232
+ end
233
+ end
234
+
235
+ def first_child
236
+ children.first
237
+ end
238
+
239
+ def conditional_parentheses(flag, &block)
240
+ if flag
241
+ parentheses(&block)
242
+ else
243
+ block.call
244
+ end
245
+ end
246
+
247
+ def children
248
+ node.children
249
+ end
250
+ end # Generation
251
+ # rubocop:enable Metrics/ModuleLength
252
+ end # Unparser
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module NodeDetails
5
+ class Send
6
+ include NodeDetails
7
+
8
+ ASSIGN_SUFFIX = '='.freeze
9
+ NON_ASSIGN_RANGE = (0..-2).freeze
10
+
11
+ private_constant(*constants(false))
12
+
13
+ children :receiver, :selector
14
+
15
+ public :receiver, :selector
16
+
17
+ def selector_binary_operator?
18
+ BINARY_OPERATORS.include?(selector)
19
+ end
20
+
21
+ def binary_syntax_allowed?
22
+ selector_binary_operator? \
23
+ && arguments.one? \
24
+ && !n_splat?(arguments.first) \
25
+ && !n_kwargs?(arguments.first)
26
+ end
27
+
28
+ def selector_unary_operator?
29
+ UNARY_OPERATORS.include?(selector)
30
+ end
31
+
32
+ def assignment_operator?
33
+ assignment? && !selector_binary_operator? && !selector_unary_operator?
34
+ end
35
+
36
+ def arguments?
37
+ arguments.any?
38
+ end
39
+
40
+ def non_assignment_selector
41
+ if assignment?
42
+ string_selector[NON_ASSIGN_RANGE]
43
+ else
44
+ string_selector
45
+ end
46
+ end
47
+
48
+ def assignment?
49
+ string_selector[-1].eql?(ASSIGN_SUFFIX)
50
+ end
51
+ memoize :assignment?
52
+
53
+ def arguments
54
+ children[2..]
55
+ end
56
+ memoize :arguments
57
+
58
+ def string_selector
59
+ selector.to_s
60
+ end
61
+ memoize :string_selector
62
+
63
+ end # Send
64
+ end # NodeDetails
65
+ end # Unparser
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module NodeDetails
5
+ include Constants, NodeHelpers
6
+
7
+ def self.included(descendant)
8
+ descendant.class_eval do
9
+ include Adamantium, Concord.new(:node)
10
+
11
+ extend DSL
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def children
18
+ node.children
19
+ end
20
+ end # NodeDetails
21
+ end # Unparser
@@ -11,9 +11,6 @@ module Unparser
11
11
  # @return [Parser::AST::Node]
12
12
  #
13
13
  # @api private
14
- #
15
- # ignore :reek:UncommunicativeMethodName
16
- # ignore :reek:UtilityFunction
17
14
  def s(type, *children)
18
15
  Parser::AST::Node.new(type, children)
19
16
  end
@@ -26,12 +23,57 @@ module Unparser
26
23
  # @param [Array] children
27
24
  #
28
25
  # @api private
29
- #
30
- # ignore :reek:UncommunicativeMethodName
31
- # ignore :reek:UtilityFunction
32
26
  def n(type, children = [])
33
27
  Parser::AST::Node.new(type, children)
34
28
  end
35
29
 
30
+ def n?(type, node)
31
+ node.type.equal?(type)
32
+ end
33
+
34
+ %i[
35
+ arg
36
+ args
37
+ array
38
+ array_pattern
39
+ begin
40
+ block
41
+ cbase
42
+ const
43
+ dstr
44
+ empty_else
45
+ ensure
46
+ hash
47
+ hash_pattern
48
+ if
49
+ in_pattern
50
+ int
51
+ kwarg
52
+ kwargs
53
+ kwsplat
54
+ lambda
55
+ match_rest
56
+ pair
57
+ rescue
58
+ send
59
+ shadowarg
60
+ splat
61
+ str
62
+ sym
63
+ ].each do |type|
64
+ name = "n_#{type}?"
65
+ define_method(name) do |node|
66
+ n?(type, node)
67
+ end
68
+ private(name)
69
+ end
70
+
71
+ def unwrap_single_begin(node)
72
+ if n_begin?(node) && node.children.one?
73
+ node.children.first
74
+ else
75
+ node
76
+ end
77
+ end
36
78
  end # NodeHelpers
37
79
  end # Unparser
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ # Validation of unparser results
5
+ class Validation
6
+ include Adamantium, Anima.new(
7
+ :generated_node,
8
+ :generated_source,
9
+ :identification,
10
+ :original_node,
11
+ :original_source
12
+ )
13
+
14
+ # Test if source could be unparsed successfully
15
+ #
16
+ # @return [Boolean]
17
+ #
18
+ # @api private
19
+ #
20
+ def success?
21
+ [
22
+ original_source,
23
+ original_node,
24
+ generated_source,
25
+ generated_node
26
+ ].all?(&:right?) && generated_node.from_right.==(original_node.from_right)
27
+ end
28
+
29
+ # Return error report
30
+ #
31
+ # @return [String]
32
+ #
33
+ # @api private
34
+ #
35
+ def report
36
+ message = [identification]
37
+
38
+ message.concat(make_report('Original-Source', :original_source))
39
+ message.concat(make_report('Generated-Source', :generated_source))
40
+ message.concat(make_report('Original-Node', :original_node))
41
+ message.concat(make_report('Generated-Node', :generated_node))
42
+ message.concat(node_diff_report)
43
+
44
+ message.join("\n")
45
+ end
46
+ memoize :report
47
+
48
+ # Create validator from string
49
+ #
50
+ # @param [String] original_source
51
+ #
52
+ # @return [Validator]
53
+ def self.from_string(original_source)
54
+ original_node = Unparser
55
+ .parse_either(original_source)
56
+
57
+ generated_source = original_node
58
+ .lmap(&method(:const_unit))
59
+ .bind(&Unparser.method(:unparse_either))
60
+
61
+ generated_node = generated_source
62
+ .lmap(&method(:const_unit))
63
+ .bind(&Unparser.method(:parse_either))
64
+
65
+ new(
66
+ identification: '(string)',
67
+ original_source: Either::Right.new(original_source),
68
+ original_node: original_node,
69
+ generated_source: generated_source,
70
+ generated_node: generated_node
71
+ )
72
+ end
73
+
74
+ # Create validator from node
75
+ #
76
+ # @param [Parser::AST::Node] original_node
77
+ #
78
+ # @return [Validator]
79
+ def self.from_node(original_node)
80
+ generated_source = Unparser.unparse_either(original_node)
81
+
82
+ generated_node = generated_source
83
+ .lmap(&method(:const_unit))
84
+ .bind(&Unparser.public_method(:parse_either))
85
+
86
+ new(
87
+ identification: '(string)',
88
+ original_source: generated_source,
89
+ original_node: Either::Right.new(original_node),
90
+ generated_source: generated_source,
91
+ generated_node: generated_node
92
+ )
93
+ end
94
+
95
+ # Create validator from file
96
+ #
97
+ # @param [Pathname] path
98
+ #
99
+ # @return [Validator]
100
+ def self.from_path(path)
101
+ from_string(path.read).with(identification: path.to_s)
102
+ end
103
+
104
+ private
105
+
106
+ def make_report(label, attribute_name)
107
+ ["#{label}:"].concat(public_send(attribute_name).either(method(:report_exception), ->(value) { [value] }))
108
+ end
109
+
110
+ def report_exception(exception)
111
+ if exception
112
+ [exception.inspect].concat(exception.backtrace.take(20))
113
+ else
114
+ ['undefined']
115
+ end
116
+ end
117
+
118
+ def node_diff_report
119
+ diff = nil
120
+
121
+ original_node.fmap do |original|
122
+ generated_node.fmap do |generated|
123
+ diff = Diff.new(
124
+ original.to_s.lines.map(&:chomp),
125
+ generated.to_s.lines.map(&:chomp)
126
+ ).colorized_diff
127
+ end
128
+ end
129
+
130
+ diff ? ['Node-Diff:', diff] : []
131
+ end
132
+
133
+ def self.const_unit(_value); end
134
+ private_class_method :const_unit
135
+
136
+ class Literal < self
137
+ def success?
138
+ original_source.eql?(generated_source)
139
+ end
140
+
141
+ def report
142
+ message = [identification]
143
+
144
+ message.concat(make_report('Original-Source', :original_source))
145
+ message.concat(make_report('Generated-Source', :generated_source))
146
+ message.concat(make_report('Original-Node', :original_node))
147
+ message.concat(make_report('Generated-Node', :generated_node))
148
+ message.concat(node_diff_report)
149
+ message.concat(source_diff_report)
150
+
151
+ message.join("\n")
152
+ end
153
+
154
+ private
155
+
156
+ def source_diff_report
157
+ diff = nil
158
+
159
+ original_source.fmap do |original|
160
+ generated_source.fmap do |generated|
161
+ diff = Diff.new(
162
+ original.split("\n", -1),
163
+ generated.split("\n", -1)
164
+ ).colorized_diff
165
+ end
166
+ end
167
+
168
+ diff ? ['Source-Diff:', diff] : []
169
+ end
170
+ end # Literal
171
+ end # Validation
172
+ end # Unparser
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ module Writer
5
+ class Binary
6
+ include Writer, Adamantium
7
+
8
+ children :left, :right
9
+
10
+ OPERATOR_TOKENS =
11
+ {
12
+ and: '&&',
13
+ or: '||'
14
+ }.freeze
15
+
16
+ KEYWORD_TOKENS =
17
+ {
18
+ and: 'and',
19
+ or: 'or'
20
+ }.freeze
21
+
22
+ KEYWORD_SYMBOLS =
23
+ {
24
+ and: :kAND,
25
+ or: :kOR
26
+ }.freeze
27
+
28
+ OPERATOR_SYMBOLS =
29
+ {
30
+ and: :tANDOP,
31
+ or: :tOROP
32
+ }.freeze
33
+
34
+ MAP =
35
+ {
36
+ kAND: 'and',
37
+ kOR: 'or',
38
+ tOROP: '||',
39
+ tANDOP: '&&'
40
+ }.freeze
41
+
42
+ NEED_KEYWORD = %i[return break next].freeze
43
+
44
+ private_constant(*constants(false))
45
+
46
+ def emit_operator
47
+ emit_with(OPERATOR_TOKENS)
48
+ end
49
+
50
+ def symbol_name
51
+ true
52
+ end
53
+
54
+ def dispatch
55
+ left_emitter.write_to_buffer
56
+ write(' ', MAP.fetch(effective_symbol), ' ')
57
+ visit(right)
58
+ end
59
+
60
+ private
61
+
62
+ def effective_symbol
63
+ if NEED_KEYWORD.include?(right.type) || NEED_KEYWORD.include?(left.type)
64
+ return keyword_symbol
65
+ end
66
+
67
+ unless left_emitter.symbol_name
68
+ return operator_symbol
69
+ end
70
+
71
+ keyword_symbol
72
+ end
73
+
74
+ def emit_with(map)
75
+ visit(left)
76
+ write(' ', map.fetch(node.type), ' ')
77
+ visit(right)
78
+ end
79
+
80
+ def keyword_symbol
81
+ KEYWORD_SYMBOLS.fetch(node.type)
82
+ end
83
+
84
+ def operator_symbol
85
+ OPERATOR_SYMBOLS.fetch(node.type)
86
+ end
87
+
88
+ def left_emitter
89
+ emitter(left)
90
+ end
91
+ memoize :left_emitter
92
+
93
+ def right_emitter
94
+ emitter(right)
95
+ end
96
+ memoize :right_emitter
97
+ end # Binary
98
+ end # Writer
99
+ end # Unparser