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,7 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
  # :markup: markdown
3
+ #--
4
+ # rbs_inline: enabled
3
5
 
4
6
  module Prism
7
+ # @rbs!
8
+ # # An internal interface for a cache that can be used to compute code
9
+ # # units from byte offsets.
10
+ # interface _CodeUnitsCache
11
+ # def []: (Integer byte_offset) -> Integer
12
+ # end
13
+
5
14
  # This represents a source of Ruby code that has been parsed. It is used in
6
15
  # conjunction with locations to allow them to resolve line numbers and source
7
16
  # ranges.
@@ -10,14 +19,33 @@ module Prism
10
19
  # be used instead of `new` and it will return either a `Source` or a
11
20
  # specialized and more performant `ASCIISource` if no multibyte characters
12
21
  # are present in the source code.
13
- def self.for(source, start_line = 1, offsets = [])
22
+ #
23
+ # Note that if you are calling this method manually, you will need to supply
24
+ # the start_line and offsets parameters. start_line is the line number that
25
+ # the source starts on, which is typically 1 but can be different if this
26
+ # source is a subset of a larger source or if this is an eval. offsets is an
27
+ # array of byte offsets for the start of each line in the source code, which
28
+ # can be calculated by iterating through the source code and recording the
29
+ # byte offset whenever a newline character is encountered. The first
30
+ # element is always 0 to mark the first line.
31
+ #
32
+ # The comments, comment_attachments, and comment_ranges parameters are the
33
+ # lists described by the attributes of the same names. They are passed in
34
+ # empty when the source is created by the parser and filled in afterward,
35
+ # because the comments themselves refer to the source.
36
+ #
37
+ # The checksum parameter is the value described by the attribute of the same
38
+ # name. It is nil when the source is not created by the parser.
39
+ #
40
+ #: (String source, Integer start_line, Array[Integer] offsets, ?Array[Comment] comments, ?Array[Comment] comment_attachments, ?Array[Integer?] comment_ranges, ?Integer? checksum) -> Source
41
+ def self.for(source, start_line, offsets, comments = [], comment_attachments = [], comment_ranges = [], checksum = nil)
14
42
  if source.ascii_only?
15
- ASCIISource.new(source, start_line, offsets)
43
+ ASCIISource.new(source, start_line, offsets, comments, comment_attachments, comment_ranges, checksum)
16
44
  elsif source.encoding == Encoding::BINARY
17
45
  source.force_encoding(Encoding::UTF_8)
18
46
 
19
47
  if source.valid_encoding?
20
- new(source, start_line, offsets)
48
+ new(source, start_line, offsets, comments, comment_attachments, comment_ranges, checksum)
21
49
  else
22
50
  # This is an extremely niche use case where the file is marked as
23
51
  # binary, contains multi-byte characters, and those characters are not
@@ -26,57 +54,125 @@ module Prism
26
54
  # problems when asking for code units, but it appears to be the
27
55
  # cleanest solution at the moment.
28
56
  source.force_encoding(Encoding::BINARY)
29
- ASCIISource.new(source, start_line, offsets)
57
+ ASCIISource.new(source, start_line, offsets, comments, comment_attachments, comment_ranges, checksum)
30
58
  end
31
59
  else
32
- new(source, start_line, offsets)
60
+ new(source, start_line, offsets, comments, comment_attachments, comment_ranges, checksum)
33
61
  end
34
62
  end
35
63
 
36
64
  # The source code that this source object represents.
37
- attr_reader :source
65
+ attr_reader :source #: String
38
66
 
39
67
  # The line number where this source starts.
40
- attr_reader :start_line
41
-
42
- # The list of newline byte offsets in the source code.
43
- attr_reader :offsets
68
+ attr_reader :start_line #: Integer
69
+
70
+ # The checksum of the source code which can be used to quickly determine if
71
+ # the source has changed.
72
+ attr_reader :checksum #: Integer?
73
+
74
+ # The list of comments that were encountered during parsing. When the source
75
+ # is parsed with the attach_comments option, these are sorted by start
76
+ # offset.
77
+ attr_reader :comments #: Array[Comment]
78
+
79
+ # The comments attached to nodes, grouped by node in order of node id. This
80
+ # is empty unless the source was parsed with the attach_comments option.
81
+ attr_reader :comment_attachments #: Array[Comment]
82
+
83
+ # Indices into comment_attachments, indexed by node id. For each node that
84
+ # owns comments, the index at which its comments begin is stored at its node
85
+ # id and the index at which they end is stored at the following node id. The
86
+ # other entries are nil. This is empty unless the source was parsed with the
87
+ # attach_comments option.
88
+ attr_reader :comment_ranges #: Array[Integer?]
89
+
90
+ # The list of newline byte offsets in the source code. When initialized from
91
+ # the C extension, this may be a packed binary string of uint32_t values
92
+ # that is lazily unpacked on first access.
93
+ #
94
+ #: () -> Array[Integer]
95
+ def offsets
96
+ offsets = @offsets
97
+ return offsets if offsets.is_a?(Array)
98
+ @offsets = offsets.unpack("L*")
99
+ end
44
100
 
45
- # Create a new source object with the given source code.
46
- def initialize(source, start_line = 1, offsets = [])
101
+ # Create a new source object with the given source code. The offsets
102
+ # parameter can be either an Array of Integer byte offsets or a packed
103
+ # binary string of uint32_t values (from the C extension).
104
+ #
105
+ #: (String source, Integer start_line, Array[Integer] | String offsets, ?Array[Comment] comments, ?Array[Comment] comment_attachments, ?Array[Integer?] comment_ranges, ?Integer? checksum) -> void
106
+ def initialize(source, start_line, offsets, comments = [], comment_attachments = [], comment_ranges = [], checksum = nil)
47
107
  @source = source
48
- @start_line = start_line # set after parsing is done
49
- @offsets = offsets # set after parsing is done
108
+ @start_line = start_line
109
+ @checksum = checksum
110
+ @offsets = offsets
111
+ @comments = comments
112
+ @comment_attachments = comment_attachments
113
+ @comment_ranges = comment_ranges
50
114
  end
51
115
 
52
116
  # Replace the value of start_line with the given value.
117
+ #
118
+ #: (Integer start_line) -> void
53
119
  def replace_start_line(start_line)
54
120
  @start_line = start_line
55
121
  end
56
122
 
