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
data/src/comments.c ADDED
@@ -0,0 +1,454 @@
1
+ #include "prism/internal/comments.h"
2
+
3
+ #include "prism/internal/arena.h"
4
+ #include "prism/internal/char.h"
5
+ #include "prism/internal/node.h"
6
+ #include "prism/internal/parser.h"
7
+
8
+ #include <assert.h>
9
+ #include <stdlib.h>
10
+ #include <string.h>
11
+
12
+ /* The state used while walking the tree to find the owner of each comment. */
13
+ typedef struct {
14
+ /* The arena for memory that is only needed during the walk. */
15
+ pm_arena_t *arena;
16
+
17
+ /* The start of the source that was parsed. */
18
+ const uint8_t *source;
19
+
20
+ /* The comments found while parsing, sorted by start offset. */
21
+ const pm_comment_t **comments;
22
+
23
+ /* The number of comments. */
24
+ uint32_t size;
25
+
26
+ /* The start offset of each comment. */
27
+ uint32_t *starts;
28
+
29
+ /*
30
+ * The result of the most recent search for the first comment after the end
31
+ * of a node. This is a hint to the search to avoid fully binary searching
32
+ * the comments list if possible.
33
+ */
34
+ uint32_t cursor;
35
+
36
+ /* The node each comment is attached to. */
37
+ const pm_node_t **attached;
38
+
39
+ /*
40
+ * A disjoint set over comment indices that maps each index to the next
41
+ * index at or after it that has not yet been claimed. It has one extra
42
+ * entry at the end that is never claimed, so that every lookup ends. This
43
+ * allows quickly finding the next unclaimed comment without having to check
44
+ * the state of each comment individually.
45
+ */
46
+ uint32_t *unclaimed;
47
+
48
+ /*
49
+ * A stack of nodes whose children are being visited. Each level of the
50
+ * walk pushes its children, visits them, then pops back to where it
51
+ * started.
52
+ */
53
+ struct {
54
+ const pm_node_t **nodes;
55
+ size_t size;
56
+ size_t capacity;
57
+ } stack;
58
+ } pm_comments_walk_t;
59
+
60
+ /* Compare two comments by start offset for qsort. */
61
+ static int
62
+ pm_comments_compare(const void *left, const void *right) {
63
+ uint32_t left_start = (*(const pm_comment_t *const *) left)->location.start;
64
+ uint32_t right_start = (*(const pm_comment_t *const *) right)->location.start;
65
+ return (left_start > right_start) - (left_start < right_start);
66
+ }
67
+
68
+ /*
69
+ * Copy the linked list of comments from the parser sorted by start offset, and
70
+ * relink the list in that order. Usually comments arrive in source order, but
71
+ * unfortunately because of heredocs they can occasionally be out of order.
72
+ */
73
+ static const pm_comment_t **
74
+ pm_comments_sort(pm_parser_t *parser, uint32_t size) {
75
+ pm_comment_t **comments = (pm_comment_t **) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_comment_t *) * size, PRISM_ALIGNOF(pm_comment_t *));
76
+
77
+ uint32_t index = 0;
78
+ for (pm_list_node_t *node = parser->comment_list.head; node != NULL; node = node->next) {
79
+ comments[index++] = (pm_comment_t *) node;
80
+ }
81
+
82
+ qsort((void *) comments, size, sizeof(pm_comment_t *), pm_comments_compare);
83
+
84
+ for (index = 0; index + 1 < size; index++) {
85
+ comments[index]->node.next = &comments[index + 1]->node;
86
+ }
87
+
88
+ comments[size - 1]->node.next = NULL;
89
+ parser->comment_list.head = &comments[0]->node;
90
+ parser->comment_list.tail = &comments[size - 1]->node;
91
+
92
+ return (const pm_comment_t **) comments;
93
+ }
94
+
95
+ /*
96
+ * Return the index of the first comment in [left, right) that starts at or
97
+ * after the given offset, or right if there is none.
98
+ */
99
+ static uint32_t
100
+ pm_comments_lower_bound(const pm_comments_walk_t *walk, uint32_t left, uint32_t right, uint32_t offset) {
101
+ while (left < right) {
102
+ uint32_t middle = left + (right - left) / 2;
103
+
104
+ if (walk->starts[middle] < offset) {
105
+ left = middle + 1;
106
+ } else {
107
+ right = middle;
108
+ }
109
+ }
110
+
111
+ return left;
112
+ }
113
+
114
+ /*
115
+ * Return the index of the first comment that starts at or after the given
116
+ * offset, or the number of comments if there is none. This gallops outward
117
+ * from the previous result to find a range to search, so that a run of nearby
118
+ * offsets costs a constant number of comparisons each.
119
+ */
120
+ static uint32_t
121
+ pm_comments_seek(pm_comments_walk_t *walk, uint32_t offset) {
122
+ const uint32_t *starts = walk->starts;
123
+
124
+ uint32_t size = walk->size;
125
+ uint32_t cursor = walk->cursor;
126
+ uint32_t left;
127
+ uint32_t right;
128
+ uint32_t step = 1;
129
+
130
+ if (cursor == size || starts[cursor] >= offset) {
131
+ /*
132
+ * The answer is at or before the cursor. Widen the range backward
133
+ * until the comment before it starts before the offset.
134
+ */
135
+ right = cursor;
136
+ left = cursor;
137
+
138
+ while (left > 0 && starts[left - 1] >= offset) {
139
+ right = left - 1;
140
+ left = (left - 1 > step) ? left - 1 - step : 0;
141
+ step *= 2;
142
+ }
143
+ } else {
144
+ /*
145
+ * The answer is after the cursor. Widen the range forward until it
146
+ * ends at a comment that starts at or after the offset.
147
+ */
148
+ left = cursor + 1;
149
+ right = left;
150
+
151
+ while (right < size && starts[right] < offset) {
152
+ left = right + 1;
153
+ right = (size - left > step) ? left + step : size;
154
+ step *= 2;
155
+ }
156
+ }
157
+
158
+ walk->cursor = pm_comments_lower_bound(walk, left, right, offset);
159
+ return walk->cursor;
160
+ }
161
+
162
+ /*
163
+ * The first comment index at or after the given index that has not yet been
164
+ * claimed, compressing the path along the way.
165
+ */
166
+ static uint32_t
167
+ pm_comments_unclaimed(pm_comments_walk_t *walk, uint32_t index) {
168
+ uint32_t *unclaimed = walk->unclaimed;
169
+
170
+ while (unclaimed[index] != index) {
171
+ unclaimed[index] = unclaimed[unclaimed[index]];
172
+ index = unclaimed[index];
173
+ }
174
+
175
+ return index;
176
+ }
177
+
178
+ /*
179
+ * Return the earliest offset before the given one from which the source up to
180
+ * it holds only whitespace and comments. Any comment that starts at or after
181
+ * the returned offset is separated from the given offset by only whitespace and
182
+ * other comments.
183
+ */
184
+ static uint32_t
185
+ pm_comments_blank_before(const pm_comments_walk_t *walk, uint32_t offset) {
186
+ uint32_t index = pm_comments_lower_bound(walk, 0, walk->size, offset);
187
+
188
+ while (true) {
189
+ offset -= (uint32_t) pm_strrspn_whitespace(walk->source, (ptrdiff_t) offset);
190
+ if (index == 0) return offset;
191
+
192
+ /* An embedded document's location includes its trailing newline, so
193
+ * the whitespace that was just skipped can end inside a comment. */
194
+ const pm_location_t *location = &walk->comments[index - 1]->location;
195
+ if (location->start >= offset || PM_LOCATION_END(location) < offset) return offset;
196
+
197
+ offset = location->start;
198
+ index--;
199
+ }
200
+ }
201
+
202
+ /*
203
+ * Return the latest offset after the given one, and no later than the given
204
+ * end, up to which the source from the given offset holds only whitespace and
205
+ * comments. Any comment that ends at or before the returned offset is
206
+ * separated from the given offset by only whitespace and other comments.
207
+ */
208
+ static uint32_t
209
+ pm_comments_blank_after(const pm_comments_walk_t *walk, uint32_t offset, uint32_t end) {
210
+ uint32_t index = pm_comments_lower_bound(walk, 0, walk->size, offset);
211
+
212
+ while (true) {
213
+ offset += (uint32_t) pm_strspn_whitespace(walk->source + offset, (ptrdiff_t) end - (ptrdiff_t) offset);
214
+ if (index == walk->size || walk->starts[index] != offset) return offset;
215
+
216
+ offset = PM_LOCATION_END(&walk->comments[index]->location);
217
+ index++;
218
+ }
219
+ }
220
+
221
+ /*
222
+ * Push a child node onto the walk stack without descending into it. The walk
223
+ * descends itself, so that it can visit children in source order and handle
224
+ * each node after all of its descendants.
225
+ */
226
+ static bool
227
+ pm_comments_push(const pm_node_t *node, void *data) {
228
+ pm_comments_walk_t *walk = (pm_comments_walk_t *) data;
229
+
230
+ if (walk->stack.size == walk->stack.capacity) {
231
+ size_t capacity = walk->stack.capacity == 0 ? 64 : walk->stack.capacity * 2;
232
+ const pm_node_t **nodes = (const pm_node_t **) pm_arena_alloc(walk->arena, sizeof(pm_node_t *) * capacity, PRISM_ALIGNOF(pm_node_t *));
233
+ if (walk->stack.size > 0) memcpy((void *) nodes, (const void *) walk->stack.nodes, sizeof(pm_node_t *) * walk->stack.size);
234
+
235
+ walk->stack.nodes = nodes;
236
+ walk->stack.capacity = capacity;
237
+ }
238
+
239
+ walk->stack.nodes[walk->stack.size++] = node;
240
+ return false;
241
+ }
242
+
243
+ /*
244
+ * Visit the given node and its descendants in post-order, attaching comments as
245
+ * the nodes are visited.
246
+ */
247
+ static void
248
+ pm_comments_visit(pm_comments_walk_t *walk, const pm_node_t *node) {
249
+ /* First, push all of the children of this node onto the stack. */
250
+ size_t base = walk->stack.size;
251
+ pm_visit_child_nodes(node, pm_comments_push, walk);
252
+ size_t top = walk->stack.size;
253
+
254
+ /* Next, in-place sort the children by their starting location. */
255
+ for (size_t index = base + 1; index < top; index++) {
256
+ const pm_node_t *child = walk->stack.nodes[index];
257
+ size_t insert = index;
258
+
259
+ while (insert > base && walk->stack.nodes[insert - 1]->location.start > child->location.start) {
260
+ walk->stack.nodes[insert] = walk->stack.nodes[insert - 1];
261
+ insert--;
262
+ }
263
+
264
+ walk->stack.nodes[insert] = child;
265
+ }
266
+
267
+ /* Next, visit all of the children so that they can claim comments. */
268
+ for (size_t index = base; index < top; index++) {
269
+ pm_comments_visit(walk, walk->stack.nodes[index]);
270
+ }
271
+
272
+ uint32_t start = node->location.start;
273
+ uint32_t end = PM_LOCATION_END(&node->location);
274
+ uint32_t after = pm_comments_seek(walk, end);
275
+
276
+ /* If this node is followed by a comment on the same line, attach it if
277
+ * appropriate. Appropriate in this case means if no other node has claimed
278
+ * it or if we are closer in distance or equal in distance to the current
279
+ * claimant. */
280
+ if (
281
+ after < walk->size &&
282
+ !PM_NODE_TYPE_P(node, PM_PROGRAM_NODE) &&
283
+ !PM_NODE_TYPE_P(node, PM_STATEMENTS_NODE) &&
284
+ (node->location.length == 0 || walk->source[end - 1] != '\n')
285
+ ) {
286
+ uint32_t comment_start = walk->starts[after];
287
+
288
+ if (memchr(walk->source + end, '\n', comment_start - end) == NULL) {
289
+ const pm_node_t *attached = walk->attached[after];
290
+ uint32_t attached_end = attached == NULL ? 0 : PM_LOCATION_END(&attached->location);
291
+
292
+ if (end >= attached_end || attached_end > comment_start) {
293
+ walk->attached[after] = node;
294
+ }
295
+ }
296
+ }
297
+
298
+ /* Grab up the remaining comments within the range of this node that were
299
+ * not claimed by any of its descendants. A comment that is separated by
300
+ * only whitespace and other comments from a statements node child, either
301
+ * the child after it or the child before it, is attached to the statements
302
+ * instead, so that the comments before the first statement and after the
303
+ * last statement in a body belong to the body. The claimed comments are in
304
+ * order, so the children around each one are found by moving forward
305
+ * through the sorted children. */
306
+ if (after > 0 && walk->starts[after - 1] >= start) {
307
+ uint32_t index = pm_comments_unclaimed(walk, pm_comments_lower_bound(walk, 0, after, start));
308
+ size_t child_index = base;
309
+
310
+ /* The boundaries around the statements children are each found once,
311
+ * when the first comment that needs them is reached, since the
312
+ * comments that follow reach the same children. */
313
+ const pm_node_t *leading = NULL;
314
+ uint32_t leading_boundary = 0;
315
+ const pm_node_t *trailing = NULL;
316
+ uint32_t trailing_boundary = 0;
317
+
318
+ while (index < after && PM_LOCATION_END(&walk->comments[index]->location) <= end) {
319
+ if (walk->attached[index] == NULL) {
320
+ uint32_t comment_end = PM_LOCATION_END(&walk->comments[index]->location);
321
+ const pm_node_t *attached = node;
322
+
323
+ while (child_index < top && walk->stack.nodes[child_index]->location.start < comment_end) child_index++;
324
+
325
+ if (child_index < top) {
326
+ const pm_node_t *child = walk->stack.nodes[child_index];
327
+
328
+ if (PM_NODE_TYPE_P(child, PM_STATEMENTS_NODE)) {
329
+ if (child != leading) {
330
+ leading = child;
331
+ leading_boundary = pm_comments_blank_before(walk, child->location.start);
332
+ }
333
+
334
+ if (walk->starts[index] >= leading_boundary) attached = child;
335
+ }
336
+ }
337
+
338
+ if (attached == node && child_index > base) {
339
+ const pm_node_t *child = walk->stack.nodes[child_index - 1];
340
+
341
+ /* A rescue clause ends at its last statement rather than at
342
+ * the keyword that follows it, and the first clause in a
343
+ * body contains the clauses after it. So a comment after
344
+ * the statements of the last clause follows the first
345
+ * clause, and belongs to the statements of the last. */
346
+ if (PM_NODE_TYPE_P(child, PM_RESCUE_NODE)) {
347
+ const pm_rescue_node_t *rescue = (const pm_rescue_node_t *) child;
348
+ while (rescue->subsequent != NULL) rescue = rescue->subsequent;
349
+ child = (const pm_node_t *) rescue->statements;
350
+ }
351
+
352
+ if (child != NULL && PM_NODE_TYPE_P(child, PM_STATEMENTS_NODE) && PM_LOCATION_END(&child->location) <= walk->starts[index]) {
353
+ if (child != trailing) {
354
+ trailing = child;
355
+ trailing_boundary = pm_comments_blank_after(walk, PM_LOCATION_END(&child->location), end);
356
+ }
357
+
358
+ if (comment_end <= trailing_boundary) attached = child;
359
+ }
360
+ }
361
+
362
+ walk->attached[index] = attached;
363
+ }
364
+
365
+ walk->unclaimed[index] = index + 1;
366
+ index = pm_comments_unclaimed(walk, index + 1);
367
+ }
368
+ }
369
+
370
+ /* Truncate the stack back to the base size. */
371
+ walk->stack.size = base;
372
+ }
373
+
374
+ /*
375
+ * Sort the parser's comment list in place by start offset, then assign every
376
+ * comment to the node that owns it. A comment is owned by:
377
+ *
378
+ * 1. the node that ends latest on the comment's line before the comment starts,
379
+ * with ties going to the node that is higher in the tree;
380
+ * 2. otherwise, the innermost node whose location contains the comment, or the
381
+ * statements node child of that node if only whitespace and other comments
382
+ * come between the statements and the comment, on either side;
383
+ * 3. otherwise, the top-level statements node of the program.
384
+ */
385
+ void
386
+ pm_comments_attach(pm_parser_t *parser, const pm_node_t *root) {
387
+ assert(PM_NODE_TYPE_P(root, PM_PROGRAM_NODE));
388
+
389
+ uint32_t size = (uint32_t) parser->comment_list.size;
390
+ if (size == 0) return;
391
+
392
+ pm_arena_t arena = { 0 };
393
+ pm_comments_walk_t walk = {
394
+ .arena = &arena,
395
+ .source = parser->start,
396
+ .comments = pm_comments_sort(parser, size),
397
+ .size = size,
398
+ .starts = (uint32_t *) pm_arena_alloc(&arena, sizeof(uint32_t) * size, PRISM_ALIGNOF(uint32_t)),
399
+ .attached = (const pm_node_t **) pm_arena_zalloc(&arena, sizeof(pm_node_t *) * size, PRISM_ALIGNOF(pm_node_t *)),
400
+ .unclaimed = (uint32_t *) pm_arena_alloc(&arena, sizeof(uint32_t) * (size + 1), PRISM_ALIGNOF(uint32_t))
401
+ };
402
+
403
+ for (uint32_t index = 0; index < size; index++) walk.starts[index] = walk.comments[index]->location.start;
404
+ for (uint32_t index = 0; index <= size; index++) walk.unclaimed[index] = index;
405
+
406
+ pm_comments_visit(&walk, root);
407
+
408
+ /*
409
+ * Attach comments that no node attached to during the walk to the
410
+ * top-level statements, then group the comments by node with a counting
411
+ * sort. Comments are placed in sorted order, so each node's comments stay
412
+ * sorted by start offset.
413
+ */
414
+ const pm_node_t *statements = (const pm_node_t *) ((const pm_program_node_t *) root)->statements;
415
+ uint32_t ranges_size = 0;
416
+
417
+ for (uint32_t index = 0; index < size; index++) {
418
+ if (walk.attached[index] == NULL) walk.attached[index] = statements;
419
+
420
+ uint32_t node_id = walk.attached[index]->node_id;
421
+ assert(node_id <= parser->node_id);
422
+ if (node_id >= ranges_size) ranges_size = node_id + 1;
423
+ }
424
+
425
+ /* The ranges only extend to the largest node id that owns a comment, since
426
+ * none of the nodes after it own any. */
427
+ pm_comment_range_t *ranges = (pm_comment_range_t *) pm_arena_zalloc(&parser->metadata_arena, sizeof(pm_comment_range_t) * ranges_size, PRISM_ALIGNOF(pm_comment_range_t));
428
+ uint32_t *attachments = (uint32_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(uint32_t) * size, PRISM_ALIGNOF(uint32_t));
429
+
430
+ for (uint32_t index = 0; index < size; index++) {
431
+ ranges[walk.attached[index]->node_id].length++;
432
+ }
433
+
434
+ uint32_t offset = 0;
435
+ for (uint32_t index = 0; index < ranges_size; index++) {
436
+ ranges[index].index = offset;
437
+ offset += ranges[index].length;
438
+ ranges[index].length = 0;
439
+ }
440
+
441
+ for (uint32_t index = 0; index < size; index++) {
442
+ pm_comment_range_t *range = &ranges[walk.attached[index]->node_id];
443
+ attachments[range->index + range->length++] = index;
444
+ }
445
+
446
+ pm_arena_cleanup(&arena);
447
+
448
+ parser->comment_attachments = (pm_comment_attachments_t) {
449
+ .comments = walk.comments,
450
+ .attachments = attachments,
451
+ .ranges = ranges,
452
+ .ranges_size = ranges_size
453
+ };
454
+ }