prism 1.9.0 → 2.0.0.rc2

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 (244) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +88 -0
  3. data/CODE_OF_CONDUCT.md +1 -1
  4. data/LICENSE.md +1 -1
  5. data/Makefile +41 -12
  6. data/README.md +35 -97
  7. data/config.yml +174 -161
  8. data/docs/build_system.md +2 -2
  9. data/docs/configuration.md +5 -4
  10. data/docs/encoding.md +2 -2
  11. data/docs/fuzzing.md +4 -11
  12. data/docs/javascript.md +19 -4
  13. data/docs/mapping.md +1 -1
  14. data/docs/releasing.md +77 -5
  15. data/docs/relocation.md +2 -4
  16. data/docs/serialization.md +22 -24
  17. data/docs/testing.md +46 -29
  18. data/ext/prism/api_node.c +1461 -1401
  19. data/ext/prism/extconf.rb +3 -4
  20. data/ext/prism/extension.c +796 -422
  21. data/ext/prism/extension.h +3 -3
  22. data/include/prism/arena.h +37 -0
  23. data/include/prism/ast.h +2727 -610
  24. data/include/prism/buffer.h +52 -0
  25. data/include/prism/comments.h +55 -0
  26. data/include/prism/compiler/accel.h +26 -0
  27. data/include/prism/compiler/align.h +36 -0
  28. data/include/prism/compiler/assume.h +25 -0
  29. data/include/prism/compiler/bswap.h +46 -0
  30. data/include/prism/compiler/endianness.h +34 -0
  31. data/include/prism/compiler/exported.h +24 -0
  32. data/include/prism/compiler/fallthrough.h +22 -0
  33. data/include/prism/compiler/filesystem.h +32 -0
  34. data/include/prism/compiler/flex_array.h +19 -0
  35. data/include/prism/compiler/force_inline.h +21 -0
  36. data/include/prism/compiler/format.h +25 -0
  37. data/include/prism/compiler/inline.h +17 -0
  38. data/include/prism/compiler/nodiscard.h +22 -0
  39. data/include/prism/compiler/nonnull.h +18 -0
  40. data/include/prism/compiler/unused.h +18 -0
  41. data/include/prism/constant_pool.h +81 -0
  42. data/include/prism/diagnostic.h +38 -403
  43. data/include/prism/errors_format.h +53 -0
  44. data/include/prism/excludes.h +29 -0
  45. data/include/prism/integer.h +41 -0
  46. data/include/prism/internal/allocator.h +68 -0
  47. data/include/prism/internal/allocator_debug.h +88 -0
  48. data/include/prism/internal/arena.h +108 -0
  49. data/include/prism/internal/bit.h +42 -0
  50. data/include/prism/internal/buffer.h +94 -0
  51. data/include/prism/internal/char.h +145 -0
  52. data/include/prism/internal/comments.h +53 -0
  53. data/include/prism/internal/constant_pool.h +152 -0
  54. data/include/prism/internal/diagnostic.h +389 -0
  55. data/include/prism/{encoding.h → internal/encoding.h} +27 -68
  56. data/include/prism/internal/integer.h +68 -0
  57. data/include/prism/internal/isinf.h +16 -0
  58. data/include/prism/internal/line_offset_list.h +34 -0
  59. data/include/prism/{util/pm_list.h → internal/list.h} +11 -52
  60. data/include/prism/internal/magic_comments.h +23 -0
  61. data/include/prism/internal/memchr.h +15 -0
  62. data/include/prism/internal/node.h +38 -0
  63. data/include/prism/internal/options.h +232 -0
  64. data/include/prism/internal/parser.h +1004 -0
  65. data/include/prism/internal/regexp.h +26 -0
  66. data/include/prism/internal/serialize.h +39 -0
  67. data/include/prism/internal/source.h +72 -0
  68. data/include/prism/{static_literals.h → internal/static_literals.h} +26 -47
  69. data/include/prism/internal/stringy.h +35 -0
  70. data/include/prism/{util/pm_strncasecmp.h → internal/strncasecmp.h} +3 -17
  71. data/include/prism/{util/pm_strpbrk.h → internal/strpbrk.h} +7 -20
  72. data/include/prism/internal/tokens.h +11 -0
  73. data/include/prism/internal/xxh3.h +16 -0
  74. data/include/prism/json.h +32 -0
  75. data/include/prism/line_offset_list.h +61 -0
  76. data/include/prism/magic_comments.h +35 -0
  77. data/include/prism/node.h +18 -53
  78. data/include/prism/options.h +146 -280
  79. data/include/prism/parser.h +356 -867
  80. data/include/prism/prettyprint.h +6 -9
  81. data/include/prism/serialize.h +115 -0
  82. data/include/prism/source.h +155 -0
  83. data/include/prism/stream.h +28 -0
  84. data/include/prism/string_query.h +74 -0
  85. data/include/prism/stringy.h +72 -0
  86. data/include/prism/version.h +12 -3
  87. data/include/prism.h +43 -320
  88. data/lib/prism/compiler.rb +319 -305
  89. data/lib/prism/desugar_compiler.rb +105 -34
  90. data/lib/prism/dispatcher.rb +646 -473
  91. data/lib/prism/dot_visitor.rb +592 -511
  92. data/lib/prism/dsl.rb +391 -25
  93. data/lib/prism/ffi.rb +258 -90
  94. data/lib/prism/inspect_visitor.rb +357 -325
  95. data/lib/prism/lex_compat.rb +254 -212
  96. data/lib/prism/mutation_compiler.rb +313 -305
  97. data/lib/prism/node.rb +20966 -6992
  98. data/lib/prism/node_ext.rb +180 -187
  99. data/lib/prism/node_find.rb +215 -0
  100. data/lib/prism/parse_result/errors.rb +7 -1
  101. data/lib/prism/parse_result/newlines.rb +276 -8
  102. data/lib/prism/parse_result.rb +468 -139
  103. data/lib/prism/pattern.rb +65 -20
  104. data/lib/prism/reflection.rb +23 -12
  105. data/lib/prism/relocation.rb +149 -87
  106. data/lib/prism/serialize.rb +3460 -539
  107. data/lib/prism/string_query.rb +24 -1
  108. data/lib/prism/translation/parser/builder.rb +10 -2
  109. data/lib/prism/translation/parser/compiler.rb +44 -52
  110. data/lib/prism/translation/parser/lexer.rb +41 -92
  111. data/lib/prism/translation/parser.rb +1 -1
  112. data/lib/prism/translation/parser_current.rb +1 -1
  113. data/lib/prism/translation/ripper/lexer.rb +8 -10
  114. data/lib/prism/translation/ripper/sexp.rb +2 -10
  115. data/lib/prism/translation/ripper/shim.rb +2 -0
  116. data/lib/prism/translation/ripper.rb +967 -209
  117. data/lib/prism/translation/ruby_parser.rb +73 -316
  118. data/lib/prism/translation.rb +2 -0
  119. data/lib/prism/visitor.rb +632 -5
  120. data/lib/prism.rb +79 -9
  121. data/prism.gemspec +128 -62
  122. data/rbi/generated/prism/compiler.rbi +489 -0
  123. data/rbi/generated/prism/desugar_compiler.rbi +306 -0
  124. data/rbi/generated/prism/dispatcher.rbi +1138 -0
  125. data/rbi/generated/prism/dot_visitor.rbi +618 -0
  126. data/rbi/generated/prism/dsl.rbi +751 -0
  127. data/rbi/generated/prism/inspect_visitor.rbi +503 -0
  128. data/rbi/generated/prism/lex_compat.rbi +162 -0
  129. data/rbi/generated/prism/mutation_compiler.rbi +464 -0
  130. data/rbi/generated/prism/node.rbi +14407 -0
  131. data/rbi/generated/prism/node_ext.rbi +259 -0
  132. data/rbi/generated/prism/node_find.rbi +79 -0
  133. data/rbi/generated/prism/parse_result/errors.rbi +21 -0
  134. data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
  135. data/rbi/generated/prism/parse_result.rbi +776 -0
  136. data/rbi/generated/prism/pattern.rbi +134 -0
  137. data/rbi/generated/prism/reflection.rbi +89 -0
  138. data/rbi/generated/prism/relocation.rbi +329 -0
  139. data/rbi/generated/prism/serialize.rbi +184 -0
  140. data/rbi/generated/prism/string_query.rbi +43 -0
  141. data/rbi/generated/prism/translation.rbi +7 -0
  142. data/rbi/generated/prism/visitor.rbi +650 -0
  143. data/rbi/generated/prism.rbi +86 -0
  144. data/rbi/prism/translation/parser_versions.rbi +5 -0
  145. data/rbi/rubyvm/node_find.rbi +14 -0
  146. data/sig/generated/prism/compiler.rbs +492 -0
  147. data/sig/generated/prism/desugar_compiler.rbs +305 -0
  148. data/sig/generated/prism/dispatcher.rbs +1292 -0
  149. data/sig/generated/prism/dot_visitor.rbs +635 -0
  150. data/sig/generated/prism/dsl.rbs +928 -0
  151. data/sig/generated/prism/inspect_visitor.rbs +509 -0
  152. data/sig/generated/prism/lex_compat.rbs +166 -0
  153. data/sig/{prism → generated/prism}/mutation_compiler.rbs +309 -4
  154. data/sig/generated/prism/node.rbs +20896 -0
  155. data/sig/generated/prism/node_ext.rbs +290 -0
  156. data/sig/generated/prism/node_find.rbs +90 -0
  157. data/sig/generated/prism/parse_result/errors.rbs +22 -0
  158. data/sig/generated/prism/parse_result/newlines.rbs +223 -0
  159. data/sig/generated/prism/parse_result.rbs +871 -0
  160. data/sig/generated/prism/pattern.rbs +156 -0
  161. data/sig/generated/prism/reflection.rbs +101 -0
  162. data/sig/generated/prism/relocation.rbs +428 -0
  163. data/sig/generated/prism/serialize.rbs +198 -0
  164. data/sig/generated/prism/string_query.rbs +43 -0
  165. data/sig/generated/prism/translation.rbs +8 -0
  166. data/sig/generated/prism/visitor.rbs +1111 -0
  167. data/sig/generated/prism.rbs +84 -0
  168. data/sig/manifest.yaml +2 -0
  169. data/src/arena.c +117 -0
  170. data/src/{util/pm_buffer.c → buffer.c} +53 -21
  171. data/src/{util/pm_char.c → char.c} +28 -57
  172. data/src/comments.c +454 -0
  173. data/src/constant_pool.c +406 -0
  174. data/src/diagnostic.c +97 -65
  175. data/src/encoding.c +8 -3
  176. data/src/errors_format.c +519 -0
  177. data/src/{util/pm_integer.c → integer.c} +33 -22
  178. data/src/json.c +5733 -0
  179. data/src/line_offset_list.c +100 -0
  180. data/src/list.c +24 -0
  181. data/src/{util/pm_memchr.c → memchr.c} +8 -6
  182. data/src/node.c +3504 -7674
  183. data/src/options.c +162 -49
  184. data/src/parser.c +367 -0
  185. data/src/prettyprint.c +421 -354
  186. data/src/prism.c +8402 -7161
  187. data/src/regexp.c +961 -45
  188. data/src/serialize.c +460 -424
  189. data/src/source.c +518 -0
  190. data/src/static_literals.c +111 -43
  191. data/src/string_query.c +180 -0
  192. data/src/stringy.c +91 -0
  193. data/src/{util/pm_strncasecmp.c → strncasecmp.c} +5 -4
  194. data/src/strpbrk.c +447 -0
  195. data/src/{token_type.c → tokens.c} +39 -18
  196. data/src/xxh3.c +292 -0
  197. metadata +128 -63
  198. data/ext/prism/api_pack.c +0 -276
  199. data/include/prism/defines.h +0 -260
  200. data/include/prism/pack.h +0 -163
  201. data/include/prism/regexp.h +0 -43
  202. data/include/prism/util/pm_buffer.h +0 -236
  203. data/include/prism/util/pm_char.h +0 -204
  204. data/include/prism/util/pm_constant_pool.h +0 -218
  205. data/include/prism/util/pm_integer.h +0 -130
  206. data/include/prism/util/pm_memchr.h +0 -29
  207. data/include/prism/util/pm_newline_list.h +0 -113
  208. data/include/prism/util/pm_string.h +0 -200
  209. data/lib/prism/pack.rb +0 -230
  210. data/lib/prism/parse_result/comments.rb +0 -188
  211. data/rbi/prism/compiler.rbi +0 -12
  212. data/rbi/prism/dsl.rbi +0 -524
  213. data/rbi/prism/inspect_visitor.rbi +0 -12
  214. data/rbi/prism/node.rbi +0 -8750
  215. data/rbi/prism/node_ext.rbi +0 -107
  216. data/rbi/prism/parse_result.rbi +0 -404
  217. data/rbi/prism/reflection.rbi +0 -58
  218. data/rbi/prism/string_query.rbi +0 -12
  219. data/rbi/prism/visitor.rbi +0 -473
  220. data/rbi/prism.rbi +0 -63
  221. data/sig/prism/compiler.rbs +0 -9
  222. data/sig/prism/dispatcher.rbs +0 -19
  223. data/sig/prism/dot_visitor.rbs +0 -6
  224. data/sig/prism/dsl.rbs +0 -351
  225. data/sig/prism/inspect_visitor.rbs +0 -22
  226. data/sig/prism/lex_compat.rbs +0 -10
  227. data/sig/prism/node.rbs +0 -4042
  228. data/sig/prism/node_ext.rbs +0 -149
  229. data/sig/prism/pack.rbs +0 -43
  230. data/sig/prism/parse_result/comments.rbs +0 -38
  231. data/sig/prism/parse_result.rbs +0 -197
  232. data/sig/prism/pattern.rbs +0 -13
  233. data/sig/prism/reflection.rbs +0 -50
  234. data/sig/prism/relocation.rbs +0 -185
  235. data/sig/prism/serialize.rbs +0 -8
  236. data/sig/prism/string_query.rbs +0 -11
  237. data/sig/prism/visitor.rbs +0 -169
  238. data/sig/prism.rbs +0 -272
  239. data/src/pack.c +0 -509
  240. data/src/util/pm_constant_pool.c +0 -342
  241. data/src/util/pm_list.c +0 -49
  242. data/src/util/pm_newline_list.c +0 -125
  243. data/src/util/pm_string.c +0 -381
  244. data/src/util/pm_strpbrk.c +0 -206