123
+ # Replace the value of checksum with the given value.
124
+ #
125
+ #: (Integer checksum) -> void
126
+ def replace_checksum(checksum)
127
+ @checksum = checksum
128
+ end
129
+
57
130
  # Replace the value of offsets with the given value.
131
+ #
132
+ #: (Array[Integer] offsets) -> void
58
133
  def replace_offsets(offsets)
59
- @offsets.replace(offsets)
134
+ @offsets = offsets
135
+ end
136
+
137
+ # The comments attached to the node with the given id, ordered by start
138
+ # offset. Comments are only attached when the source is parsed with the
139
+ # attach_comments option.
140
+ #
141
+ #: (Integer node_id) -> Array[Comment]
142
+ def comments_for(node_id)
143
+ start = comment_ranges[node_id]
144
+ finish = comment_ranges[node_id + 1]
145
+ return [] if start.nil? || finish.nil?
146
+
147
+ comment_attachments[start...finish] || []
60
148
  end
61
149
 
62
150
  # Returns the encoding of the source code, which is set by parameters to the
63
151
  # parser or by the encoding magic comment.
152
+ #
153
+ #: () -> Encoding
64
154
  def encoding
65
155
  source.encoding
66
156
  end
67
157
 
68
158
  # Returns the lines of the source code as an array of strings.
159
+ #
160
+ #: () -> Array[String]
69
161
  def lines
70
162
  source.lines
71
163
  end
72
164
 
73
165
  # Perform a byteslice on the source code using the given byte offset and
74
166
  # byte length.
167
+ #
168
+ #: (Integer byte_offset, Integer length) -> String
75
169
  def slice(byte_offset, length)
76
170
  source.byteslice(byte_offset, length) or raise
77
171
  end
78
172
 
79
173
  # Converts the line number and column in bytes to a byte offset.
174
+ #
175
+ #: (Integer line, Integer column) -> Integer
80
176
  def byte_offset(line, column)
81
177
  normal = line - @start_line
82
178
  raise IndexError if normal < 0
@@ -87,33 +183,45 @@ module Prism
87
183
 
88
184
  # Binary search through the offsets to find the line number for the given
89
185
  # byte offset.
186
+ #
187
+ #: (Integer byte_offset) -> Integer
90
188
  def line(byte_offset)
91
189
  start_line + find_line(byte_offset)
92
190
  end
93
191
 
94
192
  # Return the byte offset of the start of the line corresponding to the given
95
193
  # byte offset.
194
+ #
195
+ #: (Integer byte_offset) -> Integer
96
196
  def line_start(byte_offset)
97
197
  offsets[find_line(byte_offset)]
98
198
  end
99
199
 
100
200
  # Returns the byte offset of the end of the line corresponding to the given
101
201
  # byte offset.
202
+ #
203
+ #: (Integer byte_offset) -> Integer
102
204
  def line_end(byte_offset)
103
205
  offsets[find_line(byte_offset) + 1] || source.bytesize
104
206
  end
105
207
 
106
- # Return the column number for the given byte offset.
208
+ # Return the column in bytes for the given byte offset.
209
+ #
210
+ #: (Integer byte_offset) -> Integer
107
211
  def column(byte_offset)
108
212
  byte_offset - line_start(byte_offset)
109
213
  end
110
214
 
111
215
  # Return the character offset for the given byte offset.
216
+ #
217
+ #: (Integer byte_offset) -> Integer
112
218
  def character_offset(byte_offset)
113
219
  (source.byteslice(0, byte_offset) or raise).length
114
220
  end
115
221
 
116
- # Return the column number in characters for the given byte offset.
222
+ # Return the column in characters for the given byte offset.
223
+ #
224
+ #: (Integer byte_offset) -> Integer
117
225
  def character_column(byte_offset)
118
226
  character_offset(byte_offset) - character_offset(line_start(byte_offset))
119
227
  end
@@ -130,7 +238,11 @@ module Prism
130
238
  # possible that the given byte offset will not occur on a character
131
239
  # boundary. Second, it's possible that the source code will contain a
132
240
  # character that has no equivalent in the given encoding.
241
+ #
242
+ #: (Integer byte_offset, Encoding encoding) -> Integer
133
243
  def code_units_offset(byte_offset, encoding)
244
+ return byte_offset if encoding == Encoding::UTF_8
245
+
134
246
  byteslice = (source.byteslice(0, byte_offset) or raise).encode(encoding, invalid: :replace, undef: :replace)
135
247
 
136
248
  if encoding == Encoding::UTF_16LE || encoding == Encoding::UTF_16BE
@@ -142,28 +254,37 @@ module Prism
142
254
 
143
255
  # Generate a cache that targets a specific encoding for calculating code
144
256
  # unit offsets.
257
+ #
258
+ #: (Encoding encoding) -> CodeUnitsCache
145
259
  def code_units_cache(encoding)
146
260
  CodeUnitsCache.new(source, encoding)
147
261
  end
148
262
 
149
- # Returns the column number in code units for the given encoding for the
263
+ # Returns the column in code units for the given encoding for the
150
264
  # given byte offset.
265
+ #
266
+ #: (Integer byte_offset, Encoding encoding) -> Integer
151
267
  def code_units_column(byte_offset, encoding)
152
268
  code_units_offset(byte_offset, encoding) - code_units_offset(line_start(byte_offset), encoding)
153
269
  end
154
270
 
155
271
  # Freeze this object and the objects it contains.
272
+ #
273
+ #: () -> void
156
274
  def deep_freeze
157
275
  source.freeze
158
276
  offsets.freeze
277
+ comments.freeze
278
+ comment_attachments.freeze
279
+ comment_ranges.freeze
159
280
  freeze
160
281
  end
161
282
 
162
- private
163
-
164
- # Binary search through the offsets to find the line number for the given
283
+ # Binary search through the offsets to find the index for the given
165
284
  # byte offset.
166
- def find_line(byte_offset)
285
+ #
286
+ #: (Integer byte_offset) -> Integer
287
+ def find_line(byte_offset) # :nodoc:
167
288
  index = offsets.bsearch_index { |offset| offset > byte_offset } || offsets.length
168
289
  index - 1
169
290
  end
@@ -184,38 +305,69 @@ module Prism
184
305
  # has not yet been implemented.
185
306
  #
186
307
  class CodeUnitsCache
