prism 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -1
  3. data/README.md +2 -1
  4. data/docs/releasing.md +67 -17
  5. data/docs/ruby_parser_translation.md +19 -0
  6. data/docs/serialization.md +2 -0
  7. data/ext/prism/api_node.c +1982 -1538
  8. data/ext/prism/extension.c +12 -7
  9. data/ext/prism/extension.h +2 -2
  10. data/include/prism/diagnostic.h +3 -4
  11. data/include/prism/encoding.h +7 -0
  12. data/include/prism/util/pm_constant_pool.h +1 -1
  13. data/include/prism/util/pm_newline_list.h +4 -3
  14. data/include/prism/util/pm_strpbrk.h +4 -1
  15. data/include/prism/version.h +2 -2
  16. data/lib/prism/desugar_compiler.rb +225 -80
  17. data/lib/prism/dsl.rb +302 -299
  18. data/lib/prism/ffi.rb +103 -77
  19. data/lib/prism/lex_compat.rb +1 -0
  20. data/lib/prism/node.rb +3624 -2114
  21. data/lib/prism/node_ext.rb +25 -2
  22. data/lib/prism/parse_result.rb +56 -19
  23. data/lib/prism/serialize.rb +605 -303
  24. data/lib/prism/translation/parser/compiler.rb +1 -1
  25. data/lib/prism/translation/parser/rubocop.rb +11 -3
  26. data/lib/prism/translation/parser.rb +25 -12
  27. data/lib/prism/translation/parser33.rb +12 -0
  28. data/lib/prism/translation/parser34.rb +12 -0
  29. data/lib/prism/translation/ripper.rb +696 -0
  30. data/lib/prism/translation/ruby_parser.rb +1521 -0
  31. data/lib/prism/translation.rb +3 -3
  32. data/lib/prism.rb +0 -1
  33. data/prism.gemspec +6 -2
  34. data/src/diagnostic.c +10 -11
  35. data/src/encoding.c +16 -17
  36. data/src/options.c +7 -2
  37. data/src/prettyprint.c +3 -3
  38. data/src/prism.c +172 -97
  39. data/src/serialize.c +24 -13
  40. data/src/token_type.c +3 -3
  41. data/src/util/pm_constant_pool.c +1 -1
  42. data/src/util/pm_newline_list.c +6 -3
  43. data/src/util/pm_strpbrk.c +122 -14
  44. metadata +8 -4
  45. data/lib/prism/ripper_compat.rb +0 -285
