prism 1.9.0 → 2.0.0.rc1

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