308
+ # Counter used for UTF-8, where one code unit equals one byte.
309
+ class UTF8Counter # :nodoc:
310
+ #: (Integer byte_offset, Integer byte_length) -> Integer
311
+ def count(byte_offset, byte_length)
312
+ byte_length
313
+ end
314
+ end
315
+
187
316
  class UTF16Counter # :nodoc:
317
+ # @rbs @source: String
318
+ # @rbs @encoding: Encoding
319
+
320
+ #: (String source, Encoding encoding) -> void
188
321
  def initialize(source, encoding)
189
322
  @source = source
190
323
  @encoding = encoding
191
324
  end
192
325
 
326
+ #: (Integer byte_offset, Integer byte_length) -> Integer
193
327
  def count(byte_offset, byte_length)
194
- @source.byteslice(byte_offset, byte_length).encode(@encoding, invalid: :replace, undef: :replace).bytesize / 2
328
+ (@source.byteslice(byte_offset, byte_length) or raise).encode(@encoding, invalid: :replace, undef: :replace).bytesize / 2
195
329
  end
196
330
  end
197
331
 
198
- class LengthCounter # :nodoc:
332
+ # Counter used for UTF-32, where one code unit equals one code point and
333
+ # matches String#length. Also used as a best-effort fallback for any other
334
+ # encoding that does not have a dedicated counter.
335
+ class UTF32Counter # :nodoc:
336
+ # @rbs @source: String
337
+ # @rbs @encoding: Encoding
338
+
339
+ #: (String source, Encoding encoding) -> void
199
340
  def initialize(source, encoding)
200
341
  @source = source
201
342
  @encoding = encoding
202
343
  end
203
344
 
345
+ #: (Integer byte_offset, Integer byte_length) -> Integer
204
346
  def count(byte_offset, byte_length)
205
- @source.byteslice(byte_offset, byte_length).encode(@encoding, invalid: :replace, undef: :replace).length
347
+ (@source.byteslice(byte_offset, byte_length) or raise).encode(@encoding, invalid: :replace, undef: :replace).length
206
348
  end
207
349
  end
208
350
 
209
- private_constant :UTF16Counter, :LengthCounter
351
+ private_constant :UTF8Counter, :UTF16Counter, :UTF32Counter
352
+
353
+ # @rbs @source: String
354
+ # @rbs @counter: UTF8Counter | UTF16Counter | UTF32Counter
355
+ # @rbs @cache: Hash[Integer, Integer]
356
+ # @rbs @offsets: Array[Integer]
210
357
 
211
358
  # Initialize a new cache with the given source and encoding.
359
+ #
360
+ #: (String source, Encoding encoding) -> void
212
361
  def initialize(source, encoding)
213
362
  @source = source
214
363
  @counter =
215
- if encoding == Encoding::UTF_16LE || encoding == Encoding::UTF_16BE
364
+ case encoding
365
+ when Encoding::UTF_8
366
+ UTF8Counter.new
367
+ when Encoding::UTF_16LE, Encoding::UTF_16BE
216
368
  UTF16Counter.new(source, encoding)
217
369
  else
218
- LengthCounter.new(source, encoding)
370
+ UTF32Counter.new(source, encoding)
219
371
  end
220
372
 
221
373
  @cache = {} #: Hash[Integer, Integer]
@@ -223,6 +375,8 @@ module Prism
223
375
  end
224
376
 
225
377
  # Retrieve the code units offset from the given byte offset.
378
+ #
379
+ #: (Integer byte_offset) -> Integer
226
380
  def [](byte_offset)
227
381
  @cache[byte_offset] ||=
228
382
  if (index = @offsets.bsearch_index { |offset| offset > byte_offset }).nil?
@@ -249,11 +403,15 @@ module Prism
249
403
  # at that point we will treat everything as single-byte characters.
250
404
  class ASCIISource < Source
251
405
  # Return the character offset for the given byte offset.
406
+ #
407
+ #: (Integer byte_offset) -> Integer
252
408
  def character_offset(byte_offset)
253
409
  byte_offset
254
410
  end
255
411
 
256
- # Return the column number in characters for the given byte offset.
412
+ # Return the column in characters for the given byte offset.
413
+ #
414
+ #: (Integer byte_offset) -> Integer
257
415
  def character_column(byte_offset)
258
416
  byte_offset - line_start(byte_offset)
259
417
  end
@@ -264,6 +422,8 @@ module Prism
264
422
  # This method is tested with UTF-8, UTF-16, and UTF-32. If there is the
265
423
  # concept of code units that differs from the number of characters in other
266
424
  # encodings, it is not captured here.
425
+ #
426
+ #: (Integer byte_offset, Encoding encoding) -> Integer
267
427
  def code_units_offset(byte_offset, encoding)
268
428
  byte_offset
269
429
  end
@@ -271,6 +431,8 @@ module Prism
271
431
  # Returns a cache that is the identity function in order to maintain the
272
432
  # same interface. We can do this because code units are always equivalent to
273
433
  # byte offsets for ASCII-only sources.
434
+ #
435
+ #: (Encoding encoding) -> _CodeUnitsCache
274
436
  def code_units_cache(encoding)
275
437
  ->(byte_offset) { byte_offset }
276
438
  end
@@ -278,6 +440,8 @@ module Prism
278
440
  # Specialized version of `code_units_column` that does not depend on
279
441
  # `code_units_offset`, which is a more expensive operation. This is
280
442
  # essentially the same as `Prism::Source#column`.
443
+ #
444
+ #: (Integer byte_offset, Encoding encoding) -> Integer
281
445
  def code_units_column(byte_offset, encoding)
282
446
  byte_offset - line_start(byte_offset)
283
447
  end
@@ -287,79 +451,57 @@ module Prism
287
451
  class Location
288
452
  # A Source object that is used to determine more information from the given
289
453
  # offset and length.
290
- attr_reader :source
454
+ attr_reader :source #: Source
291
455
  protected :source
292
456
 
293
457
  # The byte offset from the beginning of the source where this location
294
458
  # starts.
295
- attr_reader :start_offset
459
+ attr_reader :start_offset #: Integer
296
460
 
297
461
  # The length of this location in bytes.
298
- attr_reader :length
462
+ attr_reader :length #: Integer
299
463
 
300
464
  # Create a new location object with the given source, start byte offset, and
301
465
  # byte length.
