unparser 0.6.15 → 0.7.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/unparser/anima.rb +11 -0
  3. data/lib/unparser/ast/local_variable_scope.rb +28 -24
  4. data/lib/unparser/ast.rb +18 -22
  5. data/lib/unparser/buffer.rb +44 -2
  6. data/lib/unparser/cli.rb +26 -5
  7. data/lib/unparser/either.rb +6 -6
  8. data/lib/unparser/emitter/array.rb +0 -4
  9. data/lib/unparser/emitter/array_pattern.rb +1 -9
  10. data/lib/unparser/emitter/assignment.rb +7 -8
  11. data/lib/unparser/emitter/begin.rb +0 -6
  12. data/lib/unparser/emitter/binary.rb +1 -1
  13. data/lib/unparser/emitter/block.rb +3 -4
  14. data/lib/unparser/emitter/def.rb +1 -1
  15. data/lib/unparser/emitter/dstr.rb +6 -5
  16. data/lib/unparser/emitter/flow_modifier.rb +0 -6
  17. data/lib/unparser/emitter/for.rb +1 -1
  18. data/lib/unparser/emitter/hash.rb +0 -8
  19. data/lib/unparser/emitter/hash_pattern.rb +1 -1
  20. data/lib/unparser/emitter/index.rb +0 -4
  21. data/lib/unparser/emitter/op_assign.rb +0 -10
  22. data/lib/unparser/emitter/primitive.rb +0 -13
  23. data/lib/unparser/emitter/regexp.rb +5 -17
  24. data/lib/unparser/emitter/rescue.rb +7 -1
  25. data/lib/unparser/emitter/root.rb +2 -9
  26. data/lib/unparser/emitter/send.rb +1 -5
  27. data/lib/unparser/emitter/string.rb +31 -0
  28. data/lib/unparser/emitter.rb +9 -10
  29. data/lib/unparser/generation.rb +8 -14
  30. data/lib/unparser/node_details.rb +1 -0
  31. data/lib/unparser/validation.rb +68 -28
  32. data/lib/unparser/writer/dynamic_string.rb +128 -135
  33. data/lib/unparser/writer/regexp.rb +98 -0
  34. data/lib/unparser/writer/resbody.rb +30 -1
  35. data/lib/unparser/writer/rescue.rb +2 -6
  36. data/lib/unparser/writer/send.rb +8 -14
  37. data/lib/unparser/writer.rb +32 -1
  38. data/lib/unparser.rb +103 -30
  39. metadata +15 -13
@@ -7,19 +7,6 @@ module Unparser
7
7
 
8
8
  children :value
9
9
 
10
- # Emitter for primitives based on Object#inspect
11
- class Inspect < self
12
-
13
- handle :str
14
-
15
- private
16
-
17
- def dispatch
18
- write(value.inspect)
19
- end
20
-
21
- end # Inspect
22
-
23
10
  class Symbol < self
24
11
 
25
12
  handle :sym
@@ -4,32 +4,20 @@ module Unparser
4
4
  class Emitter
5
5
  # Emitter for regexp literals
6
6
  class Regexp < self
7
- handle :regexp
8
7
 
9
- define_group(:body, 0..-2)
8
+ handle :regexp
10
9
 
11
10
  private
12
11
 
13
12
  def dispatch
14
- parentheses('/', '/') do
15
- body.each(&method(:emit_body))
16
- end
17
- emit_options
13
+ writer.dispatch
18
14
  end
19
15
 
20
- def emit_options
21
- write(children.last.children.join)
16
+ def writer
17
+ writer_with(Writer::Regexp, node:)
22
18
  end
19
+ memoize :writer
23
20
 
24
- def emit_body(node)
25
- if n_begin?(node)
26
- write('#{')
27
- node.children.each(&method(:visit))
28
- write('}')
29
- else
30
- buffer.append_without_prefix(node.children.first.gsub('/', '\/'))
31
- end
32
- end
33
21
  end # Regexp
34
22
  end # Emitter
35
23
  end # Unparser
@@ -9,7 +9,13 @@ module Unparser
9
9
  private
10
10
 
11
11
  def dispatch
12
- emit_rescue_postcontrol(node)
12
+ resbody = children.fetch(1)
13
+
14
+ if resbody.children.fetch(1)
15
+ emit_rescue_regular(node)
16
+ else
17
+ emit_rescue_postcontrol(node)
18
+ end
13
19
  end
14
20
  end # Rescue
