prism 2.0.0.rc1-x86_64-linux-gnu

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 (216) hide show
  1. checksums.yaml +7 -0
  2. data/BSDmakefile +58 -0
  3. data/CHANGELOG.md +870 -0
  4. data/CODE_OF_CONDUCT.md +76 -0
  5. data/CONTRIBUTING.md +58 -0
  6. data/LICENSE.md +7 -0
  7. data/Makefile +145 -0
  8. data/README.md +81 -0
  9. data/config.yml +4752 -0
  10. data/docs/build_system.md +119 -0
  11. data/docs/configuration.md +69 -0
  12. data/docs/cruby_compilation.md +27 -0
  13. data/docs/design.md +53 -0
  14. data/docs/encoding.md +121 -0
  15. data/docs/fuzzing.md +81 -0
  16. data/docs/heredocs.md +36 -0
  17. data/docs/javascript.md +133 -0
  18. data/docs/local_variable_depth.md +229 -0
  19. data/docs/mapping.md +117 -0
  20. data/docs/parser_translation.md +24 -0
  21. data/docs/parsing_rules.md +22 -0
  22. data/docs/releasing.md +147 -0
  23. data/docs/relocation.md +32 -0
  24. data/docs/ripper_translation.md +63 -0
  25. data/docs/ruby_api.md +45 -0
  26. data/docs/ruby_parser_translation.md +19 -0
  27. data/docs/serialization.md +230 -0
  28. data/docs/testing.md +72 -0
  29. data/ext/prism/api_node.c +7005 -0
  30. data/ext/prism/extconf.rb +126 -0
  31. data/ext/prism/extension.c +1783 -0
  32. data/ext/prism/extension.h +19 -0
  33. data/include/prism/arena.h +37 -0
  34. data/include/prism/ast.h +10371 -0
  35. data/include/prism/buffer.h +52 -0
  36. data/include/prism/comments.h +55 -0
  37. data/include/prism/compiler/accel.h +24 -0
  38. data/include/prism/compiler/align.h +36 -0
  39. data/include/prism/compiler/assume.h +25 -0
  40. data/include/prism/compiler/exported.h +24 -0
  41. data/include/prism/compiler/fallthrough.h +22 -0
  42. data/include/prism/compiler/filesystem.h +32 -0
  43. data/include/prism/compiler/flex_array.h +19 -0
  44. data/include/prism/compiler/force_inline.h +21 -0
  45. data/include/prism/compiler/format.h +25 -0
  46. data/include/prism/compiler/inline.h +17 -0
  47. data/include/prism/compiler/nodiscard.h +22 -0
  48. data/include/prism/compiler/nonnull.h +18 -0
  49. data/include/prism/compiler/unused.h +18 -0
  50. data/include/prism/constant_pool.h +81 -0
  51. data/include/prism/diagnostic.h +93 -0
  52. data/include/prism/errors_format.h +53 -0
  53. data/include/prism/excludes.h +29 -0
  54. data/include/prism/integer.h +41 -0
  55. data/include/prism/internal/allocator.h +68 -0
  56. data/include/prism/internal/allocator_debug.h +88 -0
  57. data/include/prism/internal/arena.h +108 -0
  58. data/include/prism/internal/bit.h +42 -0
  59. data/include/prism/internal/buffer.h +91 -0
  60. data/include/prism/internal/char.h +145 -0
  61. data/include/prism/internal/comments.h +53 -0
  62. data/include/prism/internal/constant_pool.h +152 -0
  63. data/include/prism/internal/diagnostic.h +389 -0
  64. data/include/prism/internal/encoding.h +242 -0
  65. data/include/prism/internal/integer.h +68 -0
  66. data/include/prism/internal/isinf.h +16 -0
  67. data/include/prism/internal/line_offset_list.h +34 -0
  68. data/include/prism/internal/list.h +62 -0
  69. data/include/prism/internal/magic_comments.h +23 -0
  70. data/include/prism/internal/memchr.h +15 -0
  71. data/include/prism/internal/node.h +38 -0
  72. data/include/prism/internal/options.h +232 -0
  73. data/include/prism/internal/parser.h +984 -0
  74. data/include/prism/internal/regexp.h +26 -0
  75. data/include/prism/internal/serialize.h +39 -0
  76. data/include/prism/internal/source.h +72 -0
  77. data/include/prism/internal/static_literals.h +100 -0
  78. data/include/prism/internal/stringy.h +35 -0
  79. data/include/prism/internal/strncasecmp.h +18 -0
  80. data/include/prism/internal/strpbrk.h +33 -0
  81. data/include/prism/internal/tokens.h +11 -0
  82. data/include/prism/json.h +32 -0
  83. data/include/prism/line_offset_list.h +61 -0
  84. data/include/prism/magic_comments.h +35 -0
  85. data/include/prism/node.h +94 -0
  86. data/include/prism/options.h +354 -0
  87. data/include/prism/parser.h +404 -0
  88. data/include/prism/prettyprint.h +31 -0
  89. data/include/prism/serialize.h +115 -0
  90. data/include/prism/source.h +155 -0
  91. data/include/prism/stream.h +28 -0
  92. data/include/prism/string_query.h +63 -0
  93. data/include/prism/stringy.h +72 -0
  94. data/include/prism/version.h +38 -0
  95. data/include/prism.h +131 -0
  96. data/lib/prism/3.3/prism.so +0 -0
  97. data/lib/prism/3.4/prism.so +0 -0
  98. data/lib/prism/4.0/prism.so +0 -0
  99. data/lib/prism/compiler.rb +815 -0
  100. data/lib/prism/desugar_compiler.rb +463 -0
  101. data/lib/prism/dispatcher.rb +2383 -0
  102. data/lib/prism/dot_visitor.rb +4848 -0
  103. data/lib/prism/dsl.rb +1369 -0
  104. data/lib/prism/ffi.rb +740 -0
  105. data/lib/prism/inspect_visitor.rb +2425 -0
  106. data/lib/prism/lex_compat.rb +953 -0
  107. data/lib/prism/mutation_compiler.rb +780 -0
  108. data/lib/prism/node.rb +33204 -0
  109. data/lib/prism/node_ext.rb +504 -0
  110. data/lib/prism/node_find.rb +215 -0
  111. data/lib/prism/parse_result/errors.rb +72 -0
  112. data/lib/prism/parse_result/newlines.rb +423 -0
  113. data/lib/prism/parse_result.rb +1221 -0
  114. data/lib/prism/pattern.rb +314 -0
  115. data/lib/prism/polyfill/append_as_bytes.rb +15 -0
  116. data/lib/prism/polyfill/byteindex.rb +13 -0
  117. data/lib/prism/polyfill/scan_byte.rb +14 -0
  118. data/lib/prism/polyfill/unpack1.rb +14 -0
  119. data/lib/prism/polyfill/warn.rb +36 -0
  120. data/lib/prism/reflection.rb +427 -0
  121. data/lib/prism/relocation.rb +567 -0
  122. data/lib/prism/serialize.rb +5308 -0
  123. data/lib/prism/string_query.rb +46 -0
  124. data/lib/prism/translation/parser/builder.rb +70 -0
  125. data/lib/prism/translation/parser/compiler.rb +2226 -0
  126. data/lib/prism/translation/parser/lexer.rb +769 -0
  127. data/lib/prism/translation/parser.rb +376 -0
  128. data/lib/prism/translation/parser_current.rb +26 -0
  129. data/lib/prism/translation/parser_versions.rb +36 -0
  130. data/lib/prism/translation/ripper/filter.rb +53 -0
  131. data/lib/prism/translation/ripper/lexer.rb +133 -0
  132. data/lib/prism/translation/ripper/sexp.rb +118 -0
  133. data/lib/prism/translation/ripper/shim.rb +7 -0
  134. data/lib/prism/translation/ripper.rb +4278 -0
  135. data/lib/prism/translation/ruby_parser.rb +1721 -0
  136. data/lib/prism/translation.rb +20 -0
  137. data/lib/prism/visitor.rb +1440 -0
  138. data/lib/prism.rb +168 -0
  139. data/prism.gemspec +234 -0
  140. data/rbi/generated/prism/compiler.rbi +489 -0
  141. data/rbi/generated/prism/desugar_compiler.rbi +306 -0
  142. data/rbi/generated/prism/dispatcher.rbi +1138 -0
  143. data/rbi/generated/prism/dot_visitor.rbi +618 -0
  144. data/rbi/generated/prism/dsl.rbi +751 -0
  145. data/rbi/generated/prism/inspect_visitor.rbi +503 -0
  146. data/rbi/generated/prism/lex_compat.rbi +162 -0
  147. data/rbi/generated/prism/mutation_compiler.rbi +464 -0
  148. data/rbi/generated/prism/node.rbi +13681 -0
  149. data/rbi/generated/prism/node_ext.rbi +259 -0
  150. data/rbi/generated/prism/node_find.rbi +79 -0
  151. data/rbi/generated/prism/parse_result/errors.rbi +21 -0
  152. data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
  153. data/rbi/generated/prism/parse_result.rbi +764 -0
  154. data/rbi/generated/prism/pattern.rbi +134 -0
  155. data/rbi/generated/prism/reflection.rbi +89 -0
  156. data/rbi/generated/prism/relocation.rbi +329 -0
  157. data/rbi/generated/prism/serialize.rbi +181 -0
  158. data/rbi/generated/prism/string_query.rbi +36 -0
  159. data/rbi/generated/prism/translation.rbi +7 -0
  160. data/rbi/generated/prism/visitor.rbi +650 -0
  161. data/rbi/generated/prism.rbi +86 -0
  162. data/rbi/prism/translation/parser.rbi +11 -0
  163. data/rbi/prism/translation/parser_versions.rbi +28 -0
  164. data/rbi/prism/translation/ripper.rbi +15 -0
  165. data/rbi/rubyvm/node_find.rbi +14 -0
  166. data/sig/generated/prism/compiler.rbs +492 -0
  167. data/sig/generated/prism/desugar_compiler.rbs +305 -0
  168. data/sig/generated/prism/dispatcher.rbs +1292 -0
  169. data/sig/generated/prism/dot_visitor.rbs +635 -0
  170. data/sig/generated/prism/dsl.rbs +928 -0
  171. data/sig/generated/prism/inspect_visitor.rbs +509 -0
  172. data/sig/generated/prism/lex_compat.rbs +166 -0
  173. data/sig/generated/prism/mutation_compiler.rbs +464 -0
  174. data/sig/generated/prism/node.rbs +20160 -0
  175. data/sig/generated/prism/node_ext.rbs +290 -0
  176. data/sig/generated/prism/node_find.rbs +90 -0
  177. data/sig/generated/prism/parse_result/errors.rbs +22 -0
  178. data/sig/generated/prism/parse_result/newlines.rbs +223 -0
  179. data/sig/generated/prism/parse_result.rbs +859 -0
  180. data/sig/generated/prism/pattern.rbs +156 -0
  181. data/sig/generated/prism/reflection.rbs +101 -0
  182. data/sig/generated/prism/relocation.rbs +428 -0
  183. data/sig/generated/prism/serialize.rbs +195 -0
  184. data/sig/generated/prism/string_query.rbs +36 -0
  185. data/sig/generated/prism/translation.rbs +8 -0
  186. data/sig/generated/prism/visitor.rbs +1111 -0
  187. data/sig/generated/prism.rbs +84 -0
  188. data/sig/manifest.yaml +2 -0
  189. data/src/arena.c +117 -0
  190. data/src/buffer.c +380 -0
  191. data/src/char.c +289 -0
  192. data/src/comments.c +454 -0
  193. data/src/constant_pool.c +406 -0
  194. data/src/diagnostic.c +886 -0
  195. data/src/encoding.c +5345 -0
  196. data/src/errors_format.c +519 -0
  197. data/src/integer.c +681 -0
  198. data/src/json.c +5733 -0
  199. data/src/line_offset_list.c +100 -0
  200. data/src/list.c +24 -0
  201. data/src/memchr.c +37 -0
  202. data/src/node.c +4515 -0
  203. data/src/options.c +451 -0
  204. data/src/parser.c +349 -0
  205. data/src/prettyprint.c +9024 -0
  206. data/src/prism.c +23739 -0
  207. data/src/regexp.c +1706 -0
  208. data/src/serialize.c +2309 -0
  209. data/src/source.c +518 -0
  210. data/src/static_literals.c +685 -0
  211. data/src/string_query.c +166 -0
  212. data/src/stringy.c +91 -0
  213. data/src/strncasecmp.c +37 -0
  214. data/src/strpbrk.c +447 -0
  215. data/src/tokens.c +724 -0
  216. metadata +259 -0