466
+ #
467
+ #: (Source source, Integer start_offset, Integer length) -> void
302
468
  def initialize(source, start_offset, length)
303
469
  @source = source
304
470
  @start_offset = start_offset
305
471
  @length = length
306
-
307
- # These are used to store comments that are associated with this location.
308
- # They are initialized to `nil` to save on memory when there are no
309
- # comments to be attached and/or the comment-related APIs are not used.
310
- @leading_comments = nil
311
- @trailing_comments = nil
312
- end
313
-
314
- # These are the comments that are associated with this location that exist
315
- # before the start of this location.
316
- def leading_comments
317
- @leading_comments ||= []
318
- end
319
-
320
- # Attach a comment to the leading comments of this location.
321
- def leading_comment(comment)
322
- leading_comments << comment
323
- end
324
-
325
- # These are the comments that are associated with this location that exist
326
- # after the end of this location.
327
- def trailing_comments
328
- @trailing_comments ||= []
329
- end
330
-
331
- # Attach a comment to the trailing comments of this location.
332
- def trailing_comment(comment)
333
- trailing_comments << comment
334
- end
335
-
336
- # Returns all comments that are associated with this location (both leading
337
- # and trailing comments).
338
- def comments
339
- [*@leading_comments, *@trailing_comments]
340
472
  end
341
473
 
342
474
  # Create a new location object with the given options.
475
+ #
476
+ #: (?source: Source, ?start_offset: Integer, ?length: Integer) -> Location
343
477
  def copy(source: self.source, start_offset: self.start_offset, length: self.length)
344
478
  Location.new(source, start_offset, length)
345
479
  end
346
480
 
347
481
  # Returns a new location that is the result of chopping off the last byte.
482
+ #
483
+ #: () -> Location
348
484
  def chop
349
485
  copy(length: length == 0 ? length : length - 1)
350
486
  end
351
487
 
352
488
  # Returns a string representation of this location.
353
- def inspect
489
+ #
490
+ #: () -> String
491
+ def inspect # :nodoc:
354
492
  "#<Prism::Location @start_offset=#{@start_offset} @length=#{@length} start_line=#{start_line}>"
355
493
  end
356
494
 
357
495
  # Returns all of the lines of the source code associated with this location.
496
+ #
497
+ #: () -> Array[String]
358
498
  def source_lines
359
499
  source.lines
360
500
  end
361
501
 
362
502
  # The source code that this location represents.
503
+ #
504
+ #: () -> String
363
505
  def slice
364
506
  source.slice(start_offset, length)
365
507
  end
@@ -367,6 +509,8 @@ module Prism
367
509
  # The source code that this location represents starting from the beginning
368
510
  # of the line that this location starts on to the end of the line that this
369
511
  # location ends on.
512
+ #
513
+ #: () -> String
370
514
  def slice_lines
371
515
  line_start = source.line_start(start_offset)
372
516
  line_end = source.line_end(end_offset)
@@ -375,118 +519,160 @@ module Prism
375
519
 
376
520
  # The character offset from the beginning of the source where this location
377
521
  # starts.
522
+ #
523
+ #: () -> Integer
378
524
  def start_character_offset
379
525
  source.character_offset(start_offset)
380
526
  end
381
527
 
382
528
  # The offset from the start of the file in code units of the given encoding.
529
+ #
530
+ #: (Encoding encoding) -> Integer
383
531
  def start_code_units_offset(encoding = Encoding::UTF_16LE)
384
532
  source.code_units_offset(start_offset, encoding)
385
533
  end
386
534
 
387
535
  # The start offset from the start of the file in code units using the given
388
536
  # cache to fetch or calculate the value.
537
+ #
538
+ #: (_CodeUnitsCache cache) -> Integer
389
539
  def cached_start_code_units_offset(cache)
390
540
  cache[start_offset]
391
541
  end
392
542
 
393
543
  # The byte offset from the beginning of the source where this location ends.
544
+ #
545
+ #: () -> Integer
394
546
  def end_offset
395
547
  start_offset + length
396
548
  end
397
549
 
398
550
  # The character offset from the beginning of the source where this location
399
551
  # ends.
552
+ #
553
+ #: () -> Integer
400
554
  def end_character_offset
401
555
  source.character_offset(end_offset)
402
556
  end
403
557
 
404
558
  # The offset from the start of the file in code units of the given encoding.
559
+ #
560
+ #: (Encoding encoding) -> Integer
405
561
  def end_code_units_offset(encoding = Encoding::UTF_16LE)
406
562
  source.code_units_offset(end_offset, encoding)
407
563
  end
408
564
 
409
565
  # The end offset from the start of the file in code units using the given
410
566
  # cache to fetch or calculate the value.
567
+ #
568
+ #: (_CodeUnitsCache cache) -> Integer
411
569
  def cached_end_code_units_offset(cache)
412
570
  cache[end_offset]
413
571
  end
414
572
 
415
573
  # The line number where this location starts.
574
+ #
575
+ #: () -> Integer
416
576
  def start_line
417
577
  source.line(start_offset)
418
578
  end
419
579
 
420
580
  # The content of the line where this location starts before this location.
581
+ #
582
+ #: () -> String
421
583
  def start_line_slice
422
584
  offset = source.line_start(start_offset)
423
585
  source.slice(offset, start_offset - offset)
424
586
  end
425
587
 
426
588
  # The line number where this location ends.
589
+ #
590
+ #: () -> Integer
427
591
  def end_line
428
592
  source.line(end_offset)
429
593
  end
430
594
 
431
- # The column number in bytes where this location starts from the start of
595
+ # The column in bytes where this location starts from the start of
432
596
  # the line.
597
+ #
598
+ #: () -> Integer
433
599
  def start_column
434
600
  source.column(start_offset)
435
601
  end
436
602
 
437
- # The column number in characters where this location ends from the start of
603
+ # The column in characters where this location ends from the start of
438
604
  # the line.
605
+ #
606
+ #: () -> Integer
439
607
  def start_character_column
440
608
  source.character_column(start_offset)
441
609
  end
442
610
 
443
- # The column number in code units of the given encoding where this location
611
+ # The column in code units of the given encoding where this location
444
612
  # starts from the start of the line.
613
+ #
614
+ #: (?Encoding encoding) -> Integer
445
615
  def start_code_units_column(encoding = Encoding::UTF_16LE)
446
616
  source.code_units_column(start_offset, encoding)
447
617
  end
