rensei 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6cc6cc047b5640e168c637b0dada84c773b645347a66385ad3fb06c7b7e08346
4
+ data.tar.gz: 70800f0c78fadf6816b9b9762e5f26fd7be3a012515cd7b90149dd2d911149a3
5
+ SHA512:
6
+ metadata.gz: c7cf2901a240748cfa044253a269a55154d0516829ccab38b166caa2ed8c5f4eed43589ec98aca49e2aaa9f629c7c6ef2736bf878c4de676daf1852d41f97dcc
7
+ data.tar.gz: bdf5db3fd89679a2c5985b63a2905de5f2a7c42555264a53cb41ea3f24bbb0743fd2ed27a131e477d84c30ca3db6901099aa30242ed577b5bcbbbbefeefd5d53
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.7.1
6
+ before_install: gem install bundler -v 2.1.4
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rensei.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "rspec", "~> 3.0"
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 manga_osyo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,54 @@
1
+ # Rensei
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rensei`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ Unparse from `RubyVM::AbstractSyntaxTree::Node` to Ruby code.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'rensei'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install rensei
22
+
23
+ ## Usage
24
+
25
+ ```ruby
26
+ require "rensei"
27
+
28
+ code = <<~EOS
29
+ 10.times do |i|
30
+ puts i
31
+ end
32
+ EOS
33
+
34
+ ast = RubyVM::AbstractSyntaxTree.parse(code)
35
+
36
+ ruby = Rensei.unparse(ast)
37
+ puts ruby
38
+ # => 10.times() { |i| puts(i) }
39
+ ```
40
+
41
+ ## Development
42
+
43
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
44
+
45
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
46
+
47
+ ## Contributing
48
+
49
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rensei.
50
+
51
+
52
+ ## License
53
+
54
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "rensei"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,11 @@
1
+ require "rensei/version"
2
+ require "rensei/unparser"
3
+ require "rensei/node_to_hash"
4
+
5
+ module Rensei
6
+ class Error < StandardError; end
7
+
8
+ def self.unparse(code)
9
+ Unparser.unparse(code)
10
+ end
11
+ end
@@ -0,0 +1,54 @@
1
+ require "json"
2
+
3
+ module Rensei
4
+ module NodeToHash
5
+ refine RubyVM::AbstractSyntaxTree::Node do
6
+ using NodeToHash
7
+ using Module.new {
8
+ refine Object do
9
+ def to_hashable
10
+ to_s
11
+ end
12
+ end
13
+ [
14
+ NilClass,
15
+ FalseClass,
16
+ TrueClass,
17
+ Array,
18
+ Symbol,
19
+ Numeric,
20
+ Regexp,
21
+ ].each { |klass|
22
+ refine klass do
23
+ def to_hashable
24
+ self
25
+ end
26
+ end
27
+ }
28
+ }
29
+
30
+ def to_hashable(ignore_codeposition: true, &block)
31
+ block = :to_hashable unless block
32
+ {
33
+ type: type,
34
+ children: children.map(&block),
35
+ }.tap { |it|
36
+ it.merge!(
37
+ first_column: first_column,
38
+ last_column: last_column,
39
+ first_lineno: first_lineno,
40
+ last_lineno: last_lineno
41
+ ) unless ignore_codeposition
42
+ }
43
+ end
44
+
45
+ def to_json(ignore_codeposition: true)
46
+ to_hashable(ignore_codeposition: ignore_codeposition, &:to_json)
47
+ end
48
+
49
+ def to_h(ignore_codeposition: true)
50
+ to_hashable(ignore_codeposition: ignore_codeposition)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,1398 @@
1
+ module Rensei
2
+ module Unparser
3
+ class NoImplemented < StandardError; end
4
+
5
+ using Module.new {
6
+ refine Hash do
7
+ def type
8
+ self[:type]
9
+ end
10
+
11
+ def children
12
+ self[:children]
13
+ end
14
+
15
+ def except(*keys)
16
+ slice(*self.keys - keys)
17
+ end
18
+ end
19
+
20
+ refine String do
21
+ def bracket(prefix = "(", suffix = ")")
22
+ "#{prefix}#{self}#{suffix}"
23
+ end
24
+
25
+ def escape
26
+ dump[1..-2]
27
+ end
28
+ end
29
+ }
30
+
31
+ module Base
32
+ def unparse(node, opt = {})
33
+ return node unless RubyVM::AbstractSyntaxTree::Node === node || Hash === node
34
+ method_name = "NODE_#{node.type}"
35
+ respond_to?(method_name, true) ? send(method_name, node, opt.dup) : node
36
+ end
37
+
38
+ private
39
+
40
+ def _unparse(opt = {})
41
+ proc { |node| unparse(node, opt.dup) }
42
+ end
43
+
44
+ # statement sequence
45
+ # format: [nd_head]; ...; [nd_next]
46
+ # example: foo; bar
47
+ def NODE_BLOCK(node, opt = {})
48
+ node.children.each_slice(2).map { |head, next_|
49
+ if head&.type == :BEGIN && opt.delete(:NODE_BEGIN_WITHOUT_BEGIN)
50
+ "#{unparse(next_, opt)}"
51
+ # Support by BEGIN { foo }
52
+ elsif next_&.type == :BEGIN
53
+ "BEGIN { #{unparse(head.children.first, opt)} }"
54
+ elsif head.type == :BEGIN
55
+ "begin; #{unparse(next_, opt)}; end"
56
+ else
57
+ "#{unparse(head, opt)};#{next_ ? " #{unparse(next_, opt)}" : ""}"
58
+ end
59
+ }.join("; ")
60
+ end
61
+
62
+ # if statement
63
+ # format: if [nd_cond] then [nd_body] else [nd_else] end
64
+ # example: if x == 1 then foo else bar end
65
+ def NODE_IF(node, opt = {})
66
+ node.children.then { |cond, body, else_|
67
+ <<~EOS.chomp
68
+ if #{unparse(cond, opt)}
69
+ #{unparse(body, opt)}#{else_ ? "\nelse\n #{unparse(else_, opt)}" : ""}
70
+ end
71
+ EOS
72
+ }
73
+ end
74
+
75
+ # unless statement
76
+ # format: unless [nd_cond] then [nd_body] else [nd_else] end
77
+ # example: unless x == 1 then foo else bar end
78
+ def NODE_UNLESS(node, opt = {})
79
+ node.children.then { |cond, body, else_|
80
+ <<~EOS.chomp
81
+ unless #{unparse(cond, opt)}
82
+ #{unparse(body, opt)}#{else_ ? "\nelse\n #{unparse(else_, opt)}" : ""}
83
+ end
84
+ EOS
85
+ }
86
+ end
87
+
88
+ # case statement
89
+ # format: case [nd_head]; [nd_body]; end
90
+ # example: case x; when 1; foo; when 2; bar; else baz; end
91
+ def NODE_CASE(node, opt)
92
+ node.children.then { |head, body, else_|
93
+ <<~EOS.chomp
94
+ case #{unparse(head, opt)}
95
+ #{unparse(body, opt)}#{else_ ? "\nelse\n#{unparse(else_, opt)}" : ""}
96
+ end
97
+ EOS
98
+ }
99
+ end
100
+
101
+ # case statement with no head
102
+ # format: case; [nd_body]; end
103
+ # example: case; when 1; foo; when 2; bar; else baz; end
104
+ def NODE_CASE2(node, opt = {})
105
+ node.children.then { |_, body, else_|
106
+ <<~EOS.chomp
107
+ case
108
+ #{unparse(body, opt)}#{else_ ? "\nelse\n#{unparse(else_, opt)}" : ""}
109
+ end
110
+ EOS
111
+ }
112
+ end
113
+
114
+ # when clause
115
+ # format: when [nd_head]; [nd_body]; (when or else) [nd_next]
116
+ # example: case x; when 1; foo; when 2; bar; else baz; end
117
+ def NODE_WHEN(node, opt = {})
118
+ node.children.then { |head, body, next_|
119
+ <<~EOS.chomp
120
+ when #{unparse(head, opt.merge(NODE_ARRAY_EXPAND: true))}
121
+ #{unparse(body, opt)}
122
+ #{next_&.type == :WHEN || next_.nil? ? unparse(next_, opt) : "else\n #{unparse(next_, opt)}"}
123
+ EOS
124
+ }
125
+ end
126
+
127
+ # while statement
128
+ # format: while [nd_cond]; [nd_body]; end
129
+ # example: while x == 1; foo; end
130
+ def NODE_WHILE(node, opt = {})
131
+ node.children.then { |cond, body, state|
132
+ # MEMO: state is not supported Ruby 2.6
133
+ state = true if state.nil?
134
+ if state
135
+ <<~EOS.chomp
136
+ while #{unparse(cond, opt)}
137
+ #{unparse(body, opt)}
138
+ end
139
+ EOS
140
+ # state: begin-end-while
141
+ else
142
+ <<~EOS.chomp
143
+ begin
144
+ #{unparse(body, opt)}
145
+ end while #{unparse(cond, opt)}
146
+ EOS
147
+ end
148
+ }
149
+ end
150
+
151
+ # until statement
152
+ # format: until [nd_cond]; [nd_body]; end
153
+ # example: until x == 1; foo; end
154
+ def NODE_UNTIL(node, opt = {})
155
+ node.children.then { |cond, body, state|
156
+ # MEMO: state is not supported Ruby 2.6
157
+ state = true if state.nil?
158
+ if state
159
+ <<~EOS.chomp
160
+ until #{unparse(cond, opt)}
161
+ #{unparse(body, opt)}
162
+ end
163
+ EOS
164
+ # state: begin-end-until
165
+ else
166
+ <<~EOS.chomp
167
+ begin
168
+ #{unparse(body, opt)}
169
+ end until #{unparse(cond, opt)}
170
+ EOS
171
+ end
172
+ }
173
+ end
174
+
175
+ # method call with block
176
+ # format: [nd_iter] { [nd_body] }
177
+ # example: 3.times { foo }
178
+ def NODE_ITER(node, opt = {})
179
+ node.children.then { |iter, body|
180
+ "#{unparse(iter, opt)} { #{unparse(body, opt)} }"
181
+ }
182
+ end
183
+
184
+ # for statement
185
+ # format: for * in [nd_iter] do [nd_body] end
186
+ # example: for i in 1..3 do foo end
187
+ def NODE_FOR(node, opt = {})
188
+ node.children.then { |iter, body, var|
189
+ scope = unparse_NODE_SCOPE(body, opt)
190
+ <<~EOS.chomp
191
+ for #{unparse(scope[:args], opt) || "*"} in #{unparse(iter, opt)} do
192
+ #{scope[:body]}
193
+ end
194
+ EOS
195
+ }
196
+ end
197
+
198
+ # vars of for statement with masgn
199
+ # format: for [nd_var] in ... do ... end
200
+ # example: for x, y in 1..3 do foo end
201
+ def NODE_FOR_MASGN(node, opt = {})
202
+ # Support from NODE_MASGN
203
+ end
204
+
205
+ # break statement
206
+ # format: break [nd_stts]
207
+ # example: break 1
208
+ def NODE_BREAK(node, opt = {})
209
+ node.children.then { |stts,|
210
+ "break #{unparse(stts, opt)}"
211
+ }
212
+ end
213
+
214
+ # next statement
215
+ # format: next [nd_stts]
216
+ # example: next 1
217
+ def NODE_NEXT(node, opt = {})
218
+ node.children.then { |stts,|
219
+ "next #{unparse(stts, opt)}"
220
+ }
221
+ end
222
+
223
+ # return statement
224
+ # format: return [nd_stts]
225
+ # example: return 1
226
+ def NODE_RETURN(node, opt = {})
227
+ node.children.then { |stts,|
228
+ "(return #{unparse(stts, opt)})"
229
+ }
230
+ end
231
+
232
+ # redo statement
233
+ # format: redo
234
+ # example: redo
235
+ def NODE_REDO(node, opt = {})
236
+ "redo"
237
+ end
238
+
239
+ # retry statement
240
+ # format: retry
241
+ # example: retry
242
+ def NODE_RETRY(node, opt = {})
243
+ "retry"
244
+ end
245
+
246
+ # begin statement
247
+ # format: begin; [nd_body]; end
248
+ # example: begin; 1; end
249
+ def NODE_BEGIN(node, opt = {})
250
+ return "" if node.children.first.nil?
251
+ # return "begin; end" if node.children.first.nil?
252
+ node.children.then { |body|
253
+ <<~EOS.chomp
254
+ begin
255
+ #{unparse(body, opt)}
256
+ end
257
+ EOS
258
+ }
259
+ end
260
+
261
+ # rescue clause
262
+ # format: begin; [nd_body]; (rescue) [nd_resq]; else [nd_else]; end
263
+ # example: begin; foo; rescue; bar; else; baz; end
264
+ def NODE_RESCUE(node, opt = {})
265
+ node.children.then { |body, resq, else_|
266
+ <<~EOS.chomp
267
+ begin
268
+ #{unparse(body, opt)}
269
+ #{unparse(resq, opt)}#{else_ ? "\nelse\n #{unparse(else_)}" : ""}
270
+ end
271
+ EOS
272
+ }
273
+ end
274
+
275
+ # rescue clause (cont'd)
276
+ # format: rescue [nd_args]; [nd_body]; (rescue) [nd_head]
277
+ # example: begin; foo; rescue; bar; else; baz; end
278
+ def NODE_RESBODY(node, opt = {})
279
+ node.children.then { |args, body|
280
+ args_ = args&.then { |it| unparse(it, opt.merge(NODE_ARRAY_EXPAND: true)) + ";" }
281
+ if body.type == :BLOCK
282
+ if body.children.first&.children[1]&.type == :ERRINFO
283
+ "rescue #{args_}#{unparse(body, opt)}"
284
+ else
285
+ "rescue #{args_ || ";"}#{unparse(body, opt)}"
286
+ end
287
+ else
288
+ # MEMO: Support by `begin; foo; rescue; bar; else; baz; end`
289
+ "rescue #{args_ || ";"} #{unparse(body, opt)}"
290
+ end
291
+ }
292
+ end
293
+
294
+ # ensure clause
295
+ # format: begin; [nd_head]; ensure; [nd_ensr]; end
296
+ # example: begin; foo; ensure; bar; end
297
+ def NODE_ENSURE(node, opt = {})
298
+ node.children.then { |head, ensr|
299
+ <<~EOS.chomp
300
+ begin
301
+ #{unparse(head, opt)}
302
+ ensure
303
+ #{unparse(ensr, opt)}
304
+ end
305
+ EOS
306
+ }
307
+ end
308
+
309
+ # && operator
310
+ # format: [nd_1st] && [nd_2nd]
311
+ # example: foo && bar
312
+ def NODE_AND(node, opt = {})
313
+ node.children.then { |args|
314
+ "(#{args.map { |node| unparse(node, opt) }.join(" && ")})"
315
+ }
316
+ end
317
+
318
+ # || operator
319
+ # format: [nd_1st] || [nd_2nd]
320
+ # example: foo || bar
321
+ def NODE_OR(node, opt = {})
322
+ node.children.then { |args|
323
+ "(#{args.map { |node| unparse(node, opt) }.join(" || ")})"
324
+ }
325
+ end
326
+
327
+ # multiple assignment
328
+ # format: [nd_head], [nd_args] = [nd_value]
329
+ # example: a, b = foo
330
+ def NODE_MASGN(node, opt = {})
331
+ node_masgn = opt[:_NODE_MASGN]
332
+ node.children.then { |right, left, _NODE_SPECIAL_NO_NAME_REST = :dummy|
333
+ # Support: for x, y in 1..3 do foo end
334
+ _right = unparse(right, opt)&.then { |it|
335
+ right.type == :FOR_MASGN || it.empty? ? "" : " = #{it}"
336
+ }
337
+
338
+
339
+ # Support: (a,) = value
340
+ if _right&.empty?
341
+ _left = unparse(left, opt.merge(NODE_ARRAY_EXPAND: true)) || "*"
342
+ elsif left
343
+ if node_masgn && left.children.count <= 2
344
+ _left = left.children.map(&_unparse(opt.merge(NODE_ARRAY_EXPAND: true))).first
345
+ else
346
+ _left = left.children.map(&_unparse(opt.merge(NODE_ARRAY_EXPAND: true))).join(", ")
347
+ end
348
+ end
349
+
350
+ # Support: for * in 1..3 do foo end
351
+ # * = foo
352
+ # a, b, * = foo
353
+ if _NODE_SPECIAL_NO_NAME_REST
354
+ # Support `(a, *) = foo` by 2.6
355
+ if _NODE_SPECIAL_NO_NAME_REST == :dummy
356
+ vname = ""
357
+ elsif _NODE_SPECIAL_NO_NAME_REST != :NODE_SPECIAL_NO_NAME_REST
358
+ vname = unparse(_NODE_SPECIAL_NO_NAME_REST, opt)
359
+ end
360
+ if node_masgn
361
+ _left = left.nil? || _left.empty? ? "*#{vname}" : "#{_left}, *#{vname}"
362
+ else
363
+ _left = left.nil? || _left.empty? ? "*#{vname}" : "#{_left}*#{vname}"
364
+ end
365
+ end
366
+
367
+ "(#{_left})#{_right}"
368
+ }
369
+ end
370
+
371
+ # focal variable assignment
372
+ # lormat: [nd_vid](lvar) = [nd_value]
373
+ # example: x = foo
374
+ def NODE_LASGN(node, opt = {})
375
+ node.children.then { |vid, value|
376
+ if value == :NODE_SPECIAL_REQUIRED_KEYWORD
377
+ "#{vid}:"
378
+ elsif opt.delete(:KW_ARG)
379
+ "#{vid}:#{value&.then { |it| " #{unparse(it, opt)}" }}"
380
+ elsif value.nil? || (_value = unparse(value, opt)).empty?
381
+ "#{vid}"
382
+ elsif opt.delete(:NODE_LASGN_EXPAND)
383
+ "#{vid} = #{_value}"
384
+ elsif value.type == :ERRINFO
385
+ "=> #{vid}"
386
+ else
387
+ "(#{vid} = #{_value})"
388
+ end
389
+ }
390
+ end
391
+
392
+ # dynamic variable assignment (out of current scope)
393
+ # format: [nd_vid](dvar) = [nd_value]
394
+ # example: x = nil; 1.times { x = foo }
395
+ def NODE_DASGN(node, opt = {})
396
+ node.children.then { |vid, value|
397
+ if value
398
+ "(#{vid} = #{unparse(value, opt)})"
399
+ else
400
+ # Support: `hoge(a = 1) { a, b = c }`
401
+ "#{vid}"
402
+ end
403
+ }
404
+ end
405
+
406
+ # dynamic variable assignment (in current scope)
407
+ # format: [nd_vid](current dvar) = [nd_value]
408
+ # example: 1.times { x = foo }
409
+ def NODE_DASGN_CURR(node, opt = {})
410
+ node.children.then { |vid, value|
411
+ if value == :NODE_SPECIAL_REQUIRED_KEYWORD
412
+ "#{vid}:"
413
+ elsif opt.delete(:KW_ARG)
414
+ "#{vid}:#{value&.then { |it| " #{unparse(it, opt)}" }}"
415
+ elsif value.nil?
416
+ "#{vid}"
417
+ elsif opt.delete(:NODE_DASGN_CURR_EXPAND)
418
+ "#{vid} = #{unparse(value, opt)}"
419
+ else
420
+ "(#{vid} = #{unparse(value, opt)})"
421
+ end
422
+ }
423
+ end
424
+
425
+ # instance variable assignment
426
+ # format: [nd_vid](ivar) = [nd_value]
427
+ # example: @x = foo
428
+ def NODE_IASGN(node, opt = {})
429
+ node.children.then { |vid, value|
430
+ if value
431
+ "(#{vid} = #{unparse(value, opt)})"
432
+ else
433
+ # Support: `hoge(@a = 1) { @a, b = c }`
434
+ "#{vid}"
435
+ end
436
+ }
437
+ end
438
+
439
+ # class variable assignment
440
+ # format: [nd_vid](cvar) = [nd_value]
441
+ # example: @@x = foo
442
+ # nd_vid, "class variable"
443
+ def NODE_CVASGN(node, opt = {})
444
+ node.children.then { |vid, value|
445
+ if value
446
+ "(#{vid} = #{unparse(value, opt)})"
447
+ else
448
+ # Support: `hoge(@@a = 1) { @@a, b = c }`
449
+ "#{vid}"
450
+ end
451
+ }
452
+ end
453
+
454
+ # global variable assignment
455
+ # format: [nd_entry](gvar) = [nd_value]
456
+ # example: $x = foo
457
+ def NODE_GASGN(node, opt = {})
458
+ node.children.then { |vid, value|
459
+ if value
460
+ "(#{vid} = #{unparse(value, opt)})"
461
+ else
462
+ # Support: `hoge($a = 1) { $a, b = c }`
463
+ "#{vid}"
464
+ end
465
+ }
466
+ end
467
+
468
+ # constant declaration
469
+ # format: [nd_else]::[nd_vid](constant) = [nd_value]
470
+ # example: X = foo
471
+ def NODE_CDECL(node, opt = {})
472
+ node.children.then { |else_, vid, value|
473
+ rvalue = Symbol === vid ? value : (value || vid)
474
+ if rvalue
475
+ "(#{unparse(else_, opt)} = #{unparse(rvalue, opt)})"
476
+ else
477
+ "#{unparse(else_, opt)}"
478
+ end
479
+ }
480
+ end
481
+
482
+ # array assignment with operator
483
+ # format: [nd_recv] [ [nd_args->nd_head] ] [nd_mid]= [nd_args->nd_body]
484
+ # example: ary[1] += foo
485
+ def NODE_OP_ASGN1(node, opt = {})
486
+ node.children.then { |recv, op, head, mid|
487
+ "(#{unparse(recv, opt)}[#{unparse(head, opt.merge(NODE_ARRAY_EXPAND: true))}] #{op}= #{unparse(mid, opt.merge(NODE_ARRAY_EXPAND: true))})"
488
+ }
489
+ end
490
+
491
+ # attr assignment with operator
492
+ # format: [nd_recv].[attr] [nd_next->nd_mid]= [nd_value]
493
+ # where [attr]: [nd_next->nd_vid]
494
+ # example: struct.field += foo
495
+ def NODE_OP_ASGN2(node, opt = {})
496
+ raise NoImplemented, "Non supported `struct.field += foo`."
497
+ end
498
+
499
+ # assignment with && operator
500
+ # format: [nd_head] &&= [nd_value]
501
+ # example: foo &&= bar
502
+ def NODE_OP_ASGN_AND(node, opt = {})
503
+ node.children.then { |head, op, value|
504
+ # MEMO: implement `foo &&= bar` to `(foo = (foo && bar))`
505
+ # but, AST is not equal
506
+ # value_ = value.children.then { |left, right|
507
+ # "(#{unparse(left, opt)} && #{unparse(right, opt)})"
508
+ # }
509
+ # "(#{unparse(head)} = #{value_})"
510
+ value.children.then { |left, right|
511
+ "(#{unparse(left, opt)} &&= #{unparse(right, opt)})"
512
+ }
513
+ }
514
+ end
515
+
516
+ # assignment with || operator
517
+ # format: [nd_head] ||= [nd_value]
518
+ # example: foo ||= bar
519
+ def NODE_OP_ASGN_OR(node, opt = {})
520
+ node.children.then { |head, op, value|
521
+ value.children.then { |left, right|
522
+ "(#{unparse(left, opt)} ||= #{unparse(right, opt)})"
523
+ }
524
+ }
525
+ end
526
+
527
+ # constant declaration with operator
528
+ # format: [nd_head](constant) [nd_aid]= [nd_value]
529
+ # example: A::B ||= 1
530
+ def NODE_OP_CDECL(node, opt = {})
531
+ node.children.then { |head, aid, value|
532
+ "(#{unparse(head, opt)} #{aid}= #{unparse(value, opt)})"
533
+ }
534
+ end
535
+
536
+ # method invocation
537
+ # format: [nd_recv].[nd_mid]([nd_args])
538
+ # example: obj.foo(1)
539
+ def NODE_CALL(node, opt = {})
540
+ node.children.then { |receiver, mid, args|
541
+ "#{unparse(receiver, opt)}.#{unparse(mid, opt)}(#{unparse(args, opt.merge(NODE_ARRAY_EXPAND: true))})"
542
+ }
543
+ end
544
+
545
+ # method invocation
546
+ # format: [nd_recv] [nd_mid] [nd_args]
547
+ # example: foo + bar
548
+ def NODE_OPCALL(node, opt = {})
549
+ node.children.then { |left, op, right|
550
+ # Support !hoge
551
+ if right == nil
552
+ "(#{op.to_s.delete_suffix("@")}#{unparse(left, opt)})"
553
+ else
554
+ "(#{unparse(left, opt)} #{op} #{unparse(right, opt.merge(NODE_ARRAY_EXPAND: true))})"
555
+ end
556
+ }
557
+ end
558
+
559
+ # function call
560
+ # format: [nd_mid]([nd_args])
561
+ # example: foo(1)
562
+ def NODE_FCALL(node, opt = {})
563
+ node.children.then { |mid, args|
564
+ # Support `self[key]`
565
+ if mid == :[]
566
+ "self[#{unparse(args, opt.merge(NODE_ARRAY_EXPAND: true))}]"
567
+ else
568
+ "#{unparse(mid, opt)}(#{unparse(args, opt.merge(NODE_ARRAY_EXPAND: true))})"
569
+ end
570
+ }
571
+ end
572
+
573
+ # function call with no argument
574
+ # format: [nd_mid]
575
+ # example: foo
576
+ def NODE_VCALL(node, opt = {})
577
+ node.children.first.to_s
578
+ end
579
+
580
+ # safe method invocation
581
+ # format: [nd_recv]&.[nd_mid]([nd_args])
582
+ # example: obj&.foo(1)
583
+ def NODE_QCALL(node, opt = {})
584
+ node.children.then { |receiver, mid, args|
585
+ "#{unparse(receiver, opt)}&.#{unparse(mid, opt)}(#{unparse(args, opt.merge(NODE_ARRAY_EXPAND: true))})"
586
+ }
587
+ end
588
+
589
+ # super invocation
590
+ # format: super [nd_args]
591
+ # example: super 1
592
+ def NODE_SUPER(node, opt = {})
593
+ node.children.then { |args, |
594
+ "super(#{unparse(args, opt.merge(NODE_ARRAY_EXPAND: true))})"
595
+ }
596
+ end
597
+
598
+ # super invocation with no argument
599
+ # format: super
600
+ # example: super
601
+ def NODE_ZSUPER(node, opt = {})
602
+ "super"
603
+ end
604
+
605
+ # list constructor
606
+ # format: [ [nd_head], [nd_next].. ] (length: [nd_alen])
607
+ # example: [1, 2, 3]
608
+ def NODE_ARRAY(node, opt = {})
609
+ node.children.then { |*args, _nil|
610
+ if opt[:NODE_ARRAY_EXPAND]
611
+ "#{args.map(&_unparse(opt.except(:NODE_ARRAY_EXPAND))).join(", ")}"
612
+ else
613
+ "[#{args.map(&_unparse(opt)).join(", ")}]"
614
+ end
615
+ }
616
+ end
617
+
618
+ # return arguments
619
+ # format: [ [nd_head], [nd_next].. ] (length: [nd_alen])
620
+ # example: return 1, 2, 3
621
+ def NODE_VALUES(node, opt = {})
622
+ node.children[0..-2].map(&_unparse(opt)).join(", ")
623
+ end
624
+
625
+ # empty list constructor
626
+ # format: []
627
+ # example: []
628
+ def NODE_ZARRAY(node, opt = {})
629
+ "[]"
630
+ end
631
+
632
+ # keyword arguments
633
+ # format: nd_head
634
+ # example: a: 1, b: 2
635
+ # or
636
+ # hash constructor
637
+ # format: { [nd_head] }
638
+ # example: { 1 => 2, 3 => 4 }
639
+ def NODE_HASH(node, opt = {})
640
+ node.children.then { |head,|
641
+ if head.nil?
642
+ "{}"
643
+ # Support `foo(**kwd)`
644
+ elsif head.children.first.nil?
645
+ "**#{unparse(head.children[1], opt)}"
646
+ else
647
+ "{ #{
648
+ (head).children[0..-2].map(&_unparse(opt)).each_slice(2).map { |key, value|
649
+ "#{key} => #{value}"
650
+ }.join(", ")
651
+ } }"
652
+ end
653
+ }
654
+ end
655
+
656
+ # yield invocation
657
+ # format: yield [nd_head]
658
+ # example: yield 1
659
+ def NODE_YIELD(node, opt = {})
660
+ node.children.then { |head,|
661
+ "yield(#{unparse(head, opt.merge(NODE_ARRAY_EXPAND: true))})"
662
+ }
663
+ end
664
+
665
+ # local variable reference
666
+ # format: [nd_vid](lvar)
667
+ # example: x
668
+ def NODE_LVAR(node, opt = {})
669
+ node.children.first.to_s
670
+ end
671
+
672
+ # dynamic variable reference
673
+ # format: [nd_vid](dvar)
674
+ # example: 1.times { x = 1; x }
675
+ def NODE_DVAR(node, opt = {})
676
+ node.children.then { |vid, dvar|
677
+ vid.to_s
678
+ }
679
+ end
680
+
681
+ # instance variable reference
682
+ # format: [nd_vid](ivar)
683
+ # example: @x
684
+ def NODE_IVAR(node, opt = {})
685
+ node.children.then { |vid,|
686
+ vid.to_s
687
+ }
688
+ end
689
+
690
+ # constant reference
691
+ # format: [nd_vid](constant)
692
+ # example: X
693
+ def NODE_CONST(node, opt = {})
694
+ node.children.then { |vid,|
695
+ "#{vid}"
696
+ }
697
+ end
698
+
699
+ # class variable reference
700
+ # format: [nd_vid](cvar)
701
+ # example: @@x
702
+ def NODE_CVAR(node, opt = {})
703
+ node.children.then { |vid,|
704
+ vid.to_s
705
+ }
706
+ end
707
+
708
+ # global variable reference
709
+ # format: [nd_entry](gvar)
710
+ # example: $x
711
+ def NODE_GVAR(node, opt = {})
712
+ node.children.then { |vid,|
713
+ vid.to_s
714
+ }
715
+ end
716
+
717
+ # nth special variable reference
718
+ # format: $[nd_nth]
719
+ # example: $1, $2, ..
720
+ def NODE_NTH_REF(node, opt = {})
721
+ node.children.then { |vid,|
722
+ vid.to_s
723
+ }
724
+ end
725
+
726
+ # back special variable reference
727
+ # format: $[nd_nth]
728
+ # example: $&, $`, $', $+
729
+ def NODE_BACK_REF(node, opt = {})
730
+ node.children.then { |vid,|
731
+ vid.to_s
732
+ }
733
+ end
734
+
735
+ # match expression (against $_ implicitly)
736
+ # format: [nd_lit] (in condition)
737
+ # example: if /foo/; foo; end
738
+ def NODE_MATCH(node, opt = {})
739
+ node.children.then { |lit,|
740
+ lit.inspect
741
+ }
742
+ end
743
+
744
+ # match expression (regexp first)
745
+ # format: [nd_recv] =~ [nd_value]
746
+ # example: /foo/ =~ 'foo'
747
+ def NODE_MATCH2(node, opt = {})
748
+ node.children.then { |recv, value|
749
+ "#{unparse(recv, opt)} =~ #{unparse(value, opt)}"
750
+ }
751
+ end
752
+
753
+ # match expression (regexp second)
754
+ # format: [nd_recv] =~ [nd_value]
755
+ # example: 'foo' =~ /foo/
756
+ def NODE_MATCH3(node, opt = {})
757
+ node.children.then { |recv, value|
758
+ "#{unparse(value, opt)} =~ #{unparse(recv, opt)}"
759
+ }
760
+ end
761
+
762
+ # literal
763
+ # format: [nd_lit]
764
+ # example: 1, /foo/
765
+ def NODE_LIT(node, opt = {})
766
+ node.children.first.inspect
767
+ end
768
+
769
+ # string literal
770
+ # format: [nd_lit]
771
+ # example: 'foo'
772
+ def NODE_STR(node, opt = {})
773
+ node.children.first.dump
774
+ if opt.delete(:NODE_STR_IGNORE_QUOTE)
775
+ node.children.first.to_s.escape
776
+ else
777
+ node.children.first.dump
778
+ end
779
+ end
780
+
781
+ # xstring literal
782
+ # format: [nd_lit]
783
+ # example: `foo`
784
+ def NODE_XSTR(node, opt = {})
785
+ node.children.then { |lit,|
786
+ "`#{lit.to_s}`"
787
+ }
788
+ end
789
+
790
+ # once evaluation
791
+ # format: [nd_body]
792
+ # example: /foo#{ bar }baz/o
793
+ def NODE_ONCE(node, opt = {})
794
+ "#{NODE_DREGX(node.children.first, opt)}o"
795
+ end
796
+
797
+ # string literal with interpolation
798
+ # format: [nd_lit]
799
+ # example: \"foo#{ bar }baz\"
800
+ def NODE_DSTR(node, opt = {})
801
+ "\"#{_NODE_DSTR_without_quote(node, opt)}\""
802
+ end
803
+ def _NODE_DSTR_without_quote(node, opt)
804
+ node.children.then { |prefix, lit, suffix|
805
+ suffix_ = suffix&.children&.compact&.map { |it|
806
+ if it.type == :STR
807
+ unparse(it, opt.merge(NODE_STR_IGNORE_QUOTE: true))
808
+ else
809
+ unparse(it, opt.merge(NODE_STR_IGNORE_QUOTE: false))
810
+ end
811
+ }&.join
812
+ "#{prefix&.escape}#{unparse(lit, opt)}#{suffix_}"
813
+ }
814
+ end
815
+
816
+ # xstring literal with interpolation
817
+ # format: [nd_lit]
818
+ # example: `foo#{ bar }baz`
819
+ def NODE_DXSTR(node, opt = {})
820
+ "`#{_NODE_DSTR_without_quote(node, opt)}`"
821
+ end
822
+
823
+ # regexp literal with interpolation
824
+ # format: [nd_lit]
825
+ # example: /foo#{ bar }baz/
826
+ def NODE_DREGX(node, opt = {})
827
+ node.children.then { |prefix, lit, suffix|
828
+ suffix_ = suffix&.children&.compact&.map { |it|
829
+ unparse(it, opt).then do |it|
830
+ it.undump
831
+ rescue
832
+ it
833
+ end
834
+ }&.join
835
+ "/#{prefix}#{unparse(lit, opt)}#{suffix_}/"
836
+ }
837
+ end
838
+
839
+ # symbol literal with interpolation
840
+ # format: [nd_lit]
841
+ # example: :\"foo#{ bar }baz\"
842
+ def NODE_DSYM(node, opt = {})
843
+ ":#{NODE_DSTR(node, opt)}"
844
+ end
845
+
846
+ # interpolation expression
847
+ # format: \"..#{ [nd_lit] }..\"
848
+ # example: \"foo#{ bar }baz\"
849
+ def NODE_EVSTR(node, opt = {})
850
+ node.children.then { |lit,|
851
+ "\#{#{unparse(lit, opt)}}"
852
+ }
853
+ end
854
+
855
+ # splat argument following arguments
856
+ # format: ..(*[nd_head], [nd_body..])
857
+ # example: foo(*ary, post_arg1, post_arg2)
858
+ def NODE_ARGSCAT(node, opt = {})
859
+ node.children.then { |head, body|
860
+ if body.type == :ARRAY
861
+ "#{unparse(head, opt)}, #{unparse(body, opt)}"
862
+ else
863
+ "#{unparse(head, opt)}, *#{unparse(body, opt)}"
864
+ end
865
+ }
866
+ end
867
+
868
+ # splat argument following one argument
869
+ # format: ..(*[nd_head], [nd_body])
870
+ # example: foo(*ary, post_arg)
871
+ def NODE_ARGSPUSH(node, opt = {})
872
+ node.children.then { |head, body|
873
+ "#{unparse(head, opt)}, #{unparse(body, opt)}"
874
+ }
875
+ end
876
+
877
+ # splat argument
878
+ # format: *[nd_head]
879
+ # example: foo(*ary)
880
+ def NODE_SPLAT(node, opt = {})
881
+ node.children.then { |head,|
882
+ "*#{unparse(head, opt.merge(NODE_ARRAY_EXPAND: false))}"
883
+ }
884
+ end
885
+
886
+ # arguments with block argument
887
+ # format: ..([nd_head], &[nd_body])
888
+ # example: foo(x, &blk)
889
+ def NODE_BLOCK_PASS(node, opt = {})
890
+ node.children.then { |head, body|
891
+ "#{head&.then { |it| "#{unparse(it, opt)}, " }}&#{unparse(body, opt)}"
892
+ }
893
+ end
894
+
895
+ # method definition
896
+ # format: def [nd_mid] [nd_defn]; end
897
+ # example: def foo; bar; end
898
+ def NODE_DEFN(node, opt = {})
899
+ node.children.then { |mid, defn|
900
+ info = unparse_NODE_SCOPE(defn, opt)
901
+ <<~EOS.chomp
902
+ def #{mid}(#{info[:args]})
903
+ #{info[:body]}
904
+ end
905
+ EOS
906
+ }
907
+ end
908
+
909
+ # singleton method definition
910
+ # format: def [nd_recv].[nd_mid] [nd_defn]; end
911
+ # example: def obj.foo; bar; end
912
+ def NODE_DEFS(node, opt = {})
913
+ node.children.then { |recv, mid, defn|
914
+ info = unparse_NODE_SCOPE(defn, opt)
915
+ <<~EOS.chomp
916
+ def #{unparse(recv, opt)}.#{mid}(#{info[:args]})
917
+ #{info[:body]}
918
+ end
919
+ EOS
920
+ }
921
+ end
922
+
923
+ # method alias statement
924
+ # format: alias [nd_1st] [nd_2nd]
925
+ # example: alias bar foo
926
+ def NODE_ALIAS(node, opt = {})
927
+ node.children.then { |nd_1st, nd_2nd|
928
+ "alias #{unparse(nd_1st, opt)} #{unparse(nd_2nd, opt)}"
929
+ }
930
+ end
931
+
932
+ # global variable alias statement
933
+ # format: alias [nd_alias](gvar) [nd_orig](gvar)
934
+ # example: alias $y $x
935
+ def NODE_VALIAS(node, opt = {})
936
+ node.children.then { |nd_1st, nd_2nd|
937
+ "alias #{nd_1st} #{nd_2nd}"
938
+ }
939
+ end
940
+
941
+ # method undef statement
942
+ # format: undef [nd_undef]
943
+ # example: undef foo
944
+ def NODE_UNDEF(node, opt = {})
945
+ node.children.then { |nd_undef,|
946
+ "undef #{unparse(nd_undef, opt)}"
947
+ }
948
+ end
949
+
950
+ # class definition
951
+ # format: class [nd_cpath] < [nd_super]; [nd_body]; end
952
+ # example: class C2 < C; ..; end
953
+ def NODE_CLASS(node, opt = {})
954
+ node.children.then { |cpath, super_, body|
955
+ <<~EOS.chomp
956
+ class #{unparse(cpath, opt)}#{super_&.then { |it| " < #{unparse(it, opt)}" } }
957
+ #{unparse(body, opt.merge(NODE_BEGIN_WITHOUT_BEGIN: true))}
958
+ end
959
+ EOS
960
+ }
961
+ end
962
+
963
+ # module definition
964
+ # format: module [nd_cpath]; [nd_body]; end
965
+ # example: module M; ..; end
966
+ def NODE_MODULE(node, opt = {})
967
+ node.children.then { |cpath, body|
968
+ <<~EOS.chomp
969
+ module #{unparse(cpath, opt)}
970
+ #{unparse(body, opt.merge(NODE_BEGIN_WITHOUT_BEGIN: true))}
971
+ end
972
+ EOS
973
+ }
974
+ end
975
+
976
+ # singleton class definition
977
+ # format: class << [nd_recv]; [nd_body]; end
978
+ # example: class << obj; ..; end
979
+ def NODE_SCLASS(node, opt = {})
980
+ node.children.then { |recv, body|
981
+ <<~EOS.chomp
982
+ class << #{unparse(recv, opt)}
983
+ #{unparse(body, opt.merge(NODE_BEGIN_WITHOUT_BEGIN: true))}
984
+ end
985
+ EOS
986
+ }
987
+ end
988
+
989
+ # scoped constant reference
990
+ # format: [nd_head]::[nd_mid]
991
+ # example: M::C
992
+ def NODE_COLON2(node, opt = {})
993
+ node.children.then { |head, mid|
994
+ "#{unparse(head, opt)&.then { |it| "#{unparse(head, opt)}::" }}#{mid}"
995
+ }
996
+ end
997
+
998
+ # top-level constant reference
999
+ # format: ::[nd_mid]
1000
+ # example: ::object
1001
+ def NODE_COLON3(node, opt = {})
1002
+ node.children.then { |mid,|
1003
+ "::#{mid}"
1004
+ }
1005
+ end
1006
+
1007
+ # range constructor (incl.)
1008
+ # format: [nd_beg]..[nd_end]
1009
+ # example: 1..5
1010
+ def NODE_DOT2(node, opt = {})
1011
+ node.children.then { |beg, end_|
1012
+ "(#{unparse(beg, opt)}..#{unparse(end_, opt)})"
1013
+ }
1014
+ end
1015
+
1016
+ # range constructor (excl.)
1017
+ # format: [nd_beg]...[nd_end]
1018
+ # example: 1...5
1019
+ def NODE_DOT3(node, opt = {})
1020
+ node.children.then { |beg, end_|
1021
+ "(#{unparse(beg, opt)}...#{unparse(end_, opt)})"
1022
+ }
1023
+ end
1024
+
1025
+ # flip-flop condition (incl.)
1026
+ # format: [nd_beg]..[nd_end]
1027
+ # example: if (x==1)..(x==5); foo; end
1028
+ def NODE_FLIP2(node, opt = {})
1029
+ node.children.then { |beg, end_|
1030
+ "(#{unparse(beg, opt)})..(#{unparse(end_, opt)})"
1031
+ }
1032
+ end
1033
+
1034
+ # flip-flop condition (excl.)
1035
+ # format: [nd_beg]...[nd_end]
1036
+ # example: if (x==1)...(x==5); foo; end
1037
+ def NODE_FLIP3(node, opt = {})
1038
+ node.children.then { |beg, end_|
1039
+ "(#{unparse(beg, opt)})...(#{unparse(end_, opt)})"
1040
+ }
1041
+ end
1042
+
1043
+ # self
1044
+ # format: self
1045
+ # example: self
1046
+ def NODE_SELF(*)
1047
+ "self"
1048
+ end
1049
+
1050
+ # nil
1051
+ # format: nil
1052
+ # example: nil
1053
+ def NODE_NIL(*)
1054
+ "nil"
1055
+ end
1056
+
1057
+ # true
1058
+ # format: true
1059
+ # example: true
1060
+ def NODE_TRUE(*)
1061
+ "true"
1062
+ end
1063
+
1064
+ # false
1065
+ # format: false
1066
+ # example: false
1067
+ def NODE_FALSE(*)
1068
+ "false"
1069
+ end
1070
+
1071
+ # virtual reference to $!
1072
+ # format: rescue => id
1073
+ # example: rescue => id
1074
+ def NODE_ERRINFO(node, opt = {})
1075
+ "rescue"
1076
+ end
1077
+
1078
+ # defined? expression
1079
+ # format: defined?([nd_head])
1080
+ # example: defined?(foo)
1081
+ def NODE_DEFINED(node, opt = {})
1082
+ node.children.then { |head,|
1083
+ "defined?(#{unparse(head, opt)})"
1084
+ }
1085
+ end
1086
+
1087
+ # post-execution
1088
+ # format: END { [nd_body] }
1089
+ # example: END { foo }
1090
+ def NODE_POSTEXE(node, opt = {})
1091
+ node.children.then { |body,|
1092
+ "END { #{unparse(body, opt)} }"
1093
+ }
1094
+ end
1095
+
1096
+ # attr assignment
1097
+ # format: [nd_recv].[nd_mid] = [nd_args]
1098
+ # example: struct.field = foo
1099
+ def NODE_ATTRASGN(node, opt = {})
1100
+ node.children.then { |recv, mid, args|
1101
+ if mid == :[]=
1102
+ *args_, right, _ = args.children
1103
+ "#{unparse(recv, opt)}[#{args_.map(&_unparse(opt.merge(NODE_ARRAY_EXPAND: true))).join(", ")}] = #{unparse(right, opt.merge(NODE_ARRAY_EXPAND: true))}"
1104
+ else
1105
+ "#{unparse(recv, opt)}.#{mid}#{unparse(args, opt.merge(NODE_ARRAY_EXPAND: true))}"
1106
+ end
1107
+ }
1108
+ end
1109
+
1110
+ # lambda expression
1111
+ # format: -> [nd_body]
1112
+ # example: -> { foo }
1113
+ def NODE_LAMBDA(node, opt = {})
1114
+ node.children.then { |scope,|
1115
+ result = unparse_NODE_SCOPE(scope, opt)
1116
+ "-> (#{result[:args]}) { #{result[:body]} }"
1117
+ }
1118
+ end
1119
+
1120
+ # optional arguments
1121
+ # format: def method_name([nd_body=some], [nd_next..])
1122
+ # example: def foo(a, b=1, c); end
1123
+ def NODE_OPT_ARG(node, opt = {})
1124
+ node.children.map(&_unparse(opt.merge(NODE_DASGN_CURR_EXPAND: true, NODE_LASGN_EXPAND: true))).compact.join(", ")
1125
+ end
1126
+ def unparse_NODE_OPT_ARG(node, opt = {})
1127
+ node.children.then { |head, children|
1128
+ [unparse(head, opt)] + (children ? unparse_NODE_OPT_ARG(children, opt) : [])
1129
+ }
1130
+ end
1131
+
1132
+ # keyword arguments
1133
+ # format: def method_name([nd_body=some], [nd_next..])
1134
+ # example: def foo(a:1, b:2); end
1135
+ def NODE_KW_ARG(node, opt = {})
1136
+ node.children[0..-1].map(&_unparse(opt.merge(KW_ARG: true))).compact.join(", ")
1137
+ end
1138
+ def unparse_NODE_KW_ARG(node, opt = {})
1139
+ node.children.then { |head, children|
1140
+ [unparse(head, opt)] + (children ? unparse_NODE_KW_ARG(children, opt) : [])
1141
+ }
1142
+ end
1143
+
1144
+ # post arguments
1145
+ # format: *[nd_1st], [nd_2nd..] = ..
1146
+ # example: a, *rest, z = foo
1147
+ def NODE_POSTARG(node, opt = {})
1148
+ node.children.then { |_1st, _2nd|
1149
+ "#{unparse(_1st, opt)}, #{unparse(_2nd, opt.merge(NODE_ARRAY_EXPAND: true))}"
1150
+ }
1151
+ end
1152
+
1153
+ # method parameters
1154
+ # format: def method_name(.., [nd_opt=some], *[nd_rest], [nd_pid], .., &[nd_body])
1155
+ # example: def foo(a, b, opt1=1, opt2=2, *rest, y, z, &blk); end
1156
+ def NODE_ARGS(node, opt_ = {})
1157
+ (
1158
+ pre_num,
1159
+ pre_init,
1160
+ opt,
1161
+ first_post,
1162
+ post_num,
1163
+ post_init,
1164
+ rest,
1165
+ kw,
1166
+ kwrest,
1167
+ block
1168
+ ) = node.children
1169
+ "#{unparse(opt, opt_)}#{unparse(kw, opt_)}"
1170
+ end
1171
+ def unparse_NODE_ARGS(node, opt = {})
1172
+ %i(
1173
+ pre_num
1174
+ pre_init
1175
+ opt
1176
+ first_post
1177
+ post_num
1178
+ post_init
1179
+ rest
1180
+ kw
1181
+ kwrest
1182
+ block
1183
+ ).map.with_index { |key, i| [key, node.children[i]] }.to_h.then { |info|
1184
+ info.merge(
1185
+ unparsed_pre_init: info[:pre_init]&.then { |node|
1186
+ node.type == :BLOCK ? node.children.map(&_unparse(opt)) : [unparse(node, opt)]
1187
+ } || [],
1188
+ unparsed_post_init: info[:post_init]&.then { |node|
1189
+ node.type == :BLOCK ? node.children.map(&_unparse(opt)) : [unparse(node, opt)]
1190
+ } || [],
1191
+ unparsed_opt: info[:opt]&.then { |it| unparse_NODE_OPT_ARG(it, opt.merge(NODE_DASGN_CURR_EXPAND: true, NODE_LASGN_EXPAND: true)) } || [],
1192
+ unparsed_rest: unparse(info[:rest], opt)&.then { |it|
1193
+ if it == :NODE_SPECIAL_EXCESSIVE_COMMA
1194
+ [" "]
1195
+ else
1196
+ ["*#{it}"]
1197
+ end
1198
+ } || [],
1199
+ unparsed_kw: info[:kw]&.then { |it| unparse_NODE_KW_ARG(it, opt.merge(NODE_DASGN_CURR_EXPAND: true, NODE_LASGN_EXPAND: true, KW_ARG: true)) } || [],
1200
+ unparsed_kwrest: info[:kwrest]&.then { |it| "**#{unparse(it, opt)}" },
1201
+ )
1202
+ }
1203
+ end
1204
+
1205
+ # new scope
1206
+ # format: [nd_tbl]: local table, [nd_args]: arguments, [nd_body]: body
1207
+ def NODE_SCOPE(node, opt = {})
1208
+ result = unparse_NODE_SCOPE(node, opt)
1209
+ if result[:args].empty?
1210
+ "#{result[:body]}"
1211
+ elsif opt[:ITER]
1212
+ "|#{result[:args]}| #{result[:body]}"
1213
+ else
1214
+ "|#{result[:args]}| #{result[:body]}"
1215
+ end
1216
+ end
1217
+ def unparse_NODE_SCOPE(node, opt = {})
1218
+ node.children.then { |tbl, args, body|
1219
+ break { args: "", body: unparse(body, opt) } if args.nil?
1220
+
1221
+ # info = unparse_NODE_ARGS(args, opt)
1222
+ info = unparse_NODE_ARGS(args, opt.merge(_NODE_MASGN: true))
1223
+
1224
+ # Support proc { |**| }
1225
+ break { args: "**", body: unparse(body, opt) } if tbl == [nil] && info[:kwrest]
1226
+
1227
+ pre_args = []
1228
+ [info[:pre_num], info[:unparsed_pre_init]&.size || 0].max.times {
1229
+ tbl.shift.then { |it|
1230
+ pre_args << (it ? it : info[:unparsed_pre_init].shift)
1231
+ }
1232
+ }
1233
+
1234
+ # skip to opt args
1235
+ # e.g. a = 1, b = 2
1236
+ tbl = tbl.drop(info[:unparsed_opt].count)
1237
+
1238
+ # skip to rest args
1239
+ # e.g. *a
1240
+ tbl = tbl.drop(info[:unparsed_rest].count)
1241
+
1242
+ star = nil
1243
+ tbl.take_while(&:nil?).tap { |nils|
1244
+ if info[:unparsed_post_init].count < nils.count
1245
+ star = "*"
1246
+ tbl = tbl.drop(1)
1247
+ end
1248
+ }
1249
+
1250
+ post_args = []
1251
+ [info[:post_num], info[:unparsed_post_init]&.size || 0].max.times {
1252
+ tbl.shift.then { |it|
1253
+ post_args << (it ? it : info[:unparsed_post_init].shift)
1254
+ }
1255
+ }
1256
+
1257
+ # skip to kw args
1258
+ # e.g. a:, b: c: 1
1259
+ tbl = tbl.drop(info[:unparsed_kw].count.tap { |count| break count + 1 if count != 0 })
1260
+
1261
+ if info[:unparsed_kwrest] == "**" && tbl.fetch(0, 1) != nil
1262
+ kwrest = ""
1263
+ else
1264
+ kwrest = info[:unparsed_kwrest]
1265
+ end
1266
+
1267
+ params = [
1268
+ pre_args,
1269
+ info[:unparsed_opt].join(", "),
1270
+ info[:unparsed_rest].join(", "),
1271
+ star,
1272
+ post_args,
1273
+ info[:unparsed_post_init].join(", "),
1274
+ info[:unparsed_kw].join(", "),
1275
+ kwrest,
1276
+ info[:block]&.then { |str| "&#{str}" },
1277
+ ].compact.reject(&:empty?).join(", ")
1278
+
1279
+ { args: params, body: unparse(body, opt) }
1280
+ }
1281
+ end
1282
+
1283
+ def NODE_ARGS_AUX(node, opt = {})
1284
+ ""
1285
+ end
1286
+
1287
+ def NODE_LAST(node, opt = {})
1288
+ ""
1289
+ end
1290
+ end
1291
+
1292
+ module Ruby2_6_0
1293
+ include Base
1294
+ end
1295
+
1296
+ module Ruby2_7_0
1297
+ include Ruby2_6_0
1298
+
1299
+ private
1300
+
1301
+ # case statement (pattern matching)
1302
+ # format: case [nd_head]; [nd_body]; end
1303
+ # example: case x; in 1; foo; in 2; bar; else baz; end
1304
+ def NODE_CASE3(node, opt = {})
1305
+ # Ruby 2.7
1306
+ # :TODO:
1307
+ node
1308
+ end
1309
+
1310
+ # list constructor
1311
+ # format: [ [nd_head], [nd_next].. ] (length: [nd_alen])
1312
+ # example: [1, 2, 3]
1313
+ def NODE_LIST(node, opt = {})
1314
+ NODE_ARRAY(node, opt)
1315
+ end
1316
+
1317
+ def NODE_ZLIST(node, opt = {})
1318
+ NODE_ZARRAY(node, opt)
1319
+ end
1320
+
1321
+ def NODE_IN(node, opt = {})
1322
+ # TODO
1323
+ end
1324
+
1325
+ # splat argument following arguments
1326
+ # format: ..(*[nd_head], [nd_body..])
1327
+ # example: foo(*ary, post_arg1, post_arg2)
1328
+ def NODE_ARGSCAT(node, opt = {})
1329
+ node.children.then { |head, body|
1330
+ if body.type == :LIST
1331
+ "#{unparse(head, opt)}, #{unparse(body, opt)}"
1332
+ else
1333
+ "#{unparse(head, opt)}, *#{unparse(body, opt)}"
1334
+ end
1335
+ }
1336
+ end
1337
+
1338
+ # array pattern
1339
+ # format: [nd_pconst]([pre_args], ..., *[rest_arg], [post_args], ...)
1340
+ def NODE_ARYPTN(node, opt = {})
1341
+ # :TODO:
1342
+ node
1343
+ end
1344
+
1345
+ def NODE_HSHPTN(node, opt = {})
1346
+ # :TODO:
1347
+ node
1348
+ end
1349
+ end
1350
+
1351
+ module Ruby3_0_0
1352
+ include Ruby2_7_0
1353
+
1354
+ # attr assignment with operator
1355
+ # format: [nd_recv].[attr] [nd_next->nd_mid]= [nd_value]
1356
+ # where [attr]: [nd_next->nd_vid]
1357
+ # example: struct.field += foo
1358
+ def NODE_OP_ASGN2(node, opt = {})
1359
+ node.children.then { |recv, _, attr, op, mid|
1360
+ "#{unparse(recv, opt)}.#{attr} #{op}= #{unparse(mid, opt)}"
1361
+ }
1362
+ end
1363
+
1364
+ # string literal with interpolation
1365
+ # format: [nd_lit]
1366
+ # example: \"foo#{ bar }baz\"
1367
+ def NODE_DSTR(node, opt = {})
1368
+ node.children.then { |prefix, lit, suffix|
1369
+ # Add support `"foo#{ "hoge" }baz"`
1370
+ if lit.nil? && suffix.nil?
1371
+ "\"\#{#{prefix.dump}\}\""
1372
+ else
1373
+ super
1374
+ end
1375
+ }
1376
+ end
1377
+ end
1378
+
1379
+ case RUBY_VERSION
1380
+ when ("2.6.0"..."2.7.0")
1381
+ VERSION = Ruby2_6_0
1382
+ when ("2.7.0"..."3.0.0")
1383
+ VERSION = Ruby2_7_0
1384
+ when ("3.0.0"...)
1385
+ VERSION = Ruby3_0_0
1386
+ else
1387
+ railse "Not implemented Ruby version #{RUBY_VERSION}"
1388
+ end
1389
+
1390
+ class Caller
1391
+ include VERSION
1392
+ end
1393
+
1394
+ def self.unparse(node)
1395
+ Caller.new.unparse(node)
1396
+ end
1397
+ end
1398
+ end