@@ -1,26 +1,64 @@
1
1
  # frozen_string_literal: true
2
2
  # :markup: markdown
3
+ #--
4
+ # rbs_inline: enabled
3
5
 
4
6
  module Prism
7
+ # @rbs!
8
+ # module Translation
9
+ # class Ripper
10
+ # EXPR_NONE: Integer
11
+ # EXPR_BEG: Integer
12
+ # EXPR_MID: Integer
13
+ # EXPR_END: Integer
14
+ # EXPR_CLASS: Integer
15
+ # EXPR_VALUE: Integer
16
+ # EXPR_ARG: Integer
17
+ # EXPR_CMDARG: Integer
18
+ # EXPR_ENDARG: Integer
19
+ # EXPR_ENDFN: Integer
20
+ #
21
+ # class Lexer < Ripper
22
+ # class State
23
+ # def self.[]: (Integer value) -> State
24
+ # end
25
+ # end
26
+ #
27
+ # class LineAndColumnCache
28
+ # def initialize: (Source source) -> void
29
+ #
30
+ # def line_and_column: (Integer byte_offset) -> [Integer, Integer]
31
+ # end
32
+ # end
33
+ # end
34
+
5
35
  # This class is responsible for lexing the source using prism and then
6
36
  # converting those tokens to be compatible with Ripper. In the vast majority
