prism 0.24.0 → 0.25.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/BSDmakefile +58 -0
  3. data/CHANGELOG.md +50 -1
  4. data/Makefile +5 -2
  5. data/README.md +45 -6
  6. data/config.yml +499 -4
  7. data/docs/build_system.md +31 -0
  8. data/docs/configuration.md +2 -0
  9. data/docs/cruby_compilation.md +1 -1
  10. data/docs/parser_translation.md +14 -9
  11. data/docs/releasing.md +2 -2
  12. data/docs/ripper_translation.md +50 -0
  13. data/docs/ruby_api.md +1 -0
  14. data/docs/serialization.md +26 -5
  15. data/ext/prism/api_node.c +911 -815
  16. data/ext/prism/api_pack.c +9 -0
  17. data/ext/prism/extconf.rb +27 -11
  18. data/ext/prism/extension.c +313 -66
  19. data/ext/prism/extension.h +5 -4
  20. data/include/prism/ast.h +213 -64
  21. data/include/prism/defines.h +106 -2
  22. data/include/prism/diagnostic.h +134 -71
  23. data/include/prism/encoding.h +22 -4
  24. data/include/prism/node.h +93 -0
  25. data/include/prism/options.h +82 -7
  26. data/include/prism/pack.h +11 -0
  27. data/include/prism/parser.h +198 -53
  28. data/include/prism/prettyprint.h +8 -0
  29. data/include/prism/static_literals.h +118 -0
  30. data/include/prism/util/pm_buffer.h +65 -2
  31. data/include/prism/util/pm_constant_pool.h +18 -1
  32. data/include/prism/util/pm_integer.h +119 -0
  33. data/include/prism/util/pm_list.h +1 -1
  34. data/include/prism/util/pm_newline_list.h +8 -0
  35. data/include/prism/util/pm_string.h +26 -2
  36. data/include/prism/version.h +2 -2
  37. data/include/prism.h +59 -1
  38. data/lib/prism/compiler.rb +8 -1
  39. data/lib/prism/debug.rb +46 -3
  40. data/lib/prism/desugar_compiler.rb +1 -1
  41. data/lib/prism/dispatcher.rb +29 -0
  42. data/lib/prism/dot_visitor.rb +87 -16
  43. data/lib/prism/dsl.rb +24 -12
  44. data/lib/prism/ffi.rb +67 -12
  45. data/lib/prism/lex_compat.rb +17 -15
  46. data/lib/prism/mutation_compiler.rb +11 -0
  47. data/lib/prism/node.rb +2096 -2499
  48. data/lib/prism/node_ext.rb +77 -29
  49. data/lib/prism/pack.rb +4 -0
  50. data/lib/prism/parse_result/comments.rb +34 -17
  51. data/lib/prism/parse_result/newlines.rb +3 -1
  52. data/lib/prism/parse_result.rb +78 -32
  53. data/lib/prism/pattern.rb +16 -4
  54. data/lib/prism/polyfill/string.rb +12 -0
  55. data/lib/prism/serialize.rb +439 -102
  56. data/lib/prism/translation/parser/compiler.rb +152 -50
  57. data/lib/prism/translation/parser/lexer.rb +103 -22
  58. data/lib/prism/translation/parser/rubocop.rb +41 -13
  59. data/lib/prism/translation/parser.rb +119 -7
  60. data/lib/prism/translation/parser33.rb +1 -1
  61. data/lib/prism/translation/parser34.rb +1 -1
  62. data/lib/prism/translation/ripper/sexp.rb +125 -0
  63. data/lib/prism/translation/ripper/shim.rb +5 -0
  64. data/lib/prism/translation/ripper.rb +3212 -462
  65. data/lib/prism/translation/ruby_parser.rb +35 -18
  66. data/lib/prism/translation.rb +3 -1
  67. data/lib/prism/visitor.rb +10 -0
  68. data/lib/prism.rb +8 -2
  69. data/prism.gemspec +33 -4
  70. data/rbi/prism/compiler.rbi +14 -0
  71. data/rbi/prism/desugar_compiler.rbi +5 -0
  72. data/rbi/prism/mutation_compiler.rbi +5 -0
  73. data/rbi/prism/node.rbi +8221 -0
  74. data/rbi/prism/node_ext.rbi +102 -0
  75. data/rbi/prism/parse_result.rbi +304 -0
  76. data/rbi/prism/translation/parser/compiler.rbi +13 -0
  77. data/rbi/prism/translation/ripper/ripper_compiler.rbi +5 -0
  78. data/rbi/prism/translation/ripper.rbi +25 -0
  79. data/rbi/prism/translation/ruby_parser.rbi +11 -0
  80. data/rbi/prism/visitor.rbi +470 -0
  81. data/rbi/prism.rbi +39 -7749
  82. data/sig/prism/compiler.rbs +9 -0
  83. data/sig/prism/dispatcher.rbs +16 -0
  84. data/sig/prism/dot_visitor.rbs +6 -0
  85. data/sig/prism/dsl.rbs +462 -0
  86. data/sig/prism/mutation_compiler.rbs +158 -0
  87. data/sig/prism/node.rbs +3529 -0
  88. data/sig/prism/node_ext.rbs +78 -0
  89. data/sig/prism/pack.rbs +43 -0
  90. data/sig/prism/parse_result.rbs +127 -0
  91. data/sig/prism/pattern.rbs +13 -0
  92. data/sig/prism/serialize.rbs +7 -0
  93. data/sig/prism/visitor.rbs +168 -0
  94. data/sig/prism.rbs +188 -4767
  95. data/src/diagnostic.c +575 -230
  96. data/src/encoding.c +211 -108
  97. data/src/node.c +7526 -447
  98. data/src/options.c +36 -12
  99. data/src/pack.c +33 -17
  100. data/src/prettyprint.c +1294 -1385
  101. data/src/prism.c +3628 -1099
  102. data/src/regexp.c +17 -2
  103. data/src/serialize.c +47 -28
  104. data/src/static_literals.c +552 -0
  105. data/src/token_type.c +1 -0
  106. data/src/util/pm_buffer.c +147 -20
  107. data/src/util/pm_char.c +4 -4
  108. data/src/util/pm_constant_pool.c +35 -11
  109. data/src/util/pm_integer.c +629 -0
  110. data/src/util/pm_list.c +1 -1
  111. data/src/util/pm_newline_list.c +14 -5
  112. data/src/util/pm_string.c +134 -5
  113. data/src/util/pm_string_list.c +2 -2
  114. metadata +35 -6
  115. data/docs/ripper.md +0 -36
  116. data/rbi/prism_static.rbi +0 -207
  117. data/sig/prism_static.rbs +0 -201
@@ -4,67 +4,436 @@ require "ripper"
4
4
 
5
5
  module Prism
6
6
  module Translation
7
- # Note: This integration is not finished, and therefore still has many
8
- # inconsistencies with Ripper. If you'd like to help out, pull requests would
9
- # be greatly appreciated!
7
+ # This class provides a compatibility layer between prism and Ripper. It
8
+ # functions by parsing the entire tree first and then walking it and
9
+ # executing each of the Ripper callbacks as it goes. To use this class, you
10
+ # treat `Prism::Translation::Ripper` effectively as you would treat the
11
+ # `Ripper` class.
10
12
  #
11
- # This class is meant to provide a compatibility layer between prism and
12
- # Ripper. It functions by parsing the entire tree first and then walking it
13
- # and executing each of the Ripper callbacks as it goes.
13
+ # Note that this class will serve the most common use cases, but Ripper's
14
+ # API is extensive and undocumented. It relies on reporting the state of the
15
+ # parser at any given time. We do our best to replicate that here, but
16
+ # because it is a different architecture it is not possible to perfectly
17
+ # replicate the behavior of Ripper.
14
18
  #
15
- # This class is going to necessarily be slower than the native Ripper API. It
16
- # is meant as a stopgap until developers migrate to using prism. It is also
17
- # meant as a test harness for the prism parser.
19
+ # The main known difference is that we may omit dispatching some events in
20
+ # some cases. This impacts the following events:
21
+ #
22
+ # * on_assign_error
23
+ # * on_comma
24
+ # * on_ignored_nl
25
+ # * on_ignored_sp
26
+ # * on_kw
27
+ # * on_label_end
28
+ # * on_lbrace
29
+ # * on_lbracket
30
+ # * on_lparen
31
+ # * on_nl
32
+ # * on_op
33
+ # * on_operator_ambiguous
34
+ # * on_rbrace
35
+ # * on_rbracket
36
+ # * on_rparen
37
+ # * on_semicolon
38
+ # * on_sp
39
+ # * on_symbeg
40
+ # * on_tstring_beg
41
+ # * on_tstring_end
18
42
  #
19
- # To use this class, you treat `Prism::Translation::Ripper` effectively as you would
20
- # treat the `Ripper` class.
21
43
  class Ripper < Compiler
22
- # This class mirrors the ::Ripper::SexpBuilder subclass of ::Ripper that
23
- # returns the arrays of [type, *children].
24
- class SexpBuilder < Ripper
25
- private
44
+ # Parses the given Ruby program read from +src+.
45
+ # +src+ must be a String or an IO or a object with a #gets method.
46
+ def Ripper.parse(src, filename = "(ripper)", lineno = 1)
47
+ new(src, filename, lineno).parse
48
+ end
26
49
 
27
- ::Ripper::PARSER_EVENTS.each do |event|
28
- define_method(:"on_#{event}") do |*args|
29
- [event, *args]
30
- end
31
- end
50
+ # Tokenizes the Ruby program and returns an array of an array,
51
+ # which is formatted like
52
+ # <code>[[lineno, column], type, token, state]</code>.
53
+ # The +filename+ argument is mostly ignored.
54
+ # By default, this method does not handle syntax errors in +src+,
55
+ # use the +raise_errors+ keyword to raise a SyntaxError for an error in +src+.
56
+ #
57
+ # require 'ripper'
58
+ # require 'pp'
59
+ #
60
+ # pp Ripper.lex("def m(a) nil end")
61
+ # #=> [[[1, 0], :on_kw, "def", FNAME ],
62
+ # [[1, 3], :on_sp, " ", FNAME ],
63
+ # [[1, 4], :on_ident, "m", ENDFN ],
64
+ # [[1, 5], :on_lparen, "(", BEG|LABEL],
65
+ # [[1, 6], :on_ident, "a", ARG ],
66
+ # [[1, 7], :on_rparen, ")", ENDFN ],
67
+ # [[1, 8], :on_sp, " ", BEG ],
68
+ # [[1, 9], :on_kw, "nil", END ],
69
+ # [[1, 12], :on_sp, " ", END ],
70
+ # [[1, 13], :on_kw, "end", END ]]
71
+ #
72
+ def Ripper.lex(src, filename = "-", lineno = 1, raise_errors: false)
73
+ result = Prism.lex_compat(src, filepath: filename, line: lineno)
32
74
 
33
- ::Ripper::SCANNER_EVENTS.each do |event|
34
- define_method(:"on_#{event}") do |value|
35
- [:"@#{event}", value, [lineno, column]]
36
- end
75
+ if result.failure? && raise_errors
76
+ raise SyntaxError, result.errors.first.message
77
+ else
78
+ result.value
37
79
  end
38
80
  end
39
81
 
40
- # This class mirrors the ::Ripper::SexpBuilderPP subclass of ::Ripper that
41
- # returns the same values as ::Ripper::SexpBuilder except with a couple of
42
- # niceties that flatten linked lists into arrays.
43
- class SexpBuilderPP < SexpBuilder
44
- private
82
+ # This contains a table of all of the parser events and their
83
+ # corresponding arity.
84
+ PARSER_EVENT_TABLE = {
85
+ BEGIN: 1,
86
+ END: 1,
87
+ alias: 2,
88
+ alias_error: 2,
89
+ aref: 2,
90
+ aref_field: 2,
91
+ arg_ambiguous: 1,
92
+ arg_paren: 1,
93
+ args_add: 2,
94
+ args_add_block: 2,
95
+ args_add_star: 2,
96
+ args_forward: 0,
97
+ args_new: 0,
98
+ array: 1,
99
+ aryptn: 4,
100
+ assign: 2,
101
+ assign_error: 2,
102
+ assoc_new: 2,
103
+ assoc_splat: 1,
104
+ assoclist_from_args: 1,
105
+ bare_assoc_hash: 1,
106
+ begin: 1,
107
+ binary: 3,
108
+ block_var: 2,
109
+ blockarg: 1,
110
+ bodystmt: 4,
111
+ brace_block: 2,
112
+ break: 1,
113
+ call: 3,
114
+ case: 2,
115
+ class: 3,
116
+ class_name_error: 2,
117
+ command: 2,
118
+ command_call: 4,
119
+ const_path_field: 2,
120
+ const_path_ref: 2,
121
+ const_ref: 1,
122
+ def: 3,
123
+ defined: 1,
124
+ defs: 5,
125
+ do_block: 2,
126
+ dot2: 2,
127
+ dot3: 2,
128
+ dyna_symbol: 1,
129
+ else: 1,
130
+ elsif: 3,
131
+ ensure: 1,
132
+ excessed_comma: 0,
133
+ fcall: 1,
134
+ field: 3,
135
+ fndptn: 4,
136
+ for: 3,
137
+ hash: 1,
138
+ heredoc_dedent: 2,
139
+ hshptn: 3,
140
+ if: 3,
141
+ if_mod: 2,
142
+ ifop: 3,
143
+ in: 3,
144
+ kwrest_param: 1,
145
+ lambda: 2,
146
+ magic_comment: 2,
147
+ massign: 2,
148
+ method_add_arg: 2,
149
+ method_add_block: 2,
150
+ mlhs_add: 2,
151
+ mlhs_add_post: 2,
152
+ mlhs_add_star: 2,
153
+ mlhs_new: 0,
154
+ mlhs_paren: 1,
155
+ module: 2,
156
+ mrhs_add: 2,
157
+ mrhs_add_star: 2,
158
+ mrhs_new: 0,
159
+ mrhs_new_from_args: 1,
160
+ next: 1,
161
+ nokw_param: 1,
162
+ opassign: 3,
163
+ operator_ambiguous: 2,
164
+ param_error: 2,
165
+ params: 7,
166
+ paren: 1,
167
+ parse_error: 1,
168
+ program: 1,
169
+ qsymbols_add: 2,
170
+ qsymbols_new: 0,
171
+ qwords_add: 2,
172
+ qwords_new: 0,
173
+ redo: 0,
174
+ regexp_add: 2,
175
+ regexp_literal: 2,
176
+ regexp_new: 0,
177
+ rescue: 4,
178
+ rescue_mod: 2,
179
+ rest_param: 1,
180
+ retry: 0,
181
+ return: 1,
182
+ return0: 0,
183
+ sclass: 2,
184
+ stmts_add: 2,
185
+ stmts_new: 0,
186
+ string_add: 2,
187
+ string_concat: 2,
188
+ string_content: 0,
189
+ string_dvar: 1,
190
+ string_embexpr: 1,
191
+ string_literal: 1,
192
+ super: 1,
193
+ symbol: 1,
194
+ symbol_literal: 1,
195
+ symbols_add: 2,
196
+ symbols_new: 0,
197
+ top_const_field: 1,
198
+ top_const_ref: 1,
199
+ unary: 2,
200
+ undef: 1,
201
+ unless: 3,
202
+ unless_mod: 2,
203
+ until: 2,
204
+ until_mod: 2,
205
+ var_alias: 2,
206
+ var_field: 1,
207
+ var_ref: 1,
208
+ vcall: 1,
209
+ void_stmt: 0,
210
+ when: 3,
211
+ while: 2,
212
+ while_mod: 2,
213
+ word_add: 2,
214
+ word_new: 0,
215
+ words_add: 2,
216
+ words_new: 0,
217
+ xstring_add: 2,
218
+ xstring_literal: 1,
219
+ xstring_new: 0,
220
+ yield: 1,
221
+ yield0: 0,
222
+ zsuper: 0
223
+ }
224
+
225
+ # This contains a table of all of the scanner events and their
226
+ # corresponding arity.
227
+ SCANNER_EVENT_TABLE = {
228
+ CHAR: 1,
229
+ __end__: 1,
230
+ backref: 1,
231
+ backtick: 1,
232
+ comma: 1,
233
+ comment: 1,
234
+ const: 1,
235
+ cvar: 1,
236
+ embdoc: 1,
237
+ embdoc_beg: 1,
238
+ embdoc_end: 1,
239
+ embexpr_beg: 1,
240
+ embexpr_end: 1,
241
+ embvar: 1,
242
+ float: 1,
243
+ gvar: 1,
244
+ heredoc_beg: 1,
245
+ heredoc_end: 1,
246
+ ident: 1,
247
+ ignored_nl: 1,
248
+ imaginary: 1,
249
+ int: 1,
250
+ ivar: 1,
251
+ kw: 1,
252
+ label: 1,
253
+ label_end: 1,
254
+ lbrace: 1,
255
+ lbracket: 1,
256
+ lparen: 1,
257
+ nl: 1,
258
+ op: 1,
259
+ period: 1,
260
+ qsymbols_beg: 1,
261
+ qwords_beg: 1,
262
+ rational: 1,
263
+ rbrace: 1,
264
+ rbracket: 1,
265
+ regexp_beg: 1,
266
+ regexp_end: 1,
267
+ rparen: 1,
268
+ semicolon: 1,
269
+ sp: 1,
270
+ symbeg: 1,
271
+ symbols_beg: 1,
272
+ tlambda: 1,
273
+ tlambeg: 1,
274
+ tstring_beg: 1,
275
+ tstring_content: 1,
276
+ tstring_end: 1,
277
+ words_beg: 1,
278
+ words_sep: 1,
279
+ ignored_sp: 1
280
+ }
281
+
282
+ # This array contains name of parser events.
283
+ PARSER_EVENTS = PARSER_EVENT_TABLE.keys
284
+
285
+ # This array contains name of scanner events.
286
+ SCANNER_EVENTS = SCANNER_EVENT_TABLE.keys
287
+
288
+ # This array contains name of all ripper events.
289
+ EVENTS = PARSER_EVENTS + SCANNER_EVENTS
290
+
291
+ # A list of all of the Ruby keywords.
292
+ KEYWORDS = [
293
+ "alias",
294
+ "and",
295
+ "begin",
296
+ "BEGIN",
297
+ "break",
298
+ "case",
299
+ "class",
300
+ "def",
301
+ "defined?",
302
+ "do",
303
+ "else",
304
+ "elsif",
305
+ "end",
306
+ "END",
307
+ "ensure",
308
+ "false",
309
+ "for",
310
+ "if",
311
+ "in",
312
+ "module",
313
+ "next",
314
+ "nil",
315
+ "not",
316
+ "or",
317
+ "redo",
318
+ "rescue",
319
+ "retry",
320
+ "return",
321
+ "self",
322
+ "super",
323
+ "then",
324
+ "true",
325
+ "undef",
326
+ "unless",
327
+ "until",
328
+ "when",
329
+ "while",
330
+ "yield",
331
+ "__ENCODING__",
332
+ "__FILE__",
333
+ "__LINE__"
334
+ ]
335
+
336
+ # A list of all of the Ruby binary operators.
337
+ BINARY_OPERATORS = [
338
+ :!=,
339
+ :!~,
340
+ :=~,
341
+ :==,
342
+ :===,
343
+ :<=>,
344
+ :>,
345
+ :>=,
346
+ :<,
347
+ :<=,
348
+ :&,
349
+ :|,
350
+ :^,
351
+ :>>,
352
+ :<<,
353
+ :-,
354
+ :+,
355
+ :%,
356
+ :/,
357
+ :*,
358
+ :**
359
+ ]
45
360
 
