prettier 2.0.0 → 3.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.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +52 -6
  3. data/README.md +17 -16
  4. data/exe/rbprettier +2 -2
  5. data/lib/prettier/rake/task.rb +5 -5
  6. data/lib/prettier.rb +12 -11
  7. data/node_modules/prettier/bin-prettier.js +48 -18924
  8. data/node_modules/prettier/cli.js +12335 -0
  9. data/node_modules/prettier/doc.js +1306 -4755
  10. data/node_modules/prettier/index.js +37468 -57614
  11. data/node_modules/prettier/package.json +3 -2
  12. data/node_modules/prettier/parser-angular.js +2 -66
  13. data/node_modules/prettier/parser-babel.js +27 -22
  14. data/node_modules/prettier/parser-espree.js +26 -22
  15. data/node_modules/prettier/parser-flow.js +26 -22
  16. data/node_modules/prettier/parser-glimmer.js +27 -1
  17. data/node_modules/prettier/parser-graphql.js +15 -1
  18. data/node_modules/prettier/parser-html.js +21 -117
  19. data/node_modules/prettier/parser-markdown.js +61 -19
  20. data/node_modules/prettier/parser-meriyah.js +19 -22
  21. data/node_modules/prettier/parser-postcss.js +76 -22
  22. data/node_modules/prettier/parser-typescript.js +280 -22
  23. data/node_modules/prettier/parser-yaml.js +150 -15
  24. data/node_modules/prettier/third-party.js +8660 -11030
  25. data/package.json +11 -25
  26. data/rubocop.yml +6 -6
  27. data/src/getInfo.js +23 -0
  28. data/{dist/parser → src}/netcat.js +0 -1
  29. data/src/parseSync.js +216 -0
  30. data/src/plugin.js +170 -0
  31. data/{dist/parser → src}/server.rb +50 -27
  32. metadata +95 -75
  33. data/bin/console +0 -7
  34. data/dist/haml/embed.js +0 -53
  35. data/dist/haml/parser.js +0 -31
  36. data/dist/haml/parser.rb +0 -143
  37. data/dist/haml/printer.js +0 -336
  38. data/dist/parser/getInfo.js +0 -17
  39. data/dist/parser/parseSync.js +0 -179
  40. data/dist/plugin.js +0 -143
  41. data/dist/prettier.js +0 -15
  42. data/dist/rbs/parser.js +0 -34
  43. data/dist/rbs/parser.rb +0 -98
  44. data/dist/rbs/printer.js +0 -517
  45. data/dist/ruby/embed.js +0 -110
  46. data/dist/ruby/nodes/alias.js +0 -59
  47. data/dist/ruby/nodes/aref.js +0 -53
  48. data/dist/ruby/nodes/args.js +0 -165
  49. data/dist/ruby/nodes/arrays.js +0 -126
  50. data/dist/ruby/nodes/assign.js +0 -41
  51. data/dist/ruby/nodes/blocks.js +0 -87
  52. data/dist/ruby/nodes/calls.js +0 -260
  53. data/dist/ruby/nodes/case.js +0 -50
  54. data/dist/ruby/nodes/class.js +0 -54
  55. data/dist/ruby/nodes/commands.js +0 -124
  56. data/dist/ruby/nodes/conditionals.js +0 -242
  57. data/dist/ruby/nodes/constants.js +0 -38
  58. data/dist/ruby/nodes/flow.js +0 -66
  59. data/dist/ruby/nodes/hashes.js +0 -130
  60. data/dist/ruby/nodes/heredocs.js +0 -30
  61. data/dist/ruby/nodes/hooks.js +0 -35
  62. data/dist/ruby/nodes/ints.js +0 -27
  63. data/dist/ruby/nodes/lambdas.js +0 -69
  64. data/dist/ruby/nodes/loops.js +0 -73
  65. data/dist/ruby/nodes/massign.js +0 -73
  66. data/dist/ruby/nodes/methods.js +0 -70
  67. data/dist/ruby/nodes/operators.js +0 -70
  68. data/dist/ruby/nodes/params.js +0 -89
  69. data/dist/ruby/nodes/patterns.js +0 -122
  70. data/dist/ruby/nodes/regexp.js +0 -45
  71. data/dist/ruby/nodes/rescue.js +0 -85
  72. data/dist/ruby/nodes/return.js +0 -75
  73. data/dist/ruby/nodes/statements.js +0 -111
  74. data/dist/ruby/nodes/strings.js +0 -218
  75. data/dist/ruby/nodes/super.js +0 -30
  76. data/dist/ruby/nodes/undef.js +0 -26
  77. data/dist/ruby/nodes.js +0 -151
  78. data/dist/ruby/parser.js +0 -34
  79. data/dist/ruby/parser.rb +0 -3636
  80. data/dist/ruby/printer.js +0 -129
  81. data/dist/ruby/toProc.js +0 -93
  82. data/dist/types/haml.js +0 -4
  83. data/dist/types/plugin.js +0 -3
  84. data/dist/types/rbs.js +0 -4
  85. data/dist/types/ruby.js +0 -4
  86. data/dist/types/utils.js +0 -2
  87. data/dist/types.js +0 -30
  88. data/dist/utils/containsAssignment.js +0 -15
  89. data/dist/utils/getTrailingComma.js +0 -6
  90. data/dist/utils/hasAncestor.js +0 -15
  91. data/dist/utils/inlineEnsureParens.js +0 -49
  92. data/dist/utils/isEmptyBodyStmt.js +0 -10
  93. data/dist/utils/isEmptyStmts.js +0 -10
  94. data/dist/utils/literal.js +0 -8
  95. data/dist/utils/literallineWithoutBreakParent.js +0 -8
  96. data/dist/utils/makeCall.js +0 -13
  97. data/dist/utils/noIndent.js +0 -11
  98. data/dist/utils/printEmptyCollection.js +0 -44
  99. data/dist/utils/skipAssignIndent.js +0 -15
  100. data/dist/utils.js +0 -30
