unparser 0.6.5 → 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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -6
  3. data/bin/unparser +1 -1
  4. data/lib/unparser/adamantium.rb +3 -1
  5. data/lib/unparser/anima.rb +11 -0
  6. data/lib/unparser/ast/local_variable_scope.rb +28 -25
  7. data/lib/unparser/ast.rb +18 -22
  8. data/lib/unparser/buffer.rb +43 -15
  9. data/lib/unparser/cli.rb +30 -7
  10. data/lib/unparser/color.rb +5 -0
  11. data/lib/unparser/either.rb +6 -6
  12. data/lib/unparser/emitter/args.rb +5 -1
  13. data/lib/unparser/emitter/argument.rb +6 -4
  14. data/lib/unparser/emitter/array.rb +0 -4
  15. data/lib/unparser/emitter/array_pattern.rb +1 -9
  16. data/lib/unparser/emitter/assignment.rb +17 -8
  17. data/lib/unparser/emitter/begin.rb +0 -6
  18. data/lib/unparser/emitter/binary.rb +1 -1
  19. data/lib/unparser/emitter/block.rb +13 -6
  20. data/lib/unparser/emitter/def.rb +1 -1
  21. data/lib/unparser/emitter/dstr.rb +6 -5
  22. data/lib/unparser/emitter/dsym.rb +1 -1
  23. data/lib/unparser/emitter/ensure.rb +16 -0
  24. data/lib/unparser/emitter/flipflop.rb +7 -2
  25. data/lib/unparser/emitter/flow_modifier.rb +1 -7
  26. data/lib/unparser/emitter/for.rb +1 -1
  27. data/lib/unparser/emitter/hash.rb +0 -16
  28. data/lib/unparser/emitter/hash_pattern.rb +1 -1
  29. data/lib/unparser/emitter/in_pattern.rb +9 -1
  30. data/lib/unparser/emitter/index.rb +0 -4
  31. data/lib/unparser/emitter/kwbegin.rb +1 -1
  32. data/lib/unparser/emitter/match_pattern.rb +7 -11
  33. data/lib/unparser/emitter/match_pattern_p.rb +6 -1
  34. data/lib/unparser/emitter/mlhs.rb +7 -1
  35. data/lib/unparser/emitter/op_assign.rb +0 -5
  36. data/lib/unparser/emitter/pair.rb +31 -5
  37. data/lib/unparser/emitter/primitive.rb +19 -6
  38. data/lib/unparser/emitter/range.rb +23 -2
  39. data/lib/unparser/emitter/regexp.rb +5 -17
  40. data/lib/unparser/emitter/rescue.rb +7 -1
  41. data/lib/unparser/emitter/root.rb +2 -9
  42. data/lib/unparser/emitter/send.rb +1 -5
  43. data/lib/unparser/emitter/string.rb +31 -0
  44. data/lib/unparser/emitter/xstr.rb +8 -1
  45. data/lib/unparser/emitter.rb +9 -10
  46. data/lib/unparser/generation.rb +33 -29
  47. data/lib/unparser/node_details/send.rb +4 -3
  48. data/lib/unparser/node_details.rb +1 -0
  49. data/lib/unparser/node_helpers.rb +19 -9
  50. data/lib/unparser/util.rb +23 -0
  51. data/lib/unparser/validation.rb +70 -28
  52. data/lib/unparser/writer/array.rb +51 -0
  53. data/lib/unparser/writer/binary.rb +8 -4
  54. data/lib/unparser/writer/dynamic_string.rb +127 -146
  55. data/lib/unparser/writer/regexp.rb +101 -0
  56. data/lib/unparser/writer/resbody.rb +37 -3
  57. data/lib/unparser/writer/rescue.rb +3 -7
  58. data/lib/unparser/writer/send/unary.rb +9 -4
  59. data/lib/unparser/writer/send.rb +8 -14
  60. data/lib/unparser/writer.rb +31 -1
  61. data/lib/unparser.rb +149 -38
  62. metadata +38 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd425c8abea3ba512e8652c7f913b5ed09b5254fa868358f000f3af1fcd3ed82