7
37
  # of cases, this is a one-to-one mapping of the token type. Everything else
8
38
  # generally lines up. However, there are a few cases that require special
9
39
  # handling.
10
40
  class LexCompat # :nodoc:
41
+ # @rbs!
42
+ # # A token produced by the Ripper lexer that Prism is replicating.
43
+ # type lex_compat_token = [[Integer, Integer], Symbol, String, untyped]
44
+
11
45
  # A result class specialized for holding tokens produced by the lexer.
12
46
  class Result < Prism::Result
13
47
  # The list of tokens that were produced by the lexer.
14
- attr_reader :value
48
+ attr_reader :value #: Array[lex_compat_token]
15
49
 
16
50
  # Create a new lex compat result object with the given values.
17
- def initialize(value, comments, magic_comments, data_loc, errors, warnings, source)
51
+ #
52
+ #: (Array[lex_compat_token] value, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void
53
+ def initialize(value, magic_comments, data_loc, errors, warnings, continuable, source)
18
54
  @value = value
19
- super(comments, magic_comments, data_loc, errors, warnings, source)
55
+ super(magic_comments, data_loc, errors, warnings, continuable, source)
20
56
  end
21
57
 
22
58
  # Implement the hash pattern matching interface for Result.
23
- def deconstruct_keys(keys)
59
+ #
60
+ #: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
61
+ def deconstruct_keys(keys) # :nodoc:
24
62
  super.merge!(value: value)
25
63
  end
26
64
  end
@@ -40,6 +78,8 @@ module Prism
40
78
  BANG_EQUAL: :on_op,
41
79
  BANG_TILDE: :on_op,
42
80
  BRACE_LEFT: :on_lbrace,
81
+ BRACE_LEFT_ARGUMENT: :on_lbrace,
82
+ BRACE_LEFT_HASH: :on_lbrace,
43
83
  BRACE_RIGHT: :on_rbrace,
44
84
  BRACKET_LEFT: :on_lbracket,
45
85
  BRACKET_LEFT_ARRAY: :on_lbracket,
@@ -102,6 +142,8 @@ module Prism
102
142
  KEYWORD_DEF: :on_kw,
103
143
  KEYWORD_DEFINED: :on_kw,
104
144
  KEYWORD_DO: :on_kw,
145
+ KEYWORD_DO_BLOCK: :on_kw,
146
+ KEYWORD_DO_LAMBDA: :on_kw,
105
147
  KEYWORD_DO_LOOP: :on_kw,
106
148
  KEYWORD_ELSE: :on_kw,
107
149
  KEYWORD_ELSIF: :on_kw,
@@ -149,8 +191,10 @@ module Prism
149
191
  MINUS_EQUAL: :on_op,
150
192
  MINUS_GREATER: :on_tlambda,
151
193
  NEWLINE: :on_nl,
194
+ NEWLINE_TERMINATOR: :on_ignored_nl,
152
195
  NUMBERED_REFERENCE: :on_backref,
153
196
  PARENTHESIS_LEFT: :on_lparen,
197
+ PARENTHESIS_LEFT_GROUPING: :on_lparen,
154
198
  PARENTHESIS_LEFT_PARENTHESES: :on_lparen,
155
199
  PARENTHESIS_RIGHT: :on_rparen,
156
200
  PERCENT: :on_op,
@@ -193,60 +237,10 @@ module Prism
193
237
  USTAR: :on_op,
194
238
  USTAR_STAR: :on_op,
195
239
  WORDS_SEP: :on_words_sep,
240
+ XSTRING_BEGIN: :on_backtick,
196
241
  "__END__": :on___end__
197
242
  }.freeze
198
243
 