46
- def _dispatch_event_new # :nodoc:
47
- []
48
- end
361
+ private_constant :KEYWORDS, :BINARY_OPERATORS
49
362
 
50
- def _dispatch_event_push(list, item) # :nodoc:
51
- list << item
52
- list
363
+ # Parses +src+ and create S-exp tree.
364
+ # Returns more readable tree rather than Ripper.sexp_raw.
365
+ # This method is mainly for developer use.
366
+ # The +filename+ argument is mostly ignored.
367
+ # By default, this method does not handle syntax errors in +src+,
368
+ # returning +nil+ in such cases. Use the +raise_errors+ keyword
369
+ # to raise a SyntaxError for an error in +src+.
370
+ #
371
+ # require "ripper"
372
+ # require "pp"
373
+ #
374
+ # pp Ripper.sexp("def m(a) nil end")
375
+ # #=> [:program,
376
+ # [[:def,
377
+ # [:@ident, "m", [1, 4]],
378
+ # [:paren, [:params, [[:@ident, "a", [1, 6]]], nil, nil, nil, nil, nil, nil]],
379
+ # [:bodystmt, [[:var_ref, [:@kw, "nil", [1, 9]]]], nil, nil, nil]]]]
380
+ #
381
+ def Ripper.sexp(src, filename = "-", lineno = 1, raise_errors: false)
382
+ builder = SexpBuilderPP.new(src, filename, lineno)
383
+ sexp = builder.parse
384
+ if builder.error?
385
+ if raise_errors
386
+ raise SyntaxError, builder.error
387
+ end
388
+ else
389
+ sexp
53
390
  end
391
+ end
54
392
 
55
- ::Ripper::PARSER_EVENT_TABLE.each do |event, arity|
56
- case event
57
- when /_new\z/
58
- alias_method :"on_#{event}", :_dispatch_event_new if arity == 0
59
- when /_add\z/
60
- alias_method :"on_#{event}", :_dispatch_event_push
393
+ # Parses +src+ and create S-exp tree.
394
+ # This method is mainly for developer use.
395
+ # The +filename+ argument is mostly ignored.
396
+ # By default, this method does not handle syntax errors in +src+,
397
+ # returning +nil+ in such cases. Use the +raise_errors+ keyword
398
+ # to raise a SyntaxError for an error in +src+.
399
+ #
400
+ # require 'ripper'
401
+ # require 'pp'
402
+ #
403
+ # pp Ripper.sexp_raw("def m(a) nil end")
404
+ # #=> [:program,
405
+ # [:stmts_add,
406
+ # [:stmts_new],
407
+ # [:def,
408
+ # [:@ident, "m", [1, 4]],
409
+ # [:paren, [:params, [[:@ident, "a", [1, 6]]], nil, nil, nil]],
410
+ # [:bodystmt,
411
+ # [:stmts_add, [:stmts_new], [:var_ref, [:@kw, "nil", [1, 9]]]],
412
+ # nil,
413
+ # nil,
414
+ # nil]]]]
415
+ #
416
+ def Ripper.sexp_raw(src, filename = "-", lineno = 1, raise_errors: false)
417
+ builder = SexpBuilder.new(src, filename, lineno)
418
+ sexp = builder.parse
419
+ if builder.error?
420
+ if raise_errors
421
+ raise SyntaxError, builder.error
61
422
  end
423
+ else
424
+ sexp
62
425
  end
63
426
  end
64
427
 
428
+ autoload :SexpBuilder, "prism/translation/ripper/sexp"
429
+ autoload :SexpBuilderPP, "prism/translation/ripper/sexp"
430
+
65
431
  # The source that is being parsed.
66
432
  attr_reader :source
67
433
 
434
+ # The filename of the source being parsed.
435
+ attr_reader :filename
436
+
68
437
  # The current line number of the parser.
69
438
  attr_reader :lineno
70
439
 
@@ -72,16 +441,17 @@ module Prism
72
441
  attr_reader :column
73
442
 
74
443
  # Create a new Translation::Ripper object with the given source.
75
- def initialize(source)
444
+ def initialize(source, filename = "(ripper)", lineno = 1)
76
445
  @source = source
446
+ @filename = filename
447
+ @lineno = lineno
448
+ @column = 0
77
449
  @result = nil
78
- @lineno = nil
79
- @column = nil
80
450
  end
81
451
 
82
- ############################################################################
452
+ ##########################################################################
83
453
  # Public interface
84
- ############################################################################
454
+ ##########################################################################
85
455
 
86
456
  # True if the parser encountered an error during parsing.
87
457
  def error?
@@ -90,13 +460,80 @@ module Prism
90
460
 
91
461
  # Parse the source and return the result.
92
462
  def parse
463
+ result.comments.each do |comment|
464
+ location = comment.location
465
+ bounds(location)
466
+
467
+ if comment.is_a?(InlineComment)
468
+ on_comment(comment.slice)
469
+ else
470
+ offset = location.start_offset
471
+ lines = comment.slice.lines
472
+
473
+ lines.each_with_index do |line, index|
474
+ bounds(location.copy(start_offset: offset))
475
+
476
+ if index == 0
477
+ on_embdoc_beg(line)
478
+ elsif index == lines.size - 1
479
+ on_embdoc_end(line)
480
+ else
481
+ on_embdoc(line)
482
+ end
483
+
484
+ offset += line.bytesize
485
+ end
486
+ end
487
+ end
488
+
93
489
  result.magic_comments.each do |magic_comment|
94
490
  on_magic_comment(magic_comment.key, magic_comment.value)
95
491
  end
96
492
 
493
+ unless result.data_loc.nil?
494
+ on___end__(result.data_loc.slice.each_line.first)
495
+ end
496
+
497
+ result.warnings.each do |warning|
498
+ bounds(warning.location)
499
+
500
+ if warning.level == :default
501
+ warning(warning.message)
502
+ else
503
+ case warning.type
504
+ when :ambiguous_first_argument_plus
505
+ on_arg_ambiguous("+")
506
+ when :ambiguous_first_argument_minus
507
+ on_arg_ambiguous("-")
508
+ when :ambiguous_slash
509
+ on_arg_ambiguous("/")
510
+ else
511
+ warn(warning.message)
512
+ end
513
+ end
514
+ end
515
+
97
516
  if error?
98
517
  result.errors.each do |error|
99
- on_parse_error(error.message)
518
+ location = error.location
519
+ bounds(location)
520
+
521
+ case error.type
522
+ when :alias_argument
523
+ on_alias_error("can't make alias for the number variables", location.slice)
524
+ when :argument_formal_class
525
+ on_param_error("formal argument cannot be a class variable", location.slice)
526
+ when :argument_format_constant
527
+ on_param_error("formal argument cannot be a constant", location.slice)
528
+ when :argument_formal_global
529
+ on_param_error("formal argument cannot be a global variable", location.slice)
530
+ when :argument_formal_ivar
531
+ on_param_error("formal argument cannot be an instance variable", location.slice)
532
+ when :class_name, :module_name
533
+ on_class_name_error("class/module name must be CONSTANT", location.slice)
534
+ else
535
+ on_parse_error(error.message)
536
+ end
100
537
  end
101
538
 
102
539
  nil
@@ -105,556 +542,2829 @@ module Prism
105
542
  end
106
543
  end
107
544
 
108
- ############################################################################
545
+ ##########################################################################
109
546
  # Visitor methods
110
- ############################################################################
547
+ ##########################################################################
548
+
549
+ # alias foo bar
550
+ # ^^^^^^^^^^^^^
551
+ def visit_alias_method_node(node)
552
+ new_name = visit(node.new_name)
553
+ old_name = visit(node.old_name)
111
554
 
112
- # Visit an ArrayNode node.
113
- def visit_array_node(node)
114
- elements = visit_elements(node.elements) unless node.elements.empty?
115
555
  bounds(node.location)
116
- on_array(elements)
556
+ on_alias(new_name, old_name)
117
557
  end
118
558
 
119
- # Visit a CallNode node.
120
- # Ripper distinguishes between many different method-call
121
- # nodes -- unary and binary operators, "command" calls with
122
- # no parentheses, and call/fcall/vcall.
123
- def visit_call_node(node)
124
- return visit_aref_node(node) if node.name == :[]
125
- return visit_aref_field_node(node) if node.name == :[]=
126
-
127
- if node.variable_call?
128
- raise NotImplementedError unless node.receiver.nil?
129
-
130
- bounds(node.message_loc)
131
- return on_vcall(on_ident(node.message))
132
- end
559
+ # alias $foo $bar
560
+ # ^^^^^^^^^^^^^^^
561
+ def visit_alias_global_variable_node(node)
562
+ new_name = visit_alias_global_variable_node_value(node.new_name)
563
+ old_name = visit_alias_global_variable_node_value(node.old_name)
133
564
 
134
- if node.opening_loc.nil?
135
- return visit_no_paren_call(node)
136
- end
565
+ bounds(node.location)
566
+ on_var_alias(new_name, old_name)
567
+ end
137
568
 
138
- # A non-operator method call with parentheses
569
+ # Visit one side of an alias global variable node.
570
+ private def visit_alias_global_variable_node_value(node)
571
+ bounds(node.location)
139
572
 
140
- args = if node.arguments.nil?
141
- on_arg_paren(nil)
573
+ case node
574
+ when BackReferenceReadNode
575
+ on_backref(node.slice)
576
+ when GlobalVariableReadNode
577
+ on_gvar(node.name.to_s)
142
578
  else
143
- on_arg_paren(on_args_add_block(visit_elements(node.arguments.arguments), false))
579
+ raise
144
580
  end
581
+ end
145
582
 
146
- bounds(node.message_loc)
147
- ident_val = on_ident(node.message)
583
+ # foo => bar | baz
584
+ # ^^^^^^^^^
585
+ def visit_alternation_pattern_node(node)
586
+ left = visit_pattern_node(node.left)
587
+ right = visit_pattern_node(node.right)
148
588
 
149
589
  bounds(node.location)
150
- args_call_val = on_method_add_arg(on_fcall(ident_val), args)
151
- if node.block
152
- block_val = visit(node.block)
590
+ on_binary(left, :|, right)
591
+ end
153
592
 
154
- return on_method_add_block(args_call_val, block_val)
593
+ # Visit a pattern within a pattern match. This is used to bypass the
594
+ # parenthesis node that can be used to wrap patterns.
595
+ private def visit_pattern_node(node)
596
+ if node.is_a?(ParenthesesNode)
597
+ visit(node.body)
155
598
  else
156
- return args_call_val
599
+ visit(node)
157
600
  end
158
601
  end
159
602
 
160
- # Visit a LocalVariableWriteNode.
161
- def visit_local_variable_write_node(node)
162
- bounds(node.name_loc)
163
- ident_val = on_ident(node.name.to_s)
164
- on_assign(on_var_field(ident_val), visit(node.value))
165
- end
166
-
167
- # Visit a LocalVariableAndWriteNode.
168
- def visit_local_variable_and_write_node(node)
169
- visit_binary_op_assign(node)
170
- end
603
+ # a and b
604
+ # ^^^^^^^
605
+ def visit_and_node(node)
606
+ left = visit(node.left)
607
+ right = visit(node.right)
171
608
 
172
- # Visit a LocalVariableOrWriteNode.
173
- def visit_local_variable_or_write_node(node)
174
- visit_binary_op_assign(node)
609
+ bounds(node.location)
610
+ on_binary(left, node.operator.to_sym, right)
175
611
  end
176
612
 
177
- # Visit nodes for +=, *=, -=, etc., called LocalVariableOperatorWriteNodes.
178
- def visit_local_variable_operator_write_node(node)
179
- visit_binary_op_assign(node, operator: "#{node.operator}=")
180
- end
613
+ # []
614
+ # ^^
615
+ def visit_array_node(node)
616
+ case (opening = node.opening)
617
+ when /^%w/
618
+ opening_loc = node.opening_loc
619
+ bounds(opening_loc)
620
+ on_qwords_beg(opening)
181
621
 
182
- # Visit a LocalVariableReadNode.
183
- def visit_local_variable_read_node(node)
184
- bounds(node.location)
185
- ident_val = on_ident(node.slice)
622
+ elements = on_qwords_new
623
+ previous = nil
186
624
 
187
- on_var_ref(ident_val)
188
- end
625
+ node.elements.each do |element|
626
+ visit_words_sep(opening_loc, previous, element)
189
627
 
190
- # Visit a BlockNode.
191
- def visit_block_node(node)
192
- params_val = node.parameters.nil? ? nil : visit(node.parameters)
628
+ bounds(element.location)
629
+ elements = on_qwords_add(elements, on_tstring_content(element.content))
193
630
 
194
- body_val = node.body.nil? ? on_stmts_add(on_stmts_new, on_void_stmt) : visit(node.body)
631
+ previous = element
632
+ end
195
633
 
196
- on_brace_block(params_val, body_val)
197
- end
634
+ bounds(node.closing_loc)
635
+ on_tstring_end(node.closing)
636
+ when /^%i/
637
+ opening_loc = node.opening_loc
638
+ bounds(opening_loc)
639
+ on_qsymbols_beg(opening)
198
640
 
199
- # Visit a BlockParametersNode.
200
- def visit_block_parameters_node(node)
201
- on_block_var(visit(node.parameters), no_block_value)
202
- end
641
+ elements = on_qsymbols_new
642
+ previous = nil
203
643
 
204
- # Visit a ParametersNode.
205
- # This will require expanding as we support more kinds of parameters.
206
- def visit_parameters_node(node)
207
- #on_params(required, optional, nil, nil, nil, nil, nil)
208
- on_params(visit_all(node.requireds), nil, nil, nil, nil, nil, nil)
209
- end
644
+ node.elements.each do |element|
645
+ visit_words_sep(opening_loc, previous, element)
210
646
 
211
- # Visit a RequiredParameterNode.
212
- def visit_required_parameter_node(node)
213
- bounds(node.location)
214
- on_ident(node.name.to_s)
215
- end
647
+ bounds(element.location)
648
+ elements = on_qsymbols_add(elements, on_tstring_content(element.value))
216
649
 
217
- # Visit a BreakNode.
218
- def visit_break_node(node)
219
- return on_break(on_args_new) if node.arguments.nil?
650
+ previous = element
651
+ end
220
652
 
221
- args_val = visit_elements(node.arguments.arguments)
222
- on_break(on_args_add_block(args_val, false))
223
- end
653
+ bounds(node.closing_loc)
654
+ on_tstring_end(node.closing)
655
+ when /^%W/
656
+ opening_loc = node.opening_loc
657
+ bounds(opening_loc)
658
+ on_words_beg(opening)
659
+
660
+ elements = on_words_new
661
+ previous = nil
662
+
663
+ node.elements.each do |element|
664
+ visit_words_sep(opening_loc, previous, element)
665
+
666
+ bounds(element.location)
667
+ elements =
668
+ on_words_add(
669
+ elements,
670
+ if element.is_a?(StringNode)
671
+ on_word_add(on_word_new, on_tstring_content(element.content))
672
+ else
673
+ element.parts.inject(on_word_new) do |word, part|
674
+ word_part =
675
+ if part.is_a?(StringNode)
676
+ bounds(part.location)
677
+ on_tstring_content(part.content)
678
+ else
679
+ visit(part)
680
+ end
681
+
682
+ on_word_add(word, word_part)
683
+ end
684
+ end
685
+ )
686
+
687
+ previous = element
688
+ end
224
689
 
225
- # Visit an AliasMethodNode.
226
- def visit_alias_method_node(node)
227
- # For both the old and new name, if there is a colon in the symbol
228
- # name (e.g. 'alias :foo :bar') then we do *not* emit the [:symbol] wrapper around
229
- # the lexer token (e.g. :@ident) inside [:symbol_literal]. But if there
230
- # is no colon (e.g. 'alias foo bar') then we *do* still emit the [:symbol] wrapper.
690
+ bounds(node.closing_loc)
691
+ on_tstring_end(node.closing)
692
+ when /^%I/
693
+ opening_loc = node.opening_loc
694
+ bounds(opening_loc)
695
+ on_symbols_beg(opening)
696
+
697
+ elements = on_symbols_new
698
+ previous = nil
699
+
700
+ node.elements.each do |element|
701
+ visit_words_sep(opening_loc, previous, element)
702
+
703
+ bounds(element.location)
704
+ elements =
705
+ on_symbols_add(
706
+ elements,
707
+ if element.is_a?(SymbolNode)
708
+ on_word_add(on_word_new, on_tstring_content(element.value))
709
+ else
710
+ element.parts.inject(on_word_new) do |word, part|
711
+ word_part =
712
+ if part.is_a?(StringNode)
713
+ bounds(part.location)
714
+ on_tstring_content(part.content)
715
+ else
716
+ visit(part)
717
+ end
718
+
719
+ on_word_add(word, word_part)
720
+ end
721
+ end
722
+ )
723
+
724
+ previous = element
725
+ end
231
726
 
