prism 2.0.0.rc1-x86_64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. checksums.yaml +7 -0
  2. data/BSDmakefile +58 -0
  3. data/CHANGELOG.md +870 -0
  4. data/CODE_OF_CONDUCT.md +76 -0
  5. data/CONTRIBUTING.md +58 -0
  6. data/LICENSE.md +7 -0
  7. data/Makefile +145 -0
  8. data/README.md +81 -0
  9. data/config.yml +4752 -0
  10. data/docs/build_system.md +119 -0
  11. data/docs/configuration.md +69 -0
  12. data/docs/cruby_compilation.md +27 -0
  13. data/docs/design.md +53 -0
  14. data/docs/encoding.md +121 -0
  15. data/docs/fuzzing.md +81 -0
  16. data/docs/heredocs.md +36 -0
  17. data/docs/javascript.md +133 -0
  18. data/docs/local_variable_depth.md +229 -0
  19. data/docs/mapping.md +117 -0
  20. data/docs/parser_translation.md +24 -0
  21. data/docs/parsing_rules.md +22 -0
  22. data/docs/releasing.md +147 -0
  23. data/docs/relocation.md +32 -0
  24. data/docs/ripper_translation.md +63 -0
  25. data/docs/ruby_api.md +45 -0
  26. data/docs/ruby_parser_translation.md +19 -0
  27. data/docs/serialization.md +230 -0
  28. data/docs/testing.md +72 -0
  29. data/ext/prism/api_node.c +7005 -0
  30. data/ext/prism/extconf.rb +126 -0
  31. data/ext/prism/extension.c +1783 -0
  32. data/ext/prism/extension.h +19 -0
  33. data/include/prism/arena.h +37 -0
  34. data/include/prism/ast.h +10371 -0
  35. data/include/prism/buffer.h +52 -0
  36. data/include/prism/comments.h +55 -0
  37. data/include/prism/compiler/accel.h +24 -0
  38. data/include/prism/compiler/align.h +36 -0
  39. data/include/prism/compiler/assume.h +25 -0
  40. data/include/prism/compiler/exported.h +24 -0
  41. data/include/prism/compiler/fallthrough.h +22 -0
  42. data/include/prism/compiler/filesystem.h +32 -0
  43. data/include/prism/compiler/flex_array.h +19 -0
  44. data/include/prism/compiler/force_inline.h +21 -0
  45. data/include/prism/compiler/format.h +25 -0
  46. data/include/prism/compiler/inline.h +17 -0
  47. data/include/prism/compiler/nodiscard.h +22 -0
  48. data/include/prism/compiler/nonnull.h +18 -0
  49. data/include/prism/compiler/unused.h +18 -0
  50. data/include/prism/constant_pool.h +81 -0
  51. data/include/prism/diagnostic.h +93 -0
  52. data/include/prism/errors_format.h +53 -0
  53. data/include/prism/excludes.h +29 -0
  54. data/include/prism/integer.h +41 -0
  55. data/include/prism/internal/allocator.h +68 -0
  56. data/include/prism/internal/allocator_debug.h +88 -0
  57. data/include/prism/internal/arena.h +108 -0
  58. data/include/prism/internal/bit.h +42 -0
  59. data/include/prism/internal/buffer.h +91 -0
  60. data/include/prism/internal/char.h +145 -0
  61. data/include/prism/internal/comments.h +53 -0
  62. data/include/prism/internal/constant_pool.h +152 -0
  63. data/include/prism/internal/diagnostic.h +389 -0
  64. data/include/prism/internal/encoding.h +242 -0
  65. data/include/prism/internal/integer.h +68 -0
  66. data/include/prism/internal/isinf.h +16 -0
  67. data/include/prism/internal/line_offset_list.h +34 -0
  68. data/include/prism/internal/list.h +62 -0
  69. data/include/prism/internal/magic_comments.h +23 -0
  70. data/include/prism/internal/memchr.h +15 -0
  71. data/include/prism/internal/node.h +38 -0
  72. data/include/prism/internal/options.h +232 -0
  73. data/include/prism/internal/parser.h +984 -0
  74. data/include/prism/internal/regexp.h +26 -0
  75. data/include/prism/internal/serialize.h +39 -0
  76. data/include/prism/internal/source.h +72 -0
  77. data/include/prism/internal/static_literals.h +100 -0
  78. data/include/prism/internal/stringy.h +35 -0
  79. data/include/prism/internal/strncasecmp.h +18 -0
  80. data/include/prism/internal/strpbrk.h +33 -0
  81. data/include/prism/internal/tokens.h +11 -0
  82. data/include/prism/json.h +32 -0
  83. data/include/prism/line_offset_list.h +61 -0
  84. data/include/prism/magic_comments.h +35 -0
  85. data/include/prism/node.h +94 -0
  86. data/include/prism/options.h +354 -0
  87. data/include/prism/parser.h +404 -0
  88. data/include/prism/prettyprint.h +31 -0
  89. data/include/prism/serialize.h +115 -0
  90. data/include/prism/source.h +155 -0
  91. data/include/prism/stream.h +28 -0
  92. data/include/prism/string_query.h +63 -0
  93. data/include/prism/stringy.h +72 -0
  94. data/include/prism/version.h +38 -0
  95. data/include/prism.h +131 -0
  96. data/lib/prism/3.3/prism.so +0 -0
  97. data/lib/prism/3.4/prism.so +0 -0
  98. data/lib/prism/4.0/prism.so +0 -0
  99. data/lib/prism/compiler.rb +815 -0
  100. data/lib/prism/desugar_compiler.rb +463 -0
  101. data/lib/prism/dispatcher.rb +2383 -0
  102. data/lib/prism/dot_visitor.rb +4848 -0
  103. data/lib/prism/dsl.rb +1369 -0
  104. data/lib/prism/ffi.rb +740 -0
  105. data/lib/prism/inspect_visitor.rb +2425 -0
  106. data/lib/prism/lex_compat.rb +953 -0
  107. data/lib/prism/mutation_compiler.rb +780 -0
  108. data/lib/prism/node.rb +33204 -0
  109. data/lib/prism/node_ext.rb +504 -0
  110. data/lib/prism/node_find.rb +215 -0
  111. data/lib/prism/parse_result/errors.rb +72 -0
  112. data/lib/prism/parse_result/newlines.rb +423 -0
  113. data/lib/prism/parse_result.rb +1221 -0
  114. data/lib/prism/pattern.rb +314 -0
  115. data/lib/prism/polyfill/append_as_bytes.rb +15 -0
  116. data/lib/prism/polyfill/byteindex.rb +13 -0
  117. data/lib/prism/polyfill/scan_byte.rb +14 -0
  118. data/lib/prism/polyfill/unpack1.rb +14 -0
  119. data/lib/prism/polyfill/warn.rb +36 -0
  120. data/lib/prism/reflection.rb +427 -0
  121. data/lib/prism/relocation.rb +567 -0
  122. data/lib/prism/serialize.rb +5308 -0
  123. data/lib/prism/string_query.rb +46 -0
  124. data/lib/prism/translation/parser/builder.rb +70 -0
  125. data/lib/prism/translation/parser/compiler.rb +2226 -0
  126. data/lib/prism/translation/parser/lexer.rb +769 -0
  127. data/lib/prism/translation/parser.rb +376 -0
  128. data/lib/prism/translation/parser_current.rb +26 -0
  129. data/lib/prism/translation/parser_versions.rb +36 -0
  130. data/lib/prism/translation/ripper/filter.rb +53 -0
  131. data/lib/prism/translation/ripper/lexer.rb +133 -0
  132. data/lib/prism/translation/ripper/sexp.rb +118 -0
  133. data/lib/prism/translation/ripper/shim.rb +7 -0
  134. data/lib/prism/translation/ripper.rb +4278 -0
  135. data/lib/prism/translation/ruby_parser.rb +1721 -0
  136. data/lib/prism/translation.rb +20 -0
  137. data/lib/prism/visitor.rb +1440 -0
  138. data/lib/prism.rb +168 -0
  139. data/prism.gemspec +234 -0
  140. data/rbi/generated/prism/compiler.rbi +489 -0
  141. data/rbi/generated/prism/desugar_compiler.rbi +306 -0
  142. data/rbi/generated/prism/dispatcher.rbi +1138 -0
  143. data/rbi/generated/prism/dot_visitor.rbi +618 -0
  144. data/rbi/generated/prism/dsl.rbi +751 -0
  145. data/rbi/generated/prism/inspect_visitor.rbi +503 -0
  146. data/rbi/generated/prism/lex_compat.rbi +162 -0
  147. data/rbi/generated/prism/mutation_compiler.rbi +464 -0
  148. data/rbi/generated/prism/node.rbi +13681 -0
  149. data/rbi/generated/prism/node_ext.rbi +259 -0
  150. data/rbi/generated/prism/node_find.rbi +79 -0
  151. data/rbi/generated/prism/parse_result/errors.rbi +21 -0
  152. data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
  153. data/rbi/generated/prism/parse_result.rbi +764 -0
  154. data/rbi/generated/prism/pattern.rbi +134 -0
  155. data/rbi/generated/prism/reflection.rbi +89 -0
  156. data/rbi/generated/prism/relocation.rbi +329 -0
  157. data/rbi/generated/prism/serialize.rbi +181 -0
  158. data/rbi/generated/prism/string_query.rbi +36 -0
  159. data/rbi/generated/prism/translation.rbi +7 -0
  160. data/rbi/generated/prism/visitor.rbi +650 -0
  161. data/rbi/generated/prism.rbi +86 -0
  162. data/rbi/prism/translation/parser.rbi +11 -0
  163. data/rbi/prism/translation/parser_versions.rbi +28 -0
  164. data/rbi/prism/translation/ripper.rbi +15 -0
  165. data/rbi/rubyvm/node_find.rbi +14 -0
  166. data/sig/generated/prism/compiler.rbs +492 -0
  167. data/sig/generated/prism/desugar_compiler.rbs +305 -0
  168. data/sig/generated/prism/dispatcher.rbs +1292 -0
  169. data/sig/generated/prism/dot_visitor.rbs +635 -0
  170. data/sig/generated/prism/dsl.rbs +928 -0
  171. data/sig/generated/prism/inspect_visitor.rbs +509 -0
  172. data/sig/generated/prism/lex_compat.rbs +166 -0
  173. data/sig/generated/prism/mutation_compiler.rbs +464 -0
  174. data/sig/generated/prism/node.rbs +20160 -0
  175. data/sig/generated/prism/node_ext.rbs +290 -0
  176. data/sig/generated/prism/node_find.rbs +90 -0
  177. data/sig/generated/prism/parse_result/errors.rbs +22 -0
  178. data/sig/generated/prism/parse_result/newlines.rbs +223 -0
  179. data/sig/generated/prism/parse_result.rbs +859 -0
  180. data/sig/generated/prism/pattern.rbs +156 -0
  181. data/sig/generated/prism/reflection.rbs +101 -0
  182. data/sig/generated/prism/relocation.rbs +428 -0
  183. data/sig/generated/prism/serialize.rbs +195 -0
  184. data/sig/generated/prism/string_query.rbs +36 -0
  185. data/sig/generated/prism/translation.rbs +8 -0
  186. data/sig/generated/prism/visitor.rbs +1111 -0
  187. data/sig/generated/prism.rbs +84 -0
  188. data/sig/manifest.yaml +2 -0
  189. data/src/arena.c +117 -0
  190. data/src/buffer.c +380 -0
  191. data/src/char.c +289 -0
  192. data/src/comments.c +454 -0
  193. data/src/constant_pool.c +406 -0
  194. data/src/diagnostic.c +886 -0
  195. data/src/encoding.c +5345 -0
  196. data/src/errors_format.c +519 -0
  197. data/src/integer.c +681 -0
  198. data/src/json.c +5733 -0
  199. data/src/line_offset_list.c +100 -0
  200. data/src/list.c +24 -0
  201. data/src/memchr.c +37 -0
  202. data/src/node.c +4515 -0
  203. data/src/options.c +451 -0
  204. data/src/parser.c +349 -0
  205. data/src/prettyprint.c +9024 -0
  206. data/src/prism.c +23739 -0
  207. data/src/regexp.c +1706 -0
  208. data/src/serialize.c +2309 -0
  209. data/src/source.c +518 -0
  210. data/src/static_literals.c +685 -0
  211. data/src/string_query.c +166 -0
  212. data/src/stringy.c +91 -0
  213. data/src/strncasecmp.c +37 -0
  214. data/src/strpbrk.c +447 -0
  215. data/src/tokens.c +724 -0
  216. metadata +259 -0