15
21
  end # Emitter
@@ -2,22 +2,15 @@
2
2
 
3
3
  module Unparser
4
4
  class Emitter
5
- # Root emitter a special case
6
5
  class Root < self
7
- include Concord::Public.new(:buffer, :node, :comments)
8
- include LocalVariableRoot
9
-
10
6
  END_NL = %i[class sclass module begin].freeze
11
7
 
12
8
  private_constant(*constants(false))
13
9
 
14
10
  def dispatch
15
- if children.any?
16
- emit_body(node, indent: false)
17
- else
18
- visit_deep(node)
19
- end
11
+ emit_body(node, indent: false)
20
12
 
13
+ buffer.nl_flush_heredocs
21
14
  emit_eof_comments
22
15
 
23
16
  nl if END_NL.include?(node.type) && !buffer.fresh_line?
@@ -10,10 +10,6 @@ module Unparser
10
10
  writer.emit_mlhs
11
11
  end
12
12
 
13
- def emit_heredoc_reminders
14
- writer.emit_heredoc_reminders
15
- end
16
-
17
13
  private
18
14
 
19
15
  def dispatch
@@ -21,7 +17,7 @@ module Unparser
21
17
  end
22
18
 
23
19
  def writer
24
- writer_with(Writer::Send, node)
20
+ writer_with(Writer::Send, node:)
25
21
  end
26
22
  memoize :writer
27
23
  end # Send
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ class Emitter
5
+ # Base class for primitive emitters
6
+ class String < self
7
+ children :value
8
+
9
+ handle :str
10
+
11
+ private
12
+
13
+ def dispatch
14
+ if explicit_encoding && !value.encoding.equal?(explicit_encoding)
15
+ write_utf8_escaped
16
+ else
17
+ write(value.inspect)
18
+ end
19
+ end
20
+
21
+ def write_utf8_escaped
22
+ write('"')
23
+ value.each_codepoint do |codepoint|
24
+ write("\\u{#{codepoint.to_s(16)}}")
25
+ end
26
+ write('"')
27
+ end
28
+
29
+ end # String
30
+ end # Emitter
31
+ end # Unparser
@@ -6,7 +6,7 @@ module Unparser
6
6
  # Emitter base class
7
7
  class Emitter
8
8
  include Adamantium, AbstractType, Constants, Generation, NodeHelpers
9
- include Anima.new(:buffer, :comments, :node, :local_variable_scope)
9
+ include Anima.new(:buffer, :comments, :explicit_encoding, :local_variable_scope, :node)
10
10
 
11
11
  public :node
12
12
 
@@ -22,10 +22,9 @@ module Unparser
22
22
  #
23
23
  # @return [Parser::AST::Node]
24
24
  #
25
- # @api private
26
- #
25
+ # mutant:disable
27
26
  def local_variable_scope
28
- AST::LocalVariableScope.new(node)
27
+ AST::LocalVariableScope.new(node: node, static_local_variables: Set.new)
29
28
  end
30
29
 
31
30
  def self.included(descendant)
@@ -67,7 +66,7 @@ module Unparser
67
66
  # @api private
68
67
  #
69
68
  # rubocop:disable Metrics/ParameterLists
70
- def self.emitter(buffer:, comments:, node:, local_variable_scope:)
69
+ def self.emitter(buffer:, explicit_encoding:, comments:, node:, local_variable_scope:)
71
70
  type = node.type
72
71
 
73
72
  klass = REGISTRY.fetch(type) do
@@ -75,10 +74,11 @@ module Unparser
75
74
  end
76
75
 
77
76
  klass.new(
78
- buffer: buffer,
79
- comments: comments,
80
- local_variable_scope: local_variable_scope,
81
- node: node
77
+ buffer:,
78
+ comments:,
79
+ explicit_encoding:,
80
+ local_variable_scope:,
81
+ node:
82
82
  )
83
83
  end
84
84
  # rubocop:enable Metrics/ParameterLists
@@ -90,6 +90,5 @@ module Unparser
90
90
  # @api private
91
91
  #
92
92
  abstract_method :dispatch
93
-
94
93
  end # Emitter
95
94
  end # Unparser
@@ -7,8 +7,6 @@ module Unparser
7
7
 
8
8
  private_constant(*constants(false))
9
9
 
10
- def emit_heredoc_reminders; end
11
-
12
10
  def symbol_name; end
13
11
 
14
12
  def write_to_buffer