4
- data.tar.gz: caf2e58c2f862bd94f63d3290876b61620cf100a22c88a07d3916d87f253c0c0
3
+ metadata.gz: e0e776dd7b47efb62b8ad18d7262d247362944f28ea67edb7e518d8a94c74649
4
+ data.tar.gz: 759ec6fb240c4c62835bd8c789877fc26fc974563d2566687a63b2c0dec1072f
5
5
  SHA512:
6
- metadata.gz: f45782eed90b778fbde30cdbd90f388a4b9f649e0080c26618e9243014618774d3afd9eb2076c7a47d0a0d43e137d1940382c85d0dea0915afa232f87d9e950d
7
- data.tar.gz: 2fda013558e05453f9ea1540b64bb51a9bd9269f6166395bd92f775aed3523abc5878b1c92c5ba4f0352842b6b4fb532f317f8a5d54aa108822f94dfcb7d9e9a
6
+ metadata.gz: 11fe99943d2575e00ca2fc150a78cad1f2f830b3249f3692f473da7e520775fc9423b254fb2f7613e4b6811f03dd3d2af0d4bba1d0f2f143e048ab65281771ff
7
+ data.tar.gz: f08db4117aa2ebba9c5ac7a9e057ca7ddc9fc0613170752ac10cc7515058f491fe756b880db73629e2e7a8328cc979872b4c682197c061de39da52a44796587f
data/README.md CHANGED
@@ -10,13 +10,13 @@ The following constraints apply:
10
10
 
11
11
  * No support for macruby extensions
