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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +54 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rensei.rb +11 -0
- data/lib/rensei/node_to_hash.rb +54 -0
- data/lib/rensei/unparser.rb +1398 -0
- data/lib/rensei/version.rb +3 -0
- data/rensei.gemspec +23 -0
- data/sample/bocchi.rb +55 -0
- data/sample/sample.rb +12 -0
- metadata +59 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -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__)
|
data/bin/setup
ADDED
data/lib/rensei.rb
ADDED
@@ -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
|