199
- # When we produce tokens, we produce the same arrays that Ripper does.
200
- # However, we add a couple of convenience methods onto them to make them a
201
- # little easier to work with. We delegate all other methods to the array.
202
- class Token < BasicObject
203
- # Create a new token object with the given ripper-compatible array.
204
- def initialize(array)
205
- @array = array
206
- end
207
-
208
- # The location of the token in the source.
209
- def location
210
- @array[0]
211
- end
212
-
213
- # The type of the token.
214
- def event
215
- @array[1]
216
- end
217
-
218
- # The slice of the source that this token represents.
219
- def value
220
- @array[2]
221
- end
222
-
223
- # The state of the lexer when this token was produced.
224
- def state
225
- @array[3]
226
- end
227
-
228
- # We want to pretend that this is just an Array.
229
- def ==(other) # :nodoc:
230
- @array == other
231
- end
232
-
233
- def respond_to_missing?(name, include_private = false) # :nodoc:
234
- @array.respond_to?(name, include_private)
235
- end
236
-
237
- def method_missing(name, ...) # :nodoc:
238
- @array.send(name, ...)
239
- end
240
- end
241
-
242
- # Tokens where state should be ignored
243
- # used for :on_sp, :on_comment, :on_heredoc_end, :on_embexpr_end
244
- class IgnoreStateToken < Token
245
- def ==(other) # :nodoc:
246
- self[0...-1] == other[0...-1]
247
- end
248
- end
249
-
250
244
  # A heredoc in this case is a list of tokens that belong to the body of the
251
245
  # heredoc that should be appended onto the list of tokens when the heredoc
252
246
  # closes.
@@ -256,16 +250,19 @@ module Prism
256
250
  # order back into the token stream and set the state of the last token to
257
251
  # the state that the heredoc was opened in.
258
252
  class PlainHeredoc # :nodoc:
259
- attr_reader :tokens
253
+ attr_reader :tokens #: Array[lex_compat_token]
260
254
 
255
+ #: () -> void
261
256
  def initialize
262
257
  @tokens = []
263
258
  end
264
259
 
260
+ #: (lex_compat_token token) -> void
265
261
  def <<(token)
266
262
  tokens << token
267
263
  end
268
264
 
265
+ #: () -> Array[lex_compat_token]
269
266
  def to_a
270
267
  tokens
271
268
  end
@@ -275,22 +272,26 @@ module Prism
275
272
  # that need to be split on "\\\n" to mimic Ripper's behavior. We also need
276
273
  # to keep track of the state that the heredoc was opened in.
277
274
  class DashHeredoc # :nodoc:
278
- attr_reader :split, :tokens
275
+ attr_reader :split #: bool
276
+ attr_reader :tokens #: Array[lex_compat_token]
279
277
 
278
+ #: (bool split) -> void
280
279
  def initialize(split)
281
280
  @split = split
282
281
  @tokens = []
283
282
  end
284
283
 
284
+ #: (lex_compat_token token) -> void
285
285
  def <<(token)
286
286
  tokens << token
287
287
  end
288
288
 
289
+ #: () -> Array[lex_compat_token]
289
290
  def to_a
290
291
  embexpr_balance = 0
291
292
 
292
- tokens.each_with_object([]) do |token, results| #$ Array[Token]
293
- case token.event
293
+ tokens.each_with_object([]) do |token, results| #$ Array[lex_compat_token]
294
+ case token[1]
294
295
  when :on_embexpr_beg
295
296
  embexpr_balance += 1
296
297
  results << token
@@ -305,9 +306,9 @@ module Prism
305
306
  if split
306
307
  # Split on "\\\n" to mimic Ripper's behavior. Use a lookbehind
307
308
  # to keep the delimiter in the result.
308
- token.value.split(/(?<=[^\\]\\\n)|(?<=[^\\]\\\r\n)/).each_with_index do |value, index|
309
+ token[2].split(/(?<=[^\\]\\\n)|(?<=[^\\]\\\r\n)/).each_with_index do |value, index|
309
310
  column = 0 if index > 0
310
- results << Token.new([[lineno, column], :on_tstring_content, value, token.state])
311
+ results << [[lineno, column], :on_tstring_content, value, token[3]]
311
312
  lineno += value.count("\n")
312
313
  end
313
314
  else
@@ -336,8 +337,13 @@ module Prism
336
337
  class DedentingHeredoc # :nodoc:
337
338
  TAB_WIDTH = 8
338
339
 
339
- attr_reader :tokens, :dedent_next, :dedent, :embexpr_balance
340
+ attr_reader :tokens #: Array[lex_compat_token]
341
+ attr_reader :dedent_next #: bool
342
+ attr_reader :dedent #: Integer?
343
+ attr_reader :embexpr_balance #: Integer
344
+ # @rbs @ended_on_newline: bool
340
345
 
346
+ #: () -> void
341
347
  def initialize
342
348
  @tokens = []
343
349
  @dedent_next = true
@@ -349,8 +355,10 @@ module Prism
349
355
  # As tokens are coming in, we track the minimum amount of common leading
350
356
  # whitespace on plain string content tokens. This allows us to later
351
357
  # remove that amount of whitespace from the beginning of each line.
358
+ #
359
+ #: (lex_compat_token token) -> void
352
360
  def <<(token)
353
- case token.event
361
+ case token[1]
354
362
  when :on_embexpr_beg, :on_heredoc_beg
355
363
  @embexpr_balance += 1
356
364
  @dedent = 0 if @dedent_next && @ended_on_newline
@@ -358,10 +366,10 @@ module Prism
358
366
  @embexpr_balance -= 1
359
367
  when :on_tstring_content
360
368
  if embexpr_balance == 0
361
- line = token.value
369
+ line = token[2]
362
370
 
363
371
  if dedent_next && !(line.strip.empty? && line.end_with?("\n"))
364
- leading = line[/\A(\s*)\n?/, 1]
372
+ leading = line[/\A(\s*)\n?/, 1] #: String
365
373
  next_dedent = 0
366
374
 
367
375
  leading.each_char do |char|
@@ -381,20 +389,21 @@ module Prism
381
389
  end
382
390
  end
383
391
 
384
- @dedent_next = token.event == :on_tstring_content && embexpr_balance == 0
392
+ @dedent_next = token[1] == :on_tstring_content && embexpr_balance == 0
385
393
  @ended_on_newline = false
386
394
  tokens << token
387
395
  end
388
396
 
397
+ #: () -> Array[lex_compat_token]
389
398
  def to_a
390
399
  # If every line in the heredoc is blank, we still need to split up the
391
400
  # string content token into multiple tokens.
392
401
  if dedent.nil?
393
- results = [] #: Array[Token]
402
+ results = [] #: Array[lex_compat_token]
394
403
  embexpr_balance = 0
395
404
 
396
405
  tokens.each do |token|
397
- case token.event
406
+ case token[1]
398
407
  when :on_embexpr_beg, :on_heredoc_beg
399
408
  embexpr_balance += 1
400
409
  results << token