@@ -0,0 +1,567 @@
1
+ # frozen_string_literal: true
2
+ # :markup: markdown
3
+ #--
4
+ # rbs_inline: enabled
5
+
6
+ module Prism
7
+ # Prism parses deterministically for the same input. This provides a nice
8
+ # property that is exposed through the #node_id API on nodes. Effectively this
9
+ # means that for the same input, these values will remain consistent every
10
+ # time the source is parsed. This means we can reparse the source same with a
11
+ # #node_id value and find the exact same node again.
12
+ #
13
+ # The Relocation module provides an API around this property. It allows you to
14
+ # "save" nodes and locations using a minimal amount of memory (just the
15
+ # node_id and a field identifier) and then reify them later.
16
+ module Relocation
17
+ # @rbs!
18
+ # type entry_value = untyped
19
+ # type entry_values = Hash[Symbol, entry_value]
20
+ #
21
+ # interface _Value
22
+ # def start_line: () -> Integer
23
+ # def end_line: () -> Integer
24
+ # def start_offset: () -> Integer
25
+ # def end_offset: () -> Integer
26
+ # def start_character_offset: () -> Integer
27
+ # def end_character_offset: () -> Integer
28
+ # def cached_start_code_units_offset: (_CodeUnitsCache cache) -> Integer
29
+ # def cached_end_code_units_offset: (_CodeUnitsCache cache) -> Integer
30
+ # def start_column: () -> Integer
31
+ # def end_column: () -> Integer
32
+ # def start_character_column: () -> Integer
33
+ # def end_character_column: () -> Integer
34
+ # def cached_start_code_units_column: (_CodeUnitsCache cache) -> Integer
35
+ # def cached_end_code_units_column: (_CodeUnitsCache cache) -> Integer
36
+ # end
37
+ #
38
+ # interface _Field
39
+ # def fields: (_Value value) -> entry_values
40
+ # end
41
+
42
+ # An entry in a repository that will lazily reify its values when they are
43
+ # first accessed.
44
+ class Entry
45
+ # Raised if a value that could potentially be on an entry is missing
46
+ # because it was either not configured on the repository or it has not yet
47
+ # been fetched.
48
+ class MissingValueError < StandardError
49
+ end
50
+
51
+ # @rbs @repository: Repository?
52
+ # @rbs @values: Hash[Symbol, untyped]?
53
+
54
+ # Initialize a new entry with the given repository.
55
+ #
56
+ #: (Repository repository) -> void
57
+ def initialize(repository)
58
+ @repository = repository
59
+ @values = nil
60
+ end
61
+
62
+ # Fetch the filepath of the value.
63
+ #
64
+ #: () -> String
65
+ def filepath
66
+ fetch_value(:filepath)
67
+ end
68
+
69
+ # Fetch the start line of the value.
70
+ #
71
+ #: () -> Integer
72
+ def start_line
73
+ fetch_value(:start_line)
74
+ end
75
+
76
+ # Fetch the end line of the value.
77
+ #
78
+ #: () -> Integer
79
+ def end_line
80
+ fetch_value(:end_line)
81
+ end
82
+
83
+ # Fetch the start byte offset of the value.
84
+ #
85
+ #: () -> Integer
86
+ def start_offset
87
+ fetch_value(:start_offset)
88
+ end
89
+
90
+ # Fetch the end byte offset of the value.
91
+ #
92
+ #: () -> Integer
93
+ def end_offset
94
+ fetch_value(:end_offset)
95
+ end
96
+
97
+ # Fetch the start character offset of the value.
98
+ #
99
+ #: () -> Integer
100
+ def start_character_offset
101
+ fetch_value(:start_character_offset)
102
+ end
103
+
104
+ # Fetch the end character offset of the value.
105
+ #
106
+ #: () -> Integer
107
+ def end_character_offset
108
+ fetch_value(:end_character_offset)
109
+ end
110
+
111
+ # Fetch the start code units offset of the value, for the encoding that
112
+ # was configured on the repository.
113
+ #
114
+ #: () -> Integer
115
+ def start_code_units_offset
116
+ fetch_value(:start_code_units_offset)
117
+ end
118
+
119
+ # Fetch the end code units offset of the value, for the encoding that was
120
+ # configured on the repository.
121
+ #
122
+ #: () -> Integer
123
+ def end_code_units_offset
124
+ fetch_value(:end_code_units_offset)
125
+ end
126
+
127
+ # Fetch the start byte column of the value.
128
+ #
129
+ #: () -> Integer
130
+ def start_column
131
+ fetch_value(:start_column)
132
+ end
133
+
134
+ # Fetch the end byte column of the value.
135
+ #
136
+ #: () -> Integer
137
+ def end_column
138
+ fetch_value(:end_column)
139
+ end
140
+
141
+ # Fetch the start character column of the value.
142
+ #
143
+ #: () -> Integer
144
+ def start_character_column
145
+ fetch_value(:start_character_column)
146
+ end
147
+
148
+ # Fetch the end character column of the value.
149
+ #
150
+ #: () -> Integer
151
+ def end_character_column
152
+ fetch_value(:end_character_column)
153
+ end
154
+
155
+ # Fetch the start code units column of the value, for the encoding that
156
+ # was configured on the repository.
157
+ #
158
+ #: () -> Integer
159
+ def start_code_units_column
160
+ fetch_value(:start_code_units_column)
161
+ end
162
+
163
+ # Fetch the end code units column of the value, for the encoding that was
164
+ # configured on the repository.
165
+ #
166
+ #: () -> Integer
167
+ def end_code_units_column
168
+ fetch_value(:end_code_units_column)
169
+ end
170
+
171
+ # Reify the values on this entry with the given values. This is an
172
+ # internal-only API that is called from the repository when it is time to
173
+ # reify the values.
174
+ #
175
+ #: (entry_values values) -> void
176
+ def reify!(values) # :nodoc:
177
+ @repository = nil
178
+ @values = values
179
+ end
180
+
181
+ private
182
+
183
+ # Fetch a value from the entry, raising an error if it is missing.
184
+ #
185
+ #: (Symbol name) -> entry_value
186
+ def fetch_value(name)
187
+ values.fetch(name) do
188
+ raise MissingValueError, "No value for #{name}, make sure the " \
189
+ "repository has been properly configured"
190
+ end
191
+ end
192
+
193
+ # Return the values from the repository, reifying them if necessary.
194
+ #
195
+ #: () -> entry_values
196
+ def values
197
+ @values || (@repository&.reify!; @values) #: entry_values
198
+ end
199
+ end
200
+
201
+ # Represents the source of a repository that will be reparsed.
202
+ class Source
203
+ # The value that will need to be reparsed.
204
+ attr_reader :value #: untyped
205
+
206
+ # Initialize the source with the given value.
207
+ #
208
+ #: (untyped value) -> void
209
+ def initialize(value)
210
+ @value = value
211
+ end
212
+
213
+ # Reparse the value and return the parse result.
214
+ #
215
+ #: () -> ParseResult
216
+ def result
217
+ raise NotImplementedError, "Subclasses must implement #result"
218
+ end
219
+
220
+ # Create a code units cache for the given encoding.
221
+ #
222
+ #: (Encoding encoding) -> _CodeUnitsCache
223
+ def code_units_cache(encoding)
224
+ result.code_units_cache(encoding)
225
+ end
226
+ end
227
+
228
+ # A source that is represented by a file path.
229
+ class SourceFilepath < Source
230
+ # Reparse the file and return the parse result.
231
+ #
232
+ #: () -> ParseResult
233
+ def result
234
+ Prism.parse_file(value)
235
+ end
236
+ end
237
+
238
+ # A source that is represented by a string.
239
+ class SourceString < Source
240
+ # Reparse the string and return the parse result.
241
+ #
242
+ #: () -> ParseResult
243
+ def result
244
+ Prism.parse(value)
245
+ end
246
+ end
247
+
248
+ # A field that represents the file path.
249
+ class FilepathField
250
+ # The file path that this field represents.
251
+ attr_reader :value #: String
252
+
253
+ # Initialize a new field with the given file path.
254
+ #
255
+ #: (String value) -> void
256
+ def initialize(value)
257
+ @value = value
258
+ end
259
+
260
+ # Fetch the file path.
261
+ #
262
+ #: (_Value _value) -> entry_values
263
+ def fields(_value)
264
+ { filepath: value }
265
+ end
266
+ end
267
+
268
+ # A field representing the start and end lines.
269
+ class LinesField
270
+ # Fetches the start and end line of a value.
271
+ #
272
+ #: (_Value value) -> entry_values
273
+ def fields(value)
274
+ { start_line: value.start_line, end_line: value.end_line }
275
+ end
276
+ end
277
+
278
+ # A field representing the start and end byte offsets.
279
+ class OffsetsField
280
+ # Fetches the start and end byte offset of a value.
281
+ #
282
+ #: (_Value value) -> entry_values
283
+ def fields(value)
284
+ { start_offset: value.start_offset, end_offset: value.end_offset }
285
+ end
286
+ end
287
+
288
+ # A field representing the start and end character offsets.
289
+ class CharacterOffsetsField
290
+ # Fetches the start and end character offset of a value.
291
+ #
292
+ #: (_Value value) -> entry_values
293
+ def fields(value)
294
+ {
295
+ start_character_offset: value.start_character_offset,
296
+ end_character_offset: value.end_character_offset
297
+ }
298
+ end
299
+ end
300
+
301
+ # A field representing the start and end code unit offsets.
302
+ class CodeUnitOffsetsField
303
+ # A pointer to the repository object that is used for lazily creating a
304
+ # code units cache.
305
+ attr_reader :repository #: Repository
306
+
307
+ # The associated encoding for the code units.
308
+ attr_reader :encoding #: Encoding
309
+
310
+ # @rbs @cache: _CodeUnitsCache?
311
+
312
+ # Initialize a new field with the associated repository and encoding.
313
+ #
314
+ #: (Repository repository, Encoding encoding) -> void
315
+ def initialize(repository, encoding)
316
+ @repository = repository
317
+ @encoding = encoding
318
+ @cache = nil
319
+ end
320
+
321
+ # Fetches the start and end code units offset of a value for a particular
322
+ # encoding.
323
+ #
324
+ #: (_Value value) -> entry_values
325
+ def fields(value)
326
+ {
327
+ start_code_units_offset: value.cached_start_code_units_offset(cache),
328
+ end_code_units_offset: value.cached_end_code_units_offset(cache)
329
+ }
330
+ end
331
+
332
+ private
333
+
334
+ # Lazily create a code units cache for the associated encoding.
335
+ #
336
+ #: () -> _CodeUnitsCache
337
+ def cache
338
+ @cache ||= repository.code_units_cache(encoding)
339
+ end
340
+ end
341
+
342
+ # A field representing the start and end byte columns.
343
+ class ColumnsField
344
+ # Fetches the start and end byte column of a value.
345
+ #
346
+ #: (_Value value) -> entry_values
347
+ def fields(value)
348
+ { start_column: value.start_column, end_column: value.end_column }
349
+ end
350
+ end
351
+
352
+ # A field representing the start and end character columns.
353
+ class CharacterColumnsField
354
+ # Fetches the start and end character column of a value.
355
+ #
356
+ #: (_Value value) -> entry_values
357
+ def fields(value)
358
+ {
359
+ start_character_column: value.start_character_column,
360
+ end_character_column: value.end_character_column
361
+ }
362
+ end
363
+ end
364
+
365
+ # A field representing the start and end code unit columns for a specific
366
+ # encoding.
367
+ class CodeUnitColumnsField
368
+ # The repository object that is used for lazily creating a code units
369
+ # cache.
370
+ attr_reader :repository #: Repository
371
+
372
+ # The associated encoding for the code units.
373
+ attr_reader :encoding #: Encoding
374
+
375
+ # @rbs @cache: _CodeUnitsCache?
376
+
377
+ # Initialize a new field with the associated repository and encoding.
378
+ #
379
+ #: (Repository repository, Encoding encoding) -> void
380
+ def initialize(repository, encoding)
381
+ @repository = repository
382
+ @encoding = encoding
383
+ @cache = nil
384
+ end
385
+
386
+ # Fetches the start and end code units column of a value for a particular
387
+ # encoding.
388
+ #
389
+ #: (_Value value) -> entry_values
390
+ def fields(value)
391
+ {
392
+ start_code_units_column: value.cached_start_code_units_column(cache),
393
+ end_code_units_column: value.cached_end_code_units_column(cache)
394
+ }
395
+ end
396
+
397
+ private
398
+
399
+ # Lazily create a code units cache for the associated encoding.
400
+ #
401
+ #: () -> _CodeUnitsCache
402
+ def cache
403
+ @cache ||= repository.code_units_cache(encoding)
404
+ end
405
+ end
406
+
407
+ # A repository is a configured collection of fields and a set of entries
408
+ # that knows how to reparse a source and reify the values.
409
+ class Repository
410
+ # Raised when multiple fields of the same type are configured on the same
411
+ # repository.
412
+ class ConfigurationError < StandardError
413
+ end
414
+
415
+ # The source associated with this repository. This will be either a
416
+ # SourceFilepath (the most common use case) or a SourceString.
417
+ attr_reader :source #: Source
418
+
419
+ # The fields that have been configured on this repository.
420
+ attr_reader :fields #: Hash[Symbol, _Field]
421
+
422
+ # The entries that have been saved on this repository.
423
+ attr_reader :entries #: Hash[Integer, Hash[Symbol, Entry]]
424
+
425
+ # Initialize a new repository with the given source.
426
+ #
427
+ #: (Source source) -> void
428
+ def initialize(source)
429
+ @source = source
430
+ @fields = {}
431
+ @entries = Hash.new { |hash, node_id| hash[node_id] = {} }
432
+ end
433
+
434
+ # Create a code units cache for the given encoding from the source.
435
+ #
436
+ #: (Encoding encoding) -> _CodeUnitsCache
437
+ def code_units_cache(encoding)
438
+ source.code_units_cache(encoding)
439
+ end
440
+
441
+ # Configure the filepath field for this repository and return self.
442
+ #
443
+ #: () -> self
444
+ def filepath
445
+ raise ConfigurationError, "Can only specify filepath for a filepath source" unless source.is_a?(SourceFilepath)
446
+ field(:filepath, FilepathField.new(source.value))
447
+ end
448
+
449
+ # Configure the lines field for this repository and return self.
450
+ #
451
+ #: () -> self
452
+ def lines
453
+ field(:lines, LinesField.new)
454
+ end
455
+
456
+ # Configure the offsets field for this repository and return self.
457
+ #
458
+ #: () -> self
459
+ def offsets
460
+ field(:offsets, OffsetsField.new)
461
+ end
462
+
463
+ # Configure the character offsets field for this repository and return
464
+ # self.
465
+ #
466
+ #: () -> self
467
+ def character_offsets
468
+ field(:character_offsets, CharacterOffsetsField.new)
469
+ end
470
+
471
+ # Configure the code unit offsets field for this repository for a specific
472
+ # encoding and return self.
473
+ #
474
+ #: (Encoding encoding) -> self
475
+ def code_unit_offsets(encoding)
476
+ field(:code_unit_offsets, CodeUnitOffsetsField.new(self, encoding))
477
+ end
478
+
479
+ # Configure the columns field for this repository and return self.
480
+ #
481
+ #: () -> self
482
+ def columns
483
+ field(:columns, ColumnsField.new)
484
+ end
485
+
486
+ # Configure the character columns field for this repository and return
487
+ # self.
488
+ #
489
+ #: () -> self
490
+ def character_columns
491
+ field(:character_columns, CharacterColumnsField.new)
492
+ end
493
+
494
+ # Configure the code unit columns field for this repository for a specific
495
+ # encoding and return self.
496
+ #
497
+ #: (Encoding encoding) -> self
498
+ def code_unit_columns(encoding)
499
+ field(:code_unit_columns, CodeUnitColumnsField.new(self, encoding))
500
+ end
501
+
502
+ # This method is called from nodes and locations when they want to enter
503
+ # themselves into the repository. It it internal-only and meant to be
504
+ # called from the #save* APIs.
505
+ #
506
+ #: (Integer node_id, Symbol field_name) -> Entry
507
+ def enter(node_id, field_name) # :nodoc:
508
+ entry = Entry.new(self)
509
+ @entries[node_id][field_name] = entry
510
+ entry
511
+ end
512
+
513
+ # This method is called from the entries in the repository when they need
514
+ # to reify their values. It is internal-only and meant to be called from
515
+ # the various value APIs.
516
+ #
517
+ #: () -> void
518
+ def reify! # :nodoc:
519
+ result = source.result
520
+
521
+ queue = [result.value] #: Array[Prism::node]
522
+ while (node = queue.shift)
523
+ @entries[node.node_id].each do |field_name, entry|
524
+ value = node.public_send(field_name)
525
+ values = {} #: entry_values
526
+
527
+ fields.each_value do |field|
528
+ values.merge!(field.fields(value))
529
+ end
530
+
531
+ entry.reify!(values)
532
+ end
533
+
534
+ queue.concat(node.compact_child_nodes)
535
+ end
536
+
537
+ @entries.clear
538
+ end
539
+
540
+ private
541
+
542
+ # Append the given field to the repository and return the repository so
543
+ # that these calls can be chained.
544
+ #
545
+ #: (Symbol name, _Field) -> self
546
+ def field(name, value)
547
+ raise ConfigurationError, "Cannot specify multiple #{name} fields" if @fields.key?(name)
548
+ @fields[name] = value
549
+ self
550
+ end
551
+ end
552
+
553
+ # Create a new repository for the given filepath.
554
+ #
555
+ #: (String value) -> Repository
556
+ def self.filepath(value)
557
+ Repository.new(SourceFilepath.new(value))
558
+ end
559
+
560
+ # Create a new repository for the given string.
561
+ #
562
+ #: (String value) -> Repository
563
+ def self.string(value)
564
+ Repository.new(SourceString.new(value))
565
+ end
566
+ end
567
+ end