@@ -0,0 +1,696 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ripper"
4
+
5
+ module Prism
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!
10
+ #
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.
14
+ #
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.
18
+ #
19
+ # To use this class, you treat `Prism::Translation::Ripper` effectively as you would
20
+ # treat the `Ripper` class.
21
+ 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
26
+
27
+ ::Ripper::PARSER_EVENTS.each do |event|
28
+ define_method(:"on_#{event}") do |*args|
29
+ [event, *args]
30
+ end
31
+ end
32
+
33
+ ::Ripper::SCANNER_EVENTS.each do |event|
34
+ define_method(:"on_#{event}") do |value|
35
+ [:"@#{event}", value, [lineno, column]]
36
+ end
37
+ end
38
+ end
39
+
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
45
+
46
+ def _dispatch_event_new # :nodoc:
47
+ []
48
+ end
49
+
50
+ def _dispatch_event_push(list, item) # :nodoc:
51
+ list << item
52
+ list
53
+ end
54
+
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
61
+ end
62
+ end
63
+ end
64
+
65
+ # The source that is being parsed.
66
+ attr_reader :source
67
+
68
+ # The current line number of the parser.
69
+ attr_reader :lineno
70
+
71
+ # The current column number of the parser.
72
+ attr_reader :column
73
+
74
+ # Create a new Translation::Ripper object with the given source.
75
+ def initialize(source)
76
+ @source = source
77
+ @result = nil
78
+ @lineno = nil
79
+ @column = nil
80
+ end
81
+
82
+ ############################################################################
83
+ # Public interface
84
+ ############################################################################
85
+
86
+ # True if the parser encountered an error during parsing.
87
+ def error?
88
+ result.failure?
89
+ end
90
+
91
+ # Parse the source and return the result.
92
+ def parse
93
+ result.magic_comments.each do |magic_comment|
94
+ on_magic_comment(magic_comment.key, magic_comment.value)
95
+ end
96
+
97
+ if error?
98
+ result.errors.each do |error|
99
+ on_parse_error(error.message)
100
+ end
101
+
102
+ nil
103
+ else
104
+ result.value.accept(self)
105
+ end
106
+ end
107
+
108
+ ############################################################################
109
+ # Visitor methods
110
+ ############################################################################
111
+
112
+ # Visit an ArrayNode node.
113
+ def visit_array_node(node)
114
+ elements = visit_elements(node.elements) unless node.elements.empty?
115
+ bounds(node.location)
116
+ on_array(elements)
117
+ end
118
+
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
133
+
134
+ if node.opening_loc.nil?
135
+ return visit_no_paren_call(node)
136
+ end
137
+
138
+ # A non-operator method call with parentheses
139
+
140
+ args = if node.arguments.nil?
141
+ on_arg_paren(nil)
142
+ else
143
+ on_arg_paren(on_args_add_block(visit_elements(node.arguments.arguments), false))
144
+ end
145
+
146
+ bounds(node.message_loc)
147
+ ident_val = on_ident(node.message)
148
+
149
+ 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)
153
+
154
+ return on_method_add_block(args_call_val, block_val)
155
+ else
156
+ return args_call_val
157
+ end
158
+ end
159
+
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
171
+
172
+ # Visit a LocalVariableOrWriteNode.
173
+ def visit_local_variable_or_write_node(node)
174
+ visit_binary_op_assign(node)
175
+ end
176
+
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
181
+
182
+ # Visit a LocalVariableReadNode.
183
+ def visit_local_variable_read_node(node)
184
+ bounds(node.location)
185
+ ident_val = on_ident(node.slice)
186
+
187
+ on_var_ref(ident_val)
188
+ end
189
+
190
+ # Visit a BlockNode.
191
+ def visit_block_node(node)
192
+ params_val = node.parameters.nil? ? nil : visit(node.parameters)
193
+
194
+ body_val = node.body.nil? ? on_stmts_add(on_stmts_new, on_void_stmt) : visit(node.body)
195
+
196
+ on_brace_block(params_val, body_val)
197
+ end
198
+
199
+ # Visit a BlockParametersNode.
200
+ def visit_block_parameters_node(node)
201
+ on_block_var(visit(node.parameters), no_block_value)
202
+ end
203
+
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
210
+
211
+ # Visit a RequiredParameterNode.
212
+ def visit_required_parameter_node(node)
213
+ bounds(node.location)
214
+ on_ident(node.name.to_s)
215
+ end
216
+
217
+ # Visit a BreakNode.
218
+ def visit_break_node(node)
219
+ return on_break(on_args_new) if node.arguments.nil?
220
+
221
+ args_val = visit_elements(node.arguments.arguments)
222
+ on_break(on_args_add_block(args_val, false))
223
+ end
224
+
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.
231
+
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)
239
+ else
240
+ old_name_val = visit(node.old_name)
241
+ end
242
+
243
+ on_alias(new_name_val, old_name_val)
244
+ end
245
+
246
+ # Visit an AliasGlobalVariableNode.
247
+ def visit_alias_global_variable_node(node)
248
+ on_var_alias(visit(node.new_name), visit(node.old_name))
249
+ end
250
+
251
+ # Visit a GlobalVariableReadNode.
252
+ def visit_global_variable_read_node(node)
253
+ bounds(node.location)
254
+ on_gvar(node.name.to_s)
255
+ end
256
+
257
+ # Visit a BackReferenceReadNode.
258
+ def visit_back_reference_read_node(node)
259
+ bounds(node.location)
260
+ on_backref(node.name.to_s)
261
+ end
262
+
263
+ # Visit an AndNode.
264
+ def visit_and_node(node)
265
+ visit_binary_operator(node)
266
+ end
267
+
268
+ # Visit an OrNode.
269
+ def visit_or_node(node)
270
+ visit_binary_operator(node)
271
+ end
272
+
273
+ # Visit a TrueNode.
274
+ def visit_true_node(node)
275
+ bounds(node.location)
276
+ on_var_ref(on_kw("true"))
277
+ end
278
+
279
+ # Visit a FalseNode.
280
+ def visit_false_node(node)
281
+ bounds(node.location)
282
+ on_var_ref(on_kw("false"))
283
+ end
284
+
285
+ # Visit a FloatNode node.
286
+ def visit_float_node(node)
287
+ visit_number(node) { |text| on_float(text) }
288
+ end
289
+
290
+ # Visit a ImaginaryNode node.
291
+ def visit_imaginary_node(node)
292
+ visit_number(node) { |text| on_imaginary(text) }
293
+ end
294
+
295
+ # Visit an IntegerNode node.
296
+ def visit_integer_node(node)
297
+ visit_number(node) { |text| on_int(text) }
298
+ end
299
+
300
+ # Visit a ParenthesesNode node.
301
+ def visit_parentheses_node(node)
302
+ body =
303
+ if node.body.nil?
304
+ on_stmts_add(on_stmts_new, on_void_stmt)
305
+ else
306
+ visit(node.body)
307
+ end
308
+
309
+ bounds(node.location)
310
+ on_paren(body)
311
+ end
312
+
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))
319
+ end
320
+
321
+ # Visit a RescueNode node.
322
+ def visit_rescue_node(node)
323
+ visit(node.statements)
324
+ end
325
+
326
+ # Visit a ProgramNode node.
327
+ def visit_program_node(node)
328
+ statements = visit(node.statements)
329
+ bounds(node.location)
330
+ on_program(statements)
331
+ end
332
+
333
+ # Visit a RangeNode node.
334
+ def visit_range_node(node)
335
+ left = visit(node.left)
336
+ right = visit(node.right)
337
+
338
+ bounds(node.location)
339
+ if node.exclude_end?
340
+ on_dot3(left, right)
341
+ else
342
+ on_dot2(left, right)
343
+ end
344
+ end
345
+
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
369
+
370
+ # Visit an EmbeddedStatementsNode node.
371
+ def visit_embedded_statements_node(node)
372
+ visit(node.statements)
373
+ end
374
+
375
+ # Visit a SymbolNode node.
376
+ def visit_symbol_node(node)
377
+ visit_symbol_literal_node(node)
378
+ end
379
+
380
+ # Visit an InterpolatedSymbolNode node.
381
+ def visit_interpolated_symbol_node(node)
382
+ on_dyna_symbol(visit_enumerated_node(node))
383
+ end
384
+
385
+ # Visit a StatementsNode node.
386
+ def visit_statements_node(node)
387
+ bounds(node.location)
388
+ node.body.inject(on_stmts_new) do |stmts, stmt|
389
+ on_stmts_add(stmts, visit(stmt))
390
+ end
391
+ end
392
+
393
+ ############################################################################
394
+ # Entrypoints for subclasses
395
+ ############################################################################
396
+
397
+ # This is a convenience method that runs the SexpBuilder subclass parser.
398
+ def self.sexp_raw(source)
399
+ SexpBuilder.new(source).parse
400
+ end
401
+
402
+ # This is a convenience method that runs the SexpBuilderPP subclass parser.
403
+ def self.sexp(source)
404
+ SexpBuilderPP.new(source).parse
405
+ end
406
+
407
+ private
408
+
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)
421
+ 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)
428
+
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
435
+ else
436
+ on_args_add_block(visit_elements(node.arguments.arguments))
437
+ 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
+ else
441
+ if node.arguments.nil?
442
+ return on_command(ident_val, nil)
443
+ else
444
+ args = on_args_add_block(visit_elements(node.arguments.arguments), false)
445
+ return on_command(ident_val, args)
446
+ end
447
+ end
448
+ else
449
+ operator = node.call_operator_loc.slice
450
+ if operator == "." || operator == "&."
451
+ left_val = visit(node.receiver)
452
+
453
+ bounds(node.call_operator_loc)
454
+ operator_val = operator == "." ? on_period(node.call_operator) : on_op(node.call_operator)
455
+
456
+ bounds(node.message_loc)
457
+ right_val = on_ident(node.message)
458
+
459
+ call_val = on_call(left_val, operator_val, right_val)
460
+
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
466
+ end
467
+ else
468
+ raise NotImplementedError, "operator other than . or &. for call: #{operator.inspect}"
469
+ end
470
+ end
471
+ end
472
+
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))
478
+ end
479
+ end
480
+
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"
492
+ end
493
+ end
494
+
495
+ parts.inject(on_string_content) do |items, item|
496
+ on_string_add(items, item)
497
+ end
498
+ end
499
+
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)
504
+
505
+ bounds(node.operator_loc)
506
+ op_val = on_op(operator)
507
+
508
+ on_opassign(on_var_field(ident_val), op_val, visit(node.value))
509
+ end
510
+
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)
517
+ end
518
+
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)
526
+ end
527
+
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
+ ]
575
+
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)
586
+ else
587
+ tstring_val = on_tstring_content(str_val)
588
+ return on_dyna_symbol(on_string_add(on_string_content, tstring_val))
589
+ end
590
+ end
591
+
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)
602
+ else
603
+ token_val = on_ident(node_name)
604
+ end
605
+ sym_val = no_symbol_wrapper ? token_val : on_symbol(token_val)
606
+ on_symbol_literal(sym_val)
607
+ end
608
+
609
+ # Visit a node that represents a number. We need to explicitly handle the
610
+ # unary - operator.
611
+ def visit_number(node)
612
+ slice = node.slice
613
+ location = node.location
614
+
615
+ if slice[0] == "-"
616
+ bounds_values(location.start_line, location.start_column + 1)
617
+ value = yield slice[1..-1]
618
+
619
+ bounds(node.location)
620
+ on_unary(visit_unary_operator(:-@), value)
621
+ else
622
+ bounds(location)
623
+ yield slice
624
+ end
625
+ end
626
+
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
648
+ end
649
+ end
650
+
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
+ # This method is responsible for updating lineno and column information
659
+ # to reflect the current node.
660
+ #
661
+ # This method could be drastically improved with some caching on the start
662
+ # of every line, but for now it's good enough.
663
+ def bounds(location)
664
+ @lineno = location.start_line
665
+ @column = location.start_column
666
+ end
667
+
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
673
+ end
674
+
675
+ # Lazily initialize the parse result.
676
+ def result
677
+ @result ||= Prism.parse(source)
678
+ end
679
+
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:
687
+
688
+ alias_method :on_parse_error, :_dispatch1
689
+ alias_method :on_magic_comment, :_dispatch2
690
+
691
+ (::Ripper::SCANNER_EVENT_TABLE.merge(::Ripper::PARSER_EVENT_TABLE)).each do |event, arity|
692
+ alias_method :"on_#{event}", :"_dispatch#{arity}"
693
+ end
694
+ end
695
+ end
696
+ end