unparser 0.6.15 → 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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/lib/unparser/anima.rb +11 -0
  4. data/lib/unparser/ast/local_variable_scope.rb +28 -24
  5. data/lib/unparser/ast.rb +18 -22
  6. data/lib/unparser/buffer.rb +44 -2
  7. data/lib/unparser/cli.rb +28 -5
  8. data/lib/unparser/color.rb +2 -2
  9. data/lib/unparser/either.rb +6 -6
  10. data/lib/unparser/emitter/args.rb +5 -1
  11. data/lib/unparser/emitter/array.rb +0 -4
  12. data/lib/unparser/emitter/array_pattern.rb +1 -9
  13. data/lib/unparser/emitter/assignment.rb +17 -8
  14. data/lib/unparser/emitter/begin.rb +0 -6
  15. data/lib/unparser/emitter/binary.rb +1 -1
  16. data/lib/unparser/emitter/block.rb +13 -6
  17. data/lib/unparser/emitter/def.rb +1 -1
  18. data/lib/unparser/emitter/dstr.rb +6 -5
  19. data/lib/unparser/emitter/dsym.rb +1 -1
  20. data/lib/unparser/emitter/ensure.rb +16 -0
  21. data/lib/unparser/emitter/flow_modifier.rb +1 -7
  22. data/lib/unparser/emitter/for.rb +1 -1
  23. data/lib/unparser/emitter/hash.rb +0 -8
  24. data/lib/unparser/emitter/hash_pattern.rb +1 -1
  25. data/lib/unparser/emitter/in_pattern.rb +9 -1
  26. data/lib/unparser/emitter/index.rb +0 -4
  27. data/lib/unparser/emitter/kwbegin.rb +1 -1
  28. data/lib/unparser/emitter/match_pattern.rb +6 -1
  29. data/lib/unparser/emitter/match_pattern_p.rb +6 -1
  30. data/lib/unparser/emitter/mlhs.rb +7 -1
  31. data/lib/unparser/emitter/op_assign.rb +0 -10
  32. data/lib/unparser/emitter/primitive.rb +0 -13
  33. data/lib/unparser/emitter/range.rb +23 -2
  34. data/lib/unparser/emitter/regexp.rb +5 -17
  35. data/lib/unparser/emitter/rescue.rb +7 -1
  36. data/lib/unparser/emitter/root.rb +2 -9
  37. data/lib/unparser/emitter/send.rb +1 -5
  38. data/lib/unparser/emitter/string.rb +31 -0
  39. data/lib/unparser/emitter/xstr.rb +8 -1
  40. data/lib/unparser/emitter.rb +9 -10
  41. data/lib/unparser/generation.rb +14 -14
  42. data/lib/unparser/node_details.rb +1 -0
  43. data/lib/unparser/node_helpers.rb +18 -9
  44. data/lib/unparser/util.rb +23 -0
  45. data/lib/unparser/validation.rb +68 -28
  46. data/lib/unparser/writer/array.rb +51 -0
  47. data/lib/unparser/writer/binary.rb +8 -4
  48. data/lib/unparser/writer/dynamic_string.rb +128 -135
  49. data/lib/unparser/writer/regexp.rb +101 -0
  50. data/lib/unparser/writer/resbody.rb +37 -3
  51. data/lib/unparser/writer/rescue.rb +3 -7
  52. data/lib/unparser/writer/send/unary.rb +9 -4
  53. data/lib/unparser/writer/send.rb +8 -14
  54. data/lib/unparser/writer.rb +31 -1
  55. data/lib/unparser.rb +149 -38
  56. metadata +33 -17
@@ -7,15 +7,16 @@ module Unparser
7
7
 
8
8
  handle :dstr
9
9
 
10
- def emit_heredoc_reminders
11
- writer_with(Writer::DynamicString, node).emit_heredoc_reminder
12
- end
13
-
14
10
  private
15
11
 
16
12
  def dispatch
17
- writer_with(Writer::DynamicString, node).dispatch
13
+ dstr_writer.dispatch
14
+ end
15
+
16
+ def dstr_writer
17
+ writer_with(Writer::DynamicString, node:)
18
18
  end
19
+ memoize :dstr_writer
19
20
 
20
21
  end # DStr