@@ -151,7 +149,7 @@ module Unparser
151
149
  emit_body_member(head)
152
150
 
153
151
  tail.each do |child|
154
- nl
152
+ buffer.ensure_nl
155
153
 
156
154
  nl if EXTRA_NL.include?(child.type)
157
155
 
@@ -208,21 +206,20 @@ module Unparser
208
206
  end
209
207
 
210
208
  def emit_rescue_postcontrol(node)
211
- writer = writer_with(Writer::Rescue, node)
209
+ writer = writer_with(Writer::Rescue, node:)
212
210
  writer.emit_postcontrol
213
- writer.emit_heredoc_reminders
214
211
  end
215
212
 
216
213
  def emit_rescue_regular(node)
217
- writer_with(Writer::Rescue, node).emit_regular
214
+ writer_with(Writer::Rescue, node:).emit_regular
218
215
  end
219
216
 
220
- def writer_with(klass, node)
221
- klass.new(to_h.merge(node: node))
217
+ def emitter(node)
218
+ Emitter.emitter(**to_h, node: node)
222
219
  end
223
220
 
224
- def emitter(node)
225
- Emitter.emitter(**to_h.merge(node: node))
221
+ def writer_with(klass, node:, **attributes)
222
+ klass.new(to_h.merge(node: node, **attributes))
226
223
  end
227
224
 
228
225
  def visit(node)
@@ -230,10 +227,7 @@ module Unparser
230
227
  end
231
228
 
232
229
  def visit_deep(node)
233
- emitter(node).tap do |emitter|
234
- emitter.write_to_buffer
235
- emitter.emit_heredoc_reminders
236
- end
230
+ emitter(node).tap(&:write_to_buffer)
237
231
  end
238
232
 
239
233
  def first_child
@@ -4,6 +4,7 @@ module Unparser
4
4
  module NodeDetails
5
5
  include Constants, NodeHelpers
6
6
 
7
+ # mutant:disable
7
8
  def self.included(descendant)
8
9
  descendant.class_eval do
9
10
  include Adamantium, Concord.new(:node)
@@ -7,10 +7,14 @@ module Unparser
7
7
  :generated_node,
8
8
  :generated_source,
9
9
  :identification,
10
- :original_node,
10
+ :original_ast,
11
11
  :original_source
12
12
  )
13
13
 
14
+ class PhaseException
15
+ include Anima.new(:exception, :phase)
16
+ end
17
+
14
18
  # Test if source could be unparsed successfully
15
19
  #
16
20
  # @return [Boolean]
@@ -18,10 +22,11 @@ module Unparser
18
22
  # @api private
19
23
  #
20
24
  # rubocop:disable Style/OperatorMethodCall
25
+ # mutant:disable
21
26
  def success?
22
27
  [
23
28
  original_source,
24
- original_node,
29
+ original_ast,
25
30
  generated_source,
26
31
  generated_node
27
32
  ].all?(&:right?) && generated_node.from_right.==(original_node.from_right)
@@ -33,7 +38,7 @@ module Unparser
33
38
  # @return [String]
34
39
  #
35
40
  # @api private
36
- #
41
+ # mutant:disable
37
42
  def report
38
43
  message = [identification]
39
44
 
@@ -47,48 +52,57 @@ module Unparser
47
52
  end
48
53
  memoize :report
49
54
 
55
+ # mutant:disable
56
+ def original_node
57
+ original_ast.fmap(&:node)
58
+ end
59
+
50
60
  # Create validator from string
51
61
  #
52
62
  # @param [String] original_source
53
63
  #
54
64
  # @return [Validator]
65
+ # mutant:disable
55
66
  def self.from_string(original_source)
56
- original_node = Unparser
57
- .parse_either(original_source)
67
+ original_ast = parse_ast_either(original_source)
58
68
 
59
- generated_source = original_node
69
+ generated_source = original_ast
60
70
  .lmap(&method(:const_unit))
61
- .bind(&Unparser.method(:unparse_either))
71
+ .bind(&method(:unparse_ast_either))
62
72
 
63
73
  generated_node = generated_source
64
74
  .lmap(&method(:const_unit))
65
- .bind(&Unparser.method(:parse_either))
75
+ .bind(&method(:parse_ast_either))
76
+ .fmap(&:node)
66
77
 