232
- if node.new_name.is_a?(SymbolNode) && !node.new_name.opening
233
- new_name_val = visit_symbol_literal_node(node.new_name, no_symbol_wrapper: true)
234
- else
235
- new_name_val = visit(node.new_name)
236
- end
237
- if node.old_name.is_a?(SymbolNode) && !node.old_name.opening
238
- old_name_val = visit_symbol_literal_node(node.old_name, no_symbol_wrapper: true)
727
+ bounds(node.closing_loc)
728
+ on_tstring_end(node.closing)
239
729
  else
240
- old_name_val = visit(node.old_name)
730
+ bounds(node.opening_loc)
731
+ on_lbracket(opening)
732
+
733
+ elements = visit_arguments(node.elements) unless node.elements.empty?
734
+
735
+ bounds(node.closing_loc)
736
+ on_rbracket(node.closing)
241
737
  end
242
738
 
243
- on_alias(new_name_val, old_name_val)
739
+ bounds(node.location)
740
+ on_array(elements)
244
741
  end
245
742
 
246
- # Visit an AliasGlobalVariableNode.
247
- def visit_alias_global_variable_node(node)
248
- on_var_alias(visit(node.new_name), visit(node.old_name))
743
+ # Dispatch a words_sep event that contains the space between the elements
744
+ # of list literals.
745
+ private def visit_words_sep(opening_loc, previous, current)
746
+ end_offset = (previous.nil? ? opening_loc : previous.location).end_offset
747
+ start_offset = current.location.start_offset
748
+
749
+ if end_offset != start_offset
750
+ bounds(current.location.copy(start_offset: end_offset))
751
+ on_words_sep(source.byteslice(end_offset...start_offset))
752
+ end
249
753
  end
250
754
 
251
- # Visit a GlobalVariableReadNode.
252
- def visit_global_variable_read_node(node)
253
- bounds(node.location)
254
- on_gvar(node.name.to_s)
755
+ # Visit a list of elements, like the elements of an array or arguments.
756
+ private def visit_arguments(elements)
757
+ bounds(elements.first.location)
758
+ elements.inject(on_args_new) do |args, element|
759
+ arg = visit(element)
760
+ bounds(element.location)
761
+
762
+ case element
763
+ when BlockArgumentNode
764
+ on_args_add_block(args, arg)
765
+ when SplatNode
766
+ on_args_add_star(args, arg)
767
+ else
768
+ on_args_add(args, arg)
769
+ end
770
+ end
255
771
  end
256
772
 
257
- # Visit a BackReferenceReadNode.
258
- def visit_back_reference_read_node(node)
773
+ # foo => [bar]
774
+ # ^^^^^
775
+ def visit_array_pattern_node(node)
776
+ constant = visit(node.constant)
777
+ requireds = visit_all(node.requireds) if node.requireds.any?
778
+ rest =
779
+ if (rest_node = node.rest).is_a?(SplatNode)
780
+ if rest_node.expression.nil?
781
+ bounds(rest_node.location)
782
+ on_var_field(nil)
783
+ else
784
+ visit(rest_node.expression)
785
+ end
786
+ end
787
+
788
+ posts = visit_all(node.posts) if node.posts.any?
789
+
259
790
  bounds(node.location)
260
- on_backref(node.name.to_s)
791
+ on_aryptn(constant, requireds, rest, posts)
261
792
  end
262
793
 
263
- # Visit an AndNode.
264
- def visit_and_node(node)
265
- visit_binary_operator(node)
794
+ # foo(bar)
795
+ # ^^^
796
+ def visit_arguments_node(node)
797
+ arguments, _ = visit_call_node_arguments(node, nil, false)
798
+ arguments
266
799
  end
267
800
 
268
- # Visit an OrNode.
269
- def visit_or_node(node)
270
- visit_binary_operator(node)
271
- end
801
+ # { a: 1 }
802
+ # ^^^^
803
+ def visit_assoc_node(node)
804
+ key = visit(node.key)
805
+ value = visit(node.value)
272
806
 
273
- # Visit a TrueNode.
274
- def visit_true_node(node)
275
807
  bounds(node.location)
276
- on_var_ref(on_kw("true"))
808
+ on_assoc_new(key, value)
277
809
  end
278
810
 
279
- # Visit a FalseNode.
280
- def visit_false_node(node)
811
+ # def foo(**); bar(**); end
812
+ # ^^
813
+ #
814
+ # { **foo }
815
+ # ^^^^^
816
+ def visit_assoc_splat_node(node)
817
+ value = visit(node.value)
818
+
281
819
  bounds(node.location)
282
- on_var_ref(on_kw("false"))
820
+ on_assoc_splat(value)
283
821
  end
284
822
 
285
- # Visit a FloatNode node.
286
- def visit_float_node(node)
287
- visit_number(node) { |text| on_float(text) }
823
+ # $+
824
+ # ^^
825
+ def visit_back_reference_read_node(node)
826
+ bounds(node.location)
827
+ on_backref(node.slice)
288
828
  end
289
829
 
290
- # Visit a ImaginaryNode node.
291
- def visit_imaginary_node(node)
292
- visit_number(node) { |text| on_imaginary(text) }
293
- end
830
+ # begin end
831
+ # ^^^^^^^^^
832
+ def visit_begin_node(node)
833
+ clauses = visit_begin_node_clauses(node.begin_keyword_loc, node, false)
294
834
 
295
- # Visit an IntegerNode node.
296
- def visit_integer_node(node)
297
- visit_number(node) { |text| on_int(text) }
835
+ bounds(node.location)
836
+ on_begin(clauses)
298
837
  end
299
838
 
300
- # Visit a ParenthesesNode node.
301
- def visit_parentheses_node(node)
302
- body =
303
- if node.body.nil?
839
+ # Visit the clauses of a begin node to form an on_bodystmt call.
840
+ private def visit_begin_node_clauses(location, node, allow_newline)
841
+ statements =
842
+ if node.statements.nil?
304
843
  on_stmts_add(on_stmts_new, on_void_stmt)
305
844
  else
306
- visit(node.body)
845
+ body = node.statements.body
846
+ body.unshift(nil) if void_stmt?(location, node.statements.body[0].location, allow_newline)
847
+
848
+ bounds(node.statements.location)
849
+ visit_statements_node_body(body)
307
850
  end
308
851
 
852
+ rescue_clause = visit(node.rescue_clause)
853
+ else_clause =
854
+ unless (else_clause_node = node.else_clause).nil?
855
+ else_statements =
856
+ if else_clause_node.statements.nil?
857
+ [nil]
858
+ else
859
+ body = else_clause_node.statements.body
860
+ body.unshift(nil) if void_stmt?(else_clause_node.else_keyword_loc, else_clause_node.statements.body[0].location, allow_newline)
861
+ body
862
+ end
863
+
864
+ bounds(else_clause_node.location)
865
+ visit_statements_node_body(else_statements)
866
+ end
867
+ ensure_clause = visit(node.ensure_clause)
868
+
309
869
  bounds(node.location)
310
- on_paren(body)
870
+ on_bodystmt(statements, rescue_clause, else_clause, ensure_clause)
311
871
  end
312
872
 
313
- # Visit a BeginNode node.
314
- # This is not at all bulletproof against different structures of begin/rescue/else/ensure/end.
315
- def visit_begin_node(node)
316
- rescue_val = node.rescue_clause ? on_rescue(nil, nil, visit(node.rescue_clause), nil) : nil
317
- ensure_val = node.ensure_clause ? on_ensure(visit(node.ensure_clause.statements)) : nil
318
- on_begin(on_bodystmt(visit(node.statements), rescue_val, nil, ensure_val))
873
+ # Visit the body of a structure that can have either a set of statements
874
+ # or statements wrapped in rescue/else/ensure.
875
+ private def visit_body_node(location, node, allow_newline = false)
876
+ case node
877
+ when nil
878
+ bounds(location)
879
+ on_bodystmt(visit_statements_node_body([nil]), nil, nil, nil)
880
+ when StatementsNode
881
+ body = [*node.body]
882
+ body.unshift(nil) if void_stmt?(location, body[0].location, allow_newline)
883
+ stmts = visit_statements_node_body(body)
884
+
885
+ bounds(node.body.first.location)
886
+ on_bodystmt(stmts, nil, nil, nil)
887
+ when BeginNode
888
+ visit_begin_node_clauses(location, node, allow_newline)
889
+ else
890
+ raise
891
+ end
319
892
  end
320
893
 
321
- # Visit a RescueNode node.
322
- def visit_rescue_node(node)
323
- visit(node.statements)
894
+ # foo(&bar)
895
+ # ^^^^
896
+ def visit_block_argument_node(node)
897
+ visit(node.expression)
324
898
  end
325
899
 
326
- # Visit a ProgramNode node.
327
- def visit_program_node(node)
328
- statements = visit(node.statements)
900
+ # foo { |; bar| }
901
+ # ^^^
902
+ def visit_block_local_variable_node(node)
329
903
  bounds(node.location)
330
- on_program(statements)
904
+ on_ident(node.name.to_s)
331
905
  end
332
906
 
333
- # Visit a RangeNode node.
334
- def visit_range_node(node)
335
- left = visit(node.left)
336
- right = visit(node.right)
907
+ # Visit a BlockNode.
908
+ def visit_block_node(node)
909
+ braces = node.opening == "{"
910
+ parameters = visit(node.parameters)
337
911
 
338
- bounds(node.location)
339
- if node.exclude_end?
340
- on_dot3(left, right)
912
+ body =
913
+ case node.body
914
+ when nil
915
+ bounds(node.location)
916
+ stmts = on_stmts_add(on_stmts_new, on_void_stmt)
917
+
918
+ bounds(node.location)
919
+ braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
920
+ when StatementsNode
921
+ stmts = node.body.body
922
+ stmts.unshift(nil) if void_stmt?(node.parameters&.location || node.opening_loc, node.body.location, false)
923
+ stmts = visit_statements_node_body(stmts)
924
+
925
+ bounds(node.body.location)
926
+ braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
927
+ when BeginNode
928
+ visit_body_node(node.parameters&.location || node.opening_loc, node.body)
929
+ else
930
+ raise
931
+ end
932
+
933
+ if braces
934
+ bounds(node.location)
935
+ on_brace_block(parameters, body)
341
936
  else
342
- on_dot2(left, right)
937
+ bounds(node.location)
938
+ on_do_block(parameters, body)
343
939
  end
344
940
  end
345
941
 
346
- # Visit a RationalNode node.
347
- def visit_rational_node(node)
348
- visit_number(node) { |text| on_rational(text) }
349
- end
350
-
351
- # Visit a StringNode node.
352
- def visit_string_node(node)
353
- bounds(node.content_loc)
354
- tstring_val = on_tstring_content(node.unescaped.to_s)
355
- on_string_literal(on_string_add(on_string_content, tstring_val))
356
- end
357
-
358
- # Visit an XStringNode node.
359
- def visit_x_string_node(node)
360
- bounds(node.content_loc)
361
- tstring_val = on_tstring_content(node.unescaped.to_s)
362
- on_xstring_literal(on_xstring_add(on_xstring_new, tstring_val))
363
- end
364
-
365
- # Visit an InterpolatedStringNode node.
366
- def visit_interpolated_string_node(node)
367
- on_string_literal(visit_enumerated_node(node))
368
- end
942
+ # def foo(&bar); end
943
+ # ^^^^
944
+ def visit_block_parameter_node(node)
945
+ if node.name_loc.nil?
946
+ bounds(node.location)
947
+ on_blockarg(nil)
948
+ else
949
+ bounds(node.name_loc)
950
+ name = visit_token(node.name.to_s)
369
951
 
370
- # Visit an EmbeddedStatementsNode node.
371
- def visit_embedded_statements_node(node)
372
- visit(node.statements)
952
+ bounds(node.location)
953
+ on_blockarg(name)
954
+ end
373
955
  end
374
956
 
375
- # Visit a SymbolNode node.
376
- def visit_symbol_node(node)
377
- visit_symbol_literal_node(node)
378
- end
957
+ # A block's parameters.
958
+ def visit_block_parameters_node(node)
959
+ parameters =
960
+ if node.parameters.nil?
961
+ on_params(nil, nil, nil, nil, nil, nil, nil)
962
+ else
963
+ visit(node.parameters)
964
+ end
379
965
 
380
- # Visit an InterpolatedSymbolNode node.
381
- def visit_interpolated_symbol_node(node)
382
- on_dyna_symbol(visit_enumerated_node(node))
383
- end
966
+ locals =
967
+ if node.locals.any?
968
+ visit_all(node.locals)
969
+ else
970
+ false
971
+ end
384
972
 
385
- # Visit a StatementsNode node.
386
- def visit_statements_node(node)
387
973
  bounds(node.location)
388
- node.body.inject(on_stmts_new) do |stmts, stmt|
389
- on_stmts_add(stmts, visit(stmt))
390
- end
974
+ on_block_var(parameters, locals)
391
975
  end
392
976
 
393
- ############################################################################
394
- # Entrypoints for subclasses
395
- ############################################################################
977
+ # break
978
+ # ^^^^^
979
+ #
980
+ # break foo
981
+ # ^^^^^^^^^
982
+ def visit_break_node(node)
983
+ if node.arguments.nil?
984
+ bounds(node.location)
985
+ on_break(on_args_new)
986
+ else
987
+ arguments = visit(node.arguments)
396
988
 
397
- # This is a convenience method that runs the SexpBuilder subclass parser.
398
- def self.sexp_raw(source)
399
- SexpBuilder.new(source).parse
989
+ bounds(node.location)
990
+ on_break(arguments)
991
+ end
400
992
  end
401
993
 
402
- # This is a convenience method that runs the SexpBuilderPP subclass parser.
403
- def self.sexp(source)
404
- SexpBuilderPP.new(source).parse
405
- end
994
+ # foo
995
+ # ^^^
996
+ #
997
+ # foo.bar
998
+ # ^^^^^^^
999
+ #
1000
+ # foo.bar() {}
1001
+ # ^^^^^^^^^^^^
1002
+ def visit_call_node(node)
1003
+ if node.call_operator_loc.nil?
1004
+ case node.name
1005
+ when :[]
1006
+ receiver = visit(node.receiver)
1007
+ arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
406
1008
 
407
- private
1009
+ bounds(node.location)
1010
+ call = on_aref(receiver, arguments)
408
1011
 
409
- # Generate Ripper events for a CallNode with no opening_loc
410
- def visit_no_paren_call(node)
411
- # No opening_loc can mean an operator. It can also mean a
412
- # method call with no parentheses.
413
- if node.message.match?(/^[[:punct:]]/)
414
- left = visit(node.receiver)
415
- if node.arguments&.arguments&.length == 1
416
- right = visit(node.arguments.arguments.first)
417
-
418
- return on_binary(left, node.name, right)
419
- elsif !node.arguments || node.arguments.empty?
420
- return on_unary(node.name, left)
1012
+ if block.nil?
1013
+ call
1014
+ else
1015
+ bounds(node.location)
1016
+ on_method_add_block(call, block)
1017
+ end
1018
+ when :[]=
1019
+ receiver = visit(node.receiver)
1020
+
1021
+ *arguments, last_argument = node.arguments.arguments
1022
+ arguments << node.block if !node.block.nil?
1023
+
1024
+ arguments =
1025
+ if arguments.any?
1026
+ args = visit_arguments(arguments)
1027
+
1028
+ if !node.block.nil?
1029
+ args
1030
+ else
1031
+ bounds(arguments.first.location)
1032
+ on_args_add_block(args, false)
1033
+ end
1034
+ end
1035
+
1036
+ bounds(node.location)
1037
+ call = on_aref_field(receiver, arguments)
1038
+ value = visit_write_value(last_argument)
1039
+
1040
+ bounds(last_argument.location)
1041
+ on_assign(call, value)
1042
+ when :-@, :+@, :~
1043
+ receiver = visit(node.receiver)
1044
+
1045
+ bounds(node.location)
1046
+ on_unary(node.name, receiver)
1047
+ when :!
1048
+ receiver = visit(node.receiver)
1049
+
1050
+ bounds(node.location)
1051
+ on_unary(node.message == "not" ? :not : :!, receiver)
1052
+ when *BINARY_OPERATORS
1053
+ receiver = visit(node.receiver)
1054
+ value = visit(node.arguments.arguments.first)
1055
+
1056
+ bounds(node.location)
1057
+ on_binary(receiver, node.name, value)
421
1058
  else
422
- raise NotImplementedError, "More than two arguments for operator"
423
- end
424
- elsif node.call_operator_loc.nil?
425
- # In Ripper a method call like "puts myvar" with no parentheses is a "command".
426
- bounds(node.message_loc)
427
- ident_val = on_ident(node.message)
1059
+ bounds(node.message_loc)
1060
+ message = visit_token(node.message, false)
428
1061
 
429
- # Unless it has a block, and then it's an fcall (e.g. "foo { bar }")
430
- if node.block
431
- block_val = visit(node.block)
432
- # In these calls, even if node.arguments is nil, we still get an :args_new call.
433
- args = if node.arguments.nil?
434
- on_args_new
1062
+ if node.variable_call?
1063
+ on_vcall(message)
435
1064
  else