21
22
  end # Emitter
@@ -33,7 +33,7 @@ module Unparser
33
33
 
34
34
  def emit_begin_child(component)
35
35
  write('#{')
36
- visit(unwrap_single_begin(component))
36
+ visit(Util.one(component.children)) if component.children.any?
37
37
  write('}')
38
38
  end
39
39
  end # DSym
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ class Emitter
5
+ # Emitter for ensure nodes
6
+ class Ensure < self
7
+ handle :ensure
8
+
9
+ private
10
+
11
+ def dispatch
12
+ emit_ensure(node)
13
+ end
14
+ end # Ensure
15
+ end # Emitter
16
+ end # Unparser
@@ -14,12 +14,6 @@ module Unparser
14
14
 
15
15
  handle(*MAP.keys)
16
16
 
17
- def emit_heredoc_reminders
18
- children.each do |node|
19
- emitter(node).emit_heredoc_reminders
20
- end
21
- end
22
-
23
17
  private
24
18
 
25
19
  def dispatch
@@ -27,7 +21,7 @@ module Unparser
27
21
 
28
22
  if children.one? && n_if?(children.first)
29
23
  ws
30
- emitter(children.first).emit_ternary
24
+ emitter(Util.one(children)).emit_ternary
31
25
  else
32
26
  emit_arguments unless children.empty?
33
27
  end
@@ -18,7 +18,7 @@ module Unparser
18
18
  end
19
19
 
20
20
  def emit_condition
21
- visit(condition)
21
+ emitter(condition).emit_mlhs
22
22
  write(' in ')
23
23
  visit(assignment)
24
24
  write(' do')
@@ -6,10 +6,6 @@ module Unparser
6
6
  class Hash < self
7
7
  handle :hash
8
8
 
9
- def emit_heredoc_reminders
10
- children.each(&method(:emit_heredoc_reminder_member))
11
- end
12
-
13
9
  private
14
10
 
15
11
  def dispatch
@@ -24,10 +20,6 @@ module Unparser
24
20
  end
25
21
  end
26
22
 
27
- def emit_heredoc_reminder_member(node)
28
- emitter(node.children.last).emit_heredoc_reminders if n_pair?(node)
29
- end
30
-
31
23
  def emit_hash_body
32
24
  delimited(children)
33
25
  end
@@ -32,7 +32,7 @@ module Unparser
32
32
  when :match_var
33
33
  emit_match_var(node)
34
34
  when :match_rest
35
- writer_with(MatchRest, node).emit_hash_pattern
35
+ writer_with(MatchRest, node:).emit_hash_pattern
36
36
  else
37
37
  visit(node)
38
38
  end
@@ -16,7 +16,7 @@ module Unparser
16
16
 
17
17
  ws
18
18
 
19
- visit(target)
19
+ dispatch_target(target)
20
20
 
21
21
  if unless_guard
22
22
  ws
@@ -31,6 +31,14 @@ module Unparser
31
31
  nl
32
32
  end
33
33
  end
34
+
35
+ def dispatch_target(target)
36
+ if n_array?(target)
37
+ writer_with(Writer::Array, node: target).emit_compact
38
+ else
39
+ visit(target)
40
+ end
41
+ end
34
42
  end # InPattern
35
43
  end # Emitter
36
44
  end # Unparser
@@ -40,10 +40,6 @@ module Unparser
40
40
 
41
41
  private_constant(*constants(false))
42
42
 
43
- def emit_heredoc_reminders
44
- emitter(children.last).emit_heredoc_reminders
45
- end
46
-
47
43
  def dispatch
48
44
  emit_receiver
49
45
  emit_operation(children[VALUE_RANGE])
@@ -12,7 +12,7 @@ module Unparser
12
12
  write('begin')
13
13
 
14
14
  if children.one?
15
- emit_body_ensure_rescue(children.first)
15
+ emit_body_ensure_rescue(Util.one(children))
16
16
  else
17
17
  indented do
18
18
  emit_multiple_body
@@ -14,7 +14,12 @@ module Unparser
14
14
  def dispatch
15
15
  visit(target)
16
16
  write(' => ')
17
- visit(pattern)
17
+
18
+ if n_array?(pattern)
19
+ writer_with(Writer::Array, node: pattern).emit_compact
20
+ else
21
+ visit(pattern)
22
+ end
18
23
  end