@@ -406,9 +415,9 @@ module Prism
406
415
  lineno = token[0][0]
407
416
  column = token[0][1]
408
417
 
409
- token.value.split(/(?<=\n)/).each_with_index do |value, index|
418
+ token[2].split(/(?<=\n)/).each_with_index do |value, index|
410
419
  column = 0 if index > 0
411
- results << Token.new([[lineno, column], :on_tstring_content, value, token.state])
420
+ results << [[lineno, column], :on_tstring_content, value, token[3]]
412
421
  lineno += 1
413
422
  end
414
423
  else
@@ -425,7 +434,7 @@ module Prism
425
434
  # If the minimum common whitespace is 0, then we need to concatenate
426
435
  # string nodes together that are immediately adjacent.
427
436
  if dedent == 0
428
- results = [] #: Array[Token]
437
+ results = [] #: Array[lex_compat_token]
429
438
  embexpr_balance = 0
430
439
 
431
440
  index = 0
@@ -436,15 +445,15 @@ module Prism
436
445
  results << token
437
446
  index += 1
438
447
 
439
- case token.event
448
+ case token[1]
440
449
  when :on_embexpr_beg, :on_heredoc_beg
441
450
  embexpr_balance += 1
442
451
  when :on_embexpr_end, :on_heredoc_end
443
452
  embexpr_balance -= 1
444
453
  when :on_tstring_content
445
454
  if embexpr_balance == 0
446
- while index < max_index && tokens[index].event == :on_tstring_content && !token.value.match?(/\\\r?\n\z/)
447
- token.value << tokens[index].value
455
+ while index < max_index && tokens[index][1] == :on_tstring_content && !token[2].match?(/\\\r?\n\z/)
456
+ token[2] << tokens[index][2]
448
457
  index += 1
449
458
  end
450
459
  end
@@ -458,7 +467,7 @@ module Prism
458
467
  # insert on_ignored_sp tokens for the amount of dedent that we need to
459
468
  # perform. We also need to remove the dedent from the beginning of
460
469
  # each line of plain string content tokens.
461
- results = [] #: Array[Token]
470
+ results = [] #: Array[lex_compat_token]
462
471
  dedent_next = true
463
472
  embexpr_balance = 0
464
473
 
@@ -467,7 +476,7 @@ module Prism
467
476
  # whitespace calculation we performed above. This is because
468
477
  # checking if the subsequent token needs to be dedented is common to
469
478
  # both the dedent calculation and the ignored_sp insertion.
470
- case token.event
479
+ case token[1]
471
480
  when :on_embexpr_beg
472
481
  embexpr_balance += 1
473
482
  results << token
@@ -479,7 +488,7 @@ module Prism
479
488
  # Here we're going to split the string on newlines, but maintain
480
489
  # the newlines in the resulting array. We'll do that with a look
481
490
  # behind assertion.
482
- splits = token.value.split(/(?<=\n)/)
491
+ splits = token[2].split(/(?<=\n)/)
483
492
  index = 0
484
493
 
485
494
  while index < splits.length
@@ -497,7 +506,8 @@ module Prism
497
506
  # line or this line doesn't start with whitespace, then we
498
507
  # should concatenate the rest of the string to match ripper.
499
508
  if dedent == 0 && (!dedent_next || !line.start_with?(/\s/))
500
- line = splits[index..].join
509
+ unjoined = splits[index..] #: Array[String]
510
+ line = unjoined.join
501
511
  index = splits.length
502
512
  end
503
513
 
@@ -536,12 +546,12 @@ module Prism
536
546
  ignored = deleted_chars.join
537
547
  line.delete_prefix!(ignored)
538
548
 
539
- results << Token.new([[lineno, 0], :on_ignored_sp, ignored, token[3]])
549
+ results << [[lineno, 0], :on_ignored_sp, ignored, token[3]]
540
550
  column = ignored.length
541
551
  end
542
552
  end
543
553
 
544
- results << Token.new([[lineno, column], token[1], line, token[3]]) unless line.empty?
554
+ results << [[lineno, column], token[1], line, token[3]] unless line.empty?
545
555
  index += 1
546
556
  end
547
557
  else
@@ -552,7 +562,7 @@ module Prism
552
562
  end
553
563
 
554
564
  dedent_next =
555
- ((token.event == :on_tstring_content) || (token.event == :on_heredoc_end)) &&
565
+ ((token[1] == :on_tstring_content) || (token[1] == :on_heredoc_end)) &&
556
566
  embexpr_balance == 0
557
567
  end
558
568
 
@@ -562,12 +572,14 @@ module Prism
562
572
 
563
573
  # Here we will split between the two types of heredocs and return the
564
574
  # object that will store their tokens.
575
+ #
576
+ #: (lex_compat_token opening) -> (PlainHeredoc | DashHeredoc | DedentingHeredoc)
565
577
  def self.build(opening)
566
- case opening.value[2]
578
+ case opening[2][2]
567
579
  when "~"
568
580
  DedentingHeredoc.new
569
581
  when "-"
570
- DashHeredoc.new(opening.value[3] != "'")
582
+ DashHeredoc.new(opening[2][3] != "'")
571
583
  else
572
584
  PlainHeredoc.new
573
585
  end
@@ -581,31 +593,74 @@ module Prism
581
593
  BOM_FLUSHED = RUBY_VERSION >= "3.3.0"
582
594
  private_constant :BOM_FLUSHED
583
595
 
584
- attr_reader :options
596
+ attr_reader :options #: Hash[Symbol, untyped]
597
+ # @rbs @source: String
585
598
 
586
- def initialize(code, **options)
587
- @code = code
599
+ #: (String source, **untyped options) -> void
600
+ def initialize(source, **options)
601
+ @source = source
588
602
  @options = options
589
603
  end
590
604
 
605
+ #: () -> Result
591
606
  def result
592
- tokens = [] #: Array[LexCompat::Token]
607
+ tokens = [] #: Array[lex_compat_token]
593
608
 
594
609
  state = :default
595
610
  heredoc_stack = [[]] #: Array[Array[Heredoc::PlainHeredoc | Heredoc::DashHeredoc | Heredoc::DedentingHeredoc]]
596
611
 
597
- result = Prism.lex(@code, **options)
612
+ result = Prism.lex(@source, **options)
598
613
  source = result.source
599
614
  result_value = result.value
600
- previous_state = nil #: State?
615
+ previous_state = nil #: Translation::Ripper::Lexer::State?
601
616
  last_heredoc_end = nil #: Integer?