436
- on_args_add_block(visit_elements(node.arguments.arguments))
1065
+ arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1066
+ call =
1067
+ if node.opening_loc.nil? && arguments&.any?
1068
+ bounds(node.location)
1069
+ on_command(message, arguments)
1070
+ elsif !node.opening_loc.nil?
1071
+ bounds(node.location)
1072
+ on_method_add_arg(on_fcall(message), on_arg_paren(arguments))
1073
+ else
1074
+ bounds(node.location)
1075
+ on_method_add_arg(on_fcall(message), on_args_new)
1076
+ end
1077
+
1078
+ if block.nil?
1079
+ call
1080
+ else
1081
+ bounds(node.block.location)
1082
+ on_method_add_block(call, block)
1083
+ end
1084
+ end
1085
+ end
1086
+ else
1087
+ receiver = visit(node.receiver)
1088
+
1089
+ bounds(node.call_operator_loc)
1090
+ call_operator = visit_token(node.call_operator)
1091
+
1092
+ message =
1093
+ if node.message_loc.nil?
1094
+ :call
1095
+ else
1096
+ bounds(node.message_loc)
1097
+ visit_token(node.message, false)
1098
+ end
1099
+
1100
+ if node.name.end_with?("=") && !node.message.end_with?("=") && !node.arguments.nil? && node.block.nil?
1101
+ value = visit_write_value(node.arguments.arguments.first)
1102
+
1103
+ bounds(node.location)
1104
+ on_assign(on_field(receiver, call_operator, message), value)
1105
+ else
1106
+ arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1107
+ call =
1108
+ if node.opening_loc.nil?
1109
+ bounds(node.location)
1110
+
1111
+ if node.arguments.nil? && !node.block.is_a?(BlockArgumentNode)
1112
+ on_call(receiver, call_operator, message)
1113
+ else
1114
+ on_command_call(receiver, call_operator, message, arguments)
1115
+ end
1116
+ else
1117
+ bounds(node.opening_loc)
1118
+ arguments = on_arg_paren(arguments)
1119
+
1120
+ bounds(node.location)
1121
+ on_method_add_arg(on_call(receiver, call_operator, message), arguments)
1122
+ end
1123
+
1124
+ if block.nil?
1125
+ call
1126
+ else
1127
+ bounds(node.block.location)
1128
+ on_method_add_block(call, block)
1129
+ end
1130
+ end
1131
+ end
1132
+ end
1133
+
1134
+ # Visit the arguments and block of a call node and return the arguments
1135
+ # and block as they should be used.
1136
+ private def visit_call_node_arguments(arguments_node, block_node, trailing_comma)
1137
+ arguments = arguments_node&.arguments || []
1138
+ block = block_node
1139
+
1140
+ if block.is_a?(BlockArgumentNode)
1141
+ arguments << block
1142
+ block = nil
1143
+ end
1144
+
1145
+ [
1146
+ if arguments.length == 1 && arguments.first.is_a?(ForwardingArgumentsNode)
1147
+ visit(arguments.first)
1148
+ elsif arguments.any?
1149
+ args = visit_arguments(arguments)
1150
+
1151
+ if block_node.is_a?(BlockArgumentNode) || arguments.last.is_a?(ForwardingArgumentsNode) || command?(arguments.last) || trailing_comma
1152
+ args
1153
+ else
1154
+ bounds(arguments.first.location)
1155
+ on_args_add_block(args, false)
1156
+ end
1157
+ end,
1158
+ visit(block)
1159
+ ]
1160
+ end
1161
+
1162
+ # Returns true if the given node is a command node.
1163
+ private def command?(node)
1164
+ node.is_a?(CallNode) &&
1165
+ node.opening_loc.nil? &&
1166
+ (!node.arguments.nil? || node.block.is_a?(BlockArgumentNode)) &&
1167
+ !BINARY_OPERATORS.include?(node.name)
1168
+ end
1169
+
1170
+ # foo.bar += baz
1171
+ # ^^^^^^^^^^^^^^^
1172
+ def visit_call_operator_write_node(node)
1173
+ receiver = visit(node.receiver)
1174
+
1175
+ bounds(node.call_operator_loc)
1176
+ call_operator = visit_token(node.call_operator)
1177
+
1178
+ bounds(node.message_loc)
1179
+ message = visit_token(node.message)
1180
+
1181
+ bounds(node.location)
1182
+ target = on_field(receiver, call_operator, message)
1183
+
1184
+ bounds(node.operator_loc)
1185
+ operator = on_op("#{node.operator}=")
1186
+ value = visit_write_value(node.value)
1187
+
1188
+ bounds(node.location)
1189
+ on_opassign(target, operator, value)
1190
+ end
1191
+
1192
+ # foo.bar &&= baz
1193
+ # ^^^^^^^^^^^^^^^
1194
+ def visit_call_and_write_node(node)
1195
+ receiver = visit(node.receiver)
1196
+
1197
+ bounds(node.call_operator_loc)
1198
+ call_operator = visit_token(node.call_operator)
1199
+
1200
+ bounds(node.message_loc)
1201
+ message = visit_token(node.message)
1202
+
1203
+ bounds(node.location)
1204
+ target = on_field(receiver, call_operator, message)
1205
+
1206
+ bounds(node.operator_loc)
1207
+ operator = on_op("&&=")
1208
+ value = visit_write_value(node.value)
1209
+
1210
+ bounds(node.location)
1211
+ on_opassign(target, operator, value)
1212
+ end
1213
+
1214
+ # foo.bar ||= baz
1215
+ # ^^^^^^^^^^^^^^^
1216
+ def visit_call_or_write_node(node)
1217
+ receiver = visit(node.receiver)
1218
+
1219
+ bounds(node.call_operator_loc)
1220
+ call_operator = visit_token(node.call_operator)
1221
+
1222
+ bounds(node.message_loc)
1223
+ message = visit_token(node.message)
1224
+
1225
+ bounds(node.location)
1226
+ target = on_field(receiver, call_operator, message)
1227
+
1228
+ bounds(node.operator_loc)
1229
+ operator = on_op("||=")
1230
+ value = visit_write_value(node.value)
1231
+
1232
+ bounds(node.location)
1233
+ on_opassign(target, operator, value)
1234
+ end
1235
+
1236
+ # foo.bar, = 1
1237
+ # ^^^^^^^
1238
+ def visit_call_target_node(node)
1239
+ if node.call_operator == "::"
1240
+ receiver = visit(node.receiver)
1241
+
1242
+ bounds(node.message_loc)
1243
+ message = visit_token(node.message)
1244
+
1245
+ bounds(node.location)
1246
+ on_const_path_field(receiver, message)
1247
+ else
1248
+ receiver = visit(node.receiver)
1249
+
1250
+ bounds(node.call_operator_loc)
1251
+ call_operator = visit_token(node.call_operator)
1252
+
1253
+ bounds(node.message_loc)
1254
+ message = visit_token(node.message)
1255
+
1256
+ bounds(node.location)
1257
+ on_field(receiver, call_operator, message)
1258
+ end
1259
+ end
1260
+
1261
+ # foo => bar => baz
1262
+ # ^^^^^^^^^^
1263
+ def visit_capture_pattern_node(node)
1264
+ value = visit(node.value)
1265
+ target = visit(node.target)
1266
+
1267
+ bounds(node.location)
1268
+ on_binary(value, :"=>", target)
1269
+ end
1270
+
1271
+ # case foo; when bar; end
1272
+ # ^^^^^^^^^^^^^^^^^^^^^^^
1273
+ def visit_case_node(node)
1274
+ predicate = visit(node.predicate)
1275
+ clauses =
1276
+ node.conditions.reverse_each.inject(visit(node.consequent)) do |consequent, condition|
1277
+ on_when(*visit(condition), consequent)
1278
+ end
1279
+
1280
+ bounds(node.location)
1281
+ on_case(predicate, clauses)
1282
+ end
1283
+
1284
+ # case foo; in bar; end
1285
+ # ^^^^^^^^^^^^^^^^^^^^^
1286
+ def visit_case_match_node(node)
1287
+ predicate = visit(node.predicate)
1288
+ clauses =
1289
+ node.conditions.reverse_each.inject(visit(node.consequent)) do |consequent, condition|
1290
+ on_in(*visit(condition), consequent)
1291
+ end
1292
+
1293
+ bounds(node.location)
1294
+ on_case(predicate, clauses)
1295
+ end
1296
+
1297
+ # class Foo; end
1298
+ # ^^^^^^^^^^^^^^
1299
+ def visit_class_node(node)
1300
+ constant_path =
1301
+ if node.constant_path.is_a?(ConstantReadNode)
1302
+ bounds(node.constant_path.location)
1303
+ on_const_ref(on_const(node.constant_path.name.to_s))
1304
+ else
1305
+ visit(node.constant_path)
1306
+ end
1307
+
1308
+ superclass = visit(node.superclass)
1309
+ bodystmt = visit_body_node(node.superclass&.location || node.constant_path.location, node.body, node.superclass.nil?)
1310
+
1311
+ bounds(node.location)
1312
+ on_class(constant_path, superclass, bodystmt)
1313
+ end
1314
+
1315
+ # @@foo
1316
+ # ^^^^^
1317
+ def visit_class_variable_read_node(node)
1318
+ bounds(node.location)
1319
+ on_var_ref(on_cvar(node.slice))
1320
+ end
1321
+
1322
+ # @@foo = 1
1323
+ # ^^^^^^^^^
1324
+ #
1325
+ # @@foo, @@bar = 1
1326
+ # ^^^^^ ^^^^^
1327
+ def visit_class_variable_write_node(node)
1328
+ bounds(node.name_loc)
1329
+ target = on_var_field(on_cvar(node.name.to_s))
1330
+ value = visit_write_value(node.value)
1331
+
1332
+ bounds(node.location)
1333
+ on_assign(target, value)
1334
+ end
1335
+
1336
+ # @@foo += bar
1337
+ # ^^^^^^^^^^^^
1338
+ def visit_class_variable_operator_write_node(node)
1339
+ bounds(node.name_loc)
1340
+ target = on_var_field(on_cvar(node.name.to_s))
1341
+
1342
+ bounds(node.operator_loc)
1343
+ operator = on_op("#{node.operator}=")
1344
+ value = visit_write_value(node.value)
1345
+
1346
+ bounds(node.location)
1347
+ on_opassign(target, operator, value)
1348
+ end
1349
+
1350
+ # @@foo &&= bar
1351
+ # ^^^^^^^^^^^^^
1352
+ def visit_class_variable_and_write_node(node)
1353
+ bounds(node.name_loc)
1354
+ target = on_var_field(on_cvar(node.name.to_s))
1355
+
1356
+ bounds(node.operator_loc)
1357
+ operator = on_op("&&=")
1358
+ value = visit_write_value(node.value)
1359
+
1360
+ bounds(node.location)
1361
+ on_opassign(target, operator, value)
1362
+ end
1363
+
1364
+ # @@foo ||= bar
1365
+ # ^^^^^^^^^^^^^
1366
+ def visit_class_variable_or_write_node(node)
1367
+ bounds(node.name_loc)
1368
+ target = on_var_field(on_cvar(node.name.to_s))
1369
+
1370
+ bounds(node.operator_loc)
1371
+ operator = on_op("||=")
1372
+ value = visit_write_value(node.value)
1373
+
1374
+ bounds(node.location)
1375
+ on_opassign(target, operator, value)
1376
+ end
1377
+
1378
+ # @@foo, = bar
1379
+ # ^^^^^
1380
+ def visit_class_variable_target_node(node)
1381
+ bounds(node.location)
1382
+ on_var_field(on_cvar(node.name.to_s))
1383
+ end
1384
+
1385
+ # Foo
1386
+ # ^^^
1387
+ def visit_constant_read_node(node)
1388
+ bounds(node.location)
1389
+ on_var_ref(on_const(node.name.to_s))
1390
+ end
1391
+
1392
+ # Foo = 1
1393
+ # ^^^^^^^
1394
+ #
1395
+ # Foo, Bar = 1
1396
+ # ^^^ ^^^
1397
+ def visit_constant_write_node(node)
1398
+ bounds(node.name_loc)
1399
+ target = on_var_field(on_const(node.name.to_s))
1400
+ value = visit_write_value(node.value)
1401
+
1402
+ bounds(node.location)
1403
+ on_assign(target, value)
1404
+ end
1405
+
1406
+ # Foo += bar
1407
+ # ^^^^^^^^^^^
1408
+ def visit_constant_operator_write_node(node)
1409
+ bounds(node.name_loc)
1410
+ target = on_var_field(on_const(node.name.to_s))
1411
+
1412
+ bounds(node.operator_loc)
1413
+ operator = on_op("#{node.operator}=")
1414
+ value = visit_write_value(node.value)
1415
+
1416
+ bounds(node.location)
1417
+ on_opassign(target, operator, value)
1418
+ end
1419
+
1420
+ # Foo &&= bar
1421
+ # ^^^^^^^^^^^^
1422
+ def visit_constant_and_write_node(node)
1423
+ bounds(node.name_loc)
1424
+ target = on_var_field(on_const(node.name.to_s))
1425
+
1426
+ bounds(node.operator_loc)
1427
+ operator = on_op("&&=")
1428
+ value = visit_write_value(node.value)
1429
+
1430
+ bounds(node.location)
1431
+ on_opassign(target, operator, value)
1432
+ end
1433
+
1434
+ # Foo ||= bar
1435
+ # ^^^^^^^^^^^^
1436
+ def visit_constant_or_write_node(node)
1437
+ bounds(node.name_loc)
1438
+ target = on_var_field(on_const(node.name.to_s))
1439
+
1440
+ bounds(node.operator_loc)
1441
+ operator = on_op("||=")
1442
+ value = visit_write_value(node.value)
1443
+
1444
+ bounds(node.location)
1445
+ on_opassign(target, operator, value)
1446
+ end
1447
+
1448
+ # Foo, = bar
1449
+ # ^^^
1450
+ def visit_constant_target_node(node)
1451
+ bounds(node.location)
1452
+ on_var_field(on_const(node.name.to_s))
1453
+ end
1454
+
1455
+ # Foo::Bar
1456
+ # ^^^^^^^^
1457
+ def visit_constant_path_node(node)
1458
+ if node.parent.nil?
1459
+ bounds(node.child.location)
1460
+ child = on_const(node.child.name.to_s)
1461
+
1462
+ bounds(node.location)
1463
+ on_top_const_ref(child)
1464
+ else
1465
+ parent = visit(node.parent)
1466
+
1467
+ bounds(node.child.location)
1468
+ child = on_const(node.child.name.to_s)
1469
+
1470
+ bounds(node.location)
1471
+ on_const_path_ref(parent, child)
1472
+ end
1473
+ end
1474
+
1475
+ # Foo::Bar = 1
1476
+ # ^^^^^^^^^^^^
1477
+ #
1478
+ # Foo::Foo, Bar::Bar = 1
1479
+ # ^^^^^^^^ ^^^^^^^^
1480
+ def visit_constant_path_write_node(node)
1481
+ target = visit_constant_path_write_node_target(node.target)
1482
+ value = visit_write_value(node.value)
1483
+
1484
+ bounds(node.location)
1485
+ on_assign(target, value)
1486
+ end
1487
+
1488
+ # Visit a constant path that is part of a write node.
1489
+ private def visit_constant_path_write_node_target(node)
1490
+ if node.parent.nil?
1491
+ bounds(node.child.location)
1492
+ child = on_const(node.child.name.to_s)
1493
+
1494
+ bounds(node.location)
1495
+ on_top_const_field(child)
1496
+ else
1497
+ parent = visit(node.parent)
1498
+
1499
+ bounds(node.child.location)
1500
+ child = on_const(node.child.name.to_s)
1501
+
1502
+ bounds(node.location)
1503
+ on_const_path_field(parent, child)
1504
+ end
1505
+ end
1506
+
1507
+ # Foo::Bar += baz
1508
+ # ^^^^^^^^^^^^^^^
1509
+ def visit_constant_path_operator_write_node(node)
1510
+ target = visit_constant_path_write_node_target(node.target)
1511
+ value = visit(node.value)
1512
+
1513
+ bounds(node.operator_loc)
1514
+ operator = on_op("#{node.operator}=")
1515
+ value = visit_write_value(node.value)
1516
+
1517
+ bounds(node.location)
1518
+ on_opassign(target, operator, value)
1519
+ end
1520
+
1521
+ # Foo::Bar &&= baz
1522
+ # ^^^^^^^^^^^^^^^^
1523
+ def visit_constant_path_and_write_node(node)
1524
+ target = visit_constant_path_write_node_target(node.target)
1525
+ value = visit(node.value)
1526
+
1527
+ bounds(node.operator_loc)
1528
+ operator = on_op("&&=")
1529
+ value = visit_write_value(node.value)
1530
+
1531
+ bounds(node.location)
1532
+ on_opassign(target, operator, value)
1533
+ end
1534
+
1535
+ # Foo::Bar ||= baz
1536
+ # ^^^^^^^^^^^^^^^^
1537
+ def visit_constant_path_or_write_node(node)
1538
+ target = visit_constant_path_write_node_target(node.target)
1539
+ value = visit(node.value)
1540
+
1541
+ bounds(node.operator_loc)
1542
+ operator = on_op("||=")
1543
+ value = visit_write_value(node.value)
1544
+
1545
+ bounds(node.location)
1546
+ on_opassign(target, operator, value)
1547
+ end
1548
+
1549
+ # Foo::Bar, = baz
1550
+ # ^^^^^^^^
1551
+ def visit_constant_path_target_node(node)
1552
+ visit_constant_path_write_node_target(node)
1553
+ end
1554
+
1555
+ # def foo; end
1556
+ # ^^^^^^^^^^^^
1557
+ #
1558
+ # def self.foo; end
1559
+ # ^^^^^^^^^^^^^^^^^
1560
+ def visit_def_node(node)
1561
+ receiver = visit(node.receiver)
1562
+ operator =
1563
+ if !node.operator_loc.nil?
1564
+ bounds(node.operator_loc)
1565
+ visit_token(node.operator)
1566
+ end
1567
+
1568
+ bounds(node.name_loc)
1569
+ name = visit_token(node.name_loc.slice)
1570
+
1571
+ parameters =
1572
+ if node.parameters.nil?
1573
+ bounds(node.location)
1574
+ on_params(nil, nil, nil, nil, nil, nil, nil)
1575
+ else
1576
+ visit(node.parameters)
1577
+ end
1578
+
1579
+ if !node.lparen_loc.nil?
1580
+ bounds(node.lparen_loc)
1581
+ parameters = on_paren(parameters)
1582
+ end
1583
+
1584
+ bodystmt =
1585
+ if node.equal_loc.nil?
1586
+ visit_body_node(node.rparen_loc || node.end_keyword_loc, node.body)
1587
+ else
1588
+ body = visit(node.body.body.first)
1589
+
1590
+ bounds(node.body.location)
1591
+ on_bodystmt(body, nil, nil, nil)
1592
+ end
1593
+
1594
+ bounds(node.location)
1595
+ if receiver.nil?
1596
+ on_def(name, parameters, bodystmt)
1597
+ else
1598
+ on_defs(receiver, operator, name, parameters, bodystmt)
1599
+ end
1600
+ end
1601
+
1602
+ # defined? a
1603
+ # ^^^^^^^^^^
1604
+ #
1605
+ # defined?(a)
1606
+ # ^^^^^^^^^^^
1607
+ def visit_defined_node(node)
1608
+ bounds(node.location)
1609
+ on_defined(visit(node.value))
1610
+ end
1611
+
1612
+ # if foo then bar else baz end
1613
+ # ^^^^^^^^^^^^
1614
+ def visit_else_node(node)
1615
+ statements =
1616
+ if node.statements.nil?
1617
+ [nil]
1618
+ else
1619
+ body = node.statements.body
1620
+ body.unshift(nil) if void_stmt?(node.else_keyword_loc, node.statements.body[0].location, false)
1621
+ body
1622
+ end
1623
+
1624
+ bounds(node.location)
1625
+ on_else(visit_statements_node_body(statements))
1626
+ end
1627
+
1628
+ # "foo #{bar}"
1629
+ # ^^^^^^
1630
+ def visit_embedded_statements_node(node)
1631
+ bounds(node.opening_loc)
1632
+ on_embexpr_beg(node.opening)
1633
+
1634
+ statements =
1635
+ if node.statements.nil?
1636
+ bounds(node.location)
1637
+ on_stmts_add(on_stmts_new, on_void_stmt)
1638
+ else
1639
+ visit(node.statements)
1640
+ end
1641
+
1642
+ bounds(node.closing_loc)
1643
+ on_embexpr_end(node.closing)
1644
+
1645
+ bounds(node.location)
1646
+ on_string_embexpr(statements)
1647
+ end
1648
+
1649
+ # "foo #@bar"
1650
+ # ^^^^^
1651
+ def visit_embedded_variable_node(node)
1652
+ bounds(node.operator_loc)
1653
+ on_embvar(node.operator)
1654
+
1655
+ variable = visit(node.variable)
1656
+
1657
+ bounds(node.location)
1658
+ on_string_dvar(variable)
1659
+ end
1660
+
1661
+ # Visit an EnsureNode node.
1662
+ def visit_ensure_node(node)
1663
+ statements =
1664
+ if node.statements.nil?
1665
+ [nil]
1666
+ else
1667
+ body = node.statements.body
1668
+ body.unshift(nil) if void_stmt?(node.ensure_keyword_loc, body[0].location, false)
1669
+ body
1670
+ end
1671
+
1672
+ statements = visit_statements_node_body(statements)
1673
+
1674
+ bounds(node.location)
1675
+ on_ensure(statements)
1676
+ end
1677
+
1678
+ # false
1679
+ # ^^^^^
1680
+ def visit_false_node(node)
1681
+ bounds(node.location)
1682
+ on_var_ref(on_kw("false"))
1683
+ end
1684
+
1685
+ # foo => [*, bar, *]
1686
+ # ^^^^^^^^^^^
1687
+ def visit_find_pattern_node(node)
1688
+ constant = visit(node.constant)
1689
+ left =
1690
+ if node.left.expression.nil?
1691
+ bounds(node.left.location)
1692
+ on_var_field(nil)
1693
+ else
1694
+ visit(node.left.expression)
1695
+ end
1696
+
1697
+ requireds = visit_all(node.requireds) if node.requireds.any?
1698
+ right =
1699
+ if node.right.expression.nil?
1700
+ bounds(node.right.location)
1701
+ on_var_field(nil)
1702
+ else
1703
+ visit(node.right.expression)
1704
+ end
1705
+
1706
+ bounds(node.location)
1707
+ on_fndptn(constant, left, requireds, right)
1708
+ end
1709
+
1710
+ # if foo .. bar; end
1711
+ # ^^^^^^^^^^
1712
+ def visit_flip_flop_node(node)
1713
+ left = visit(node.left)
1714
+ right = visit(node.right)
1715
+
1716
+ bounds(node.location)
1717
+ if node.exclude_end?
1718
+ on_dot3(left, right)
1719
+ else
1720
+ on_dot2(left, right)
1721
+ end
1722
+ end
1723
+
1724
+ # 1.0
1725
+ # ^^^
1726
+ def visit_float_node(node)
1727
+ visit_number_node(node) { |text| on_float(text) }
1728
+ end
1729
+
1730
+ # for foo in bar do end
1731
+ # ^^^^^^^^^^^^^^^^^^^^^
1732
+ def visit_for_node(node)
1733
+ index = visit(node.index)
1734
+ collection = visit(node.collection)
1735
+ statements =
1736
+ if node.statements.nil?
1737
+ bounds(node.location)
1738
+ on_stmts_add(on_stmts_new, on_void_stmt)
1739
+ else
1740
+ visit(node.statements)
1741
+ end
1742
+
1743
+ bounds(node.location)
1744
+ on_for(index, collection, statements)
1745
+ end
1746
+
1747
+ # def foo(...); bar(...); end
1748
+ # ^^^
1749
+ def visit_forwarding_arguments_node(node)
1750
+ bounds(node.location)
1751
+ on_args_forward
1752
+ end
1753
+
1754
+ # def foo(...); end
1755
+ # ^^^
1756
+ def visit_forwarding_parameter_node(node)
1757
+ bounds(node.location)
1758
+ on_args_forward
1759
+ end
1760
+
1761
+ # super
1762
+ # ^^^^^
1763
+ #
1764
+ # super {}
1765
+ # ^^^^^^^^
1766
+ def visit_forwarding_super_node(node)
1767
+ if node.block.nil?
1768
+ bounds(node.location)
1769
+ on_zsuper
1770
+ else
1771
+ block = visit(node.block)
1772
+
1773
+ bounds(node.location)
1774
+ on_method_add_block(on_zsuper, block)
1775
+ end
1776
+ end
1777
+
1778
+ # $foo
1779
+ # ^^^^
1780
+ def visit_global_variable_read_node(node)
1781
+ bounds(node.location)
1782
+ on_var_ref(on_gvar(node.name.to_s))
1783
+ end
1784
+
1785
+ # $foo = 1
1786
+ # ^^^^^^^^
1787
+ #
1788
+ # $foo, $bar = 1
1789
+ # ^^^^ ^^^^
1790
+ def visit_global_variable_write_node(node)
1791
+ bounds(node.name_loc)
1792
+ target = on_var_field(on_gvar(node.name.to_s))
1793
+ value = visit_write_value(node.value)
1794
+
1795
+ bounds(node.location)
1796
+ on_assign(target, value)
1797
+ end
1798
+
1799
+ # $foo += bar
1800
+ # ^^^^^^^^^^^
1801
+ def visit_global_variable_operator_write_node(node)
1802
+ bounds(node.name_loc)
1803
+ target = on_var_field(on_gvar(node.name.to_s))
1804
+
1805
+ bounds(node.operator_loc)
1806
+ operator = on_op("#{node.operator}=")
1807
+ value = visit_write_value(node.value)
1808
+
1809
+ bounds(node.location)
1810
+ on_opassign(target, operator, value)
1811
+ end
1812
+
1813
+ # $foo &&= bar
1814
+ # ^^^^^^^^^^^^
1815
+ def visit_global_variable_and_write_node(node)
1816
+ bounds(node.name_loc)
1817
+ target = on_var_field(on_gvar(node.name.to_s))
1818
+
1819
+ bounds(node.operator_loc)
1820
+ operator = on_op("&&=")
1821
+ value = visit_write_value(node.value)
1822
+
1823
+ bounds(node.location)
1824
+ on_opassign(target, operator, value)
1825
+ end
1826
+
1827
+ # $foo ||= bar
1828
+ # ^^^^^^^^^^^^
1829
+ def visit_global_variable_or_write_node(node)
1830
+ bounds(node.name_loc)
1831
+ target = on_var_field(on_gvar(node.name.to_s))
1832
+
1833
+ bounds(node.operator_loc)
1834
+ operator = on_op("||=")
1835
+ value = visit_write_value(node.value)
1836
+
1837
+ bounds(node.location)
1838
+ on_opassign(target, operator, value)
1839
+ end
1840
+
1841
+ # $foo, = bar
1842
+ # ^^^^
1843
+ def visit_global_variable_target_node(node)
1844
+ bounds(node.location)
1845
+ on_var_field(on_gvar(node.name.to_s))
1846
+ end
1847
+
1848
+ # {}
1849
+ # ^^
1850
+ def visit_hash_node(node)
1851
+ elements =
1852
+ if node.elements.any?
1853
+ args = visit_all(node.elements)
1854
+
1855
+ bounds(node.elements.first.location)
1856
+ on_assoclist_from_args(args)
1857
+ end
1858
+
1859
+ bounds(node.location)
1860
+ on_hash(elements)
1861
+ end
1862
+
1863
+ # foo => {}
1864
+ # ^^
1865
+ def visit_hash_pattern_node(node)
1866
+ constant = visit(node.constant)
1867
+ elements =
1868
+ if node.elements.any? || !node.rest.nil?
1869
+ node.elements.map do |element|
1870
+ [
1871
+ if (key = element.key).opening_loc.nil?
1872
+ visit(key)
1873
+ else
1874
+ bounds(key.value_loc)
1875
+ if (value = key.value).empty?
1876
+ on_string_content
1877
+ else
1878
+ on_string_add(on_string_content, on_tstring_content(value))
1879
+ end
1880
+ end,
1881
+ visit(element.value)
1882
+ ]
1883
+ end
1884
+ end
1885
+
1886
+ rest =
1887
+ case node.rest
1888
+ when AssocSplatNode
1889
+ visit(node.rest.value)
1890
+ when NoKeywordsParameterNode
1891
+ bounds(node.rest.location)
1892
+ on_var_field(visit(node.rest))
1893
+ end
1894
+
1895
+ bounds(node.location)
1896
+ on_hshptn(constant, elements, rest)
1897
+ end
1898
+
1899
+ # if foo then bar end
1900
+ # ^^^^^^^^^^^^^^^^^^^
1901
+ #
1902
+ # bar if foo
1903
+ # ^^^^^^^^^^
1904
+ #
1905
+ # foo ? bar : baz
1906
+ # ^^^^^^^^^^^^^^^
1907
+ def visit_if_node(node)
1908
+ if node.then_keyword == "?"
1909
+ predicate = visit(node.predicate)
1910
+ truthy = visit(node.statements.body.first)
1911
+ falsy = visit(node.consequent.statements.body.first)
1912
+
1913
+ bounds(node.location)
1914
+ on_ifop(predicate, truthy, falsy)
1915
+ elsif node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
1916
+ predicate = visit(node.predicate)
1917
+ statements =
1918
+ if node.statements.nil?
1919
+ bounds(node.location)
1920
+ on_stmts_add(on_stmts_new, on_void_stmt)
1921
+ else
1922
+ visit(node.statements)
1923
+ end
1924
+ consequent = visit(node.consequent)
1925
+
1926
+ bounds(node.location)
1927
+ if node.if_keyword == "if"
1928
+ on_if(predicate, statements, consequent)
1929
+ else
1930
+ on_elsif(predicate, statements, consequent)
1931
+ end
1932
+ else
1933
+ statements = visit(node.statements.body.first)
1934
+ predicate = visit(node.predicate)
1935
+
1936
+ bounds(node.location)
1937
+ on_if_mod(predicate, statements)
1938
+ end
1939
+ end
1940
+
1941
+ # 1i
1942
+ # ^^
1943
+ def visit_imaginary_node(node)
1944
+ visit_number_node(node) { |text| on_imaginary(text) }
1945
+ end
1946
+
1947
+ # { foo: }
1948
+ # ^^^^
1949
+ def visit_implicit_node(node)
1950
+ end
1951
+
1952
+ # foo { |bar,| }
1953
+ # ^
1954
+ def visit_implicit_rest_node(node)
1955
+ bounds(node.location)
1956
+ on_excessed_comma
1957
+ end
1958
+
1959
+ # case foo; in bar; end
1960
+ # ^^^^^^^^^^^^^^^^^^^^^
1961
+ def visit_in_node(node)
1962
+ # This is a special case where we're not going to call on_in directly
1963
+ # because we don't have access to the consequent. Instead, we'll return
1964
+ # the component parts and let the parent node handle it.
1965
+ pattern = visit_pattern_node(node.pattern)
1966
+ statements =
1967
+ if node.statements.nil?
1968
+ bounds(node.location)
1969
+ on_stmts_add(on_stmts_new, on_void_stmt)
1970
+ else
1971
+ visit(node.statements)
1972
+ end
1973
+
1974
+ [pattern, statements]
1975
+ end
1976
+
1977
+ # foo[bar] += baz
1978
+ # ^^^^^^^^^^^^^^^
1979
+ def visit_index_operator_write_node(node)
1980
+ receiver = visit(node.receiver)
1981
+ arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
1982
+
1983
+ bounds(node.location)
1984
+ target = on_aref_field(receiver, arguments)
1985
+
1986
+ bounds(node.operator_loc)
1987
+ operator = on_op("#{node.operator}=")
1988
+ value = visit_write_value(node.value)
1989
+
1990
+ bounds(node.location)
1991
+ on_opassign(target, operator, value)
1992
+ end
1993
+
1994
+ # foo[bar] &&= baz
1995
+ # ^^^^^^^^^^^^^^^^
1996
+ def visit_index_and_write_node(node)
1997
+ receiver = visit(node.receiver)
1998
+ arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
1999
+
2000
+ bounds(node.location)
2001
+ target = on_aref_field(receiver, arguments)
2002
+
2003
+ bounds(node.operator_loc)
2004
+ operator = on_op("&&=")
2005
+ value = visit_write_value(node.value)
2006
+
2007
+ bounds(node.location)
2008
+ on_opassign(target, operator, value)
2009
+ end
2010
+
2011
+ # foo[bar] ||= baz
2012
+ # ^^^^^^^^^^^^^^^^
2013
+ def visit_index_or_write_node(node)
2014
+ receiver = visit(node.receiver)
2015
+ arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2016
+
2017
+ bounds(node.location)
2018
+ target = on_aref_field(receiver, arguments)
2019
+
2020
+ bounds(node.operator_loc)
2021
+ operator = on_op("||=")
2022
+ value = visit_write_value(node.value)
2023
+
2024
+ bounds(node.location)
2025
+ on_opassign(target, operator, value)
2026
+ end
2027
+
2028
+ # foo[bar], = 1
2029
+ # ^^^^^^^^
2030
+ def visit_index_target_node(node)
2031
+ receiver = visit(node.receiver)
2032
+ arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2033
+
2034
+ bounds(node.location)
2035
+ on_aref_field(receiver, arguments)
2036
+ end
2037
+
2038
+ # @foo
2039
+ # ^^^^
2040
+ def visit_instance_variable_read_node(node)
2041
+ bounds(node.location)
2042
+ on_var_ref(on_ivar(node.name.to_s))
2043
+ end
2044
+
2045
+ # @foo = 1
2046
+ # ^^^^^^^^
2047
+ def visit_instance_variable_write_node(node)
2048
+ bounds(node.name_loc)
2049
+ target = on_var_field(on_ivar(node.name.to_s))
2050
+ value = visit_write_value(node.value)
2051
+
2052
+ bounds(node.location)
2053
+ on_assign(target, value)
2054
+ end
2055
+
2056
+ # @foo += bar
2057
+ # ^^^^^^^^^^^
2058
+ def visit_instance_variable_operator_write_node(node)
2059
+ bounds(node.name_loc)
2060
+ target = on_var_field(on_ivar(node.name.to_s))
2061
+
2062
+ bounds(node.operator_loc)
2063
+ operator = on_op("#{node.operator}=")
2064
+ value = visit_write_value(node.value)
2065
+
2066
+ bounds(node.location)
2067
+ on_opassign(target, operator, value)
2068
+ end
2069
+
2070
+ # @foo &&= bar
2071
+ # ^^^^^^^^^^^^
2072
+ def visit_instance_variable_and_write_node(node)
2073
+ bounds(node.name_loc)
2074
+ target = on_var_field(on_ivar(node.name.to_s))
2075
+
2076
+ bounds(node.operator_loc)
2077
+ operator = on_op("&&=")
2078
+ value = visit_write_value(node.value)
2079
+
2080
+ bounds(node.location)
2081
+ on_opassign(target, operator, value)
2082
+ end
2083
+
2084
+ # @foo ||= bar
2085
+ # ^^^^^^^^^^^^
2086
+ def visit_instance_variable_or_write_node(node)
2087
+ bounds(node.name_loc)
2088
+ target = on_var_field(on_ivar(node.name.to_s))
2089
+
2090
+ bounds(node.operator_loc)
2091
+ operator = on_op("||=")
2092
+ value = visit_write_value(node.value)
2093
+
2094
+ bounds(node.location)
2095
+ on_opassign(target, operator, value)
2096
+ end
2097
+
2098
+ # @foo, = bar
2099
+ # ^^^^
2100
+ def visit_instance_variable_target_node(node)
2101
+ bounds(node.location)
2102
+ on_var_field(on_ivar(node.name.to_s))
2103
+ end
2104
+
2105
+ # 1
2106
+ # ^
2107
+ def visit_integer_node(node)
2108
+ visit_number_node(node) { |text| on_int(text) }
2109
+ end
2110
+
2111
+ # if /foo #{bar}/ then end
2112
+ # ^^^^^^^^^^^^
2113
+ def visit_interpolated_match_last_line_node(node)
2114
+ bounds(node.opening_loc)
2115
+ on_regexp_beg(node.opening)
2116
+
2117
+ bounds(node.parts.first.location)
2118
+ parts =
2119
+ node.parts.inject(on_regexp_new) do |content, part|
2120
+ on_regexp_add(content, visit_string_content(part))
2121
+ end
2122
+
2123
+ bounds(node.closing_loc)
2124
+ closing = on_regexp_end(node.closing)
2125
+
2126
+ bounds(node.location)
2127
+ on_regexp_literal(parts, closing)
2128
+ end
2129
+
2130
+ # /foo #{bar}/
2131
+ # ^^^^^^^^^^^^
2132
+ def visit_interpolated_regular_expression_node(node)
2133
+ bounds(node.opening_loc)
2134
+ on_regexp_beg(node.opening)
2135
+
2136
+ bounds(node.parts.first.location)
2137
+ parts =
2138
+ node.parts.inject(on_regexp_new) do |content, part|
2139
+ on_regexp_add(content, visit_string_content(part))
2140
+ end
2141
+
2142
+ bounds(node.closing_loc)
2143
+ closing = on_regexp_end(node.closing)
2144
+
2145
+ bounds(node.location)
2146
+ on_regexp_literal(parts, closing)
2147
+ end
2148
+
2149
+ # "foo #{bar}"
2150
+ # ^^^^^^^^^^^^
2151
+ def visit_interpolated_string_node(node)
2152
+ if node.opening&.start_with?("<<~")
2153
+ heredoc = visit_heredoc_string_node(node)
2154
+
2155
+ bounds(node.location)
2156
+ on_string_literal(heredoc)
2157
+ elsif !node.heredoc? && node.parts.length > 1 && node.parts.any? { |part| (part.is_a?(StringNode) || part.is_a?(InterpolatedStringNode)) && !part.opening_loc.nil? }
2158
+ first, *rest = node.parts
2159
+ rest.inject(visit(first)) do |content, part|
2160
+ concat = visit(part)
2161
+
2162
+ bounds(part.location)
2163
+ on_string_concat(content, concat)
2164
+ end
2165
+ else
2166
+ bounds(node.parts.first.location)
2167
+ parts =
2168
+ node.parts.inject(on_string_content) do |content, part|
2169
+ on_string_add(content, visit_string_content(part))
2170
+ end
2171
+
2172
+ bounds(node.location)
2173
+ on_string_literal(parts)
2174
+ end
2175
+ end
2176
+
2177
+ # :"foo #{bar}"
2178
+ # ^^^^^^^^^^^^^
2179
+ def visit_interpolated_symbol_node(node)
2180
+ bounds(node.parts.first.location)
2181
+ parts =
2182
+ node.parts.inject(on_string_content) do |content, part|
2183
+ on_string_add(content, visit_string_content(part))
2184
+ end
2185
+
2186
+ bounds(node.location)
2187
+ on_dyna_symbol(parts)
2188
+ end
2189
+
2190
+ # `foo #{bar}`
2191
+ # ^^^^^^^^^^^^
2192
+ def visit_interpolated_x_string_node(node)
2193
+ if node.opening.start_with?("<<~")
2194
+ heredoc = visit_heredoc_x_string_node(node)
2195
+
2196
+ bounds(node.location)
2197
+ on_xstring_literal(heredoc)
2198
+ else
2199
+ bounds(node.parts.first.location)
2200
+ parts =
2201
+ node.parts.inject(on_xstring_new) do |content, part|
2202
+ on_xstring_add(content, visit_string_content(part))
2203
+ end
2204
+
2205
+ bounds(node.location)
2206
+ on_xstring_literal(parts)
2207
+ end
2208
+ end
2209
+
2210
+ # Visit an individual part of a string-like node.
2211
+ private def visit_string_content(part)
2212
+ if part.is_a?(StringNode)
2213
+ bounds(part.content_loc)
2214
+ on_tstring_content(part.content)
2215
+ else
2216
+ visit(part)
2217
+ end
2218
+ end
2219
+
2220
+ # -> { it }
2221
+ # ^^^^^^^^^
2222
+ def visit_it_parameters_node(node)
2223
+ end
2224
+
2225
+ # foo(bar: baz)
2226
+ # ^^^^^^^^
2227
+ def visit_keyword_hash_node(node)
2228
+ elements = visit_all(node.elements)
2229
+
2230
+ bounds(node.location)
2231
+ on_bare_assoc_hash(elements)
2232
+ end
2233
+
2234
+ # def foo(**bar); end
2235
+ # ^^^^^
2236
+ #
2237
+ # def foo(**); end
2238
+ # ^^
2239
+ def visit_keyword_rest_parameter_node(node)
2240
+ if node.name_loc.nil?
2241
+ bounds(node.location)
2242
+ on_kwrest_param(nil)
2243
+ else
2244
+ bounds(node.name_loc)
2245
+ name = on_ident(node.name.to_s)
2246
+
2247
+ bounds(node.location)
2248
+ on_kwrest_param(name)
2249
+ end
2250
+ end
2251
+
2252
+ # -> {}
2253
+ def visit_lambda_node(node)
2254
+ bounds(node.operator_loc)
2255
+ on_tlambda(node.operator)
2256
+
2257
+ parameters =
2258
+ if node.parameters.is_a?(BlockParametersNode)
2259
+ # Ripper does not track block-locals within lambdas, so we skip
2260
+ # directly to the parameters here.
2261
+ params =
2262
+ if node.parameters.parameters.nil?
2263
+ bounds(node.location)
2264
+ on_params(nil, nil, nil, nil, nil, nil, nil)
2265
+ else
2266
+ visit(node.parameters.parameters)
2267
+ end
2268
+
2269
+ if node.parameters.opening_loc.nil?
2270
+ params
2271
+ else
2272
+ bounds(node.parameters.opening_loc)
2273
+ on_paren(params)
437
2274
  end