448
618
 
449
619
  # The start column in code units using the given cache to fetch or calculate
450
620
  # the value.
621
+ #
622
+ #: (_CodeUnitsCache cache) -> Integer
451
623
  def cached_start_code_units_column(cache)
452
624
  cache[start_offset] - cache[source.line_start(start_offset)]
453
625
  end
454
626
 
455
- # The column number in bytes where this location ends from the start of the
627
+ # The column in bytes where this location ends from the start of the
456
628
  # line.
629
+ #
630
+ #: () -> Integer
457
631
  def end_column
458
632
  source.column(end_offset)
459
633
  end
460
634
 
461
- # The column number in characters where this location ends from the start of
635
+ # The column in characters where this location ends from the start of
462
636
  # the line.
637
+ #
638
+ #: () -> Integer
463
639
  def end_character_column
464
640
  source.character_column(end_offset)
465
641
  end
466
642
 
467
- # The column number in code units of the given encoding where this location
643
+ # The column in code units of the given encoding where this location
468
644
  # ends from the start of the line.
645
+ #
646
+ #: (?Encoding encoding) -> Integer
469
647
  def end_code_units_column(encoding = Encoding::UTF_16LE)
470
648
  source.code_units_column(end_offset, encoding)
471
649
  end
472
650
 
473
651
  # The end column in code units using the given cache to fetch or calculate
474
652
  # the value.
653
+ #
654
+ #: (_CodeUnitsCache cache) -> Integer
475
655
  def cached_end_code_units_column(cache)
476
656
  cache[end_offset] - cache[source.line_start(end_offset)]
477
657
  end
478
658
 
479
659
  # Implement the hash pattern matching interface for Location.
480
- def deconstruct_keys(keys)
660
+ #
661
+ #: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
662
+ def deconstruct_keys(keys) # :nodoc:
481
663
  { start_offset: start_offset, end_offset: end_offset }
482
664
  end
483
665
 
484
666
  # Implement the pretty print interface for Location.
485
- def pretty_print(q)
667
+ #
668
+ #: (PP q) -> void
669
+ def pretty_print(q) # :nodoc:
486
670
  q.text("(#{start_line},#{start_column})-(#{end_line},#{end_column})")
487
671
  end
488
672
 
489
673
  # Returns true if the given other location is equal to this location.
674
+ #
675
+ #: (untyped other) -> bool
490
676
  def ==(other)
491
677
  Location === other &&
492
678
  other.start_offset == start_offset &&
@@ -496,6 +682,8 @@ module Prism
496
682
  # Returns a new location that stretches from this location to the given
497
683
  # other location. Raises an error if this location is not before the other
498
684
  # location or if they don't share the same source.
685
+ #
686
+ #: (Location other) -> Location
499
687
  def join(other)
500
688
  raise "Incompatible sources" if source != other.source
501
689
  raise "Incompatible locations" if start_offset > other.start_offset
@@ -506,6 +694,8 @@ module Prism
506
694
  # Join this location with the first occurrence of the string in the source
507
695
  # that occurs after this location on the same line, and return the new
508
696
  # location. This will raise an error if the string does not exist.
697
+ #
698
+ #: (String string) -> Location
509
699
  def adjoin(string)
510
700
  line_suffix = source.slice(end_offset, source.line_end(end_offset) - end_offset)
511
701
 
@@ -519,23 +709,38 @@ module Prism
519
709
  # This represents a comment that was encountered during parsing. It is the
520
710
  # base class for all comment types.
521
711
  class Comment
522
- # The location of this comment in the source.
523
- attr_reader :location
712
+ # The Location of this comment in the source.
713
+ attr_reader :location #: Location
524
714
 
525
715
  # Create a new comment object with the given location.
716
+ #
717
+ #: (Location location) -> void
526
718
  def initialize(location)
527
719
  @location = location
528
720
  end
529
721
 
530
722
  # Implement the hash pattern matching interface for Comment.
531
- def deconstruct_keys(keys)
723
+ #
724
+ #: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
725
+ def deconstruct_keys(keys) # :nodoc:
532
726
  { location: location }
533
727
  end
534
728
 
535
729
  # Returns the content of the comment by slicing it from the source code.
730
+ #
731
+ #: () -> String
536
732
  def slice
537
733
  location.slice
538
734
  end
735
+
736
+ # Returns true if this comment happens on the same line as other code and
737
+ # false if the comment is by itself. This can only be true for inline
738
+ # comments and should be false for block comments.
739
+ #
740
+ #: () -> bool
741
+ def trailing?
742
+ raise NotImplementedError, "trailing? is not implemented for #{self.class}"
743
+ end
539
744
  end
540
745
 
541
746
  # InlineComment objects are the most common. They correspond to comments in
@@ -543,12 +748,18 @@ module Prism
543
748
  class InlineComment < Comment
544
749
  # Returns true if this comment happens on the same line as other code and
545
750
  # false if the comment is by itself.
751
+ #
752
+ #: () -> bool
546
753
  def trailing?
547
754
  !location.start_line_slice.strip.empty?
755
+ rescue ArgumentError # invalid bytes, never whitespace only
756
+ false
548
757
  end
549
758
 
550
759
  # Returns a string representation of this comment.
551
- def inspect
760
+ #
761
+ #: () -> String
762
+ def inspect # :nodoc:
552
763
  "#<Prism::InlineComment @location=#{location.inspect}>"
553
764
  end
554
765
  end
@@ -556,13 +767,17 @@ module Prism
556
767
  # EmbDocComment objects correspond to comments that are surrounded by =begin
557
768
  # and =end.
558
769
  class EmbDocComment < Comment
559
- # This can only be true for inline comments.
770
+ # Returns false. This can only be true for inline comments.
771
+ #
772
+ #: () -> bool
560
773
  def trailing?
561
774
  false
562
775
  end
563
776
 
564
777
  # Returns a string representation of this comment.
565
- def inspect
778
+ #
779
+ #: () -> String
780
+ def inspect # :nodoc:
566
781
  "#<Prism::EmbDocComment @location=#{location.inspect}>"
567
782
  end
568
783
  end
@@ -570,34 +785,44 @@ module Prism
570
785
  # This represents a magic comment that was encountered during parsing.
571
786
  class MagicComment
572
787
  # A Location object representing the location of the key in the source.
573
- attr_reader :key_loc
788
+ attr_reader :key_loc #: Location
574
789
 
