rensei 0.1.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.
@@ -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