syntax_tree 5.3.0 → 6.0.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 +4 -4
- data/.rubocop.yml +12 -1
- data/CHANGELOG.md +64 -1
- data/Gemfile.lock +2 -2
- data/README.md +28 -9
- data/Rakefile +12 -8
- data/bin/console +1 -0
- data/bin/whitequark +79 -0
- data/doc/changing_structure.md +16 -0
- data/lib/syntax_tree/basic_visitor.rb +44 -5
- data/lib/syntax_tree/cli.rb +2 -2
- data/lib/syntax_tree/dsl.rb +23 -11
- data/lib/syntax_tree/{visitor/field_visitor.rb → field_visitor.rb} +54 -55
- data/lib/syntax_tree/formatter.rb +1 -1
- data/lib/syntax_tree/index.rb +56 -54
- data/lib/syntax_tree/json_visitor.rb +55 -0
- data/lib/syntax_tree/language_server.rb +157 -2
- data/lib/syntax_tree/match_visitor.rb +120 -0
- data/lib/syntax_tree/mermaid.rb +177 -0
- data/lib/syntax_tree/mermaid_visitor.rb +69 -0
- data/lib/syntax_tree/{visitor/mutation_visitor.rb → mutation_visitor.rb} +27 -27
- data/lib/syntax_tree/node.rb +198 -107
- data/lib/syntax_tree/parser.rb +322 -118
- data/lib/syntax_tree/pretty_print_visitor.rb +83 -0
- data/lib/syntax_tree/reflection.rb +241 -0
- data/lib/syntax_tree/translation/parser.rb +3019 -0
- data/lib/syntax_tree/translation/rubocop_ast.rb +21 -0
- data/lib/syntax_tree/translation.rb +28 -0
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/with_scope.rb +244 -0
- data/lib/syntax_tree/yarv/basic_block.rb +53 -0
- data/lib/syntax_tree/yarv/calldata.rb +91 -0
- data/lib/syntax_tree/yarv/compiler.rb +110 -100
- data/lib/syntax_tree/yarv/control_flow_graph.rb +257 -0
- data/lib/syntax_tree/yarv/data_flow_graph.rb +338 -0
- data/lib/syntax_tree/yarv/decompiler.rb +1 -1
- data/lib/syntax_tree/yarv/disassembler.rb +104 -80
- data/lib/syntax_tree/yarv/instruction_sequence.rb +43 -18
- data/lib/syntax_tree/yarv/instructions.rb +203 -649
- data/lib/syntax_tree/yarv/legacy.rb +12 -24
- data/lib/syntax_tree/yarv/sea_of_nodes.rb +534 -0
- data/lib/syntax_tree/yarv.rb +18 -0
- data/lib/syntax_tree.rb +88 -56
- data/tasks/sorbet.rake +277 -0
- data/tasks/whitequark.rake +87 -0
- metadata +23 -11
- data/.gitmodules +0 -9
- data/lib/syntax_tree/language_server/inlay_hints.rb +0 -159
- data/lib/syntax_tree/visitor/environment.rb +0 -84
- data/lib/syntax_tree/visitor/json_visitor.rb +0 -55
- data/lib/syntax_tree/visitor/match_visitor.rb +0 -122
- data/lib/syntax_tree/visitor/pretty_print_visitor.rb +0 -85
- data/lib/syntax_tree/visitor/with_environment.rb +0 -140
@@ -0,0 +1,3019 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SyntaxTree
|
4
|
+
module Translation
|
5
|
+
# This visitor is responsible for converting the syntax tree produced by
|
6
|
+
# Syntax Tree into the syntax tree produced by the whitequark/parser gem.
|
7
|
+
class Parser < BasicVisitor
|
8
|
+
# Heredocs are represented _very_ differently in the parser gem from how
|
9
|
+
# they are represented in the Syntax Tree AST. This class is responsible
|
10
|
+
# for handling the translation.
|
11
|
+
class HeredocBuilder
|
12
|
+
Line = Struct.new(:value, :segments)
|
13
|
+
|
14
|
+
attr_reader :node, :segments
|
15
|
+
|
16
|
+
def initialize(node)
|
17
|
+
@node = node
|
18
|
+
@segments = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def <<(segment)
|
22
|
+
if segment.type == :str && segments.last &&
|
23
|
+
segments.last.type == :str &&
|
24
|
+
!segments.last.children.first.end_with?("\n")
|
25
|
+
segments.last.children.first << segment.children.first
|
26
|
+
else
|
27
|
+
segments << segment
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def trim!
|
32
|
+
return unless node.beginning.value[2] == "~"
|
33
|
+
lines = [Line.new(+"", [])]
|
34
|
+
|
35
|
+
segments.each do |segment|
|
36
|
+
lines.last.segments << segment
|
37
|
+
|
38
|
+
if segment.type == :str
|
39
|
+
lines.last.value << segment.children.first
|
40
|
+
lines << Line.new(+"", []) if lines.last.value.end_with?("\n")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
lines.pop if lines.last.value.empty?
|
45
|
+
return if lines.empty?
|
46
|
+
|
47
|
+
segments.clear
|
48
|
+
lines.each do |line|
|
49
|
+
remaining = node.dedent
|
50
|
+
|
51
|
+
line.segments.each do |segment|
|
52
|
+
if segment.type == :str
|
53
|
+
if remaining > 0
|
54
|
+
whitespace = segment.children.first[/^\s{0,#{remaining}}/]
|
55
|
+
segment.children.first.sub!(/^#{whitespace}/, "")
|
56
|
+
remaining -= whitespace.length
|
57
|
+
end
|
58
|
+
|
59
|
+
if node.beginning.value[3] != "'" && segments.any? &&
|
60
|
+
segments.last.type == :str &&
|
61
|
+
segments.last.children.first.end_with?("\\\n")
|
62
|
+
segments.last.children.first.gsub!(/\\\n\z/, "")
|
63
|
+
segments.last.children.first.concat(segment.children.first)
|
64
|
+
elsif !segment.children.first.empty?
|
65
|
+
segments << segment
|
66
|
+
end
|
67
|
+
else
|
68
|
+
segments << segment
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
attr_reader :buffer, :stack
|
76
|
+
|
77
|
+
def initialize(buffer)
|
78
|
+
@buffer = buffer
|
79
|
+
@stack = []
|
80
|
+
end
|
81
|
+
|
82
|
+
# For each node that we visit, we keep track of it in a stack as we
|
83
|
+
# descend into its children. We do this so that child nodes can reflect on
|
84
|
+
# their parents if they need additional information about their context.
|
85
|
+
def visit(node)
|
86
|
+
stack << node
|
87
|
+
result = super
|
88
|
+
stack.pop
|
89
|
+
result
|
90
|
+
end
|
91
|
+
|
92
|
+
visit_methods do
|
93
|
+
# Visit an AliasNode node.
|
94
|
+
def visit_alias(node)
|
95
|
+
s(
|
96
|
+
:alias,
|
97
|
+
[visit(node.left), visit(node.right)],
|
98
|
+
smap_keyword_bare(
|
99
|
+
srange_length(node.start_char, 5),
|
100
|
+
srange_node(node)
|
101
|
+
)
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Visit an ARefNode.
|
106
|
+
def visit_aref(node)
|
107
|
+
if ::Parser::Builders::Default.emit_index
|
108
|
+
if node.index.nil?
|
109
|
+
s(
|
110
|
+
:index,
|
111
|
+
[visit(node.collection)],
|
112
|
+
smap_index(
|
113
|
+
srange_find(node.collection.end_char, node.end_char, "["),
|
114
|
+
srange_length(node.end_char, -1),
|
115
|
+
srange_node(node)
|
116
|
+
)
|
117
|
+
)
|
118
|
+
else
|
119
|
+
s(
|
120
|
+
:index,
|
121
|
+
[visit(node.collection)].concat(visit_all(node.index.parts)),
|
122
|
+
smap_index(
|
123
|
+
srange_find_between(node.collection, node.index, "["),
|
124
|
+
srange_length(node.end_char, -1),
|
125
|
+
srange_node(node)
|
126
|
+
)
|
127
|
+
)
|
128
|
+
end
|
129
|
+
else
|
130
|
+
if node.index.nil?
|
131
|
+
s(
|
132
|
+
:send,
|
133
|
+
[visit(node.collection), :[]],
|
134
|
+
smap_send_bare(
|
135
|
+
srange_find(node.collection.end_char, node.end_char, "[]"),
|
136
|
+
srange_node(node)
|
137
|
+
)
|
138
|
+
)
|
139
|
+
else
|
140
|
+
s(
|
141
|
+
:send,
|
142
|
+
[visit(node.collection), :[], *visit_all(node.index.parts)],
|
143
|
+
smap_send_bare(
|
144
|
+
srange(
|
145
|
+
srange_find_between(
|
146
|
+
node.collection,
|
147
|
+
node.index,
|
148
|
+
"["
|
149
|
+
).begin_pos,
|
150
|
+
node.end_char
|
151
|
+
),
|
152
|
+
srange_node(node)
|
153
|
+
)
|
154
|
+
)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Visit an ARefField node.
|
160
|
+
def visit_aref_field(node)
|
161
|
+
if ::Parser::Builders::Default.emit_index
|
162
|
+
if node.index.nil?
|
163
|
+
s(
|
164
|
+
:indexasgn,
|
165
|
+
[visit(node.collection)],
|
166
|
+
smap_index(
|
167
|
+
srange_find(node.collection.end_char, node.end_char, "["),
|
168
|
+
srange_length(node.end_char, -1),
|
169
|
+
srange_node(node)
|
170
|
+
)
|
171
|
+
)
|
172
|
+
else
|
173
|
+
s(
|
174
|
+
:indexasgn,
|
175
|
+
[visit(node.collection)].concat(visit_all(node.index.parts)),
|
176
|
+
smap_index(
|
177
|
+
srange_find_between(node.collection, node.index, "["),
|
178
|
+
srange_length(node.end_char, -1),
|
179
|
+
srange_node(node)
|
180
|
+
)
|
181
|
+
)
|
182
|
+
end
|
183
|
+
else
|
184
|
+
if node.index.nil?
|
185
|
+
s(
|
186
|
+
:send,
|
187
|
+
[visit(node.collection), :[]=],
|
188
|
+
smap_send_bare(
|
189
|
+
srange_find(node.collection.end_char, node.end_char, "[]"),
|
190
|
+
srange_node(node)
|
191
|
+
)
|
192
|
+
)
|
193
|
+
else
|
194
|
+
s(
|
195
|
+
:send,
|
196
|
+
[visit(node.collection), :[]=].concat(
|
197
|
+
visit_all(node.index.parts)
|
198
|
+
),
|
199
|
+
smap_send_bare(
|
200
|
+
srange(
|
201
|
+
srange_find_between(
|
202
|
+
node.collection,
|
203
|
+
node.index,
|
204
|
+
"["
|
205
|
+
).begin_pos,
|
206
|
+
node.end_char
|
207
|
+
),
|
208
|
+
srange_node(node)
|
209
|
+
)
|
210
|
+
)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Visit an ArgBlock node.
|
216
|
+
def visit_arg_block(node)
|
217
|
+
s(
|
218
|
+
:block_pass,
|
219
|
+
[visit(node.value)],
|
220
|
+
smap_operator(srange_length(node.start_char, 1), srange_node(node))
|
221
|
+
)
|
222
|
+
end
|
223
|
+
|
224
|
+
# Visit an ArgStar node.
|
225
|
+
def visit_arg_star(node)
|
226
|
+
if stack[-3].is_a?(MLHSParen) && stack[-3].contents.is_a?(MLHS)
|
227
|
+
if node.value.nil?
|
228
|
+
s(:restarg, [], smap_variable(nil, srange_node(node)))
|
229
|
+
else
|
230
|
+
s(
|
231
|
+
:restarg,
|
232
|
+
[node.value.value.to_sym],
|
233
|
+
smap_variable(srange_node(node.value), srange_node(node))
|
234
|
+
)
|
235
|
+
end
|
236
|
+
else
|
237
|
+
s(
|
238
|
+
:splat,
|
239
|
+
node.value.nil? ? [] : [visit(node.value)],
|
240
|
+
smap_operator(
|
241
|
+
srange_length(node.start_char, 1),
|
242
|
+
srange_node(node)
|
243
|
+
)
|
244
|
+
)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# Visit an ArgsForward node.
|
249
|
+
def visit_args_forward(node)
|
250
|
+
s(:forwarded_args, [], smap(srange_node(node)))
|
251
|
+
end
|
252
|
+
|
253
|
+
# Visit an ArrayLiteral node.
|
254
|
+
def visit_array(node)
|
255
|
+
s(
|
256
|
+
:array,
|
257
|
+
node.contents ? visit_all(node.contents.parts) : [],
|
258
|
+
if node.lbracket.nil?
|
259
|
+
smap_collection_bare(srange_node(node))
|
260
|
+
else
|
261
|
+
smap_collection(
|
262
|
+
srange_node(node.lbracket),
|
263
|
+
srange_length(node.end_char, -1),
|
264
|
+
srange_node(node)
|
265
|
+
)
|
266
|
+
end
|
267
|
+
)
|
268
|
+
end
|
269
|
+
|
270
|
+
# Visit an AryPtn node.
|
271
|
+
def visit_aryptn(node)
|
272
|
+
type = :array_pattern
|
273
|
+
children = visit_all(node.requireds)
|
274
|
+
|
275
|
+
if node.rest.is_a?(VarField)
|
276
|
+
if !node.rest.value.nil?
|
277
|
+
children << s(:match_rest, [visit(node.rest)], nil)
|
278
|
+
elsif node.posts.empty? &&
|
279
|
+
node.rest.start_char == node.rest.end_char
|
280
|
+
# Here we have an implicit rest, as in [foo,]. parser has a
|
281
|
+
# specific type for these patterns.
|
282
|
+
type = :array_pattern_with_tail
|
283
|
+
else
|
284
|
+
children << s(:match_rest, [], nil)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
if node.constant
|
289
|
+
s(
|
290
|
+
:const_pattern,
|
291
|
+
[
|
292
|
+
visit(node.constant),
|
293
|
+
s(
|
294
|
+
type,
|
295
|
+
children + visit_all(node.posts),
|
296
|
+
smap_collection_bare(
|
297
|
+
srange(node.constant.end_char + 1, node.end_char - 1)
|
298
|
+
)
|
299
|
+
)
|
300
|
+
],
|
301
|
+
smap_collection(
|
302
|
+
srange_length(node.constant.end_char, 1),
|
303
|
+
srange_length(node.end_char, -1),
|
304
|
+
srange_node(node)
|
305
|
+
)
|
306
|
+
)
|
307
|
+
else
|
308
|
+
s(
|
309
|
+
type,
|
310
|
+
children + visit_all(node.posts),
|
311
|
+
if buffer.source[node.start_char] == "["
|
312
|
+
smap_collection(
|
313
|
+
srange_length(node.start_char, 1),
|
314
|
+
srange_length(node.end_char, -1),
|
315
|
+
srange_node(node)
|
316
|
+
)
|
317
|
+
else
|
318
|
+
smap_collection_bare(srange_node(node))
|
319
|
+
end
|
320
|
+
)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
# Visit an Assign node.
|
325
|
+
def visit_assign(node)
|
326
|
+
target = visit(node.target)
|
327
|
+
location =
|
328
|
+
target
|
329
|
+
.location
|
330
|
+
.with_operator(srange_find_between(node.target, node.value, "="))
|
331
|
+
.with_expression(srange_node(node))
|
332
|
+
|
333
|
+
s(target.type, target.children + [visit(node.value)], location)
|
334
|
+
end
|
335
|
+
|
336
|
+
# Visit an Assoc node.
|
337
|
+
def visit_assoc(node)
|
338
|
+
if node.value.nil?
|
339
|
+
expression = srange(node.start_char, node.end_char - 1)
|
340
|
+
|
341
|
+
type, location =
|
342
|
+
if node.key.value.start_with?(/[A-Z]/)
|
343
|
+
[:const, smap_constant(nil, expression, expression)]
|
344
|
+
else
|
345
|
+
[:send, smap_send_bare(expression, expression)]
|
346
|
+
end
|
347
|
+
|
348
|
+
s(
|
349
|
+
:pair,
|
350
|
+
[
|
351
|
+
visit(node.key),
|
352
|
+
s(type, [nil, node.key.value.chomp(":").to_sym], location)
|
353
|
+
],
|
354
|
+
smap_operator(
|
355
|
+
srange_length(node.key.end_char, -1),
|
356
|
+
srange_node(node)
|
357
|
+
)
|
358
|
+
)
|
359
|
+
else
|
360
|
+
s(
|
361
|
+
:pair,
|
362
|
+
[visit(node.key), visit(node.value)],
|
363
|
+
smap_operator(
|
364
|
+
srange_search_between(node.key, node.value, "=>") ||
|
365
|
+
srange_length(node.key.end_char, -1),
|
366
|
+
srange_node(node)
|
367
|
+
)
|
368
|
+
)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# Visit an AssocSplat node.
|
373
|
+
def visit_assoc_splat(node)
|
374
|
+
s(
|
375
|
+
:kwsplat,
|
376
|
+
[visit(node.value)],
|
377
|
+
smap_operator(srange_length(node.start_char, 2), srange_node(node))
|
378
|
+
)
|
379
|
+
end
|
380
|
+
|
381
|
+
# Visit a Backref node.
|
382
|
+
def visit_backref(node)
|
383
|
+
location = smap(srange_node(node))
|
384
|
+
|
385
|
+
if node.value.match?(/^\$\d+$/)
|
386
|
+
s(:nth_ref, [node.value[1..].to_i], location)
|
387
|
+
else
|
388
|
+
s(:back_ref, [node.value.to_sym], location)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
# Visit a BareAssocHash node.
|
393
|
+
def visit_bare_assoc_hash(node)
|
394
|
+
s(
|
395
|
+
if ::Parser::Builders::Default.emit_kwargs &&
|
396
|
+
!stack[-2].is_a?(ArrayLiteral)
|
397
|
+
:kwargs
|
398
|
+
else
|
399
|
+
:hash
|
400
|
+
end,
|
401
|
+
visit_all(node.assocs),
|
402
|
+
smap_collection_bare(srange_node(node))
|
403
|
+
)
|
404
|
+
end
|
405
|
+
|
406
|
+
# Visit a BEGINBlock node.
|
407
|
+
def visit_BEGIN(node)
|
408
|
+
s(
|
409
|
+
:preexe,
|
410
|
+
[visit(node.statements)],
|
411
|
+
smap_keyword(
|
412
|
+
srange_length(node.start_char, 5),
|
413
|
+
srange_find(node.start_char + 5, node.statements.start_char, "{"),
|
414
|
+
srange_length(node.end_char, -1),
|
415
|
+
srange_node(node)
|
416
|
+
)
|
417
|
+
)
|
418
|
+
end
|
419
|
+
|
420
|
+
# Visit a Begin node.
|
421
|
+
def visit_begin(node)
|
422
|
+
location =
|
423
|
+
smap_collection(
|
424
|
+
srange_length(node.start_char, 5),
|
425
|
+
srange_length(node.end_char, -3),
|
426
|
+
srange_node(node)
|
427
|
+
)
|
428
|
+
|
429
|
+
if node.bodystmt.empty?
|
430
|
+
s(:kwbegin, [], location)
|
431
|
+
elsif node.bodystmt.rescue_clause.nil? &&
|
432
|
+
node.bodystmt.ensure_clause.nil? &&
|
433
|
+
node.bodystmt.else_clause.nil?
|
434
|
+
child = visit(node.bodystmt.statements)
|
435
|
+
|
436
|
+
s(
|
437
|
+
:kwbegin,
|
438
|
+
child.type == :begin ? child.children : [child],
|
439
|
+
location
|
440
|
+
)
|
441
|
+
else
|
442
|
+
s(:kwbegin, [visit(node.bodystmt)], location)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
# Visit a Binary node.
|
447
|
+
def visit_binary(node)
|
448
|
+
case node.operator
|
449
|
+
when :|
|
450
|
+
current = -2
|
451
|
+
while stack[current].is_a?(Binary) && stack[current].operator == :|
|
452
|
+
current -= 1
|
453
|
+
end
|
454
|
+
|
455
|
+
if stack[current].is_a?(In)
|
456
|
+
s(:match_alt, [visit(node.left), visit(node.right)], nil)
|
457
|
+
else
|
458
|
+
visit(canonical_binary(node))
|
459
|
+
end
|
460
|
+
when :"=>", :"&&", :and, :"||", :or
|
461
|
+
s(
|
462
|
+
{ "=>": :match_as, "&&": :and, "||": :or }.fetch(
|
463
|
+
node.operator,
|
464
|
+
node.operator
|
465
|
+
),
|
466
|
+
[visit(node.left), visit(node.right)],
|
467
|
+
smap_operator(
|
468
|
+
srange_find_between(node.left, node.right, node.operator.to_s),
|
469
|
+
srange_node(node)
|
470
|
+
)
|
471
|
+
)
|
472
|
+
when :=~
|
473
|
+
# When you use a regular expression on the left hand side of a =~
|
474
|
+
# operator and it doesn't have interpolatoin, then its named capture
|
475
|
+
# groups introduce local variables into the scope. In this case the
|
476
|
+
# parser gem has a different node (match_with_lvasgn) instead of the
|
477
|
+
# regular send.
|
478
|
+
if node.left.is_a?(RegexpLiteral) && node.left.parts.length == 1 &&
|
479
|
+
node.left.parts.first.is_a?(TStringContent)
|
480
|
+
s(
|
481
|
+
:match_with_lvasgn,
|
482
|
+
[visit(node.left), visit(node.right)],
|
483
|
+
smap_operator(
|
484
|
+
srange_find_between(
|
485
|
+
node.left,
|
486
|
+
node.right,
|
487
|
+
node.operator.to_s
|
488
|
+
),
|
489
|
+
srange_node(node)
|
490
|
+
)
|
491
|
+
)
|
492
|
+
else
|
493
|
+
visit(canonical_binary(node))
|
494
|
+
end
|
495
|
+
else
|
496
|
+
visit(canonical_binary(node))
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
# Visit a BlockArg node.
|
501
|
+
def visit_blockarg(node)
|
502
|
+
if node.name.nil?
|
503
|
+
s(:blockarg, [nil], smap_variable(nil, srange_node(node)))
|
504
|
+
else
|
505
|
+
s(
|
506
|
+
:blockarg,
|
507
|
+
[node.name.value.to_sym],
|
508
|
+
smap_variable(srange_node(node.name), srange_node(node))
|
509
|
+
)
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
# Visit a BlockVar node.
|
514
|
+
def visit_block_var(node)
|
515
|
+
shadowargs =
|
516
|
+
node.locals.map do |local|
|
517
|
+
s(
|
518
|
+
:shadowarg,
|
519
|
+
[local.value.to_sym],
|
520
|
+
smap_variable(srange_node(local), srange_node(local))
|
521
|
+
)
|
522
|
+
end
|
523
|
+
|
524
|
+
params = node.params
|
525
|
+
children =
|
526
|
+
if ::Parser::Builders::Default.emit_procarg0 && node.arg0?
|
527
|
+
# There is a special node type in the parser gem for when a single
|
528
|
+
# required parameter to a block would potentially be expanded
|
529
|
+
# automatically. We handle that case here.
|
530
|
+
required = params.requireds.first
|
531
|
+
procarg0 =
|
532
|
+
if ::Parser::Builders::Default.emit_arg_inside_procarg0 &&
|
533
|
+
required.is_a?(Ident)
|
534
|
+
s(
|
535
|
+
:procarg0,
|
536
|
+
[
|
537
|
+
s(
|
538
|
+
:arg,
|
539
|
+
[required.value.to_sym],
|
540
|
+
smap_variable(
|
541
|
+
srange_node(required),
|
542
|
+
srange_node(required)
|
543
|
+
)
|
544
|
+
)
|
545
|
+
],
|
546
|
+
smap_collection_bare(srange_node(required))
|
547
|
+
)
|
548
|
+
else
|
549
|
+
child = visit(required)
|
550
|
+
s(:procarg0, child, child.location)
|
551
|
+
end
|
552
|
+
|
553
|
+
[procarg0]
|
554
|
+
else
|
555
|
+
visit(params).children
|
556
|
+
end
|
557
|
+
|
558
|
+
s(
|
559
|
+
:args,
|
560
|
+
children + shadowargs,
|
561
|
+
smap_collection(
|
562
|
+
srange_length(node.start_char, 1),
|
563
|
+
srange_length(node.end_char, -1),
|
564
|
+
srange_node(node)
|
565
|
+
)
|
566
|
+
)
|
567
|
+
end
|
568
|
+
|
569
|
+
# Visit a BodyStmt node.
|
570
|
+
def visit_bodystmt(node)
|
571
|
+
result = visit(node.statements)
|
572
|
+
|
573
|
+
if node.rescue_clause
|
574
|
+
rescue_node = visit(node.rescue_clause)
|
575
|
+
|
576
|
+
children = [result] + rescue_node.children
|
577
|
+
location = rescue_node.location
|
578
|
+
|
579
|
+
if node.else_clause
|
580
|
+
children.pop
|
581
|
+
children << visit(node.else_clause)
|
582
|
+
|
583
|
+
location =
|
584
|
+
smap_condition(
|
585
|
+
nil,
|
586
|
+
nil,
|
587
|
+
srange_length(node.else_clause.start_char - 3, -4),
|
588
|
+
nil,
|
589
|
+
srange(
|
590
|
+
location.expression.begin_pos,
|
591
|
+
node.else_clause.end_char
|
592
|
+
)
|
593
|
+
)
|
594
|
+
end
|
595
|
+
|
596
|
+
result = s(rescue_node.type, children, location)
|
597
|
+
end
|
598
|
+
|
599
|
+
if node.ensure_clause
|
600
|
+
ensure_node = visit(node.ensure_clause)
|
601
|
+
|
602
|
+
expression =
|
603
|
+
(
|
604
|
+
if result
|
605
|
+
result.location.expression.join(
|
606
|
+
ensure_node.location.expression
|
607
|
+
)
|
608
|
+
else
|
609
|
+
ensure_node.location.expression
|
610
|
+
end
|
611
|
+
)
|
612
|
+
location = ensure_node.location.with_expression(expression)
|
613
|
+
|
614
|
+
result =
|
615
|
+
s(ensure_node.type, [result] + ensure_node.children, location)
|
616
|
+
end
|
617
|
+
|
618
|
+
result
|
619
|
+
end
|
620
|
+
|
621
|
+
# Visit a Break node.
|
622
|
+
def visit_break(node)
|
623
|
+
s(
|
624
|
+
:break,
|
625
|
+
visit_all(node.arguments.parts),
|
626
|
+
smap_keyword_bare(
|
627
|
+
srange_length(node.start_char, 5),
|
628
|
+
srange_node(node)
|
629
|
+
)
|
630
|
+
)
|
631
|
+
end
|
632
|
+
|
633
|
+
# Visit a CallNode node.
|
634
|
+
def visit_call(node)
|
635
|
+
visit_command_call(
|
636
|
+
CommandCall.new(
|
637
|
+
receiver: node.receiver,
|
638
|
+
operator: node.operator,
|
639
|
+
message: node.message,
|
640
|
+
arguments: node.arguments,
|
641
|
+
block: nil,
|
642
|
+
location: node.location
|
643
|
+
)
|
644
|
+
)
|
645
|
+
end
|
646
|
+
|
647
|
+
# Visit a Case node.
|
648
|
+
def visit_case(node)
|
649
|
+
clauses = [node.consequent]
|
650
|
+
while clauses.last && !clauses.last.is_a?(Else)
|
651
|
+
clauses << clauses.last.consequent
|
652
|
+
end
|
653
|
+
|
654
|
+
else_token =
|
655
|
+
if clauses.last.is_a?(Else)
|
656
|
+
srange_length(clauses.last.start_char, 4)
|
657
|
+
end
|
658
|
+
|
659
|
+
s(
|
660
|
+
node.consequent.is_a?(In) ? :case_match : :case,
|
661
|
+
[visit(node.value)] + clauses.map { |clause| visit(clause) },
|
662
|
+
smap_condition(
|
663
|
+
srange_length(node.start_char, 4),
|
664
|
+
nil,
|
665
|
+
else_token,
|
666
|
+
srange_length(node.end_char, -3),
|
667
|
+
srange_node(node)
|
668
|
+
)
|
669
|
+
)
|
670
|
+
end
|
671
|
+
|
672
|
+
# Visit a CHAR node.
|
673
|
+
def visit_CHAR(node)
|
674
|
+
s(
|
675
|
+
:str,
|
676
|
+
[node.value[1..]],
|
677
|
+
smap_collection(
|
678
|
+
srange_length(node.start_char, 1),
|
679
|
+
nil,
|
680
|
+
srange_node(node)
|
681
|
+
)
|
682
|
+
)
|
683
|
+
end
|
684
|
+
|
685
|
+
# Visit a ClassDeclaration node.
|
686
|
+
def visit_class(node)
|
687
|
+
operator =
|
688
|
+
if node.superclass
|
689
|
+
srange_find_between(node.constant, node.superclass, "<")
|
690
|
+
end
|
691
|
+
|
692
|
+
s(
|
693
|
+
:class,
|
694
|
+
[
|
695
|
+
visit(node.constant),
|
696
|
+
visit(node.superclass),
|
697
|
+
visit(node.bodystmt)
|
698
|
+
],
|
699
|
+
smap_definition(
|
700
|
+
srange_length(node.start_char, 5),
|
701
|
+
operator,
|
702
|
+
srange_node(node.constant),
|
703
|
+
srange_length(node.end_char, -3)
|
704
|
+
).with_expression(srange_node(node))
|
705
|
+
)
|
706
|
+
end
|
707
|
+
|
708
|
+
# Visit a Command node.
|
709
|
+
def visit_command(node)
|
710
|
+
visit_command_call(
|
711
|
+
CommandCall.new(
|
712
|
+
receiver: nil,
|
713
|
+
operator: nil,
|
714
|
+
message: node.message,
|
715
|
+
arguments: node.arguments,
|
716
|
+
block: node.block,
|
717
|
+
location: node.location
|
718
|
+
)
|
719
|
+
)
|
720
|
+
end
|
721
|
+
|
722
|
+
# Visit a CommandCall node.
|
723
|
+
def visit_command_call(node)
|
724
|
+
children = [
|
725
|
+
visit(node.receiver),
|
726
|
+
node.message == :call ? :call : node.message.value.to_sym
|
727
|
+
]
|
728
|
+
|
729
|
+
begin_token = nil
|
730
|
+
end_token = nil
|
731
|
+
|
732
|
+
case node.arguments
|
733
|
+
when Args
|
734
|
+
children += visit_all(node.arguments.parts)
|
735
|
+
when ArgParen
|
736
|
+
case node.arguments.arguments
|
737
|
+
when nil
|
738
|
+
# skip
|
739
|
+
when ArgsForward
|
740
|
+
children << visit(node.arguments.arguments)
|
741
|
+
else
|
742
|
+
children += visit_all(node.arguments.arguments.parts)
|
743
|
+
end
|
744
|
+
|
745
|
+
begin_token = srange_length(node.arguments.start_char, 1)
|
746
|
+
end_token = srange_length(node.arguments.end_char, -1)
|
747
|
+
end
|
748
|
+
|
749
|
+
dot_bound =
|
750
|
+
if node.arguments
|
751
|
+
node.arguments.start_char
|
752
|
+
elsif node.block
|
753
|
+
node.block.start_char
|
754
|
+
else
|
755
|
+
node.end_char
|
756
|
+
end
|
757
|
+
|
758
|
+
expression =
|
759
|
+
if node.arguments.is_a?(ArgParen)
|
760
|
+
srange(node.start_char, node.arguments.end_char)
|
761
|
+
elsif node.arguments.is_a?(Args) && node.arguments.parts.any?
|
762
|
+
last_part = node.arguments.parts.last
|
763
|
+
end_char =
|
764
|
+
if last_part.is_a?(Heredoc)
|
765
|
+
last_part.beginning.end_char
|
766
|
+
else
|
767
|
+
last_part.end_char
|
768
|
+
end
|
769
|
+
|
770
|
+
srange(node.start_char, end_char)
|
771
|
+
elsif node.block
|
772
|
+
srange_node(node.message)
|
773
|
+
else
|
774
|
+
srange_node(node)
|
775
|
+
end
|
776
|
+
|
777
|
+
call =
|
778
|
+
s(
|
779
|
+
if node.operator.is_a?(Op) && node.operator.value == "&."
|
780
|
+
:csend
|
781
|
+
else
|
782
|
+
:send
|
783
|
+
end,
|
784
|
+
children,
|
785
|
+
smap_send(
|
786
|
+
if node.operator == :"::"
|
787
|
+
srange_find(
|
788
|
+
node.receiver.end_char,
|
789
|
+
if node.message == :call
|
790
|
+
dot_bound
|
791
|
+
else
|
792
|
+
node.message.start_char
|
793
|
+
end,
|
794
|
+
"::"
|
795
|
+
)
|
796
|
+
elsif node.operator
|
797
|
+
srange_node(node.operator)
|
798
|
+
end,
|
799
|
+
node.message == :call ? nil : srange_node(node.message),
|
800
|
+
begin_token,
|
801
|
+
end_token,
|
802
|
+
expression
|
803
|
+
)
|
804
|
+
)
|
805
|
+
|
806
|
+
if node.block
|
807
|
+
type, arguments = block_children(node.block)
|
808
|
+
|
809
|
+
s(
|
810
|
+
type,
|
811
|
+
[call, arguments, visit(node.block.bodystmt)],
|
812
|
+
smap_collection(
|
813
|
+
srange_node(node.block.opening),
|
814
|
+
srange_length(
|
815
|
+
node.end_char,
|
816
|
+
node.block.opening.is_a?(Kw) ? -3 : -1
|
817
|
+
),
|
818
|
+
srange_node(node)
|
819
|
+
)
|
820
|
+
)
|
821
|
+
else
|
822
|
+
call
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
# Visit a Const node.
|
827
|
+
def visit_const(node)
|
828
|
+
s(
|
829
|
+
:const,
|
830
|
+
[nil, node.value.to_sym],
|
831
|
+
smap_constant(nil, srange_node(node), srange_node(node))
|
832
|
+
)
|
833
|
+
end
|
834
|
+
|
835
|
+
# Visit a ConstPathField node.
|
836
|
+
def visit_const_path_field(node)
|
837
|
+
if node.parent.is_a?(VarRef) && node.parent.value.is_a?(Kw) &&
|
838
|
+
node.parent.value.value == "self" && node.constant.is_a?(Ident)
|
839
|
+
s(:send, [visit(node.parent), :"#{node.constant.value}="], nil)
|
840
|
+
else
|
841
|
+
s(
|
842
|
+
:casgn,
|
843
|
+
[visit(node.parent), node.constant.value.to_sym],
|
844
|
+
smap_constant(
|
845
|
+
srange_find_between(node.parent, node.constant, "::"),
|
846
|
+
srange_node(node.constant),
|
847
|
+
srange_node(node)
|
848
|
+
)
|
849
|
+
)
|
850
|
+
end
|
851
|
+
end
|
852
|
+
|
853
|
+
# Visit a ConstPathRef node.
|
854
|
+
def visit_const_path_ref(node)
|
855
|
+
s(
|
856
|
+
:const,
|
857
|
+
[visit(node.parent), node.constant.value.to_sym],
|
858
|
+
smap_constant(
|
859
|
+
srange_find_between(node.parent, node.constant, "::"),
|
860
|
+
srange_node(node.constant),
|
861
|
+
srange_node(node)
|
862
|
+
)
|
863
|
+
)
|
864
|
+
end
|
865
|
+
|
866
|
+
# Visit a ConstRef node.
|
867
|
+
def visit_const_ref(node)
|
868
|
+
s(
|
869
|
+
:const,
|
870
|
+
[nil, node.constant.value.to_sym],
|
871
|
+
smap_constant(nil, srange_node(node.constant), srange_node(node))
|
872
|
+
)
|
873
|
+
end
|
874
|
+
|
875
|
+
# Visit a CVar node.
|
876
|
+
def visit_cvar(node)
|
877
|
+
s(
|
878
|
+
:cvar,
|
879
|
+
[node.value.to_sym],
|
880
|
+
smap_variable(srange_node(node), srange_node(node))
|
881
|
+
)
|
882
|
+
end
|
883
|
+
|
884
|
+
# Visit a DefNode node.
|
885
|
+
def visit_def(node)
|
886
|
+
name = node.name.value.to_sym
|
887
|
+
args =
|
888
|
+
case node.params
|
889
|
+
when Params
|
890
|
+
child = visit(node.params)
|
891
|
+
|
892
|
+
s(
|
893
|
+
child.type,
|
894
|
+
child.children,
|
895
|
+
smap_collection_bare(child.location&.expression)
|
896
|
+
)
|
897
|
+
when Paren
|
898
|
+
child = visit(node.params.contents)
|
899
|
+
|
900
|
+
s(
|
901
|
+
child.type,
|
902
|
+
child.children,
|
903
|
+
smap_collection(
|
904
|
+
srange_length(node.params.start_char, 1),
|
905
|
+
srange_length(node.params.end_char, -1),
|
906
|
+
srange_node(node.params)
|
907
|
+
)
|
908
|
+
)
|
909
|
+
else
|
910
|
+
s(:args, [], smap_collection_bare(nil))
|
911
|
+
end
|
912
|
+
|
913
|
+
location =
|
914
|
+
if node.endless?
|
915
|
+
smap_method_definition(
|
916
|
+
srange_length(node.start_char, 3),
|
917
|
+
nil,
|
918
|
+
srange_node(node.name),
|
919
|
+
nil,
|
920
|
+
srange_find_between(
|
921
|
+
(node.params || node.name),
|
922
|
+
node.bodystmt,
|
923
|
+
"="
|
924
|
+
),
|
925
|
+
srange_node(node)
|
926
|
+
)
|
927
|
+
else
|
928
|
+
smap_method_definition(
|
929
|
+
srange_length(node.start_char, 3),
|
930
|
+
nil,
|
931
|
+
srange_node(node.name),
|
932
|
+
srange_length(node.end_char, -3),
|
933
|
+
nil,
|
934
|
+
srange_node(node)
|
935
|
+
)
|
936
|
+
end
|
937
|
+
|
938
|
+
if node.target
|
939
|
+
target =
|
940
|
+
node.target.is_a?(Paren) ? node.target.contents : node.target
|
941
|
+
|
942
|
+
s(
|
943
|
+
:defs,
|
944
|
+
[visit(target), name, args, visit(node.bodystmt)],
|
945
|
+
smap_method_definition(
|
946
|
+
location.keyword,
|
947
|
+
srange_node(node.operator),
|
948
|
+
location.name,
|
949
|
+
location.end,
|
950
|
+
location.assignment,
|
951
|
+
location.expression
|
952
|
+
)
|
953
|
+
)
|
954
|
+
else
|
955
|
+
s(:def, [name, args, visit(node.bodystmt)], location)
|
956
|
+
end
|
957
|
+
end
|
958
|
+
|
959
|
+
# Visit a Defined node.
|
960
|
+
def visit_defined(node)
|
961
|
+
paren_range = (node.start_char + 8)...node.end_char
|
962
|
+
begin_token, end_token =
|
963
|
+
if buffer.source[paren_range].include?("(")
|
964
|
+
[
|
965
|
+
srange_find(paren_range.begin, paren_range.end, "("),
|
966
|
+
srange_length(node.end_char, -1)
|
967
|
+
]
|
968
|
+
end
|
969
|
+
|
970
|
+
s(
|
971
|
+
:defined?,
|
972
|
+
[visit(node.value)],
|
973
|
+
smap_keyword(
|
974
|
+
srange_length(node.start_char, 8),
|
975
|
+
begin_token,
|
976
|
+
end_token,
|
977
|
+
srange_node(node)
|
978
|
+
)
|
979
|
+
)
|
980
|
+
end
|
981
|
+
|
982
|
+
# Visit a DynaSymbol node.
|
983
|
+
def visit_dyna_symbol(node)
|
984
|
+
location =
|
985
|
+
if node.quote
|
986
|
+
smap_collection(
|
987
|
+
srange_length(node.start_char, node.quote.length),
|
988
|
+
srange_length(node.end_char, -1),
|
989
|
+
srange_node(node)
|
990
|
+
)
|
991
|
+
else
|
992
|
+
smap_collection_bare(srange_node(node))
|
993
|
+
end
|
994
|
+
|
995
|
+
if node.parts.length == 1 && node.parts.first.is_a?(TStringContent)
|
996
|
+
s(:sym, ["\"#{node.parts.first.value}\"".undump.to_sym], location)
|
997
|
+
else
|
998
|
+
s(:dsym, visit_all(node.parts), location)
|
999
|
+
end
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
# Visit an Else node.
|
1003
|
+
def visit_else(node)
|
1004
|
+
if node.statements.empty? && stack[-2].is_a?(Case)
|
1005
|
+
s(:empty_else, [], nil)
|
1006
|
+
else
|
1007
|
+
visit(node.statements)
|
1008
|
+
end
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
# Visit an Elsif node.
|
1012
|
+
def visit_elsif(node)
|
1013
|
+
else_token =
|
1014
|
+
case node.consequent
|
1015
|
+
when Elsif
|
1016
|
+
srange_length(node.consequent.start_char, 5)
|
1017
|
+
when Else
|
1018
|
+
srange_length(node.consequent.start_char, 4)
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
expression = srange(node.start_char, node.statements.end_char - 1)
|
1022
|
+
|
1023
|
+
s(
|
1024
|
+
:if,
|
1025
|
+
[
|
1026
|
+
visit(node.predicate),
|
1027
|
+
visit(node.statements),
|
1028
|
+
visit(node.consequent)
|
1029
|
+
],
|
1030
|
+
smap_condition(
|
1031
|
+
srange_length(node.start_char, 5),
|
1032
|
+
nil,
|
1033
|
+
else_token,
|
1034
|
+
nil,
|
1035
|
+
expression
|
1036
|
+
)
|
1037
|
+
)
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
# Visit an ENDBlock node.
|
1041
|
+
def visit_END(node)
|
1042
|
+
s(
|
1043
|
+
:postexe,
|
1044
|
+
[visit(node.statements)],
|
1045
|
+
smap_keyword(
|
1046
|
+
srange_length(node.start_char, 3),
|
1047
|
+
srange_find(node.start_char + 3, node.statements.start_char, "{"),
|
1048
|
+
srange_length(node.end_char, -1),
|
1049
|
+
srange_node(node)
|
1050
|
+
)
|
1051
|
+
)
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
# Visit an Ensure node.
|
1055
|
+
def visit_ensure(node)
|
1056
|
+
start_char = node.start_char
|
1057
|
+
end_char =
|
1058
|
+
if node.statements.empty?
|
1059
|
+
start_char + 6
|
1060
|
+
else
|
1061
|
+
node.statements.body.last.end_char
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
s(
|
1065
|
+
:ensure,
|
1066
|
+
[visit(node.statements)],
|
1067
|
+
smap_condition(
|
1068
|
+
srange_length(start_char, 6),
|
1069
|
+
nil,
|
1070
|
+
nil,
|
1071
|
+
nil,
|
1072
|
+
srange(start_char, end_char)
|
1073
|
+
)
|
1074
|
+
)
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
# Visit a Field node.
|
1078
|
+
def visit_field(node)
|
1079
|
+
message =
|
1080
|
+
case stack[-2]
|
1081
|
+
when Assign, MLHS
|
1082
|
+
Ident.new(
|
1083
|
+
value: "#{node.name.value}=",
|
1084
|
+
location: node.name.location
|
1085
|
+
)
|
1086
|
+
else
|
1087
|
+
node.name
|
1088
|
+
end
|
1089
|
+
|
1090
|
+
visit_command_call(
|
1091
|
+
CommandCall.new(
|
1092
|
+
receiver: node.parent,
|
1093
|
+
operator: node.operator,
|
1094
|
+
message: message,
|
1095
|
+
arguments: nil,
|
1096
|
+
block: nil,
|
1097
|
+
location: node.location
|
1098
|
+
)
|
1099
|
+
)
|
1100
|
+
end
|
1101
|
+
|
1102
|
+
# Visit a FloatLiteral node.
|
1103
|
+
def visit_float(node)
|
1104
|
+
operator =
|
1105
|
+
if %w[+ -].include?(buffer.source[node.start_char])
|
1106
|
+
srange_length(node.start_char, 1)
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
s(
|
1110
|
+
:float,
|
1111
|
+
[node.value.to_f],
|
1112
|
+
smap_operator(operator, srange_node(node))
|
1113
|
+
)
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
# Visit a FndPtn node.
|
1117
|
+
def visit_fndptn(node)
|
1118
|
+
left, right =
|
1119
|
+
[node.left, node.right].map do |child|
|
1120
|
+
location =
|
1121
|
+
smap_operator(
|
1122
|
+
srange_length(child.start_char, 1),
|
1123
|
+
srange_node(child)
|
1124
|
+
)
|
1125
|
+
|
1126
|
+
if child.is_a?(VarField) && child.value.nil?
|
1127
|
+
s(:match_rest, [], location)
|
1128
|
+
else
|
1129
|
+
s(:match_rest, [visit(child)], location)
|
1130
|
+
end
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
inner =
|
1134
|
+
s(
|
1135
|
+
:find_pattern,
|
1136
|
+
[left, *visit_all(node.values), right],
|
1137
|
+
smap_collection(
|
1138
|
+
srange_length(node.start_char, 1),
|
1139
|
+
srange_length(node.end_char, -1),
|
1140
|
+
srange_node(node)
|
1141
|
+
)
|
1142
|
+
)
|
1143
|
+
|
1144
|
+
if node.constant
|
1145
|
+
s(:const_pattern, [visit(node.constant), inner], nil)
|
1146
|
+
else
|
1147
|
+
inner
|
1148
|
+
end
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
# Visit a For node.
|
1152
|
+
def visit_for(node)
|
1153
|
+
s(
|
1154
|
+
:for,
|
1155
|
+
[visit(node.index), visit(node.collection), visit(node.statements)],
|
1156
|
+
smap_for(
|
1157
|
+
srange_length(node.start_char, 3),
|
1158
|
+
srange_find_between(node.index, node.collection, "in"),
|
1159
|
+
srange_search_between(node.collection, node.statements, "do") ||
|
1160
|
+
srange_search_between(node.collection, node.statements, ";"),
|
1161
|
+
srange_length(node.end_char, -3),
|
1162
|
+
srange_node(node)
|
1163
|
+
)
|
1164
|
+
)
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
# Visit a GVar node.
|
1168
|
+
def visit_gvar(node)
|
1169
|
+
s(
|
1170
|
+
:gvar,
|
1171
|
+
[node.value.to_sym],
|
1172
|
+
smap_variable(srange_node(node), srange_node(node))
|
1173
|
+
)
|
1174
|
+
end
|
1175
|
+
|
1176
|
+
# Visit a HashLiteral node.
|
1177
|
+
def visit_hash(node)
|
1178
|
+
s(
|
1179
|
+
:hash,
|
1180
|
+
visit_all(node.assocs),
|
1181
|
+
smap_collection(
|
1182
|
+
srange_length(node.start_char, 1),
|
1183
|
+
srange_length(node.end_char, -1),
|
1184
|
+
srange_node(node)
|
1185
|
+
)
|
1186
|
+
)
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
# Visit a Heredoc node.
|
1190
|
+
def visit_heredoc(node)
|
1191
|
+
heredoc = HeredocBuilder.new(node)
|
1192
|
+
|
1193
|
+
# For each part of the heredoc, if it's a string content node, split
|
1194
|
+
# it into multiple string content nodes, one for each line. Otherwise,
|
1195
|
+
# visit the node as normal.
|
1196
|
+
node.parts.each do |part|
|
1197
|
+
if part.is_a?(TStringContent) && part.value.count("\n") > 1
|
1198
|
+
index = part.start_char
|
1199
|
+
lines = part.value.split("\n")
|
1200
|
+
|
1201
|
+
lines.each do |line|
|
1202
|
+
length = line.length + 1
|
1203
|
+
location = smap_collection_bare(srange_length(index, length))
|
1204
|
+
|
1205
|
+
heredoc << s(:str, ["#{line}\n"], location)
|
1206
|
+
index += length
|
1207
|
+
end
|
1208
|
+
else
|
1209
|
+
heredoc << visit(part)
|
1210
|
+
end
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
# Now that we have all of the pieces on the heredoc, we can trim it if
|
1214
|
+
# it is a heredoc that supports trimming (i.e., it has a ~ on the
|
1215
|
+
# declaration).
|
1216
|
+
heredoc.trim!
|
1217
|
+
|
1218
|
+
# Generate the location for the heredoc, which goes from the
|
1219
|
+
# declaration to the ending delimiter.
|
1220
|
+
location =
|
1221
|
+
smap_heredoc(
|
1222
|
+
srange_node(node.beginning),
|
1223
|
+
srange(
|
1224
|
+
if node.parts.empty?
|
1225
|
+
node.beginning.end_char + 1
|
1226
|
+
else
|
1227
|
+
node.parts.first.start_char
|
1228
|
+
end,
|
1229
|
+
node.ending.start_char
|
1230
|
+
),
|
1231
|
+
srange(node.ending.start_char, node.ending.end_char - 1)
|
1232
|
+
)
|
1233
|
+
|
1234
|
+
# Finally, decide which kind of heredoc node to generate based on its
|
1235
|
+
# declaration and contents.
|
1236
|
+
if node.beginning.value.match?(/`\w+`\z/)
|
1237
|
+
s(:xstr, heredoc.segments, location)
|
1238
|
+
elsif heredoc.segments.length == 1
|
1239
|
+
segment = heredoc.segments.first
|
1240
|
+
s(segment.type, segment.children, location)
|
1241
|
+
else
|
1242
|
+
s(:dstr, heredoc.segments, location)
|
1243
|
+
end
|
1244
|
+
end
|
1245
|
+
|
1246
|
+
# Visit a HshPtn node.
|
1247
|
+
def visit_hshptn(node)
|
1248
|
+
children =
|
1249
|
+
node.keywords.map do |(keyword, value)|
|
1250
|
+
next s(:pair, [visit(keyword), visit(value)], nil) if value
|
1251
|
+
|
1252
|
+
case keyword
|
1253
|
+
when DynaSymbol
|
1254
|
+
raise if keyword.parts.length > 1
|
1255
|
+
s(:match_var, [keyword.parts.first.value.to_sym], nil)
|
1256
|
+
when Label
|
1257
|
+
s(:match_var, [keyword.value.chomp(":").to_sym], nil)
|
1258
|
+
end
|
1259
|
+
end
|
1260
|
+
|
1261
|
+
if node.keyword_rest.is_a?(VarField)
|
1262
|
+
children << if node.keyword_rest.value.nil?
|
1263
|
+
s(:match_rest, [], nil)
|
1264
|
+
elsif node.keyword_rest.value == :nil
|
1265
|
+
s(:match_nil_pattern, [], nil)
|
1266
|
+
else
|
1267
|
+
s(:match_rest, [visit(node.keyword_rest)], nil)
|
1268
|
+
end
|
1269
|
+
end
|
1270
|
+
|
1271
|
+
inner = s(:hash_pattern, children, nil)
|
1272
|
+
if node.constant
|
1273
|
+
s(:const_pattern, [visit(node.constant), inner], nil)
|
1274
|
+
else
|
1275
|
+
inner
|
1276
|
+
end
|
1277
|
+
end
|
1278
|
+
|
1279
|
+
# Visit an Ident node.
|
1280
|
+
def visit_ident(node)
|
1281
|
+
s(
|
1282
|
+
:lvar,
|
1283
|
+
[node.value.to_sym],
|
1284
|
+
smap_variable(srange_node(node), srange_node(node))
|
1285
|
+
)
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
# Visit an IfNode node.
|
1289
|
+
def visit_if(node)
|
1290
|
+
predicate =
|
1291
|
+
case node.predicate
|
1292
|
+
when RangeNode
|
1293
|
+
type =
|
1294
|
+
node.predicate.operator.value == ".." ? :iflipflop : :eflipflop
|
1295
|
+
s(type, visit(node.predicate).children, nil)
|
1296
|
+
when RegexpLiteral
|
1297
|
+
s(:match_current_line, [visit(node.predicate)], nil)
|
1298
|
+
when Unary
|
1299
|
+
if node.predicate.operator.value == "!" &&
|
1300
|
+
node.predicate.statement.is_a?(RegexpLiteral)
|
1301
|
+
s(
|
1302
|
+
:send,
|
1303
|
+
[
|
1304
|
+
s(:match_current_line, [visit(node.predicate.statement)]),
|
1305
|
+
:!
|
1306
|
+
],
|
1307
|
+
nil
|
1308
|
+
)
|
1309
|
+
else
|
1310
|
+
visit(node.predicate)
|
1311
|
+
end
|
1312
|
+
else
|
1313
|
+
visit(node.predicate)
|
1314
|
+
end
|
1315
|
+
|
1316
|
+
s(
|
1317
|
+
:if,
|
1318
|
+
[predicate, visit(node.statements), visit(node.consequent)],
|
1319
|
+
if node.modifier?
|
1320
|
+
smap_keyword_bare(
|
1321
|
+
srange_find_between(node.statements, node.predicate, "if"),
|
1322
|
+
srange_node(node)
|
1323
|
+
)
|
1324
|
+
else
|
1325
|
+
begin_start = node.predicate.end_char
|
1326
|
+
begin_end =
|
1327
|
+
if node.statements.empty?
|
1328
|
+
node.statements.end_char
|
1329
|
+
else
|
1330
|
+
node.statements.body.first.start_char
|
1331
|
+
end
|
1332
|
+
|
1333
|
+
begin_token =
|
1334
|
+
if buffer.source[begin_start...begin_end].include?("then")
|
1335
|
+
srange_find(begin_start, begin_end, "then")
|
1336
|
+
elsif buffer.source[begin_start...begin_end].include?(";")
|
1337
|
+
srange_find(begin_start, begin_end, ";")
|
1338
|
+
end
|
1339
|
+
|
1340
|
+
else_token =
|
1341
|
+
case node.consequent
|
1342
|
+
when Elsif
|
1343
|
+
srange_length(node.consequent.start_char, 5)
|
1344
|
+
when Else
|
1345
|
+
srange_length(node.consequent.start_char, 4)
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
smap_condition(
|
1349
|
+
srange_length(node.start_char, 2),
|
1350
|
+
begin_token,
|
1351
|
+
else_token,
|
1352
|
+
srange_length(node.end_char, -3),
|
1353
|
+
srange_node(node)
|
1354
|
+
)
|
1355
|
+
end
|
1356
|
+
)
|
1357
|
+
end
|
1358
|
+
|
1359
|
+
# Visit an IfOp node.
|
1360
|
+
def visit_if_op(node)
|
1361
|
+
s(
|
1362
|
+
:if,
|
1363
|
+
[visit(node.predicate), visit(node.truthy), visit(node.falsy)],
|
1364
|
+
smap_ternary(
|
1365
|
+
srange_find_between(node.predicate, node.truthy, "?"),
|
1366
|
+
srange_find_between(node.truthy, node.falsy, ":"),
|
1367
|
+
srange_node(node)
|
1368
|
+
)
|
1369
|
+
)
|
1370
|
+
end
|
1371
|
+
|
1372
|
+
# Visit an Imaginary node.
|
1373
|
+
def visit_imaginary(node)
|
1374
|
+
s(
|
1375
|
+
:complex,
|
1376
|
+
[
|
1377
|
+
# We have to do an eval here in order to get the value in case
|
1378
|
+
# it's something like 42ri. to_c will not give the right value in
|
1379
|
+
# that case. Maybe there's an API for this but I can't find it.
|
1380
|
+
eval(node.value)
|
1381
|
+
],
|
1382
|
+
smap_operator(nil, srange_node(node))
|
1383
|
+
)
|
1384
|
+
end
|
1385
|
+
|
1386
|
+
# Visit an In node.
|
1387
|
+
def visit_in(node)
|
1388
|
+
case node.pattern
|
1389
|
+
when IfNode
|
1390
|
+
s(
|
1391
|
+
:in_pattern,
|
1392
|
+
[
|
1393
|
+
visit(node.pattern.statements),
|
1394
|
+
s(:if_guard, [visit(node.pattern.predicate)], nil),
|
1395
|
+
visit(node.statements)
|
1396
|
+
],
|
1397
|
+
nil
|
1398
|
+
)
|
1399
|
+
when UnlessNode
|
1400
|
+
s(
|
1401
|
+
:in_pattern,
|
1402
|
+
[
|
1403
|
+
visit(node.pattern.statements),
|
1404
|
+
s(:unless_guard, [visit(node.pattern.predicate)], nil),
|
1405
|
+
visit(node.statements)
|
1406
|
+
],
|
1407
|
+
nil
|
1408
|
+
)
|
1409
|
+
else
|
1410
|
+
begin_token =
|
1411
|
+
srange_search_between(node.pattern, node.statements, "then")
|
1412
|
+
|
1413
|
+
end_char =
|
1414
|
+
if begin_token || node.statements.empty?
|
1415
|
+
node.statements.end_char - 1
|
1416
|
+
else
|
1417
|
+
node.statements.body.last.start_char
|
1418
|
+
end
|
1419
|
+
|
1420
|
+
s(
|
1421
|
+
:in_pattern,
|
1422
|
+
[visit(node.pattern), nil, visit(node.statements)],
|
1423
|
+
smap_keyword(
|
1424
|
+
srange_length(node.start_char, 2),
|
1425
|
+
begin_token,
|
1426
|
+
nil,
|
1427
|
+
srange(node.start_char, end_char)
|
1428
|
+
)
|
1429
|
+
)
|
1430
|
+
end
|
1431
|
+
end
|
1432
|
+
|
1433
|
+
# Visit an Int node.
|
1434
|
+
def visit_int(node)
|
1435
|
+
operator =
|
1436
|
+
if %w[+ -].include?(buffer.source[node.start_char])
|
1437
|
+
srange_length(node.start_char, 1)
|
1438
|
+
end
|
1439
|
+
|
1440
|
+
s(:int, [node.value.to_i], smap_operator(operator, srange_node(node)))
|
1441
|
+
end
|
1442
|
+
|
1443
|
+
# Visit an IVar node.
|
1444
|
+
def visit_ivar(node)
|
1445
|
+
s(
|
1446
|
+
:ivar,
|
1447
|
+
[node.value.to_sym],
|
1448
|
+
smap_variable(srange_node(node), srange_node(node))
|
1449
|
+
)
|
1450
|
+
end
|
1451
|
+
|
1452
|
+
# Visit a Kw node.
|
1453
|
+
def visit_kw(node)
|
1454
|
+
location = smap(srange_node(node))
|
1455
|
+
|
1456
|
+
case node.value
|
1457
|
+
when "__FILE__"
|
1458
|
+
s(:str, [buffer.name], location)
|
1459
|
+
when "__LINE__"
|
1460
|
+
s(
|
1461
|
+
:int,
|
1462
|
+
[node.location.start_line + buffer.first_line - 1],
|
1463
|
+
location
|
1464
|
+
)
|
1465
|
+
when "__ENCODING__"
|
1466
|
+
if ::Parser::Builders::Default.emit_encoding
|
1467
|
+
s(:__ENCODING__, [], location)
|
1468
|
+
else
|
1469
|
+
s(:const, [s(:const, [nil, :Encoding], nil), :UTF_8], location)
|
1470
|
+
end
|
1471
|
+
else
|
1472
|
+
s(node.value.to_sym, [], location)
|
1473
|
+
end
|
1474
|
+
end
|
1475
|
+
|
1476
|
+
# Visit a KwRestParam node.
|
1477
|
+
def visit_kwrest_param(node)
|
1478
|
+
if node.name.nil?
|
1479
|
+
s(:kwrestarg, [], smap_variable(nil, srange_node(node)))
|
1480
|
+
else
|
1481
|
+
s(
|
1482
|
+
:kwrestarg,
|
1483
|
+
[node.name.value.to_sym],
|
1484
|
+
smap_variable(srange_node(node.name), srange_node(node))
|
1485
|
+
)
|
1486
|
+
end
|
1487
|
+
end
|
1488
|
+
|
1489
|
+
# Visit a Label node.
|
1490
|
+
def visit_label(node)
|
1491
|
+
s(
|
1492
|
+
:sym,
|
1493
|
+
[node.value.chomp(":").to_sym],
|
1494
|
+
smap_collection_bare(srange(node.start_char, node.end_char - 1))
|
1495
|
+
)
|
1496
|
+
end
|
1497
|
+
|
1498
|
+
# Visit a Lambda node.
|
1499
|
+
def visit_lambda(node)
|
1500
|
+
args =
|
1501
|
+
node.params.is_a?(LambdaVar) ? node.params : node.params.contents
|
1502
|
+
args_node = visit(args)
|
1503
|
+
|
1504
|
+
type = :block
|
1505
|
+
if args.empty? && (maximum = num_block_type(node.statements))
|
1506
|
+
type = :numblock
|
1507
|
+
args_node = maximum
|
1508
|
+
end
|
1509
|
+
|
1510
|
+
begin_token, end_token =
|
1511
|
+
if (
|
1512
|
+
srange =
|
1513
|
+
srange_search_between(node.params, node.statements, "{")
|
1514
|
+
)
|
1515
|
+
[srange, srange_length(node.end_char, -1)]
|
1516
|
+
else
|
1517
|
+
[
|
1518
|
+
srange_find_between(node.params, node.statements, "do"),
|
1519
|
+
srange_length(node.end_char, -3)
|
1520
|
+
]
|
1521
|
+
end
|
1522
|
+
|
1523
|
+
selector = srange_length(node.start_char, 2)
|
1524
|
+
|
1525
|
+
s(
|
1526
|
+
type,
|
1527
|
+
[
|
1528
|
+
if ::Parser::Builders::Default.emit_lambda
|
1529
|
+
s(:lambda, [], smap(selector))
|
1530
|
+
else
|
1531
|
+
s(:send, [nil, :lambda], smap_send_bare(selector, selector))
|
1532
|
+
end,
|
1533
|
+
args_node,
|
1534
|
+
visit(node.statements)
|
1535
|
+
],
|
1536
|
+
smap_collection(begin_token, end_token, srange_node(node))
|
1537
|
+
)
|
1538
|
+
end
|
1539
|
+
|
1540
|
+
# Visit a LambdaVar node.
|
1541
|
+
def visit_lambda_var(node)
|
1542
|
+
shadowargs =
|
1543
|
+
node.locals.map do |local|
|
1544
|
+
s(
|
1545
|
+
:shadowarg,
|
1546
|
+
[local.value.to_sym],
|
1547
|
+
smap_variable(srange_node(local), srange_node(local))
|
1548
|
+
)
|
1549
|
+
end
|
1550
|
+
|
1551
|
+
location =
|
1552
|
+
if node.start_char == node.end_char
|
1553
|
+
smap_collection_bare(nil)
|
1554
|
+
else
|
1555
|
+
smap_collection(
|
1556
|
+
srange_length(node.start_char, 1),
|
1557
|
+
srange_length(node.end_char, -1),
|
1558
|
+
srange_node(node)
|
1559
|
+
)
|
1560
|
+
end
|
1561
|
+
|
1562
|
+
s(:args, visit(node.params).children + shadowargs, location)
|
1563
|
+
end
|
1564
|
+
|
1565
|
+
# Visit an MAssign node.
|
1566
|
+
def visit_massign(node)
|
1567
|
+
s(
|
1568
|
+
:masgn,
|
1569
|
+
[visit(node.target), visit(node.value)],
|
1570
|
+
smap_operator(
|
1571
|
+
srange_find_between(node.target, node.value, "="),
|
1572
|
+
srange_node(node)
|
1573
|
+
)
|
1574
|
+
)
|
1575
|
+
end
|
1576
|
+
|
1577
|
+
# Visit a MethodAddBlock node.
|
1578
|
+
def visit_method_add_block(node)
|
1579
|
+
case node.call
|
1580
|
+
when Break, Next, ReturnNode
|
1581
|
+
type, arguments = block_children(node.block)
|
1582
|
+
call = visit(node.call)
|
1583
|
+
|
1584
|
+
s(
|
1585
|
+
call.type,
|
1586
|
+
[
|
1587
|
+
s(
|
1588
|
+
type,
|
1589
|
+
[*call.children, arguments, visit(node.block.bodystmt)],
|
1590
|
+
nil
|
1591
|
+
)
|
1592
|
+
],
|
1593
|
+
nil
|
1594
|
+
)
|
1595
|
+
when ARef, Super, ZSuper
|
1596
|
+
type, arguments = block_children(node.block)
|
1597
|
+
|
1598
|
+
s(
|
1599
|
+
type,
|
1600
|
+
[visit(node.call), arguments, visit(node.block.bodystmt)],
|
1601
|
+
nil
|
1602
|
+
)
|
1603
|
+
else
|
1604
|
+
visit_command_call(
|
1605
|
+
CommandCall.new(
|
1606
|
+
receiver: node.call.receiver,
|
1607
|
+
operator: node.call.operator,
|
1608
|
+
message: node.call.message,
|
1609
|
+
arguments: node.call.arguments,
|
1610
|
+
block: node.block,
|
1611
|
+
location: node.location
|
1612
|
+
)
|
1613
|
+
)
|
1614
|
+
end
|
1615
|
+
end
|
1616
|
+
|
1617
|
+
# Visit an MLHS node.
|
1618
|
+
def visit_mlhs(node)
|
1619
|
+
s(
|
1620
|
+
:mlhs,
|
1621
|
+
node.parts.map do |part|
|
1622
|
+
if part.is_a?(Ident)
|
1623
|
+
s(
|
1624
|
+
:arg,
|
1625
|
+
[part.value.to_sym],
|
1626
|
+
smap_variable(srange_node(part), srange_node(part))
|
1627
|
+
)
|
1628
|
+
else
|
1629
|
+
visit(part)
|
1630
|
+
end
|
1631
|
+
end,
|
1632
|
+
smap_collection_bare(srange_node(node))
|
1633
|
+
)
|
1634
|
+
end
|
1635
|
+
|
1636
|
+
# Visit an MLHSParen node.
|
1637
|
+
def visit_mlhs_paren(node)
|
1638
|
+
child = visit(node.contents)
|
1639
|
+
|
1640
|
+
s(
|
1641
|
+
child.type,
|
1642
|
+
child.children,
|
1643
|
+
smap_collection(
|
1644
|
+
srange_length(node.start_char, 1),
|
1645
|
+
srange_length(node.end_char, -1),
|
1646
|
+
srange_node(node)
|
1647
|
+
)
|
1648
|
+
)
|
1649
|
+
end
|
1650
|
+
|
1651
|
+
# Visit a ModuleDeclaration node.
|
1652
|
+
def visit_module(node)
|
1653
|
+
s(
|
1654
|
+
:module,
|
1655
|
+
[visit(node.constant), visit(node.bodystmt)],
|
1656
|
+
smap_definition(
|
1657
|
+
srange_length(node.start_char, 6),
|
1658
|
+
nil,
|
1659
|
+
srange_node(node.constant),
|
1660
|
+
srange_length(node.end_char, -3)
|
1661
|
+
).with_expression(srange_node(node))
|
1662
|
+
)
|
1663
|
+
end
|
1664
|
+
|
1665
|
+
# Visit an MRHS node.
|
1666
|
+
def visit_mrhs(node)
|
1667
|
+
visit_array(
|
1668
|
+
ArrayLiteral.new(
|
1669
|
+
lbracket: nil,
|
1670
|
+
contents: Args.new(parts: node.parts, location: node.location),
|
1671
|
+
location: node.location
|
1672
|
+
)
|
1673
|
+
)
|
1674
|
+
end
|
1675
|
+
|
1676
|
+
# Visit a Next node.
|
1677
|
+
def visit_next(node)
|
1678
|
+
s(
|
1679
|
+
:next,
|
1680
|
+
visit_all(node.arguments.parts),
|
1681
|
+
smap_keyword_bare(
|
1682
|
+
srange_length(node.start_char, 4),
|
1683
|
+
srange_node(node)
|
1684
|
+
)
|
1685
|
+
)
|
1686
|
+
end
|
1687
|
+
|
1688
|
+
# Visit a Not node.
|
1689
|
+
def visit_not(node)
|
1690
|
+
if node.statement.nil?
|
1691
|
+
begin_token = srange_find(node.start_char, nil, "(")
|
1692
|
+
end_token = srange_find(node.start_char, nil, ")")
|
1693
|
+
|
1694
|
+
s(
|
1695
|
+
:send,
|
1696
|
+
[
|
1697
|
+
s(
|
1698
|
+
:begin,
|
1699
|
+
[],
|
1700
|
+
smap_collection(
|
1701
|
+
begin_token,
|
1702
|
+
end_token,
|
1703
|
+
begin_token.join(end_token)
|
1704
|
+
)
|
1705
|
+
),
|
1706
|
+
:!
|
1707
|
+
],
|
1708
|
+
smap_send_bare(
|
1709
|
+
srange_length(node.start_char, 3),
|
1710
|
+
srange_node(node)
|
1711
|
+
)
|
1712
|
+
)
|
1713
|
+
else
|
1714
|
+
begin_token, end_token =
|
1715
|
+
if node.parentheses?
|
1716
|
+
[
|
1717
|
+
srange_find(
|
1718
|
+
node.start_char + 3,
|
1719
|
+
node.statement.start_char,
|
1720
|
+
"("
|
1721
|
+
),
|
1722
|
+
srange_length(node.end_char, -1)
|
1723
|
+
]
|
1724
|
+
end
|
1725
|
+
|
1726
|
+
s(
|
1727
|
+
:send,
|
1728
|
+
[visit(node.statement), :!],
|
1729
|
+
smap_send(
|
1730
|
+
nil,
|
1731
|
+
srange_length(node.start_char, 3),
|
1732
|
+
begin_token,
|
1733
|
+
end_token,
|
1734
|
+
srange_node(node)
|
1735
|
+
)
|
1736
|
+
)
|
1737
|
+
end
|
1738
|
+
end
|
1739
|
+
|
1740
|
+
# Visit an OpAssign node.
|
1741
|
+
def visit_opassign(node)
|
1742
|
+
target = visit(node.target)
|
1743
|
+
location =
|
1744
|
+
target
|
1745
|
+
.location
|
1746
|
+
.with_expression(srange_node(node))
|
1747
|
+
.with_operator(srange_node(node.operator))
|
1748
|
+
|
1749
|
+
case node.operator.value
|
1750
|
+
when "||="
|
1751
|
+
s(:or_asgn, [target, visit(node.value)], location)
|
1752
|
+
when "&&="
|
1753
|
+
s(:and_asgn, [target, visit(node.value)], location)
|
1754
|
+
else
|
1755
|
+
s(
|
1756
|
+
:op_asgn,
|
1757
|
+
[
|
1758
|
+
target,
|
1759
|
+
node.operator.value.chomp("=").to_sym,
|
1760
|
+
visit(node.value)
|
1761
|
+
],
|
1762
|
+
location
|
1763
|
+
)
|
1764
|
+
end
|
1765
|
+
end
|
1766
|
+
|
1767
|
+
# Visit a Params node.
|
1768
|
+
def visit_params(node)
|
1769
|
+
children = []
|
1770
|
+
|
1771
|
+
children +=
|
1772
|
+
node.requireds.map do |required|
|
1773
|
+
case required
|
1774
|
+
when MLHSParen
|
1775
|
+
visit(required)
|
1776
|
+
else
|
1777
|
+
s(
|
1778
|
+
:arg,
|
1779
|
+
[required.value.to_sym],
|
1780
|
+
smap_variable(srange_node(required), srange_node(required))
|
1781
|
+
)
|
1782
|
+
end
|
1783
|
+
end
|
1784
|
+
|
1785
|
+
children +=
|
1786
|
+
node.optionals.map do |(name, value)|
|
1787
|
+
s(
|
1788
|
+
:optarg,
|
1789
|
+
[name.value.to_sym, visit(value)],
|
1790
|
+
smap_variable(
|
1791
|
+
srange_node(name),
|
1792
|
+
srange_node(name).join(srange_node(value))
|
1793
|
+
).with_operator(srange_find_between(name, value, "="))
|
1794
|
+
)
|
1795
|
+
end
|
1796
|
+
|
1797
|
+
if node.rest && !node.rest.is_a?(ExcessedComma)
|
1798
|
+
children << visit(node.rest)
|
1799
|
+
end
|
1800
|
+
|
1801
|
+
children +=
|
1802
|
+
node.posts.map do |post|
|
1803
|
+
s(
|
1804
|
+
:arg,
|
1805
|
+
[post.value.to_sym],
|
1806
|
+
smap_variable(srange_node(post), srange_node(post))
|
1807
|
+
)
|
1808
|
+
end
|
1809
|
+
|
1810
|
+
children +=
|
1811
|
+
node.keywords.map do |(name, value)|
|
1812
|
+
key = name.value.chomp(":").to_sym
|
1813
|
+
|
1814
|
+
if value
|
1815
|
+
s(
|
1816
|
+
:kwoptarg,
|
1817
|
+
[key, visit(value)],
|
1818
|
+
smap_variable(
|
1819
|
+
srange(name.start_char, name.end_char - 1),
|
1820
|
+
srange_node(name).join(srange_node(value))
|
1821
|
+
)
|
1822
|
+
)
|
1823
|
+
else
|
1824
|
+
s(
|
1825
|
+
:kwarg,
|
1826
|
+
[key],
|
1827
|
+
smap_variable(
|
1828
|
+
srange(name.start_char, name.end_char - 1),
|
1829
|
+
srange_node(name)
|
1830
|
+
)
|
1831
|
+
)
|
1832
|
+
end
|
1833
|
+
end
|
1834
|
+
|
1835
|
+
case node.keyword_rest
|
1836
|
+
when nil, ArgsForward
|
1837
|
+
# do nothing
|
1838
|
+
when :nil
|
1839
|
+
children << s(
|
1840
|
+
:kwnilarg,
|
1841
|
+
[],
|
1842
|
+
smap_variable(srange_length(node.end_char, -3), srange_node(node))
|
1843
|
+
)
|
1844
|
+
else
|
1845
|
+
children << visit(node.keyword_rest)
|
1846
|
+
end
|
1847
|
+
|
1848
|
+
children << visit(node.block) if node.block
|
1849
|
+
|
1850
|
+
if node.keyword_rest.is_a?(ArgsForward)
|
1851
|
+
location = smap(srange_node(node.keyword_rest))
|
1852
|
+
|
1853
|
+
# If there are no other arguments and we have the emit_forward_arg
|
1854
|
+
# option enabled, then the entire argument list is represented by a
|
1855
|
+
# single forward_args node.
|
1856
|
+
if children.empty? && !::Parser::Builders::Default.emit_forward_arg
|
1857
|
+
return s(:forward_args, [], location)
|
1858
|
+
end
|
1859
|
+
|
1860
|
+
# Otherwise, we need to insert a forward_arg node into the list of
|
1861
|
+
# parameters before any keyword rest or block parameters.
|
1862
|
+
index =
|
1863
|
+
node.requireds.length + node.optionals.length +
|
1864
|
+
node.keywords.length
|
1865
|
+
children.insert(index, s(:forward_arg, [], location))
|
1866
|
+
end
|
1867
|
+
|
1868
|
+
location =
|
1869
|
+
unless children.empty?
|
1870
|
+
first = children.first.location.expression
|
1871
|
+
last = children.last.location.expression
|
1872
|
+
smap_collection_bare(first.join(last))
|
1873
|
+
end
|
1874
|
+
|
1875
|
+
s(:args, children, location)
|
1876
|
+
end
|
1877
|
+
|
1878
|
+
# Visit a Paren node.
|
1879
|
+
def visit_paren(node)
|
1880
|
+
location =
|
1881
|
+
smap_collection(
|
1882
|
+
srange_length(node.start_char, 1),
|
1883
|
+
srange_length(node.end_char, -1),
|
1884
|
+
srange_node(node)
|
1885
|
+
)
|
1886
|
+
|
1887
|
+
if node.contents.nil? ||
|
1888
|
+
(node.contents.is_a?(Statements) && node.contents.empty?)
|
1889
|
+
s(:begin, [], location)
|
1890
|
+
else
|
1891
|
+
child = visit(node.contents)
|
1892
|
+
child.type == :begin ? child : s(:begin, [child], location)
|
1893
|
+
end
|
1894
|
+
end
|
1895
|
+
|
1896
|
+
# Visit a PinnedBegin node.
|
1897
|
+
def visit_pinned_begin(node)
|
1898
|
+
s(
|
1899
|
+
:pin,
|
1900
|
+
[
|
1901
|
+
s(
|
1902
|
+
:begin,
|
1903
|
+
[visit(node.statement)],
|
1904
|
+
smap_collection(
|
1905
|
+
srange_length(node.start_char + 1, 1),
|
1906
|
+
srange_length(node.end_char, -1),
|
1907
|
+
srange(node.start_char + 1, node.end_char)
|
1908
|
+
)
|
1909
|
+
)
|
1910
|
+
],
|
1911
|
+
smap_send_bare(srange_length(node.start_char, 1), srange_node(node))
|
1912
|
+
)
|
1913
|
+
end
|
1914
|
+
|
1915
|
+
# Visit a PinnedVarRef node.
|
1916
|
+
def visit_pinned_var_ref(node)
|
1917
|
+
s(
|
1918
|
+
:pin,
|
1919
|
+
[visit(node.value)],
|
1920
|
+
smap_send_bare(srange_length(node.start_char, 1), srange_node(node))
|
1921
|
+
)
|
1922
|
+
end
|
1923
|
+
|
1924
|
+
# Visit a Program node.
|
1925
|
+
def visit_program(node)
|
1926
|
+
visit(node.statements)
|
1927
|
+
end
|
1928
|
+
|
1929
|
+
# Visit a QSymbols node.
|
1930
|
+
def visit_qsymbols(node)
|
1931
|
+
parts =
|
1932
|
+
node.elements.map do |element|
|
1933
|
+
SymbolLiteral.new(value: element, location: element.location)
|
1934
|
+
end
|
1935
|
+
|
1936
|
+
visit_array(
|
1937
|
+
ArrayLiteral.new(
|
1938
|
+
lbracket: node.beginning,
|
1939
|
+
contents: Args.new(parts: parts, location: node.location),
|
1940
|
+
location: node.location
|
1941
|
+
)
|
1942
|
+
)
|
1943
|
+
end
|
1944
|
+
|
1945
|
+
# Visit a QWords node.
|
1946
|
+
def visit_qwords(node)
|
1947
|
+
visit_array(
|
1948
|
+
ArrayLiteral.new(
|
1949
|
+
lbracket: node.beginning,
|
1950
|
+
contents: Args.new(parts: node.elements, location: node.location),
|
1951
|
+
location: node.location
|
1952
|
+
)
|
1953
|
+
)
|
1954
|
+
end
|
1955
|
+
|
1956
|
+
# Visit a RangeNode node.
|
1957
|
+
def visit_range(node)
|
1958
|
+
s(
|
1959
|
+
node.operator.value == ".." ? :irange : :erange,
|
1960
|
+
[visit(node.left), visit(node.right)],
|
1961
|
+
smap_operator(srange_node(node.operator), srange_node(node))
|
1962
|
+
)
|
1963
|
+
end
|
1964
|
+
|
1965
|
+
# Visit an RAssign node.
|
1966
|
+
def visit_rassign(node)
|
1967
|
+
s(
|
1968
|
+
node.operator.value == "=>" ? :match_pattern : :match_pattern_p,
|
1969
|
+
[visit(node.value), visit(node.pattern)],
|
1970
|
+
smap_operator(srange_node(node.operator), srange_node(node))
|
1971
|
+
)
|
1972
|
+
end
|
1973
|
+
|
1974
|
+
# Visit a Rational node.
|
1975
|
+
def visit_rational(node)
|
1976
|
+
s(:rational, [node.value.to_r], smap_operator(nil, srange_node(node)))
|
1977
|
+
end
|
1978
|
+
|
1979
|
+
# Visit a Redo node.
|
1980
|
+
def visit_redo(node)
|
1981
|
+
s(:redo, [], smap_keyword_bare(srange_node(node), srange_node(node)))
|
1982
|
+
end
|
1983
|
+
|
1984
|
+
# Visit a RegexpLiteral node.
|
1985
|
+
def visit_regexp_literal(node)
|
1986
|
+
s(
|
1987
|
+
:regexp,
|
1988
|
+
visit_all(node.parts).push(
|
1989
|
+
s(
|
1990
|
+
:regopt,
|
1991
|
+
node.ending.scan(/[a-z]/).sort.map(&:to_sym),
|
1992
|
+
smap(srange_length(node.end_char, -(node.ending.length - 1)))
|
1993
|
+
)
|
1994
|
+
),
|
1995
|
+
smap_collection(
|
1996
|
+
srange_length(node.start_char, node.beginning.length),
|
1997
|
+
srange_length(node.end_char - node.ending.length, 1),
|
1998
|
+
srange_node(node)
|
1999
|
+
)
|
2000
|
+
)
|
2001
|
+
end
|
2002
|
+
|
2003
|
+
# Visit a Rescue node.
|
2004
|
+
def visit_rescue(node)
|
2005
|
+
# In the parser gem, there is a separation between the rescue node and
|
2006
|
+
# the rescue body. They have different bounds, so we have to calculate
|
2007
|
+
# those here.
|
2008
|
+
start_char = node.start_char
|
2009
|
+
|
2010
|
+
body_end_char =
|
2011
|
+
if node.statements.empty?
|
2012
|
+
start_char + 6
|
2013
|
+
else
|
2014
|
+
node.statements.body.last.end_char
|
2015
|
+
end
|
2016
|
+
|
2017
|
+
end_char =
|
2018
|
+
if node.consequent
|
2019
|
+
end_node = node.consequent
|
2020
|
+
end_node = end_node.consequent while end_node.consequent
|
2021
|
+
|
2022
|
+
if end_node.statements.empty?
|
2023
|
+
start_char + 6
|
2024
|
+
else
|
2025
|
+
end_node.statements.body.last.end_char
|
2026
|
+
end
|
2027
|
+
else
|
2028
|
+
body_end_char
|
2029
|
+
end
|
2030
|
+
|
2031
|
+
# These locations are reused for multiple children.
|
2032
|
+
keyword = srange_length(start_char, 6)
|
2033
|
+
body_expression = srange(start_char, body_end_char)
|
2034
|
+
expression = srange(start_char, end_char)
|
2035
|
+
|
2036
|
+
exceptions =
|
2037
|
+
case node.exception&.exceptions
|
2038
|
+
when nil
|
2039
|
+
nil
|
2040
|
+
when MRHS
|
2041
|
+
visit_array(
|
2042
|
+
ArrayLiteral.new(
|
2043
|
+
lbracket: nil,
|
2044
|
+
contents:
|
2045
|
+
Args.new(
|
2046
|
+
parts: node.exception.exceptions.parts,
|
2047
|
+
location: node.exception.exceptions.location
|
2048
|
+
),
|
2049
|
+
location: node.exception.exceptions.location
|
2050
|
+
)
|
2051
|
+
)
|
2052
|
+
else
|
2053
|
+
visit_array(
|
2054
|
+
ArrayLiteral.new(
|
2055
|
+
lbracket: nil,
|
2056
|
+
contents:
|
2057
|
+
Args.new(
|
2058
|
+
parts: [node.exception.exceptions],
|
2059
|
+
location: node.exception.exceptions.location
|
2060
|
+
),
|
2061
|
+
location: node.exception.exceptions.location
|
2062
|
+
)
|
2063
|
+
)
|
2064
|
+
end
|
2065
|
+
|
2066
|
+
resbody =
|
2067
|
+
if node.exception.nil?
|
2068
|
+
s(
|
2069
|
+
:resbody,
|
2070
|
+
[nil, nil, visit(node.statements)],
|
2071
|
+
smap_rescue_body(keyword, nil, nil, body_expression)
|
2072
|
+
)
|
2073
|
+
elsif node.exception.variable.nil?
|
2074
|
+
s(
|
2075
|
+
:resbody,
|
2076
|
+
[exceptions, nil, visit(node.statements)],
|
2077
|
+
smap_rescue_body(keyword, nil, nil, body_expression)
|
2078
|
+
)
|
2079
|
+
else
|
2080
|
+
s(
|
2081
|
+
:resbody,
|
2082
|
+
[
|
2083
|
+
exceptions,
|
2084
|
+
visit(node.exception.variable),
|
2085
|
+
visit(node.statements)
|
2086
|
+
],
|
2087
|
+
smap_rescue_body(
|
2088
|
+
keyword,
|
2089
|
+
srange_find(
|
2090
|
+
node.start_char + 6,
|
2091
|
+
node.exception.variable.start_char,
|
2092
|
+
"=>"
|
2093
|
+
),
|
2094
|
+
nil,
|
2095
|
+
body_expression
|
2096
|
+
)
|
2097
|
+
)
|
2098
|
+
end
|
2099
|
+
|
2100
|
+
children = [resbody]
|
2101
|
+
if node.consequent
|
2102
|
+
children += visit(node.consequent).children
|
2103
|
+
else
|
2104
|
+
children << nil
|
2105
|
+
end
|
2106
|
+
|
2107
|
+
s(:rescue, children, smap_condition_bare(expression))
|
2108
|
+
end
|
2109
|
+
|
2110
|
+
# Visit a RescueMod node.
|
2111
|
+
def visit_rescue_mod(node)
|
2112
|
+
keyword = srange_find_between(node.statement, node.value, "rescue")
|
2113
|
+
|
2114
|
+
s(
|
2115
|
+
:rescue,
|
2116
|
+
[
|
2117
|
+
visit(node.statement),
|
2118
|
+
s(
|
2119
|
+
:resbody,
|
2120
|
+
[nil, nil, visit(node.value)],
|
2121
|
+
smap_rescue_body(
|
2122
|
+
keyword,
|
2123
|
+
nil,
|
2124
|
+
nil,
|
2125
|
+
keyword.join(srange_node(node.value))
|
2126
|
+
)
|
2127
|
+
),
|
2128
|
+
nil
|
2129
|
+
],
|
2130
|
+
smap_condition_bare(srange_node(node))
|
2131
|
+
)
|
2132
|
+
end
|
2133
|
+
|
2134
|
+
# Visit a RestParam node.
|
2135
|
+
def visit_rest_param(node)
|
2136
|
+
if node.name
|
2137
|
+
s(
|
2138
|
+
:restarg,
|
2139
|
+
[node.name.value.to_sym],
|
2140
|
+
smap_variable(srange_node(node.name), srange_node(node))
|
2141
|
+
)
|
2142
|
+
else
|
2143
|
+
s(:restarg, [], smap_variable(nil, srange_node(node)))
|
2144
|
+
end
|
2145
|
+
end
|
2146
|
+
|
2147
|
+
# Visit a Retry node.
|
2148
|
+
def visit_retry(node)
|
2149
|
+
s(:retry, [], smap_keyword_bare(srange_node(node), srange_node(node)))
|
2150
|
+
end
|
2151
|
+
|
2152
|
+
# Visit a ReturnNode node.
|
2153
|
+
def visit_return(node)
|
2154
|
+
s(
|
2155
|
+
:return,
|
2156
|
+
node.arguments ? visit_all(node.arguments.parts) : [],
|
2157
|
+
smap_keyword_bare(
|
2158
|
+
srange_length(node.start_char, 6),
|
2159
|
+
srange_node(node)
|
2160
|
+
)
|
2161
|
+
)
|
2162
|
+
end
|
2163
|
+
|
2164
|
+
# Visit an SClass node.
|
2165
|
+
def visit_sclass(node)
|
2166
|
+
s(
|
2167
|
+
:sclass,
|
2168
|
+
[visit(node.target), visit(node.bodystmt)],
|
2169
|
+
smap_definition(
|
2170
|
+
srange_length(node.start_char, 5),
|
2171
|
+
srange_find(node.start_char + 5, node.target.start_char, "<<"),
|
2172
|
+
nil,
|
2173
|
+
srange_length(node.end_char, -3)
|
2174
|
+
).with_expression(srange_node(node))
|
2175
|
+
)
|
2176
|
+
end
|
2177
|
+
|
2178
|
+
# Visit a Statements node.
|
2179
|
+
def visit_statements(node)
|
2180
|
+
children =
|
2181
|
+
node.body.reject do |child|
|
2182
|
+
child.is_a?(Comment) || child.is_a?(EmbDoc) ||
|
2183
|
+
child.is_a?(EndContent) || child.is_a?(VoidStmt)
|
2184
|
+
end
|
2185
|
+
|
2186
|
+
case children.length
|
2187
|
+
when 0
|
2188
|
+
nil
|
2189
|
+
when 1
|
2190
|
+
visit(children.first)
|
2191
|
+
else
|
2192
|
+
s(
|
2193
|
+
:begin,
|
2194
|
+
visit_all(children),
|
2195
|
+
smap_collection_bare(
|
2196
|
+
srange(children.first.start_char, children.last.end_char)
|
2197
|
+
)
|
2198
|
+
)
|
2199
|
+
end
|
2200
|
+
end
|
2201
|
+
|
2202
|
+
# Visit a StringConcat node.
|
2203
|
+
def visit_string_concat(node)
|
2204
|
+
s(
|
2205
|
+
:dstr,
|
2206
|
+
[visit(node.left), visit(node.right)],
|
2207
|
+
smap_collection_bare(srange_node(node))
|
2208
|
+
)
|
2209
|
+
end
|
2210
|
+
|
2211
|
+
# Visit a StringDVar node.
|
2212
|
+
def visit_string_dvar(node)
|
2213
|
+
visit(node.variable)
|
2214
|
+
end
|
2215
|
+
|
2216
|
+
# Visit a StringEmbExpr node.
|
2217
|
+
def visit_string_embexpr(node)
|
2218
|
+
s(
|
2219
|
+
:begin,
|
2220
|
+
visit(node.statements).then { |child| child ? [child] : [] },
|
2221
|
+
smap_collection(
|
2222
|
+
srange_length(node.start_char, 2),
|
2223
|
+
srange_length(node.end_char, -1),
|
2224
|
+
srange_node(node)
|
2225
|
+
)
|
2226
|
+
)
|
2227
|
+
end
|
2228
|
+
|
2229
|
+
# Visit a StringLiteral node.
|
2230
|
+
def visit_string_literal(node)
|
2231
|
+
location =
|
2232
|
+
if node.quote
|
2233
|
+
smap_collection(
|
2234
|
+
srange_length(node.start_char, node.quote.length),
|
2235
|
+
srange_length(node.end_char, -1),
|
2236
|
+
srange_node(node)
|
2237
|
+
)
|
2238
|
+
else
|
2239
|
+
smap_collection_bare(srange_node(node))
|
2240
|
+
end
|
2241
|
+
|
2242
|
+
if node.parts.empty?
|
2243
|
+
s(:str, [""], location)
|
2244
|
+
elsif node.parts.length == 1 && node.parts.first.is_a?(TStringContent)
|
2245
|
+
child = visit(node.parts.first)
|
2246
|
+
s(child.type, child.children, location)
|
2247
|
+
else
|
2248
|
+
s(:dstr, visit_all(node.parts), location)
|
2249
|
+
end
|
2250
|
+
end
|
2251
|
+
|
2252
|
+
# Visit a Super node.
|
2253
|
+
def visit_super(node)
|
2254
|
+
if node.arguments.is_a?(Args)
|
2255
|
+
s(
|
2256
|
+
:super,
|
2257
|
+
visit_all(node.arguments.parts),
|
2258
|
+
smap_keyword_bare(
|
2259
|
+
srange_length(node.start_char, 5),
|
2260
|
+
srange_node(node)
|
2261
|
+
)
|
2262
|
+
)
|
2263
|
+
else
|
2264
|
+
case node.arguments.arguments
|
2265
|
+
when nil
|
2266
|
+
s(
|
2267
|
+
:super,
|
2268
|
+
[],
|
2269
|
+
smap_keyword(
|
2270
|
+
srange_length(node.start_char, 5),
|
2271
|
+
srange_find(node.start_char + 5, node.end_char, "("),
|
2272
|
+
srange_length(node.end_char, -1),
|
2273
|
+
srange_node(node)
|
2274
|
+
)
|
2275
|
+
)
|
2276
|
+
when ArgsForward
|
2277
|
+
s(:super, [visit(node.arguments.arguments)], nil)
|
2278
|
+
else
|
2279
|
+
s(
|
2280
|
+
:super,
|
2281
|
+
visit_all(node.arguments.arguments.parts),
|
2282
|
+
smap_keyword(
|
2283
|
+
srange_length(node.start_char, 5),
|
2284
|
+
srange_find(node.start_char + 5, node.end_char, "("),
|
2285
|
+
srange_length(node.end_char, -1),
|
2286
|
+
srange_node(node)
|
2287
|
+
)
|
2288
|
+
)
|
2289
|
+
end
|
2290
|
+
end
|
2291
|
+
end
|
2292
|
+
|
2293
|
+
# Visit a SymbolLiteral node.
|
2294
|
+
def visit_symbol_literal(node)
|
2295
|
+
begin_token =
|
2296
|
+
if buffer.source[node.start_char] == ":"
|
2297
|
+
srange_length(node.start_char, 1)
|
2298
|
+
end
|
2299
|
+
|
2300
|
+
s(
|
2301
|
+
:sym,
|
2302
|
+
[node.value.value.to_sym],
|
2303
|
+
smap_collection(begin_token, nil, srange_node(node))
|
2304
|
+
)
|
2305
|
+
end
|
2306
|
+
|
2307
|
+
# Visit a Symbols node.
|
2308
|
+
def visit_symbols(node)
|
2309
|
+
parts =
|
2310
|
+
node.elements.map do |element|
|
2311
|
+
part = element.parts.first
|
2312
|
+
|
2313
|
+
if element.parts.length == 1 && part.is_a?(TStringContent)
|
2314
|
+
SymbolLiteral.new(value: part, location: part.location)
|
2315
|
+
else
|
2316
|
+
DynaSymbol.new(
|
2317
|
+
parts: element.parts,
|
2318
|
+
quote: nil,
|
2319
|
+
location: element.location
|
2320
|
+
)
|
2321
|
+
end
|
2322
|
+
end
|
2323
|
+
|
2324
|
+
visit_array(
|
2325
|
+
ArrayLiteral.new(
|
2326
|
+
lbracket: node.beginning,
|
2327
|
+
contents: Args.new(parts: parts, location: node.location),
|
2328
|
+
location: node.location
|
2329
|
+
)
|
2330
|
+
)
|
2331
|
+
end
|
2332
|
+
|
2333
|
+
# Visit a TopConstField node.
|
2334
|
+
def visit_top_const_field(node)
|
2335
|
+
s(
|
2336
|
+
:casgn,
|
2337
|
+
[
|
2338
|
+
s(:cbase, [], smap(srange_length(node.start_char, 2))),
|
2339
|
+
node.constant.value.to_sym
|
2340
|
+
],
|
2341
|
+
smap_constant(
|
2342
|
+
srange_length(node.start_char, 2),
|
2343
|
+
srange_node(node.constant),
|
2344
|
+
srange_node(node)
|
2345
|
+
)
|
2346
|
+
)
|
2347
|
+
end
|
2348
|
+
|
2349
|
+
# Visit a TopConstRef node.
|
2350
|
+
def visit_top_const_ref(node)
|
2351
|
+
s(
|
2352
|
+
:const,
|
2353
|
+
[
|
2354
|
+
s(:cbase, [], smap(srange_length(node.start_char, 2))),
|
2355
|
+
node.constant.value.to_sym
|
2356
|
+
],
|
2357
|
+
smap_constant(
|
2358
|
+
srange_length(node.start_char, 2),
|
2359
|
+
srange_node(node.constant),
|
2360
|
+
srange_node(node)
|
2361
|
+
)
|
2362
|
+
)
|
2363
|
+
end
|
2364
|
+
|
2365
|
+
# Visit a TStringContent node.
|
2366
|
+
def visit_tstring_content(node)
|
2367
|
+
dumped = node.value.gsub(/([^[:ascii:]])/) { $1.dump[1...-1] }
|
2368
|
+
|
2369
|
+
s(
|
2370
|
+
:str,
|
2371
|
+
["\"#{dumped}\"".undump],
|
2372
|
+
smap_collection_bare(srange_node(node))
|
2373
|
+
)
|
2374
|
+
end
|
2375
|
+
|
2376
|
+
# Visit a Unary node.
|
2377
|
+
def visit_unary(node)
|
2378
|
+
# Special handling here for flipflops
|
2379
|
+
if node.statement.is_a?(Paren) &&
|
2380
|
+
node.statement.contents.is_a?(Statements) &&
|
2381
|
+
node.statement.contents.body.length == 1 &&
|
2382
|
+
(range = node.statement.contents.body.first).is_a?(RangeNode) &&
|
2383
|
+
node.operator == "!"
|
2384
|
+
type = range.operator.value == ".." ? :iflipflop : :eflipflop
|
2385
|
+
return(
|
2386
|
+
s(
|
2387
|
+
:send,
|
2388
|
+
[s(:begin, [s(type, visit(range).children, nil)], nil), :!],
|
2389
|
+
nil
|
2390
|
+
)
|
2391
|
+
)
|
2392
|
+
end
|
2393
|
+
|
2394
|
+
visit(canonical_unary(node))
|
2395
|
+
end
|
2396
|
+
|
2397
|
+
# Visit an Undef node.
|
2398
|
+
def visit_undef(node)
|
2399
|
+
s(
|
2400
|
+
:undef,
|
2401
|
+
visit_all(node.symbols),
|
2402
|
+
smap_keyword_bare(
|
2403
|
+
srange_length(node.start_char, 5),
|
2404
|
+
srange_node(node)
|
2405
|
+
)
|
2406
|
+
)
|
2407
|
+
end
|
2408
|
+
|
2409
|
+
# Visit an UnlessNode node.
|
2410
|
+
def visit_unless(node)
|
2411
|
+
predicate =
|
2412
|
+
case node.predicate
|
2413
|
+
when RegexpLiteral
|
2414
|
+
s(:match_current_line, [visit(node.predicate)], nil)
|
2415
|
+
when Unary
|
2416
|
+
if node.predicate.operator.value == "!" &&
|
2417
|
+
node.predicate.statement.is_a?(RegexpLiteral)
|
2418
|
+
s(
|
2419
|
+
:send,
|
2420
|
+
[
|
2421
|
+
s(:match_current_line, [visit(node.predicate.statement)]),
|
2422
|
+
:!
|
2423
|
+
],
|
2424
|
+
nil
|
2425
|
+
)
|
2426
|
+
else
|
2427
|
+
visit(node.predicate)
|
2428
|
+
end
|
2429
|
+
else
|
2430
|
+
visit(node.predicate)
|
2431
|
+
end
|
2432
|
+
|
2433
|
+
s(
|
2434
|
+
:if,
|
2435
|
+
[predicate, visit(node.consequent), visit(node.statements)],
|
2436
|
+
if node.modifier?
|
2437
|
+
smap_keyword_bare(
|
2438
|
+
srange_find_between(node.statements, node.predicate, "unless"),
|
2439
|
+
srange_node(node)
|
2440
|
+
)
|
2441
|
+
else
|
2442
|
+
smap_condition(
|
2443
|
+
srange_length(node.start_char, 6),
|
2444
|
+
srange_search_between(node.predicate, node.statements, "then"),
|
2445
|
+
nil,
|
2446
|
+
srange_length(node.end_char, -3),
|
2447
|
+
srange_node(node)
|
2448
|
+
)
|
2449
|
+
end
|
2450
|
+
)
|
2451
|
+
end
|
2452
|
+
|
2453
|
+
# Visit an UntilNode node.
|
2454
|
+
def visit_until(node)
|
2455
|
+
s(
|
2456
|
+
loop_post?(node) ? :until_post : :until,
|
2457
|
+
[visit(node.predicate), visit(node.statements)],
|
2458
|
+
if node.modifier?
|
2459
|
+
smap_keyword_bare(
|
2460
|
+
srange_find_between(node.statements, node.predicate, "until"),
|
2461
|
+
srange_node(node)
|
2462
|
+
)
|
2463
|
+
else
|
2464
|
+
smap_keyword(
|
2465
|
+
srange_length(node.start_char, 5),
|
2466
|
+
srange_search_between(node.predicate, node.statements, "do") ||
|
2467
|
+
srange_search_between(node.predicate, node.statements, ";"),
|
2468
|
+
srange_length(node.end_char, -3),
|
2469
|
+
srange_node(node)
|
2470
|
+
)
|
2471
|
+
end
|
2472
|
+
)
|
2473
|
+
end
|
2474
|
+
|
2475
|
+
# Visit a VarField node.
|
2476
|
+
def visit_var_field(node)
|
2477
|
+
name = node.value.value.to_sym
|
2478
|
+
match_var =
|
2479
|
+
[stack[-3], stack[-2]].any? do |parent|
|
2480
|
+
case parent
|
2481
|
+
when AryPtn, FndPtn, HshPtn, In, RAssign
|
2482
|
+
true
|
2483
|
+
when Binary
|
2484
|
+
parent.operator == :"=>"
|
2485
|
+
else
|
2486
|
+
false
|
2487
|
+
end
|
2488
|
+
end
|
2489
|
+
|
2490
|
+
if match_var
|
2491
|
+
s(
|
2492
|
+
:match_var,
|
2493
|
+
[name],
|
2494
|
+
smap_variable(srange_node(node.value), srange_node(node.value))
|
2495
|
+
)
|
2496
|
+
elsif node.value.is_a?(Const)
|
2497
|
+
s(
|
2498
|
+
:casgn,
|
2499
|
+
[nil, name],
|
2500
|
+
smap_constant(nil, srange_node(node.value), srange_node(node))
|
2501
|
+
)
|
2502
|
+
else
|
2503
|
+
location = smap_variable(srange_node(node), srange_node(node))
|
2504
|
+
|
2505
|
+
case node.value
|
2506
|
+
when CVar
|
2507
|
+
s(:cvasgn, [name], location)
|
2508
|
+
when GVar
|
2509
|
+
s(:gvasgn, [name], location)
|
2510
|
+
when Ident
|
2511
|
+
s(:lvasgn, [name], location)
|
2512
|
+
when IVar
|
2513
|
+
s(:ivasgn, [name], location)
|
2514
|
+
when VarRef
|
2515
|
+
s(:lvasgn, [name], location)
|
2516
|
+
else
|
2517
|
+
s(:match_rest, [], nil)
|
2518
|
+
end
|
2519
|
+
end
|
2520
|
+
end
|
2521
|
+
|
2522
|
+
# Visit a VarRef node.
|
2523
|
+
def visit_var_ref(node)
|
2524
|
+
visit(node.value)
|
2525
|
+
end
|
2526
|
+
|
2527
|
+
# Visit a VCall node.
|
2528
|
+
def visit_vcall(node)
|
2529
|
+
visit_command_call(
|
2530
|
+
CommandCall.new(
|
2531
|
+
receiver: nil,
|
2532
|
+
operator: nil,
|
2533
|
+
message: node.value,
|
2534
|
+
arguments: nil,
|
2535
|
+
block: nil,
|
2536
|
+
location: node.location
|
2537
|
+
)
|
2538
|
+
)
|
2539
|
+
end
|
2540
|
+
|
2541
|
+
# Visit a When node.
|
2542
|
+
def visit_when(node)
|
2543
|
+
keyword = srange_length(node.start_char, 4)
|
2544
|
+
begin_token =
|
2545
|
+
if buffer.source[node.statements.start_char] == ";"
|
2546
|
+
srange_length(node.statements.start_char, 1)
|
2547
|
+
end
|
2548
|
+
|
2549
|
+
end_char =
|
2550
|
+
if node.statements.body.empty?
|
2551
|
+
node.statements.end_char
|
2552
|
+
else
|
2553
|
+
node.statements.body.last.end_char
|
2554
|
+
end
|
2555
|
+
|
2556
|
+
s(
|
2557
|
+
:when,
|
2558
|
+
visit_all(node.arguments.parts) + [visit(node.statements)],
|
2559
|
+
smap_keyword(
|
2560
|
+
keyword,
|
2561
|
+
begin_token,
|
2562
|
+
nil,
|
2563
|
+
srange(keyword.begin_pos, end_char)
|
2564
|
+
)
|
2565
|
+
)
|
2566
|
+
end
|
2567
|
+
|
2568
|
+
# Visit a WhileNode node.
|
2569
|
+
def visit_while(node)
|
2570
|
+
s(
|
2571
|
+
loop_post?(node) ? :while_post : :while,
|
2572
|
+
[visit(node.predicate), visit(node.statements)],
|
2573
|
+
if node.modifier?
|
2574
|
+
smap_keyword_bare(
|
2575
|
+
srange_find_between(node.statements, node.predicate, "while"),
|
2576
|
+
srange_node(node)
|
2577
|
+
)
|
2578
|
+
else
|
2579
|
+
smap_keyword(
|
2580
|
+
srange_length(node.start_char, 5),
|
2581
|
+
srange_search_between(node.predicate, node.statements, "do") ||
|
2582
|
+
srange_search_between(node.predicate, node.statements, ";"),
|
2583
|
+
srange_length(node.end_char, -3),
|
2584
|
+
srange_node(node)
|
2585
|
+
)
|
2586
|
+
end
|
2587
|
+
)
|
2588
|
+
end
|
2589
|
+
|
2590
|
+
# Visit a Word node.
|
2591
|
+
def visit_word(node)
|
2592
|
+
visit_string_literal(
|
2593
|
+
StringLiteral.new(
|
2594
|
+
parts: node.parts,
|
2595
|
+
quote: nil,
|
2596
|
+
location: node.location
|
2597
|
+
)
|
2598
|
+
)
|
2599
|
+
end
|
2600
|
+
|
2601
|
+
# Visit a Words node.
|
2602
|
+
def visit_words(node)
|
2603
|
+
visit_array(
|
2604
|
+
ArrayLiteral.new(
|
2605
|
+
lbracket: node.beginning,
|
2606
|
+
contents: Args.new(parts: node.elements, location: node.location),
|
2607
|
+
location: node.location
|
2608
|
+
)
|
2609
|
+
)
|
2610
|
+
end
|
2611
|
+
|
2612
|
+
# Visit an XStringLiteral node.
|
2613
|
+
def visit_xstring_literal(node)
|
2614
|
+
s(
|
2615
|
+
:xstr,
|
2616
|
+
visit_all(node.parts),
|
2617
|
+
smap_collection(
|
2618
|
+
srange_length(
|
2619
|
+
node.start_char,
|
2620
|
+
buffer.source[node.start_char] == "%" ? 3 : 1
|
2621
|
+
),
|
2622
|
+
srange_length(node.end_char, -1),
|
2623
|
+
srange_node(node)
|
2624
|
+
)
|
2625
|
+
)
|
2626
|
+
end
|
2627
|
+
|
2628
|
+
def visit_yield(node)
|
2629
|
+
case node.arguments
|
2630
|
+
when nil
|
2631
|
+
s(
|
2632
|
+
:yield,
|
2633
|
+
[],
|
2634
|
+
smap_keyword_bare(
|
2635
|
+
srange_length(node.start_char, 5),
|
2636
|
+
srange_node(node)
|
2637
|
+
)
|
2638
|
+
)
|
2639
|
+
when Args
|
2640
|
+
s(
|
2641
|
+
:yield,
|
2642
|
+
visit_all(node.arguments.parts),
|
2643
|
+
smap_keyword_bare(
|
2644
|
+
srange_length(node.start_char, 5),
|
2645
|
+
srange_node(node)
|
2646
|
+
)
|
2647
|
+
)
|
2648
|
+
else
|
2649
|
+
s(
|
2650
|
+
:yield,
|
2651
|
+
visit_all(node.arguments.contents.parts),
|
2652
|
+
smap_keyword(
|
2653
|
+
srange_length(node.start_char, 5),
|
2654
|
+
srange_length(node.arguments.start_char, 1),
|
2655
|
+
srange_length(node.end_char, -1),
|
2656
|
+
srange_node(node)
|
2657
|
+
)
|
2658
|
+
)
|
2659
|
+
end
|
2660
|
+
end
|
2661
|
+
|
2662
|
+
# Visit a ZSuper node.
|
2663
|
+
def visit_zsuper(node)
|
2664
|
+
s(
|
2665
|
+
:zsuper,
|
2666
|
+
[],
|
2667
|
+
smap_keyword_bare(
|
2668
|
+
srange_length(node.start_char, 5),
|
2669
|
+
srange_node(node)
|
2670
|
+
)
|
2671
|
+
)
|
2672
|
+
end
|
2673
|
+
end
|
2674
|
+
|
2675
|
+
private
|
2676
|
+
|
2677
|
+
def block_children(node)
|
2678
|
+
arguments =
|
2679
|
+
if node.block_var
|
2680
|
+
visit(node.block_var)
|
2681
|
+
else
|
2682
|
+
s(:args, [], smap_collection_bare(nil))
|
2683
|
+
end
|
2684
|
+
|
2685
|
+
type = :block
|
2686
|
+
if !node.block_var && (maximum = num_block_type(node.bodystmt))
|
2687
|
+
type = :numblock
|
2688
|
+
arguments = maximum
|
2689
|
+
end
|
2690
|
+
|
2691
|
+
[type, arguments]
|
2692
|
+
end
|
2693
|
+
|
2694
|
+
# Convert a Unary node into a canonical CommandCall node.
|
2695
|
+
def canonical_unary(node)
|
2696
|
+
# For integers and floats with a leading + or -, parser represents them
|
2697
|
+
# as just their values with the signs attached.
|
2698
|
+
if %w[+ -].include?(node.operator) &&
|
2699
|
+
(node.statement.is_a?(Int) || node.statement.is_a?(FloatLiteral))
|
2700
|
+
return(
|
2701
|
+
node.statement.class.new(
|
2702
|
+
value: "#{node.operator}#{node.statement.value}",
|
2703
|
+
location: node.location
|
2704
|
+
)
|
2705
|
+
)
|
2706
|
+
end
|
2707
|
+
|
2708
|
+
value = { "+" => "+@", "-" => "-@" }.fetch(node.operator, node.operator)
|
2709
|
+
length = node.operator.length
|
2710
|
+
|
2711
|
+
CommandCall.new(
|
2712
|
+
receiver: node.statement,
|
2713
|
+
operator: nil,
|
2714
|
+
message:
|
2715
|
+
Op.new(
|
2716
|
+
value: value,
|
2717
|
+
location:
|
2718
|
+
Location.new(
|
2719
|
+
start_line: node.location.start_line,
|
2720
|
+
start_char: node.start_char,
|
2721
|
+
start_column: node.location.start_column,
|
2722
|
+
end_line: node.location.start_line,
|
2723
|
+
end_char: node.start_char + length,
|
2724
|
+
end_column: node.location.start_column + length
|
2725
|
+
)
|
2726
|
+
),
|
2727
|
+
arguments: nil,
|
2728
|
+
block: nil,
|
2729
|
+
location: node.location
|
2730
|
+
)
|
2731
|
+
end
|
2732
|
+
|
2733
|
+
# Convert a Binary node into a canonical CommandCall node.
|
2734
|
+
def canonical_binary(node)
|
2735
|
+
operator = node.operator.to_s
|
2736
|
+
|
2737
|
+
start_char = node.left.end_char
|
2738
|
+
end_char = node.right.start_char
|
2739
|
+
|
2740
|
+
index = buffer.source[start_char...end_char].index(operator)
|
2741
|
+
start_line =
|
2742
|
+
node.location.start_line +
|
2743
|
+
buffer.source[start_char...index].count("\n")
|
2744
|
+
start_column =
|
2745
|
+
index - (buffer.source[start_char...index].rindex("\n") || 0)
|
2746
|
+
|
2747
|
+
op_location =
|
2748
|
+
Location.new(
|
2749
|
+
start_line: start_line,
|
2750
|
+
start_column: start_column,
|
2751
|
+
start_char: start_char + index,
|
2752
|
+
end_line: start_line,
|
2753
|
+
end_column: start_column + operator.length,
|
2754
|
+
end_char: start_char + index + operator.length
|
2755
|
+
)
|
2756
|
+
|
2757
|
+
CommandCall.new(
|
2758
|
+
receiver: node.left,
|
2759
|
+
operator: nil,
|
2760
|
+
message: Op.new(value: operator, location: op_location),
|
2761
|
+
arguments:
|
2762
|
+
Args.new(parts: [node.right], location: node.right.location),
|
2763
|
+
block: nil,
|
2764
|
+
location: node.location
|
2765
|
+
)
|
2766
|
+
end
|
2767
|
+
|
2768
|
+
# When you have a begin..end while or begin..end until, it's a special
|
2769
|
+
# kind of syntax that executes the block in a loop. In this case the
|
2770
|
+
# parser gem has a special node type for it.
|
2771
|
+
def loop_post?(node)
|
2772
|
+
node.modifier? && node.statements.is_a?(Statements) &&
|
2773
|
+
node.statements.body.length == 1 &&
|
2774
|
+
node.statements.body.first.is_a?(Begin)
|
2775
|
+
end
|
2776
|
+
|
2777
|
+
# We need to find if we should transform this block into a numblock
|
2778
|
+
# since there could be new numbered variables like _1.
|
2779
|
+
def num_block_type(statements)
|
2780
|
+
variables = []
|
2781
|
+
queue = [statements]
|
2782
|
+
|
2783
|
+
while (child_node = queue.shift)
|
2784
|
+
if child_node.is_a?(VarRef) && child_node.value.is_a?(Ident) &&
|
2785
|
+
child_node.value.value =~ /^_(\d+)$/
|
2786
|
+
variables << $1.to_i
|
2787
|
+
end
|
2788
|
+
|
2789
|
+
queue += child_node.child_nodes.compact
|
2790
|
+
end
|
2791
|
+
|
2792
|
+
variables.max
|
2793
|
+
end
|
2794
|
+
|
2795
|
+
# This method comes almost directly from the parser gem and creates a new
|
2796
|
+
# parser gem node from the given s-expression. type is expected to be a
|
2797
|
+
# symbol, children is expected to be an array, and location is expected to
|
2798
|
+
# be a source map.
|
2799
|
+
def s(type, children, location)
|
2800
|
+
::Parser::AST::Node.new(type, children, location: location)
|
2801
|
+
end
|
2802
|
+
|
2803
|
+
# Constructs a plain source map just for an expression.
|
2804
|
+
def smap(expression)
|
2805
|
+
::Parser::Source::Map.new(expression)
|
2806
|
+
end
|
2807
|
+
|
2808
|
+
# Constructs a new source map for a collection.
|
2809
|
+
def smap_collection(begin_token, end_token, expression)
|
2810
|
+
::Parser::Source::Map::Collection.new(
|
2811
|
+
begin_token,
|
2812
|
+
end_token,
|
2813
|
+
expression
|
2814
|
+
)
|
2815
|
+
end
|
2816
|
+
|
2817
|
+
# Constructs a new source map for a collection without a begin or end.
|
2818
|
+
def smap_collection_bare(expression)
|
2819
|
+
smap_collection(nil, nil, expression)
|
2820
|
+
end
|
2821
|
+
|
2822
|
+
# Constructs a new source map for a conditional expression.
|
2823
|
+
def smap_condition(
|
2824
|
+
keyword,
|
2825
|
+
begin_token,
|
2826
|
+
else_token,
|
2827
|
+
end_token,
|
2828
|
+
expression
|
2829
|
+
)
|
2830
|
+
::Parser::Source::Map::Condition.new(
|
2831
|
+
keyword,
|
2832
|
+
begin_token,
|
2833
|
+
else_token,
|
2834
|
+
end_token,
|
2835
|
+
expression
|
2836
|
+
)
|
2837
|
+
end
|
2838
|
+
|
2839
|
+
# Constructs a new source map for a conditional expression with no begin
|
2840
|
+
# or end.
|
2841
|
+
def smap_condition_bare(expression)
|
2842
|
+
smap_condition(nil, nil, nil, nil, expression)
|
2843
|
+
end
|
2844
|
+
|
2845
|
+
# Constructs a new source map for a constant reference.
|
2846
|
+
def smap_constant(double_colon, name, expression)
|
2847
|
+
::Parser::Source::Map::Constant.new(double_colon, name, expression)
|
2848
|
+
end
|
2849
|
+
|
2850
|
+
# Constructs a new source map for a class definition.
|
2851
|
+
def smap_definition(keyword, operator, name, end_token)
|
2852
|
+
::Parser::Source::Map::Definition.new(
|
2853
|
+
keyword,
|
2854
|
+
operator,
|
2855
|
+
name,
|
2856
|
+
end_token
|
2857
|
+
)
|
2858
|
+
end
|
2859
|
+
|
2860
|
+
# Constructs a new source map for a for loop.
|
2861
|
+
def smap_for(keyword, in_token, begin_token, end_token, expression)
|
2862
|
+
::Parser::Source::Map::For.new(
|
2863
|
+
keyword,
|
2864
|
+
in_token,
|
2865
|
+
begin_token,
|
2866
|
+
end_token,
|
2867
|
+
expression
|
2868
|
+
)
|
2869
|
+
end
|
2870
|
+
|
2871
|
+
# Constructs a new source map for a heredoc.
|
2872
|
+
def smap_heredoc(expression, heredoc_body, heredoc_end)
|
2873
|
+
::Parser::Source::Map::Heredoc.new(
|
2874
|
+
expression,
|
2875
|
+
heredoc_body,
|
2876
|
+
heredoc_end
|
2877
|
+
)
|
2878
|
+
end
|
2879
|
+
|
2880
|
+
# Construct a source map for an index operation.
|
2881
|
+
def smap_index(begin_token, end_token, expression)
|
2882
|
+
::Parser::Source::Map::Index.new(begin_token, end_token, expression)
|
2883
|
+
end
|
2884
|
+
|
2885
|
+
# Constructs a new source map for the use of a keyword.
|
2886
|
+
def smap_keyword(keyword, begin_token, end_token, expression)
|
2887
|
+
::Parser::Source::Map::Keyword.new(
|
2888
|
+
keyword,
|
2889
|
+
begin_token,
|
2890
|
+
end_token,
|
2891
|
+
expression
|
2892
|
+
)
|
2893
|
+
end
|
2894
|
+
|
2895
|
+
# Constructs a new source map for the use of a keyword without a begin or
|
2896
|
+
# end token.
|
2897
|
+
def smap_keyword_bare(keyword, expression)
|
2898
|
+
smap_keyword(keyword, nil, nil, expression)
|
2899
|
+
end
|
2900
|
+
|
2901
|
+
# Constructs a new source map for a method definition.
|
2902
|
+
def smap_method_definition(
|
2903
|
+
keyword,
|
2904
|
+
operator,
|
2905
|
+
name,
|
2906
|
+
end_token,
|
2907
|
+
assignment,
|
2908
|
+
expression
|
2909
|
+
)
|
2910
|
+
::Parser::Source::Map::MethodDefinition.new(
|
2911
|
+
keyword,
|
2912
|
+
operator,
|
2913
|
+
name,
|
2914
|
+
end_token,
|
2915
|
+
assignment,
|
2916
|
+
expression
|
2917
|
+
)
|
2918
|
+
end
|
2919
|
+
|
2920
|
+
# Constructs a new source map for an operator.
|
2921
|
+
def smap_operator(operator, expression)
|
2922
|
+
::Parser::Source::Map::Operator.new(operator, expression)
|
2923
|
+
end
|
2924
|
+
|
2925
|
+
# Constructs a source map for the body of a rescue clause.
|
2926
|
+
def smap_rescue_body(keyword, assoc, begin_token, expression)
|
2927
|
+
::Parser::Source::Map::RescueBody.new(
|
2928
|
+
keyword,
|
2929
|
+
assoc,
|
2930
|
+
begin_token,
|
2931
|
+
expression
|
2932
|
+
)
|
2933
|
+
end
|
2934
|
+
|
2935
|
+
# Constructs a new source map for a method call.
|
2936
|
+
def smap_send(dot, selector, begin_token, end_token, expression)
|
2937
|
+
::Parser::Source::Map::Send.new(
|
2938
|
+
dot,
|
2939
|
+
selector,
|
2940
|
+
begin_token,
|
2941
|
+
end_token,
|
2942
|
+
expression
|
2943
|
+
)
|
2944
|
+
end
|
2945
|
+
|
2946
|
+
# Constructs a new source map for a method call without a begin or end.
|
2947
|
+
def smap_send_bare(selector, expression)
|
2948
|
+
smap_send(nil, selector, nil, nil, expression)
|
2949
|
+
end
|
2950
|
+
|
2951
|
+
# Constructs a new source map for a ternary expression.
|
2952
|
+
def smap_ternary(question, colon, expression)
|
2953
|
+
::Parser::Source::Map::Ternary.new(question, colon, expression)
|
2954
|
+
end
|
2955
|
+
|
2956
|
+
# Constructs a new source map for a variable.
|
2957
|
+
def smap_variable(name, expression)
|
2958
|
+
::Parser::Source::Map::Variable.new(name, expression)
|
2959
|
+
end
|
2960
|
+
|
2961
|
+
# Constructs a new source range from the given start and end offsets.
|
2962
|
+
def srange(start_char, end_char)
|
2963
|
+
::Parser::Source::Range.new(buffer, start_char, end_char)
|
2964
|
+
end
|
2965
|
+
|
2966
|
+
# Constructs a new source range by finding the given needle in the given
|
2967
|
+
# range of the source. If the needle is not found, returns nil.
|
2968
|
+
def srange_search(start_char, end_char, needle)
|
2969
|
+
index = buffer.source[start_char...end_char].index(needle)
|
2970
|
+
return unless index
|
2971
|
+
|
2972
|
+
offset = start_char + index
|
2973
|
+
srange(offset, offset + needle.length)
|
2974
|
+
end
|
2975
|
+
|
2976
|
+
# Constructs a new source range by searching for the given needle between
|
2977
|
+
# the end location of the start node and the start location of the end
|
2978
|
+
# node. If the needle is not found, returns nil.
|
2979
|
+
def srange_search_between(start_node, end_node, needle)
|
2980
|
+
srange_search(start_node.end_char, end_node.start_char, needle)
|
2981
|
+
end
|
2982
|
+
|
2983
|
+
# Constructs a new source range by finding the given needle in the given
|
2984
|
+
# range of the source. If it needle is not found, raises an error.
|
2985
|
+
def srange_find(start_char, end_char, needle)
|
2986
|
+
srange = srange_search(start_char, end_char, needle)
|
2987
|
+
|
2988
|
+
unless srange
|
2989
|
+
slice = buffer.source[start_char...end_char].inspect
|
2990
|
+
raise "Could not find #{needle.inspect} in #{slice}"
|
2991
|
+
end
|
2992
|
+
|
2993
|
+
srange
|
2994
|
+
end
|
2995
|
+
|
2996
|
+
# Constructs a new source range by finding the given needle between the
|
2997
|
+
# end location of the start node and the start location of the end node.
|
2998
|
+
# If the needle is not found, returns raises an error.
|
2999
|
+
def srange_find_between(start_node, end_node, needle)
|
3000
|
+
srange_find(start_node.end_char, end_node.start_char, needle)
|
3001
|
+
end
|
3002
|
+
|
3003
|
+
# Constructs a new source range from the given start offset and length.
|
3004
|
+
def srange_length(start_char, length)
|
3005
|
+
if length > 0
|
3006
|
+
srange(start_char, start_char + length)
|
3007
|
+
else
|
3008
|
+
srange(start_char + length, start_char)
|
3009
|
+
end
|
3010
|
+
end
|
3011
|
+
|
3012
|
+
# Constructs a new source range using the given node's location.
|
3013
|
+
def srange_node(node)
|
3014
|
+
location = node.location
|
3015
|
+
srange(location.start_char, location.end_char)
|
3016
|
+
end
|
3017
|
+
end
|
3018
|
+
end
|
3019
|
+
end
|