602
- eof_token = nil
617
+ eof_token = nil #: Token?
618
+
619
+ # The two tokens Ripper scanned most recently, oldest last. Tokens that
620
+ # this loop does not emit are not part of Ripper's view, so they never
621
+ # become either one.
622
+ previous_token = nil #: Token?
623
+ previous_previous_token = nil #: Token?
624
+
625
+ # The lex state in effect at each embedded expression that is still open,
626
+ # innermost last, and the state at the opener of the one that closed most
627
+ # recently. Ending an embedded expression restores the state its opener
628
+ # had, which on_regexp_end below has to reproduce.
629
+ embexpr_states = [] #: Array[Integer]
630
+ last_embexpr_state = nil #: Integer?
603
631
 
604
632
  bom = source.slice(0, 3) == "\xEF\xBB\xBF"
605
633
 
606
- result_value.each_with_index do |(token, lex_state), index|
607
- lineno = token.location.start_line
608
- column = token.location.start_column
634
+ last_comment_token = nil #: lex_compat_token?
635
+ last_comment_end = nil #: Integer?
636
+
637
+ result_value.each_with_index do |prism_token, index|
638
+ lineno = prism_token.location.start_line
639
+ column = prism_token.location.start_column
640
+
641
+ # Ripper is a scanner, so every event it emits corresponds to source
642
+ # characters. Implicit word separators have none.
643
+ next if prism_token.type == :WORDS_SEP_IMPLICIT
644
+
645
+ event = RIPPER.fetch(prism_token.type)
646
+ value = prism_token.value
647
+ lex_state = Translation::Ripper::Lexer::State[prism_token._ripper_state]
648
+
649
+ # A comment token does not include its terminating newline, but
650
+ # ripper's comment value does, so the newline token that directly
651
+ # follows a comment is folded back into it.
652
+ if last_comment_token && last_comment_end == prism_token.location.start_offset && (event == :on_nl || event == :on_ignored_nl)
653
+ last_comment_token[2] += value
654
+ last_comment_token = nil
655
+ last_comment_end = nil
656
+
657
+ # The newline's characters are part of the comment Ripper emits, so
658
+ # it is still the token Ripper scanned most recently, and the trailing
659
+ # whitespace check at on_eof measures from its end.
660
+ previous_previous_token = previous_token
661
+ previous_token = prism_token
662
+ next
663
+ end
609
664
 
610
665
  # If there's a UTF-8 byte-order mark as the start of the file, then for
611
666
  # certain tokens ripper sets the first token back by 3 bytes. It also
@@ -617,46 +672,45 @@ module Prism
617
672
 
618
673
  if index == 0 && column == 0 && !BOM_FLUSHED
619
674
  flushed =
620
- case token.type
675
+ case prism_token.type
621
676
  when :BACK_REFERENCE, :INSTANCE_VARIABLE, :CLASS_VARIABLE,
622
677
  :GLOBAL_VARIABLE, :NUMBERED_REFERENCE, :PERCENT_LOWER_I,
623
678
  :PERCENT_LOWER_X, :PERCENT_LOWER_W, :PERCENT_UPPER_I,
624
679
  :PERCENT_UPPER_W, :STRING_BEGIN
625
680
  true
626
681
  when :REGEXP_BEGIN, :SYMBOL_BEGIN
627
- token.value.start_with?("%")
682
+ value.start_with?("%")
628
683
  else
629
684
  false
630
685
  end
631
686
 
632
687
  unless flushed
633
688
  column -= 3
634
- value = token.value
635
689
  value.prepend(String.new("\xEF\xBB\xBF", encoding: value.encoding))
636
690
  end
637
691
  end
638
692
  end
639
693
 
640
- event = RIPPER.fetch(token.type)
641
- value = token.value
642
- lex_state = Translation::Ripper::Lexer::State.cached(lex_state)
643
-
644
- token =
694
+ lex_compat_token =
645
695
  case event
646
696
  when :on___end__
647
697
  # Ripper doesn't include the rest of the token in the event, so we need to
648
698
  # trim it down to just the content on the first line.
649
- value = value[0..value.index("\n")]
650
- Token.new([[lineno, column], event, value, lex_state])
699
+ value = value[0..value.index("\n")] #: String
700
+ [[lineno, column], event, value, lex_state]
651
701
  when :on_comment
652
- IgnoreStateToken.new([[lineno, column], event, value, lex_state])
702
+ [[lineno, column], event, value, lex_state]
653
703
  when :on_heredoc_end
654
704
  # Heredoc end tokens can be emitted in an odd order, so we don't
655
705
  # want to bother comparing the state on them.
656
- last_heredoc_end = token.location.end_offset
657
- IgnoreStateToken.new([[lineno, column], event, value, lex_state])
706
+ last_heredoc_end = prism_token.location.end_offset
707
+ [[lineno, column], event, value, lex_state]
708
+ when :on_embexpr_beg
709
+ embexpr_states.push(prism_token._ripper_state)
710
+ [[lineno, column], event, value, lex_state]
658
711
  when :on_embexpr_end
659
- IgnoreStateToken.new([[lineno, column], event, value, lex_state])
712
+ last_embexpr_state = embexpr_states.pop
713
+ [[lineno, column], event, value, lex_state]
660
714
  when :on_words_sep
661
715
  # Ripper emits one token each per line.
662
716
  value.each_line.with_index do |line, index|
@@ -664,9 +718,9 @@ module Prism
664
718
  lineno += 1
665
719
  column = 0
666
720
  end
667
- tokens << Token.new([[lineno, column], event, line, lex_state])
721
+ tokens << [[lineno, column], event, line, lex_state]
668
722
  end
669
- tokens.pop
723
+ tokens.pop #: lex_compat_token
670
724
  when :on_regexp_end
671
725
  # On regex end, Ripper scans and then sets end state, so the ripper
672
726
  # lexed output is begin, when it should be end. prism sets lex state
@@ -674,63 +728,64 @@ module Prism
674
728
  # Ripper's lexed state. So here, if it's a regexp end token, we
675
729
  # output the state as the previous state, solely for the sake of
676
730
  # comparison.
677
- previous_token = result_value[index - 1][0]
678
731
  lex_state =
679
- if RIPPER.fetch(previous_token.type) == :on_embexpr_end
680
- # If the previous token is embexpr_end, then we have to do even
681
- # more processing. The end of an embedded expression sets the
682
- # state to the state that it had at the beginning of the
683
- # embedded expression. So we have to go and find that state and
684
- # set it here.
685
- counter = 1
686
- current_index = index - 1
687
-
688
- until counter == 0
689
- current_index -= 1
690
- current_event = RIPPER.fetch(result_value[current_index][0].type)
691
- counter += { on_embexpr_beg: -1, on_embexpr_end: 1 }[current_event] || 0
692
- end
693
-
694
- Translation::Ripper::Lexer::State.cached(result_value[current_index][1])
732
+ if previous_token&.type == :EMBEXPR_END && last_embexpr_state
733
+ # An embedded expression that just closed restored the state its
734
+ # opener had, so that is the state Ripper reports here.
735
+ Translation::Ripper::Lexer::State[last_embexpr_state]
695
736
  else