438
- method_args_val = on_method_add_arg(on_fcall(ident_val), args)
439
- return on_method_add_block(method_args_val, block_val)
440
2275
  else
441
- if node.arguments.nil?
442
- return on_command(ident_val, nil)
2276
+ bounds(node.location)
2277
+ on_params(nil, nil, nil, nil, nil, nil, nil)
2278
+ end
2279
+
2280
+ braces = node.opening == "{"
2281
+ if braces
2282
+ bounds(node.opening_loc)
2283
+ on_tlambeg(node.opening)
2284
+ end
2285
+
2286
+ body =
2287
+ case node.body
2288
+ when nil
2289
+ bounds(node.location)
2290
+ stmts = on_stmts_add(on_stmts_new, on_void_stmt)
2291
+
2292
+ bounds(node.location)
2293
+ braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
2294
+ when StatementsNode
2295
+ stmts = node.body.body
2296
+ stmts.unshift(nil) if void_stmt?(node.parameters&.location || node.opening_loc, node.body.location, false)
2297
+ stmts = visit_statements_node_body(stmts)
2298
+
2299
+ bounds(node.body.location)
2300
+ braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
2301
+ when BeginNode
2302
+ visit_body_node(node.opening_loc, node.body)
2303
+ else
2304
+ raise
2305
+ end
2306
+
2307
+ bounds(node.location)
2308
+ on_lambda(parameters, body)
2309
+ end
2310
+
2311
+ # foo
2312
+ # ^^^
2313
+ def visit_local_variable_read_node(node)
2314
+ bounds(node.location)
2315
+
2316
+ if node.name == :"0it"
2317
+ on_vcall(on_ident(node.slice))
2318
+ else
2319
+ on_var_ref(on_ident(node.slice))
2320
+ end
2321
+ end
2322
+
2323
+ # foo = 1
2324
+ # ^^^^^^^
2325
+ def visit_local_variable_write_node(node)
2326
+ bounds(node.name_loc)
2327
+ target = on_var_field(on_ident(node.name_loc.slice))
2328
+ value = visit_write_value(node.value)
2329
+
2330
+ bounds(node.location)
2331
+ on_assign(target, value)
2332
+ end
2333
+
2334
+ # foo += bar
2335
+ # ^^^^^^^^^^
2336
+ def visit_local_variable_operator_write_node(node)
2337
+ bounds(node.name_loc)
2338
+ target = on_var_field(on_ident(node.name_loc.slice))
2339
+
2340
+ bounds(node.operator_loc)
2341
+ operator = on_op("#{node.operator}=")
2342
+ value = visit_write_value(node.value)
2343
+
2344
+ bounds(node.location)
2345
+ on_opassign(target, operator, value)
2346
+ end
2347
+
2348
+ # foo &&= bar
2349
+ # ^^^^^^^^^^^
2350
+ def visit_local_variable_and_write_node(node)
2351
+ bounds(node.name_loc)
2352
+ target = on_var_field(on_ident(node.name_loc.slice))
2353
+
2354
+ bounds(node.operator_loc)
2355
+ operator = on_op("&&=")
2356
+ value = visit_write_value(node.value)
2357
+
2358
+ bounds(node.location)
2359
+ on_opassign(target, operator, value)
2360
+ end
2361
+
2362
+ # foo ||= bar
2363
+ # ^^^^^^^^^^^
2364
+ def visit_local_variable_or_write_node(node)
2365
+ bounds(node.name_loc)
2366
+ target = on_var_field(on_ident(node.name_loc.slice))
2367
+
2368
+ bounds(node.operator_loc)
2369
+ operator = on_op("||=")
2370
+ value = visit_write_value(node.value)
2371
+
2372
+ bounds(node.location)
2373
+ on_opassign(target, operator, value)
2374
+ end
2375
+
2376
+ # foo, = bar
2377
+ # ^^^
2378
+ def visit_local_variable_target_node(node)
2379
+ bounds(node.location)
2380
+ on_var_field(on_ident(node.name.to_s))
2381
+ end
2382
+
2383
+ # if /foo/ then end
2384
+ # ^^^^^
2385
+ def visit_match_last_line_node(node)
2386
+ bounds(node.opening_loc)
2387
+ on_regexp_beg(node.opening)
2388
+
2389
+ bounds(node.content_loc)
2390
+ tstring_content = on_tstring_content(node.content)
2391
+
2392
+ bounds(node.closing_loc)
2393
+ closing = on_regexp_end(node.closing)
2394
+
2395
+ on_regexp_literal(on_regexp_add(on_regexp_new, tstring_content), closing)
2396
+ end
2397
+
2398
+ # foo in bar
2399
+ # ^^^^^^^^^^
2400
+ def visit_match_predicate_node(node)
2401
+ value = visit(node.value)
2402
+ pattern = on_in(visit_pattern_node(node.pattern), nil, nil)
2403
+
2404
+ on_case(value, pattern)
2405
+ end
2406
+
2407
+ # foo => bar
2408
+ # ^^^^^^^^^^
2409
+ def visit_match_required_node(node)
2410
+ value = visit(node.value)
2411
+ pattern = on_in(visit_pattern_node(node.pattern), nil, nil)
2412
+
2413
+ on_case(value, pattern)
2414
+ end
2415
+
2416
+ # /(?<foo>foo)/ =~ bar
2417
+ # ^^^^^^^^^^^^^^^^^^^^
2418
+ def visit_match_write_node(node)
2419
+ visit(node.call)
2420
+ end
2421
+
2422
+ # A node that is missing from the syntax tree. This is only used in the
2423
+ # case of a syntax error.
2424
+ def visit_missing_node(node)
2425
+ raise "Cannot visit missing nodes directly."
2426
+ end
2427
+
2428
+ # module Foo; end
2429
+ # ^^^^^^^^^^^^^^^
2430
+ def visit_module_node(node)
2431
+ constant_path =
2432
+ if node.constant_path.is_a?(ConstantReadNode)
2433
+ bounds(node.constant_path.location)
2434
+ on_const_ref(on_const(node.constant_path.name.to_s))
2435
+ else
2436
+ visit(node.constant_path)
2437
+ end
2438
+
2439
+ bodystmt = visit_body_node(node.constant_path.location, node.body, true)
2440
+
2441
+ bounds(node.location)
2442
+ on_module(constant_path, bodystmt)
2443
+ end
2444
+
2445
+ # (foo, bar), bar = qux
2446
+ # ^^^^^^^^^^
2447
+ def visit_multi_target_node(node)
2448
+ bounds(node.location)
2449
+ targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, true)
2450
+
2451
+ if node.lparen_loc.nil?
2452
+ targets
2453
+ else
2454
+ bounds(node.lparen_loc)
2455
+ on_mlhs_paren(targets)
2456
+ end
2457
+ end
2458
+
2459
+ # Visit the targets of a multi-target node.
2460
+ private def visit_multi_target_node_targets(lefts, rest, rights, skippable)
2461
+ if skippable && lefts.length == 1 && lefts.first.is_a?(MultiTargetNode) && rest.nil? && rights.empty?
2462
+ return visit(lefts.first)
2463
+ end
2464
+
2465
+ mlhs = on_mlhs_new
2466
+
2467
+ lefts.each do |left|
2468
+ bounds(left.location)
2469
+ mlhs = on_mlhs_add(mlhs, visit(left))
2470
+ end
2471
+
2472
+ case rest
2473
+ when nil
2474
+ # do nothing
2475
+ when ImplicitRestNode
2476
+ # these do not get put into the generated tree
2477
+ bounds(rest.location)
2478
+ on_excessed_comma
2479
+ else
2480
+ bounds(rest.location)
2481
+ mlhs = on_mlhs_add_star(mlhs, visit(rest))
2482
+ end
2483
+
2484
+ if rights.any?
2485
+ bounds(rights.first.location)
2486
+ post = on_mlhs_new
2487
+
2488
+ rights.each do |right|
2489
+ bounds(right.location)
2490
+ post = on_mlhs_add(post, visit(right))
2491
+ end
2492
+
2493
+ mlhs = on_mlhs_add_post(mlhs, post)
2494
+ end
2495
+
2496
+ mlhs
2497
+ end
2498
+
2499
+ # foo, bar = baz
2500
+ # ^^^^^^^^^^^^^^
2501
+ def visit_multi_write_node(node)
2502
+ bounds(node.location)
2503
+ targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, true)
2504
+
2505
+ unless node.lparen_loc.nil?
2506
+ bounds(node.lparen_loc)
2507
+ targets = on_mlhs_paren(targets)
2508
+ end
2509
+
2510
+ value = visit_write_value(node.value)
2511
+
2512
+ bounds(node.location)
2513
+ on_massign(targets, value)
2514
+ end
2515
+
2516
+ # next
2517
+ # ^^^^
2518
+ #
2519
+ # next foo
2520
+ # ^^^^^^^^
2521
+ def visit_next_node(node)
2522
+ if node.arguments.nil?
2523
+ bounds(node.location)
2524
+ on_next(on_args_new)
2525
+ else
2526
+ arguments = visit(node.arguments)
2527
+
2528
+ bounds(node.location)
2529
+ on_next(arguments)
2530
+ end
2531
+ end
2532
+
2533
+ # nil
2534
+ # ^^^
2535
+ def visit_nil_node(node)
2536
+ bounds(node.location)
2537
+ on_var_ref(on_kw("nil"))
2538
+ end
2539
+
2540
+ # def foo(**nil); end
2541
+ # ^^^^^
2542
+ def visit_no_keywords_parameter_node(node)
2543
+ bounds(node.location)
2544
+ on_nokw_param(nil)
2545
+
2546
+ :nil
2547
+ end
2548
+
2549
+ # -> { _1 + _2 }
2550
+ # ^^^^^^^^^^^^^^
2551
+ def visit_numbered_parameters_node(node)
2552
+ end
2553
+
2554
+ # $1
2555
+ # ^^
2556
+ def visit_numbered_reference_read_node(node)
2557
+ bounds(node.location)
2558
+ on_backref(node.slice)
2559
+ end
2560
+
2561
+ # def foo(bar: baz); end
2562
+ # ^^^^^^^^
2563
+ def visit_optional_keyword_parameter_node(node)
2564
+ bounds(node.name_loc)
2565
+ name = on_label("#{node.name}:")
2566
+ value = visit(node.value)
2567
+
2568
+ [name, value]
2569
+ end
2570
+
2571
+ # def foo(bar = 1); end
2572
+ # ^^^^^^^
2573
+ def visit_optional_parameter_node(node)
2574
+ bounds(node.name_loc)
2575
+ name = visit_token(node.name.to_s)
2576
+ value = visit(node.value)
2577
+
2578
+ [name, value]
2579
+ end
2580
+
2581
+ # a or b
2582
+ # ^^^^^^
2583
+ def visit_or_node(node)
2584
+ left = visit(node.left)
2585
+ right = visit(node.right)
2586
+
2587
+ bounds(node.location)
2588
+ on_binary(left, node.operator.to_sym, right)
2589
+ end
2590
+
2591
+ # def foo(bar, *baz); end
2592
+ # ^^^^^^^^^
2593
+ def visit_parameters_node(node)
2594
+ requireds = node.requireds.map { |required| required.is_a?(MultiTargetNode) ? visit_destructured_parameter_node(required) : visit(required) } if node.requireds.any?
2595
+ optionals = visit_all(node.optionals) if node.optionals.any?
2596
+ rest = visit(node.rest)
2597
+ posts = node.posts.map { |post| post.is_a?(MultiTargetNode) ? visit_destructured_parameter_node(post) : visit(post) } if node.posts.any?
2598
+ keywords = visit_all(node.keywords) if node.keywords.any?
2599
+ keyword_rest = visit(node.keyword_rest)
2600
+ block = visit(node.block)
2601
+
2602
+ bounds(node.location)
2603
+ on_params(requireds, optionals, rest, posts, keywords, keyword_rest, block)
2604
+ end
2605
+
2606
+ # Visit a destructured positional parameter node.
2607
+ private def visit_destructured_parameter_node(node)
2608
+ bounds(node.location)
2609
+ targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, false)
2610
+
2611
+ bounds(node.lparen_loc)
2612
+ on_mlhs_paren(targets)
2613
+ end
2614
+
2615
+ # ()
2616
+ # ^^
2617
+ #
2618
+ # (1)
2619
+ # ^^^
2620
+ def visit_parentheses_node(node)
2621
+ body =
2622
+ if node.body.nil?
2623
+ on_stmts_add(on_stmts_new, on_void_stmt)
2624
+ else
2625
+ visit(node.body)
2626
+ end
2627
+
2628
+ bounds(node.location)
2629
+ on_paren(body)
2630
+ end
2631
+
2632
+ # foo => ^(bar)
2633
+ # ^^^^^^
2634
+ def visit_pinned_expression_node(node)
2635
+ expression = visit(node.expression)
2636
+
2637
+ bounds(node.location)
2638
+ on_begin(expression)
2639
+ end
2640
+
2641
+ # foo = 1 and bar => ^foo
2642
+ # ^^^^
2643
+ def visit_pinned_variable_node(node)
2644
+ visit(node.variable)
2645
+ end
2646
+
2647
+ # END {}
2648
+ # ^^^^^^
2649
+ def visit_post_execution_node(node)
2650
+ statements =
2651
+ if node.statements.nil?
2652
+ bounds(node.location)
2653
+ on_stmts_add(on_stmts_new, on_void_stmt)
2654
+ else
2655
+ visit(node.statements)
2656
+ end
2657
+
2658
+ bounds(node.location)
2659
+ on_END(statements)
2660
+ end
2661
+
2662
+ # BEGIN {}
2663
+ # ^^^^^^^^
2664
+ def visit_pre_execution_node(node)
2665
+ statements =
2666
+ if node.statements.nil?
2667
+ bounds(node.location)
2668
+ on_stmts_add(on_stmts_new, on_void_stmt)
2669
+ else
2670
+ visit(node.statements)
2671
+ end
2672
+
2673
+ bounds(node.location)
2674
+ on_BEGIN(statements)
2675
+ end
2676
+
2677
+ # The top-level program node.
2678
+ def visit_program_node(node)
2679
+ body = node.statements.body
2680
+ body << nil if body.empty?
2681
+ statements = visit_statements_node_body(body)
2682
+
2683
+ bounds(node.location)
2684
+ on_program(statements)
2685
+ end
2686
+
2687
+ # 0..5
2688
+ # ^^^^
2689
+ def visit_range_node(node)
2690
+ left = visit(node.left)
2691
+ right = visit(node.right)
2692
+
2693
+ bounds(node.location)
2694
+ if node.exclude_end?
2695
+ on_dot3(left, right)
2696
+ else
2697
+ on_dot2(left, right)
2698
+ end
2699
+ end
2700
+
2701
+ # 1r
2702
+ # ^^
2703
+ def visit_rational_node(node)
2704
+ visit_number_node(node) { |text| on_rational(text) }
2705
+ end
2706
+
2707
+ # redo
2708
+ # ^^^^
2709
+ def visit_redo_node(node)
2710
+ bounds(node.location)
2711
+ on_redo
2712
+ end
2713
+
2714
+ # /foo/
2715
+ # ^^^^^
2716
+ def visit_regular_expression_node(node)
2717
+ bounds(node.opening_loc)
2718
+ on_regexp_beg(node.opening)
2719
+
2720
+ if node.content.empty?
2721
+ bounds(node.closing_loc)
2722
+ closing = on_regexp_end(node.closing)
2723
+
2724
+ on_regexp_literal(on_regexp_new, closing)
2725
+ else
2726
+ bounds(node.content_loc)
2727
+ tstring_content = on_tstring_content(node.content)
2728
+
2729
+ bounds(node.closing_loc)
2730
+ closing = on_regexp_end(node.closing)
2731
+
2732
+ on_regexp_literal(on_regexp_add(on_regexp_new, tstring_content), closing)
2733
+ end
2734
+ end
2735
+
2736
+ # def foo(bar:); end
2737
+ # ^^^^
2738
+ def visit_required_keyword_parameter_node(node)
2739
+ bounds(node.name_loc)
2740
+ [on_label("#{node.name}:"), false]
2741
+ end
2742
+
2743
+ # def foo(bar); end
2744
+ # ^^^
2745
+ def visit_required_parameter_node(node)
2746
+ bounds(node.location)
2747
+ on_ident(node.name.to_s)
2748
+ end
2749
+
2750
+ # foo rescue bar
2751
+ # ^^^^^^^^^^^^^^
2752
+ def visit_rescue_modifier_node(node)
2753
+ expression = visit_write_value(node.expression)
2754
+ rescue_expression = visit(node.rescue_expression)
2755
+
2756
+ bounds(node.location)
2757
+ on_rescue_mod(expression, rescue_expression)
2758
+ end
2759
+
2760
+ # begin; rescue; end
2761
+ # ^^^^^^^
2762
+ def visit_rescue_node(node)
2763
+ exceptions =
2764
+ case node.exceptions.length
2765
+ when 0
2766
+ nil
2767
+ when 1
2768
+ if (exception = node.exceptions.first).is_a?(SplatNode)
2769
+ bounds(exception.location)
2770
+ on_mrhs_add_star(on_mrhs_new, visit(exception))
443
2771
  else