19
24
  end # MatchPattern
20
25
  end # Emitter
@@ -13,7 +13,12 @@ module Unparser
13
13
  def dispatch
14
14
  visit(target)
15
15
  write(' in ')
16
- visit(pattern)
16
+
17
+ if n_array?(pattern)
18
+ writer_with(Writer::Array, node: pattern).emit_compact
19
+ else
20
+ visit(pattern)
21
+ end
17
22
  end
18
23
  end # MatchPatternP
19
24
  end # Emitter
@@ -6,10 +6,16 @@ module Unparser
6
6
  class MLHS < self
7
7
  handle :mlhs
8
8
 
9
- NO_COMMA = %i[arg splat mlhs restarg].freeze
9
+ NO_COMMA = %i[arg splat restarg].freeze
10
10
 
11
11
  private_constant(*constants(false))
12
12
 
13
+ def dispatch_def
14
+ parentheses do
15
+ delimited(children)
16
+ end
17
+ end
18
+
13
19
  private
14
20
 
15
21
  def dispatch
@@ -14,11 +14,6 @@ module Unparser
14
14
 
15
15
  handle(*MAP.keys)
16
16
 
17
- def emit_heredoc_reminders
18
- emitter(target).emit_heredoc_reminders
19
- emitter(expression).emit_heredoc_reminders
20
- end
21
-
22
17
  private
23
18
 
24
19
  def dispatch
@@ -35,11 +30,6 @@ module Unparser
35
30
 
36
31
  children :target, :operator, :value
37
32
 
38
- def emit_heredoc_reminders
39
- emitter(target).emit_heredoc_reminders
40
- emitter(value).emit_heredoc_reminders
41
- end
42
-
43
33
  private
44
34
 
45
35
  def dispatch
@@ -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
@@ -25,9 +25,30 @@ module Unparser
25
25
  private
26
26
 
27
27
  def dispatch
28
- visit(begin_node) if begin_node
28
+ visit_begin_node(begin_node)
29
29
  write(TOKENS.fetch(node.type))
30
- visit(end_node) if end_node
30
+ visit_end_node(end_node)
31
+ end
32
+
33
+ def visit_begin_node(node)
34
+ return unless node
35
+
36
+ if n_array?(begin_node)
37
+ writer_with(Writer::Array, node: begin_node).emit_compact
38
+ else
39
+ visit(begin_node)
40
+ end
41
+ end
42
+
43
+ def visit_end_node(node)
44
+ return unless node
45
+
46
+ write(' ') if n_range?(node)
47
+ if n_array?(node)
48
+ writer_with(Writer::Array, node: node).emit_compact
49
+ else
50
+ visit(node)
51
+ end
31
52
  end
32
53
 
33
54
  end # Range
@@ -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
@@ -41,6 +41,8 @@ module Unparser
41
41
  children.each do |child|
42
42
  if n_begin?(child)
43
43
  emit_begin(child)
44
+ elsif n_gvar?(child)
45
+ emit_gvar(child)
44
46
  else
45
47
  emit_string(child)
46
48
  end
@@ -64,9 +66,14 @@ module Unparser
64
66
 
65
67
  def emit_begin(component)
66
68
  write('#{')
67
- visit(unwrap_single_begin(component))
69
+ visit(Util.one(component.children)) if component.children.any?
68
70
  write('}')
69
71
  end
72
+
73
+ def emit_gvar(component)
74
+ write('#')
75
+ write(Util.one(component.children).to_s)
76
+ end
70
77
  end # XStr
71
78
  end # Emitter
72
79
  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
@@ -149,16 +147,22 @@ module Unparser
149
147
  def emit_body_inner(node)
150
148
  head, *tail = node.children
151
149
  emit_body_member(head)
150
+ write(';') if requires_explicit_statement_terminator?(head, tail)
152
151
 
153
152
  tail.each do |child|
154
- nl
153
+ buffer.ensure_nl
155
154
 
156
155
  nl if EXTRA_NL.include?(child.type)
157
156
 
158
157
  emit_body_member(child)
158
+ write(';') if requires_explicit_statement_terminator?(child, tail)
159
159
  end
160
160
  end