575
790
  # A Location object representing the location of the value in the source.
576
- attr_reader :value_loc
791
+ attr_reader :value_loc #: Location
577
792
 
578
793
  # Create a new magic comment object with the given key and value locations.
794
+ #
795
+ #: (Location key_loc, Location value_loc) -> void
579
796
  def initialize(key_loc, value_loc)
580
797
  @key_loc = key_loc
581
798
  @value_loc = value_loc
582
799
  end
583
800
 
584
801
  # Returns the key of the magic comment by slicing it from the source code.
802
+ #
803
+ #: () -> String
585
804
  def key
586
805
  key_loc.slice
587
806
  end
588
807
 
589
808
  # Returns the value of the magic comment by slicing it from the source code.
809
+ #
810
+ #: () -> String
590
811
  def value
591
812
  value_loc.slice
592
813
  end
593
814
 
594
815
  # Implement the hash pattern matching interface for MagicComment.
595
- def deconstruct_keys(keys)
816
+ #
817
+ #: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
818
+ def deconstruct_keys(keys) # :nodoc:
596
819
  { key_loc: key_loc, value_loc: value_loc }
597
820
  end
598
821
 
599
822
  # Returns a string representation of this magic comment.
600
- def inspect
823
+ #
824
+ #: () -> String
825
+ def inspect # :nodoc:
601
826
  "#<Prism::MagicComment @key=#{key.inspect} @value=#{value.inspect}>"
602
827
  end
603
828
  end
@@ -606,18 +831,20 @@ module Prism
606
831
  class ParseError
607
832
  # The type of error. This is an _internal_ symbol that is used for
608
833
  # communicating with translation layers. It is not meant to be public API.
609
- attr_reader :type
834
+ attr_reader :type #: Symbol
610
835
 
611
836
  # The message associated with this error.
612
- attr_reader :message
837
+ attr_reader :message #: String
613
838
 
614
839
  # A Location object representing the location of this error in the source.
615
- attr_reader :location
840
+ attr_reader :location #: Location
616
841
 
617
842
  # The level of this error.
618
- attr_reader :level
843
+ attr_reader :level #: Symbol
619
844
 
620
845
  # Create a new error object with the given message and location.
846
+ #
847
+ #: (Symbol type, String message, Location location, Symbol level) -> void
621
848
  def initialize(type, message, location, level)
622
849
  @type = type
623
850
  @message = message
@@ -626,12 +853,16 @@ module Prism
626
853
  end
627
854
 
628
855
  # Implement the hash pattern matching interface for ParseError.
629
- def deconstruct_keys(keys)
856
+ #
857
+ #: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
858
+ def deconstruct_keys(keys) # :nodoc:
630
859
  { type: type, message: message, location: location, level: level }
631
860
  end
632
861
 
633
862
  # Returns a string representation of this error.
634
- def inspect
863
+ #
864
+ #: () -> String
865
+ def inspect # :nodoc:
635
866
  "#<Prism::ParseError @type=#{@type.inspect} @message=#{@message.inspect} @location=#{@location.inspect} @level=#{@level.inspect}>"
636
867
  end
637
868
  end
@@ -640,18 +871,20 @@ module Prism
640
871
  class ParseWarning
641
872
  # The type of warning. This is an _internal_ symbol that is used for
642
873
  # communicating with translation layers. It is not meant to be public API.
643
- attr_reader :type
874
+ attr_reader :type #: Symbol
644
875
 
645
876
  # The message associated with this warning.
646
- attr_reader :message
877
+ attr_reader :message #: String
647
878
 
648
879
  # A Location object representing the location of this warning in the source.
649
- attr_reader :location
880
+ attr_reader :location #: Location
650
881
 
651
882
  # The level of this warning.
652
- attr_reader :level
883
+ attr_reader :level #: Symbol
653
884
 
654
885
  # Create a new warning object with the given message and location.
886
+ #
887
+ #: (Symbol type, String message, Location location, Symbol level) -> void
655
888
  def initialize(type, message, location, level)
656
889
  @type = type
657
890
  @message = message
@@ -660,73 +893,118 @@ module Prism
660
893
  end
661
894
 
662
895
  # Implement the hash pattern matching interface for ParseWarning.
663
- def deconstruct_keys(keys)
896
+ #
897
+ #: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
898
+ def deconstruct_keys(keys) # :nodoc:
664
899
  { type: type, message: message, location: location, level: level }
665
900
  end
666
901
 
667
902
  # Returns a string representation of this warning.
668
- def inspect
903
+ #
904
+ #: () -> String
905
+ def inspect # :nodoc:
669
906
  "#<Prism::ParseWarning @type=#{@type.inspect} @message=#{@message.inspect} @location=#{@location.inspect} @level=#{@level.inspect}>"
670
907
  end
671
908
  end
672
909
 
673
- # This represents the result of a call to ::parse or ::parse_file. It contains
674
- # the requested structure, any comments that were encounters, and any errors
675
- # that were encountered.
910
+ # This represents the result of a call to Prism.parse or Prism.parse_file.
911
+ # It contains the requested structure, any comments that were encounters,
912
+ # and any errors that were encountered.
676
913
  class Result
677
- # The list of comments that were encountered during parsing.
678
- attr_reader :comments
679
-
680
914
  # The list of magic comments that were encountered during parsing.
681
- attr_reader :magic_comments
915
+ attr_reader :magic_comments #: Array[MagicComment]
682
916
 
683
917
  # An optional location that represents the location of the __END__ marker
684
918
  # and the rest of the content of the file. This content is loaded into the
685
919
  # DATA constant when the file being parsed is the main file being executed.
686
- attr_reader :data_loc
920
+ attr_reader :data_loc #: Location?
687
921
 
688
922
  # The list of errors that were generated during parsing.
689
- attr_reader :errors
923
+ attr_reader :errors #: Array[ParseError]
690
924
 
691
925
  # The list of warnings that were generated during parsing.
692
- attr_reader :warnings
926
+ attr_reader :warnings #: Array[ParseWarning]
693
927
 
694
928
  # A Source instance that represents the source code that was parsed.
695
- attr_reader :source
929
+ attr_reader :source #: Source
696
930
 
697
931
  # Create a new result object with the given values.