444
- args = on_args_add_block(visit_elements(node.arguments.arguments), false)
445
- return on_command(ident_val, args)
2772
+ [visit(node.exceptions.first)]
2773
+ end
2774
+ else
2775
+ bounds(node.location)
2776
+ length = node.exceptions.length
2777
+
2778
+ node.exceptions.each_with_index.inject(on_args_new) do |mrhs, (exception, index)|
2779
+ arg = visit(exception)
2780
+
2781
+ bounds(exception.location)
2782
+ mrhs = on_mrhs_new_from_args(mrhs) if index == length - 1
2783
+
2784
+ if exception.is_a?(SplatNode)
2785
+ if index == length - 1
2786
+ on_mrhs_add_star(mrhs, arg)
2787
+ else
2788
+ on_args_add_star(mrhs, arg)
2789
+ end
2790
+ else
2791
+ if index == length - 1
2792
+ on_mrhs_add(mrhs, arg)
2793
+ else
2794
+ on_args_add(mrhs, arg)
2795
+ end
2796
+ end
446
2797
  end
447
2798
  end
2799
+
2800
+ reference = visit(node.reference)
2801
+ statements =
2802
+ if node.statements.nil?
2803
+ bounds(node.location)
2804
+ on_stmts_add(on_stmts_new, on_void_stmt)
2805
+ else
2806
+ visit(node.statements)
2807
+ end
2808
+
2809
+ consequent = visit(node.consequent)
2810
+
2811
+ bounds(node.location)
2812
+ on_rescue(exceptions, reference, statements, consequent)
2813
+ end
2814
+
2815
+ # def foo(*bar); end
2816
+ # ^^^^
2817
+ #
2818
+ # def foo(*); end
2819
+ # ^
2820
+ def visit_rest_parameter_node(node)
2821
+ if node.name_loc.nil?
2822
+ bounds(node.location)
2823
+ on_rest_param(nil)
448
2824
  else