696
737
  previous_state
697
738
  end
698
739
 
699
- Token.new([[lineno, column], event, value, lex_state])
740
+ [[lineno, column], event, value, lex_state]
700
741
  when :on_eof
701
- eof_token = token
702
- previous_token = result_value[index - 1][0]
703
-
704
- # If we're at the end of the file and the previous token was a
705
- # comment and there is still whitespace after the comment, then
706
- # Ripper will append a on_nl token (even though there isn't
707
- # necessarily a newline). We mirror that here.
708
- if previous_token.type == :COMMENT
709
- # If the comment is at the start of a heredoc: <<HEREDOC # comment
710
- # then the comment's end_offset is up near the heredoc_beg.
711
- # This is not the correct offset to use for figuring out if
712
- # there is trailing whitespace after the last token.
713
- # Use the greater offset of the two to determine the start of
714
- # the trailing whitespace.
715
- start_offset = [previous_token.location.end_offset, last_heredoc_end].compact.max
716
- end_offset = token.location.start_offset
717
-
718
- if start_offset < end_offset
719
- if bom
720
- start_offset += 3
721
- end_offset += 3
722
- end
742
+ eof_token = prism_token
743
+
744
+ if (previous = previous_token)
745
+ # A newline that was folded back into a comment still marks the
746
+ # comment boundary for the check below.
747
+ before = previous_previous_token
748
+ comment_boundary = previous.type == :COMMENT ||
749
+ (before && %i[NEWLINE NEWLINE_TERMINATOR IGNORED_NEWLINE].include?(previous.type) && before.type == :COMMENT && before.location.end_offset == previous.location.start_offset)
750
+
751
+ # If we're at the end of the file and the previous token was a
752
+ # comment and there is still whitespace after the comment, then
753
+ # Ripper will append a on_nl token (even though there isn't
754
+ # necessarily a newline). We mirror that here.
755
+ if comment_boundary
756
+ # If the comment is at the start of a heredoc: <<HEREDOC # comment
757
+ # then the comment's end_offset is up near the heredoc_beg.
758
+ # This is not the correct offset to use for figuring out if
759
+ # there is trailing whitespace after the last token.
760
+ # Use the greater offset of the two to determine the start of
761
+ # the trailing whitespace.
762
+ start_offset = [previous.location.end_offset, last_heredoc_end].compact.max
763
+ end_offset = prism_token.location.start_offset
764
+
765
+ if start_offset < end_offset
766
+ if bom
767
+ start_offset += 3
768
+ end_offset += 3
769
+ end
723
770
 
724
- tokens << Token.new([[lineno, 0], :on_nl, source.slice(start_offset, end_offset - start_offset), lex_state])
771
+ tokens << [[lineno, 0], :on_nl, source.slice(start_offset, end_offset - start_offset), lex_state]
772
+ end
725
773
  end
726
774
  end
727
775
 
728
- Token.new([[lineno, column], event, value, lex_state])
776
+ [[lineno, column], event, value, lex_state]
729
777
  else
730
- Token.new([[lineno, column], event, value, lex_state])
731
- end
778
+ [[lineno, column], event, value, lex_state]
779
+ end #: lex_compat_token
732
780
 
733
781
  previous_state = lex_state
782
+ previous_previous_token = previous_token
783
+ previous_token = prism_token
784
+
785
+ if event == :on_comment
786
+ last_comment_token = lex_compat_token
787
+ last_comment_end = prism_token.location.end_offset
788
+ end
734
789
 
735
790
  # The order in which tokens appear in our lexer is different from the
736
791
  # order that they appear in Ripper. When we hit the declaration of a
@@ -745,19 +800,19 @@ module Prism
745
800
  when :default
746
801
  # The default state is when there are no heredocs at all. In this
747
802
  # state we can append the token to the list of tokens and move on.
748
- tokens << token
803
+ tokens << lex_compat_token
749
804
 
750
805
  # If we get the declaration of a heredoc, then we open a new heredoc
751
806
  # and move into the heredoc_opened state.
752
807
  if event == :on_heredoc_beg
753
808
  state = :heredoc_opened
754
- heredoc_stack.last << Heredoc.build(token)
809
+ heredoc_stack.last << Heredoc.build(lex_compat_token)
755
810
  end
756
811
  when :heredoc_opened
757
812
  # The heredoc_opened state is when we've seen the declaration of a
758
813
  # heredoc and are now lexing the body of the heredoc. In this state we
759
814
  # push tokens onto the most recently created heredoc.
760
- heredoc_stack.last.last << token
815
+ heredoc_stack.last.last << lex_compat_token
761
816
 
762
817
  case event
763
818
  when :on_heredoc_beg
@@ -765,7 +820,7 @@ module Prism
765
820
  # heredoc, this means we have nested heredocs. In this case we'll
766
821
  # push a new heredoc onto the stack and stay in the heredoc_opened
767
822
  # state since we're now lexing the body of the new heredoc.
768
- heredoc_stack << [Heredoc.build(token)]
823
+ heredoc_stack << [Heredoc.build(lex_compat_token)]
769
824
  when :on_heredoc_end
770
825
  # If we receive the end of a heredoc, then we're done lexing the
771
826
  # body of the heredoc. In this case we now have a completed heredoc
@@ -774,10 +829,10 @@ module Prism
774
829
  state = :heredoc_closed
775
830
  end
776
831
  when :heredoc_closed
777
- if %i[on_nl on_ignored_nl on_comment].include?(event) || (event == :on_tstring_content && value.end_with?("\n"))
832
+ if %i[on_nl on_ignored_nl on_comment].include?(event) || ((event == :on_tstring_content) && value.end_with?("\n"))
778
833
  if heredoc_stack.size > 1
779
- flushing = heredoc_stack.pop
780
- heredoc_stack.last.last << token
834
+ flushing = heredoc_stack.pop #: Array[Heredoc::PlainHeredoc | Heredoc::DashHeredoc | Heredoc::DedentingHeredoc]
835
+ heredoc_stack.last.last << lex_compat_token
781
836
 
782
837
  flushing.each do |heredoc|
783
838
  heredoc.to_a.each do |flushed_token|
@@ -789,12 +844,12 @@ module Prism
789
844
  next
790
845
  end
791
846
  elsif event == :on_heredoc_beg
792
- tokens << token
847
+ tokens << lex_compat_token
793
848
  state = :heredoc_opened