698
- def initialize(comments, magic_comments, data_loc, errors, warnings, source)
699
- @comments = comments
932
+ #
933
+ #: (Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void
934
+ def initialize(magic_comments, data_loc, errors, warnings, continuable, source)
700
935
  @magic_comments = magic_comments
701
936
  @data_loc = data_loc
702
937
  @errors = errors
703
938
  @warnings = warnings
939
+ @continuable = continuable
704
940
  @source = source
705
941
  end
706
942
 
943
+ # The list of comments that were encountered during parsing.
944
+ #
945
+ #: () -> Array[Comment]
946
+ def comments
947
+ source.comments
948
+ end
949
+
707
950
  # Implement the hash pattern matching interface for Result.
708
- def deconstruct_keys(keys)
951
+ #
952
+ #: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
953
+ def deconstruct_keys(keys) # :nodoc:
709
954
  { comments: comments, magic_comments: magic_comments, data_loc: data_loc, errors: errors, warnings: warnings }
710
955
  end
711
956
 
712
957
  # Returns the encoding of the source code that was parsed.
958
+ #
959
+ #: () -> Encoding
713
960
  def encoding
714
961
  source.encoding
715
962
  end
716
963
 
717
964
  # Returns true if there were no errors during parsing and false if there
718
965
  # were.
966
+ #
967
+ #: () -> bool
719
968
  def success?
720
969
  errors.empty?
721
970
  end
722
971
 
723
972
  # Returns true if there were errors during parsing and false if there were
724
973
  # not.
974
+ #
975
+ #: () -> bool
725
976
  def failure?
726
977
  !success?
727
978
  end
728
979
 
980
+ # Returns true if the parsed source is an incomplete expression that could
981
+ # become valid with additional input. This is useful for REPL contexts (such
982
+ # as IRB) where the user may be entering a multi-line expression one line at
983
+ # a time and the implementation needs to determine whether to wait for more
984
+ # input or to evaluate what has been entered so far.
985
+ #
986
+ # Concretely, this returns true when every error present is caused by the
987
+ # parser reaching the end of the input before a construct was closed (e.g.
988
+ # an unclosed string, array, block, or keyword), and returns false when any
989
+ # error is caused by a token that makes the input structurally invalid
990
+ # regardless of what might follow (e.g. a stray `end`, `]`, or `)` with no
991
+ # matching opener).
992
+ #
993
+ # Examples:
994
+ #
995
+ # Prism.parse("1 + [").continuable? #=> true (unclosed array)
996
+ # Prism.parse("1 + ]").continuable? #=> false (stray ])
997
+ # Prism.parse("tap do").continuable? #=> true (unclosed block)
998
+ # Prism.parse("end.tap do").continuable? #=> false (stray end)
999
+ #
1000
+ #: () -> bool
1001
+ def continuable?
1002
+ @continuable
1003
+ end
1004
+
729
1005
  # Create a code units cache for the given encoding.
1006
+ #
1007
+ #: (Encoding encoding) -> _CodeUnitsCache
730
1008
  def code_units_cache(encoding)
731
1009
  source.code_units_cache(encoding)
732
1010
  end
@@ -734,41 +1012,42 @@ module Prism
734
1012
 
735
1013
  # This is a result specific to the `parse` and `parse_file` methods.
736
1014
  class ParseResult < Result
737
- autoload :Comments, "prism/parse_result/comments"
738
1015
  autoload :Errors, "prism/parse_result/errors"
739
1016
  autoload :Newlines, "prism/parse_result/newlines"
740
1017
 
741
- private_constant :Comments
742
1018
  private_constant :Errors
743
1019
  private_constant :Newlines
744
1020
 
745
1021
  # The syntax tree that was parsed from the source code.
746
- attr_reader :value
1022
+ attr_reader :value #: ProgramNode
747
1023
 
748
1024
  # Create a new parse result object with the given values.
749
- def initialize(value, comments, magic_comments, data_loc, errors, warnings, source)
1025
+ #
1026
+ #: (ProgramNode value, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void
1027
+ def initialize(value, magic_comments, data_loc, errors, warnings, continuable, source)
750
1028
  @value = value
751
- super(comments, magic_comments, data_loc, errors, warnings, source)
1029
+ super(magic_comments, data_loc, errors, warnings, continuable, source)
752
1030
  end
753
1031
 
754
1032
  # Implement the hash pattern matching interface for ParseResult.
755
- def deconstruct_keys(keys)
1033
+ #
1034
+ #: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
1035
+ def deconstruct_keys(keys) # :nodoc:
756
1036
  super.merge!(value: value)
757
1037
  end
758
1038
 
759
- # Attach the list of comments to their respective locations in the tree.
760
- def attach_comments!
761
- Comments.new(self).attach! # steep:ignore
762
- end
763
-
764
1039
  # Walk the tree and mark nodes that are on a new line, loosely emulating
765
1040
  # the behavior of CRuby's `:line` tracepoint event.
1041
+ #
1042
+ #: () -> void
766
1043
  def mark_newlines!
767
1044
  value.accept(Newlines.new(source.offsets.size)) # steep:ignore
768
1045
  end
769
1046
 
770
1047
  # Returns a string representation of the syntax tree with the errors
771
1048
  # displayed inline.
1049
+ #
1050
+ #: () -> String
772
1051
  def errors_format
773
1052
  Errors.new(self).format
774
1053
  end
@@ -777,16 +1056,20 @@ module Prism
777
1056
  # This is a result specific to the `lex` and `lex_file` methods.
778
1057
  class LexResult < Result
779
1058
  # The list of tokens that were parsed from the source code.
780
- attr_reader :value
1059
+ attr_reader :value #: Array[Token]
781
1060
 
782
1061
  # Create a new lex result object with the given values.
783
- def initialize(value, comments, magic_comments, data_loc, errors, warnings, source)
1062
+ #
1063
+ #: (Array[Token] value, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void
1064
+ def initialize(value, magic_comments, data_loc, errors, warnings, continuable, source)
784
1065
  @value = value
785
- super(comments, magic_comments, data_loc, errors, warnings, source)
1066
+ super(magic_comments, data_loc, errors, warnings, continuable, source)
786
1067
  end
787
1068
 
788
1069
  # Implement the hash pattern matching interface for LexResult.
789
- def deconstruct_keys(keys)
1070
+ #
1071
+ #: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
1072
+ def deconstruct_keys(keys) # :nodoc:
790
1073
  super.merge!(value: value)
791
1074
  end
