unparser 0.4.7 → 0.6.7

Sign up to get free protection for your applications and to get access to all the features.
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