12
12
  * Only support for the [modern AST](https://github.com/whitequark/parser/#usage) format
13
- * Only support for Ruby >= 2.7
13
+ * Only support for Ruby >= 3.2
14
14
 
15
15
  Notable Users:
16
16
 
17
17
  * [mutant](https://github.com/mbj/mutant) - Code review engine via mutation testing.
18
18
  * [ruby-next](https://github.com/ruby-next/ruby-next) - Ruby Syntax Backports.
19
- * May other [reverse-dependencies](https://rubygems.org/gems/unparser/reverse_dependencies).
19
+ * Many other [reverse-dependencies](https://rubygems.org/gems/unparser/reverse_dependencies).
20
20
 
21
21
  (if you want your tool to be mentioned here please PR the addition with a TLDR of your use case).
22
22
 
@@ -44,9 +44,9 @@ To preserve the comments from the source:
44
44
  require 'parser/current'
45
45
  require 'unparser'
46
46
 
47
- ast, comments = Unparser.parse_with_comments('your(ruby(code)) # with comments')
47
+ ast, comments = Unparser.parser.parse_with_comments(Unparser.buffer('your(ruby(code)) # with comments'))
48
48
 
49
- Unparser.unparse(ast, comments) # => 'your(ruby(code)) # with comments'
49
+ Unparser.unparse(ast, comments: comments) # => 'your(ruby(code)) # with comments'
50
50
  ```
51
51
 
52
52
  Passing in manually constructed AST:
@@ -114,7 +114,7 @@ Unparser currently successfully round trips almost all ruby code around. Using R
114
114
  If there is a non round trippable example that is NOT subjected to known [Limitations](#limitations).
115
115
  please report a bug.
116
116
 
117
- On CI unparser is currently tested against rubyspec with minor [excludes](https://github.com/mbj/unparser/blob/master/spec/integrations.yml).
117
+ On CI unparser is currently tested against rubyspec with minor [excludes](https://github.com/mbj/unparser/blob/main/spec/integrations.yml).
118
118
 
119
119
  Limitations:
120
120
  ------------
@@ -187,10 +187,15 @@ Contributing
187
187
  * Make your feature addition or bug fix.
188
188
  * Add tests for it. This is important so I don't break it in a
189
189
  future version unintentionally.
190
- * Commit, do not mess with Rakefile or version
190
+ * Commit, do not mess with version
191
191
  (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
192
192
  * Send me a pull request. Bonus points for topic branches.
193
193
 
194
+ Known Users
195
+ -------------
196
+
197
+ * [RailsRocket](https://www.railsrocket.app) - A no-code app builder that creates Rails apps
198
+
194
199
  License
195
200
  -------
196
201
 
data/bin/unparser CHANGED
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  trap('INT') do |status|
5
- exit! 128 + status
5
+ exit! status + 128
6
6
  end
7
7
 
8
8
  require 'unparser'
@@ -20,9 +20,11 @@ module Unparser
20
20
  # @return [Object]
21
21
  #
22
22
  # @api public
23
+ #
24
+ # mutant:disable
23
25
  def freeze
24
26
  memoized_method_cache
25
- super()
27
+ super
26
28
  end
27
29
 
28
30
  private
@@ -134,14 +134,25 @@ module Unparser
134
134
  # @param [Class, Module] scope
135
135
  #
136
136
  # @return [undefined]
137
+ #
138
+ # mutant:disable
137
139
  def included(descendant)
138
140
  descendant.instance_exec(self, attribute_names) do |anima, names|
139
141
  # Define anima method
142
+
143
+ class << self
144
+ undef_method(:anima) if method_defined?(:anima)
145
+ end
146
+
140
147
  define_singleton_method(:anima) { anima }
141
148
 
142
149
  # Define instance methods
143
150
  include InstanceMethods
144
151
 
152
+ names.each do |name|
153
+ undef_method(name) if method_defined?(name)
154
+ end
155
+
145
156
  # Define attribute readers
146
157
  attr_reader(*names)
147
158
 
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Unparser
4
- module AST
5
-
4
+ class AST
6
5
  # Calculated local variable scope for a given node
7
6
  class LocalVariableScope
8
- include Enumerable, Adamantium, Concord.new(:node)
7
+ include Adamantium, Anima.new(:static_local_variables, :node)
9
8
 
10
9
  # Initialize object
11
10
  #
@@ -13,15 +12,18 @@ module Unparser
13
12
  #
14
13
  # @return [undefined]
15
14
  #
16
- # @api private
17
- #
18
- def initialize(node)
15
+ # mutant:disable
16
+ def initialize(*arguments)
17
+ super
18
+
19
19
  items = []
20
- LocalVariableScopeEnumerator.each(node) do |*scope|
21
- items << scope
22
- end
20
+
21
+ LocalVariableScopeEnumerator.each(
22
+ node: node,
23
+ stack: static_local_variables.dup
24
+ ) { |*scope| items << scope }
25
+
23
26
  @items = items
24
- super(node)
25
27
  end
26
28
 
27
29
  # Test if local variable was first at given assignment
@@ -54,6 +56,15 @@ module Unparser
54
56
  end
55
57
  end
56
58
 
59
+ # mutant:disable
60
+ def local_variables_for_node(needle)
61
+ @items.each do |node, current|
62
+ return current if node.equal?(needle)
63
+ end
64
+
65
+ Set.new
66
+ end
67
+
57
68
  # Test if local variables where first assigned in body and read by conditional
58
69
  #
59
70
  # @param [Parser::AST::Node] body
@@ -91,21 +102,13 @@ module Unparser
91
102
  #
92
103
  # @api private
93
104
  #
94
- def initialize
95
- @stack = [Set.new]
105
+ def initialize(stack:)
106
+ @stack = [stack]
96
107
  end
97
108
 
98
109
  # Enumerate each node with its local variable scope
99
- #
100
- # @param [Parser::AST::Node] node
101
- #
102
- # @return [self]
103
- #
104
- # @api private
105
- #
106
- def self.each(node, &block)
107
- new.each(node, &block)
108
- self
110
+ def self.each(node:, stack:, &block)
111
+ new(stack: stack).each(node: node, &block)
109
112
  end
110
113
 
111
114
  # Enumerate local variable scope scope
@@ -118,7 +121,7 @@ module Unparser
118
121
  #
119
122
  # @api private
120
123
  #
121
- def each(node, &block)
124
+ def each(node:, &block)
122
125
  visit(node, &block)
123
126
  end
124
127
 
@@ -133,7 +136,7 @@ module Unparser
133
136
  enter(node)
134
137
  yield node, current.dup, before
135
138
  node.children.each do |child|
136
- visit(child, &block) if child.is_a?(Parser::AST::Node)
139
+ visit(child, &block) if child.instance_of?(Parser::AST::Node)
137
140
  end
138
141
  leave(node)
139
142
  end
@@ -143,7 +146,7 @@ module Unparser
143
146
  when *RESET_NODES
144
147
  push_reset
145
148
  when ASSIGN_NODES
146
- define(node.children.first)
149
+ value = node.children.first and define(value)
147
150
  when *INHERIT_NODES
148
151
  push_inherit
149
152
  end
data/lib/unparser/ast.rb CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  module Unparser
4
4
  # Namespace for AST processing tools
5
- module AST
5
+ class AST
6
+ include Anima.new(:comments, :explicit_encoding, :node, :static_local_variables)
7
+
6
8
  FIRST_CHILD = ->(node) { node.children.first }.freeze
7
- TAUTOLOGY = ->(_node) { true }.freeze
8
9
 
9
10
  RESET_NODES = %i[module class sclass def defs].freeze
10
11
  INHERIT_NODES = [:block].freeze
@@ -16,12 +17,22 @@ module Unparser
16
17
  arg
17
18
  kwarg
18
19
  kwoptarg
20
+ kwrestarg
19
21
  lvasgn
20
22
  optarg
21
- procarg0
22
23
  restarg
23
24
  ].to_set.freeze
24
25
 
26
+ # mutant:disable
27
+ def self.from_node(node:)
28
+ new(
29
+ comments: EMPTY_ARRAY,
30
+ explicit_encoding: nil,
31
+ node:,
32
+ static_local_variables: Set.new
33
+ )
34
+ end
35
+
25
36
  # Test for local variable inherited scope reset
26
37
  #
27
38
  # @param [Parser::AST::Node] node
@@ -69,6 +80,7 @@ module Unparser
69
80
  #
70
81
  # @api private
71
82
  #
83
+ # mutant:disable
72
84
  def self.local_variable_reads(node)
73
85
  Enumerator.new(
74
86
  node,
@@ -80,19 +92,6 @@ module Unparser
80
92
  class Enumerator
81
93
  include Adamantium, Concord.new(:node, :controller), Enumerable
82
94
 
83
- # Return new instance
84
- #
85
- # @param [Parser::AST::Node] node
86
- # @param [#call(node)] controller
87
- #
88
- # @return [Enumerator]
89
- #
90
- # @api private
91
- #
92
- def self.new(node, controller = TAUTOLOGY)
93
- super
94
- end
95
-
96
95
  # Return each node
97
96
  #
98
97
  # @return [Enumerator<Parser::AST::Node>]
@@ -103,8 +102,8 @@ module Unparser
103
102
  #
104
103
  # @api private
105
104
  #
106
- def each(&block)
107
- Walker.call(node, controller, &block)
105
+ def each(&)
106
+ Walker.call(node, controller, &)
108
107
  end
109
108
 
110
109
  # Return nodes selected by types
@@ -168,13 +167,10 @@ module Unparser
168
167
  #
169
168
  # @param [Parser::AST::Node] node
170
169
  #
171
- # @return [self]
172
- #
173
170
  # @api private
174
171
  #
175
- def self.call(node, controller = TAUTOLOGY, &block)
172
+ def self.call(node, controller, &block)
176
173
  new(block, controller).call(node)
177
- self
178
174
  end
179
175
 
180
176
  # Call walker with node
@@ -14,8 +14,10 @@ module Unparser
14
14
  # @api private
15
15
  #
16
16
  def initialize
17
- @content = +''
18
- @indent = 0
17
+ @content = +''
18
+ @heredocs = []
19
+ @indent = 0
20
+ @no_nl = true
19
21
  end
20
22
 
21
23
  # Append string
@@ -34,6 +36,13 @@ module Unparser
34
36
  self
35
37
  end
36
38
 
39
+ # Push to heredoc stack
40
+ #
41
+ # @param [String] heredoc
42
+ def push_heredoc(heredoc)
43
+ @heredocs << heredoc
44
+ end
45
+
37
46
  # Append a string without an indentation prefix
38
47
  #
39
48
  # @param [String] string
@@ -44,7 +53,6 @@ module Unparser
44
53
  #
45
54
  def append_without_prefix(string)
46
55
  write(string)
47
- self
48
56
  end
49
57
 
50
58
  # Increase indent
@@ -69,6 +77,10 @@ module Unparser
69
77
  self
70
78
  end
71
79
 
80
+ def ensure_nl
81
+ nl unless fresh_line?
82
+ end
83
+
72
84
  # Write newline
73
85
  #
74
86
  # @return [self]
@@ -76,10 +88,29 @@ module Unparser
76
88
  # @api private
77
89
  #
78
90
  def nl
91
+ @no_nl = false
79
92
  write(NL)
93
+ flush_heredocs
80
94
  self
81
95
  end
82
96
 
97
+ # Write final newline
98
+ def final_newline
99
+ return if fresh_line? || @no_nl
100
+
101
+ write(NL)
102
+ end
103
+
104
+ def nl_flush_heredocs
105
+ return if @heredocs.empty?
106
+
107
+ if fresh_line?
108
+ flush_heredocs
109
+ else
110
+ nl
111
+ end
112
+ end
113
+
83
114
  def root_indent
84
115
  before = @indent
85
116
  @indent = 0
@@ -107,18 +138,6 @@ module Unparser
107
138
  @content.dup.freeze
108
139
  end
109
140
 
110
- # Capture the content written to the buffer within the block
111
- #
112
- # @return [String]
113
- #
114
- # @api private
115
- #
116
- def capture_content
117
- size_before = content.size
118
- yield
119
- content[size_before..]
120
- end
121
-
122
141
  # Write raw fragment to buffer
123
142
  #
124
143
  # Does not do indentation logic.
@@ -131,6 +150,10 @@ module Unparser
131
150
  self
132
151
  end
133
152
 
153
+ def write_encoding(encoding)
154
+ write("# -*- encoding: #{encoding} -*-\n")
155
+ end
156
+
134
157
  private
135
158
 
136
159
  INDENT_SPACE = ' '.freeze
@@ -139,5 +162,10 @@ module Unparser
139
162
  write(INDENT_SPACE * @indent)
140
163
  end
141
164
 
165
+ def flush_heredocs
166
+ @heredocs.each(&public_method(:write))
167
+ @heredocs = []
168
+ end
169
+
142
170
  end # Buffer
143
171
  end # Unparser
data/lib/unparser/cli.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'pathname'
4
+
3
5
  module Unparser
4
6
  # Unparser CLI implementation
5
7
  class CLI
@@ -60,6 +62,7 @@ module Unparser
60
62
  #
61
63
  # @api private
62
64
  #
65
+ # mutant:disable
63
66
  def self.run(*arguments)
64
67
  new(*arguments).exit_status
65
68
  end
@@ -71,15 +74,17 @@ module Unparser
71
74
  # @return [undefined]
72
75
  #
73
76
  # @api private
77
+ # mutant:disable
74
78
  def initialize(arguments)
75
79
  @ignore = Set.new
76
80
  @targets = []
77
81
 
78
- @fail_fast = false
79
- @start_with = nil
80
- @success = true
81
- @validation = :validation
82
- @verbose = false
82
+ @fail_fast = false
83
+ @start_with = nil
84
+ @success = true
85
+ @validation = :validation
86
+ @verbose = false
87
+ @ignore_original_syntax_error = false
83
88
 
84
89
  opts = OptionParser.new do |builder|
85
90
  add_options(builder)
@@ -98,6 +103,7 @@ module Unparser
98
103
  #
99
104
  # @api private
100
105
  #
106
+ # mutant:disable
101
107
  # rubocop:disable Metrics/MethodLength
102
108
  def add_options(builder)
103
109
  builder.banner = 'usage: unparse [options] FILE [FILE]'
@@ -114,6 +120,9 @@ module Unparser
114
120
  builder.on('-l', '--literal') do
115
121
  @validation = :literal_validation
116
122
  end
123
+ builder.on('--ignore-original-syntax-error') do
124
+ @ignore_original_syntax_error = true
125
+ end
117
126
  builder.on('--ignore FILE') do |file|
118
127
  @ignore.merge(targets(file))
119
128
  end
@@ -129,6 +138,7 @@ module Unparser
129
138
  #
130
139
  # @api private
131
140
  #
141
+ # mutant:disable
132
142
  def exit_status
133
143
  effective_targets.each do |target|
134
144
  process_target(target)
@@ -140,11 +150,15 @@ module Unparser
140
150
 
141
151
  private
142
152
 
153
+ # mutant:disable
143
154
  def process_target(target)
144
155
  validation = target.public_send(@validation)
145
156
  if validation.success?
146
157
  puts validation.report if @verbose
147
158
  puts "Success: #{validation.identification}"
159
+ elsif ignore_original_syntax_error?(validation)
160
+ exception = validation.original_node.from_left
161
+ puts "#{exception.class}: #{validation.identification} #{exception}"
148
162
  else
149
163
  puts validation.report
150
164
  puts "Error: #{validation.identification}"
@@ -152,6 +166,14 @@ module Unparser
152
166
  end
153
167
  end
154
168
 
169
+ # mutant:disable
170
+ def ignore_original_syntax_error?(validation)
171
+ @ignore_original_syntax_error && validation.original_node.from_left do
172
+ nil
173
+ end.instance_of?(Parser::SyntaxError)
174
+ end
175
+
176
+ # mutant:disable
155
177
  def effective_targets
156
178
  if @start_with
157
179
  reject = true
@@ -167,13 +189,14 @@ module Unparser
167
189
  end.reject(&@ignore.method(:include?))
168
190
  end
169
191
 
192
+ # mutant:disable
170
193
  def targets(file_name)
171
194
  if File.directory?(file_name)
172
- Dir.glob(File.join(file_name, '**/*.rb')).sort
195
+ Dir.glob(File.join(file_name, '**/*.rb'))
173
196
  elsif File.file?(file_name)
174
197
  [file_name]
175
198
  else
176
- Dir.glob(file_name).sort
199
+ Dir.glob(file_name)
177
200
  end.map { |file| Target::Path.new(Pathname.new(file)) }
178
201
  end
179
202
  end # CLI
@@ -28,7 +28,12 @@ module Unparser
28
28
 
29
29
  private
30
30
 
31
+ # Well rubocop you are static so you do not have a clue here ;)
32
+ # rubocop:disable Style/RedundantInitialize
33
+ # rubocop:disable Lint/MissingSuper
31
34
  def initialize; end
35
+ # rubocop:enable Style/RedundantInitialize
36
+ # rubocop:enable Lint/MissingSuper
32
37
 
33
38
  end.new
34
39
 
@@ -54,15 +54,15 @@ module Unparser
54
54
  # Evaluate functor block
55
55
  #
56
56
  # @return [Either::Left<Object>]
57
- def fmap(&block)
58
- require_block(&block)
57
+ def fmap(&)
58
+ require_block(&)
59
59
  end
60
60
 
61
61
  # Evaluate applicative block
62
62
  #
63
63
  # @return [Either::Left<Object>]
64
- def bind(&block)
65
- require_block(&block)
64
+ def bind(&)
65
+ require_block(&)
66
66
  end
67
67
 
68
68
  # Unwrap value from left
@@ -137,8 +137,8 @@ module Unparser
137
137
  # Map over left value
138
138
  #
139
139
  # @return [Either::Right<Object>]
140
- def lmap(&block)
141
- require_block(&block)
140
+ def lmap(&)
141
+ require_block(&)
142
142
  end
143
143
 
144
144
  # Evaluate right side of branch
@@ -13,7 +13,11 @@ module Unparser
13
13
  end
14
14
 
15
15
  def emit_def_arguments
16
- delimited(normal_arguments)
16
+ if children.one? && n_mlhs?(Util.one(children))
17
+ emitter(Util.one(children)).dispatch_def
18
+ else
19
+ delimited(normal_arguments)
20
+ end
17
21
  end
18
22
 
19
23
  def emit_lambda_arguments
@@ -2,11 +2,13 @@
2
2
 
3
3
  module Unparser
4
4
  class Emitter
5
- # Emitter for block and kwrestarg arguments
6
- class Morearg < self
5
+ # Emitter for forwarding arguments
6
+ class ForwardArg < self
7
7
  MAP = {
8
- blockarg: '&',
9
- kwrestarg: '**'
8
+ blockarg: '&',
9
+ forwarded_kwrestarg: '**',
10
+ forwarded_restarg: '*',
11
+ kwrestarg: '**'
10
12
  }.freeze
11
13
 
12
14
  handle(*MAP.keys)
@@ -6,10 +6,6 @@ module Unparser
6
6
  class Array < self
7
7
  handle :array
8
8
 
9
- def emit_heredoc_reminders
10
- emitters.each(&:emit_heredoc_reminders)
11
- end
12
-
13
9
  private
14
10
 
15
11
  def dispatch
@@ -12,18 +12,10 @@ module Unparser
12
12
 
13
13
  def dispatch
14
14
  write('[')
15
- delimited(children, &method(:emit_member))
15
+ delimited(children)
16
16
  write(', ') if node_type.equal?(:array_pattern_with_tail)
17
17
  write(']')
18
18
  end
19
-
20
- def emit_member(node)
21
- if n_match_rest?(node)
22
- writer_with(MatchRest, node).emit_array_pattern
23
- else
24
- visit(node)
25
- end
26
- end
27
19
  end # Pin
28
20
  end # Emitter
29
21
  end # Unparser
@@ -11,12 +11,6 @@ module Unparser
11
11
  true
12
12
  end
13
13
 
14
- def emit_heredoc_reminders
15
- return unless right
16
-
17
- emitter(right).emit_heredoc_reminders
18
- end
19
-
20
14
  private
21
15
 
22
16
  def dispatch
@@ -30,12 +24,27 @@ module Unparser
30
24
  write(' = ')
31
25
 
32
26
  if BINARY_OPERATOR.include?(right.type)
33
- writer_with(Writer::Binary, right).emit_operator
27
+ writer_with(Writer::Binary, node: right).emit_operator
28
+ elsif n_array?(right)
29
+ emit_array
30
+ else
31
+ right_emitter.write_to_buffer
32
+ end
33
+ end
34
+
35
+ def emit_array
36
+ if right.children.size > 1
37
+ delimited(right.children)
34
38
  else
35
- visit(right)
39
+ right_emitter.write_to_buffer
36
40
  end
37
41
  end
38
42
 
43
+ def right_emitter
44
+ emitter(right)
45
+ end
46
+ memoize :right_emitter
47
+
39
48
  abstract_method :emit_left
40
49
 
41
50
  # Variable assignment emitter