794
- heredoc_stack.last << Heredoc.build(token)
849
+ heredoc_stack.last << Heredoc.build(lex_compat_token)
795
850
  next
796
851
  elsif heredoc_stack.size > 1
797
- heredoc_stack[-2].last << token
852
+ heredoc_stack[-2].last << lex_compat_token
798
853
  next
799
854
  end
800
855
 
@@ -805,34 +860,44 @@ module Prism
805
860
  heredoc_stack.last.clear
806
861
  state = :default
807
862
 
808
- tokens << token
863
+ tokens << lex_compat_token
809
864
  end
810
865
  end
811
866
 
812
- # Drop the EOF token from the list
813
- tokens = tokens[0...-1]
867
+ # Drop the EOF token from the list. The EOF token may not be
868
+ # present if the source was syntax invalid
869
+ if tokens.dig(-1, 1) == :on_eof
870
+ tokens = tokens[0...-1] #: Array[lex_compat_token]
871
+ end
814
872
 
815
873
  # We sort by location because Ripper.lex sorts.
816
- # Manually implemented instead of `sort_by!(&:location)` for performance.
817
874
  tokens.sort_by! do |token|
818
- line, column = token.location
875
+ line, column = token[0]
819
876
  source.byte_offset(line, column)
820
877
  end
821
878
 
822
- # Add :on_sp tokens
823
- tokens = add_on_sp_tokens(tokens, source, result.data_loc, bom, eof_token)
879
+ tokens = post_process_tokens(tokens, source, result.data_loc, bom, eof_token)
824
880
 
825
- Result.new(tokens, result.comments, result.magic_comments, result.data_loc, result.errors, result.warnings, source)
881
+ Result.new(tokens, result.magic_comments, result.data_loc, result.errors, result.warnings, result.continuable?, source)
826
882
  end
827
883
 
828
- def add_on_sp_tokens(tokens, source, data_loc, bom, eof_token)
829
- new_tokens = []
884
+ private
885
+
886
+ #: (Array[lex_compat_token] tokens, Source source, Location? data_loc, bool bom, Token? eof_token) -> Array[lex_compat_token]
887
+ def post_process_tokens(tokens, source, data_loc, bom, eof_token)
888
+ new_tokens = [] #: Array[lex_compat_token]
830
889
 
831
- prev_token_state = Translation::Ripper::Lexer::State.cached(Translation::Ripper::EXPR_BEG)
890
+ prev_token_state = Translation::Ripper::Lexer::State[Translation::Ripper::EXPR_BEG]
832
891
  prev_token_end = bom ? 3 : 0
833
892
 
893
+ cache = Translation::Ripper::LineAndColumnCache.new(source)
894
+
834
895
  tokens.each do |token|
835
- line, column = token.location
896
+ # Skip missing heredoc ends.
897
+ next if token[1] == :on_heredoc_end && token[2] == ""
898
+
899
+ # Add :on_sp tokens.
900
+ line, column = token[0]
836
901
  start_offset = source.byte_offset(line, column)
837
902
 
838
903
  # Ripper reports columns on line 1 without counting the BOM, so we
@@ -841,8 +906,7 @@ module Prism
841
906
 
842
907
  if start_offset > prev_token_end
843
908
  sp_value = source.slice(prev_token_end, start_offset - prev_token_end)
844
- sp_line = source.line(prev_token_end)
845
- sp_column = source.column(prev_token_end)
909
+ sp_line, sp_column = cache.line_and_column(prev_token_end)
846
910
  # Ripper reports columns on line 1 without counting the BOM
847
911
  sp_column -= 3 if sp_line == 1 && bom
848
912
  continuation_index = sp_value.byteindex("\\")
@@ -852,54 +916,32 @@ module Prism
852
916
  next_whitespace_index = continuation_index + 1
853
917
  next_whitespace_index += 1 if sp_value.byteslice(next_whitespace_index) == "\r"
854
918
  next_whitespace_index += 1
855
- first_whitespace = sp_value[0...continuation_index]
856
- continuation = sp_value[continuation_index...next_whitespace_index]
857
- second_whitespace = sp_value[next_whitespace_index..]
858
-
859
- new_tokens << IgnoreStateToken.new([
860
- [sp_line, sp_column],
861
- :on_sp,
862
- first_whitespace,
863
- prev_token_state
864
- ]) unless first_whitespace.empty?
865
-
866
- new_tokens << IgnoreStateToken.new([
867
- [sp_line, sp_column + continuation_index],
868
- :on_sp,
869
- continuation,
870
- prev_token_state
871
- ])
872
-
873
- new_tokens << IgnoreStateToken.new([
874
- [sp_line + 1, 0],
875
- :on_sp,
876
- second_whitespace,
877
- prev_token_state
878
- ]) unless second_whitespace.empty?
919
+ first_whitespace = sp_value[0...continuation_index] #: String
920
+ continuation = sp_value[continuation_index...next_whitespace_index] #: String
921
+ second_whitespace = sp_value[next_whitespace_index..] || ""
922
+
923
+ new_tokens << [[sp_line, sp_column], :on_sp, first_whitespace, prev_token_state] unless first_whitespace.empty?
924
+ new_tokens << [[sp_line, sp_column + continuation_index], :on_sp, continuation, prev_token_state]
925
+ new_tokens << [[sp_line + 1, 0], :on_sp, second_whitespace, prev_token_state] unless second_whitespace.empty?
879
926
  else
880
- new_tokens << IgnoreStateToken.new([
881
- [sp_line, sp_column],
882
- :on_sp,
883
- sp_value,
884
- prev_token_state
885
- ])
927
+ new_tokens << [[sp_line, sp_column], :on_sp, sp_value, prev_token_state]
886
928
  end
887
929
  end
888
930
 
889
931
  new_tokens << token
890
- prev_token_state = token.state
891
- prev_token_end = start_offset + token.value.bytesize
932
+ prev_token_state = token[3]
933
+ prev_token_end = start_offset + token[2].bytesize
892
934
  end
893
935
 
894
- unless data_loc # no trailing :on_sp with __END__ as it is always preceded by :on_nl
936
+ if !data_loc && eof_token # no trailing :on_sp with __END__ as it is always preceded by :on_nl
895
937
  end_offset = eof_token.location.end_offset
896
938
  if prev_token_end < end_offset
897
- new_tokens << IgnoreStateToken.new([
939
+ new_tokens << [
898
940
  [source.line(prev_token_end), source.column(prev_token_end)],
899
941
  :on_sp,
900
942
  source.slice(prev_token_end, end_offset - prev_token_end),
901
943
  prev_token_state
902
- ])
944
+ ]
903
945
  end
904
946
  end
905
947