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