792
1075
  end
@@ -795,16 +1078,20 @@ module Prism
795
1078
  class ParseLexResult < Result
796
1079
  # A tuple of the syntax tree and the list of tokens that were parsed from
797
1080
  # the source code.
798
- attr_reader :value
1081
+ attr_reader :value #: [ProgramNode, Array[Token]]
799
1082
 
800
1083
  # Create a new parse lex result object with the given values.
801
- def initialize(value, comments, magic_comments, data_loc, errors, warnings, source)
1084
+ #
1085
+ #: ([ProgramNode, Array[Token]] value, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, bool continuable, Source source) -> void
1086
+ def initialize(value, magic_comments, data_loc, errors, warnings, continuable, source)
802
1087
  @value = value
803
- super(comments, magic_comments, data_loc, errors, warnings, source)
1088
+ super(magic_comments, data_loc, errors, warnings, continuable, source)
804
1089
  end
805
1090
 
806
1091
  # Implement the hash pattern matching interface for ParseLexResult.
807
- def deconstruct_keys(keys)
1092
+ #
1093
+ #: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
1094
+ def deconstruct_keys(keys) # :nodoc:
808
1095
  super.merge!(value: value)
809
1096
  end
810
1097
  end
@@ -812,29 +1099,38 @@ module Prism
812
1099
  # This represents a token from the Ruby source.
813
1100
  class Token
814
1101
  # The Source object that represents the source this token came from.
815
- attr_reader :source
1102
+ attr_reader :source #: Source
816
1103
  private :source
817
1104
 
818
1105
  # The type of token that this token is.
819
- attr_reader :type
1106
+ attr_reader :type #: Symbol
820
1107
 
821
1108
  # A byteslice of the source that this token represents.
822
- attr_reader :value
1109
+ attr_reader :value #: String
1110
+
1111
+ # @rbs @location: Location | Integer
823
1112
 
824
1113
  # Create a new token object with the given type, value, and location.
825
- def initialize(source, type, value, location)
1114
+ #
1115
+ #: (Source source, Symbol type, String value, Location | Integer location, Integer state) -> void
1116
+ def initialize(source, type, value, location, state)
826
1117
  @source = source
827
1118
  @type = type
828
1119
  @value = value
829
1120
  @location = location
1121
+ @state = state
830
1122
  end
831
1123
 
832
1124
  # Implement the hash pattern matching interface for Token.
833
- def deconstruct_keys(keys)
1125
+ #
1126
+ #: (Array[Symbol]? keys) -> Hash[Symbol, untyped]
1127
+ def deconstruct_keys(keys) # :nodoc:
834
1128
  { type: type, value: value, location: location }
835
1129
  end
836
1130
 
837
1131
  # A Location object representing the location of this token in the source.
1132
+ #
1133
+ #: () -> Location
838
1134
  def location
839
1135
  location = @location
840
1136
  return location if location.is_a?(Location)
@@ -842,7 +1138,9 @@ module Prism
842
1138
  end
843
1139
 
844
1140
  # Implement the pretty print interface for Token.
845
- def pretty_print(q)
1141
+ #
1142
+ #: (PP q) -> void
1143
+ def pretty_print(q) # :nodoc:
846
1144
  q.group do
847
1145
  q.text(type.to_s)
848
1146
  self.location.pretty_print(q)
@@ -857,6 +1155,8 @@ module Prism
857
1155
  end
858
1156
 
859
1157
  # Returns true if the given other token is equal to this token.
1158
+ #
1159
+ #: (untyped other) -> bool
860
1160
  def ==(other)
861
1161
  Token === other &&
862
1162
  other.type == type &&
@@ -864,17 +1164,42 @@ module Prism
864
1164
  end
865
1165
 
866
1166
  # Returns a string representation of this token.
867
- def inspect
1167
+ #
1168
+ #: () -> String
1169
+ def inspect # :nodoc:
868
1170
  location
869
1171
  super
870
1172
  end
871
1173
 
872
1174
  # Freeze this object and the objects it contains.
1175
+ #
1176
+ #: () -> void
873
1177
  def deep_freeze
874
1178
  value.freeze
875
1179
  location.freeze
876
1180
  freeze
877
1181
  end
1182
+
1183
+ # For internal use only.
1184
+ #: () -> Integer
1185
+ def _ripper_state # :nodoc:
1186
+ @state
1187
+ end
1188
+
1189
+ # Backwards compatibility for Prism.lex/Prism.lex_file/Prism.parse_lex
1190
+ # when they returned a 2-element array.
1191
+
1192
+ #: (Integer index) -> Token | Integer
1193
+ def [](index) # :nodoc:
1194
+ return self if index == 0
1195
+ return @state if index == 1
1196
+ raise ArgumentError, "Invalid index #{index}"
1197
+ end
1198
+
1199
+ #: () -> Token
1200
+ def first # :nodoc:
1201
+ self
1202
+ end
878
1203
  end
879
1204
 
880
1205
  # This object is passed to the various Prism.* methods that accept the
@@ -884,14 +1209,16 @@ module Prism
884
1209
  class Scope
885
1210
  # The list of local variables that are defined in this scope. This should be
886
1211
  # defined as an array of symbols.
887
- attr_reader :locals
1212
+ attr_reader :locals #: Array[Symbol]
888
1213
 
889
1214
  # The list of local variables that are forwarded to the next scope. This
890
1215
  # should by defined as an array of symbols containing the specific values of
891
1216
  # :*, :**, :&, or :"...".
892
- attr_reader :forwarding
1217
+ attr_reader :forwarding #: Array[Symbol]
893
1218
 
894
1219
  # Create a new scope object with the given locals and forwarding.
1220
+ #
1221
+ #: (Array[Symbol] locals, Array[Symbol] forwarding) -> void
895
1222
  def initialize(locals, forwarding)
896
1223
  @locals = locals
897
1224
  @forwarding = forwarding
@@ -901,6 +1228,8 @@ module Prism
901
1228
  # Create a new scope with the given locals and forwarding options that is
902
1229
  # suitable for passing into one of the Prism.* methods that accepts the
903
1230
  # `scopes` option.
1231
+ #
1232
+ #: (?locals: Array[Symbol], ?forwarding: Array[Symbol]) -> Scope
904
1233
  def self.scope(locals: [], forwarding: [])
905
1234
  Scope.new(locals, forwarding)
906
1235
  end