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.
- checksums.yaml +7 -0
- data/BSDmakefile +58 -0
- data/CHANGELOG.md +870 -0
- data/CODE_OF_CONDUCT.md +76 -0
- data/CONTRIBUTING.md +58 -0
- data/LICENSE.md +7 -0
- data/Makefile +145 -0
- data/README.md +81 -0
- data/config.yml +4752 -0
- data/docs/build_system.md +119 -0
- data/docs/configuration.md +69 -0
- data/docs/cruby_compilation.md +27 -0
- data/docs/design.md +53 -0
- data/docs/encoding.md +121 -0
- data/docs/fuzzing.md +81 -0
- data/docs/heredocs.md +36 -0
- data/docs/javascript.md +133 -0
- data/docs/local_variable_depth.md +229 -0
- data/docs/mapping.md +117 -0
- data/docs/parser_translation.md +24 -0
- data/docs/parsing_rules.md +22 -0
- data/docs/releasing.md +147 -0
- data/docs/relocation.md +32 -0
- data/docs/ripper_translation.md +63 -0
- data/docs/ruby_api.md +45 -0
- data/docs/ruby_parser_translation.md +19 -0
- data/docs/serialization.md +230 -0
- data/docs/testing.md +72 -0
- data/ext/prism/api_node.c +7005 -0
- data/ext/prism/extconf.rb +126 -0
- data/ext/prism/extension.c +1783 -0
- data/ext/prism/extension.h +19 -0
- data/include/prism/arena.h +37 -0
- data/include/prism/ast.h +10371 -0
- data/include/prism/buffer.h +52 -0
- data/include/prism/comments.h +55 -0
- data/include/prism/compiler/accel.h +24 -0
- data/include/prism/compiler/align.h +36 -0
- data/include/prism/compiler/assume.h +25 -0
- data/include/prism/compiler/exported.h +24 -0
- data/include/prism/compiler/fallthrough.h +22 -0
- data/include/prism/compiler/filesystem.h +32 -0
- data/include/prism/compiler/flex_array.h +19 -0
- data/include/prism/compiler/force_inline.h +21 -0
- data/include/prism/compiler/format.h +25 -0
- data/include/prism/compiler/inline.h +17 -0
- data/include/prism/compiler/nodiscard.h +22 -0
- data/include/prism/compiler/nonnull.h +18 -0
- data/include/prism/compiler/unused.h +18 -0
- data/include/prism/constant_pool.h +81 -0
- data/include/prism/diagnostic.h +93 -0
- data/include/prism/errors_format.h +53 -0
- data/include/prism/excludes.h +29 -0
- data/include/prism/integer.h +41 -0
- data/include/prism/internal/allocator.h +68 -0
- data/include/prism/internal/allocator_debug.h +88 -0
- data/include/prism/internal/arena.h +108 -0
- data/include/prism/internal/bit.h +42 -0
- data/include/prism/internal/buffer.h +91 -0
- data/include/prism/internal/char.h +145 -0
- data/include/prism/internal/comments.h +53 -0
- data/include/prism/internal/constant_pool.h +152 -0
- data/include/prism/internal/diagnostic.h +389 -0
- data/include/prism/internal/encoding.h +242 -0
- data/include/prism/internal/integer.h +68 -0
- data/include/prism/internal/isinf.h +16 -0
- data/include/prism/internal/line_offset_list.h +34 -0
- data/include/prism/internal/list.h +62 -0
- data/include/prism/internal/magic_comments.h +23 -0
- data/include/prism/internal/memchr.h +15 -0
- data/include/prism/internal/node.h +38 -0
- data/include/prism/internal/options.h +232 -0
- data/include/prism/internal/parser.h +984 -0
- data/include/prism/internal/regexp.h +26 -0
- data/include/prism/internal/serialize.h +39 -0
- data/include/prism/internal/source.h +72 -0
- data/include/prism/internal/static_literals.h +100 -0
- data/include/prism/internal/stringy.h +35 -0
- data/include/prism/internal/strncasecmp.h +18 -0
- data/include/prism/internal/strpbrk.h +33 -0
- data/include/prism/internal/tokens.h +11 -0
- data/include/prism/json.h +32 -0
- data/include/prism/line_offset_list.h +61 -0
- data/include/prism/magic_comments.h +35 -0
- data/include/prism/node.h +94 -0
- data/include/prism/options.h +354 -0
- data/include/prism/parser.h +404 -0
- data/include/prism/prettyprint.h +31 -0
- data/include/prism/serialize.h +115 -0
- data/include/prism/source.h +155 -0
- data/include/prism/stream.h +28 -0
- data/include/prism/string_query.h +63 -0
- data/include/prism/stringy.h +72 -0
- data/include/prism/version.h +38 -0
- data/include/prism.h +131 -0
- data/lib/prism/3.3/prism.so +0 -0
- data/lib/prism/3.4/prism.so +0 -0
- data/lib/prism/4.0/prism.so +0 -0
- data/lib/prism/compiler.rb +815 -0
- data/lib/prism/desugar_compiler.rb +463 -0
- data/lib/prism/dispatcher.rb +2383 -0
- data/lib/prism/dot_visitor.rb +4848 -0
- data/lib/prism/dsl.rb +1369 -0
- data/lib/prism/ffi.rb +740 -0
- data/lib/prism/inspect_visitor.rb +2425 -0
- data/lib/prism/lex_compat.rb +953 -0
- data/lib/prism/mutation_compiler.rb +780 -0
- data/lib/prism/node.rb +33204 -0
- data/lib/prism/node_ext.rb +504 -0
- data/lib/prism/node_find.rb +215 -0
- data/lib/prism/parse_result/errors.rb +72 -0
- data/lib/prism/parse_result/newlines.rb +423 -0
- data/lib/prism/parse_result.rb +1221 -0
- data/lib/prism/pattern.rb +314 -0
- data/lib/prism/polyfill/append_as_bytes.rb +15 -0
- data/lib/prism/polyfill/byteindex.rb +13 -0
- data/lib/prism/polyfill/scan_byte.rb +14 -0
- data/lib/prism/polyfill/unpack1.rb +14 -0
- data/lib/prism/polyfill/warn.rb +36 -0
- data/lib/prism/reflection.rb +427 -0
- data/lib/prism/relocation.rb +567 -0
- data/lib/prism/serialize.rb +5308 -0
- data/lib/prism/string_query.rb +46 -0
- data/lib/prism/translation/parser/builder.rb +70 -0
- data/lib/prism/translation/parser/compiler.rb +2226 -0
- data/lib/prism/translation/parser/lexer.rb +769 -0
- data/lib/prism/translation/parser.rb +376 -0
- data/lib/prism/translation/parser_current.rb +26 -0
- data/lib/prism/translation/parser_versions.rb +36 -0
- data/lib/prism/translation/ripper/filter.rb +53 -0
- data/lib/prism/translation/ripper/lexer.rb +133 -0
- data/lib/prism/translation/ripper/sexp.rb +118 -0
- data/lib/prism/translation/ripper/shim.rb +7 -0
- data/lib/prism/translation/ripper.rb +4278 -0
- data/lib/prism/translation/ruby_parser.rb +1721 -0
- data/lib/prism/translation.rb +20 -0
- data/lib/prism/visitor.rb +1440 -0
- data/lib/prism.rb +168 -0
- data/prism.gemspec +234 -0
- data/rbi/generated/prism/compiler.rbi +489 -0
- data/rbi/generated/prism/desugar_compiler.rbi +306 -0
- data/rbi/generated/prism/dispatcher.rbi +1138 -0
- data/rbi/generated/prism/dot_visitor.rbi +618 -0
- data/rbi/generated/prism/dsl.rbi +751 -0
- data/rbi/generated/prism/inspect_visitor.rbi +503 -0
- data/rbi/generated/prism/lex_compat.rbi +162 -0
- data/rbi/generated/prism/mutation_compiler.rbi +464 -0
- data/rbi/generated/prism/node.rbi +13681 -0
- data/rbi/generated/prism/node_ext.rbi +259 -0
- data/rbi/generated/prism/node_find.rbi +79 -0
- data/rbi/generated/prism/parse_result/errors.rbi +21 -0
- data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
- data/rbi/generated/prism/parse_result.rbi +764 -0
- data/rbi/generated/prism/pattern.rbi +134 -0
- data/rbi/generated/prism/reflection.rbi +89 -0
- data/rbi/generated/prism/relocation.rbi +329 -0
- data/rbi/generated/prism/serialize.rbi +181 -0
- data/rbi/generated/prism/string_query.rbi +36 -0
- data/rbi/generated/prism/translation.rbi +7 -0
- data/rbi/generated/prism/visitor.rbi +650 -0
- data/rbi/generated/prism.rbi +86 -0
- data/rbi/prism/translation/parser.rbi +11 -0
- data/rbi/prism/translation/parser_versions.rbi +28 -0
- data/rbi/prism/translation/ripper.rbi +15 -0
- data/rbi/rubyvm/node_find.rbi +14 -0
- data/sig/generated/prism/compiler.rbs +492 -0
- data/sig/generated/prism/desugar_compiler.rbs +305 -0
- data/sig/generated/prism/dispatcher.rbs +1292 -0
- data/sig/generated/prism/dot_visitor.rbs +635 -0
- data/sig/generated/prism/dsl.rbs +928 -0
- data/sig/generated/prism/inspect_visitor.rbs +509 -0
- data/sig/generated/prism/lex_compat.rbs +166 -0
- data/sig/generated/prism/mutation_compiler.rbs +464 -0
- data/sig/generated/prism/node.rbs +20160 -0
- data/sig/generated/prism/node_ext.rbs +290 -0
- data/sig/generated/prism/node_find.rbs +90 -0
- data/sig/generated/prism/parse_result/errors.rbs +22 -0
- data/sig/generated/prism/parse_result/newlines.rbs +223 -0
- data/sig/generated/prism/parse_result.rbs +859 -0
- data/sig/generated/prism/pattern.rbs +156 -0
- data/sig/generated/prism/reflection.rbs +101 -0
- data/sig/generated/prism/relocation.rbs +428 -0
- data/sig/generated/prism/serialize.rbs +195 -0
- data/sig/generated/prism/string_query.rbs +36 -0
- data/sig/generated/prism/translation.rbs +8 -0
- data/sig/generated/prism/visitor.rbs +1111 -0
- data/sig/generated/prism.rbs +84 -0
- data/sig/manifest.yaml +2 -0
- data/src/arena.c +117 -0
- data/src/buffer.c +380 -0
- data/src/char.c +289 -0
- data/src/comments.c +454 -0
- data/src/constant_pool.c +406 -0
- data/src/diagnostic.c +886 -0
- data/src/encoding.c +5345 -0
- data/src/errors_format.c +519 -0
- data/src/integer.c +681 -0
- data/src/json.c +5733 -0
- data/src/line_offset_list.c +100 -0
- data/src/list.c +24 -0
- data/src/memchr.c +37 -0
- data/src/node.c +4515 -0
- data/src/options.c +451 -0
- data/src/parser.c +349 -0
- data/src/prettyprint.c +9024 -0
- data/src/prism.c +23739 -0
- data/src/regexp.c +1706 -0
- data/src/serialize.c +2309 -0
- data/src/source.c +518 -0
- data/src/static_literals.c +685 -0
- data/src/string_query.c +166 -0
- data/src/stringy.c +91 -0
- data/src/strncasecmp.c +37 -0
- data/src/strpbrk.c +447 -0
- data/src/tokens.c +724 -0
- 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
|
+
}
|