449
- operator = node.call_operator_loc.slice
450
- if operator == "." || operator == "&."
451
- left_val = visit(node.receiver)
2825
+ bounds(node.name_loc)
2826
+ on_rest_param(visit_token(node.name.to_s))
2827
+ end
2828
+ end
452
2829
 
453
- bounds(node.call_operator_loc)
454
- operator_val = operator == "." ? on_period(node.call_operator) : on_op(node.call_operator)
2830
+ # retry
2831
+ # ^^^^^
2832
+ def visit_retry_node(node)
2833
+ bounds(node.location)
2834
+ on_retry
2835
+ end
455
2836
 
456
- bounds(node.message_loc)
457
- right_val = on_ident(node.message)
2837
+ # return
2838
+ # ^^^^^^
2839
+ #
2840
+ # return 1
2841
+ # ^^^^^^^^
2842
+ def visit_return_node(node)
2843
+ if node.arguments.nil?
2844
+ bounds(node.location)
2845
+ on_return0
2846
+ else
2847
+ arguments = visit(node.arguments)
458
2848
 
459
- call_val = on_call(left_val, operator_val, right_val)
2849
+ bounds(node.location)
2850
+ on_return(arguments)
2851
+ end
2852
+ end
460
2853
 
461
- if node.block
462
- block_val = visit(node.block)
463
- return on_method_add_block(call_val, block_val)
464
- else
465
- return call_val
2854
+ # self
2855
+ # ^^^^
2856
+ def visit_self_node(node)
2857
+ bounds(node.location)
2858
+ on_var_ref(on_kw("self"))
2859
+ end
2860
+
2861
+ # A shareable constant.
2862
+ def visit_shareable_constant_node(node)
2863
+ visit(node.write)
2864
+ end
2865
+
2866
+ # class << self; end
2867
+ # ^^^^^^^^^^^^^^^^^^
2868
+ def visit_singleton_class_node(node)
2869
+ expression = visit(node.expression)
2870
+ bodystmt = visit_body_node(node.body&.location || node.end_keyword_loc, node.body)
2871
+
2872
+ bounds(node.location)
2873
+ on_sclass(expression, bodystmt)
2874
+ end
2875
+
2876
+ # __ENCODING__
2877
+ # ^^^^^^^^^^^^
2878
+ def visit_source_encoding_node(node)
2879
+ bounds(node.location)
2880
+ on_var_ref(on_kw("__ENCODING__"))
2881
+ end
2882
+
2883
+ # __FILE__
2884
+ # ^^^^^^^^
2885
+ def visit_source_file_node(node)
2886
+ bounds(node.location)
2887
+ on_var_ref(on_kw("__FILE__"))
2888
+ end
2889
+
2890
+ # __LINE__
2891
+ # ^^^^^^^^
2892
+ def visit_source_line_node(node)
2893
+ bounds(node.location)
2894
+ on_var_ref(on_kw("__LINE__"))
2895
+ end
2896
+
2897
+ # foo(*bar)
2898
+ # ^^^^
2899
+ #
2900
+ # def foo((bar, *baz)); end
2901
+ # ^^^^
2902
+ #
2903
+ # def foo(*); bar(*); end
2904
+ # ^
2905
+ def visit_splat_node(node)
2906
+ visit(node.expression)
2907
+ end
2908
+
2909
+ # A list of statements.
2910
+ def visit_statements_node(node)
2911
+ bounds(node.location)
2912
+ visit_statements_node_body(node.body)
2913
+ end
2914
+
2915
+ # Visit the list of statements of a statements node. We support nil
2916
+ # statements in the list. This would normally not be allowed by the
2917
+ # structure of the prism parse tree, but we manually add them here so that
2918
+ # we can mirror Ripper's void stmt.
2919
+ private def visit_statements_node_body(body)
2920
+ body.inject(on_stmts_new) do |stmts, stmt|
2921
+ on_stmts_add(stmts, stmt.nil? ? on_void_stmt : visit(stmt))
2922
+ end
2923
+ end
2924
+
2925
+ # "foo"
2926
+ # ^^^^^
2927
+ def visit_string_node(node)
2928
+ if (content = node.content).empty?
2929
+ bounds(node.location)
2930
+ on_string_literal(on_string_content)
2931
+ elsif (opening = node.opening) == "?"
2932
+ bounds(node.location)
2933
+ on_CHAR("?#{node.content}")
2934
+ elsif opening.start_with?("<<~")
2935
+ heredoc = visit_heredoc_string_node(node.to_interpolated)
2936
+
2937
+ bounds(node.location)
2938
+ on_string_literal(heredoc)
2939
+ else
2940
+ bounds(node.content_loc)
2941
+ tstring_content = on_tstring_content(content)
2942
+
2943
+ bounds(node.location)
2944
+ on_string_literal(on_string_add(on_string_content, tstring_content))
2945
+ end
2946
+ end
2947
+
2948
+ # Ripper gives back the escaped string content but strips out the common
2949
+ # leading whitespace. Prism gives back the unescaped string content and
2950
+ # a location for the escaped string content. Unfortunately these don't
2951
+ # work well together, so here we need to re-derive the common leading
2952
+ # whitespace.
2953
+ private def visit_heredoc_node_whitespace(parts)
2954
+ common_whitespace = nil
2955
+ dedent_next = true
2956
+
2957
+ parts.each do |part|
2958
+ if part.is_a?(StringNode)
2959
+ if dedent_next && !(content = part.content).chomp.empty?
2960
+ common_whitespace = [
2961
+ common_whitespace || Float::INFINITY,
2962
+ content[/\A\s*/].each_char.inject(0) do |part_whitespace, char|
2963
+ char == "\t" ? ((part_whitespace / 8 + 1) * 8) : (part_whitespace + 1)
2964
+ end
2965
+ ].min
466
2966
  end
2967
+
2968
+ dedent_next = true
467
2969
  else
468
- raise NotImplementedError, "operator other than . or &. for call: #{operator.inspect}"
2970
+ dedent_next = false
469
2971
  end
470
2972
  end
2973
+
2974
+ common_whitespace || 0
471
2975
  end
472
2976
 
473
- # Visit a list of elements, like the elements of an array or arguments.
474
- def visit_elements(elements)
475
- bounds(elements.first.location)
476
- elements.inject(on_args_new) do |args, element|
477
- on_args_add(args, visit(element))
2977
+ # Visit a string that is expressed using a <<~ heredoc.
2978
+ private def visit_heredoc_node(parts, base)
2979
+ common_whitespace = visit_heredoc_node_whitespace(parts)
2980
+
2981
+ if common_whitespace == 0
2982
+ bounds(parts.first.location)
2983
+
2984
+ string = []
2985
+ result = base
2986
+
2987
+ parts.each do |part|
2988
+ if part.is_a?(StringNode)
2989
+ if string.empty?
2990
+ string = [part]
2991
+ else
2992
+ string << part
2993
+ end
2994
+ else
2995
+ unless string.empty?
2996
+ bounds(string[0].location)
2997
+ result = yield result, on_tstring_content(string.map(&:content).join)
2998
+ string = []
2999
+ end
3000
+
3001
+ result = yield result, visit(part)
3002
+ end
3003
+ end
3004
+
3005
+ unless string.empty?
3006
+ bounds(string[0].location)
3007
+ result = yield result, on_tstring_content(string.map(&:content).join)
3008
+ end
3009
+
3010
+ result
3011
+ else
3012
+ bounds(parts.first.location)
3013
+ result =
3014
+ parts.inject(base) do |string_content, part|
3015
+ yield string_content, visit_string_content(part)
3016
+ end
3017
+
3018
+ bounds(parts.first.location)
3019
+ on_heredoc_dedent(result, common_whitespace)
478
3020
  end
479
3021
  end
480
3022
 
481
- # Visit an InterpolatedStringNode or an InterpolatedSymbolNode node.
482
- def visit_enumerated_node(node)
483
- parts = node.parts.map do |part|
484
- case part
485
- when StringNode
486
- bounds(part.content_loc)
487
- on_tstring_content(part.content)
488
- when EmbeddedStatementsNode
489
- on_string_embexpr(visit(part))
490
- else
491
- raise NotImplementedError, "Unexpected node type in visit_enumerated_node"
3023
+ # Visit a heredoc node that is representing a string.
3024
+ private def visit_heredoc_string_node(node)
3025
+ bounds(node.opening_loc)
3026
+ on_heredoc_beg(node.opening)
3027
+
3028
+ bounds(node.location)
3029
+ result =
3030
+ visit_heredoc_node(node.parts, on_string_content) do |parts, part|
3031
+ on_string_add(parts, part)
3032
+ end
3033
+
3034
+ bounds(node.closing_loc)
3035
+ on_heredoc_end(node.closing)
3036
+
3037
+ result
3038
+ end
3039
+
3040
+ # Visit a heredoc node that is representing an xstring.
3041
+ private def visit_heredoc_x_string_node(node)
3042
+ bounds(node.opening_loc)
3043
+ on_heredoc_beg(node.opening)
3044
+
3045
+ bounds(node.location)
3046
+ result =
3047
+ visit_heredoc_node(node.parts, on_xstring_new) do |parts, part|
3048
+ on_xstring_add(parts, part)
492
3049
  end
3050
+
3051
+ bounds(node.closing_loc)
3052
+ on_heredoc_end(node.closing)
3053
+
3054
+ result
3055
+ end
3056
+
3057
+ # super(foo)
3058
+ # ^^^^^^^^^^
3059
+ def visit_super_node(node)
3060
+ arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.rparen_loc || node.location))
3061
+
3062
+ if !node.lparen_loc.nil?
3063
+ bounds(node.lparen_loc)
3064
+ arguments = on_arg_paren(arguments)
493
3065
  end
494
3066
 
495
- parts.inject(on_string_content) do |items, item|
496
- on_string_add(items, item)
3067
+ bounds(node.location)
3068
+ call = on_super(arguments)
3069
+
3070
+ if block.nil?
3071
+ call
3072
+ else
3073
+ bounds(node.block.location)
3074
+ on_method_add_block(call, block)
497
3075
  end
498
3076
  end
499
3077
 