@@ -0,0 +1,4278 @@
1
+ # frozen_string_literal: true
2
+ # :markup: markdown
3
+
4
+ module Prism
5
+ module Translation
6
+ # This class provides a compatibility layer between prism and Ripper. It
7
+ # functions by parsing the entire tree first and then walking it and
8
+ # executing each of the Ripper callbacks as it goes. To use this class, you
9
+ # treat `Prism::Translation::Ripper` effectively as you would treat the
10
+ # `Ripper` class.
11
+ #
12
+ # Note that this class will serve the most common use cases, but Ripper's
13
+ # API is extensive and undocumented. It relies on reporting the state of the
14
+ # parser at any given time. We do our best to replicate that here, but
15
+ # because it is a different architecture it is not possible to perfectly
16
+ # replicate the behavior of Ripper.
17
+ #
18
+ # The main known difference is that we may omit dispatching some events in
19
+ # some cases. This impacts the following events:
20
+ #
21
+ # - on_assign_error
22
+ # - on_comma
23
+ # - on_ignored_nl
24
+ # - on_ignored_sp
25
+ # - on_nl
26
+ # - on_operator_ambiguous
27
+ # - on_semicolon
28
+ # - on_sp
29
+ #
30
+ class Ripper < Compiler
31
+ # Parses the given Ruby program read from +src+.
32
+ # +src+ must be a String or an IO or a object with a #gets method.
33
+ def self.parse(src, filename = "(ripper)", lineno = 1)
34
+ new(src, filename, lineno).parse
35
+ end
36
+
37
+ # Tokenizes the Ruby program and returns an array of an array,
38
+ # which is formatted like
39
+ # <code>[[lineno, column], type, token, state]</code>.
40
+ # The +filename+ argument is mostly ignored.
41
+ # By default, this method does not handle syntax errors in +src+,
42
+ # use the +raise_errors+ keyword to raise a SyntaxError for an error in +src+.
43
+ #
44
+ # require "ripper"
45
+ # require "pp"
46
+ #
47
+ # pp Ripper.lex("def m(a) nil end")
48
+ # #=> [[[1, 0], :on_kw, "def", FNAME ],
49
+ # [[1, 3], :on_sp, " ", FNAME ],
50
+ # [[1, 4], :on_ident, "m", ENDFN ],
51
+ # [[1, 5], :on_lparen, "(", BEG|LABEL],
52
+ # [[1, 6], :on_ident, "a", ARG ],
53
+ # [[1, 7], :on_rparen, ")", ENDFN ],
54
+ # [[1, 8], :on_sp, " ", BEG ],
55
+ # [[1, 9], :on_kw, "nil", END ],
56
+ # [[1, 12], :on_sp, " ", END ],
57
+ # [[1, 13], :on_kw, "end", END ]]
58
+ #
59
+ def self.lex(src, filename = "-", lineno = 1, raise_errors: false)
60
+ coerced = coerce_source(src)
61
+ result = Prism.lex_compat(coerced, filepath: filename, line: lineno, version: "current", encoding: coerced.encoding)
62
+
63
+ if result.failure? && raise_errors
64
+ raise SyntaxError, result.errors.first.message
65
+ else
66
+ result.value
67
+ end
68
+ end
69
+
70
+ # Tokenizes the Ruby program and returns an array of strings.
71
+ # The +filename+ and +lineno+ arguments are mostly ignored, since the
72
+ # return value is just the tokenized input.
73
+ # By default, this method does not handle syntax errors in +src+,
74
+ # use the +raise_errors+ keyword to raise a SyntaxError for an error in +src+.
75
+ #
76
+ # p Ripper.tokenize("def m(a) nil end")
77
+ # # => ["def", " ", "m", "(", "a", ")", " ", "nil", " ", "end"]
78
+ #
79
+ def self.tokenize(...)
80
+ lex(...).map { |token| token[2] }
81
+ end
82
+
83
+ # Mirros the various lex_types that ripper supports
84
+ def self.coerce_source(source) # :nodoc:
85
+ if source.is_a?(IO)
86
+ source.read
87
+ elsif source.respond_to?(:gets)
88
+ src = +""
89
+ while line = source.gets
90
+ src << line
91
+ end
92
+ src
93
+ else
94
+ source.to_str
95
+ end
96
+ end
97
+
98
+ # This contains a table of all of the parser events and their
99
+ # corresponding arity.
100
+ PARSER_EVENT_TABLE = {
101
+ BEGIN: 1,
102
+ END: 1,
103
+ alias: 2,
104
+ alias_error: 2,
105
+ aref: 2,
106
+ aref_field: 2,
107
+ arg_ambiguous: 1,
108
+ arg_paren: 1,
109
+ args_add: 2,
110
+ args_add_block: 2,
111
+ args_add_star: 2,
112
+ args_forward: 0,
113
+ args_new: 0,
114
+ array: 1,
115
+ aryptn: 4,
116
+ assign: 2,
117
+ assign_error: 2,
118
+ assoc_new: 2,
119
+ assoc_splat: 1,
120
+ assoclist_from_args: 1,
121
+ bare_assoc_hash: 1,
122
+ begin: 1,
123
+ binary: 3,
124
+ block_var: 2,
125
+ blockarg: 1,
126
+ bodystmt: 4,
127
+ brace_block: 2,
128
+ break: 1,
129
+ call: 3,
130
+ case: 2,
131
+ class: 3,
132
+ class_name_error: 2,
133
+ command: 2,
134
+ command_call: 4,
135
+ const_path_field: 2,
136
+ const_path_ref: 2,
137
+ const_ref: 1,
138
+ def: 3,
139
+ defined: 1,
140
+ defs: 5,
141
+ do_block: 2,
142
+ dot2: 2,
143
+ dot3: 2,
144
+ dyna_symbol: 1,
145
+ else: 1,
146
+ elsif: 3,
147
+ ensure: 1,
148
+ excessed_comma: 0,
149
+ fcall: 1,
150
+ field: 3,
151
+ fndptn: 4,
152
+ for: 3,
153
+ hash: 1,
154
+ heredoc_dedent: 2,
155
+ hshptn: 3,
156
+ if: 3,
157
+ if_mod: 2,
158
+ ifop: 3,
159
+ in: 3,
160
+ kwrest_param: 1,
161
+ lambda: 2,
162
+ magic_comment: 2,
163
+ massign: 2,
164
+ method_add_arg: 2,
165
+ method_add_block: 2,
166
+ mlhs_add: 2,
167
+ mlhs_add_post: 2,
168
+ mlhs_add_star: 2,
169
+ mlhs_new: 0,
170
+ mlhs_paren: 1,
171
+ module: 2,
172
+ mrhs_add: 2,
173
+ mrhs_add_star: 2,
174
+ mrhs_new: 0,
175
+ mrhs_new_from_args: 1,
176
+ next: 1,
177
+ nokw_param: 1,
178
+ opassign: 3,
179
+ operator_ambiguous: 2,
180
+ param_error: 2,
181
+ params: 7,
182
+ paren: 1,
183
+ parse_error: 1,
184
+ program: 1,
185
+ qsymbols_add: 2,
186
+ qsymbols_new: 0,
187
+ qwords_add: 2,
188
+ qwords_new: 0,
189
+ redo: 0,
190
+ regexp_add: 2,
191
+ regexp_literal: 2,
192
+ regexp_new: 0,
193
+ rescue: 4,
194
+ rescue_mod: 2,
195
+ rest_param: 1,
196
+ retry: 0,
197
+ return: 1,
198
+ return0: 0,
199
+ sclass: 2,
200
+ stmts_add: 2,
201
+ stmts_new: 0,
202
+ string_add: 2,
203
+ string_concat: 2,
204
+ string_content: 0,
205
+ string_dvar: 1,
206
+ string_embexpr: 1,
207
+ string_literal: 1,
208
+ super: 1,
209
+ symbol: 1,
210
+ symbol_literal: 1,
211
+ symbols_add: 2,
212
+ symbols_new: 0,
213
+ top_const_field: 1,
214
+ top_const_ref: 1,
215
+ unary: 2,
216
+ undef: 1,
217
+ unless: 3,
218
+ unless_mod: 2,
219
+ until: 2,
220
+ until_mod: 2,
221
+ var_alias: 2,
222
+ var_field: 1,
223
+ var_ref: 1,
224
+ vcall: 1,
225
+ void_stmt: 0,
226
+ when: 3,
227
+ while: 2,
228
+ while_mod: 2,
229
+ word_add: 2,
230
+ word_new: 0,
231
+ words_add: 2,
232
+ words_new: 0,
233
+ xstring_add: 2,
234
+ xstring_literal: 1,
235
+ xstring_new: 0,
236
+ yield: 1,
237
+ yield0: 0,
238
+ zsuper: 0
239
+ }
240
+
241
+ # This contains a table of all of the scanner events and their
242
+ # corresponding arity.
243
+ SCANNER_EVENT_TABLE = {
244
+ CHAR: 1,
245
+ __end__: 1,
246
+ backref: 1,
247
+ backtick: 1,
248
+ comma: 1,
249
+ comment: 1,
250
+ const: 1,
251
+ cvar: 1,
252
+ embdoc: 1,
253
+ embdoc_beg: 1,
254
+ embdoc_end: 1,
255
+ embexpr_beg: 1,
256
+ embexpr_end: 1,
257
+ embvar: 1,
258
+ float: 1,
259
+ gvar: 1,
260
+ heredoc_beg: 1,
261
+ heredoc_end: 1,
262
+ ident: 1,
263
+ ignored_nl: 1,
264
+ imaginary: 1,
265
+ int: 1,
266
+ ivar: 1,
267
+ kw: 1,
268
+ label: 1,
269
+ label_end: 1,
270
+ lbrace: 1,
271
+ lbracket: 1,
272
+ lparen: 1,
273
+ nl: 1,
274
+ op: 1,
275
+ period: 1,
276
+ qsymbols_beg: 1,
277
+ qwords_beg: 1,
278
+ rational: 1,
279
+ rbrace: 1,
280
+ rbracket: 1,
281
+ regexp_beg: 1,
282
+ regexp_end: 1,
283
+ rparen: 1,
284
+ semicolon: 1,
285
+ sp: 1,
286
+ symbeg: 1,
287
+ symbols_beg: 1,
288
+ tlambda: 1,
289
+ tlambeg: 1,
290
+ tstring_beg: 1,
291
+ tstring_content: 1,
292
+ tstring_end: 1,
293
+ words_beg: 1,
294
+ words_sep: 1,
295
+ ignored_sp: 1
296
+ }
297
+
298
+ # This array contains name of parser events.
299
+ PARSER_EVENTS = PARSER_EVENT_TABLE.keys
300
+
301
+ # This array contains name of scanner events.
302
+ SCANNER_EVENTS = SCANNER_EVENT_TABLE.keys
303
+
304
+ # This array contains name of all ripper events.
305
+ EVENTS = PARSER_EVENTS + SCANNER_EVENTS
306
+
307
+ # A list of all of the Ruby keywords.
308
+ KEYWORDS = [
309
+ "alias",
310
+ "and",
311
+ "begin",
312
+ "BEGIN",
313
+ "break",
314
+ "case",
315
+ "class",
316
+ "def",
317
+ "defined?",
318
+ "do",
319
+ "else",
320
+ "elsif",
321
+ "end",
322
+ "END",
323
+ "ensure",
324
+ "false",
325
+ "for",
326
+ "if",
327
+ "in",
328
+ "module",
329
+ "next",
330
+ "nil",
331
+ "not",
332
+ "or",
333
+ "redo",
334
+ "rescue",
335
+ "retry",
336
+ "return",
337
+ "self",
338
+ "super",
339
+ "then",
340
+ "true",
341
+ "undef",
342
+ "unless",
343
+ "until",
344
+ "when",
345
+ "while",
346
+ "yield",
347
+ "__ENCODING__",
348
+ "__FILE__",
349
+ "__LINE__"
350
+ ].to_set
351
+
352
+ # A list of all of the Ruby binary operators.
353
+ BINARY_OPERATORS = [
354
+ :!=,
355
+ :!~,
356
+ :=~,
357
+ :==,
358
+ :===,
359
+ :<=>,
360
+ :>,
361
+ :>=,
362
+ :<,
363
+ :<=,
364
+ :&,
365
+ :|,
366
+ :^,
367
+ :>>,
368
+ :<<,
369
+ :-,
370
+ :+,
371
+ :%,
372
+ :/,
373
+ :*,
374
+ :**
375
+ ].to_set
376
+
377
+ private_constant :KEYWORDS, :BINARY_OPERATORS
378
+
379
+ # Parses +src+ and create S-exp tree.
380
+ # Returns more readable tree rather than Ripper.sexp_raw.
381
+ # This method is mainly for developer use.
382
+ # The +filename+ argument is mostly ignored.
383
+ # By default, this method does not handle syntax errors in +src+,
384
+ # returning +nil+ in such cases. Use the +raise_errors+ keyword
385
+ # to raise a SyntaxError for an error in +src+.
386
+ #
387
+ # require "ripper"
388
+ # require "pp"
389
+ #
390
+ # pp Ripper.sexp("def m(a) nil end")
391
+ # #=> [:program,
392
+ # [[:def,
393
+ # [:@ident, "m", [1, 4]],
394
+ # [:paren, [:params, [[:@ident, "a", [1, 6]]], nil, nil, nil, nil, nil, nil]],
395
+ # [:bodystmt, [[:var_ref, [:@kw, "nil", [1, 9]]]], nil, nil, nil]]]]
396
+ #
397
+ def self.sexp(src, filename = "-", lineno = 1, raise_errors: false)
398
+ builder = SexpBuilderPP.new(src, filename, lineno)
399
+ sexp = builder.parse
400
+ if builder.error?
401
+ if raise_errors
402
+ raise SyntaxError, builder.error
403
+ end
404
+ else
405
+ sexp
406
+ end
407
+ end
408
+
409
+ # Parses +src+ and create S-exp tree.
410
+ # This method is mainly for developer use.
411
+ # The +filename+ argument is mostly ignored.
412
+ # By default, this method does not handle syntax errors in +src+,
413
+ # returning +nil+ in such cases. Use the +raise_errors+ keyword
414
+ # to raise a SyntaxError for an error in +src+.
415
+ #
416
+ # require "ripper"
417
+ # require "pp"
418
+ #
419
+ # pp Ripper.sexp_raw("def m(a) nil end")
420
+ # #=> [:program,
421
+ # [:stmts_add,
422
+ # [:stmts_new],
423
+ # [:def,
424
+ # [:@ident, "m", [1, 4]],
425
+ # [:paren, [:params, [[:@ident, "a", [1, 6]]], nil, nil, nil]],
426
+ # [:bodystmt,
427
+ # [:stmts_add, [:stmts_new], [:var_ref, [:@kw, "nil", [1, 9]]]],
428
+ # nil,
429
+ # nil,
430
+ # nil]]]]
431
+ #
432
+ def self.sexp_raw(src, filename = "-", lineno = 1, raise_errors: false)
433
+ builder = SexpBuilder.new(src, filename, lineno)
434
+ sexp = builder.parse
435
+ if builder.error?
436
+ if raise_errors
437
+ raise SyntaxError, builder.error
438
+ end
439
+ else
440
+ sexp
441
+ end
442
+ end
443
+
444
+ autoload :Filter, "prism/translation/ripper/filter"
445
+ autoload :Lexer, "prism/translation/ripper/lexer"
446
+ autoload :SexpBuilder, "prism/translation/ripper/sexp"
447
+ autoload :SexpBuilderPP, "prism/translation/ripper/sexp"
448
+
449
+ # Provides optimized access to line and column information.
450
+ # Ripper bounds are mostly accessed in a linear fashion, so
451
+ # we can try a linear scan first and fall back to binary search.
452
+ class LineAndColumnCache # :nodoc:
453
+ # How many should it look ahead/behind before falling back to binary searching.
454
+ WINDOW = 8
455
+ private_constant :WINDOW
456
+
457
+ #: (Source source) -> void
458
+ def initialize(source)
459
+ @source = source
460
+ @offsets = source.offsets
461
+ @hint = 0
462
+ end
463
+
464
+ #: (Integer byte_offset) -> [Integer, Integer]
465
+ def line_and_column(byte_offset)
466
+ @hint = new_hint(byte_offset) || @source.find_line(byte_offset)
467
+ return [@hint + @source.start_line, byte_offset - @offsets[@hint]]
468
+ end
469
+
470
+ private
471
+
472
+ def new_hint(byte_offset)
473
+ if @offsets[@hint] <= byte_offset
474
+ # Same line?
475
+ if (@hint + 1 >= @offsets.size || @offsets[@hint + 1] > byte_offset)
476
+ return @hint
477
+ end
478
+
479
+ # Scan forwards
480
+ limit = [@hint + WINDOW + 1, @offsets.size].min
481
+ idx = @hint + 1
482
+ while idx < limit
483
+ if @offsets[idx] > byte_offset
484
+ return idx - 1
485
+ end
486
+ if @offsets[idx] == byte_offset
487
+ return idx
488
+ end
489
+ idx += 1
490
+ end
491
+ else
492
+ # Scan backwards
493
+ limit = @hint > WINDOW ? @hint - WINDOW : 0
494
+ idx = @hint
495
+ while idx >= limit + 1
496
+ if @offsets[idx - 1] <= byte_offset
497
+ return idx - 1
498
+ end
499
+ idx -= 1
500
+ end
501
+ end
502
+
503
+ nil
504
+ end
505
+ end
506
+
507
+ # :stopdoc:
508
+ # This is not part of the public API but used by some gems.
509
+
510
+ # Ripper-internal bitflags.
511
+ LEX_STATE_NAMES = %i[
512
+ BEG END ENDARG ENDFN ARG CMDARG MID FNAME DOT CLASS LABEL LABELED FITEM
513
+ ].map.with_index.to_h { |name, i| [2 ** i, name] }.freeze
514
+ private_constant :LEX_STATE_NAMES
515
+
516
+ LEX_STATE_NAMES.each do |value, key|
517
+ const_set("EXPR_#{key}", value)
518
+ end
519
+ EXPR_NONE = 0
520
+ EXPR_VALUE = EXPR_BEG
521
+ EXPR_BEG_ANY = EXPR_BEG | EXPR_MID | EXPR_CLASS
522
+ EXPR_ARG_ANY = EXPR_ARG | EXPR_CMDARG
523
+ EXPR_END_ANY = EXPR_END | EXPR_ENDARG | EXPR_ENDFN
524
+
525
+ def self.lex_state_name(state)
526
+ LEX_STATE_NAMES.filter_map { |flag, name| name if state & flag != 0 }.join("|")
527
+ end
528
+
529
+ # :startdoc:
530
+
531
+ # The source that is being parsed.
532
+ attr_reader :source
533
+
534
+ # The filename of the source being parsed.
535
+ attr_reader :filename
536
+
537
+ # The current line number of the parser.
538
+ attr_reader :lineno
539
+
540
+ # The current column in bytes of the parser.
541
+ attr_reader :column
542
+
543
+ # Create a new Translation::Ripper object with the given source.
544
+ def initialize(source, filename = "(ripper)", lineno = 1)
545
+ @source = Ripper.coerce_source(source)
546
+ @filename = filename
547
+ @lineno = lineno
548
+ @column = 0
549
+ @result = nil
550
+ @line_and_column_cache = nil
551
+ end
552
+
553
+ ##########################################################################
554
+ # Public interface
555
+ ##########################################################################
556
+
557
+ # True if the parser encountered an error during parsing.
558
+ def error?
559
+ result.failure?
560
+ end
561
+
562
+ # Parse the source and return the result.
563
+ def parse
564
+ result.comments.each do |comment|
565
+ location = comment.location
566
+ bounds(location)
567
+
568
+ if comment.is_a?(InlineComment)
569
+ # Inline comments always contain a newline if the line itself contains it
570
+ if result.source.source.bytesize > comment.location.end_offset
571
+ on_comment("#{comment.slice}\n")
572
+ else
573
+ on_comment(comment.slice)
574
+ end
575
+ else
576
+ offset = location.start_offset
577
+ lines = comment.slice.lines
578
+
579
+ lines.each_with_index do |line, index|
580
+ bounds(location.copy(start_offset: offset))
581
+
582
+ if index == 0
583
+ on_embdoc_beg(line)
584
+ elsif index == lines.size - 1
585
+ on_embdoc_end(line)
586
+ else
587
+ on_embdoc(line)
588
+ end
589
+
590
+ offset += line.bytesize
591
+ end
592
+ end
593
+ end
594
+
595
+ result.magic_comments.each do |magic_comment|
596
+ on_magic_comment(magic_comment.key, magic_comment.value)
597
+ end
598
+
599
+ unless result.data_loc.nil?
600
+ on___end__(result.data_loc.slice.each_line.first)
601
+ end
602
+
603
+ result.warnings.each do |warning|
604
+ bounds(warning.location)
605
+
606
+ if warning.level == :default
607
+ warning(warning.message)
608
+ else
609
+ case warning.type
610
+ when :ambiguous_first_argument_plus
611
+ on_arg_ambiguous("+")
612
+ when :ambiguous_first_argument_minus
613
+ on_arg_ambiguous("-")
614
+ when :ambiguous_slash
615
+ on_arg_ambiguous("/")
616
+ else
617
+ warn(warning.message)
618
+ end
619
+ end
620
+ end
621
+
622
+ if error?
623
+ result.errors.each do |error|
624
+ location = error.location
625
+ bounds(location)
626
+
627
+ case error.type
628
+ when :alias_argument
629
+ on_alias_error("can't make alias for the number variables", location.slice)
630
+ when :argument_formal_class
631
+ on_param_error("formal argument cannot be a class variable", location.slice)
632
+ when :argument_format_constant
633
+ on_param_error("formal argument cannot be a constant", location.slice)
634
+ when :argument_formal_global
635
+ on_param_error("formal argument cannot be a global variable", location.slice)
636
+ when :argument_formal_ivar
637
+ on_param_error("formal argument cannot be an instance variable", location.slice)
638
+ when :class_name, :module_name
639
+ on_class_name_error("class/module name must be CONSTANT", location.slice)
640
+ else
641
+ on_parse_error(error.message)
642
+ end
643
+ end
644
+
645
+ nil
646
+ else
647
+ result.value.accept(self)
648
+ end
649
+ end
650
+
651
+ # Return encoding of the source.
652
+ def encoding
653
+ result.source.encoding
654
+ end
655
+
656
+ # Return true if parsed source ended by `__END__`.
657
+ def end_seen?
658
+ !!result.data_loc
659
+ end
660
+
661
+ ##########################################################################
662
+ # Visitor methods
663
+ ##########################################################################
664
+
665
+ # :stopdoc:
666
+
667
+ # alias foo bar
668
+ # ^^^^^^^^^^^^^
669
+ def visit_alias_method_node(node)
670
+ bounds(node.keyword_loc)
671
+ on_kw("alias")
672
+
673
+ new_name = visit(node.new_name)
674
+ old_name = visit(node.old_name)
675
+
676
+ bounds(node.location)
677
+ on_alias(new_name, old_name)
678
+ end
679
+
680
+ # alias $foo $bar
681
+ # ^^^^^^^^^^^^^^^
682
+ def visit_alias_global_variable_node(node)
683
+ bounds(node.keyword_loc)
684
+ on_kw("alias")
685
+
686
+ new_name = visit_alias_global_variable_node_value(node.new_name)
687
+ old_name = visit_alias_global_variable_node_value(node.old_name)
688
+
689
+ bounds(node.location)
690
+ on_var_alias(new_name, old_name)
691
+ end
692
+
693
+ # Visit one side of an alias global variable node.
694
+ private def visit_alias_global_variable_node_value(node)
695
+ bounds(node.location)
696
+
697
+ case node
698
+ when BackReferenceReadNode
699
+ on_backref(node.slice)
700
+ when GlobalVariableReadNode
701
+ on_gvar(node.name.to_s)
702
+ else
703
+ raise
704
+ end
705
+ end
706
+
707
+ # foo => bar | baz
708
+ # ^^^^^^^^^
709
+ def visit_alternation_pattern_node(node)
710
+ left = visit_pattern_node(node.left)
711
+
712
+ bounds(node.operator_loc)
713
+ on_op("|")
714
+
715
+ right = visit_pattern_node(node.right)
716
+
717
+ bounds(node.location)
718
+ on_binary(left, :|, right)
719
+ end
720
+
721
+ # Visit a pattern within a pattern match. This is used to bypass the
722
+ # parenthesis node that can be used to wrap patterns.
723
+ private def visit_pattern_node(node)
724
+ if node.is_a?(ParenthesesNode)
725
+ bounds(node.opening_loc)
726
+ on_lparen("(")
727
+ result = visit(node.body)
728
+ bounds(node.closing_loc)
729
+ on_rparen(")")
730
+
731
+ result
732
+ else
733
+ visit(node)
734
+ end
735
+ end
736
+
737
+ # a and b
738
+ # ^^^^^^^
739
+ def visit_and_node(node)
740
+ left = visit(node.left)
741
+
742
+ bounds(node.operator_loc)
743
+ if node.operator == "and"
744
+ on_kw("and")
745
+ else
746
+ on_op("&&")
747
+ end
748
+
749
+ right = visit(node.right)
750
+
751
+ bounds(node.location)
752
+ on_binary(left, node.operator.to_sym, right)
753
+ end
754
+
755
+ # []
756
+ # ^^
757
+ def visit_array_node(node)
758
+ case (opening = node.opening)
759
+ when /^%w/
760
+ opening_loc = node.opening_loc
761
+ bounds(opening_loc)
762
+ on_qwords_beg(opening)
763
+
764
+ elements = on_qwords_new
765
+ previous = nil
766
+
767
+ node.elements.each do |element|
768
+ visit_words_sep(opening_loc, previous, element)
769
+
770
+ bounds(element.location)
771
+ elements = on_qwords_add(elements, on_tstring_content(element.content))
772
+
773
+ previous = element
774
+ end
775
+
776
+ visit_words_sep(opening_loc, node.elements.last, node.closing_loc)
777
+
778
+ bounds(node.closing_loc)
779
+ on_tstring_end(node.closing)
780
+ when /^%i/
781
+ opening_loc = node.opening_loc
782
+ bounds(opening_loc)
783
+ on_qsymbols_beg(opening)
784
+
785
+ elements = on_qsymbols_new
786
+ previous = nil
787
+
788
+ node.elements.each do |element|
789
+ visit_words_sep(opening_loc, previous, element)
790
+
791
+ bounds(element.location)
792
+ elements = on_qsymbols_add(elements, on_tstring_content(element.content))
793
+
794
+ previous = element
795
+ end
796
+
797
+ visit_words_sep(opening_loc, node.elements.last, node.closing_loc)
798
+
799
+ bounds(node.closing_loc)
800
+ on_tstring_end(node.closing)
801
+ when /^%W/
802
+ opening_loc = node.opening_loc
803
+ bounds(opening_loc)
804
+ on_words_beg(opening)
805
+
806
+ elements = on_words_new
807
+ previous = nil
808
+
809
+ node.elements.each do |element|
810
+ visit_words_sep(opening_loc, previous, element)
811
+
812
+ bounds(element.location)
813
+ elements =
814
+ on_words_add(
815
+ elements,
816
+ if element.is_a?(StringNode)
817
+ on_word_add(on_word_new, on_tstring_content(element.content))
818
+ else
819
+ element.parts.inject(on_word_new) do |word, part|
820
+ word_part =
821
+ if part.is_a?(StringNode)
822
+ bounds(part.location)
823
+ on_tstring_content(part.content)
824
+ else
825
+ visit(part)
826
+ end
827
+
828
+ on_word_add(word, word_part)
829
+ end
830
+ end
831
+ )
832
+
833
+ previous = element
834
+ end
835
+
836
+ visit_words_sep(opening_loc, node.elements.last, node.closing_loc)
837
+
838
+ bounds(node.closing_loc)
839
+ on_tstring_end(node.closing)
840
+ when /^%I/
841
+ opening_loc = node.opening_loc
842
+ bounds(opening_loc)
843
+ on_symbols_beg(opening)
844
+
845
+ elements = on_symbols_new
846
+ previous = nil
847
+
848
+ node.elements.each do |element|
849
+ visit_words_sep(opening_loc, previous, element)
850
+
851
+ bounds(element.location)
852
+ elements =
853
+ on_symbols_add(
854
+ elements,
855
+ if element.is_a?(SymbolNode)
856
+ on_word_add(on_word_new, on_tstring_content(element.content))
857
+ else
858
+ element.parts.inject(on_word_new) do |word, part|
859
+ word_part =
860
+ if part.is_a?(StringNode)
861
+ bounds(part.location)
862
+ on_tstring_content(part.content)
863
+ else
864
+ visit(part)
865
+ end
866
+
867
+ on_word_add(word, word_part)
868
+ end
869
+ end
870
+ )
871
+
872
+ previous = element
873
+ end
874
+
875
+ visit_words_sep(opening_loc, node.elements.last, node.closing_loc)
876
+
877
+ bounds(node.closing_loc)
878
+ on_tstring_end(node.closing)
879
+ else
880
+ bounds(node.opening_loc)
881
+ on_lbracket(opening)
882
+
883
+ elements = visit_arguments(node.elements) unless node.elements.empty?
884
+
885
+ bounds(node.closing_loc)
886
+ on_rbracket(node.closing)
887
+ end
888
+
889
+ bounds(node.location)
890
+ on_array(elements)
891
+ end
892
+
893
+ # Dispatch words_sep events that contains the whitespace between the elements
894
+ # of list literals.
895
+ private def visit_words_sep(opening_loc, previous, current)
896
+ start_offset = (previous.nil? ? opening_loc : previous.location).end_offset
897
+ end_offset = current.start_offset
898
+ length = end_offset - start_offset
899
+
900
+ if length > 0
901
+ whitespace = source.byteslice(start_offset, length)
902
+ current_offset = start_offset
903
+ whitespace.each_line do |part|
904
+ bounds(opening_loc.copy(start_offset: current_offset, length: part.bytesize))
905
+ on_words_sep(part)
906
+ current_offset += part.bytesize
907
+ end
908
+ end
909
+ end
910
+
911
+ # Visit a list of elements, like the elements of an array or arguments.
912
+ private def visit_arguments(elements)
913
+ bounds(elements.first.location)
914
+ elements.inject(on_args_new) do |args, element|
915
+ arg = visit(element)
916
+ bounds(element.location)
917
+
918
+ case element
919
+ when BlockArgumentNode
920
+ on_args_add_block(args, arg)
921
+ when SplatNode
922
+ on_args_add_star(args, arg)
923
+ else
924
+ on_args_add(args, arg)
925
+ end
926
+ end
927
+ end
928
+
929
+ # foo => [bar]
930
+ # ^^^^^
931
+ def visit_array_pattern_node(node)
932
+ constant = visit(node.constant)
933
+
934
+ if node.opening_loc
935
+ bounds(node.opening_loc)
936
+ node.opening == "[" ? on_lbracket("[") : on_lparen("(")
937
+ end
938
+
939
+ requireds = visit_all(node.requireds) if node.requireds.any?
940
+ rest =
941
+ if (rest_node = node.rest).is_a?(SplatNode)
942
+ bounds(rest_node.operator_loc)
943
+ on_op("*")
944
+
945
+ if rest_node.expression.nil?
946
+ bounds(rest_node.location)
947
+ on_var_field(nil)
948
+ else
949
+ visit(rest_node.expression)
950
+ end
951
+ end
952
+
953
+ posts = visit_all(node.posts) if node.posts.any?
954
+
955
+ if node.closing_loc
956
+ bounds(node.closing_loc)
957
+ node.closing == "]" ? on_rbracket("]") : on_rparen(")")
958
+ end
959
+ bounds(node.location)
960
+ on_aryptn(constant, requireds, rest, posts)
961
+ end
962
+
963
+ # foo(bar)
964
+ # ^^^
965
+ def visit_arguments_node(node)
966
+ arguments, _ = visit_call_node_arguments(node, nil, false)
967
+ arguments
968
+ end
969
+
970
+ # { a: 1 }
971
+ # ^^^^
972
+ def visit_assoc_node(node)
973
+ key = visit(node.key)
974
+
975
+ if node.operator_loc
976
+ bounds(node.operator_loc)
977
+ on_op("=>")
978
+ end
979
+
980
+ value = visit(node.value)
981
+
982
+ bounds(node.location)
983
+ on_assoc_new(key, value)
984
+ end
985
+
986
+ # def foo(**); bar(**); end
987
+ # ^^
988
+ #
989
+ # { **foo }
990
+ # ^^^^^
991
+ def visit_assoc_splat_node(node)
992
+ bounds(node.operator_loc)
993
+ on_op("**")
994
+
995
+ value = visit(node.value)
996
+
997
+ bounds(node.location)
998
+ on_assoc_splat(value)
999
+ end
1000
+
1001
+ # $+
1002
+ # ^^
1003
+ def visit_back_reference_read_node(node)
1004
+ bounds(node.location)
1005
+ on_backref(node.slice)
1006
+ end
1007
+
1008
+ # begin end
1009
+ # ^^^^^^^^^
1010
+ def visit_begin_node(node)
1011
+ if node.begin_keyword_loc
1012
+ bounds(node.begin_keyword_loc)
1013
+ on_kw("begin")
1014
+ end
1015
+
1016
+ clauses = visit_begin_node_clauses(node.begin_keyword_loc, node, false)
1017
+
1018
+ if node.end_keyword_loc
1019
+ bounds(node.end_keyword_loc)
1020
+ on_kw("end")
1021
+ end
1022
+
1023
+ bounds(node.location)
1024
+ on_begin(clauses)
1025
+ end
1026
+
1027
+ # Visit the clauses of a begin node to form an on_bodystmt call.
1028
+ private def visit_begin_node_clauses(location, node, allow_newline)
1029
+ statements =
1030
+ if node.statements.nil?
1031
+ on_stmts_add(on_stmts_new, on_void_stmt)
1032
+ else
1033
+ body = node.statements.body
1034
+ body = [nil, *body] if void_stmt?(location, node.statements.body[0].location, allow_newline)
1035
+
1036
+ bounds(node.statements.location)
1037
+ visit_statements_node_body(body)
1038
+ end
1039
+
1040
+ rescue_clause = visit(node.rescue_clause)
1041
+ else_clause =
1042
+ unless (else_clause_node = node.else_clause).nil?
1043
+ bounds(else_clause_node.else_keyword_loc)
1044
+ on_kw("else")
1045
+
1046
+ else_statements =
1047
+ if else_clause_node.statements.nil?
1048
+ [nil]
1049
+ else
1050
+ body = else_clause_node.statements.body
1051
+ body = [nil, *body] if void_stmt?(else_clause_node.else_keyword_loc, else_clause_node.statements.body[0].location, allow_newline)
1052
+ body
1053
+ end
1054
+
1055
+ bounds(else_clause_node.location)
1056
+ visit_statements_node_body(else_statements)
1057
+ end
1058
+ ensure_clause = visit(node.ensure_clause)
1059
+
1060
+ bounds(node.location)
1061
+ on_bodystmt(statements, rescue_clause, else_clause, ensure_clause)
1062
+ end
1063
+
1064
+ # Visit the body of a structure that can have either a set of statements
1065
+ # or statements wrapped in rescue/else/ensure.
1066
+ private def visit_body_node(location, node, allow_newline = false)
1067
+ case node
1068
+ when nil
1069
+ bounds(location)
1070
+ on_bodystmt(visit_statements_node_body([nil]), nil, nil, nil)
1071
+ when StatementsNode
1072
+ body = [*node.body]
1073
+ body = [nil, *body] if void_stmt?(location, body[0].location, allow_newline)
1074
+ stmts = visit_statements_node_body(body)
1075
+
1076
+ bounds(node.body.first.location)
1077
+ on_bodystmt(stmts, nil, nil, nil)
1078
+ when BeginNode
1079
+ visit_begin_node_clauses(location, node, allow_newline)
1080
+ else
1081
+ raise
1082
+ end
1083
+ end
1084
+
1085
+ # foo(&bar)
1086
+ # ^^^^
1087
+ def visit_block_argument_node(node)
1088
+ bounds(node.operator_loc)
1089
+ on_op("&")
1090
+ visit(node.expression)
1091
+ end
1092
+
1093
+ # foo { |; bar| }
1094
+ # ^^^
1095
+ def visit_block_local_variable_node(node)
1096
+ bounds(node.location)
1097
+ on_ident(node.name.to_s)
1098
+ end
1099
+
1100
+ # Visit a BlockNode.
1101
+ def visit_block_node(node)
1102
+ braces = node.opening == "{"
1103
+ bounds(node.opening_loc)
1104
+ if braces
1105
+ on_lbrace("{")
1106
+ else
1107
+ on_kw("do")
1108
+ end
1109
+
1110
+ parameters = visit(node.parameters)
1111
+
1112
+ body =
1113
+ case node.body
1114
+ when nil
1115
+ bounds(node.location)
1116
+ stmts = on_stmts_add(on_stmts_new, on_void_stmt)
1117
+
1118
+ bounds(node.location)
1119
+ braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
1120
+ when StatementsNode
1121
+ stmts = node.body.body
1122
+ stmts = [nil, *stmts] if void_stmt?(node.parameters&.location || node.opening_loc, node.body.location, false)
1123
+ stmts = visit_statements_node_body(stmts)
1124
+
1125
+ bounds(node.body.location)
1126
+ braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
1127
+ when BeginNode
1128
+ visit_body_node(node.parameters&.location || node.opening_loc, node.body)
1129
+ else
1130
+ raise
1131
+ end
1132
+
1133
+ if braces
1134
+ bounds(node.closing_loc)
1135
+ on_rbrace("}")
1136
+ else
1137
+ bounds(node.closing_loc)
1138
+ on_kw("end")
1139
+ end
1140
+
1141
+ if braces
1142
+ bounds(node.location)
1143
+ on_brace_block(parameters, body)
1144
+ else
1145
+ bounds(node.location)
1146
+ on_do_block(parameters, body)
1147
+ end
1148
+ end
1149
+
1150
+ # def foo(&bar); end
1151
+ # ^^^^
1152
+ def visit_block_parameter_node(node)
1153
+ bounds(node.operator_loc)
1154
+ on_op("&")
1155
+
1156
+ if node.name_loc.nil?
1157
+ bounds(node.location)
1158
+ on_blockarg(nil)
1159
+ else
1160
+ bounds(node.name_loc)
1161
+ name = on_ident(node.name.to_s)
1162
+
1163
+ bounds(node.location)
1164
+ on_blockarg(name)
1165
+ end
1166
+ end
1167
+
1168
+ # A block's parameters.
1169
+ def visit_block_parameters_node(node)
1170
+ bounds(node.opening_loc)
1171
+ on_op("|")
1172
+
1173
+ parameters =
1174
+ if node.parameters.nil?
1175
+ on_params(nil, nil, nil, nil, nil, nil, nil)
1176
+ else
1177
+ visit(node.parameters)
1178
+ end
1179
+
1180
+ locals =
1181
+ if node.locals.any?
1182
+ visit_all(node.locals)
1183
+ else
1184
+ false
1185
+ end
1186
+
1187
+ bounds(node.closing_loc)
1188
+ on_op("|")
1189
+
1190
+ bounds(node.location)
1191
+ on_block_var(parameters, locals)
1192
+ end
1193
+
1194
+ # break
1195
+ # ^^^^^
1196
+ #
1197
+ # break foo
1198
+ # ^^^^^^^^^
1199
+ def visit_break_node(node)
1200
+ bounds(node.keyword_loc)
1201
+ on_kw("break")
1202
+
1203
+ if node.arguments.nil?
1204
+ bounds(node.location)
1205
+ on_break(on_args_new)
1206
+ else
1207
+ arguments = visit(node.arguments)
1208
+
1209
+ bounds(node.location)
1210
+ on_break(arguments)
1211
+ end
1212
+ end
1213
+
1214
+ # foo
1215
+ # ^^^
1216
+ #
1217
+ # foo.bar
1218
+ # ^^^^^^^
1219
+ #
1220
+ # foo.bar() {}
1221
+ # ^^^^^^^^^^^^
1222
+ def visit_call_node(node)
1223
+ if node.call_operator_loc.nil?
1224
+ case node.name
1225
+ when :[]
1226
+ receiver = visit(node.receiver)
1227
+
1228
+ bounds(node.opening_loc)
1229
+ on_lbracket("[")
1230
+
1231
+ arguments, block_node = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
1232
+
1233
+ bounds(node.closing_loc)
1234
+ on_rbracket("]")
1235
+
1236
+ block = visit(block_node)
1237
+
1238
+ bounds(node.location)
1239
+ call = on_aref(receiver, arguments)
1240
+
1241
+ if block_node
1242
+ bounds(node.location)
1243
+ on_method_add_block(call, block)
1244
+ else
1245
+ call
1246
+ end
1247
+ when :[]=
1248
+ receiver = visit(node.receiver)
1249
+
1250
+ bounds(node.opening_loc)
1251
+ on_lbracket("[")
1252
+
1253
+ *arguments, last_argument = node.arguments.arguments
1254
+ arguments << node.block if !node.block.nil?
1255
+
1256
+ arguments =
1257
+ if arguments.any?
1258
+ args = visit_arguments(arguments)
1259
+
1260
+ if !node.block.nil?
1261
+ args
1262
+ else
1263
+ bounds(arguments.first.location)
1264
+ on_args_add_block(args, false)
1265
+ end
1266
+ end
1267
+
1268
+ bounds(node.closing_loc)
1269
+ on_rbracket("]")
1270
+ bounds(node.equal_loc)
1271
+ on_op("=")
1272
+
1273
+ bounds(node.location)
1274
+ call = on_aref_field(receiver, arguments)
1275
+ value = visit_write_value(last_argument)
1276
+
1277
+ bounds(last_argument.location)
1278
+ on_assign(call, value)
1279
+ when :-@, :+@, :~
1280
+ bounds(node.message_loc)
1281
+ on_op(node.message)
1282
+
1283
+ receiver = visit(node.receiver)
1284
+ bounds(node.location)
1285
+ on_unary(node.name, receiver)
1286
+ when :!
1287
+ bounds(node.message_loc)
1288
+ if node.message == "not"
1289
+ on_kw("not")
1290
+
1291
+ if node.opening_loc
1292
+ bounds(node.opening_loc)
1293
+ on_lparen("(")
1294
+ end
1295
+
1296
+ receiver =
1297
+ if node.receiver.is_a?(ParenthesesNode) && node.receiver.body.nil?
1298
+ # The parens in `not()` just emit parens and nothing else.
1299
+ bounds(node.receiver.opening_loc)
1300
+ on_lparen("(")
1301
+ bounds(node.receiver.closing_loc)
1302
+ on_rparen(")")
1303
+ nil
1304
+ else
1305
+ visit(node.receiver)
1306
+ end
1307
+
1308
+ if node.closing_loc
1309
+ bounds(node.closing_loc)
1310
+ on_rparen(")")
1311
+ end
1312
+ bounds(node.location)
1313
+ on_unary(:not, receiver)
1314
+ else
1315
+ on_op("!")
1316
+
1317
+ receiver = visit(node.receiver)
1318
+
1319
+ bounds(node.location)
1320
+ on_unary(:!, receiver)
1321
+ end
1322
+ when BINARY_OPERATORS
1323
+ receiver = visit(node.receiver)
1324
+
1325
+ bounds(node.message_loc)
1326
+ on_op(node.message)
1327
+
1328
+ value = visit(node.arguments.arguments.first)
1329
+
1330
+ bounds(node.location)
1331
+ on_binary(receiver, node.name, value)
1332
+ else
1333
+ bounds(node.message_loc)
1334
+ message = visit_token(node.message, false)
1335
+
1336
+ if node.variable_call?
1337
+ on_vcall(message)
1338
+ else
1339
+ if node.opening_loc
1340
+ bounds(node.opening_loc)
1341
+ on_lparen("(")
1342
+ end
1343
+
1344
+ arguments, block_node = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1345
+
1346
+ if node.closing_loc
1347
+ bounds(node.closing_loc)
1348
+ on_rparen(")")
1349
+ end
1350
+
1351
+ block = visit(block_node)
1352
+ call =
1353
+ if node.opening_loc.nil? && get_arguments_and_block(node.arguments, node.block).first.any?
1354
+ bounds(node.location)
1355
+ on_command(message, arguments)
1356
+ elsif !node.opening_loc.nil?
1357
+ bounds(node.location)
1358
+ on_method_add_arg(on_fcall(message), on_arg_paren(arguments))
1359
+ else
1360
+ bounds(node.location)
1361
+ on_method_add_arg(on_fcall(message), on_args_new)
1362
+ end
1363
+
1364
+ if block_node
1365
+ bounds(node.block.location)
1366
+ on_method_add_block(call, block)
1367
+ else
1368
+ call
1369
+ end
1370
+ end
1371
+ end
1372
+ else
1373
+ receiver = visit(node.receiver)
1374
+
1375
+ bounds(node.call_operator_loc)
1376
+ call_operator = visit_call_operator(node.call_operator)
1377
+
1378
+ message =
1379
+ if node.message_loc.nil?
1380
+ :call
1381
+ else
1382
+ bounds(node.message_loc)
1383
+ visit_token(node.message, false)
1384
+ end
1385
+
1386
+ if node.equal_loc
1387
+ bounds(node.equal_loc)
1388
+ on_op("=")
1389
+ end
1390
+
1391
+ if node.name.end_with?("=") && !node.message.end_with?("=") && !node.arguments.nil? && node.block.nil?
1392
+ value = visit_write_value(node.arguments.arguments.first)
1393
+
1394
+ bounds(node.location)
1395
+ on_assign(on_field(receiver, call_operator, message), value)
1396
+ else
1397
+ if node.opening_loc
1398
+ bounds(node.opening_loc)
1399
+ on_lparen("(")
1400
+ end
1401
+
1402
+ arguments, block_node = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1403
+
1404
+ if node.closing_loc
1405
+ bounds(node.closing_loc)
1406
+ on_rparen(")")
1407
+ end
1408
+
1409
+ block = visit(block_node)
1410
+ call =
1411
+ if node.opening_loc.nil?
1412
+ bounds(node.location)
1413
+
1414
+ if node.arguments.nil? && !node.block.is_a?(BlockArgumentNode)
1415
+ on_call(receiver, call_operator, message)
1416
+ else
1417
+ on_command_call(receiver, call_operator, message, arguments)
1418
+ end
1419
+ else
1420
+ bounds(node.opening_loc)
1421
+ arguments = on_arg_paren(arguments)
1422
+
1423
+ bounds(node.location)
1424
+ on_method_add_arg(on_call(receiver, call_operator, message), arguments)
1425
+ end
1426
+
1427
+ if block_node
1428
+ bounds(node.block.location)
1429
+ on_method_add_block(call, block)
1430
+ else
1431
+ call
1432
+ end
1433
+ end
1434
+ end
1435
+ end
1436
+
1437
+ # Extract the arguments and block Ripper-style, which means if the block
1438
+ # is like `&b` then it's moved to arguments.
1439
+ private def get_arguments_and_block(arguments_node, block_node)
1440
+ arguments = arguments_node&.arguments || []
1441
+ block = block_node
1442
+
1443
+ if block.is_a?(BlockArgumentNode)
1444
+ arguments += [block]
1445
+ block = nil
1446
+ end
1447
+
1448
+ [arguments, block]
1449
+ end
1450
+
1451
+ # Visit the arguments and block of a call node and return the arguments
1452
+ # and block as they should be used.
1453
+ private def visit_call_node_arguments(arguments_node, block_node, trailing_comma)
1454
+ arguments, block = get_arguments_and_block(arguments_node, block_node)
1455
+
1456
+ [
1457
+ if arguments.length == 1 && arguments.first.is_a?(ForwardingArgumentsNode)
1458
+ visit(arguments.first)
1459
+ elsif arguments.any?
1460
+ args = visit_arguments(arguments)
1461
+
1462
+ if block_node.is_a?(BlockArgumentNode) || arguments.last.is_a?(ForwardingArgumentsNode) || command?(arguments.last) || trailing_comma
1463
+ args
1464
+ else
1465
+ bounds(arguments.first.location)
1466
+ on_args_add_block(args, false)
1467
+ end
1468
+ end,
1469
+ block,
1470
+ ]
1471
+ end
1472
+
1473
+ # Returns true if the given node is a command node.
1474
+ private def command?(node)
1475
+ node.is_a?(CallNode) &&
1476
+ node.opening_loc.nil? &&
1477
+ (!node.arguments.nil? || node.block.is_a?(BlockArgumentNode)) &&
1478
+ !BINARY_OPERATORS.include?(node.name)
1479
+ end
1480
+
1481
+ # foo.bar += baz
1482
+ # ^^^^^^^^^^^^^^^
1483
+ def visit_call_operator_write_node(node)
1484
+ receiver = visit(node.receiver)
1485
+
1486
+ bounds(node.call_operator_loc)
1487
+ call_operator = visit_call_operator(node.call_operator)
1488
+
1489
+ bounds(node.message_loc)
1490
+ message = visit_token(node.message)
1491
+
1492
+ bounds(node.location)
1493
+ target = on_field(receiver, call_operator, message)
1494
+
1495
+ bounds(node.binary_operator_loc)
1496
+ operator = on_op("#{node.binary_operator}=")
1497
+ value = visit_write_value(node.value)
1498
+
1499
+ bounds(node.location)
1500
+ on_opassign(target, operator, value)
1501
+ end
1502
+
1503
+ # foo.bar &&= baz
1504
+ # ^^^^^^^^^^^^^^^
1505
+ def visit_call_and_write_node(node)
1506
+ receiver = visit(node.receiver)
1507
+
1508
+ bounds(node.call_operator_loc)
1509
+ call_operator = visit_call_operator(node.call_operator)
1510
+
1511
+ bounds(node.message_loc)
1512
+ message = visit_token(node.message)
1513
+
1514
+ bounds(node.location)
1515
+ target = on_field(receiver, call_operator, message)
1516
+
1517
+ bounds(node.operator_loc)
1518
+ operator = on_op("&&=")
1519
+ value = visit_write_value(node.value)
1520
+
1521
+ bounds(node.location)
1522
+ on_opassign(target, operator, value)
1523
+ end
1524
+
1525
+ # foo.bar ||= baz
1526
+ # ^^^^^^^^^^^^^^^
1527
+ def visit_call_or_write_node(node)
1528
+ receiver = visit(node.receiver)
1529
+
1530
+ bounds(node.call_operator_loc)
1531
+ call_operator = visit_call_operator(node.call_operator)
1532
+
1533
+ bounds(node.message_loc)
1534
+ message = visit_token(node.message)
1535
+
1536
+ bounds(node.location)
1537
+ target = on_field(receiver, call_operator, message)
1538
+
1539
+ bounds(node.operator_loc)
1540
+ operator = on_op("||=")
1541
+ value = visit_write_value(node.value)
1542
+
1543
+ bounds(node.location)
1544
+ on_opassign(target, operator, value)
1545
+ end
1546
+
1547
+ # foo.bar, = 1
1548
+ # ^^^^^^^
1549
+ def visit_call_target_node(node)
1550
+ if node.call_operator == "::"
1551
+ receiver = visit(node.receiver)
1552
+
1553
+ bounds(node.call_operator_loc)
1554
+ on_op("::")
1555
+
1556
+ bounds(node.message_loc)
1557
+ message = visit_token(node.message)
1558
+
1559
+ bounds(node.location)
1560
+ on_const_path_field(receiver, message)
1561
+ else
1562
+ receiver = visit(node.receiver)
1563
+
1564
+ bounds(node.call_operator_loc)
1565
+ call_operator = visit_call_operator(node.call_operator)
1566
+
1567
+ bounds(node.message_loc)
1568
+ message = visit_token(node.message)
1569
+
1570
+ bounds(node.location)
1571
+ on_field(receiver, call_operator, message)
1572
+ end
1573
+ end
1574
+
1575
+ # foo => bar => baz
1576
+ # ^^^^^^^^^^
1577
+ def visit_capture_pattern_node(node)
1578
+ value = visit(node.value)
1579
+
1580
+ bounds(node.operator_loc)
1581
+ on_op("=>")
1582
+
1583
+ target = visit(node.target)
1584
+
1585
+ bounds(node.location)
1586
+ on_binary(value, :"=>", target)
1587
+ end
1588
+
1589
+ # case foo; when bar; end
1590
+ # ^^^^^^^^^^^^^^^^^^^^^^^
1591
+ def visit_case_node(node)
1592
+ bounds(node.case_keyword_loc)
1593
+ on_kw("case")
1594
+
1595
+ predicate = visit(node.predicate)
1596
+ visited_conditions = node.conditions.map { |condition| visit(condition) }
1597
+ visited_else_clause = visit(node.else_clause)
1598
+
1599
+ if !node.else_clause
1600
+ bounds(node.end_keyword_loc)
1601
+ on_kw("end")
1602
+ end
1603
+
1604
+ clauses =
1605
+ visited_conditions.reverse_each.inject(visited_else_clause) do |current, condition|
1606
+ on_when(*condition, current)
1607
+ end
1608
+
1609
+ bounds(node.location)
1610
+ on_case(predicate, clauses)
1611
+ end
1612
+
1613
+ # case foo; in bar; end
1614
+ # ^^^^^^^^^^^^^^^^^^^^^
1615
+ def visit_case_match_node(node)
1616
+ bounds(node.case_keyword_loc)
1617
+ on_kw("case")
1618
+
1619
+ predicate = visit(node.predicate)
1620
+ visited_conditions = node.conditions.map do | condition|
1621
+ visit(condition)
1622
+ end
1623
+ visited_else_clause = visit(node.else_clause)
1624
+
1625
+ if !node.else_clause
1626
+ bounds(node.end_keyword_loc)
1627
+ on_kw("end")
1628
+ end
1629
+
1630
+ clauses =
1631
+ visited_conditions.reverse_each.inject(visited_else_clause) do |current, condition|
1632
+ on_in(*condition, current)
1633
+ end
1634
+
1635
+ bounds(node.location)
1636
+ on_case(predicate, clauses)
1637
+ end
1638
+
1639
+ # class Foo; end
1640
+ # ^^^^^^^^^^^^^^
1641
+ def visit_class_node(node)
1642
+ bounds(node.class_keyword_loc)
1643
+ on_kw("class")
1644
+
1645
+ constant_path =
1646
+ if node.constant_path.is_a?(ConstantReadNode)
1647
+ bounds(node.constant_path.location)
1648
+ on_const_ref(on_const(node.constant_path.name.to_s))
1649
+ else
1650
+ visit(node.constant_path)
1651
+ end
1652
+
1653
+ if node.inheritance_operator_loc
1654
+ bounds(node.inheritance_operator_loc)
1655
+ on_op("<")
1656
+ end
1657
+
1658
+ superclass = visit(node.superclass)
1659
+ bodystmt = visit_body_node(node.superclass&.location || node.constant_path.location, node.body, node.superclass.nil?)
1660
+
1661
+ bounds(node.end_keyword_loc)
1662
+ on_kw("end")
1663
+
1664
+ bounds(node.location)
1665
+ on_class(constant_path, superclass, bodystmt)
1666
+ end
1667
+
1668
+ # @@foo
1669
+ # ^^^^^
1670
+ def visit_class_variable_read_node(node)
1671
+ bounds(node.location)
1672
+ on_var_ref(on_cvar(node.slice))
1673
+ end
1674
+
1675
+ # @@foo = 1
1676
+ # ^^^^^^^^^
1677
+ def visit_class_variable_write_node(node)
1678
+ bounds(node.name_loc)
1679
+ target = on_var_field(on_cvar(node.name.to_s))
1680
+
1681
+ bounds(node.operator_loc)
1682
+ on_op("=")
1683
+
1684
+ value = visit_write_value(node.value)
1685
+
1686
+ bounds(node.location)
1687
+ on_assign(target, value)
1688
+ end
1689
+
1690
+ # @@foo += bar
1691
+ # ^^^^^^^^^^^^
1692
+ def visit_class_variable_operator_write_node(node)
1693
+ bounds(node.name_loc)
1694
+ target = on_var_field(on_cvar(node.name.to_s))
1695
+
1696
+ bounds(node.binary_operator_loc)
1697
+ operator = on_op("#{node.binary_operator}=")
1698
+ value = visit_write_value(node.value)
1699
+
1700
+ bounds(node.location)
1701
+ on_opassign(target, operator, value)
1702
+ end
1703
+
1704
+ # @@foo &&= bar
1705
+ # ^^^^^^^^^^^^^
1706
+ def visit_class_variable_and_write_node(node)
1707
+ bounds(node.name_loc)
1708
+ target = on_var_field(on_cvar(node.name.to_s))
1709
+
1710
+ bounds(node.operator_loc)
1711
+ operator = on_op("&&=")
1712
+ value = visit_write_value(node.value)
1713
+
1714
+ bounds(node.location)
1715
+ on_opassign(target, operator, value)
1716
+ end
1717
+
1718
+ # @@foo ||= bar
1719
+ # ^^^^^^^^^^^^^
1720
+ def visit_class_variable_or_write_node(node)
1721
+ bounds(node.name_loc)
1722
+ target = on_var_field(on_cvar(node.name.to_s))
1723
+
1724
+ bounds(node.operator_loc)
1725
+ operator = on_op("||=")
1726
+ value = visit_write_value(node.value)
1727
+
1728
+ bounds(node.location)
1729
+ on_opassign(target, operator, value)
1730
+ end
1731
+
1732
+ # @@foo, = bar
1733
+ # ^^^^^
1734
+ def visit_class_variable_target_node(node)
1735
+ bounds(node.location)
1736
+ on_var_field(on_cvar(node.name.to_s))
1737
+ end
1738
+
1739
+ # Foo
1740
+ # ^^^
1741
+ def visit_constant_read_node(node)
1742
+ bounds(node.location)
1743
+ on_var_ref(on_const(node.name.to_s))
1744
+ end
1745
+
1746
+ # Foo = 1
1747
+ # ^^^^^^^
1748
+ def visit_constant_write_node(node)
1749
+ bounds(node.name_loc)
1750
+ target = on_var_field(on_const(node.name.to_s))
1751
+
1752
+ bounds(node.operator_loc)
1753
+ on_op("=")
1754
+
1755
+ value = visit_write_value(node.value)
1756
+
1757
+ bounds(node.location)
1758
+ on_assign(target, value)
1759
+ end
1760
+
1761
+ # Foo += bar
1762
+ # ^^^^^^^^^^^
1763
+ def visit_constant_operator_write_node(node)
1764
+ bounds(node.name_loc)
1765
+ target = on_var_field(on_const(node.name.to_s))
1766
+
1767
+ bounds(node.binary_operator_loc)
1768
+ operator = on_op("#{node.binary_operator}=")
1769
+ value = visit_write_value(node.value)
1770
+
1771
+ bounds(node.location)
1772
+ on_opassign(target, operator, value)
1773
+ end
1774
+
1775
+ # Foo &&= bar
1776
+ # ^^^^^^^^^^^^
1777
+ def visit_constant_and_write_node(node)
1778
+ bounds(node.name_loc)
1779
+ target = on_var_field(on_const(node.name.to_s))
1780
+
1781
+ bounds(node.operator_loc)
1782
+ operator = on_op("&&=")
1783
+ value = visit_write_value(node.value)
1784
+
1785
+ bounds(node.location)
1786
+ on_opassign(target, operator, value)
1787
+ end
1788
+
1789
+ # Foo ||= bar
1790
+ # ^^^^^^^^^^^^
1791
+ def visit_constant_or_write_node(node)
1792
+ bounds(node.name_loc)
1793
+ target = on_var_field(on_const(node.name.to_s))
1794
+
1795
+ bounds(node.operator_loc)
1796
+ operator = on_op("||=")
1797
+ value = visit_write_value(node.value)
1798
+
1799
+ bounds(node.location)
1800
+ on_opassign(target, operator, value)
1801
+ end
1802
+
1803
+ # Foo, = bar
1804
+ # ^^^
1805
+ def visit_constant_target_node(node)
1806
+ bounds(node.location)
1807
+ on_var_field(on_const(node.name.to_s))
1808
+ end
1809
+
1810
+ # Foo::Bar
1811
+ # ^^^^^^^^
1812
+ def visit_constant_path_node(node)
1813
+ if node.parent.nil?
1814
+ if node.delimiter_loc
1815
+ bounds(node.delimiter_loc)
1816
+ on_op("::")
1817
+ end
1818
+
1819
+ bounds(node.name_loc)
1820
+ child = on_const(node.name.to_s)
1821
+
1822
+ bounds(node.location)
1823
+ on_top_const_ref(child)
1824
+ else
1825
+ parent = visit(node.parent)
1826
+
1827
+ bounds(node.delimiter_loc)
1828
+ on_op("::")
1829
+
1830
+ bounds(node.name_loc)
1831
+ child = on_const(node.name.to_s)
1832
+
1833
+ bounds(node.location)
1834
+ on_const_path_ref(parent, child)
1835
+ end
1836
+ end
1837
+
1838
+ # Foo::Bar = 1
1839
+ # ^^^^^^^^^^^^
1840
+ def visit_constant_path_write_node(node)
1841
+ target = visit_constant_path_write_node_target(node.target)
1842
+
1843
+ bounds(node.operator_loc)
1844
+ on_op("=")
1845
+
1846
+ value = visit_write_value(node.value)
1847
+
1848
+ bounds(node.location)
1849
+ on_assign(target, value)
1850
+ end
1851
+
1852
+ # Visit a constant path that is part of a write node.
1853
+ private def visit_constant_path_write_node_target(node)
1854
+ if node.parent.nil?
1855
+ if node.delimiter_loc
1856
+ bounds(node.delimiter_loc)
1857
+ on_op("::")
1858
+ end
1859
+
1860
+ bounds(node.name_loc)
1861
+ child = on_const(node.name.to_s)
1862
+
1863
+ bounds(node.location)
1864
+ on_top_const_field(child)
1865
+ else
1866
+ parent = visit(node.parent)
1867
+
1868
+ bounds(node.delimiter_loc)
1869
+ on_op("::")
1870
+
1871
+ bounds(node.name_loc)
1872
+ child = on_const(node.name.to_s)
1873
+
1874
+ bounds(node.location)
1875
+ on_const_path_field(parent, child)
1876
+ end
1877
+ end
1878
+
1879
+ # Foo::Bar += baz
1880
+ # ^^^^^^^^^^^^^^^
1881
+ def visit_constant_path_operator_write_node(node)
1882
+ target = visit_constant_path_write_node_target(node.target)
1883
+
1884
+ bounds(node.binary_operator_loc)
1885
+ operator = on_op("#{node.binary_operator}=")
1886
+ value = visit_write_value(node.value)
1887
+
1888
+ bounds(node.location)
1889
+ on_opassign(target, operator, value)
1890
+ end
1891
+
1892
+ # Foo::Bar &&= baz
1893
+ # ^^^^^^^^^^^^^^^^
1894
+ def visit_constant_path_and_write_node(node)
1895
+ target = visit_constant_path_write_node_target(node.target)
1896
+
1897
+ bounds(node.operator_loc)
1898
+ operator = on_op("&&=")
1899
+ value = visit_write_value(node.value)
1900
+
1901
+ bounds(node.location)
1902
+ on_opassign(target, operator, value)
1903
+ end
1904
+
1905
+ # Foo::Bar ||= baz
1906
+ # ^^^^^^^^^^^^^^^^
1907
+ def visit_constant_path_or_write_node(node)
1908
+ target = visit_constant_path_write_node_target(node.target)
1909
+
1910
+ bounds(node.operator_loc)
1911
+ operator = on_op("||=")
1912
+ value = visit_write_value(node.value)
1913
+
1914
+ bounds(node.location)
1915
+ on_opassign(target, operator, value)
1916
+ end
1917
+
1918
+ # Foo::Bar, = baz
1919
+ # ^^^^^^^^
1920
+ def visit_constant_path_target_node(node)
1921
+ visit_constant_path_write_node_target(node)
1922
+ end
1923
+
1924
+ # def foo; end
1925
+ # ^^^^^^^^^^^^
1926
+ #
1927
+ # def self.foo; end
1928
+ # ^^^^^^^^^^^^^^^^^
1929
+ def visit_def_node(node)
1930
+ bounds(node.def_keyword_loc)
1931
+ on_kw("def")
1932
+
1933
+ receiver = visit(node.receiver)
1934
+ operator =
1935
+ if !node.operator_loc.nil?
1936
+ bounds(node.operator_loc)
1937
+ node.operator == "." ? on_period(".") : on_op("::")
1938
+ end
1939
+
1940
+ bounds(node.name_loc)
1941
+ name = visit_token(node.name_loc.slice)
1942
+
1943
+ if node.lparen_loc
1944
+ bounds(node.lparen_loc)
1945
+ on_lparen("(")
1946
+ end
1947
+
1948
+ parameters =
1949
+ if node.parameters.nil?
1950
+ bounds(node.location)
1951
+ on_params(nil, nil, nil, nil, nil, nil, nil)
1952
+ else
1953
+ visit(node.parameters)
1954
+ end
1955
+
1956
+ if !node.lparen_loc.nil?
1957
+ bounds(node.rparen_loc)
1958
+ on_rparen(")")
1959
+ bounds(node.lparen_loc)
1960
+ parameters = on_paren(parameters)
1961
+ end
1962
+
1963
+ if node.equal_loc
1964
+ bounds(node.equal_loc)
1965
+ on_op("=")
1966
+ end
1967
+
1968
+ bodystmt =
1969
+ if node.equal_loc.nil?
1970
+ visit_body_node(node.rparen_loc || node.end_keyword_loc, node.body)
1971
+ else
1972
+ body = visit(node.body.body.first)
1973
+
1974
+ bounds(node.body.location)
1975
+ on_bodystmt(body, nil, nil, nil)
1976
+ end
1977
+
1978
+ if node.end_keyword_loc
1979
+ bounds(node.end_keyword_loc)
1980
+ on_kw("end")
1981
+ end
1982
+
1983
+ bounds(node.location)
1984
+ if receiver
1985
+ on_defs(receiver, operator, name, parameters, bodystmt)
1986
+ else
1987
+ on_def(name, parameters, bodystmt)
1988
+ end
1989
+ end
1990
+
1991
+ # defined? a
1992
+ # ^^^^^^^^^^
1993
+ #
1994
+ # defined?(a)
1995
+ # ^^^^^^^^^^^
1996
+ def visit_defined_node(node)
1997
+ bounds(node.keyword_loc)
1998
+ on_kw("defined?")
1999
+
2000
+ if node.lparen_loc
2001
+ bounds(node.lparen_loc)
2002
+ on_lparen("(")
2003
+ end
2004
+
2005
+ expression = visit(node.value)
2006
+
2007
+ if node.rparen_loc
2008
+ bounds(node.rparen_loc)
2009
+ on_rparen(")")
2010
+ end
2011
+
2012
+ # Very weird circumstances here where something like:
2013
+ #
2014
+ # defined?
2015
+ # (1)
2016
+ #
2017
+ # gets parsed in Ruby as having only the `1` expression but in Ripper it
2018
+ # gets parsed as having a parentheses node. In this case we need to
2019
+ # synthesize that node to match Ripper's behavior.
2020
+ if node.lparen_loc && node.keyword_loc.join(node.lparen_loc).slice.include?("\n")
2021
+ bounds(node.lparen_loc.join(node.rparen_loc))
2022
+ expression = on_paren(on_stmts_add(on_stmts_new, expression))
2023
+ end
2024
+
2025
+ bounds(node.location)
2026
+ on_defined(expression)
2027
+ end
2028
+
2029
+ # if foo then bar else baz end
2030
+ # ^^^^^^^^^^^^
2031
+ def visit_else_node(node)
2032
+ bounds(node.else_keyword_loc)
2033
+ on_kw("else")
2034
+
2035
+ statements =
2036
+ if node.statements.nil?
2037
+ [nil]
2038
+ else
2039
+ body = node.statements.body
2040
+ body = [nil, *body] if void_stmt?(node.else_keyword_loc, node.statements.body[0].location, false)
2041
+ body
2042
+ end
2043
+
2044
+ else_statements = visit_statements_node_body(statements)
2045
+
2046
+ bounds(node.end_keyword_loc)
2047
+ on_kw("end")
2048
+ bounds(node.location)
2049
+ on_else(else_statements)
2050
+ end
2051
+
2052
+ # "foo #{bar}"
2053
+ # ^^^^^^
2054
+ def visit_embedded_statements_node(node)
2055
+ bounds(node.opening_loc)
2056
+ on_embexpr_beg(node.opening)
2057
+
2058
+ statements =
2059
+ if node.statements.nil?
2060
+ bounds(node.location)
2061
+ on_stmts_add(on_stmts_new, on_void_stmt)
2062
+ else
2063
+ visit(node.statements)
2064
+ end
2065
+
2066
+ bounds(node.closing_loc)
2067
+ on_embexpr_end(node.closing)
2068
+
2069
+ bounds(node.location)
2070
+ on_string_embexpr(statements)
2071
+ end
2072
+
2073
+ # "foo #@bar"
2074
+ # ^^^^^
2075
+ def visit_embedded_variable_node(node)
2076
+ bounds(node.operator_loc)
2077
+ on_embvar(node.operator)
2078
+
2079
+ variable = visit(node.variable)
2080
+
2081
+ bounds(node.location)
2082
+ on_string_dvar(variable)
2083
+ end
2084
+
2085
+ # Visit an EnsureNode node.
2086
+ def visit_ensure_node(node)
2087
+ bounds(node.ensure_keyword_loc)
2088
+ on_kw("ensure")
2089
+
2090
+ statements =
2091
+ if node.statements.nil?
2092
+ [nil]
2093
+ else
2094
+ body = node.statements.body
2095
+ body = [nil, *body] if void_stmt?(node.ensure_keyword_loc, body[0].location, false)
2096
+ body
2097
+ end
2098
+
2099
+ statements = visit_statements_node_body(statements)
2100
+
2101
+ bounds(node.location)
2102
+ on_ensure(statements)
2103
+ end
2104
+
2105
+ # false
2106
+ # ^^^^^
2107
+ def visit_false_node(node)
2108
+ bounds(node.location)
2109
+ on_var_ref(on_kw("false"))
2110
+ end
2111
+
2112
+ # foo => [*, bar, *]
2113
+ # ^^^^^^^^^^^
2114
+ def visit_find_pattern_node(node)
2115
+ constant = visit(node.constant)
2116
+
2117
+ if node.opening_loc
2118
+ bounds(node.opening_loc)
2119
+ node.opening == "[" ? on_lbracket("[") : on_lparen("(")
2120
+ end
2121
+ bounds(node.left.operator_loc)
2122
+ on_op("*")
2123
+
2124
+ left =
2125
+ if node.left.expression.nil?
2126
+ bounds(node.left.location)
2127
+ on_var_field(nil)
2128
+ else
2129
+ visit(node.left.expression)
2130
+ end
2131
+
2132
+ requireds = visit_all(node.requireds) if node.requireds.any?
2133
+
2134
+ bounds(node.right.operator_loc)
2135
+ on_op("*")
2136
+
2137
+ right =
2138
+ if node.right.expression.nil?
2139
+ bounds(node.right.location)
2140
+ on_var_field(nil)
2141
+ else
2142
+ visit(node.right.expression)
2143
+ end
2144
+
2145
+ if node.closing_loc
2146
+ bounds(node.closing_loc)
2147
+ node.closing == "]" ? on_rbracket("]") : on_rparen(")")
2148
+ end
2149
+ bounds(node.location)
2150
+ on_fndptn(constant, left, requireds, right)
2151
+ end
2152
+
2153
+ # if foo .. bar; end
2154
+ # ^^^^^^^^^^
2155
+ def visit_flip_flop_node(node)
2156
+ left = visit(node.left)
2157
+
2158
+ bounds(node.operator_loc)
2159
+ on_op(node.operator)
2160
+
2161
+ right = visit(node.right)
2162
+
2163
+ bounds(node.location)
2164
+ if node.exclude_end?
2165
+ on_dot3(left, right)
2166
+ else
2167
+ on_dot2(left, right)
2168
+ end
2169
+ end
2170
+
2171
+ # 1.0
2172
+ # ^^^
2173
+ def visit_float_node(node)
2174
+ visit_number_node(node) { |text| on_float(text) }
2175
+ end
2176
+
2177
+ # for foo in bar do end
2178
+ # ^^^^^^^^^^^^^^^^^^^^^
2179
+ def visit_for_node(node)
2180
+ bounds(node.for_keyword_loc)
2181
+ on_kw("for")
2182
+
2183
+ index = visit(node.index)
2184
+ bounds(node.in_keyword_loc)
2185
+ on_kw("in")
2186
+
2187
+ collection = visit(node.collection)
2188
+ if node.do_keyword_loc
2189
+ bounds(node.do_keyword_loc)
2190
+ on_kw("do")
2191
+ end
2192
+ statements =
2193
+ if node.statements.nil?
2194
+ bounds(node.location)
2195
+ on_stmts_add(on_stmts_new, on_void_stmt)
2196
+ else
2197
+ visit(node.statements)
2198
+ end
2199
+
2200
+ bounds(node.end_keyword_loc)
2201
+ on_kw("end")
2202
+
2203
+ bounds(node.location)
2204
+ on_for(index, collection, statements)
2205
+ end
2206
+
2207
+ # def foo(...); bar(...); end
2208
+ # ^^^
2209
+ def visit_forwarding_arguments_node(node)
2210
+ bounds(node.location)
2211
+ on_op("...")
2212
+ on_args_forward
2213
+ end
2214
+
2215
+ # def foo(...); end
2216
+ # ^^^
2217
+ def visit_forwarding_parameter_node(node)
2218
+ bounds(node.location)
2219
+ on_op("...")
2220
+ on_args_forward
2221
+ end
2222
+
2223
+ # super
2224
+ # ^^^^^
2225
+ #
2226
+ # super {}
2227
+ # ^^^^^^^^
2228
+ def visit_forwarding_super_node(node)
2229
+ bounds(node.keyword_loc)
2230
+ on_kw("super")
2231
+
2232
+ if node.block.nil?
2233
+ bounds(node.location)
2234
+ on_zsuper
2235
+ else
2236
+ block = visit(node.block)
2237
+
2238
+ bounds(node.location)
2239
+ on_method_add_block(on_zsuper, block)
2240
+ end
2241
+ end
2242
+
2243
+ # $foo
2244
+ # ^^^^
2245
+ def visit_global_variable_read_node(node)
2246
+ bounds(node.location)
2247
+ on_var_ref(on_gvar(node.name.to_s))
2248
+ end
2249
+
2250
+ # $foo = 1
2251
+ # ^^^^^^^^
2252
+ def visit_global_variable_write_node(node)
2253
+ bounds(node.name_loc)
2254
+ target = on_var_field(on_gvar(node.name.to_s))
2255
+
2256
+ bounds(node.operator_loc)
2257
+ on_op("=")
2258
+
2259
+ value = visit_write_value(node.value)
2260
+
2261
+ bounds(node.location)
2262
+ on_assign(target, value)
2263
+ end
2264
+
2265
+ # $foo += bar
2266
+ # ^^^^^^^^^^^
2267
+ def visit_global_variable_operator_write_node(node)
2268
+ bounds(node.name_loc)
2269
+ target = on_var_field(on_gvar(node.name.to_s))
2270
+
2271
+ bounds(node.binary_operator_loc)
2272
+ operator = on_op("#{node.binary_operator}=")
2273
+ value = visit_write_value(node.value)
2274
+
2275
+ bounds(node.location)
2276
+ on_opassign(target, operator, value)
2277
+ end
2278
+
2279
+ # $foo &&= bar
2280
+ # ^^^^^^^^^^^^
2281
+ def visit_global_variable_and_write_node(node)
2282
+ bounds(node.name_loc)
2283
+ target = on_var_field(on_gvar(node.name.to_s))
2284
+
2285
+ bounds(node.operator_loc)
2286
+ operator = on_op("&&=")
2287
+ value = visit_write_value(node.value)
2288
+
2289
+ bounds(node.location)
2290
+ on_opassign(target, operator, value)
2291
+ end
2292
+
2293
+ # $foo ||= bar
2294
+ # ^^^^^^^^^^^^
2295
+ def visit_global_variable_or_write_node(node)
2296
+ bounds(node.name_loc)
2297
+ target = on_var_field(on_gvar(node.name.to_s))
2298
+
2299
+ bounds(node.operator_loc)
2300
+ operator = on_op("||=")
2301
+ value = visit_write_value(node.value)
2302
+
2303
+ bounds(node.location)
2304
+ on_opassign(target, operator, value)
2305
+ end
2306
+
2307
+ # $foo, = bar
2308
+ # ^^^^
2309
+ def visit_global_variable_target_node(node)
2310
+ bounds(node.location)
2311
+ on_var_field(on_gvar(node.name.to_s))
2312
+ end
2313
+
2314
+ # {}
2315
+ # ^^
2316
+ def visit_hash_node(node)
2317
+ bounds(node.opening_loc)
2318
+ on_lbrace("{")
2319
+
2320
+ elements =
2321
+ if node.elements.any?
2322
+ args = visit_all(node.elements)
2323
+
2324
+ bounds(node.elements.first.location)
2325
+ on_assoclist_from_args(args)
2326
+ end
2327
+
2328
+ bounds(node.closing_loc)
2329
+ on_rbrace("}")
2330
+ bounds(node.location)
2331
+ on_hash(elements)
2332
+ end
2333
+
2334
+ # foo => {}
2335
+ # ^^
2336
+ def visit_hash_pattern_node(node)
2337
+ constant = visit(node.constant)
2338
+
2339
+ if node.constant
2340
+ bounds(node.opening_loc)
2341
+ node.opening == "[" ? on_lbracket("[") : on_lparen("(")
2342
+ elsif node.opening_loc
2343
+ bounds(node.opening_loc)
2344
+ on_lbrace("{")
2345
+ end
2346
+
2347
+ elements =
2348
+ if node.elements.any? || !node.rest.nil?
2349
+ node.elements.map do |element|
2350
+ [
2351
+ if (key = element.key).opening_loc.nil?
2352
+ visit(key)
2353
+ else
2354
+ bounds(key.content_loc)
2355
+ if (value = key.content).empty?
2356
+ on_string_content
2357
+ else
2358
+ on_string_add(on_string_content, on_tstring_content(value))
2359
+ end
2360
+ end,
2361
+ visit(element.value)
2362
+ ]
2363
+ end
2364
+ end
2365
+
2366
+ rest =
2367
+ case node.rest
2368
+ when AssocSplatNode
2369
+ bounds(node.rest.operator_loc)
2370
+ on_op("**")
2371
+ visit(node.rest.value)
2372
+ when NoKeywordsParameterNode
2373
+ bounds(node.rest.location)
2374
+ on_var_field(visit(node.rest))
2375
+ end
2376
+
2377
+ if node.constant
2378
+ bounds(node.closing_loc)
2379
+ node.closing == "]" ? on_rbracket("]") : on_rparen(")")
2380
+ elsif node.closing_loc
2381
+ bounds(node.closing_loc)
2382
+ on_rbrace("}")
2383
+ end
2384
+ bounds(node.location)
2385
+ on_hshptn(constant, elements, rest)
2386
+ end
2387
+
2388
+ # if foo then bar end
2389
+ # ^^^^^^^^^^^^^^^^^^^
2390
+ #
2391
+ # bar if foo
2392
+ # ^^^^^^^^^^
2393
+ #
2394
+ # foo ? bar : baz
2395
+ # ^^^^^^^^^^^^^^^
2396
+ def visit_if_node(node)
2397
+ if node.then_keyword == "?"
2398
+ predicate = visit(node.predicate)
2399
+
2400
+ bounds(node.then_keyword_loc)
2401
+ on_op("?")
2402
+
2403
+ truthy = visit(node.statements.body.first)
2404
+
2405
+ bounds(node.subsequent.else_keyword_loc)
2406
+ on_op(":")
2407
+
2408
+ falsy = visit(node.subsequent.statements.body.first)
2409
+
2410
+ bounds(node.location)
2411
+ on_ifop(predicate, truthy, falsy)
2412
+ elsif node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
2413
+ bounds(node.if_keyword_loc)
2414
+ on_kw(node.if_keyword)
2415
+ predicate = visit(node.predicate)
2416
+ if node.then_keyword_loc && node.then_keyword != "?"
2417
+ bounds(node.then_keyword_loc)
2418
+ on_kw("then")
2419
+ end
2420
+ statements =
2421
+ if node.statements.nil?
2422
+ bounds(node.location)
2423
+ on_stmts_add(on_stmts_new, on_void_stmt)
2424
+ else
2425
+ visit(node.statements)
2426
+ end
2427
+ subsequent = visit(node.subsequent)
2428
+
2429
+ if node.end_keyword_loc && !node.subsequent
2430
+ bounds(node.end_keyword_loc)
2431
+ on_kw("end")
2432
+ end
2433
+
2434
+ bounds(node.location)
2435
+ if node.if_keyword == "if"
2436
+ on_if(predicate, statements, subsequent)
2437
+ else
2438
+ on_elsif(predicate, statements, subsequent)
2439
+ end
2440
+ else
2441
+ statements = visit(node.statements.body.first)
2442
+ bounds(node.if_keyword_loc)
2443
+ on_kw(node.if_keyword)
2444
+ predicate = visit(node.predicate)
2445
+
2446
+ bounds(node.location)
2447
+ on_if_mod(predicate, statements)
2448
+ end
2449
+ end
2450
+
2451
+ # 1i
2452
+ # ^^
2453
+ def visit_imaginary_node(node)
2454
+ visit_number_node(node) { |text| on_imaginary(text) }
2455
+ end
2456
+
2457
+ # { foo: }
2458
+ # ^^^^
2459
+ def visit_implicit_node(node)
2460
+ end
2461
+
2462
+ # foo { |bar,| }
2463
+ # ^
2464
+ def visit_implicit_rest_node(node)
2465
+ bounds(node.location)
2466
+ on_excessed_comma
2467
+ end
2468
+
2469
+ # case foo; in bar; end
2470
+ # ^^^^^^^^^^^^^^^^^^^^^
2471
+ def visit_in_node(node)
2472
+ # This is a special case where we're not going to call on_in directly
2473
+ # because we don't have access to the subsequent. Instead, we'll return
2474
+ # the component parts and let the parent node handle it.
2475
+ bounds(node.in_keyword_loc)
2476
+ on_kw("in")
2477
+
2478
+ pattern = visit_pattern_node(node.pattern)
2479
+ if node.then_keyword_loc
2480
+ bounds(node.then_keyword_loc)
2481
+ on_kw("then")
2482
+ end
2483
+ statements =
2484
+ if node.statements.nil?
2485
+ bounds(node.location)
2486
+ on_stmts_add(on_stmts_new, on_void_stmt)
2487
+ else
2488
+ visit(node.statements)
2489
+ end
2490
+
2491
+ [pattern, statements]
2492
+ end
2493
+
2494
+ # foo[bar] += baz
2495
+ # ^^^^^^^^^^^^^^^
2496
+ def visit_index_operator_write_node(node)
2497
+ receiver = visit(node.receiver)
2498
+
2499
+ bounds(node.opening_loc)
2500
+ on_lbracket("[")
2501
+
2502
+ arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2503
+
2504
+ bounds(node.closing_loc)
2505
+ on_rbracket("]")
2506
+
2507
+ bounds(node.location)
2508
+ target = on_aref_field(receiver, arguments)
2509
+
2510
+ bounds(node.binary_operator_loc)
2511
+ operator = on_op("#{node.binary_operator}=")
2512
+ value = visit_write_value(node.value)
2513
+
2514
+ bounds(node.location)
2515
+ on_opassign(target, operator, value)
2516
+ end
2517
+
2518
+ # foo[bar] &&= baz
2519
+ # ^^^^^^^^^^^^^^^^
2520
+ def visit_index_and_write_node(node)
2521
+ receiver = visit(node.receiver)
2522
+
2523
+ bounds(node.opening_loc)
2524
+ on_lbracket("[")
2525
+
2526
+ arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2527
+
2528
+ bounds(node.closing_loc)
2529
+ on_rbracket("]")
2530
+
2531
+ bounds(node.location)
2532
+ target = on_aref_field(receiver, arguments)
2533
+
2534
+ bounds(node.operator_loc)
2535
+ operator = on_op("&&=")
2536
+ value = visit_write_value(node.value)
2537
+
2538
+ bounds(node.location)
2539
+ on_opassign(target, operator, value)
2540
+ end
2541
+
2542
+ # foo[bar] ||= baz
2543
+ # ^^^^^^^^^^^^^^^^
2544
+ def visit_index_or_write_node(node)
2545
+ receiver = visit(node.receiver)
2546
+
2547
+ bounds(node.opening_loc)
2548
+ on_lbracket("[")
2549
+
2550
+ arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2551
+
2552
+ bounds(node.closing_loc)
2553
+ on_rbracket("]")
2554
+
2555
+ bounds(node.location)
2556
+ target = on_aref_field(receiver, arguments)
2557
+
2558
+ bounds(node.operator_loc)
2559
+ operator = on_op("||=")
2560
+ value = visit_write_value(node.value)
2561
+
2562
+ bounds(node.location)
2563
+ on_opassign(target, operator, value)
2564
+ end
2565
+
2566
+ # foo[bar], = 1
2567
+ # ^^^^^^^^
2568
+ def visit_index_target_node(node)
2569
+ receiver = visit(node.receiver)
2570
+
2571
+ bounds(node.opening_loc)
2572
+ on_lbracket("[")
2573
+
2574
+ arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2575
+
2576
+ bounds(node.closing_loc)
2577
+ on_rbracket("]")
2578
+
2579
+ bounds(node.location)
2580
+ on_aref_field(receiver, arguments)
2581
+ end
2582
+
2583
+ # @foo
2584
+ # ^^^^
2585
+ def visit_instance_variable_read_node(node)
2586
+ bounds(node.location)
2587
+ on_var_ref(on_ivar(node.name.to_s))
2588
+ end
2589
+
2590
+ # @foo = 1
2591
+ # ^^^^^^^^
2592
+ def visit_instance_variable_write_node(node)
2593
+ bounds(node.name_loc)
2594
+ target = on_var_field(on_ivar(node.name.to_s))
2595
+
2596
+ bounds(node.operator_loc)
2597
+ on_op("=")
2598
+
2599
+ value = visit_write_value(node.value)
2600
+
2601
+ bounds(node.location)
2602
+ on_assign(target, value)
2603
+ end
2604
+
2605
+ # @foo += bar
2606
+ # ^^^^^^^^^^^
2607
+ def visit_instance_variable_operator_write_node(node)
2608
+ bounds(node.name_loc)
2609
+ target = on_var_field(on_ivar(node.name.to_s))
2610
+
2611
+ bounds(node.binary_operator_loc)
2612
+ operator = on_op("#{node.binary_operator}=")
2613
+ value = visit_write_value(node.value)
2614
+
2615
+ bounds(node.location)
2616
+ on_opassign(target, operator, value)
2617
+ end
2618
+
2619
+ # @foo &&= bar
2620
+ # ^^^^^^^^^^^^
2621
+ def visit_instance_variable_and_write_node(node)
2622
+ bounds(node.name_loc)
2623
+ target = on_var_field(on_ivar(node.name.to_s))
2624
+
2625
+ bounds(node.operator_loc)
2626
+ operator = on_op("&&=")
2627
+ value = visit_write_value(node.value)
2628
+
2629
+ bounds(node.location)
2630
+ on_opassign(target, operator, value)
2631
+ end
2632
+
2633
+ # @foo ||= bar
2634
+ # ^^^^^^^^^^^^
2635
+ def visit_instance_variable_or_write_node(node)
2636
+ bounds(node.name_loc)
2637
+ target = on_var_field(on_ivar(node.name.to_s))
2638
+
2639
+ bounds(node.operator_loc)
2640
+ operator = on_op("||=")
2641
+ value = visit_write_value(node.value)
2642
+
2643
+ bounds(node.location)
2644
+ on_opassign(target, operator, value)
2645
+ end
2646
+
2647
+ # @foo, = bar
2648
+ # ^^^^
2649
+ def visit_instance_variable_target_node(node)
2650
+ bounds(node.location)
2651
+ on_var_field(on_ivar(node.name.to_s))
2652
+ end
2653
+
2654
+ # 1
2655
+ # ^
2656
+ def visit_integer_node(node)
2657
+ visit_number_node(node) { |text| on_int(text) }
2658
+ end
2659
+
2660
+ # if /foo #{bar}/ then end
2661
+ # ^^^^^^^^^^^^
2662
+ def visit_interpolated_match_last_line_node(node)
2663
+ bounds(node.opening_loc)
2664
+ on_regexp_beg(node.opening)
2665
+
2666
+ bounds(node.parts.first.location)
2667
+ parts =
2668
+ node.parts.inject(on_regexp_new) do |content, part|
2669
+ on_regexp_add(content, visit_string_content(part))
2670
+ end
2671
+
2672
+ bounds(node.closing_loc)
2673
+ closing = on_regexp_end(node.closing)
2674
+
2675
+ bounds(node.location)
2676
+ on_regexp_literal(parts, closing)
2677
+ end
2678
+
2679
+ # /foo #{bar}/
2680
+ # ^^^^^^^^^^^^
2681
+ def visit_interpolated_regular_expression_node(node)
2682
+ bounds(node.opening_loc)
2683
+ on_regexp_beg(node.opening)
2684
+
2685
+ bounds(node.parts.first.location)
2686
+ parts =
2687
+ node.parts.inject(on_regexp_new) do |content, part|
2688
+ on_regexp_add(content, visit_string_content(part))
2689
+ end
2690
+
2691
+ bounds(node.closing_loc)
2692
+ closing = on_regexp_end(node.closing)
2693
+
2694
+ bounds(node.location)
2695
+ on_regexp_literal(parts, closing)
2696
+ end
2697
+
2698
+ # "foo #{bar}"
2699
+ # ^^^^^^^^^^^^
2700
+ def visit_interpolated_string_node(node)
2701
+ with_string_bounds(node) do
2702
+ if node.opening&.start_with?("<<~")
2703
+ heredoc = visit_heredoc_string_node(node)
2704
+
2705
+ bounds(node.location)
2706
+ on_string_literal(heredoc)
2707
+ elsif !node.heredoc? && node.parts.length > 1 && node.parts.any? { |part| (part.is_a?(StringNode) || part.is_a?(InterpolatedStringNode)) && !part.opening_loc.nil? }
2708
+ first, *rest = node.parts
2709
+ rest.inject(visit(first)) do |content, part|
2710
+ concat = visit(part)
2711
+
2712
+ bounds(part.location)
2713
+ on_string_concat(content, concat)
2714
+ end
2715
+ else
2716
+ bounds(node.parts.first.location)
2717
+ parts =
2718
+ node.parts.inject(on_string_content) do |content, part|
2719
+ on_string_add(content, visit_string_content(part))
2720
+ end
2721
+
2722
+ bounds(node.location)
2723
+ on_string_literal(parts)
2724
+ end
2725
+ end
2726
+ end
2727
+
2728
+ # :"foo #{bar}"
2729
+ # ^^^^^^^^^^^^^
2730
+ def visit_interpolated_symbol_node(node)
2731
+ with_string_bounds(node) do
2732
+ bounds(node.parts.first.location)
2733
+ parts =
2734
+ node.parts.inject(on_string_content) do |content, part|
2735
+ on_string_add(content, visit_string_content(part))
2736
+ end
2737
+
2738
+ bounds(node.location)
2739
+ on_dyna_symbol(parts)
2740
+ end
2741
+ end
2742
+
2743
+ # `foo #{bar}`
2744
+ # ^^^^^^^^^^^^
2745
+ def visit_interpolated_x_string_node(node)
2746
+ with_string_bounds(node) do
2747
+ if node.opening.start_with?("<<~")
2748
+ heredoc = visit_heredoc_x_string_node(node)
2749
+
2750
+ bounds(node.location)
2751
+ on_xstring_literal(heredoc)
2752
+ else
2753
+ bounds(node.parts.first.location)
2754
+ parts =
2755
+ node.parts.inject(on_xstring_new) do |content, part|
2756
+ on_xstring_add(content, visit_string_content(part))
2757
+ end
2758
+
2759
+ bounds(node.location)
2760
+ on_xstring_literal(parts)
2761
+ end
2762
+ end
2763
+ end
2764
+
2765
+ # Visit an individual part of a string-like node.
2766
+ private def visit_string_content(part)
2767
+ if part.is_a?(StringNode)
2768
+ bounds(part.content_loc)
2769
+ on_tstring_content(part.content)
2770
+ else
2771
+ visit(part)
2772
+ end
2773
+ end
2774
+
2775
+ # -> { it }
2776
+ # ^^
2777
+ def visit_it_local_variable_read_node(node)
2778
+ bounds(node.location)
2779
+ on_vcall(on_ident(node.slice))
2780
+ end
2781
+
2782
+ # -> { it }
2783
+ # ^^^^^^^^^
2784
+ def visit_it_parameters_node(node)
2785
+ end
2786
+
2787
+ # foo(bar: baz)
2788
+ # ^^^^^^^^
2789
+ def visit_keyword_hash_node(node)
2790
+ elements = visit_all(node.elements)
2791
+
2792
+ bounds(node.location)
2793
+ on_bare_assoc_hash(elements)
2794
+ end
2795
+
2796
+ # def foo(**bar); end
2797
+ # ^^^^^
2798
+ #
2799
+ # def foo(**); end
2800
+ # ^^
2801
+ def visit_keyword_rest_parameter_node(node)
2802
+ bounds(node.operator_loc)
2803
+ on_op("**")
2804
+
2805
+ if node.name_loc.nil?
2806
+ bounds(node.location)
2807
+ on_kwrest_param(nil)
2808
+ else
2809
+ bounds(node.name_loc)
2810
+ name = on_ident(node.name.to_s)
2811
+
2812
+ bounds(node.location)
2813
+ on_kwrest_param(name)
2814
+ end
2815
+ end
2816
+
2817
+ # -> {}
2818
+ def visit_lambda_node(node)
2819
+ bounds(node.operator_loc)
2820
+ on_tlambda(node.operator)
2821
+
2822
+ parameters =
2823
+ if node.parameters.is_a?(BlockParametersNode)
2824
+ if node.parameters.opening_loc
2825
+ bounds(node.parameters.opening_loc)
2826
+ on_lparen("(")
2827
+ end
2828
+
2829
+ # Ripper does not track block-locals within lambdas, so we skip
2830
+ # directly to the parameters here.
2831
+ params =
2832
+ if node.parameters.parameters.nil?
2833
+ bounds(node.location)
2834
+ on_params(nil, nil, nil, nil, nil, nil, nil)
2835
+ else
2836
+ visit(node.parameters.parameters)
2837
+ end
2838
+
2839
+ visit_all(node.parameters.locals)
2840
+
2841
+ if node.parameters.closing_loc
2842
+ bounds(node.parameters.closing_loc)
2843
+ on_rparen(")")
2844
+ end
2845
+
2846
+ if node.parameters.opening_loc.nil?
2847
+ params
2848
+ else
2849
+ bounds(node.parameters.opening_loc)
2850
+ on_paren(params)
2851
+ end
2852
+ else
2853
+ bounds(node.location)
2854
+ on_params(nil, nil, nil, nil, nil, nil, nil)
2855
+ end
2856
+
2857
+ braces = node.opening == "{"
2858
+ bounds(node.opening_loc)
2859
+ if braces
2860
+ on_tlambeg(node.opening)
2861
+ else
2862
+ on_kw("do")
2863
+ end
2864
+
2865
+ body =
2866
+ case node.body
2867
+ when nil
2868
+ bounds(node.location)
2869
+ stmts = on_stmts_add(on_stmts_new, on_void_stmt)
2870
+
2871
+ bounds(node.location)
2872
+ braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
2873
+ when StatementsNode
2874
+ stmts = node.body.body
2875
+ stmts = [nil, *stmts] if void_stmt?(node.parameters&.location || node.opening_loc, node.body.location, false)
2876
+ stmts = visit_statements_node_body(stmts)
2877
+
2878
+ bounds(node.body.location)
2879
+ braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
2880
+ when BeginNode
2881
+ visit_body_node(node.opening_loc, node.body)
2882
+ else
2883
+ raise
2884
+ end
2885
+
2886
+ bounds(node.closing_loc)
2887
+ if braces
2888
+ on_rbrace("}")
2889
+ else
2890
+ on_kw("end")
2891
+ end
2892
+
2893
+ bounds(node.location)
2894
+ on_lambda(parameters, body)
2895
+ end
2896
+
2897
+ # foo
2898
+ # ^^^
2899
+ def visit_local_variable_read_node(node)
2900
+ bounds(node.location)
2901
+ on_var_ref(on_ident(node.slice))
2902
+ end
2903
+
2904
+ # foo = 1
2905
+ # ^^^^^^^
2906
+ def visit_local_variable_write_node(node)
2907
+ bounds(node.name_loc)
2908
+ target = on_var_field(on_ident(node.name_loc.slice))
2909
+
2910
+ bounds(node.operator_loc)
2911
+ on_op("=")
2912
+
2913
+ value = visit_write_value(node.value)
2914
+
2915
+ bounds(node.location)
2916
+ on_assign(target, value)
2917
+ end
2918
+
2919
+ # foo += bar
2920
+ # ^^^^^^^^^^
2921
+ def visit_local_variable_operator_write_node(node)
2922
+ bounds(node.name_loc)
2923
+ target = on_var_field(on_ident(node.name_loc.slice))
2924
+
2925
+ bounds(node.binary_operator_loc)
2926
+ operator = on_op("#{node.binary_operator}=")
2927
+ value = visit_write_value(node.value)
2928
+
2929
+ bounds(node.location)
2930
+ on_opassign(target, operator, value)
2931
+ end
2932
+
2933
+ # foo &&= bar
2934
+ # ^^^^^^^^^^^
2935
+ def visit_local_variable_and_write_node(node)
2936
+ bounds(node.name_loc)
2937
+ target = on_var_field(on_ident(node.name_loc.slice))
2938
+
2939
+ bounds(node.operator_loc)
2940
+ operator = on_op("&&=")
2941
+ value = visit_write_value(node.value)
2942
+
2943
+ bounds(node.location)
2944
+ on_opassign(target, operator, value)
2945
+ end
2946
+
2947
+ # foo ||= bar
2948
+ # ^^^^^^^^^^^
2949
+ def visit_local_variable_or_write_node(node)
2950
+ bounds(node.name_loc)
2951
+ target = on_var_field(on_ident(node.name_loc.slice))
2952
+
2953
+ bounds(node.operator_loc)
2954
+ operator = on_op("||=")
2955
+ value = visit_write_value(node.value)
2956
+
2957
+ bounds(node.location)
2958
+ on_opassign(target, operator, value)
2959
+ end
2960
+
2961
+ # foo, = bar
2962
+ # ^^^
2963
+ def visit_local_variable_target_node(node)
2964
+ bounds(node.location)
2965
+ on_var_field(on_ident(node.name.to_s))
2966
+ end
2967
+
2968
+ # if /foo/ then end
2969
+ # ^^^^^
2970
+ def visit_match_last_line_node(node)
2971
+ bounds(node.opening_loc)
2972
+ on_regexp_beg(node.opening)
2973
+
2974
+ bounds(node.content_loc)
2975
+ tstring_content = on_tstring_content(node.content)
2976
+
2977
+ bounds(node.closing_loc)
2978
+ closing = on_regexp_end(node.closing)
2979
+
2980
+ on_regexp_literal(on_regexp_add(on_regexp_new, tstring_content), closing)
2981
+ end
2982
+
2983
+ # foo in bar
2984
+ # ^^^^^^^^^^
2985
+ def visit_match_predicate_node(node)
2986
+ value = visit(node.value)
2987
+ bounds(node.keyword_loc)
2988
+ on_kw("in")
2989
+ pattern = on_in(visit_pattern_node(node.pattern), nil, nil)
2990
+
2991
+ on_case(value, pattern)
2992
+ end
2993
+
2994
+ # foo => bar
2995
+ # ^^^^^^^^^^
2996
+ def visit_match_required_node(node)
2997
+ value = visit(node.value)
2998
+
2999
+ bounds(node.operator_loc)
3000
+ on_op("=>")
3001
+
3002
+ pattern = on_in(visit_pattern_node(node.pattern), nil, nil)
3003
+
3004
+ on_case(value, pattern)
3005
+ end
3006
+
3007
+ # /(?<foo>foo)/ =~ bar
3008
+ # ^^^^^^^^^^^^^^^^^^^^
3009
+ def visit_match_write_node(node)
3010
+ visit(node.call)
3011
+ end
3012
+
3013
+ # A node that is missing from the syntax tree. This is only used in the
3014
+ # case of a syntax error.
3015
+ def visit_error_recovery_node(node)
3016
+ raise "Cannot visit error recovery nodes directly."
3017
+ end
3018
+
3019
+ # module Foo; end
3020
+ # ^^^^^^^^^^^^^^^
3021
+ def visit_module_node(node)
3022
+ bounds(node.module_keyword_loc)
3023
+ on_kw("module")
3024
+
3025
+ constant_path =
3026
+ if node.constant_path.is_a?(ConstantReadNode)
3027
+ bounds(node.constant_path.location)
3028
+ on_const_ref(on_const(node.constant_path.name.to_s))
3029
+ else
3030
+ visit(node.constant_path)
3031
+ end
3032
+
3033
+ bodystmt = visit_body_node(node.constant_path.location, node.body, true)
3034
+
3035
+ bounds(node.end_keyword_loc)
3036
+ on_kw("end")
3037
+
3038
+ bounds(node.location)
3039
+ on_module(constant_path, bodystmt)
3040
+ end
3041
+
3042
+ # (foo, bar), bar = qux
3043
+ # ^^^^^^^^^^
3044
+ def visit_multi_target_node(node)
3045
+ if node.lparen_loc
3046
+ bounds(node.lparen_loc)
3047
+ on_lparen("(")
3048
+ end
3049
+
3050
+ bounds(node.location)
3051
+ targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, true)
3052
+
3053
+ if node.rparen_loc
3054
+ bounds(node.rparen_loc)
3055
+ on_rparen(")")
3056
+ end
3057
+
3058
+ if node.lparen_loc.nil?
3059
+ targets
3060
+ else
3061
+ bounds(node.lparen_loc)
3062
+ on_mlhs_paren(targets)
3063
+ end
3064
+ end
3065
+
3066
+ # Visit the targets of a multi-target node.
3067
+ private def visit_multi_target_node_targets(lefts, rest, rights, skippable)
3068
+ if skippable && lefts.length == 1 && lefts.first.is_a?(MultiTargetNode) && rest.nil? && rights.empty?
3069
+ return visit(lefts.first)
3070
+ end
3071
+
3072
+ mlhs = on_mlhs_new
3073
+
3074
+ lefts.each do |left|
3075
+ bounds(left.location)
3076
+ mlhs = on_mlhs_add(mlhs, visit(left))
3077
+ end
3078
+
3079
+ case rest
3080
+ when nil
3081
+ # do nothing
3082
+ when ImplicitRestNode
3083
+ # these do not get put into the generated tree
3084
+ bounds(rest.location)
3085
+ on_excessed_comma
3086
+ else
3087
+ bounds(rest.location)
3088
+ mlhs = on_mlhs_add_star(mlhs, visit(rest))
3089
+ end
3090
+
3091
+ if rights.any?
3092
+ bounds(rights.first.location)
3093
+ post = on_mlhs_new
3094
+
3095
+ rights.each do |right|
3096
+ bounds(right.location)
3097
+ post = on_mlhs_add(post, visit(right))
3098
+ end
3099
+
3100
+ mlhs = on_mlhs_add_post(mlhs, post)
3101
+ end
3102
+
3103
+ mlhs
3104
+ end
3105
+
3106
+ # foo, bar = baz
3107
+ # ^^^^^^^^^^^^^^
3108
+ def visit_multi_write_node(node)
3109
+ if node.lparen_loc
3110
+ bounds(node.lparen_loc)
3111
+ on_lparen("(")
3112
+ end
3113
+
3114
+ bounds(node.location)
3115
+ targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, true)
3116
+
3117
+ if node.rparen_loc
3118
+ bounds(node.rparen_loc)
3119
+ on_rparen(")")
3120
+ end
3121
+
3122
+ bounds(node.operator_loc)
3123
+ on_op("=")
3124
+
3125
+ unless node.lparen_loc.nil?
3126
+ bounds(node.lparen_loc)
3127
+ targets = on_mlhs_paren(targets)
3128
+ end
3129
+
3130
+ value = visit_write_value(node.value)
3131
+
3132
+ bounds(node.location)
3133
+ on_massign(targets, value)
3134
+ end
3135
+
3136
+ # next
3137
+ # ^^^^
3138
+ #
3139
+ # next foo
3140
+ # ^^^^^^^^
3141
+ def visit_next_node(node)
3142
+ bounds(node.keyword_loc)
3143
+ on_kw("next")
3144
+
3145
+ if node.arguments.nil?
3146
+ bounds(node.location)
3147
+ on_next(on_args_new)
3148
+ else
3149
+ arguments = visit(node.arguments)
3150
+
3151
+ bounds(node.location)
3152
+ on_next(arguments)
3153
+ end
3154
+ end
3155
+
3156
+ # nil
3157
+ # ^^^
3158
+ def visit_nil_node(node)
3159
+ bounds(node.location)
3160
+ on_var_ref(on_kw("nil"))
3161
+ end
3162
+
3163
+ # def foo(&nil); end
3164
+ # ^^^^
3165
+ def visit_no_block_parameter_node(node)
3166
+ bounds(node.operator_loc)
3167
+ on_op("&")
3168
+ bounds(node.keyword_loc)
3169
+ on_kw("nil")
3170
+ bounds(node.location)
3171
+ on_blockarg(:nil)
3172
+ end
3173
+
3174
+ # def foo(**nil); end
3175
+ # ^^^^^
3176
+ def visit_no_keywords_parameter_node(node)
3177
+ bounds(node.operator_loc)
3178
+ on_op("**")
3179
+ bounds(node.keyword_loc)
3180
+ on_kw("nil")
3181
+ bounds(node.location)
3182
+ on_nokw_param(nil)
3183
+
3184
+ :nil
3185
+ end
3186
+
3187
+ # -> { _1 + _2 }
3188
+ # ^^^^^^^^^^^^^^
3189
+ def visit_numbered_parameters_node(node)
3190
+ end
3191
+
3192
+ # $1
3193
+ # ^^
3194
+ def visit_numbered_reference_read_node(node)
3195
+ bounds(node.location)
3196
+ on_backref(node.slice)
3197
+ end
3198
+
3199
+ # def foo(bar: baz); end
3200
+ # ^^^^^^^^
3201
+ def visit_optional_keyword_parameter_node(node)
3202
+ bounds(node.name_loc)
3203
+ name = on_label("#{node.name}:")
3204
+ value = visit(node.value)
3205
+
3206
+ [name, value]
3207
+ end
3208
+
3209
+ # def foo(bar = 1); end
3210
+ # ^^^^^^^
3211
+ def visit_optional_parameter_node(node)
3212
+ bounds(node.name_loc)
3213
+ name = on_ident(node.name.to_s)
3214
+
3215
+ bounds(node.operator_loc)
3216
+ on_op("=")
3217
+
3218
+ value = visit(node.value)
3219
+
3220
+ [name, value]
3221
+ end
3222
+
3223
+ # a or b
3224
+ # ^^^^^^
3225
+ def visit_or_node(node)
3226
+ left = visit(node.left)
3227
+
3228
+ bounds(node.operator_loc)
3229
+ if node.operator == "or"
3230
+ on_kw("or")
3231
+ else
3232
+ on_op("||")
3233
+ end
3234
+
3235
+ right = visit(node.right)
3236
+
3237
+ bounds(node.location)
3238
+ on_binary(left, node.operator.to_sym, right)
3239
+ end
3240
+
3241
+ # def foo(bar, *baz); end
3242
+ # ^^^^^^^^^
3243
+ def visit_parameters_node(node)
3244
+ requireds = node.requireds.map { |required| required.is_a?(MultiTargetNode) ? visit_destructured_parameter_node(required) : visit(required) } if node.requireds.any?
3245
+ optionals = visit_all(node.optionals) if node.optionals.any?
3246
+ rest = visit(node.rest)
3247
+ posts = node.posts.map { |post| post.is_a?(MultiTargetNode) ? visit_destructured_parameter_node(post) : visit(post) } if node.posts.any?
3248
+ keywords = visit_all(node.keywords) if node.keywords.any?
3249
+ keyword_rest = visit(node.keyword_rest)
3250
+ block = visit(node.block)
3251
+
3252
+ bounds(node.location)
3253
+ on_params(requireds, optionals, rest, posts, keywords, keyword_rest, block)
3254
+ end
3255
+
3256
+ # Visit a destructured positional parameter node.
3257
+ private def visit_destructured_parameter_node(node)
3258
+ if node.lparen_loc
3259
+ bounds(node.lparen_loc)
3260
+ on_lparen("(")
3261
+ end
3262
+
3263
+ bounds(node.location)
3264
+ targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, false)
3265
+
3266
+ if node.rparen_loc
3267
+ bounds(node.rparen_loc)
3268
+ on_rparen(")")
3269
+ end
3270
+
3271
+ bounds(node.lparen_loc)
3272
+ on_mlhs_paren(targets)
3273
+ end
3274
+
3275
+ # ()
3276
+ # ^^
3277
+ #
3278
+ # (1)
3279
+ # ^^^
3280
+ def visit_parentheses_node(node)
3281
+ bounds(node.opening_loc)
3282
+ on_lparen("(")
3283
+
3284
+ body =
3285
+ if node.body.nil?
3286
+ on_stmts_add(on_stmts_new, on_void_stmt)
3287
+ else
3288
+ visit(node.body)
3289
+ end
3290
+
3291
+ bounds(node.closing_loc)
3292
+ on_rparen(")")
3293
+ bounds(node.location)
3294
+ on_paren(body)
3295
+ end
3296
+
3297
+ # foo => ^(bar)
3298
+ # ^^^^^^
3299
+ def visit_pinned_expression_node(node)
3300
+ bounds(node.operator_loc)
3301
+ on_op("^")
3302
+ bounds(node.lparen_loc)
3303
+ on_lparen("(")
3304
+
3305
+ expression = visit(node.expression)
3306
+
3307
+ bounds(node.rparen_loc)
3308
+ on_rparen(")")
3309
+ bounds(node.location)
3310
+ on_begin(expression)
3311
+ end
3312
+
3313
+ # foo = 1 and bar => ^foo
3314
+ # ^^^^
3315
+ def visit_pinned_variable_node(node)
3316
+ bounds(node.operator_loc)
3317
+ on_op("^")
3318
+
3319
+ visit(node.variable)
3320
+ end
3321
+
3322
+ # END {}
3323
+ # ^^^^^^
3324
+ def visit_post_execution_node(node)
3325
+ bounds(node.keyword_loc)
3326
+ on_kw("END")
3327
+ bounds(node.opening_loc)
3328
+ on_lbrace("{")
3329
+
3330
+ statements =
3331
+ if node.statements.nil?
3332
+ bounds(node.location)
3333
+ on_stmts_add(on_stmts_new, on_void_stmt)
3334
+ else
3335
+ visit(node.statements)
3336
+ end
3337
+
3338
+ bounds(node.closing_loc)
3339
+ on_rbrace("}")
3340
+ bounds(node.location)
3341
+ on_END(statements)
3342
+ end
3343
+
3344
+ # BEGIN {}
3345
+ # ^^^^^^^^
3346
+ def visit_pre_execution_node(node)
3347
+ bounds(node.keyword_loc)
3348
+ on_kw("BEGIN")
3349
+ bounds(node.opening_loc)
3350
+ on_lbrace("{")
3351
+
3352
+ statements =
3353
+ if node.statements.nil?
3354
+ bounds(node.location)
3355
+ on_stmts_add(on_stmts_new, on_void_stmt)
3356
+ else
3357
+ visit(node.statements)
3358
+ end
3359
+
3360
+ bounds(node.closing_loc)
3361
+ on_rbrace("}")
3362
+ bounds(node.location)
3363
+ on_BEGIN(statements)
3364
+ end
3365
+
3366
+ # The top-level program node.
3367
+ def visit_program_node(node)
3368
+ body = node.statements.body
3369
+ body = [nil] if body.empty?
3370
+ statements = visit_statements_node_body(body)
3371
+
3372
+ bounds(node.location)
3373
+ on_program(statements)
3374
+ end
3375
+
3376
+ # 0..5
3377
+ # ^^^^
3378
+ def visit_range_node(node)
3379
+ left = visit(node.left)
3380
+
3381
+ bounds(node.operator_loc)
3382
+ on_op(node.operator)
3383
+
3384
+ right = visit(node.right)
3385
+
3386
+ bounds(node.location)
3387
+ if node.exclude_end?
3388
+ on_dot3(left, right)
3389
+ else
3390
+ on_dot2(left, right)
3391
+ end
3392
+ end
3393
+
3394
+ # 1r
3395
+ # ^^
3396
+ def visit_rational_node(node)
3397
+ visit_number_node(node) { |text| on_rational(text) }
3398
+ end
3399
+
3400
+ # redo
3401
+ # ^^^^
3402
+ def visit_redo_node(node)
3403
+ bounds(node.location)
3404
+ on_kw("redo")
3405
+ on_redo
3406
+ end
3407
+
3408
+ # /foo/
3409
+ # ^^^^^
3410
+ def visit_regular_expression_node(node)
3411
+ bounds(node.opening_loc)
3412
+ on_regexp_beg(node.opening)
3413
+
3414
+ if node.content.empty?
3415
+ bounds(node.closing_loc)
3416
+ closing = on_regexp_end(node.closing)
3417
+
3418
+ on_regexp_literal(on_regexp_new, closing)
3419
+ else
3420
+ bounds(node.content_loc)
3421
+ tstring_content = on_tstring_content(node.content)
3422
+
3423
+ bounds(node.closing_loc)
3424
+ closing = on_regexp_end(node.closing)
3425
+
3426
+ on_regexp_literal(on_regexp_add(on_regexp_new, tstring_content), closing)
3427
+ end
3428
+ end
3429
+
3430
+ # def foo(bar:); end
3431
+ # ^^^^
3432
+ def visit_required_keyword_parameter_node(node)
3433
+ bounds(node.name_loc)
3434
+ [on_label("#{node.name}:"), false]
3435
+ end
3436
+
3437
+ # def foo(bar); end
3438
+ # ^^^
3439
+ def visit_required_parameter_node(node)
3440
+ bounds(node.location)
3441
+ on_ident(node.name.to_s)
3442
+ end
3443
+
3444
+ # foo rescue bar
3445
+ # ^^^^^^^^^^^^^^
3446
+ def visit_rescue_modifier_node(node)
3447
+ bounds(node.keyword_loc)
3448
+ on_kw("rescue")
3449
+
3450
+ expression = visit_write_value(node.expression)
3451
+ rescue_expression = visit(node.rescue_expression)
3452
+
3453
+ bounds(node.location)
3454
+ on_rescue_mod(expression, rescue_expression)
3455
+ end
3456
+
3457
+ # begin; rescue; end
3458
+ # ^^^^^^^
3459
+ def visit_rescue_node(node)
3460
+ bounds(node.keyword_loc)
3461
+ on_kw("rescue")
3462
+
3463
+ exceptions =
3464
+ case node.exceptions.length
3465
+ when 0
3466
+ nil
3467
+ when 1
3468
+ if (exception = node.exceptions.first).is_a?(SplatNode)
3469
+ bounds(exception.location)
3470
+ on_mrhs_add_star(on_mrhs_new, visit(exception))
3471
+ else
3472
+ [visit(node.exceptions.first)]
3473
+ end
3474
+ else
3475
+ bounds(node.location)
3476
+ length = node.exceptions.length
3477
+
3478
+ node.exceptions.each_with_index.inject(on_args_new) do |mrhs, (exception, index)|
3479
+ arg = visit(exception)
3480
+
3481
+ bounds(exception.location)
3482
+ mrhs = on_mrhs_new_from_args(mrhs) if index == length - 1
3483
+
3484
+ if exception.is_a?(SplatNode)
3485
+ if index == length - 1
3486
+ on_mrhs_add_star(mrhs, arg)
3487
+ else
3488
+ on_args_add_star(mrhs, arg)
3489
+ end
3490
+ else
3491
+ if index == length - 1
3492
+ on_mrhs_add(mrhs, arg)
3493
+ else
3494
+ on_args_add(mrhs, arg)
3495
+ end
3496
+ end
3497
+ end
3498
+ end
3499
+
3500
+ if node.operator_loc
3501
+ bounds(node.operator_loc)
3502
+ on_op("=>")
3503
+ end
3504
+
3505
+ reference = visit(node.reference)
3506
+ statements =
3507
+ if node.statements.nil?
3508
+ bounds(node.location)
3509
+ on_stmts_add(on_stmts_new, on_void_stmt)
3510
+ else
3511
+ visit(node.statements)
3512
+ end
3513
+
3514
+ subsequent = visit(node.subsequent)
3515
+
3516
+ bounds(node.location)
3517
+ on_rescue(exceptions, reference, statements, subsequent)
3518
+ end
3519
+
3520
+ # def foo(*bar); end
3521
+ # ^^^^
3522
+ #
3523
+ # def foo(*); end
3524
+ # ^
3525
+ def visit_rest_parameter_node(node)
3526
+ bounds(node.operator_loc)
3527
+ on_op("*")
3528
+
3529
+ if node.name_loc.nil?
3530
+ bounds(node.location)
3531
+ on_rest_param(nil)
3532
+ else
3533
+ bounds(node.name_loc)
3534
+ on_rest_param(on_ident(node.name.to_s))
3535
+ end
3536
+ end
3537
+
3538
+ # retry
3539
+ # ^^^^^
3540
+ def visit_retry_node(node)
3541
+ bounds(node.location)
3542
+ on_kw("retry")
3543
+ on_retry
3544
+ end
3545
+
3546
+ # return
3547
+ # ^^^^^^
3548
+ #
3549
+ # return 1
3550
+ # ^^^^^^^^
3551
+ def visit_return_node(node)
3552
+ bounds(node.keyword_loc)
3553
+ on_kw("return")
3554
+
3555
+ if node.arguments.nil?
3556
+ bounds(node.location)
3557
+ on_return0
3558
+ else
3559
+ arguments = visit(node.arguments)
3560
+
3561
+ bounds(node.location)
3562
+ on_return(arguments)
3563
+ end
3564
+ end
3565
+
3566
+ # self
3567
+ # ^^^^
3568
+ def visit_self_node(node)
3569
+ bounds(node.location)
3570
+ on_var_ref(on_kw("self"))
3571
+ end
3572
+
3573
+ # A shareable constant.
3574
+ def visit_shareable_constant_node(node)
3575
+ visit(node.write)
3576
+ end
3577
+
3578
+ # class << self; end
3579
+ # ^^^^^^^^^^^^^^^^^^
3580
+ def visit_singleton_class_node(node)
3581
+ bounds(node.class_keyword_loc)
3582
+ on_kw("class")
3583
+ bounds(node.operator_loc)
3584
+ on_op("<<")
3585
+
3586
+ expression = visit(node.expression)
3587
+ bodystmt = visit_body_node(node.body&.location || node.end_keyword_loc, node.body)
3588
+
3589
+ bounds(node.end_keyword_loc)
3590
+ on_kw("end")
3591
+
3592
+ bounds(node.location)
3593
+ on_sclass(expression, bodystmt)
3594
+ end
3595
+
3596
+ # __ENCODING__
3597
+ # ^^^^^^^^^^^^
3598
+ def visit_source_encoding_node(node)
3599
+ bounds(node.location)
3600
+ on_var_ref(on_kw("__ENCODING__"))
3601
+ end
3602
+
3603
+ # __FILE__
3604
+ # ^^^^^^^^
3605
+ def visit_source_file_node(node)
3606
+ bounds(node.location)
3607
+ on_var_ref(on_kw("__FILE__"))
3608
+ end
3609
+
3610
+ # __LINE__
3611
+ # ^^^^^^^^
3612
+ def visit_source_line_node(node)
3613
+ bounds(node.location)
3614
+ on_var_ref(on_kw("__LINE__"))
3615
+ end
3616
+
3617
+ # foo(*bar)
3618
+ # ^^^^
3619
+ #
3620
+ # def foo((bar, *baz)); end
3621
+ # ^^^^
3622
+ #
3623
+ # def foo(*); bar(*); end
3624
+ # ^
3625
+ def visit_splat_node(node)
3626
+ bounds(node.operator_loc)
3627
+ on_op("*")
3628
+ visit(node.expression)
3629
+ end
3630
+
3631
+ # A list of statements.
3632
+ def visit_statements_node(node)
3633
+ bounds(node.location)
3634
+ visit_statements_node_body(node.body)
3635
+ end
3636
+
3637
+ # Visit the list of statements of a statements node. We support nil
3638
+ # statements in the list. This would normally not be allowed by the
3639
+ # structure of the prism parse tree, but we manually add them here so that
3640
+ # we can mirror Ripper's void stmt.
3641
+ private def visit_statements_node_body(body)
3642
+ body.inject(on_stmts_new) do |stmts, stmt|
3643
+ on_stmts_add(stmts, stmt.nil? ? on_void_stmt : visit(stmt))
3644
+ end
3645
+ end
3646
+
3647
+ # "foo"
3648
+ # ^^^^^
3649
+ def visit_string_node(node)
3650
+ with_string_bounds(node) do
3651
+ if (content = node.content).empty?
3652
+ bounds(node.location)
3653
+ on_string_literal(on_string_content)
3654
+ elsif (opening = node.opening) == "?"
3655
+ bounds(node.location)
3656
+ on_CHAR("?#{node.content}")
3657
+ elsif opening.start_with?("<<~")
3658
+ heredoc = visit_heredoc_string_node(node.to_interpolated)
3659
+
3660
+ bounds(node.location)
3661
+ on_string_literal(heredoc)
3662
+ else
3663
+ bounds(node.content_loc)
3664
+ tstring_content = on_tstring_content(content)
3665
+
3666
+ bounds(node.location)
3667
+ on_string_literal(on_string_add(on_string_content, tstring_content))
3668
+ end
3669
+ end
3670
+ end
3671
+
3672
+ # Responsible for emitting the various string-like begin/end events
3673
+ private def with_string_bounds(node)
3674
+ # `foo "bar": baz` doesn't emit the closing location
3675
+ assoc = !(opening = node.opening)&.include?(":") && node.closing&.end_with?(":")
3676
+
3677
+ is_heredoc = opening&.start_with?("<<")
3678
+ if is_heredoc
3679
+ bounds(node.opening_loc)
3680
+ on_heredoc_beg(node.opening)
3681
+ elsif opening&.start_with?(":", "%s")
3682
+ bounds(node.opening_loc)
3683
+ on_symbeg(node.opening)
3684
+ elsif opening&.start_with?("`", "%x")
3685
+ bounds(node.opening_loc)
3686
+ on_backtick(node.opening)
3687
+ elsif opening && !opening.start_with?("?")
3688
+ bounds(node.opening_loc)
3689
+ on_tstring_beg(opening)
3690
+ end
3691
+
3692
+ result = yield
3693
+ if assoc
3694
+ if node.closing != ":"
3695
+ bounds(node.closing_loc)
3696
+ on_label_end(node.closing)
3697
+ end
3698
+ return result
3699
+ end
3700
+
3701
+ if is_heredoc
3702
+ bounds(node.closing_loc)
3703
+ on_heredoc_end(node.closing)
3704
+ elsif node.closing_loc
3705
+ bounds(node.closing_loc)
3706
+ on_tstring_end(node.closing)
3707
+ end
3708
+
3709
+ result
3710
+ end
3711
+
3712
+ # Ripper gives back the escaped string content but strips out the common
3713
+ # leading whitespace. Prism gives back the unescaped string content and
3714
+ # a location for the escaped string content. Unfortunately these don't
3715
+ # work well together, so here we need to re-derive the common leading
3716
+ # whitespace.
3717
+ private def visit_heredoc_node_whitespace(parts)
3718
+ common_whitespace = nil
3719
+ dedent_next = true
3720
+
3721
+ parts.each do |part|
3722
+ if part.is_a?(StringNode)
3723
+ if dedent_next && !(content = part.content).chomp.empty?
3724
+ common_whitespace = [
3725
+ common_whitespace || Float::INFINITY,
3726
+ content[/\A\s*/].each_char.inject(0) do |part_whitespace, char|
3727
+ char == "\t" ? ((part_whitespace / 8 + 1) * 8) : (part_whitespace + 1)
3728
+ end
3729
+ ].min
3730
+ end
3731
+
3732
+ dedent_next = true
3733
+ else
3734
+ dedent_next = false
3735
+ end
3736
+ end
3737
+
3738
+ common_whitespace || 0
3739
+ end
3740
+
3741
+ # Visit a string that is expressed using a <<~ heredoc.
3742
+ private def visit_heredoc_node(parts, base)
3743
+ common_whitespace = visit_heredoc_node_whitespace(parts)
3744
+
3745
+ if common_whitespace == 0
3746
+ bounds(parts.first.location)
3747
+
3748
+ string = []
3749
+ result = base
3750
+
3751
+ parts.each do |part|
3752
+ if part.is_a?(StringNode)
3753
+ if string.empty?
3754
+ string = [part]
3755
+ else
3756
+ string << part
3757
+ end
3758
+ else
3759
+ unless string.empty?
3760
+ bounds(string[0].location)
3761
+ result = yield result, on_tstring_content(string.map(&:content).join)
3762
+ string = []
3763
+ end
3764
+
3765
+ result = yield result, visit(part)
3766
+ end
3767
+ end
3768
+
3769
+ unless string.empty?
3770
+ bounds(string[0].location)
3771
+ result = yield result, on_tstring_content(string.map(&:content).join)
3772
+ end
3773
+
3774
+ result
3775
+ else
3776
+ bounds(parts.first.location)
3777
+ result =
3778
+ parts.inject(base) do |string_content, part|
3779
+ yield string_content, visit_string_content(part)
3780
+ end
3781
+
3782
+ bounds(parts.first.location)
3783
+ on_heredoc_dedent(result, common_whitespace)
3784
+ end
3785
+ end
3786
+
3787
+ # Visit a heredoc node that is representing a string.
3788
+ private def visit_heredoc_string_node(node)
3789
+ bounds(node.location)
3790
+ visit_heredoc_node(node.parts, on_string_content) do |parts, part|
3791
+ on_string_add(parts, part)
3792
+ end
3793
+ end
3794
+
3795
+ # Visit a heredoc node that is representing an xstring.
3796
+ private def visit_heredoc_x_string_node(node)
3797
+ bounds(node.location)
3798
+ visit_heredoc_node(node.parts, on_xstring_new) do |parts, part|
3799
+ on_xstring_add(parts, part)
3800
+ end
3801
+ end
3802
+
3803
+ # super(foo)
3804
+ # ^^^^^^^^^^
3805
+ def visit_super_node(node)
3806
+ bounds(node.keyword_loc)
3807
+ on_kw("super")
3808
+
3809
+ if node.lparen_loc
3810
+ bounds(node.lparen_loc)
3811
+ on_lparen("(")
3812
+ end
3813
+
3814
+ arguments, block_node = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.rparen_loc || node.location))
3815
+
3816
+ if node.rparen_loc
3817
+ bounds(node.rparen_loc)
3818
+ on_rparen(")")
3819
+ end
3820
+
3821
+ block = visit(block_node)
3822
+
3823
+ if !node.lparen_loc.nil?
3824
+ bounds(node.lparen_loc)
3825
+ arguments = on_arg_paren(arguments)
3826
+ end
3827
+
3828
+ bounds(node.location)
3829
+ call = on_super(arguments)
3830
+
3831
+ if block_node
3832
+ bounds(node.block.location)
3833
+ on_method_add_block(call, block)
3834
+ else
3835
+ call
3836
+ end
3837
+ end
3838
+
3839
+ # :foo
3840
+ # ^^^^
3841
+ def visit_symbol_node(node)
3842
+ with_string_bounds(node) do
3843
+ if (opening = node.opening)&.match?(/^%s|['"]:?$/)
3844
+ bounds(node.content_loc)
3845
+ content = on_string_content
3846
+
3847
+ if !(value = node.content).empty?
3848
+ content = on_string_add(content, on_tstring_content(value))
3849
+ end
3850
+
3851
+ bounds(node.location)
3852
+ on_dyna_symbol(content)
3853
+ elsif (closing = node.closing) == ":"
3854
+ bounds(node.location)
3855
+ on_label("#{node.content}:")
3856
+ elsif opening.nil? && node.closing_loc.nil?
3857
+ bounds(node.content_loc)
3858
+ on_symbol_literal(visit_token(node.content))
3859
+ else
3860
+ bounds(node.content_loc)
3861
+ on_symbol_literal(on_symbol(visit_token(node.content)))
3862
+ end
3863
+ end
3864
+ end
3865
+
3866
+ # true
3867
+ # ^^^^
3868
+ def visit_true_node(node)
3869
+ bounds(node.location)
3870
+ on_var_ref(on_kw("true"))
3871
+ end
3872
+
3873
+ # undef foo
3874
+ # ^^^^^^^^^
3875
+ def visit_undef_node(node)
3876
+ bounds(node.keyword_loc)
3877
+ on_kw("undef")
3878
+
3879
+ names = visit_all(node.names)
3880
+
3881
+ bounds(node.location)
3882
+ on_undef(names)
3883
+ end
3884
+
3885
+ # unless foo; bar end
3886
+ # ^^^^^^^^^^^^^^^^^^^
3887
+ #
3888
+ # bar unless foo
3889
+ # ^^^^^^^^^^^^^^
3890
+ def visit_unless_node(node)
3891
+ if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
3892
+ bounds(node.unless_keyword_loc)
3893
+ on_kw("unless")
3894
+ predicate = visit(node.predicate)
3895
+ if node.then_keyword_loc
3896
+ bounds(node.then_keyword_loc)
3897
+ on_kw("then")
3898
+ end
3899
+ statements =
3900
+ if node.statements.nil?
3901
+ bounds(node.location)
3902
+ on_stmts_add(on_stmts_new, on_void_stmt)
3903
+ else
3904
+ visit(node.statements)
3905
+ end
3906
+ else_clause = visit(node.else_clause)
3907
+
3908
+ if node.end_keyword_loc && !node.else_clause
3909
+ bounds(node.end_keyword_loc)
3910
+ on_kw("end")
3911
+ end
3912
+
3913
+ bounds(node.location)
3914
+ on_unless(predicate, statements, else_clause)
3915
+ else
3916
+ statements = visit(node.statements.body.first)
3917
+ bounds(node.unless_keyword_loc)
3918
+ on_kw("unless")
3919
+ predicate = visit(node.predicate)
3920
+
3921
+ bounds(node.location)
3922
+ on_unless_mod(predicate, statements)
3923
+ end
3924
+ end
3925
+
3926
+ # until foo; bar end
3927
+ # ^^^^^^^^^^^^^^^^^
3928
+ #
3929
+ # bar until foo
3930
+ # ^^^^^^^^^^^^^
3931
+ def visit_until_node(node)
3932
+ bounds(node.until_keyword_loc)
3933
+ on_kw("until")
3934
+
3935
+ if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
3936
+ if node.do_keyword_loc
3937
+ bounds(node.do_keyword_loc)
3938
+ on_kw("do")
3939
+ end
3940
+ predicate = visit(node.predicate)
3941
+ statements =
3942
+ if node.statements.nil?
3943
+ bounds(node.location)
3944
+ on_stmts_add(on_stmts_new, on_void_stmt)
3945
+ else
3946
+ visit(node.statements)
3947
+ end
3948
+
3949
+ if node.end_keyword_loc
3950
+ bounds(node.end_keyword_loc)
3951
+ on_kw("end")
3952
+ end
3953
+
3954
+ bounds(node.location)
3955
+ on_until(predicate, statements)
3956
+ else
3957
+ statements = visit(node.statements.body.first)
3958
+ predicate = visit(node.predicate)
3959
+
3960
+ bounds(node.location)
3961
+ on_until_mod(predicate, statements)
3962
+ end
3963
+ end
3964
+
3965
+ # case foo; when bar; end
3966
+ # ^^^^^^^^^^^^^
3967
+ def visit_when_node(node)
3968
+ # This is a special case where we're not going to call on_when directly
3969
+ # because we don't have access to the subsequent. Instead, we'll return
3970
+ # the component parts and let the parent node handle it.
3971
+ bounds(node.when_keyword_loc)
3972
+ on_kw("when")
3973
+
3974
+ conditions = visit_arguments(node.conditions)
3975
+ if node.then_keyword_loc
3976
+ bounds(node.then_keyword_loc)
3977
+ on_kw("then")
3978
+ end
3979
+ statements =
3980
+ if node.statements.nil?
3981
+ bounds(node.location)
3982
+ on_stmts_add(on_stmts_new, on_void_stmt)
3983
+ else
3984
+ visit(node.statements)
3985
+ end
3986
+
3987
+ [conditions, statements]
3988
+ end
3989
+
3990
+ # while foo; bar end
3991
+ # ^^^^^^^^^^^^^^^^^^
3992
+ #
3993
+ # bar while foo
3994
+ # ^^^^^^^^^^^^^
3995
+ def visit_while_node(node)
3996
+ if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
3997
+ bounds(node.while_keyword_loc)
3998
+ on_kw("while")
3999
+ if node.do_keyword_loc
4000
+ bounds(node.do_keyword_loc)
4001
+ on_kw("do")
4002
+ end
4003
+ predicate = visit(node.predicate)
4004
+ if node.end_keyword_loc
4005
+ bounds(node.end_keyword_loc)
4006
+ on_kw("end")
4007
+ end
4008
+ statements =
4009
+ if node.statements.nil?
4010
+ bounds(node.location)
4011
+ on_stmts_add(on_stmts_new, on_void_stmt)
4012
+ else
4013
+ visit(node.statements)
4014
+ end
4015
+
4016
+ bounds(node.location)
4017
+ on_while(predicate, statements)
4018
+ else
4019
+ statements = visit(node.statements.body.first)
4020
+ bounds(node.while_keyword_loc)
4021
+ on_kw("while")
4022
+ predicate = visit(node.predicate)
4023
+
4024
+ bounds(node.location)
4025
+ on_while_mod(predicate, statements)
4026
+ end
4027
+ end
4028
+
4029
+ # `foo`
4030
+ # ^^^^^
4031
+ def visit_x_string_node(node)
4032
+ with_string_bounds(node) do
4033
+ if node.unescaped.empty?
4034
+ bounds(node.location)
4035
+ on_xstring_literal(on_xstring_new)
4036
+ elsif node.opening.start_with?("<<~")
4037
+ heredoc = visit_heredoc_x_string_node(node.to_interpolated)
4038
+
4039
+ bounds(node.location)
4040
+ on_xstring_literal(heredoc)
4041
+ else
4042
+ bounds(node.content_loc)
4043
+ content = on_tstring_content(node.content)
4044
+
4045
+ bounds(node.location)
4046
+ on_xstring_literal(on_xstring_add(on_xstring_new, content))
4047
+ end
4048
+ end
4049
+ end
4050
+
4051
+ # yield
4052
+ # ^^^^^
4053
+ #
4054
+ # yield 1
4055
+ # ^^^^^^^
4056
+ def visit_yield_node(node)
4057
+ bounds(node.keyword_loc)
4058
+ on_kw("yield")
4059
+
4060
+ if node.arguments.nil? && node.lparen_loc.nil?
4061
+ bounds(node.location)
4062
+ on_yield0
4063
+ else
4064
+ if node.lparen_loc
4065
+ bounds(node.lparen_loc)
4066
+ on_lparen("(")
4067
+ end
4068
+
4069
+ arguments =
4070
+ if node.arguments.nil?
4071
+ bounds(node.location)
4072
+ on_args_new
4073
+ else
4074
+ visit(node.arguments)
4075
+ end
4076
+
4077
+ unless node.lparen_loc.nil?
4078
+ bounds(node.rparen_loc)
4079
+ on_rparen(")")
4080
+ bounds(node.lparen_loc)
4081
+ arguments = on_paren(arguments)
4082
+ end
4083
+
4084
+ bounds(node.location)
4085
+ on_yield(arguments)
4086
+ end
4087
+ end
4088
+
4089
+ private
4090
+
4091
+ # Lazily initialize the parse result.
4092
+ def result
4093
+ @result ||= Prism.parse(source, partial_script: true, version: "current", freeze: true, encoding: source.encoding)
4094
+ end
4095
+
4096
+ def line_and_column_cache
4097
+ @line_and_column_cache ||= LineAndColumnCache.new(result.source)
4098
+ end
4099
+
4100
+ ##########################################################################
4101
+ # Helpers
4102
+ ##########################################################################
4103
+
4104
+ # Returns true if there is a comma between the two locations.
4105
+ def trailing_comma?(left, right)
4106
+ source.byteslice(left.end_offset...right.start_offset).include?(",")
4107
+ end
4108
+
4109
+ # Returns true if there is a semicolon between the two locations.
4110
+ def void_stmt?(left, right, allow_newline)
4111
+ pattern = allow_newline ? /[;\n]/ : /;/
4112
+ source.byteslice(left.end_offset...right.start_offset).match?(pattern)
4113
+ end
4114
+
4115
+ # Visit the string content of a particular node. This method is used to
4116
+ # split into the various token types.
4117
+ def visit_token(token, allow_keywords = true)
4118
+ if token == "."
4119
+ on_period(token)
4120
+ elsif token == "`"
4121
+ on_backtick(token)
4122
+ elsif allow_keywords && KEYWORDS.include?(token)
4123
+ on_kw(token)
4124
+ elsif token.start_with?("_")
4125
+ on_ident(token)
4126
+ elsif token.match?(/^[[:upper:]]\w*$/)
4127
+ on_const(token)
4128
+ elsif token.start_with?("@@")
4129
+ on_cvar(token)
4130
+ elsif token.start_with?("@")
4131
+ on_ivar(token)
4132
+ elsif token.start_with?("$")
4133
+ on_gvar(token)
4134
+ elsif token.match?(/^[[:punct:]]/)
4135
+ on_op(token)
4136
+ else
4137
+ on_ident(token)
4138
+ end
4139
+ end
4140
+
4141
+ # Visit either `.`, `&.`, or `::`.
4142
+ def visit_call_operator(token)
4143
+ token == "." ? on_period(token) : on_op(token)
4144
+ end
4145
+
4146
+ # Visit a node that represents a number. We need to explicitly handle the
4147
+ # unary - operator.
4148
+ def visit_number_node(node)
4149
+ slice = node.slice
4150
+ location = node.location
4151
+
4152
+ if slice[0] == "-"
4153
+ bounds(location.copy(length: 1))
4154
+ on_op("-")
4155
+
4156
+ bounds(location.copy(start_offset: location.start_offset + 1))
4157
+ value = yield slice[1..-1]
4158
+
4159
+ bounds(node.location)
4160
+ on_unary(:-@, value)
4161
+ else
4162
+ bounds(location)
4163
+ yield slice
4164
+ end
4165
+ end
4166
+
4167
+ # Visit a node that represents a write value. This is used to handle the
4168
+ # special case of an implicit array that is generated without brackets.
4169
+ def visit_write_value(node)
4170
+ if node.is_a?(ArrayNode) && node.opening_loc.nil?
4171
+ elements = node.elements
4172
+ length = elements.length
4173
+
4174
+ bounds(elements.first.location)
4175
+ elements.each_with_index.inject((elements.first.is_a?(SplatNode) && length == 1) ? on_mrhs_new : on_args_new) do |args, (element, index)|
4176
+ arg = visit(element)
4177
+ bounds(element.location)
4178
+
4179
+ if index == length - 1
4180
+ if element.is_a?(SplatNode)
4181
+ mrhs = index == 0 ? args : on_mrhs_new_from_args(args)
4182
+ on_mrhs_add_star(mrhs, arg)
4183
+ else
4184
+ on_mrhs_add(on_mrhs_new_from_args(args), arg)
4185
+ end
4186
+ else
4187
+ case element
4188
+ when BlockArgumentNode
4189
+ on_args_add_block(args, arg)
4190
+ when SplatNode
4191
+ on_args_add_star(args, arg)
4192
+ else
4193
+ on_args_add(args, arg)
4194
+ end
4195
+ end
4196
+ end
4197
+ else
4198
+ visit(node)
4199
+ end
4200
+ end
4201
+
4202
+ # This method is responsible for updating lineno and column information
4203
+ # to reflect the current node.
4204
+ def bounds(location)
4205
+ @lineno, @column = line_and_column_cache.line_and_column(location.start_offset)
4206
+ end
4207
+
4208
+ # :startdoc:
4209
+
4210
+ ##########################################################################
4211
+ # Ripper interface
4212
+ ##########################################################################
4213
+
4214
+ # :stopdoc:
4215
+ def _dispatch_0; end
4216
+ def _dispatch_1(arg); arg end
4217
+ def _dispatch_2(arg, _); arg end
4218
+ def _dispatch_3(arg, _, _); arg end
4219
+ def _dispatch_4(arg, _, _, _); arg end
4220
+ def _dispatch_5(arg, _, _, _, _); arg end
4221
+ def _dispatch_7(arg, _, _, _, _, _, _); arg end
4222
+ # :startdoc:
4223
+
4224
+ #
4225
+ # Parser Events
4226
+ #
4227
+
4228
+ PARSER_EVENT_TABLE.each do |id, arity|
4229
+ alias_method "on_#{id}", "_dispatch_#{arity}"
4230
+ end
4231
+
4232
+ # This method is called when weak warning is produced by the parser.
4233
+ # +fmt+ and +args+ is printf style.
4234
+ def warn(fmt, *args)
4235
+ end
4236
+
4237
+ # This method is called when strong warning is produced by the parser.
4238
+ # +fmt+ and +args+ is printf style.
4239
+ def warning(fmt, *args)
4240
+ end
4241
+
4242
+ # This method is called when the parser found syntax error.
4243
+ def compile_error(msg)
4244
+ end
4245
+
4246
+ #
4247
+ # Scanner Events
4248
+ #
4249
+
4250
+ SCANNER_EVENTS.each do |id|
4251
+ alias_method "on_#{id}", :_dispatch_1
4252
+ end
4253
+
4254
+ # This method is provided by the Ripper C extension. It is called when a
4255
+ # string needs to be dedented because of a tilde heredoc. It is expected
4256
+ # that it will modify the string in place and return the number of bytes
4257
+ # that were removed.
4258
+ def dedent_string(string, width)
4259
+ whitespace = 0
4260
+ cursor = 0
4261
+
4262
+ while cursor < string.length && string[cursor].match?(/\s/) && whitespace < width
4263
+ if string[cursor] == "\t"
4264
+ whitespace = ((whitespace / 8 + 1) * 8)
4265
+ break if whitespace > width
4266
+ else
4267
+ whitespace += 1
4268
+ end
4269
+
4270
+ cursor += 1
4271
+ end
4272
+
4273
+ string.replace(string[cursor..])
4274
+ cursor
4275
+ end
4276
+ end
4277
+ end
4278
+ end