data/dist/ruby/parser.rb DELETED
@@ -1,3636 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # We implement our own version checking here instead of using Gem::Version so
4
- # that we can use the --disable-gems flag.
5
- RUBY_MAJOR, RUBY_MINOR, RUBY_PATCH, * = RUBY_VERSION.split('.').map(&:to_i)
6
-
7
- if (RUBY_MAJOR < 2) || ((RUBY_MAJOR == 2) && (RUBY_MINOR < 5))
8
- warn(
9
- "Ruby version #{RUBY_VERSION} not supported. " \
10
- 'Please upgrade to 2.5.0 or above.'
11
- )
12
-
13
- exit 1
14
- end
15
-
16
- require 'json' unless defined?(JSON)
17
- require 'ripper'
18
-
19
- # Ensure the module is already defined. This is mostly so that we don't have to
20
- # indent the Parser definition one more time.
21
- module Prettier
22
- end
23
-
24
- class Prettier::Parser < Ripper
25
- # Represents a line in the source. If this class is being used, it means that
26
- # every character in the string is 1 byte in length, so we can just return the
27
- # start of the line + the index.
28
- class SingleByteString
29
- def initialize(start)
30
- @start = start
31
- end
32
-
33
- def [](byteindex)
34
- @start + byteindex
35
- end
36
- end
37
-
38
- # Represents a line in the source. If this class is being used, it means that
39
- # there are characters in the string that are multi-byte, so we will build up
40
- # an array of indices, such that array[byteindex] will be equal to the index
41
- # of the character within the string.
42
- class MultiByteString
43
- def initialize(start, line)
44
- @indices = []
45
-
46
- line
47
- .each_char
48
- .with_index(start) do |char, index|
49
- char.bytesize.times { @indices << index }
50
- end
51
- end
52
-
53
- def [](byteindex)
54
- @indices[byteindex]
55
- end
56
- end
57
-
58
- # This is a small wrapper around the value of a node for those specific events
59
- # that need extra handling. (For example: statement, body statement, and
60
- # rescue nodes which all need extra information to determine their character
61
- # boundaries.)
62
- class Node
63
- attr_reader :parser, :value
64
-
65
- def initialize(parser, value)
66
- @parser = parser
67
- @value = value
68
- end
69
-
70
- def [](key)
71
- value[key]
72
- end
73
-
74
- def dig(*keys)
75
- value.dig(*keys)
76
- end
77
-
78
- def to_json(*opts)
79
- value.to_json(*opts)
80
- end
81
-
82
- def pretty_print(q)
83
- q.pp_hash(self)
84
- end
85
- end
86
-
87
- # A special parser error so that we can get nice syntax displays on the error
88
- # message when prettier prints out the results.
89
- class ParserError < StandardError
90
- attr_reader :lineno, :column
91
-
92
- def initialize(error, lineno, column)
93
- super(error)
94
- @lineno = lineno
95
- @column = column
96
- end
97
- end
98
-
99
- attr_reader :source, :lines, :scanner_events
100
-
101
- # This is an attr_accessor so Stmts objects can grab comments out of this
102
- # array and attach them to themselves.
103
- attr_accessor :comments
104
-
105
- def initialize(source, *args)
106
- super(source, *args)
107
-
108
- # We keep the source around so that we can refer back to it when we're
109
- # generating the AST. Sometimes it's easier to just reference the source
110
- # string when you want to check if it contains a certain character, for
111
- # example.
112
- @source = source
113
-
114
- # Similarly, we keep the lines of the source string around to be able to
115
- # check if certain lines contain certain characters. For example, we'll use
116
- # this to generate the content that goes after the __END__ keyword. Or we'll
117
- # use this to check if a comment has other content on its line.
118
- @lines = source.split("\n")
119
-
120
- # This is the full set of comments that have been found by the parser. It's
121
- # a running list. At the end of every block of statements, they will go in
122
- # and attempt to grab any comments that are on their own line and turn them
123
- # into regular statements. So at the end of parsing the only comments left
124
- # in here will be comments on lines that also contain code.
125
- @comments = []
126
-
127
- # This is the current embdoc (comments that start with =begin and end with
128
- # =end). Since they can't be nested, there's no need for a stack here, as
129
- # there can only be one active. These end up getting dumped into the
130
- # comments list before getting picked up by the statements that surround
131
- # them.
132
- @embdoc = nil
133
-
134
- # This is an optional node that can be present if the __END__ keyword is
135
- # used in the file. In that case, this will represent the content after that
136
- # keyword.
137
- @__end__ = nil
138
-
139
- # Heredocs can actually be nested together if you're using interpolation, so
140
- # this is a stack of heredoc nodes that are currently being created. When we
141
- # get to the scanner event that finishes off a heredoc node, we pop the top
142
- # one off. If there are others surrounding it, then the body events will now
143
- # be added to the correct nodes.
144
- @heredocs = []
145
-
146
- # This is a running list of scanner events that have fired. It's useful
147
- # mostly for maintaining location information. For example, if you're inside
148
- # the handle of a def event, then in order to determine where the AST node
149
- # started, you need to look backward in the scanner events to find a def
150
- # keyword. Most of the time, when a parser event consumes one of these
151
- # events, it will be deleted from the list. So ideally, this list stays
152
- # pretty short over the course of parsing a source string.
153
- @scanner_events = []
154
-
155
- # Here we're going to build up a list of SingleByteString or MultiByteString
156
- # objects. They're each going to represent a string in the source. They are
157
- # used by the `char_pos` method to determine where we are in the source
158
- # string.
159
- @line_counts = []
160
- last_index = 0
161
-
162
- @source.lines.each do |line|
163
- if line.size == line.bytesize
164
- @line_counts << SingleByteString.new(last_index)
165
- else
166
- @line_counts << MultiByteString.new(last_index, line)
167
- end
168
-
169
- last_index += line.size
170
- end
171
- end
172
-
173
- def self.parse(source)
174
- builder = new(source)
175
-
176
- response = builder.parse
177
- response unless builder.error?
178
- end
179
-
180
- private
181
-
182
- # This represents the current place in the source string that we've gotten to
183
- # so far. We have a memoized line_counts object that we can use to get the
184
- # number of characters that we've had to go through to get to the beginning of
185
- # this line, then we add the number of columns into this line that we've gone
186
- # through.
187
- def char_pos
188
- @line_counts[lineno - 1][column]
189
- end
190
-
191
- # As we build up a list of scanner events, we'll periodically need to go
192
- # backwards and find the ones that we've already hit in order to determine the
193
- # location information for nodes that use them. For example, if you have a
194
- # module node then you'll look backward for a @module scanner event to
195
- # determine your start location.
196
- #
197
- # This works with nesting since we're deleting scanner events from the list
198
- # once they've been used up. For example if you had nested module declarations
199
- # then the innermost declaration would grab the last @module event (which
200
- # would happen to be the innermost keyword). Then the outer one would only be
201
- # able to grab the first one. In this way all of the scanner events act as
202
- # their own stack.
203
- def find_scanner_event(type, body = :any, consume: true)
204
- index =
205
- scanner_events.rindex do |scanner_event|
206
- scanner_event[:type] == type &&
207
- (body == :any || (scanner_event[:body] == body))
208
- end
209
-
210
- if consume
211
- # If we're expecting to be able to find a scanner event and consume it,
212
- # but can't actually find it, then we need to raise an error. This is
213
- # _usually_ caused by a syntax error in the source that we're printing. It
214
- # could also be caused by accidentally attempting to consume a scanner
215
- # event twice by two different parser event handlers.
216
- unless index
217
- message = "Cannot find expected #{body == :any ? type : body}"
218
- raise ParserError.new(message, lineno, column)
219
- end
220
-
221
- scanner_events.delete_at(index)
222
- elsif index
223
- scanner_events[index]
224
- end
225
- end
226
-
227
- # A helper function to find a :: operator. We do special handling instead of
228
- # using find_scanner_event here because we don't pop off all of the ::
229
- # operators so you could end up getting the wrong information if you have for
230
- # instance ::X::Y::Z.
231
- def find_colon2_before(const)
232
- index =
233
- scanner_events.rindex do |event|
234
- event[:type] == :@op && event[:body] == '::' && event[:sc] < const[:sc]
235
- end
236
-
237
- scanner_events[index]
238
- end
239
-
240
- # Finds the next position in the source string that begins a statement. This
241
- # is used to bind statements lists and make sure they don't include a
242
- # preceding comment. For example, we want the following comment to be attached
243
- # to the class node and not the statement node:
244
- #
245
- # class Foo # :nodoc:
246
- # ...
247
- # end
248
- #
249
- # By finding the next non-space character, we can make sure that the bounds of
250
- # the statement list are correct.
251
- def find_next_statement_start(position)
252
- remaining = source[position..-1]
253
-
254
- if remaining.sub(/\A +/, '')[0] == '#'
255
- return position + remaining.index("\n")
256
- end
257
-
258
- position
259
- end
260
-
261
- # BEGIN is a parser event that represents the use of the BEGIN keyword, which
262
- # hooks into the lifecycle of the interpreter. Whatever is inside the "block"
263
- # will get executed when the program starts. The syntax looks like the
264
- # following:
265
- #
266
- # BEGIN {
267
- # # execute stuff here
268
- # }
269
- #
270
- def on_BEGIN(stmts)
271
- beging = find_scanner_event(:@lbrace)
272
- ending = find_scanner_event(:@rbrace)
273
-
274
- stmts.bind(find_next_statement_start(beging[:ec]), ending[:sc])
275
-
276
- find_scanner_event(:@kw, 'BEGIN').merge!(
277
- type: :BEGIN,
278
- body: [beging, stmts],
279
- el: ending[:el],
280
- ec: ending[:ec]
281
- )
282
- end
283
-
284
- # CHAR is a parser event that represents a single codepoint in the script
285
- # encoding. For example:
286
- #
287
- # ?a
288
- #
289
- # is a representation of the string literal "a". You can use control
290
- # characters with this as well, as in ?\C-a.
291
- #
292
- def on_CHAR(value)
293
- start_line = lineno
294
- start_char = char_pos
295
-
296
- node = {
297
- type: :@CHAR,
298
- body: value,
299
- sl: start_line,
300
- el: start_line,
301
- sc: start_char,
302
- ec: start_char + value.size
303
- }
304
-
305
- scanner_events << node
306
- node
307
- end
308
-
309
- # END is a parser event that represents the use of the END keyword, which
310
- # hooks into the lifecycle of the interpreter. Whatever is inside the "block"
311
- # will get executed when the program ends. The syntax looks like the
312
- # following:
313
- #
314
- # END {
315
- # # execute stuff here
316
- # }
317
- #
318
- def on_END(stmts)
319
- beging = find_scanner_event(:@lbrace)
320
- ending = find_scanner_event(:@rbrace)
321
-
322
- stmts.bind(find_next_statement_start(beging[:ec]), ending[:sc])
323
-
324
- find_scanner_event(:@kw, 'END').merge!(
325
- type: :END,
326
- body: [beging, stmts],
327
- el: ending[:el],
328
- ec: ending[:ec]
329
- )
330
- end
331
-
332
- # __END__ is a scanner event that represents __END__ syntax, which allows
333
- # individual scripts to keep content after the main ruby code that can be read
334
- # through the DATA constant. It looks like:
335
- #
336
- # puts DATA.read
337
- #
338
- # __END__
339
- # some other content that isn't executed by the program
340
- #
341
- def on___end__(value)
342
- start_line = lineno
343
- start_char = char_pos
344
-
345
- @__end__ = {
346
- type: :@__end__,
347
- body: lines[lineno..-1].join("\n"),
348
- sl: start_line,
349
- el: start_line,
350
- sc: start_char,
351
- ec: start_char + value.size
352
- }
353
- end
354
-
355
- # alias is a parser event that represents the use of the alias keyword with
356
- # regular arguments. This can be either symbol literals or bare words. You can
357
- # optionally use parentheses with this keyword, so we either track the
358
- # location information based on those or the final argument to the alias
359
- # method.
360
- def on_alias(left, right)
361
- beging = find_scanner_event(:@kw, 'alias')
362
-
363
- paren = source[beging[:ec]...left[:sc]].include?('(')
364
- ending = paren ? find_scanner_event(:@rparen) : right
365
-
366
- {
367
- type: :alias,
368
- body: [left, right],
369
- sl: beging[:sl],
370
- sc: beging[:sc],
371
- el: ending[:el],
372
- ec: ending[:ec]
373
- }
374
- end
375
-
376
- # aref is a parser event when you're pulling a value out of a collection at a
377
- # specific index. Put another way, it's any time you're calling the method
378
- # #[]. As an example:
379
- #
380
- # foo[index]
381
- #
382
- # The nodes usually contains two children, the collection and the index.
383
- # In some cases, you don't necessarily have the second child node, because
384
- # you can call procs with a pretty esoteric syntax. In the following
385
- # example, you wouldn't have a second child, and "foo" would be the first
386
- # child:
387
- #
388
- # foo[]
389
- #
390
- def on_aref(collection, index)
391
- find_scanner_event(:@lbracket)
392
- ending = find_scanner_event(:@rbracket)
393
-
394
- {
395
- type: :aref,
396
- body: [collection, index],
397
- sl: collection[:sl],
398
- sc: collection[:sc],
399
- el: ending[:el],
400
- ec: ending[:ec]
401
- }
402
- end
403
-
404
- # aref_field is a parser event that is very similar to aref except that it
405
- # is being used inside of an assignment.
406
- def on_aref_field(collection, index)
407
- find_scanner_event(:@lbracket)
408
- ending = find_scanner_event(:@rbracket)
409
-
410
- {
411
- type: :aref_field,
412
- body: [collection, index],
413
- sl: collection[:sl],
414
- sc: collection[:sc],
415
- el: ending[:el],
416
- ec: ending[:ec]
417
- }
418
- end
419
-
420
- # arg_ambiguous is a parser event that represents when the parser sees an
421
- # argument as ambiguous. For example, in the following snippet:
422
- #
423
- # foo //
424
- #
425
- # the question becomes if the forward slash is being used as a division
426
- # operation or if it's the start of a regular expression. We don't need to
427
- # track this event in the AST that we're generating, so we're not going to
428
- # define an explicit handler for it.
429
- #
430
- # def on_arg_ambiguous(value)
431
- # value
432
- # end
433
-
434
- # arg_paren is a parser event that represents wrapping arguments to a method
435
- # inside a set of parentheses. For example, in the follow snippet:
436
- #
437
- # foo(bar)
438
- #
439
- # there would be an arg_paren node around the args_add_block node that
440
- # represents the set of arguments being sent to the foo method. The args child
441
- # node can be nil if no arguments were passed, as in:
442
- #
443
- # foo()
444
- #
445
- def on_arg_paren(args)
446
- beging = find_scanner_event(:@lparen)
447
- rparen = find_scanner_event(:@rparen)
448
-
449
- # If the arguments exceed the ending of the parentheses, then we know we
450
- # have a heredoc in the arguments, and we need to use the bounds of the
451
- # arguments to determine how large the arg_paren is.
452
- ending = (args && args[:el] > rparen[:el]) ? args : rparen
453
-
454
- {
455
- type: :arg_paren,
456
- body: [args],
457
- sl: beging[:sl],
458
- sc: beging[:sc],
459
- el: ending[:el],
460
- ec: ending[:ec]
461
- }
462
- end
463
-
464
- # args_add is a parser event that represents a single argument inside a list
465
- # of arguments to any method call or an array. It accepts as arguments the
466
- # parent args node as well as an arg which can be anything that could be
467
- # passed as an argument.
468
- def on_args_add(args, arg)
469
- if args[:body].empty?
470
- # If this is the first argument being passed into the list of arguments,
471
- # then we're going to use the bounds of the argument to override the
472
- # parent node's location since this will be more accurate.
473
- arg.merge(type: :args, body: [arg])
474
- else
475
- args.merge!(body: args[:body] << arg, el: arg[:el], ec: arg[:ec])
476
- end
477
- end
478
-
479
- # args_add_block is a parser event that represents a list of arguments and
480
- # potentially a block argument. If no block is passed, then the second
481
- # argument will be the literal false.
482
- def on_args_add_block(args, block)
483
- ending = block || args
484
-
485
- args.merge(
486
- type: :args_add_block,
487
- body: [args, block],
488
- el: ending[:el],
489
- ec: ending[:ec]
490
- )
491
- end
492
-
493
- # args_add_star is a parser event that represents adding a splat of values
494
- # to a list of arguments. If accepts as arguments the parent args node as
495
- # well as the part that is being splatted.
496
- def on_args_add_star(args, part)
497
- beging = find_scanner_event(:@op, '*')
498
- ending = part || beging
499
-
500
- {
501
- type: :args_add_star,
502
- body: [args, part],
503
- sl: beging[:sl],
504
- sc: beging[:sc],
505
- el: ending[:el],
506
- ec: ending[:ec]
507
- }
508
- end
509
-
510
- # args_forward is a parser event that represents forwarding all kinds of
511
- # arguments onto another method call.
512
- def on_args_forward
513
- find_scanner_event(:@op, '...').merge!(type: :args_forward)
514
- end
515
-
516
- # args_new is a parser event that represents the beginning of a list of
517
- # arguments to any method call or an array. It can be followed by any
518
- # number of args_add events, which we'll append onto an array body.
519
- def on_args_new
520
- {
521
- type: :args,
522
- body: [],
523
- sl: lineno,
524
- sc: char_pos,
525
- el: lineno,
526
- ec: char_pos
527
- }
528
- end
529
-
530
- # Array nodes can contain a myriad of subnodes because of the special
531
- # array literal syntax like %w and %i. As a result, we may be looking for
532
- # an left bracket, or we may be just looking at the children to get the
533
- # bounds.
534
- def on_array(contents)
535
- if !contents || %i[args args_add_star].include?(contents[:type])
536
- beging = find_scanner_event(:@lbracket)
537
- ending = find_scanner_event(:@rbracket)
538
-
539
- {
540
- type: :array,
541
- body: [contents],
542
- sl: beging[:sl],
543
- sc: beging[:sc],
544
- el: ending[:el],
545
- ec: ending[:ec]
546
- }
547
- else
548
- ending = find_scanner_event(:@tstring_end)
549
- contents[:ec] = ending[:ec]
550
-
551
- ending.merge!(
552
- type: :array,
553
- body: [contents],
554
- sl: contents[:sl],
555
- sc: contents[:sc]
556
- )
557
- end
558
- end
559
-
560
- # aryptn is a parser event that represents matching against an array pattern
561
- # using the Ruby 2.7+ pattern matching syntax.
562
- def on_aryptn(const, preargs, splatarg, postargs)
563
- pieces = [const, *preargs, splatarg, *postargs].compact
564
-
565
- {
566
- type: :aryptn,
567
- body: [const, preargs, splatarg, postargs],
568
- sl: pieces[0][:sl],
569
- sc: pieces[0][:sc],
570
- el: pieces[-1][:el],
571
- ec: pieces[-1][:ec]
572
- }
573
- end
574
-
575
- # assign is a parser event that represents assigning something to a
576
- # variable or constant. It accepts as arguments the left side of the
577
- # expression before the equals sign and the right side of the expression.
578
- def on_assign(left, right)
579
- left.merge(
580
- type: :assign,
581
- body: [left, right],
582
- el: right[:el],
583
- ec: right[:ec]
584
- )
585
- end
586
-
587
- # assoc_new is a parser event that contains a key-value pair within a
588
- # hash. It is a child event of either an assoclist_from_args or a
589
- # bare_assoc_hash.
590
- def on_assoc_new(key, value)
591
- {
592
- type: :assoc_new,
593
- body: [key, value],
594
- sl: key[:sl],
595
- sc: key[:sc],
596
- el: value[:el],
597
- ec: value[:ec]
598
- }
599
- end
600
-
601
- # assoc_splat is a parser event that represents splatting a value into a
602
- # hash (either a hash literal or a bare hash in a method call).
603
- def on_assoc_splat(contents)
604
- find_scanner_event(:@op, '**').merge!(
605
- type: :assoc_splat,
606
- body: [contents],
607
- el: contents[:el],
608
- ec: contents[:ec]
609
- )
610
- end
611
-
612
- # assoclist_from_args is a parser event that contains a list of all of the
613
- # associations inside of a hash literal. Its parent node is always a hash.
614
- # It accepts as an argument an array of assoc events (either assoc_new or
615
- # assoc_splat).
616
- def on_assoclist_from_args(assocs)
617
- {
618
- type: :assoclist_from_args,
619
- body: assocs,
620
- sl: assocs[0][:sl],
621
- sc: assocs[0][:sc],
622
- el: assocs[-1][:el],
623
- ec: assocs[-1][:ec]
624
- }
625
- end
626
-
627
- # backref is a scanner event that represents a global variable referencing a
628
- # matched value. It comes in the form of a $ followed by a positive integer.
629
- def on_backref(value)
630
- start_line = lineno
631
- start_char = char_pos
632
-
633
- node = {
634
- type: :@backref,
635
- body: value,
636
- sl: start_line,
637
- el: start_line,
638
- sc: start_char,
639
- ec: start_char + value.size
640
- }
641
-
642
- scanner_events << node
643
- node
644
- end
645
-
646
- # backtick is a scanner event that represents the use of the ` operator. It's
647
- # usually found being used for an xstring, but could also be found as the name
648
- # of a method being defined.
649
- def on_backtick(value)
650
- start_line = lineno
651
- start_char = char_pos
652
-
653
- node = {
654
- type: :@backtick,
655
- body: value,
656
- sl: start_line,
657
- el: start_line,
658
- sc: start_char,
659
- ec: start_char + value.size
660
- }
661
-
662
- scanner_events << node
663
- node
664
- end
665
-
666
- # bare_assoc_hash is a parser event that represents a hash of contents
667
- # being passed as a method argument (and therefore has omitted braces). It
668
- # accepts as an argument an array of assoc events (either assoc_new or
669
- # assoc_splat).
670
- def on_bare_assoc_hash(assoc_news)
671
- {
672
- type: :bare_assoc_hash,
673
- body: assoc_news,
674
- sl: assoc_news[0][:sl],
675
- sc: assoc_news[0][:sc],
676
- el: assoc_news[-1][:el],
677
- ec: assoc_news[-1][:ec]
678
- }
679
- end
680
-
681
- # begin is a parser event that represents the beginning of a begin..end chain.
682
- # It includes a bodystmt event that has all of the consequent clauses.
683
- def on_begin(bodystmt)
684
- beging = find_scanner_event(:@kw, 'begin')
685
- ec =
686
- if bodystmt[:body][1..-1].any?
687
- bodystmt[:ec]
688
- else
689
- find_scanner_event(:@kw, 'end')[:ec]
690
- end
691
-
692
- bodystmt.bind(beging[:ec], ec)
693
-
694
- beging.merge!(
695
- type: :begin,
696
- body: [bodystmt],
697
- el: bodystmt[:el],
698
- ec: bodystmt[:ec]
699
- )
700
- end
701
-
702
- # binary is a parser event that represents a binary operation between two
703
- # values.
704
- def on_binary(left, oper, right)
705
- # On most Ruby implementations, oper is a Symbol that represents that
706
- # operation being performed. For instance in the example `1 < 2`, the `oper`
707
- # object would be `:<`. However, on JRuby, it's an `@op` node, so here we're
708
- # going to explicitly convert it into the same normalized form.
709
- oper = scanner_events.delete(oper)[:body] unless oper.is_a?(Symbol)
710
-
711
- {
712
- type: :binary,
713
- body: [left, oper, right],
714
- sl: left[:sl],
715
- sc: left[:sc],
716
- el: right[:el],
717
- ec: right[:ec]
718
- }
719
- end
720
-
721
- # block_var is a parser event that represents the parameters being passed to
722
- # block. Effectively they're everything contained within the pipes.
723
- def on_block_var(params, locals)
724
- index =
725
- scanner_events.rindex do |event|
726
- event[:type] == :@op && %w[| ||].include?(event[:body]) &&
727
- event[:sc] < params[:sc]
728
- end
729
-
730
- beging = scanner_events[index]
731
- ending = scanner_events[-1]
732
-
733
- {
734
- type: :block_var,
735
- body: [params, locals],
736
- sl: beging[:sl],
737
- sc: beging[:sc],
738
- el: ending[:el],
739
- ec: ending[:ec]
740
- }
741
- end
742
-
743
- # blockarg is a parser event that represents defining a block variable on
744
- # a method definition.
745
- def on_blockarg(ident)
746
- find_scanner_event(:@op, '&').merge!(
747
- type: :blockarg,
748
- body: [ident],
749
- el: ident[:el],
750
- ec: ident[:ec]
751
- )
752
- end
753
-
754
- # bodystmt can't actually determine its bounds appropriately because it
755
- # doesn't necessarily know where it started. So the parent node needs to
756
- # report back down into this one where it goes.
757
- class BodyStmt < Node
758
- def bind(sc, ec)
759
- value.merge!(sc: sc, ec: ec)
760
- parts = value[:body]
761
-
762
- # Here we're going to determine the bounds for the stmts
763
- consequent = parts[1..-1].compact.first
764
- value[:body][0].bind(sc, consequent ? consequent[:sc] : ec)
765
-
766
- # Next we're going to determine the rescue clause if there is one
767
- if parts[1]
768
- consequent = parts[2..-1].compact.first
769
- value[:body][1].bind_end(consequent ? consequent[:sc] : ec)
770
- end
771
- end
772
- end
773
-
774
- # bodystmt is a parser event that represents all of the possible combinations
775
- # of clauses within the body of a method or block.
776
- def on_bodystmt(stmts, rescued, ensured, elsed)
777
- BodyStmt.new(
778
- self,
779
- type: :bodystmt,
780
- body: [stmts, rescued, ensured, elsed],
781
- sl: lineno,
782
- sc: char_pos,
783
- el: lineno,
784
- ec: char_pos
785
- )
786
- end
787
-
788
- # brace_block is a parser event that represents passing a block to a
789
- # method call using the {..} operators. It accepts as arguments an
790
- # optional block_var event that represents any parameters to the block as
791
- # well as a stmts event that represents the statements inside the block.
792
- def on_brace_block(block_var, stmts)
793
- beging = find_scanner_event(:@lbrace)
794
- ending = find_scanner_event(:@rbrace)
795
-
796
- stmts.bind(
797
- find_next_statement_start((block_var || beging)[:ec]),
798
- ending[:sc]
799
- )
800
-
801
- {
802
- type: :brace_block,
803
- body: [block_var, stmts],
804
- beging: beging,
805
- sl: beging[:sl],
806
- sc: beging[:sc],
807
- el: [ending[:el], stmts[:el]].max,
808
- ec: ending[:ec]
809
- }
810
- end
811
-
812
- # break is a parser event that represents using the break keyword. It
813
- # accepts as an argument an args or args_add_block event that contains all
814
- # of the arguments being passed to the break.
815
- def on_break(args_add_block)
816
- beging = find_scanner_event(:@kw, 'break')
817
-
818
- # You can hit this if you are passing no arguments to break but it has a
819
- # comment right after it. In that case we can just use the location
820
- # information straight from the keyword.
821
- if args_add_block[:type] == :args
822
- return beging.merge!(type: :break, body: [args_add_block])
823
- end
824
-
825
- beging.merge!(
826
- type: :break,
827
- body: [args_add_block],
828
- el: args_add_block[:el],
829
- ec: args_add_block[:ec]
830
- )
831
- end
832
-
833
- # call is a parser event representing a method call with no arguments. It
834
- # accepts as arguments the receiver of the method, the operator being used
835
- # to send the method (., ::, or &.), and the value that is being sent to
836
- # the receiver (which can be another nested call as well).
837
- #
838
- # There is one esoteric syntax that comes into play here as well. If the
839
- # sending argument to this method is the symbol :call, then it represents
840
- # calling a lambda in a very odd looking way, as in:
841
- #
842
- # foo.(1, 2, 3)
843
- #
844
- def on_call(receiver, oper, sending)
845
- ending = sending
846
-
847
- if sending == :call
848
- ending = oper
849
-
850
- # Special handling here for Ruby <= 2.5 because the oper argument to this
851
- # method wasn't a parser event here it was just a plain symbol.
852
- ending = receiver if RUBY_MAJOR <= 2 && RUBY_MINOR <= 5
853
- end
854
-
855
- {
856
- type: :call,
857
- body: [receiver, oper, sending],
858
- sl: receiver[:sl],
859
- sc: receiver[:sc],
860
- el: [ending[:el], receiver[:el]].max,
861
- ec: ending[:ec]
862
- }
863
- end
864
-
865
- # case is a parser event that represents the beginning of a case chain.
866
- # It accepts as arguments the switch of the case and the consequent
867
- # clause.
868
- def on_case(switch, consequent)
869
- beging =
870
- if event = find_scanner_event(:@kw, 'case', consume: false)
871
- scanner_events.delete(event).merge!(type: :case)
872
- else
873
- keyword = find_scanner_event(:@kw, 'in', consume: false)
874
- switch.merge(type: :rassign, keyword: keyword)
875
- end
876
-
877
- beging.merge!(
878
- body: [switch, consequent],
879
- el: consequent[:el],
880
- ec: consequent[:ec]
881
- )
882
- end
883
-
884
- # class is a parser event that represents defining a class. It accepts as
885
- # arguments the name of the class, the optional name of the superclass,
886
- # and the bodystmt event that represents the statements evaluated within
887
- # the context of the class.
888
- def on_class(const, superclass, bodystmt)
889
- beging = find_scanner_event(:@kw, 'class')
890
- ending = find_scanner_event(:@kw, 'end')
891
-
892
- bodystmt.bind(
893
- find_next_statement_start((superclass || const)[:ec]),
894
- ending[:sc]
895
- )
896
-
897
- {
898
- type: :class,
899
- body: [const, superclass, bodystmt],
900
- sl: beging[:sl],
901
- sc: beging[:sc],
902
- el: ending[:el],
903
- ec: ending[:ec]
904
- }
905
- end
906
-
907
- # comma is a scanner event that represents the use of the comma operator.
908
- def on_comma(value)
909
- start_line = lineno
910
- start_char = char_pos
911
-
912
- node = {
913
- type: :@comma,
914
- body: value,
915
- sl: start_line,
916
- el: start_line,
917
- sc: start_char,
918
- ec: start_char + value.size
919
- }
920
-
921
- scanner_events << node
922
- node
923
- end
924
-
925
- # command is a parser event representing a method call with arguments and
926
- # no parentheses. It accepts as arguments the name of the method and the
927
- # arguments being passed to the method.
928
- def on_command(ident, args)
929
- {
930
- type: :command,
931
- body: [ident, args],
932
- sl: ident[:sl],
933
- sc: ident[:sc],
934
- el: args[:el],
935
- ec: args[:ec]
936
- }
937
- end
938
-
939
- # command_call is a parser event representing a method call on an object
940
- # with arguments and no parentheses. It accepts as arguments the receiver
941
- # of the method, the operator being used to send the method, the name of
942
- # the method, and the arguments being passed to the method.
943
- def on_command_call(receiver, oper, ident, args)
944
- ending = args || ident
945
-
946
- {
947
- type: :command_call,
948
- body: [receiver, oper, ident, args],
949
- sl: receiver[:sl],
950
- sc: receiver[:sc],
951
- el: ending[:el],
952
- ec: ending[:ec]
953
- }
954
- end
955
-
956
- # We keep track of each comment as it comes in and then eventually add
957
- # them to the top of the generated AST so that prettier can start adding
958
- # them back into the final representation. Comments come in including
959
- # their starting pound sign and the newline at the end, so we also chop
960
- # those off.
961
- def on_comment(value)
962
- # If there is an encoding magic comment at the top of the file, ripper
963
- # will actually change into that encoding for the storage of the string.
964
- # This will break everything when we attempt to print as JSON, so we need to
965
- # force the encoding back into UTF-8 so that it won't break.
966
- body = value[1..-1].chomp.force_encoding('UTF-8')
967
-
968
- start_line = lineno
969
- start_char = char_pos
970
-
971
- @comments << {
972
- type: :@comment,
973
- value: body,
974
- inline: value.strip != lines[lineno - 1],
975
- sl: start_line,
976
- el: start_line,
977
- sc: start_char,
978
- ec: start_char + value.length - 1
979
- }
980
- end
981
-
982
- # const is a scanner event that represents a literal value that _looks like_
983
- # a constant. This could actually be a reference to a constant. It could also
984
- # be something that looks like a constant in another context, as in a method
985
- # call to a capitalized method, a symbol that starts with a capital letter,
986
- # etc.
987
- def on_const(value)
988
- start_line = lineno
989
- start_char = char_pos
990
-
991
- node = {
992
- type: :@const,
993
- body: value,
994
- sl: start_line,
995
- el: start_line,
996
- sc: start_char,
997
- ec: start_char + value.size
998
- }
999
-
1000
- scanner_events << node
1001
- node
1002
- end
1003
-
1004
- # A const_path_field is a parser event that is always the child of some
1005
- # kind of assignment. It represents when you're assigning to a constant
1006
- # that is being referenced as a child of another variable. For example:
1007
- #
1008
- # foo::X = 1
1009
- #
1010
- def on_const_path_field(left, const)
1011
- {
1012
- type: :const_path_field,
1013
- body: [left, const],
1014
- sl: left[:sl],
1015
- sc: left[:sc],
1016
- el: const[:el],
1017
- ec: const[:ec]
1018
- }
1019
- end
1020
-
1021
- # A const_path_ref is a parser event that is a very similar to
1022
- # const_path_field except that it is not involved in an assignment. It
1023
- # looks like the following example: foo::Bar, where left is foo and const is
1024
- # Bar.
1025
- def on_const_path_ref(left, const)
1026
- {
1027
- type: :const_path_ref,
1028
- body: [left, const],
1029
- sl: left[:sl],
1030
- sc: left[:sc],
1031
- el: const[:el],
1032
- ec: const[:ec]
1033
- }
1034
- end
1035
-
1036
- # A const_ref is a parser event that represents the name of the constant
1037
- # being used in a class or module declaration. In the following example it
1038
- # is the @const scanner event that has the contents of Foo.
1039
- #
1040
- # class Foo; end
1041
- #
1042
- def on_const_ref(const)
1043
- const.merge(type: :const_ref, body: [const])
1044
- end
1045
-
1046
- # cvar is a scanner event that represents the use of a class variable.
1047
- def on_cvar(value)
1048
- start_line = lineno
1049
- start_char = char_pos
1050
-
1051
- node = {
1052
- type: :@cvar,
1053
- body: value,
1054
- sl: start_line,
1055
- el: start_line,
1056
- sc: start_char,
1057
- ec: start_char + value.size
1058
- }
1059
-
1060
- scanner_events << node
1061
- node
1062
- end
1063
-
1064
- # A def is a parser event that represents defining a regular method on the
1065
- # current self object. It accepts as arguments the ident (the name of the
1066
- # method being defined), the params (the parameter declaration for the
1067
- # method), and a bodystmt node which represents the statements inside the
1068
- # method. As an example, here are the parts that go into this:
1069
- #
1070
- # def foo(bar) do baz end
1071
- # │ │ │
1072
- # │ │ └> bodystmt
1073
- # │ └> params
1074
- # └> ident
1075
- #
1076
- # You can also have single-line methods since Ruby 3.0+, which have slightly
1077
- # different syntax but still flow through this method. Those look like:
1078
- #
1079
- # def foo = bar
1080
- # | |
1081
- # | └> stmt
1082
- # └> ident
1083
- #
1084
- def on_def(ident, params, bodystmt)
1085
- # Make sure to delete this scanner event in case you're defining something
1086
- # like def class which would lead to this being a kw and causing all kinds
1087
- # of trouble
1088
- scanner_events.delete(ident)
1089
-
1090
- # Find the beginning of the method definition, which works for single-line
1091
- # and normal method definitions.
1092
- beging = find_scanner_event(:@kw, 'def')
1093
-
1094
- # If we don't have a bodystmt node, then we have a single-line method
1095
- if bodystmt[:type] != :bodystmt
1096
- return(
1097
- {
1098
- type: :defsl,
1099
- body: [ident, params, bodystmt],
1100
- sl: beging[:sl],
1101
- sc: beging[:sc],
1102
- el: bodystmt[:el],
1103
- ec: bodystmt[:ec]
1104
- }
1105
- )
1106
- end
1107
-
1108
- if params[:type] == :params && !params[:body].any?
1109
- location = ident[:ec]
1110
- params.merge!(sc: location, ec: location)
1111
- end
1112
-
1113
- ending = find_scanner_event(:@kw, 'end')
1114
-
1115
- bodystmt.bind(find_next_statement_start(params[:ec]), ending[:sc])
1116
-
1117
- {
1118
- type: :def,
1119
- body: [ident, params, bodystmt],
1120
- sl: beging[:sl],
1121
- sc: beging[:sc],
1122
- el: ending[:el],
1123
- ec: ending[:ec]
1124
- }
1125
- end
1126
-
1127
- # A defs is a parser event that represents defining a singleton method on
1128
- # an object. It accepts the same arguments as the def event, as well as
1129
- # the target and operator that on which this method is being defined. As
1130
- # an example, here are the parts that go into this:
1131
- #
1132
- # def foo.bar(baz) do baz end
1133
- # │ │ │ │ │
1134
- # │ │ │ │ │
1135
- # │ │ │ │ └> bodystmt
1136
- # │ │ │ └> params
1137
- # │ │ └> ident
1138
- # │ └> oper
1139
- # └> target
1140
- #
1141
- def on_defs(target, oper, ident, params, bodystmt)
1142
- # Make sure to delete this scanner event in case you're defining something
1143
- # like def class which would lead to this being a kw and causing all kinds
1144
- # of trouble
1145
- scanner_events.delete(ident)
1146
-
1147
- if params[:type] == :params && !params[:body].any?
1148
- location = ident[:ec]
1149
- params.merge!(sc: location, ec: location)
1150
- end
1151
-
1152
- beging = find_scanner_event(:@kw, 'def')
1153
- ending = find_scanner_event(:@kw, 'end')
1154
-
1155
- bodystmt.bind(find_next_statement_start(params[:ec]), ending[:sc])
1156
-
1157
- {
1158
- type: :defs,
1159
- body: [target, oper, ident, params, bodystmt],
1160
- sl: beging[:sl],
1161
- sc: beging[:sc],
1162
- el: ending[:el],
1163
- ec: ending[:ec]
1164
- }
1165
- end
1166
-
1167
- # A defined node represents the rather unique defined? operator. It can be
1168
- # used with and without parentheses. If they're present, we use them to
1169
- # determine our bounds, otherwise we use the value that's being passed to
1170
- # the operator.
1171
- def on_defined(value)
1172
- beging = find_scanner_event(:@kw, 'defined?')
1173
-
1174
- paren = source[beging[:ec]...value[:sc]].include?('(')
1175
- ending = paren ? find_scanner_event(:@rparen) : value
1176
-
1177
- beging.merge!(
1178
- type: :defined,
1179
- body: [value],
1180
- el: ending[:el],
1181
- ec: ending[:ec]
1182
- )
1183
- end
1184
-
1185
- # do_block is a parser event that represents passing a block to a method
1186
- # call using the do..end keywords. It accepts as arguments an optional
1187
- # block_var event that represents any parameters to the block as well as
1188
- # a bodystmt event that represents the statements inside the block.
1189
- def on_do_block(block_var, bodystmt)
1190
- beging = find_scanner_event(:@kw, 'do')
1191
- ending = find_scanner_event(:@kw, 'end')
1192
-
1193
- bodystmt.bind(
1194
- find_next_statement_start((block_var || beging)[:ec]),
1195
- ending[:sc]
1196
- )
1197
-
1198
- {
1199
- type: :do_block,
1200
- body: [block_var, bodystmt],
1201
- beging: beging,
1202
- sl: beging[:sl],
1203
- sc: beging[:sc],
1204
- el: ending[:el],
1205
- ec: ending[:ec]
1206
- }
1207
- end
1208
-
1209
- # dot2 is a parser event that represents using the .. operator between two
1210
- # expressions. Usually this is to create a range object but sometimes it's to
1211
- # use the flip-flop operator.
1212
- def on_dot2(left, right)
1213
- operator = find_scanner_event(:@op, '..')
1214
-
1215
- beging = left || operator
1216
- ending = right || operator
1217
-
1218
- {
1219
- type: :dot2,
1220
- body: [left, right],
1221
- sl: beging[:sl],
1222
- sc: beging[:sc],
1223
- el: ending[:el],
1224
- ec: ending[:ec]
1225
- }
1226
- end
1227
-
1228
- # dot3 is a parser event that represents using the ... operator between two
1229
- # expressions. Usually this is to create a range object but sometimes it's to
1230
- # use the flip-flop operator.
1231
- def on_dot3(left, right)
1232
- operator = find_scanner_event(:@op, '...')
1233
-
1234
- beging = left || operator
1235
- ending = right || operator
1236
-
1237
- {
1238
- type: :dot3,
1239
- body: [left, right],
1240
- sl: beging[:sl],
1241
- sc: beging[:sc],
1242
- el: ending[:el],
1243
- ec: ending[:ec]
1244
- }
1245
- end
1246
-
1247
- # A dyna_symbol is a parser event that represents a symbol literal that
1248
- # uses quotes to interpolate its value. For example, if you had a variable
1249
- # foo and you wanted a symbol that contained its value, you would write:
1250
- #
1251
- # :"#{foo}"
1252
- #
1253
- # As such, they accept as one argument a string node, which is the same
1254
- # node that gets accepted into a string_literal (since we're basically
1255
- # talking about a string literal with a : character at the beginning).
1256
- #
1257
- # They can also come in another flavor which is a dynamic symbol as a hash
1258
- # key. This is kind of an interesting syntax which results in us having to
1259
- # look for a @label_end scanner event instead to get our bearings. That
1260
- # kind of code would look like:
1261
- #
1262
- # { "#{foo}": bar }
1263
- #
1264
- # which would be the same symbol as above.
1265
- def on_dyna_symbol(string)
1266
- if find_scanner_event(:@symbeg, consume: false)
1267
- # A normal dynamic symbol
1268
- beging = find_scanner_event(:@symbeg)
1269
- ending = find_scanner_event(:@tstring_end)
1270
-
1271
- beging.merge(
1272
- type: :dyna_symbol,
1273
- quote: beging[:body],
1274
- body: string[:body],
1275
- el: ending[:el],
1276
- ec: ending[:ec]
1277
- )
1278
- else
1279
- # A dynamic symbol as a hash key
1280
- beging = find_scanner_event(:@tstring_beg)
1281
- ending = find_scanner_event(:@label_end)
1282
-
1283
- string.merge!(
1284
- type: :dyna_symbol,
1285
- quote: ending[:body][0],
1286
- sl: beging[:sl],
1287
- sc: beging[:sc],
1288
- el: ending[:el],
1289
- ec: ending[:ec]
1290
- )
1291
- end
1292
- end
1293
-
1294
- # else is a parser event that represents the end of a if, unless, or begin
1295
- # chain. It accepts as an argument the statements that are contained
1296
- # within the else clause.
1297
- def on_else(stmts)
1298
- beging = find_scanner_event(:@kw, 'else')
1299
-
1300
- # else can either end with an end keyword (in which case we'll want to
1301
- # consume that event) or it can end with an ensure keyword (in which case
1302
- # we'll leave that to the ensure to handle).
1303
- index =
1304
- scanner_events.rindex do |event|
1305
- event[:type] == :@kw && %w[end ensure].include?(event[:body])
1306
- end
1307
-
1308
- event = scanner_events[index]
1309
- ending = event[:body] == 'end' ? scanner_events.delete_at(index) : event
1310
-
1311
- stmts.bind(beging[:ec], ending[:sc])
1312
-
1313
- {
1314
- type: :else,
1315
- body: [stmts],
1316
- sl: beging[:sl],
1317
- sc: beging[:sc],
1318
- el: ending[:el],
1319
- ec: ending[:ec]
1320
- }
1321
- end
1322
-
1323
- # elsif is a parser event that represents another clause in an if chain.
1324
- # It accepts as arguments the predicate of the else if, the statements
1325
- # that are contained within the else if clause, and the optional
1326
- # consequent clause.
1327
- def on_elsif(predicate, stmts, consequent)
1328
- beging = find_scanner_event(:@kw, 'elsif')
1329
- ending = consequent || find_scanner_event(:@kw, 'end')
1330
-
1331
- stmts.bind(predicate[:ec], ending[:sc])
1332
-
1333
- {
1334
- type: :elsif,
1335
- body: [predicate, stmts, consequent],
1336
- sl: beging[:sl],
1337
- sc: beging[:sc],
1338
- el: ending[:el],
1339
- ec: ending[:ec]
1340
- }
1341
- end
1342
-
1343
- # This is a scanner event that gets hit when we're inside an embdoc and
1344
- # receive a new line of content. Here we are guaranteed to already have
1345
- # initialized the @embdoc variable so we can just append the new line onto
1346
- # the existing content.
1347
- def on_embdoc(value)
1348
- @embdoc[:value] << value
1349
- end
1350
-
1351
- # embdocs are long comments that are surrounded by =begin..=end. They
1352
- # cannot be nested, so we don't need to worry about keeping a stack around
1353
- # like we do with heredocs. Instead we can just track the current embdoc
1354
- # and add to it as we get content. It always starts with this scanner
1355
- # event, so here we'll initialize the current embdoc.
1356
- def on_embdoc_beg(value)
1357
- @embdoc = { type: :@embdoc, value: value, sl: lineno, sc: char_pos }
1358
- end
1359
-
1360
- # This is the final scanner event for embdocs. It receives the =end. Here
1361
- # we can finalize the embdoc with its location information and the final
1362
- # piece of the string. We then add it to the list of comments so that
1363
- # prettier can place it into the final source string.
1364
- def on_embdoc_end(value)
1365
- @comments <<
1366
- @embdoc.merge!(
1367
- value: @embdoc[:value] << value.chomp,
1368
- el: lineno,
1369
- ec: char_pos + value.length - 1
1370
- )
1371
-
1372
- @embdoc = nil
1373
- end
1374
-
1375
- # embexpr_beg is a scanner event that represents using interpolation inside of
1376
- # a string, xstring, heredoc, or regexp. Its value is the string literal "#{".
1377
- def on_embexpr_beg(value)
1378
- start_line = lineno
1379
- start_char = char_pos
1380
-
1381
- node = {
1382
- type: :@embexpr_beg,
1383
- body: value,
1384
- sl: start_line,
1385
- el: start_line,
1386
- sc: start_char,
1387
- ec: start_char + value.size
1388
- }
1389
-
1390
- scanner_events << node
1391
- node
1392
- end
1393
-
1394
- # embexpr_end is a scanner event that represents the end of an interpolated
1395
- # expression in a string, xstring, heredoc, or regexp. Its value is the string
1396
- # literal "}".
1397
- def on_embexpr_end(value)
1398
- start_line = lineno
1399
- start_char = char_pos
1400
-
1401
- node = {
1402
- type: :@embexpr_end,
1403
- body: value,
1404
- sl: start_line,
1405
- el: start_line,
1406
- sc: start_char,
1407
- ec: start_char + value.size
1408
- }
1409
-
1410
- scanner_events << node
1411
- node
1412
- end
1413
-
1414
- # embvar is a scanner event that represents the use of shorthand interpolation
1415
- # for an instance, class, or global variable into a string, xstring, heredoc,
1416
- # or regexp. Its value is the string literal "#". For example, in the
1417
- # following snippet:
1418
- #
1419
- # "#@foo"
1420
- #
1421
- # the embvar would be triggered by the "#", then an ivar event for the @foo
1422
- # instance variable. That would all get bound up into a string_dvar node in
1423
- # the final AST.
1424
- def on_embvar(value)
1425
- start_line = lineno
1426
- start_char = char_pos
1427
-
1428
- node = {
1429
- type: :@embvar,
1430
- body: value,
1431
- sl: start_line,
1432
- el: start_line,
1433
- sc: start_char,
1434
- ec: start_char + value.size
1435
- }
1436
-
1437
- scanner_events << node
1438
- node
1439
- end
1440
-
1441
- # ensure is a parser event that represents the use of the ensure keyword
1442
- # and its subsequent statements.
1443
- def on_ensure(stmts)
1444
- beging = find_scanner_event(:@kw, 'ensure')
1445
-
1446
- # Specifically not using find_scanner_event here because we don't want to
1447
- # consume the :@end event, because that would break def..ensure..end chains.
1448
- index =
1449
- scanner_events.rindex do |scanner_event|
1450
- scanner_event[:type] == :@kw && scanner_event[:body] == 'end'
1451
- end
1452
-
1453
- ending = scanner_events[index]
1454
- stmts.bind(find_next_statement_start(beging[:ec]), ending[:sc])
1455
-
1456
- {
1457
- type: :ensure,
1458
- body: [beging, stmts],
1459
- sl: beging[:sl],
1460
- sc: beging[:sc],
1461
- el: ending[:el],
1462
- ec: ending[:ec]
1463
- }
1464
- end
1465
-
1466
- # An excessed_comma is a special kind of parser event that represents a comma
1467
- # at the end of a list of parameters. It's a very strange node. It accepts a
1468
- # different number of arguments depending on Ruby version, which is why we
1469
- # have the anonymous splat there.
1470
- def on_excessed_comma(*)
1471
- find_scanner_event(:@comma).merge!(type: :excessed_comma)
1472
- end
1473
-
1474
- # An fcall is a parser event that represents the piece of a method call
1475
- # that comes before any arguments (i.e., just the name of the method).
1476
- def on_fcall(ident)
1477
- ident.merge(type: :fcall, body: [ident])
1478
- end
1479
-
1480
- # A field is a parser event that is always the child of an assignment. It
1481
- # accepts as arguments the left side of operation, the operator (. or ::),
1482
- # and the right side of the operation. For example:
1483
- #
1484
- # foo.x = 1
1485
- #
1486
- def on_field(left, oper, right)
1487
- {
1488
- type: :field,
1489
- body: [left, oper, right],
1490
- sl: left[:sl],
1491
- sc: left[:sc],
1492
- el: right[:el],
1493
- ec: right[:ec]
1494
- }
1495
- end
1496
-
1497
- # float is a scanner event that represents a floating point value literal.
1498
- def on_float(value)
1499
- start_line = lineno
1500
- start_char = char_pos
1501
-
1502
- node = {
1503
- type: :@float,
1504
- body: value,
1505
- sl: start_line,
1506
- el: start_line,
1507
- sc: start_char,
1508
- ec: start_char + value.size
1509
- }
1510
-
1511
- scanner_events << node
1512
- node
1513
- end
1514
-
1515
- # fndptn is a parser event that represents matching against a pattern where
1516
- # you find a pattern in an array using the Ruby 3.0+ pattern matching syntax.
1517
- def on_fndptn(const, presplat, args, postsplat)
1518
- beging = const || find_scanner_event(:@lbracket)
1519
- ending = find_scanner_event(:@rbracket)
1520
-
1521
- {
1522
- type: :fndptn,
1523
- body: [const, presplat, args, postsplat],
1524
- sl: beging[:sl],
1525
- sc: beging[:sc],
1526
- el: ending[:el],
1527
- ec: ending[:ec]
1528
- }
1529
- end
1530
-
1531
- # for is a parser event that represents using the somewhat esoteric for
1532
- # loop. It accepts as arguments an ident which is the iterating variable,
1533
- # an enumerable for that which is being enumerated, and a stmts event that
1534
- # represents the statements inside the for loop.
1535
- def on_for(ident, enum, stmts)
1536
- beging = find_scanner_event(:@kw, 'for')
1537
- ending = find_scanner_event(:@kw, 'end')
1538
-
1539
- # Consume the do keyword if it exists so that it doesn't get confused for
1540
- # some other block
1541
- do_event = find_scanner_event(:@kw, 'do', consume: false)
1542
- if do_event && do_event[:sc] > enum[:ec] && do_event[:ec] < ending[:sc]
1543
- scanner_events.delete(do_event)
1544
- end
1545
-
1546
- stmts.bind((do_event || enum)[:ec], ending[:sc])
1547
-
1548
- {
1549
- type: :for,
1550
- body: [ident, enum, stmts],
1551
- sl: beging[:sl],
1552
- sc: beging[:sc],
1553
- el: ending[:el],
1554
- ec: ending[:ec]
1555
- }
1556
- end
1557
-
1558
- # gvar is a scanner event that represents a global variable literal.
1559
- def on_gvar(value)
1560
- start_line = lineno
1561
- start_char = char_pos
1562
-
1563
- node = {
1564
- type: :@gvar,
1565
- body: value,
1566
- sl: start_line,
1567
- el: start_line,
1568
- sc: start_char,
1569
- ec: start_char + value.size
1570
- }
1571
-
1572
- scanner_events << node
1573
- node
1574
- end
1575
-
1576
- # hash is a parser event that represents a hash literal. It accepts as an
1577
- # argument an optional assoclist_from_args event which contains the
1578
- # contents of the hash.
1579
- def on_hash(assoclist_from_args)
1580
- beging = find_scanner_event(:@lbrace)
1581
- ending = find_scanner_event(:@rbrace)
1582
-
1583
- if assoclist_from_args
1584
- # Here we're going to expand out the location information for the assocs
1585
- # node so that it can grab up any remaining comments inside the hash.
1586
- assoclist_from_args.merge!(sc: beging[:ec], ec: ending[:sc])
1587
- end
1588
-
1589
- {
1590
- type: :hash,
1591
- body: [assoclist_from_args],
1592
- sl: beging[:sl],
1593
- sc: beging[:sc],
1594
- el: ending[:el],
1595
- ec: ending[:ec]
1596
- }
1597
- end
1598
-
1599
- # This is a scanner event that represents the beginning of the heredoc. It
1600
- # includes the declaration (which we call beging here, which is just short
1601
- # for beginning). The declaration looks something like <<-HERE or <<~HERE.
1602
- # If the downcased version of the declaration actually matches an existing
1603
- # prettier parser, we'll later attempt to print it using that parser and
1604
- # printer through our embed function.
1605
- def on_heredoc_beg(beging)
1606
- location = {
1607
- sl: lineno,
1608
- el: lineno,
1609
- sc: char_pos,
1610
- ec: char_pos + beging.length + 1
1611
- }
1612
-
1613
- # Here we're going to artificially create an extra node type so that if
1614
- # there are comments after the declaration of a heredoc, they get printed.
1615
- location
1616
- .merge(
1617
- type: :heredoc,
1618
- beging: location.merge(type: :@heredoc_beg, body: beging)
1619
- )
1620
- .tap { |node| @heredocs << node }
1621
- end
1622
-
1623
- # This is a parser event that occurs when you're using a heredoc with a
1624
- # tilde. These are considered `heredoc_dedent` nodes, whereas the hyphen
1625
- # heredocs show up as string literals.
1626
- def on_heredoc_dedent(string, _width)
1627
- @heredocs[-1].merge!(body: string[:body])
1628
- end
1629
-
1630
- # This is a scanner event that represents the end of the heredoc.
1631
- def on_heredoc_end(ending)
1632
- @heredocs[-1].merge!(ending: ending.chomp, el: lineno, ec: char_pos)
1633
- end
1634
-
1635
- # hshptn is a parser event that represents matching against a hash pattern
1636
- # using the Ruby 2.7+ pattern matching syntax.
1637
- def on_hshptn(const, kw, kwrest)
1638
- pieces = [const, kw, kwrest].flatten(2).compact
1639
-
1640
- {
1641
- type: :hshptn,
1642
- body: [const, kw, kwrest],
1643
- sl: pieces[0][:sl],
1644
- sc: pieces[0][:sc],
1645
- el: pieces[-1][:el],
1646
- ec: pieces[-1][:ec]
1647
- }
1648
- end
1649
-
1650
- # ident is a scanner event that represents an identifier anywhere in code. It
1651
- # can actually represent a whole bunch of stuff, depending on where it is in
1652
- # the AST. Like comments, we need to force the encoding here so JSON doesn't
1653
- # break.
1654
- def on_ident(value)
1655
- start_line = lineno
1656
- start_char = char_pos
1657
-
1658
- node = {
1659
- type: :@ident,
1660
- body: value.force_encoding('UTF-8'),
1661
- sl: start_line,
1662
- el: start_line,
1663
- sc: start_char,
1664
- ec: start_char + value.size
1665
- }
1666
-
1667
- scanner_events << node
1668
- node
1669
- end
1670
-
1671
- # if is a parser event that represents the first clause in an if chain.
1672
- # It accepts as arguments the predicate of the if, the statements that are
1673
- # contained within the if clause, and the optional consequent clause.
1674
- def on_if(predicate, stmts, consequent)
1675
- beging = find_scanner_event(:@kw, 'if')
1676
- ending = consequent || find_scanner_event(:@kw, 'end')
1677
-
1678
- stmts.bind(predicate[:ec], ending[:sc])
1679
-
1680
- {
1681
- type: :if,
1682
- body: [predicate, stmts, consequent],
1683
- sl: beging[:sl],
1684
- sc: beging[:sc],
1685
- el: ending[:el],
1686
- ec: ending[:ec]
1687
- }
1688
- end
1689
-
1690
- # ifop is a parser event that represents a ternary operator. It accepts as
1691
- # arguments the predicate to the ternary, the truthy clause, and the falsy
1692
- # clause.
1693
- def on_ifop(predicate, truthy, falsy)
1694
- predicate.merge(
1695
- type: :ifop,
1696
- body: [predicate, truthy, falsy],
1697
- el: falsy[:el],
1698
- ec: falsy[:ec]
1699
- )
1700
- end
1701
-
1702
- # if_mod is a parser event that represents the modifier form of an if
1703
- # statement. It accepts as arguments the predicate of the if and the
1704
- # statement that are contained within the if clause.
1705
- def on_if_mod(predicate, statement)
1706
- find_scanner_event(:@kw, 'if')
1707
-
1708
- {
1709
- type: :if_mod,
1710
- body: [predicate, statement],
1711
- sl: statement[:sl],
1712
- sc: statement[:sc],
1713
- el: predicate[:el],
1714
- ec: predicate[:ec]
1715
- }
1716
- end
1717
-
1718
- # ignored_nl is a special kind of scanner event that passes nil as the value.
1719
- # You can trigger the ignored_nl event with the following snippet:
1720
- #
1721
- # foo.bar
1722
- # .baz
1723
- #
1724
- # We don't need to track this event in the AST that we're generating, so we're
1725
- # not going to define an explicit handler for it.
1726
- #
1727
- # def on_ignored_nl(value)
1728
- # value
1729
- # end
1730
-
1731
- # ignored_sp is a scanner event that represents the space before the content
1732
- # of each line of a squiggly heredoc that will be removed from the string
1733
- # before it gets transformed into a string literal. For example, in the
1734
- # following snippet:
1735
- #
1736
- # <<~HERE
1737
- # foo
1738
- # bar
1739
- # HERE
1740
- #
1741
- # You would have two ignored_sp events, the first with two spaces and the
1742
- # second with four. We don't need to track this event in the AST that we're
1743
- # generating, so we're not going to define an explicit handler for it.
1744
- #
1745
- # def on_ignored_sp(value)
1746
- # value
1747
- # end
1748
-
1749
- # imaginary is a scanner event that represents an imaginary number literal.
1750
- # They become instances of the Complex class.
1751
- def on_imaginary(value)
1752
- start_line = lineno
1753
- start_char = char_pos
1754
-
1755
- node = {
1756
- type: :@imaginary,
1757
- body: value,
1758
- sl: start_line,
1759
- el: start_line,
1760
- sc: start_char,
1761
- ec: start_char + value.size
1762
- }
1763
-
1764
- scanner_events << node
1765
- node
1766
- end
1767
-
1768
- # in is a parser event that represents using the in keyword within the
1769
- # Ruby 2.7+ pattern matching syntax. Alternatively in Ruby 3+ it is also used
1770
- # to handle rightward assignment for pattern matching.
1771
- def on_in(pattern, stmts, consequent)
1772
- # Here we have a rightward assignment
1773
- return pattern unless stmts
1774
-
1775
- beging = find_scanner_event(:@kw, 'in')
1776
- ending = consequent || find_scanner_event(:@kw, 'end')
1777
-
1778
- stmts.bind(beging[:ec], ending[:sc])
1779
-
1780
- beging.merge!(
1781
- type: :in,
1782
- body: [pattern, stmts, consequent],
1783
- el: ending[:el],
1784
- ec: ending[:ec]
1785
- )
1786
- end
1787
-
1788
- # int is a scanner event the represents a number literal.
1789
- def on_int(value)
1790
- start_line = lineno
1791
- start_char = char_pos
1792
-
1793
- node = {
1794
- type: :@int,
1795
- body: value,
1796
- sl: start_line,
1797
- el: start_line,
1798
- sc: start_char,
1799
- ec: start_char + value.size
1800
- }
1801
-
1802
- scanner_events << node
1803
- node
1804
- end
1805
-
1806
- # ivar is a scanner event the represents an instance variable literal.
1807
- def on_ivar(value)
1808
- start_line = lineno
1809
- start_char = char_pos
1810
-
1811
- node = {
1812
- type: :@ivar,
1813
- body: value,
1814
- sl: start_line,
1815
- el: start_line,
1816
- sc: start_char,
1817
- ec: start_char + value.size
1818
- }
1819
-
1820
- scanner_events << node
1821
- node
1822
- end
1823
-
1824
- # kw is a scanner event the represents the use of a keyword. It can be
1825
- # anywhere in the AST, so you end up seeing it quite a lot.
1826
- def on_kw(value)
1827
- start_line = lineno
1828
- start_char = char_pos
1829
-
1830
- node = {
1831
- type: :@kw,
1832
- body: value,
1833
- sl: start_line,
1834
- el: start_line,
1835
- sc: start_char,
1836
- ec: start_char + value.size
1837
- }
1838
-
1839
- scanner_events << node
1840
- node
1841
- end
1842
-
1843
- # kwrest_param is a parser event that represents defining a parameter in a
1844
- # method definition that accepts all remaining keyword parameters.
1845
- def on_kwrest_param(ident)
1846
- oper = find_scanner_event(:@op, '**')
1847
- return oper.merge!(type: :kwrest_param, body: [nil]) unless ident
1848
-
1849
- oper.merge!(
1850
- type: :kwrest_param,
1851
- body: [ident],
1852
- el: ident[:el],
1853
- ec: ident[:ec]
1854
- )
1855
- end
1856
-
1857
- # label is a scanner event that represents the use of an identifier to
1858
- # associate with an object. You can find it in a hash key, as in:
1859
- #
1860
- # { foo: bar }
1861
- #
1862
- # in this case "foo:" would be the body of the label. You can also find it in
1863
- # pattern matching, as in:
1864
- #
1865
- # case foo
1866
- # in bar:
1867
- # bar
1868
- # end
1869
- #
1870
- # in this case "bar:" would be the body of the label.
1871
- def on_label(value)
1872
- start_line = lineno
1873
- start_char = char_pos
1874
-
1875
- node = {
1876
- type: :@label,
1877
- body: value,
1878
- sl: start_line,
1879
- el: start_line,
1880
- sc: start_char,
1881
- ec: start_char + value.size
1882
- }
1883
-
1884
- scanner_events << node
1885
- node
1886
- end
1887
-
1888
- # label_end is a scanner event that represents the end of a dynamic symbol. If
1889
- # for example you had the following hash:
1890
- #
1891
- # { "foo": bar }
1892
- #
1893
- # then the string "\":" would be the value of this label_end. It's useful for
1894
- # determining the type of quote being used by the label.
1895
- def on_label_end(value)
1896
- start_line = lineno
1897
- start_char = char_pos
1898
-
1899
- node = {
1900
- type: :@label_end,
1901
- body: value,
1902
- sl: start_line,
1903
- el: start_line,
1904
- sc: start_char,
1905
- ec: start_char + value.size
1906
- }
1907
-
1908
- scanner_events << node
1909
- node
1910
- end
1911
-
1912
- # lambda is a parser event that represents using a "stabby" lambda
1913
- # literal. It accepts as arguments a params event that represents any
1914
- # parameters to the lambda and a stmts event that represents the
1915
- # statements inside the lambda.
1916
- #
1917
- # It can be wrapped in either {..} or do..end so we look for either of
1918
- # those combinations to get our bounds.
1919
- def on_lambda(params, stmts)
1920
- beging = find_scanner_event(:@tlambda)
1921
-
1922
- if event = find_scanner_event(:@tlambeg, consume: false)
1923
- opening = scanner_events.delete(event)
1924
- closing = find_scanner_event(:@rbrace)
1925
- else
1926
- opening = find_scanner_event(:@kw, 'do')
1927
- closing = find_scanner_event(:@kw, 'end')
1928
- end
1929
-
1930
- stmts.bind(opening[:ec], closing[:sc])
1931
-
1932
- {
1933
- type: :lambda,
1934
- body: [params, stmts],
1935
- sl: beging[:sl],
1936
- sc: beging[:sc],
1937
- el: closing[:el],
1938
- ec: closing[:ec]
1939
- }
1940
- end
1941
-
1942
- # lbrace is a scanner event representing the use of a left brace, i.e., "{".
1943
- def on_lbrace(value)
1944
- start_line = lineno
1945
- start_char = char_pos
1946
-
1947
- node = {
1948
- type: :@lbrace,
1949
- body: value,
1950
- sl: start_line,
1951
- el: start_line,
1952
- sc: start_char,
1953
- ec: start_char + value.size
1954
- }
1955
-
1956
- scanner_events << node
1957
- node
1958
- end
1959
-
1960
- # lbracket is a scanner event representing the use of a left bracket, i.e.,
1961
- # "[".
1962
- def on_lbracket(value)
1963
- start_line = lineno
1964
- start_char = char_pos
1965
-
1966
- node = {
1967
- type: :@lbracket,
1968
- body: value,
1969
- sl: start_line,
1970
- el: start_line,
1971
- sc: start_char,
1972
- ec: start_char + value.size
1973
- }
1974
-
1975
- scanner_events << node
1976
- node
1977
- end
1978
-
1979
- # lparen is a scanner event representing the use of a left parenthesis, i.e.,
1980
- # "(".
1981
- def on_lparen(value)
1982
- start_line = lineno
1983
- start_char = char_pos
1984
-
1985
- node = {
1986
- type: :@lparen,
1987
- body: value,
1988
- sl: start_line,
1989
- el: start_line,
1990
- sc: start_char,
1991
- ec: start_char + value.size
1992
- }
1993
-
1994
- scanner_events << node
1995
- node
1996
- end
1997
-
1998
- # magic_comment is a scanner event that represents the use of a pragma at the
1999
- # beginning of the file. Usually it will inside something like
2000
- # frozen_string_literal (the key) with a value of true (the value). Both
2001
- # children come is a string literals. We're going to leave these alone as they
2002
- # come in all kinds of shapes and sizes.
2003
- #
2004
- # def on_magic_comment(key, value)
2005
- # @magic_comment = { value: " #{key}: #{value}" }
2006
- # end
2007
-
2008
- # massign is a parser event that is a parent node of any kind of multiple
2009
- # assignment. This includes splitting out variables on the left like:
2010
- #
2011
- # a, b, c = foo
2012
- #
2013
- # as well as splitting out variables on the right, as in:
2014
- #
2015
- # foo = a, b, c
2016
- #
2017
- # Both sides support splats, as well as variables following them. There's
2018
- # also slightly odd behavior that you can achieve with the following:
2019
- #
2020
- # a, = foo
2021
- #
2022
- # In this case a would receive only the first value of the foo enumerable,
2023
- # in which case we need to explicitly track the comma and add it onto the
2024
- # child node.
2025
- def on_massign(left, right)
2026
- left[:comma] = true if source[left[:ec]...right[:sc]].strip.start_with?(',')
2027
-
2028
- {
2029
- type: :massign,
2030
- body: [left, right],
2031
- sl: left[:sl],
2032
- sc: left[:sc],
2033
- el: right[:el],
2034
- ec: right[:ec]
2035
- }
2036
- end
2037
-
2038
- # method_add_arg is a parser event that represents a method call with
2039
- # arguments and parentheses. It accepts as arguments the method being called
2040
- # and the arg_paren event that contains the arguments to the method.
2041
- def on_method_add_arg(fcall, arg_paren)
2042
- # You can hit this if you are passing no arguments to a method that ends in
2043
- # a question mark. Because it knows it has to be a method and not a local
2044
- # variable. In that case we can just use the location information straight
2045
- # from the fcall.
2046
- if arg_paren[:type] == :args
2047
- return fcall.merge(type: :method_add_arg, body: [fcall, arg_paren])
2048
- end
2049
-
2050
- {
2051
- type: :method_add_arg,
2052
- body: [fcall, arg_paren],
2053
- sl: fcall[:sl],
2054
- sc: fcall[:sc],
2055
- el: arg_paren[:el],
2056
- ec: arg_paren[:ec]
2057
- }
2058
- end
2059
-
2060
- # method_add_block is a parser event that represents a method call with a
2061
- # block argument. It accepts as arguments the method being called and the
2062
- # block event.
2063
- def on_method_add_block(method_add_arg, block)
2064
- {
2065
- type: :method_add_block,
2066
- body: [method_add_arg, block],
2067
- sl: method_add_arg[:sl],
2068
- sc: method_add_arg[:sc],
2069
- el: block[:el],
2070
- ec: block[:ec]
2071
- }
2072
- end
2073
-
2074
- # An mlhs_new is a parser event that represents the beginning of the left
2075
- # side of a multiple assignment. It is followed by any number of mlhs_add
2076
- # nodes that each represent another variable being assigned.
2077
- def on_mlhs_new
2078
- {
2079
- type: :mlhs,
2080
- body: [],
2081
- sl: lineno,
2082
- sc: char_pos,
2083
- el: lineno,
2084
- ec: char_pos
2085
- }
2086
- end
2087
-
2088
- # An mlhs_add is a parser event that represents adding another variable
2089
- # onto a list of assignments. It accepts as arguments the parent mlhs node
2090
- # as well as the part that is being added to the list.
2091
- def on_mlhs_add(mlhs, part)
2092
- if mlhs[:body].empty?
2093
- part.merge(type: :mlhs, body: [part])
2094
- else
2095
- mlhs.merge!(body: mlhs[:body] << part, el: part[:el], ec: part[:ec])
2096
- end
2097
- end
2098
-
2099
- # An mlhs_add_post is a parser event that represents adding another set of
2100
- # variables onto a list of assignments after a splat variable. It accepts
2101
- # as arguments the previous mlhs_add_star node that represented the splat
2102
- # as well another mlhs node that represents all of the variables after the
2103
- # splat.
2104
- def on_mlhs_add_post(mlhs_add_star, mlhs)
2105
- mlhs_add_star.merge(
2106
- type: :mlhs_add_post,
2107
- body: [mlhs_add_star, mlhs],
2108
- el: mlhs[:el],
2109
- ec: mlhs[:ec]
2110
- )
2111
- end
2112
-
2113
- # An mlhs_add_star is a parser event that represents a splatted variable
2114
- # inside of a multiple assignment on the left hand side. It accepts as
2115
- # arguments the parent mlhs node as well as the part that represents the
2116
- # splatted variable.
2117
- def on_mlhs_add_star(mlhs, part)
2118
- beging = find_scanner_event(:@op, '*')
2119
- ending = part || beging
2120
-
2121
- {
2122
- type: :mlhs_add_star,
2123
- body: [mlhs, part],
2124
- sl: beging[:sl],
2125
- sc: beging[:sc],
2126
- el: ending[:el],
2127
- ec: ending[:ec]
2128
- }
2129
- end
2130
-
2131
- # An mlhs_paren is a parser event that represents parentheses being used
2132
- # to deconstruct values in a multiple assignment on the left hand side. It
2133
- # accepts as arguments the contents of the inside of the parentheses,
2134
- # which is another mlhs node.
2135
- def on_mlhs_paren(contents)
2136
- beging = find_scanner_event(:@lparen)
2137
- ending = find_scanner_event(:@rparen)
2138
-
2139
- if source[beging[:ec]...ending[:sc]].strip.end_with?(',')
2140
- contents[:comma] = true
2141
- end
2142
-
2143
- {
2144
- type: :mlhs_paren,
2145
- body: [contents],
2146
- sl: beging[:sl],
2147
- sc: beging[:sc],
2148
- el: ending[:el],
2149
- ec: ending[:ec]
2150
- }
2151
- end
2152
-
2153
- # module is a parser event that represents defining a module. It accepts
2154
- # as arguments the name of the module and the bodystmt event that
2155
- # represents the statements evaluated within the context of the module.
2156
- def on_module(const, bodystmt)
2157
- beging = find_scanner_event(:@kw, 'module')
2158
- ending = find_scanner_event(:@kw, 'end')
2159
-
2160
- bodystmt.bind(find_next_statement_start(const[:ec]), ending[:sc])
2161
-
2162
- {
2163
- type: :module,
2164
- body: [const, bodystmt],
2165
- sl: beging[:sl],
2166
- sc: beging[:sc],
2167
- el: ending[:el],
2168
- ec: ending[:ec]
2169
- }
2170
- end
2171
-
2172
- # An mrhs_new is a parser event that represents the beginning of a list of
2173
- # values that are being assigned within a multiple assignment node. It can
2174
- # be followed by any number of mrhs_add nodes that we'll build up into an
2175
- # array body.
2176
- def on_mrhs_new
2177
- {
2178
- type: :mrhs,
2179
- body: [],
2180
- sl: lineno,
2181
- sc: char_pos,
2182
- el: lineno,
2183
- ec: char_pos
2184
- }
2185
- end
2186
-
2187
- # An mrhs_add is a parser event that represents adding another value onto
2188
- # a list on the right hand side of a multiple assignment.
2189
- def on_mrhs_add(mrhs, part)
2190
- if mrhs[:body].empty?
2191
- part.merge(type: :mrhs, body: [part])
2192
- else
2193
- mrhs.merge!(body: mrhs[:body] << part, el: part[:el], ec: part[:ec])
2194
- end
2195
- end
2196
-
2197
- # An mrhs_add_star is a parser event that represents using the splat
2198
- # operator to expand out a value on the right hand side of a multiple
2199
- # assignment.
2200
- def on_mrhs_add_star(mrhs, part)
2201
- beging = find_scanner_event(:@op, '*')
2202
- ending = part || beging
2203
-
2204
- {
2205
- type: :mrhs_add_star,
2206
- body: [mrhs, part],
2207
- sl: beging[:sl],
2208
- sc: beging[:sc],
2209
- el: ending[:el],
2210
- ec: ending[:ec]
2211
- }
2212
- end
2213
-
2214
- # An mrhs_new_from_args is a parser event that represents the shorthand
2215
- # of a multiple assignment that allows you to assign values using just
2216
- # commas as opposed to assigning from an array. For example, in the
2217
- # following segment the right hand side of the assignment would trigger
2218
- # this event:
2219
- #
2220
- # foo = 1, 2, 3
2221
- #
2222
- def on_mrhs_new_from_args(args)
2223
- args.merge(type: :mrhs_new_from_args, body: [args])
2224
- end
2225
-
2226
- # next is a parser event that represents using the next keyword. It
2227
- # accepts as an argument an args or args_add_block event that contains all
2228
- # of the arguments being passed to the next.
2229
- def on_next(args_add_block)
2230
- find_scanner_event(:@kw, 'next').merge!(
2231
- type: :next,
2232
- body: [args_add_block],
2233
- el: args_add_block[:el],
2234
- ec: args_add_block[:ec]
2235
- )
2236
- end
2237
-
2238
- # nl is a scanner event representing a newline in the source. As you can
2239
- # imagine, it will typically get triggered quite a few times. We don't need to
2240
- # track this event in the AST that we're generating, so we're not going to
2241
- # define an explicit handler for it.
2242
- #
2243
- # def on_nl(value)
2244
- # value
2245
- # end
2246
-
2247
- # nokw_param is a parser event that represents the use of the special 2.7+
2248
- # syntax to indicate a method should take no additional keyword arguments. For
2249
- # example in the following snippet:
2250
- #
2251
- # def foo(**nil) end
2252
- #
2253
- # this is saying that foo should not accept any keyword arguments. Its value
2254
- # is always nil. We don't need to track this event in the AST that we're
2255
- # generating, so we're not going to define an explicit handler for it.
2256
- #
2257
- # def on_nokw_param(value)
2258
- # value
2259
- # end
2260
-
2261
- # op is a scanner event representing an operator literal in the source. For
2262
- # example, in the following snippet:
2263
- #
2264
- # 1 + 2
2265
- #
2266
- # the + sign is an operator.
2267
- def on_op(value)
2268
- start_line = lineno
2269
- start_char = char_pos
2270
-
2271
- node = {
2272
- type: :@op,
2273
- body: value,
2274
- sl: start_line,
2275
- el: start_line,
2276
- sc: start_char,
2277
- ec: start_char + value.size
2278
- }
2279
-
2280
- scanner_events << node
2281
- node
2282
- end
2283
-
2284
- # opassign is a parser event that represents assigning something to a
2285
- # variable or constant using an operator like += or ||=. It accepts as
2286
- # arguments the left side of the expression before the operator, the
2287
- # operator itself, and the right side of the expression.
2288
- def on_opassign(left, oper, right)
2289
- left.merge(
2290
- type: :opassign,
2291
- body: [left, oper, right],
2292
- el: right[:el],
2293
- ec: right[:ec]
2294
- )
2295
- end
2296
-
2297
- # operator_ambiguous is a parser event that represents when the parsers sees
2298
- # an operator as ambiguous. For example, in the following snippet:
2299
- #
2300
- # foo %[]
2301
- #
2302
- # the question becomes if the percent sign is being used as a method call or
2303
- # if it's the start of a string literal. We don't need to track this event in
2304
- # the AST that we're generating, so we're not going to define an explicit
2305
- # handler for it.
2306
- #
2307
- # def on_operator_ambiguous(value)
2308
- # value
2309
- # end
2310
-
2311
- # params is a parser event that represents defining parameters on a
2312
- # method. They have a somewhat interesting structure in that they are an
2313
- # array of arrays where the position in the top-level array indicates the
2314
- # type of param and the subarray is the list of parameters of that type.
2315
- # We therefore have to flatten them down to get to the location.
2316
- def on_params(*types)
2317
- flattened = types.flatten(2).select { |type| type.is_a?(Hash) }
2318
- location =
2319
- if flattened.any?
2320
- {
2321
- sl: flattened[0][:sl],
2322
- sc: flattened[0][:sc],
2323
- el: flattened[-1][:el],
2324
- ec: flattened[-1][:ec]
2325
- }
2326
- else
2327
- { sl: lineno, sc: char_pos, el: lineno, ec: char_pos }
2328
- end
2329
-
2330
- location.merge!(type: :params, body: types)
2331
- end
2332
-
2333
- # A paren is a parser event that represents using parentheses pretty much
2334
- # anywhere in a Ruby program. It accepts as arguments the contents, which
2335
- # can be either params or statements.
2336
- def on_paren(contents)
2337
- lparen = find_scanner_event(:@lparen)
2338
- rparen = find_scanner_event(:@rparen)
2339
-
2340
- if contents && contents[:type] == :params
2341
- contents.merge!(
2342
- sc: find_next_statement_start(lparen[:ec]),
2343
- ec: rparen[:sc]
2344
- )
2345
- end
2346
-
2347
- {
2348
- type: :paren,
2349
- lparen: lparen,
2350
- body: [contents],
2351
- sl: lparen[:sl],
2352
- sc: lparen[:sc],
2353
- el: rparen[:el],
2354
- ec: rparen[:ec]
2355
- }
2356
- end
2357
-
2358
- # If we encounter a parse error, just immediately bail out so that our runner
2359
- # can catch it.
2360
- def on_parse_error(error, *)
2361
- raise ParserError.new(error, lineno, column)
2362
- end
2363
- alias on_alias_error on_parse_error
2364
- alias on_assign_error on_parse_error
2365
- alias on_class_name_error on_parse_error
2366
- alias on_param_error on_parse_error
2367
-
2368
- # period is a scanner event that represents the use of the period operator. It
2369
- # is usually found in method calls.
2370
- def on_period(value)
2371
- start_line = lineno
2372
- start_char = char_pos
2373
-
2374
- {
2375
- type: :@period,
2376
- body: value,
2377
- sl: start_line,
2378
- el: start_line,
2379
- sc: start_char,
2380
- ec: start_char + value.size
2381
- }
2382
- end
2383
-
2384
- # The program node is the very top of the AST. Here we'll attach all of
2385
- # the comments that we've gathered up over the course of parsing the
2386
- # source string. We'll also attach on the __END__ content if there was
2387
- # some found at the end of the source string.
2388
- def on_program(stmts)
2389
- range = { sl: 1, el: lines.length, sc: 0, ec: source.length }
2390
-
2391
- stmts[:body] << @__end__ if @__end__
2392
- stmts.bind(0, source.length)
2393
-
2394
- range.merge(type: :program, body: [stmts], comments: @comments)
2395
- end
2396
-
2397
- # qsymbols_beg is a scanner event that represents the beginning of a symbol
2398
- # literal array. For example in the following snippet:
2399
- #
2400
- # %i[foo bar baz]
2401
- #
2402
- # a qsymbols_beg would be triggered with the value of "%i[".
2403
- def on_qsymbols_beg(value)
2404
- start_line = lineno
2405
- start_char = char_pos
2406
-
2407
- node = {
2408
- type: :@qsymbols_beg,
2409
- body: value,
2410
- sl: start_line,
2411
- el: start_line,
2412
- sc: start_char,
2413
- ec: start_char + value.size
2414
- }
2415
-
2416
- scanner_events << node
2417
- node
2418
- end
2419
-
2420
- # qsymbols_new is a parser event that represents the beginning of a symbol
2421
- # literal array, like %i[one two three]. It can be followed by any number
2422
- # of qsymbols_add events, which we'll append onto an array body.
2423
- def on_qsymbols_new
2424
- find_scanner_event(:@qsymbols_beg).merge!(type: :qsymbols, body: [])
2425
- end
2426
-
2427
- # qsymbols_add is a parser event that represents an element inside of a
2428
- # symbol literal array like %i[one two three]. It accepts as arguments the
2429
- # parent qsymbols node as well as a tstring_content scanner event
2430
- # representing the bare words.
2431
- def on_qsymbols_add(qsymbols, tstring_content)
2432
- qsymbols.merge!(
2433
- body: qsymbols[:body] << tstring_content,
2434
- el: tstring_content[:el],
2435
- ec: tstring_content[:ec]
2436
- )
2437
- end
2438
-
2439
- # qwords_beg is a scanner event that represents the beginning of a word
2440
- # literal array. For example in the following snippet:
2441
- #
2442
- # %w[foo bar baz]
2443
- #
2444
- # a qwords_beg would be triggered with the value of "%w[".
2445
- def on_qwords_beg(value)
2446
- start_line = lineno
2447
- start_char = char_pos
2448
-
2449
- node = {
2450
- type: :@qwords_beg,
2451
- body: value,
2452
- sl: start_line,
2453
- el: start_line,
2454
- sc: start_char,
2455
- ec: start_char + value.size
2456
- }
2457
-
2458
- scanner_events << node
2459
- node
2460
- end
2461
-
2462
- # qwords_new is a parser event that represents the beginning of a string
2463
- # literal array, like %w[one two three]. It can be followed by any number
2464
- # of qwords_add events, which we'll append onto an array body.
2465
- def on_qwords_new
2466
- find_scanner_event(:@qwords_beg).merge!(type: :qwords, body: [])
2467
- end
2468
-
2469
- # qsymbols_add is a parser event that represents an element inside of a
2470
- # symbol literal array like %i[one two three]. It accepts as arguments the
2471
- # parent qsymbols node as well as a tstring_content scanner event
2472
- # representing the bare words.
2473
- def on_qwords_add(qwords, tstring_content)
2474
- qwords.merge!(
2475
- body: qwords[:body] << tstring_content,
2476
- el: tstring_content[:el],
2477
- ec: tstring_content[:ec]
2478
- )
2479
- end
2480
-
2481
- # rational is a scanner event that represents a rational number literal.
2482
- def on_rational(value)
2483
- start_line = lineno
2484
- start_char = char_pos
2485
-
2486
- node = {
2487
- type: :@rational,
2488
- body: value,
2489
- sl: start_line,
2490
- el: start_line,
2491
- sc: start_char,
2492
- ec: start_char + value.size
2493
- }
2494
-
2495
- scanner_events << node
2496
- node
2497
- end
2498
-
2499
- # rbrace is a scanner event that represents the use of a right brace, i.e.,
2500
- # "}".
2501
- def on_rbrace(value)
2502
- start_line = lineno
2503
- start_char = char_pos
2504
-
2505
- node = {
2506
- type: :@rbrace,
2507
- body: value,
2508
- sl: start_line,
2509
- el: start_line,
2510
- sc: start_char,
2511
- ec: start_char + value.size
2512
- }
2513
-
2514
- scanner_events << node
2515
- node
2516
- end
2517
-
2518
- # rbracket is a scanner event that represents the use of a right bracket,
2519
- # i.e., "]".
2520
- def on_rbracket(value)
2521
- start_line = lineno
2522
- start_char = char_pos
2523
-
2524
- node = {
2525
- type: :@rbracket,
2526
- body: value,
2527
- sl: start_line,
2528
- el: start_line,
2529
- sc: start_char,
2530
- ec: start_char + value.size
2531
- }
2532
-
2533
- scanner_events << node
2534
- node
2535
- end
2536
-
2537
- # redo is a parser event that represents the bare redo keyword. It has no
2538
- # body as it accepts no arguments.
2539
- def on_redo
2540
- find_scanner_event(:@kw, 'redo').merge!(type: :redo)
2541
- end
2542
-
2543
- # regexp_add is a parser event that represents a piece of a regular expression
2544
- # body. It accepts as arguments the parent regexp node as well as a
2545
- # tstring_content scanner event representing string content, a
2546
- # string_embexpr parser event representing interpolated content, or a
2547
- # string_dvar parser event representing an interpolated variable.
2548
- def on_regexp_add(regexp, piece)
2549
- regexp.merge!(
2550
- body: regexp[:body] << piece,
2551
- el: regexp[:el],
2552
- ec: regexp[:ec]
2553
- )
2554
- end
2555
-
2556
- # regexp_beg is a scanner event that represents the start of a regular
2557
- # expression. It can take a couple of forms since regexp can either start with
2558
- # a forward slash or a %r.
2559
- def on_regexp_beg(value)
2560
- start_line = lineno
2561
- start_char = char_pos
2562
-
2563
- node = {
2564
- type: :@regexp_beg,
2565
- body: value,
2566
- sl: start_line,
2567
- el: start_line,
2568
- sc: start_char,
2569
- ec: start_char + value.size
2570
- }
2571
-
2572
- scanner_events << node
2573
- node
2574
- end
2575
-
2576
- # regexp_end is a scanner event that represents the end of a regular
2577
- # expression. It will contain the closing brace or slash, as well as any flags
2578
- # being passed to the regexp.
2579
- def on_regexp_end(value)
2580
- start_line = lineno
2581
- start_char = char_pos
2582
-
2583
- {
2584
- type: :@regexp_end,
2585
- body: value,
2586
- sl: start_line,
2587
- el: start_line,
2588
- sc: start_char,
2589
- ec: start_char + value.size
2590
- }
2591
- end
2592
-
2593
- # regexp_literal is a parser event that represents a regular expression.
2594
- # It accepts as arguments a regexp node which is a built-up array of
2595
- # pieces that go into the regexp content, as well as the ending used to
2596
- # close out the regexp which includes any modifiers.
2597
- def on_regexp_literal(regexp, ending)
2598
- regexp.merge!(
2599
- type: :regexp_literal,
2600
- ending: ending[:body],
2601
- el: ending[:el],
2602
- ec: ending[:ec]
2603
- )
2604
- end
2605
-
2606
- # regexp_new is a parser event that represents the beginning of a regular
2607
- # expression literal, like /foo/. It can be followed by any number of
2608
- # regexp_add events, which we'll append onto an array body.
2609
- def on_regexp_new
2610
- beging = find_scanner_event(:@regexp_beg)
2611
- beging.merge!(type: :regexp, body: [], beging: beging[:body])
2612
- end
2613
-
2614
- # rescue is a special kind of node where you have a rescue chain but it
2615
- # doesn't really have all of the information that it needs in order to
2616
- # determine its ending. Therefore it relies on its parent bodystmt node to
2617
- # report its ending to it.
2618
- class Rescue < Node
2619
- def bind_end(ec)
2620
- value.merge!(ec: ec)
2621
-
2622
- stmts = value[:body][1]
2623
- consequent = value[:body][2]
2624
-
2625
- if consequent
2626
- consequent.bind_end(ec)
2627
- stmts.bind_end(consequent[:sc])
2628
- else
2629
- stmts.bind_end(ec)
2630
- end
2631
- end
2632
- end
2633
-
2634
- # rescue is a parser event that represents the use of the rescue keyword
2635
- # inside of a bodystmt.
2636
- def on_rescue(exceptions, variable, stmts, consequent)
2637
- beging = find_scanner_event(:@kw, 'rescue')
2638
- exceptions = exceptions[0] if exceptions.is_a?(Array)
2639
-
2640
- last_node = variable || exceptions || beging
2641
- stmts.bind(find_next_statement_start(last_node[:ec]), char_pos)
2642
-
2643
- # We add an additional inner node here that ripper doesn't provide so that
2644
- # we have a nice place to attach inline comment. But we only need it if we
2645
- # have an exception or a variable that we're rescuing.
2646
- rescue_ex =
2647
- if exceptions || variable
2648
- {
2649
- type: :rescue_ex,
2650
- body: [exceptions, variable],
2651
- sl: beging[:sl],
2652
- sc: beging[:ec] + 1,
2653
- el: last_node[:el],
2654
- ec: last_node[:ec]
2655
- }
2656
- end
2657
-
2658
- Rescue.new(
2659
- self,
2660
- beging.merge!(
2661
- type: :rescue,
2662
- body: [rescue_ex, stmts, consequent],
2663
- el: lineno,
2664
- ec: char_pos
2665
- )
2666
- )
2667
- end
2668
-
2669
- # rescue_mod represents the modifier form of a rescue clause. It accepts as
2670
- # arguments the statement that may raise an error and the value that should
2671
- # be used if it does.
2672
- def on_rescue_mod(statement, rescued)
2673
- find_scanner_event(:@kw, 'rescue')
2674
-
2675
- {
2676
- type: :rescue_mod,
2677
- body: [statement, rescued],
2678
- sl: statement[:sl],
2679
- sc: statement[:sc],
2680
- el: rescued[:el],
2681
- ec: rescued[:ec]
2682
- }
2683
- end
2684
-
2685
- # rest_param is a parser event that represents defining a parameter in a
2686
- # method definition that accepts all remaining positional parameters. It
2687
- # accepts as an argument an optional identifier for the parameter. If it
2688
- # is omitted, then we're just using the plain operator.
2689
- def on_rest_param(ident)
2690
- oper = find_scanner_event(:@op, '*')
2691
- return oper.merge!(type: :rest_param, body: [nil]) unless ident
2692
-
2693
- oper.merge!(
2694
- type: :rest_param,
2695
- body: [ident],
2696
- el: ident[:el],
2697
- ec: ident[:ec]
2698
- )
2699
- end
2700
-
2701
- # retry is a parser event that represents the bare retry keyword. It has
2702
- # no body as it accepts no arguments.
2703
- def on_retry
2704
- find_scanner_event(:@kw, 'retry').merge!(type: :retry)
2705
- end
2706
-
2707
- # return is a parser event that represents using the return keyword with
2708
- # arguments. It accepts as an argument an args_add_block event that
2709
- # contains all of the arguments being passed.
2710
- def on_return(args_add_block)
2711
- find_scanner_event(:@kw, 'return').merge!(
2712
- type: :return,
2713
- body: [args_add_block],
2714
- el: args_add_block[:el],
2715
- ec: args_add_block[:ec]
2716
- )
2717
- end
2718
-
2719
- # return0 is a parser event that represents the bare return keyword. It
2720
- # has no body as it accepts no arguments. This is as opposed to the return
2721
- # parser event, which is the version where you're returning one or more
2722
- # values.
2723
- def on_return0
2724
- find_scanner_event(:@kw, 'return').merge!(type: :return0)
2725
- end
2726
-
2727
- # rparen is a scanner event that represents the use of a right parenthesis,
2728
- # i.e., ")".
2729
- def on_rparen(value)
2730
- start_line = lineno
2731
- start_char = char_pos
2732
-
2733
- node = {
2734
- type: :@rparen,
2735
- body: value,
2736
- sl: start_line,
2737
- el: start_line,
2738
- sc: start_char,
2739
- ec: start_char + value.size
2740
- }
2741
-
2742
- scanner_events << node
2743
- node
2744
- end
2745
-
2746
- # sclass is a parser event that represents a block of statements that
2747
- # should be evaluated within the context of the singleton class of an
2748
- # object. It's frequently used to define singleton methods. It looks like
2749
- # the following example:
2750
- #
2751
- # class << self do foo end
2752
- # │ │
2753
- # │ └> bodystmt
2754
- # └> target
2755
- #
2756
- def on_sclass(target, bodystmt)
2757
- beging = find_scanner_event(:@kw, 'class')
2758
- ending = find_scanner_event(:@kw, 'end')
2759
-
2760
- bodystmt.bind(find_next_statement_start(target[:ec]), ending[:sc])
2761
-
2762
- {
2763
- type: :sclass,
2764
- body: [target, bodystmt],
2765
- sl: beging[:sl],
2766
- sc: beging[:sc],
2767
- el: ending[:el],
2768
- ec: ending[:ec]
2769
- }
2770
- end
2771
-
2772
- # semicolon is a scanner event that represents the use of a semicolon in the
2773
- # source. We don't need to track this event in the AST that we're generating,
2774
- # so we're not going to define an explicit handler for it.
2775
- #
2776
- # def on_semicolon(value)
2777
- # value
2778
- # end
2779
-
2780
- # sp is a scanner event that represents the use of a space in the source. As
2781
- # you can imagine, this event gets triggered quite often. We don't need to
2782
- # track this event in the AST that we're generating, so we're not going to
2783
- # define an explicit handler for it.
2784
- #
2785
- # def on_sp(value)
2786
- # value
2787
- # end
2788
-
2789
- # stmts_add is a parser event that represents a single statement inside a
2790
- # list of statements within any lexical block. It accepts as arguments the
2791
- # parent stmts node as well as an stmt which can be any expression in
2792
- # Ruby.
2793
- def on_stmts_add(stmts, stmt)
2794
- stmts << stmt
2795
- end
2796
-
2797
- # Everything that has a block of code inside of it has a list of statements.
2798
- # Normally we would just track those as a node that has an array body, but we
2799
- # have some special handling in order to handle empty statement lists. They
2800
- # need to have the right location information, so all of the parent node of
2801
- # stmts nodes will report back down the location information. We then
2802
- # propagate that onto void_stmt nodes inside the stmts in order to make sure
2803
- # all comments get printed appropriately.
2804
- class Stmts < Node
2805
- def bind(sc, ec)
2806
- value.merge!(sc: sc, ec: ec)
2807
-
2808
- if value[:body][0][:type] == :void_stmt
2809
- value[:body][0].merge!(sc: sc, ec: sc)
2810
- end
2811
-
2812
- attach_comments(sc, ec)
2813
- end
2814
-
2815
- def bind_end(ec)
2816
- value.merge!(ec: ec)
2817
- end
2818
-
2819
- def <<(statement)
2820
- if value[:body].any?
2821
- value.merge!(statement.slice(:el, :ec))
2822
- else
2823
- value.merge!(statement.slice(:sl, :el, :sc, :ec))
2824
- end
2825
-
2826
- value[:body] << statement
2827
- self
2828
- end
2829
-
2830
- private
2831
-
2832
- def attach_comments(sc, ec)
2833
- attachable =
2834
- parser.comments.select do |comment|
2835
- comment[:type] == :@comment && !comment[:inline] &&
2836
- sc <= comment[:sc] && ec >= comment[:ec] &&
2837
- !comment[:value].include?('prettier-ignore')
2838
- end
2839
-
2840
- return if attachable.empty?
2841
-
2842
- parser.comments -= attachable
2843
- value[:body] = (value[:body] + attachable).sort_by! { |node| node[:sc] }
2844
- end
2845
- end
2846
-
2847
- # stmts_new is a parser event that represents the beginning of a list of
2848
- # statements within any lexical block. It can be followed by any number of
2849
- # stmts_add events, which we'll append onto an array body.
2850
- def on_stmts_new
2851
- Stmts.new(
2852
- self,
2853
- type: :stmts,
2854
- body: [],
2855
- sl: lineno,
2856
- el: lineno,
2857
- sc: char_pos,
2858
- ec: char_pos
2859
- )
2860
- end
2861
-
2862
- # string_add is a parser event that represents a piece of a string. It
2863
- # could be plain @tstring_content, string_embexpr, or string_dvar nodes.
2864
- # It accepts as arguments the parent string node as well as the additional
2865
- # piece of the string.
2866
- def on_string_add(string, piece)
2867
- string.merge!(body: string[:body] << piece, el: piece[:el], ec: piece[:ec])
2868
- end
2869
-
2870
- # string_concat is a parser event that represents concatenating two
2871
- # strings together using a backward slash, as in the following example:
2872
- #
2873
- # 'foo' \
2874
- # 'bar'
2875
- #
2876
- def on_string_concat(left, right)
2877
- {
2878
- type: :string_concat,
2879
- body: [left, right],
2880
- sl: left[:sl],
2881
- sc: left[:sc],
2882
- el: right[:el],
2883
- ec: right[:ec]
2884
- }
2885
- end
2886
-
2887
- # string_content is a parser event that represents the beginning of the
2888
- # contents of a string, which will either be embedded inside of a
2889
- # string_literal or a dyna_symbol node. It will have an array body so that
2890
- # we can build up a list of @tstring_content, string_embexpr, and
2891
- # string_dvar nodes.
2892
- def on_string_content
2893
- {
2894
- type: :string,
2895
- body: [],
2896
- sl: lineno,
2897
- el: lineno,
2898
- sc: char_pos,
2899
- ec: char_pos
2900
- }
2901
- end
2902
-
2903
- # string_dvar is a parser event that represents a very special kind of
2904
- # interpolation into string. It allows you to take an instance variable,
2905
- # class variable, or global variable and omit the braces when
2906
- # interpolating. For example, if you wanted to interpolate the instance
2907
- # variable @foo into a string, you could do "#@foo".
2908
- def on_string_dvar(var_ref)
2909
- find_scanner_event(:@embvar).merge!(
2910
- type: :string_dvar,
2911
- body: [var_ref],
2912
- el: var_ref[:el],
2913
- ec: var_ref[:ec]
2914
- )
2915
- end
2916
-
2917
- # string_embexpr is a parser event that represents interpolated content.
2918
- # It can go a bunch of different parent nodes, including regexp, strings,
2919
- # xstrings, heredocs, dyna_symbols, etc. Basically it's anywhere you see
2920
- # the #{} construct.
2921
- def on_string_embexpr(stmts)
2922
- beging = find_scanner_event(:@embexpr_beg)
2923
- ending = find_scanner_event(:@embexpr_end)
2924
-
2925
- stmts.bind(beging[:ec], ending[:sc])
2926
-
2927
- {
2928
- type: :string_embexpr,
2929
- body: [stmts],
2930
- sl: beging[:sl],
2931
- sc: beging[:sc],
2932
- el: ending[:el],
2933
- ec: ending[:ec]
2934
- }
2935
- end
2936
-
2937
- # String literals are either going to be a normal string or they're going
2938
- # to be a heredoc if we've just closed a heredoc.
2939
- def on_string_literal(string)
2940
- heredoc = @heredocs[-1]
2941
-
2942
- if heredoc && heredoc[:ending]
2943
- @heredocs.pop.merge!(body: string[:body])
2944
- else
2945
- beging = find_scanner_event(:@tstring_beg)
2946
- ending = find_scanner_event(:@tstring_end)
2947
-
2948
- {
2949
- type: :string_literal,
2950
- body: string[:body],
2951
- quote: beging[:body],
2952
- sl: beging[:sl],
2953
- sc: beging[:sc],
2954
- el: ending[:el],
2955
- ec: ending[:ec]
2956
- }
2957
- end
2958
- end
2959
-
2960
- # A super is a parser event that represents using the super keyword with
2961
- # any number of arguments. It can optionally use parentheses (represented
2962
- # by an arg_paren node) or just skip straight to the arguments (with an
2963
- # args_add_block node).
2964
- def on_super(contents)
2965
- find_scanner_event(:@kw, 'super').merge!(
2966
- type: :super,
2967
- body: [contents],
2968
- el: contents[:el],
2969
- ec: contents[:ec]
2970
- )
2971
- end
2972
-
2973
- # symbeg is a scanner event that represents the beginning of a symbol literal.
2974
- # In most cases it will contain just ":" as in the value, but if its a dynamic
2975
- # symbol being defined it will contain ":'" or ":\"".
2976
- def on_symbeg(value)
2977
- start_line = lineno
2978
- start_char = char_pos
2979
-
2980
- node = {
2981
- type: :@symbeg,
2982
- body: value,
2983
- sl: start_line,
2984
- el: start_line,
2985
- sc: start_char,
2986
- ec: start_char + value.size
2987
- }
2988
-
2989
- scanner_events << node
2990
- node
2991
- end
2992
-
2993
- # A symbol is a parser event that immediately descends from a symbol
2994
- # literal and contains an ident representing the contents of the symbol.
2995
- def on_symbol(ident)
2996
- # When ripper is lexing source text, it turns symbols into keywords if their
2997
- # contents match, which will mess up the location information of all of our
2998
- # other nodes. So for example instead of { type: :@ident, body: "class" }
2999
- # you would instead get { type: :@kw, body: "class" }.
3000
- #
3001
- # In order to take care of this, we explicitly delete this scanner event
3002
- # from the stack to make sure it doesn't screw things up.
3003
- scanner_events.pop
3004
-
3005
- ident.merge(type: :symbol, body: [ident])
3006
- end
3007
-
3008
- # A symbol_literal represents a symbol in the system with no interpolation
3009
- # (as opposed to a dyna_symbol). As its only argument it accepts either a
3010
- # symbol node (for most cases) or an ident node (in the case that we're
3011
- # using bare words, as in an alias node like alias foo bar).
3012
- def on_symbol_literal(contents)
3013
- if scanner_events[-1] == contents
3014
- contents.merge(type: :symbol_literal, body: [contents])
3015
- else
3016
- beging = find_scanner_event(:@symbeg)
3017
- contents.merge!(type: :symbol_literal, sc: beging[:sc])
3018
- end
3019
- end
3020
-
3021
- # symbols_beg is a scanner event that represents the start of a symbol literal
3022
- # array with interpolation. For example, in the following snippet:
3023
- #
3024
- # %I[foo bar baz]
3025
- #
3026
- # symbols_beg would be triggered with the value of "%I".
3027
- def on_symbols_beg(value)
3028
- start_line = lineno
3029
- start_char = char_pos
3030
-
3031
- node = {
3032
- type: :@symbols_beg,
3033
- body: value,
3034
- sl: start_line,
3035
- el: start_line,
3036
- sc: start_char,
3037
- ec: start_char + value.size
3038
- }
3039
-
3040
- scanner_events << node
3041
- node
3042
- end
3043
-
3044
- # symbols_new is a parser event that represents the beginning of a symbol
3045
- # literal array that accepts interpolation, like %I[one #{two} three]. It
3046
- # can be followed by any number of symbols_add events, which we'll append
3047
- # onto an array body.
3048
- def on_symbols_new
3049
- find_scanner_event(:@symbols_beg).merge!(type: :symbols, body: [])
3050
- end
3051
-
3052
- # symbols_add is a parser event that represents an element inside of a
3053
- # symbol literal array that accepts interpolation, like
3054
- # %I[one #{two} three]. It accepts as arguments the parent symbols node as
3055
- # well as a word_add parser event.
3056
- def on_symbols_add(symbols, word_add)
3057
- symbols.merge!(
3058
- body: symbols[:body] << word_add,
3059
- el: word_add[:el],
3060
- ec: word_add[:ec]
3061
- )
3062
- end
3063
-
3064
- # tlambda is a scanner event that represents the beginning of a lambda
3065
- # literal. It always has the value of "->".
3066
- def on_tlambda(value)
3067
- start_line = lineno
3068
- start_char = char_pos
3069
-
3070
- node = {
3071
- type: :@tlambda,
3072
- body: value,
3073
- sl: start_line,
3074
- el: start_line,
3075
- sc: start_char,
3076
- ec: start_char + value.size
3077
- }
3078
-
3079
- scanner_events << node
3080
- node
3081
- end
3082
-
3083
- # tlambeg is a scanner event that represents the beginning of the body of a
3084
- # lambda literal. It always has the value of "{".
3085
- def on_tlambeg(value)
3086
- start_line = lineno
3087
- start_char = char_pos
3088
-
3089
- node = {
3090
- type: :@tlambeg,
3091
- body: value,
3092
- sl: start_line,
3093
- el: start_line,
3094
- sc: start_char,
3095
- ec: start_char + value.size
3096
- }
3097
-
3098
- scanner_events << node
3099
- node
3100
- end
3101
-
3102
- # A top_const_field is a parser event that is always the child of some
3103
- # kind of assignment. It represents when you're assigning to a constant
3104
- # that is being referenced at the top level. For example:
3105
- #
3106
- # ::X = 1
3107
- #
3108
- def on_top_const_field(const)
3109
- beging = find_colon2_before(const)
3110
- const.merge(
3111
- type: :top_const_field,
3112
- body: [const],
3113
- sl: beging[:sl],
3114
- sc: beging[:sc]
3115
- )
3116
- end
3117
-
3118
- # A top_const_ref is a parser event that is a very similar to
3119
- # top_const_field except that it is not involved in an assignment. It
3120
- # looks like the following example:
3121
- #
3122
- # ::X
3123
- #
3124
- def on_top_const_ref(const)
3125
- beging = find_colon2_before(const)
3126
- const.merge(
3127
- type: :top_const_ref,
3128
- body: [const],
3129
- sl: beging[:sl],
3130
- sc: beging[:sc]
3131
- )
3132
- end
3133
-
3134
- # tstring_beg is a scanner event that represents the beginning of a string
3135
- # literal. It can represent either of the quotes for its value, or it can have
3136
- # a %q/%Q with delimiter.
3137
- def on_tstring_beg(value)
3138
- start_line = lineno
3139
- start_char = char_pos
3140
-
3141
- node = {
3142
- type: :@tstring_beg,
3143
- body: value,
3144
- sl: start_line,
3145
- el: start_line,
3146
- sc: start_char,
3147
- ec: start_char + value.size
3148
- }
3149
-
3150
- scanner_events << node
3151
- node
3152
- end
3153
-
3154
- # tstring_content is a scanner event that represents plain characters inside
3155
- # of a string, heredoc, xstring, or regexp. Like comments, we need to force
3156
- # the encoding here so JSON doesn't break.
3157
- def on_tstring_content(value)
3158
- start_line = lineno
3159
- start_char = char_pos
3160
-
3161
- {
3162
- type: :@tstring_content,
3163
- body: value.force_encoding('UTF-8'),
3164
- sl: start_line,
3165
- el: start_line,
3166
- sc: start_char,
3167
- ec: start_char + value.size
3168
- }
3169
- end
3170
-
3171
- # tstring_end is a scanner event that represents the end of a string literal.
3172
- # It can either contain quotes, or it can have the end delimiter of a %q/%Q
3173
- # literal.
3174
- def on_tstring_end(value)
3175
- start_line = lineno
3176
- start_char = char_pos
3177
-
3178
- node = {
3179
- type: :@tstring_end,
3180
- body: value,
3181
- sl: start_line,
3182
- el: start_line,
3183
- sc: start_char,
3184
- ec: start_char + value.size
3185
- }
3186
-
3187
- scanner_events << node
3188
- node
3189
- end
3190
-
3191
- # A unary node represents a unary method being called on an expression, as
3192
- # in !, ~, or not. We have somewhat special handling of the not operator
3193
- # since if it has parentheses they don't get reported as a paren node for
3194
- # some reason.
3195
- def on_unary(oper, value)
3196
- if oper == :not
3197
- node = find_scanner_event(:@kw, 'not')
3198
-
3199
- paren = source[node[:ec]...value[:sc]].include?('(')
3200
- ending = paren ? find_scanner_event(:@rparen) : value
3201
-
3202
- node.merge!(
3203
- type: :unary,
3204
- oper: oper,
3205
- body: [value],
3206
- el: ending[:el],
3207
- ec: ending[:ec],
3208
- paren: paren
3209
- )
3210
- else
3211
- # Special case instead of using find_scanner_event here. It turns out that
3212
- # if you have a range that goes from a negative number to a negative
3213
- # number then you can end up with a .. or a ... that's higher in the
3214
- # stack. So we need to explicitly disallow those operators.
3215
- index =
3216
- scanner_events.rindex do |scanner_event|
3217
- scanner_event[:type] == :@op && scanner_event[:sc] < value[:sc] &&
3218
- !%w[.. ...].include?(scanner_event[:body])
3219
- end
3220
-
3221
- beging = scanner_events.delete_at(index)
3222
- beging.merge!(
3223
- type: :unary,
3224
- oper: oper[0],
3225
- body: [value],
3226
- el: value[:el],
3227
- ec: value[:ec]
3228
- )
3229
- end
3230
- end
3231
-
3232
- # undef nodes represent using the keyword undef. It accepts as an argument
3233
- # an array of symbol_literal nodes that represent each message that the
3234
- # user is attempting to undefine. We use the keyword to get the beginning
3235
- # location and the last symbol to get the ending.
3236
- def on_undef(symbol_literals)
3237
- last = symbol_literals.last
3238
-
3239
- find_scanner_event(:@kw, 'undef').merge!(
3240
- type: :undef,
3241
- body: symbol_literals,
3242
- el: last[:el],
3243
- ec: last[:ec]
3244
- )
3245
- end
3246
-
3247
- # unless is a parser event that represents the first clause in an unless
3248
- # chain. It accepts as arguments the predicate of the unless, the
3249
- # statements that are contained within the unless clause, and the optional
3250
- # consequent clause.
3251
- def on_unless(predicate, stmts, consequent)
3252
- beging = find_scanner_event(:@kw, 'unless')
3253
- ending = consequent || find_scanner_event(:@kw, 'end')
3254
-
3255
- stmts.bind(predicate[:ec], ending[:sc])
3256
-
3257
- {
3258
- type: :unless,
3259
- body: [predicate, stmts, consequent],
3260
- sl: beging[:sl],
3261
- sc: beging[:sc],
3262
- el: ending[:el],
3263
- ec: ending[:ec]
3264
- }
3265
- end
3266
-
3267
- # unless_mod is a parser event that represents the modifier form of an
3268
- # unless statement. It accepts as arguments the predicate of the unless
3269
- # and the statement that are contained within the unless clause.
3270
- def on_unless_mod(predicate, statement)
3271
- find_scanner_event(:@kw, 'unless')
3272
-
3273
- {
3274
- type: :unless_mod,
3275
- body: [predicate, statement],
3276
- sl: statement[:sl],
3277
- sc: statement[:sc],
3278
- el: predicate[:el],
3279
- ec: predicate[:ec]
3280
- }
3281
- end
3282
-
3283
- # until is a parser event that represents an until loop. It accepts as
3284
- # arguments the predicate to the until and the statements that are
3285
- # contained within the until clause.
3286
- def on_until(predicate, stmts)
3287
- beging = find_scanner_event(:@kw, 'until')
3288
- ending = find_scanner_event(:@kw, 'end')
3289
-
3290
- # Consume the do keyword if it exists so that it doesn't get confused for
3291
- # some other block
3292
- do_event = find_scanner_event(:@kw, 'do', consume: false)
3293
- if do_event && do_event[:sc] > predicate[:ec] && do_event[:ec] < ending[:sc]
3294
- scanner_events.delete(do_event)
3295
- end
3296
-
3297
- stmts.bind(predicate[:ec], ending[:sc])
3298
-
3299
- {
3300
- type: :until,
3301
- body: [predicate, stmts],
3302
- sl: beging[:sl],
3303
- sc: beging[:sc],
3304
- el: ending[:el],
3305
- ec: ending[:ec]
3306
- }
3307
- end
3308
-
3309
- # until_mod is a parser event that represents the modifier form of an
3310
- # until loop. It accepts as arguments the predicate to the until and the
3311
- # statement that is contained within the until loop.
3312
- def on_until_mod(predicate, statement)
3313
- find_scanner_event(:@kw, 'until')
3314
-
3315
- {
3316
- type: :until_mod,
3317
- body: [predicate, statement],
3318
- sl: statement[:sl],
3319
- sc: statement[:sc],
3320
- el: predicate[:el],
3321
- ec: predicate[:ec]
3322
- }
3323
- end
3324
-
3325
- # var_alias is a parser event that represents when you're using the alias
3326
- # keyword with global variable arguments. You can optionally use
3327
- # parentheses with this keyword, so we either track the location
3328
- # information based on those or the final argument to the alias method.
3329
- def on_var_alias(left, right)
3330
- beging = find_scanner_event(:@kw, 'alias')
3331
-
3332
- paren = source[beging[:ec]...left[:sc]].include?('(')
3333
- ending = paren ? find_scanner_event(:@rparen) : right
3334
-
3335
- {
3336
- type: :var_alias,
3337
- body: [left, right],
3338
- sl: beging[:sl],
3339
- sc: beging[:sc],
3340
- el: ending[:el],
3341
- ec: ending[:ec]
3342
- }
3343
- end
3344
-
3345
- # var_ref is a parser event that represents using either a local variable,
3346
- # a nil literal, a true or false literal, or a numbered block variable.
3347
- def on_var_ref(contents)
3348
- contents.merge(type: :var_ref, body: [contents])
3349
- end
3350
-
3351
- # var_field is a parser event that represents a variable that is being
3352
- # assigned a value. As such, it is always a child of an assignment type
3353
- # node. For example, in the following example foo is a var_field:
3354
- #
3355
- # foo = 1
3356
- #
3357
- def on_var_field(ident)
3358
- if ident
3359
- ident.merge(type: :var_field, body: [ident])
3360
- else
3361
- # You can hit this pattern if you're assigning to a splat using pattern
3362
- # matching syntax in Ruby 2.7+
3363
- { type: :var_field, body: nil }
3364
- end
3365
- end
3366
-
3367
- # vcall nodes are any plain named thing with Ruby that could be either a
3368
- # local variable or a method call. They accept as an argument the ident
3369
- # scanner event that contains their content.
3370
- #
3371
- # Access controls like private, protected, and public are reported as
3372
- # vcall nodes since they're technically method calls. We want to be able
3373
- # add new lines around them as necessary, so here we're going to
3374
- # explicitly track those as a different node type.
3375
- def on_vcall(ident)
3376
- @controls ||= %w[private protected public].freeze
3377
-
3378
- body = ident[:body]
3379
- type =
3380
- if @controls.include?(body) && body == lines[lineno - 1].strip
3381
- :access_ctrl
3382
- else
3383
- :vcall
3384
- end
3385
-
3386
- ident.merge(type: type, body: [ident])
3387
- end
3388
-
3389
- # void_stmt is a special kind of parser event that represents an empty lexical
3390
- # block of code. It often will have comments attached to it, so it requires
3391
- # some special handling.
3392
- def on_void_stmt
3393
- { type: :void_stmt, sl: lineno, el: lineno, sc: char_pos, ec: char_pos }
3394
- end
3395
-
3396
- # when is a parser event that represents another clause in a case chain.
3397
- # It accepts as arguments the predicate of the when, the statements that
3398
- # are contained within the else if clause, and the optional consequent
3399
- # clause.
3400
- def on_when(predicate, stmts, consequent)
3401
- beging = find_scanner_event(:@kw, 'when')
3402
- ending = consequent || find_scanner_event(:@kw, 'end')
3403
-
3404
- stmts.bind(predicate[:ec], ending[:sc])
3405
-
3406
- {
3407
- type: :when,
3408
- body: [predicate, stmts, consequent],
3409
- sl: beging[:sl],
3410
- sc: beging[:sc],
3411
- el: ending[:el],
3412
- ec: ending[:ec]
3413
- }
3414
- end
3415
-
3416
- # while is a parser event that represents a while loop. It accepts as
3417
- # arguments the predicate to the while and the statements that are
3418
- # contained within the while clause.
3419
- def on_while(predicate, stmts)
3420
- beging = find_scanner_event(:@kw, 'while')
3421
- ending = find_scanner_event(:@kw, 'end')
3422
-
3423
- # Consume the do keyword if it exists so that it doesn't get confused for
3424
- # some other block
3425
- do_event = find_scanner_event(:@kw, 'do', consume: false)
3426
- if do_event && do_event[:sc] > predicate[:ec] && do_event[:ec] < ending[:sc]
3427
- scanner_events.delete(do_event)
3428
- end
3429
-
3430
- stmts.bind(predicate[:ec], ending[:sc])
3431
-
3432
- {
3433
- type: :while,
3434
- body: [predicate, stmts],
3435
- sl: beging[:sl],
3436
- sc: beging[:sc],
3437
- el: ending[:el],
3438
- ec: ending[:ec]
3439
- }
3440
- end
3441
-
3442
- # while_mod is a parser event that represents the modifier form of an
3443
- # while loop. It accepts as arguments the predicate to the while and the
3444
- # statement that is contained within the while loop.
3445
- def on_while_mod(predicate, statement)
3446
- find_scanner_event(:@kw, 'while')
3447
-
3448
- {
3449
- type: :while_mod,
3450
- body: [predicate, statement],
3451
- sl: statement[:sl],
3452
- sc: statement[:sc],
3453
- el: predicate[:el],
3454
- ec: predicate[:ec]
3455
- }
3456
- end
3457
-
3458
- # word_new is a parser event that represents the beginning of a word
3459
- # within a special array literal (either strings or symbols) that accepts
3460
- # interpolation. For example, in the following array, there are three
3461
- # word nodes:
3462
- #
3463
- # %W[one a#{two}a three]
3464
- #
3465
- # Each word inside that array is represented as its own node, which is in
3466
- # terms of the parser a tree of word_new and word_add nodes. For our
3467
- # purposes, we're going to report this as a word node and build up an
3468
- # array body of our parts.
3469
- def on_word_new
3470
- { type: :word, body: [] }
3471
- end
3472
-
3473
- # word_add is a parser event that represents a piece of a word within a
3474
- # special array literal that accepts interpolation. It accepts as
3475
- # arguments the parent word node as well as the additional piece of the
3476
- # word, which can be either a @tstring_content node for a plain string
3477
- # piece or a string_embexpr for an interpolated piece.
3478
- def on_word_add(word, piece)
3479
- if word[:body].empty?
3480
- # Here we're making sure we get the correct bounds by using the
3481
- # location information from the first piece.
3482
- piece.merge(type: :word, body: [piece])
3483
- else
3484
- word.merge!(body: word[:body] << piece, el: piece[:el], ec: piece[:ec])
3485
- end
3486
- end
3487
-
3488
- # words_beg is a scanner event that represents the start of a word literal
3489
- # array with interpolation. For example, in the following snippet:
3490
- #
3491
- # %W[foo bar baz]
3492
- #
3493
- # words_beg would be triggered with the value of "%W".
3494
- def on_words_beg(value)
3495
- start_line = lineno
3496
- start_char = char_pos
3497
-
3498
- node = {
3499
- type: :@words_beg,
3500
- body: value,
3501
- sl: start_line,
3502
- el: start_line,
3503
- sc: start_char,
3504
- ec: start_char + value.size
3505
- }
3506
-
3507
- scanner_events << node
3508
- node
3509
- end
3510
-
3511
- # words_sep is a scanner event that represents the separate between two words
3512
- # inside of a word literal array. It contains any amount of whitespace
3513
- # characters that are used to delimit the words. For example,
3514
- #
3515
- # %w[
3516
- # foo
3517
- # bar
3518
- # baz
3519
- # ]
3520
- #
3521
- # in the snippet above there would be two words_sep events triggered, one
3522
- # between foo and bar and one between bar and baz. We don't need to track this
3523
- # event in the AST that we're generating, so we're not going to define an
3524
- # explicit handler for it.
3525
- #
3526
- # def on_words_sep(value)
3527
- # value
3528
- # end
3529
-
3530
- # words_new is a parser event that represents the beginning of a string
3531
- # literal array that accepts interpolation, like %W[one #{two} three]. It
3532
- # can be followed by any number of words_add events, which we'll append
3533
- # onto an array body.
3534
- def on_words_new
3535
- find_scanner_event(:@words_beg).merge!(type: :words, body: [])
3536
- end
3537
-
3538
- # words_add is a parser event that represents an element inside of a
3539
- # string literal array that accepts interpolation, like
3540
- # %W[one #{two} three]. It accepts as arguments the parent words node as
3541
- # well as a word_add parser event.
3542
- def on_words_add(words, word_add)
3543
- words.merge!(
3544
- body: words[:body] << word_add,
3545
- el: word_add[:el],
3546
- ec: word_add[:ec]
3547
- )
3548
- end
3549
-
3550
- # xstring_new is a parser event that represents the beginning of a string
3551
- # of commands that gets sent out to the terminal, like `ls`. It can
3552
- # optionally include interpolation much like a regular string, so we're
3553
- # going to build up an array body.
3554
- #
3555
- # If the xstring actually starts with a heredoc declaration, then we're
3556
- # going to let heredocs continue to do their thing and instead just use
3557
- # its location information.
3558
- def on_xstring_new
3559
- heredoc = @heredocs[-1]
3560
-
3561
- if heredoc && heredoc[:beging][3] = '`'
3562
- heredoc.merge(type: :xstring, body: [])
3563
- elsif RUBY_MAJOR <= 2 && RUBY_MINOR <= 5 && RUBY_PATCH < 7
3564
- { type: :xstring, body: [] }
3565
- else
3566
- find_scanner_event(:@backtick).merge!(type: :xstring, body: [])
3567
- end
3568
- end
3569
-
3570
- # xstring_add is a parser event that represents a piece of a string of
3571
- # commands that gets sent out to the terminal, like `ls`. It accepts two
3572
- # arguments, the parent xstring node as well as the piece that is being
3573
- # added to the string. Because it supports interpolation this is either a
3574
- # tstring_content scanner event representing bare string content or a
3575
- # string_embexpr representing interpolated content.
3576
- def on_xstring_add(xstring, piece)
3577
- xstring.merge!(
3578
- body: xstring[:body] << piece,
3579
- el: piece[:el],
3580
- ec: piece[:ec]
3581
- )
3582
- end
3583
-
3584
- # xstring_literal is a parser event that represents a string of commands
3585
- # that gets sent to the terminal, like `ls`. It accepts as its only
3586
- # argument an xstring node that is a built up array representation of all
3587
- # of the parts of the string (including the plain string content and the
3588
- # interpolated content).
3589
- #
3590
- # They can also use heredocs to present themselves, as in the example:
3591
- #
3592
- # <<-`SHELL`
3593
- # ls
3594
- # SHELL
3595
- #
3596
- # In this case we need to change the node type to be a heredoc instead of
3597
- # an xstring_literal in order to get the right formatting.
3598
- def on_xstring_literal(xstring)
3599
- heredoc = @heredocs[-1]
3600
-
3601
- if heredoc && heredoc[:beging][3] = '`'
3602
- heredoc.merge!(body: xstring[:body])
3603
- else
3604
- ending = find_scanner_event(:@tstring_end)
3605
- xstring.merge!(type: :xstring_literal, el: ending[:el], ec: ending[:ec])
3606
- end
3607
- end
3608
-
3609
- # yield is a parser event that represents using the yield keyword with
3610
- # arguments. It accepts as an argument an args_add_block event that
3611
- # contains all of the arguments being passed.
3612
- def on_yield(args_add_block)
3613
- find_scanner_event(:@kw, 'yield').merge!(
3614
- type: :yield,
3615
- body: [args_add_block],
3616
- el: args_add_block[:el],
3617
- ec: args_add_block[:ec]
3618
- )
3619
- end
3620
-
3621
- # yield0 is a parser event that represents the bare yield keyword. It has
3622
- # no body as it accepts no arguments. This is as opposed to the yield
3623
- # parser event, which is the version where you're yielding one or more
3624
- # values.
3625
- def on_yield0
3626
- find_scanner_event(:@kw, 'yield').merge!(type: :yield0)
3627
- end
3628
-
3629
- # zsuper is a parser event that represents the bare super keyword. It has
3630
- # no body as it accepts no arguments. This is as opposed to the super
3631
- # parser event, which is the version where you're calling super with one
3632
- # or more values.
3633
- def on_zsuper
3634
- find_scanner_event(:@kw, 'super').merge!(type: :zsuper)
3635
- end
3636
- end