500
- # Visit an operation-and-assign node, such as +=.
501
- def visit_binary_op_assign(node, operator: node.operator)
502
- bounds(node.name_loc)
503
- ident_val = on_ident(node.name.to_s)
3078
+ # :foo
3079
+ # ^^^^
3080
+ def visit_symbol_node(node)
3081
+ if (opening = node.opening)&.match?(/^%s|['"]:?$/)
3082
+ bounds(node.value_loc)
3083
+ content = on_string_content
504
3084
 
505
- bounds(node.operator_loc)
506
- op_val = on_op(operator)
3085
+ if !(value = node.value).empty?
3086
+ content = on_string_add(content, on_tstring_content(value))
3087
+ end
507
3088
 
508
- on_opassign(on_var_field(ident_val), op_val, visit(node.value))
3089
+ on_dyna_symbol(content)
3090
+ elsif (closing = node.closing) == ":"
3091
+ bounds(node.location)
3092
+ on_label("#{node.value}:")
3093
+ elsif opening.nil? && node.closing_loc.nil?
3094
+ bounds(node.value_loc)
3095
+ on_symbol_literal(visit_token(node.value))
3096
+ else
3097
+ bounds(node.value_loc)
3098
+ on_symbol_literal(on_symbol(visit_token(node.value)))
3099
+ end
509
3100
  end
510
3101
 
511
- # In Prism this is a CallNode with :[] as the operator.
512
- # In Ripper it's an :aref.
513
- def visit_aref_node(node)
514
- first_arg_val = visit(node.arguments.arguments[0])
515
- args_val = on_args_add_block(on_args_add(on_args_new, first_arg_val), false)
516
- on_aref(visit(node.receiver), args_val)
3102
+ # true
3103
+ # ^^^^
3104
+ def visit_true_node(node)
3105
+ bounds(node.location)
3106
+ on_var_ref(on_kw("true"))
517
3107
  end
518
3108
 
519
- # In Prism this is a CallNode with :[]= as the operator.
520
- # In Ripper it's an :aref_field.
521
- def visit_aref_field_node(node)
522
- first_arg_val = visit(node.arguments.arguments[0])
523
- args_val = on_args_add_block(on_args_add(on_args_new, first_arg_val), false)
524
- assign_val = visit(node.arguments.arguments[1])
525
- on_assign(on_aref_field(visit(node.receiver), args_val), assign_val)
3109
+ # undef foo
3110
+ # ^^^^^^^^^
3111
+ def visit_undef_node(node)
3112
+ names = visit_all(node.names)
3113
+
3114
+ bounds(node.location)
3115
+ on_undef(names)
526
3116
  end
527
3117
 
528
- # In an alias statement Ripper will emit @kw instead of @ident if the object
529
- # being aliased is a Ruby keyword. For instance, in the line "alias :foo :if",
530
- # the :if is treated as a lexer keyword. So we need to know what symbols are
531
- # also keywords.
532
- RUBY_KEYWORDS = [
533
- "alias",
534
- "and",
535
- "begin",
536
- "BEGIN",
537
- "break",
538
- "case",
539
- "class",
540
- "def",
541
- "defined?",
542
- "do",
543
- "else",
544
- "elsif",
545
- "end",
546
- "END",
547
- "ensure",
548
- "false",
549
- "for",
550
- "if",
551
- "in",
552
- "module",
553
- "next",
554
- "nil",
555
- "not",
556
- "or",
557
- "redo",
558
- "rescue",
559
- "retry",
560
- "return",
561
- "self",
562
- "super",
563
- "then",
564
- "true",
565
- "undef",
566
- "unless",
567
- "until",
568
- "when",
569
- "while",
570
- "yield",
571
- "__ENCODING__",
572
- "__FILE__",
573
- "__LINE__",
574
- ]
3118
+ # unless foo; bar end
3119
+ # ^^^^^^^^^^^^^^^^^^^
3120
+ #
3121
+ # bar unless foo
3122
+ # ^^^^^^^^^^^^^^
3123
+ def visit_unless_node(node)
3124
+ if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
3125
+ predicate = visit(node.predicate)
3126
+ statements =
3127
+ if node.statements.nil?
3128
+ bounds(node.location)
3129
+ on_stmts_add(on_stmts_new, on_void_stmt)
3130
+ else
3131
+ visit(node.statements)
3132
+ end
3133
+ consequent = visit(node.consequent)
575
3134
 
576
- # Ripper has several methods of emitting a symbol literal. Inside an alias
577
- # sometimes it suppresses the [:symbol] wrapper around ident. If the symbol
578
- # is also the name of a keyword (e.g. :if) it will emit a :@kw wrapper, not
579
- # an :@ident wrapper, with similar treatment for constants and operators.
580
- def visit_symbol_literal_node(node, no_symbol_wrapper: false)
581
- if (opening = node.opening) && (['"', "'"].include?(opening[-1]) || opening.start_with?("%s"))
582
- bounds(node.value_loc)
583
- str_val = node.value.to_s
584
- if str_val == ""
585
- return on_dyna_symbol(on_string_content)
3135
+ bounds(node.location)
3136
+ on_unless(predicate, statements, consequent)
3137
+ else
3138
+ statements = visit(node.statements.body.first)
3139
+ predicate = visit(node.predicate)
3140
+
3141
+ bounds(node.location)
3142
+ on_unless_mod(predicate, statements)
3143
+ end
3144
+ end
3145
+
3146
+ # until foo; bar end
3147
+ # ^^^^^^^^^^^^^^^^^
3148
+ #
3149
+ # bar until foo
3150
+ # ^^^^^^^^^^^^^
3151
+ def visit_until_node(node)
3152
+ if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
3153
+ predicate = visit(node.predicate)
3154
+ statements =
3155
+ if node.statements.nil?
3156
+ bounds(node.location)
3157
+ on_stmts_add(on_stmts_new, on_void_stmt)
3158
+ else
3159
+ visit(node.statements)
3160
+ end
3161
+
3162
+ bounds(node.location)
3163
+ on_until(predicate, statements)
3164
+ else
3165
+ statements = visit(node.statements.body.first)
3166
+ predicate = visit(node.predicate)
3167
+
3168
+ bounds(node.location)
3169
+ on_until_mod(predicate, statements)
3170
+ end
3171
+ end
3172
+
3173
+ # case foo; when bar; end
3174
+ # ^^^^^^^^^^^^^
3175
+ def visit_when_node(node)
3176
+ # This is a special case where we're not going to call on_when directly
3177
+ # because we don't have access to the consequent. Instead, we'll return
3178
+ # the component parts and let the parent node handle it.
3179
+ conditions = visit_arguments(node.conditions)
3180
+ statements =
3181
+ if node.statements.nil?
3182
+ bounds(node.location)
3183
+ on_stmts_add(on_stmts_new, on_void_stmt)
586
3184
  else
587
- tstring_val = on_tstring_content(str_val)
588
- return on_dyna_symbol(on_string_add(on_string_content, tstring_val))
3185
+ visit(node.statements)
3186
+ end
3187
+
3188
+ [conditions, statements]
3189
+ end
3190
+
3191
+ # while foo; bar end
3192
+ # ^^^^^^^^^^^^^^^^^^
3193
+ #
3194
+ # bar while foo
3195
+ # ^^^^^^^^^^^^^
3196
+ def visit_while_node(node)
3197
+ if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
3198
+ predicate = visit(node.predicate)
3199
+ statements =
3200
+ if node.statements.nil?
3201
+ bounds(node.location)
3202
+ on_stmts_add(on_stmts_new, on_void_stmt)
3203
+ else
3204
+ visit(node.statements)
3205
+ end
3206
+
3207
+ bounds(node.location)
3208
+ on_while(predicate, statements)
3209
+ else
3210
+ statements = visit(node.statements.body.first)
3211
+ predicate = visit(node.predicate)
3212
+
3213
+ bounds(node.location)
3214
+ on_while_mod(predicate, statements)
3215
+ end
3216
+ end
3217
+
3218
+ # `foo`
3219
+ # ^^^^^
3220
+ def visit_x_string_node(node)
3221
+ if node.unescaped.empty?
3222
+ bounds(node.location)
3223
+ on_xstring_literal(on_xstring_new)
3224
+ elsif node.opening.start_with?("<<~")
3225
+ heredoc = visit_heredoc_x_string_node(node.to_interpolated)
3226
+
3227
+ bounds(node.location)
3228
+ on_xstring_literal(heredoc)
3229
+ else
3230
+ bounds(node.content_loc)
3231
+ content = on_tstring_content(node.content)
3232
+
3233
+ bounds(node.location)
3234
+ on_xstring_literal(on_xstring_add(on_xstring_new, content))
3235
+ end
3236
+ end
3237
+
3238
+ # yield
3239
+ # ^^^^^
3240
+ #
3241
+ # yield 1
3242
+ # ^^^^^^^
3243
+ def visit_yield_node(node)
3244
+ if node.arguments.nil? && node.lparen_loc.nil?
3245
+ bounds(node.location)
3246
+ on_yield0
3247
+ else
3248
+ arguments =
3249
+ if node.arguments.nil?
3250
+ bounds(node.location)
3251
+ on_args_new
3252
+ else
3253
+ visit(node.arguments)
3254
+ end
3255
+
3256
+ unless node.lparen_loc.nil?
3257
+ bounds(node.lparen_loc)
3258
+ arguments = on_paren(arguments)
589
3259
  end
3260
+
3261
+ bounds(node.location)
3262
+ on_yield(arguments)
590
3263
  end
3264
+ end
3265
+
3266
+ private
3267
+
3268
+ # Lazily initialize the parse result.
3269
+ def result
3270
+ @result ||= Prism.parse(source)
3271
+ end
3272
+
3273
+ ##########################################################################
3274
+ # Helpers
3275
+ ##########################################################################
3276
+
3277
+ # Returns true if there is a comma between the two locations.
3278
+ def trailing_comma?(left, right)
3279
+ source.byteslice(left.end_offset...right.start_offset).include?(",")
3280
+ end
3281
+
3282
+ # Returns true if there is a semicolon between the two locations.
3283
+ def void_stmt?(left, right, allow_newline)
3284
+ pattern = allow_newline ? /[;\n]/ : /;/
3285
+ source.byteslice(left.end_offset...right.start_offset).match?(pattern)
3286
+ end
591
3287
 
592
- bounds(node.value_loc)
593
- node_name = node.value.to_s
594
- if RUBY_KEYWORDS.include?(node_name)
595
- token_val = on_kw(node_name)
596
- elsif node_name.length == 0
597
- raise NotImplementedError
598
- elsif /[[:upper:]]/.match(node_name[0])
599
- token_val = on_const(node_name)
600
- elsif /[[:punct:]]/.match(node_name[0])
601
- token_val = on_op(node_name)
3288
+ # Visit the string content of a particular node. This method is used to
3289
+ # split into the various token types.
3290
+ def visit_token(token, allow_keywords = true)
3291
+ case token
3292
+ when "."
3293
+ on_period(token)
3294
+ when "`"
3295
+ on_backtick(token)
3296
+ when *(allow_keywords ? KEYWORDS : [])
3297
+ on_kw(token)
3298
+ when /^_/
3299
+ on_ident(token)
3300
+ when /^[[:upper:]]\w*$/
3301
+ on_const(token)
3302
+ when /^@@/
3303
+ on_cvar(token)
3304
+ when /^@/
3305
+ on_ivar(token)
3306
+ when /^\$/
3307
+ on_gvar(token)
3308
+ when /^[[:punct:]]/
3309
+ on_op(token)
602
3310
  else
603
- token_val = on_ident(node_name)
3311
+ on_ident(token)
604
3312
  end
605
- sym_val = no_symbol_wrapper ? token_val : on_symbol(token_val)
606
- on_symbol_literal(sym_val)
607
3313
  end
608
3314
 
609
3315
  # Visit a node that represents a number. We need to explicitly handle the
610
3316
  # unary - operator.
611
- def visit_number(node)
3317
+ def visit_number_node(node)
612
3318
  slice = node.slice
613
3319
  location = node.location
614
3320
 
615
3321
  if slice[0] == "-"
616
- bounds_values(location.start_line, location.start_column + 1)
3322
+ bounds(location.copy(start_offset: location.start_offset + 1))
617
3323
  value = yield slice[1..-1]
618
3324
 
619
3325
  bounds(node.location)
620
- on_unary(visit_unary_operator(:-@), value)
3326
+ on_unary(:-@, value)
621
3327
  else
622
3328
  bounds(location)
623
3329
  yield slice
624
3330
  end
625
3331
  end
626
3332
 
627
- if RUBY_ENGINE == "jruby" && Gem::Version.new(JRUBY_VERSION) < Gem::Version.new("9.4.6.0")
628
- # JRuby before 9.4.6.0 uses :- for unary minus instead of :-@
629
- def visit_unary_operator(value)
630
- value == :-@ ? :- : value
631
- end
632
- else
633
- # For most Rubies and JRuby after 9.4.6.0 this is a no-op.
634
- def visit_unary_operator(value)
635
- value
636
- end
637
- end
638
-
639
- if RUBY_ENGINE == "jruby"
640
- # For JRuby, "no block" in an on_block_var is nil
641
- def no_block_value
642
- nil
643
- end
644
- else
645
- # For CRuby et al, "no block" in an on_block_var is false
646
- def no_block_value
647
- false
3333
+ # Visit a node that represents a write value. This is used to handle the
3334
+ # special case of an implicit array that is generated without brackets.
3335
+ def visit_write_value(node)
3336
+ if node.is_a?(ArrayNode) && node.opening_loc.nil?
3337
+ elements = node.elements
3338
+ length = elements.length
3339
+
3340
+ bounds(elements.first.location)
3341
+ elements.each_with_index.inject((elements.first.is_a?(SplatNode) && length == 1) ? on_mrhs_new : on_args_new) do |args, (element, index)|
3342
+ arg = visit(element)
3343
+ bounds(element.location)
3344
+
3345
+ if index == length - 1
3346
+ if element.is_a?(SplatNode)
3347
+ mrhs = index == 0 ? args : on_mrhs_new_from_args(args)
3348
+ on_mrhs_add_star(mrhs, arg)
3349
+ else
3350
+ on_mrhs_add(on_mrhs_new_from_args(args), arg)
3351
+ end
3352
+ else
3353
+ case element
3354
+ when BlockArgumentNode
3355
+ on_args_add_block(args, arg)
3356
+ when SplatNode
3357
+ on_args_add_star(args, arg)
3358
+ else
3359
+ on_args_add(args, arg)
3360
+ end
3361
+ end
3362
+ end
3363
+ else
3364
+ visit(node)
648
3365
  end
649
3366
  end
650
3367
 
651
- # Visit a binary operator node like an AndNode or OrNode
652
- def visit_binary_operator(node)
653
- left_val = visit(node.left)
654
- right_val = visit(node.right)
655
- on_binary(left_val, node.operator.to_sym, right_val)
656
- end
657
-
658
3368
  # This method is responsible for updating lineno and column information
659
3369
  # to reflect the current node.
660
3370
  #
@@ -665,31 +3375,71 @@ module Prism
665
3375
  @column = location.start_column
666
3376
  end
667
3377
 
668
- # If we need to do something unusual, we can directly update the line number
669
- # and column to reflect the current node.
670
- def bounds_values(lineno, column)
671
- @lineno = lineno
672
- @column = column
3378
+ ##########################################################################
3379
+ # Ripper interface
3380
+ ##########################################################################
3381
+
3382
+ # :stopdoc:
3383
+ def _dispatch_0; end
3384
+ def _dispatch_1(_); end
3385
+ def _dispatch_2(_, _); end
3386
+ def _dispatch_3(_, _, _); end
3387
+ def _dispatch_4(_, _, _, _); end
3388
+ def _dispatch_5(_, _, _, _, _); end
3389
+ def _dispatch_7(_, _, _, _, _, _, _); end
3390
+ # :startdoc:
3391
+
3392
+ #
3393
+ # Parser Events
3394
+ #
3395
+
3396
+ PARSER_EVENT_TABLE.each do |id, arity|
3397
+ alias_method "on_#{id}", "_dispatch_#{arity}"
673
3398
  end
674
3399
 
675
- # Lazily initialize the parse result.
676
- def result
677
- @result ||= Prism.parse(source)
3400
+ # This method is called when weak warning is produced by the parser.
3401
+ # +fmt+ and +args+ is printf style.
3402
+ def warn(fmt, *args)
3403
+ end
3404
+
3405
+ # This method is called when strong warning is produced by the parser.
3406
+ # +fmt+ and +args+ is printf style.
3407
+ def warning(fmt, *args)
3408
+ end
3409
+
3410
+ # This method is called when the parser found syntax error.
3411
+ def compile_error(msg)
678
3412
  end
679
3413
 
680
- def _dispatch0; end # :nodoc:
681
- def _dispatch1(_); end # :nodoc:
682
- def _dispatch2(_, _); end # :nodoc:
683
- def _dispatch3(_, _, _); end # :nodoc:
684
- def _dispatch4(_, _, _, _); end # :nodoc:
685
- def _dispatch5(_, _, _, _, _); end # :nodoc:
686
- def _dispatch7(_, _, _, _, _, _, _); end # :nodoc:
3414
+ #
3415
+ # Scanner Events
3416
+ #
3417
+
3418
+ SCANNER_EVENTS.each do |id|
3419
+ alias_method "on_#{id}", :_dispatch_1
3420
+ end
687
3421
 
688
- alias_method :on_parse_error, :_dispatch1
689
- alias_method :on_magic_comment, :_dispatch2
3422
+ # This method is provided by the Ripper C extension. It is called when a
3423
+ # string needs to be dedented because of a tilde heredoc. It is expected
3424
+ # that it will modify the string in place and return the number of bytes
3425
+ # that were removed.
3426
+ def dedent_string(string, width)
3427
+ whitespace = 0
3428
+ cursor = 0
3429
+
3430
+ while cursor < string.length && string[cursor].match?(/\s/) && whitespace < width
3431
+ if string[cursor] == "\t"
3432
+ whitespace = ((whitespace / 8 + 1) * 8)
3433
+ break if whitespace > width
3434
+ else
3435
+ whitespace += 1
3436
+ end
3437
+
3438
+ cursor += 1
3439
+ end
690
3440
 
691
- (::Ripper::SCANNER_EVENT_TABLE.merge(::Ripper::PARSER_EVENT_TABLE)).each do |event, arity|
692
- alias_method :"on_#{event}", :"_dispatch#{arity}"
3441
+ string.replace(string[cursor..])
3442
+ cursor
693
3443
  end
694
3444
  end
695
3445
  end