67
78
  new(
68
- identification: '(string)',
69
- original_source: Either::Right.new(original_source),
70
- original_node: original_node,
79
+ generated_node: generated_node,
71
80
  generated_source: generated_source,
72
- generated_node: generated_node
81
+ identification: '(string)',
82
+ original_ast: original_ast,
83
+ original_source: Either::Right.new(original_source)
73
84
  )
74
85
  end
75
86
 
76
- # Create validator from node
87
+ # Create validator from ast
77
88
  #
78
- # @param [Parser::AST::Node] original_node
89
+ # @param [Unparser::AST] ast
79
90
  #
80
91
  # @return [Validator]
81
- def self.from_node(original_node)
82
- generated_source = Unparser.unparse_either(original_node)
92
+ #
93
+ # mutant:disable
94
+ def self.from_ast(ast:)
95
+ generated_source = Unparser.unparse_ast_either(ast)
83
96
 
84
97
  generated_node = generated_source
85
98
  .lmap(&method(:const_unit))
86
- .bind(&Unparser.public_method(:parse_either))
99
+ .bind(&method(:parse_ast_either))
100
+ .fmap(&:node)
87
101
 
88
102
  new(
89
103
  identification: '(string)',
90
104
  original_source: generated_source,
91
- original_node: Either::Right.new(original_node),
105
+ original_ast: Either::Right.new(ast),
92
106
  generated_source: generated_source,
93
107
  generated_node: generated_node
94
108
  )
@@ -99,24 +113,45 @@ module Unparser
99
113
  # @param [Pathname] path
100
114
  #
101
115
  # @return [Validator]
116
+ #
117
+ # mutant:disable
102
118
  def self.from_path(path)
103
- from_string(path.read).with(identification: path.to_s)
119
+ from_string(path.read.freeze).with(identification: path.to_s)
104
120
  end
105
121
 
122
+ # mutant:disable
123
+ def self.unparse_ast_either(ast)
124
+ Unparser.unparse_ast_either(ast)
125
+ end
126
+ private_class_method :unparse_ast_either
127
+
128
+ # mutant:disable
129
+ def self.parse_ast_either(source)
130
+ Unparser.parse_ast_either(source)
131
+ end
132
+ private_class_method :parse_ast_either
133
+
134
+ # mutant:disable
135
+ def self.const_unit(_); end
136
+ private_class_method :const_unit
137
+
106
138
  private
107
139
 
140
+ # mutant:disable
108
141
  def make_report(label, attribute_name)
109
142
  ["#{label}:"].concat(public_send(attribute_name).either(method(:report_exception), ->(value) { [value] }))
110
143
  end
111
144
 
112
- def report_exception(exception)
113
- if exception
114
- [exception.inspect].concat(exception.backtrace.take(20))
145
+ # mutant:disable
146
+ def report_exception(phase_exception)
147
+ if phase_exception
148
+ [phase_exception.inspect].concat(phase_exception.backtrace.take(20))
115
149
  else
116
- ['undefined']
150
+ %w[undefined]
117
151
  end
118
152
  end
119
153
 
154
+ # mutant:disable
120
155
  def node_diff_report
121
156
  diff = nil
122
157
 
@@ -132,14 +167,13 @@ module Unparser
132
167
  diff ? ['Node-Diff:', diff] : []
133
168
  end
134
169
 
135
- def self.const_unit(_value); end
136
- private_class_method :const_unit
137
-
138
170
  class Literal < self
171
+ # mutant:disable
139
172
  def success?
140
173
  original_source.eql?(generated_source)
141
174
  end
142
175
 
176
+ # mutant:disable
143
177
  def report
144
178
  message = [identification]
145
179
 
@@ -155,20 +189,26 @@ module Unparser
155
189
 
156
190
  private
157
191
 
192
+ # mutant:disable
158
193
  def source_diff_report
159
194
  diff = nil
160
195
 
161
196
  original_source.fmap do |original|
162
197
  generated_source.fmap do |generated|
163
198
  diff = Diff.new(
164
- original.split("\n", -1),
165
- generated.split("\n", -1)
199
+ encode(original).split("\n", -1),
200
+ encode(generated).split("\n", -1)
166
201
  ).colorized_diff
167
202
  end
168
203
  end
169
204
 
170
205
  diff ? ['Source-Diff:', diff] : []
171
206
  end
207
+
208
+ # mutant:disable
209
+ def encode(string)
210
+ string.encode('UTF-8', invalid: :replace, undef: :replace)
211
+ end
172
212
  end # Literal
173
213
  end # Validation
174
214
  end # Unparser