161
161
 
162
+ def requires_explicit_statement_terminator?(node, nodes_group)
163
+ n_range?(node) && node.children.fetch(1).nil? && !node.eql?(nodes_group.fetch(-1))
164
+ end
165
+
162
166
  def emit_body_member(node)
163
167
  if n_rescue?(node)
164
168
  emit_rescue_postcontrol(node)
@@ -208,21 +212,20 @@ module Unparser
208
212
  end
209
213
 
210
214
  def emit_rescue_postcontrol(node)
211
- writer = writer_with(Writer::Rescue, node)
215
+ writer = writer_with(Writer::Rescue, node:)
212
216
  writer.emit_postcontrol
213
- writer.emit_heredoc_reminders
214
217
  end
215
218
 
216
219
  def emit_rescue_regular(node)
217
- writer_with(Writer::Rescue, node).emit_regular
220
+ writer_with(Writer::Rescue, node:).emit_regular
218
221
  end
219
222
 
220
- def writer_with(klass, node)
221
- klass.new(to_h.merge(node: node))
223
+ def emitter(node)
224
+ Emitter.emitter(**to_h, node: node)
222
225
  end
223
226
 
224
- def emitter(node)
225
- Emitter.emitter(**to_h.merge(node: node))
227
+ def writer_with(klass, node:, **attributes)
228
+ klass.new(to_h.merge(node: node, **attributes))
226
229
  end
227
230
 
228
231
  def visit(node)
@@ -230,10 +233,7 @@ module Unparser
230
233
  end
231
234
 
232
235
  def visit_deep(node)
233
- emitter(node).tap do |emitter|
234
- emitter.write_to_buffer
235
- emitter.emit_heredoc_reminders
236
- end
236
+ emitter(node).tap(&:write_to_buffer)
237
237
  end
238
238
 
239
239
  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)
@@ -31,7 +31,16 @@ module Unparser
31
31
  node.type.equal?(type)
32
32
  end
33
33
 
34
+ def n_flipflop?(node)
35
+ n_iflipflop?(node) || n_eflipflop?(node)
36
+ end
37
+
38
+ def n_range?(node)
39
+ n_irange?(node) || n_erange?(node)
40
+ end
41
+
34
42
  %i[
43
+ and
35
44
  arg
36
45
  args
37
46
  array
@@ -41,19 +50,26 @@ module Unparser
41
50
  cbase
42
51
  const
43
52
  dstr
53
+ eflipflop
44
54
  empty_else
55
+ erange
45
56
  ensure
57
+ gvar
46
58
  hash
47
59
  hash_pattern
48
60
  if
61
+ iflipflop
49
62
  in_pattern
50
63
  int
64
+ irange
51
65
  kwarg
52
66
  kwargs
53
67
  kwsplat
54
68
  lambda
55
69
  lvar
56
70
  match_rest
71
+ mlhs
72
+ or
57
73
  pair
58
74
  rescue
59
75
  send
@@ -61,20 +77,13 @@ module Unparser
61
77
  splat
62
78
  str
63
79
  sym
64
- ].each do |type|
80
+ xstr
81
+ ].to_set.each do |type|
65
82
  name = "n_#{type}?"
66
83
  define_method(name) do |node|
67
84
  n?(type, node)
68
85
  end
69
86
  private(name)
70
87
  end
71
-
72
- def unwrap_single_begin(node)
73
- if n_begin?(node) && node.children.one?
74
- node.children.first
75
- else
76
- node
77
- end
78
- end
79
88
  end # NodeHelpers
80
89
  end # Unparser
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unparser
4
+ # Original code before vendoring and reduction from: https://github.com/mbj/mutant/blob/main/lib/mutant/util.rb
5
+ module Util
6
+ # Error raised by `Util.one` if size is not exactly one
7
+ SizeError = Class.new(IndexError)
8
+
9
+ # Return only element in array if it contains exactly one member
10
+ #
11
+ # @param array [Array]
12
+ #
13
+ # @return [Object] first entry
14
+ def self.one(array)
15
+ case array
16
+ in [value]
17
+ value
18
+ else
19
+ fail SizeError, "expected size to be exactly 1 but size was #{array.size}"
20
+ end
21
+ end
22
+ end
23
+ end