prism 2.0.0 → 2.1.0
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 +4 -4
- data/CHANGELOG.md +51 -42
- data/Makefile +8 -8
- data/README.md +3 -1
- data/docs/fuzzing.md +5 -5
- data/ext/prism/extension.h +1 -1
- data/include/prism/internal/comments.h +2 -1
- data/include/prism/internal/parser.h +26 -15
- data/include/prism/internal/static_literals.h +24 -64
- data/include/prism/version.h +2 -2
- data/lib/prism/serialize.rb +1 -1
- data/prism.gemspec +1 -1
- data/src/arena.c +6 -1
- data/src/comments.c +9 -8
- data/src/diagnostic.c +16 -4
- data/src/integer.c +6 -1
- data/src/prism.c +51 -50
- data/src/static_literals.c +280 -405
- metadata +1 -1
data/src/static_literals.c
CHANGED
|
@@ -7,280 +7,117 @@
|
|
|
7
7
|
#include "prism/internal/buffer.h"
|
|
8
8
|
#include "prism/internal/integer.h"
|
|
9
9
|
#include "prism/internal/isinf.h"
|
|
10
|
+
#include "prism/internal/parser.h"
|
|
10
11
|
#include "prism/internal/stringy.h"
|
|
12
|
+
#include "prism/internal/xxh3.h"
|
|
11
13
|
|
|
12
14
|
#include <assert.h>
|
|
15
|
+
#include <inttypes.h>
|
|
13
16
|
#include <math.h>
|
|
14
17
|
#include <stdio.h>
|
|
15
18
|
#include <stdlib.h>
|
|
16
19
|
#include <string.h>
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
+
* Hash the key built from the given values. Nodes that compare as equal must
|
|
23
|
+
* produce identical keys, so each value is either held directly or is the
|
|
24
|
+
* digest of a variable-length value. The key is an array so that its values are
|
|
25
|
+
* contiguous with no padding between them, which makes every hashed byte
|
|
26
|
+
* defined.
|
|
22
27
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
/** The start of the source being parsed. */
|
|
28
|
-
const uint8_t *start;
|
|
29
|
-
|
|
30
|
-
/** The line number that the parser starts on. */
|
|
31
|
-
int32_t start_line;
|
|
32
|
-
|
|
33
|
-
/** The encoding that the parser is using. */
|
|
34
|
-
const pm_encoding_t *encoding;
|
|
35
|
-
} pm_static_literals_metadata_t;
|
|
36
|
-
|
|
37
|
-
static PRISM_INLINE uint32_t
|
|
38
|
-
murmur_scramble(uint32_t value) {
|
|
39
|
-
value *= 0xcc9e2d51;
|
|
40
|
-
value = (value << 15) | (value >> 17);
|
|
41
|
-
value *= 0x1b873593;
|
|
42
|
-
return value;
|
|
28
|
+
static PRISM_INLINE uint64_t
|
|
29
|
+
key_hash(uint64_t type, uint64_t a, uint64_t b) {
|
|
30
|
+
const uint64_t key[3] = { type, a, b };
|
|
31
|
+
return pm_xxh3((const uint8_t *) key, sizeof(key));
|
|
43
32
|
}
|
|
44
33
|
|
|
45
34
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
35
|
+
* Return the hash of the value of an integer. An integer that fits into a
|
|
36
|
+
* uint32_t keys on its value directly, and a larger one keys on the digest of
|
|
37
|
+
* its words.
|
|
49
38
|
*/
|
|
50
|
-
static
|
|
51
|
-
murmur_hash(const uint8_t *key, size_t length) {
|
|
52
|
-
uint32_t hash = 0x9747b28c;
|
|
53
|
-
uint32_t segment;
|
|
54
|
-
|
|
55
|
-
for (size_t index = length >> 2; index; index--) {
|
|
56
|
-
memcpy(&segment, key, sizeof(uint32_t));
|
|
57
|
-
key += sizeof(uint32_t);
|
|
58
|
-
hash ^= murmur_scramble(segment);
|
|
59
|
-
hash = (hash << 13) | (hash >> 19);
|
|
60
|
-
hash = hash * 5 + 0xe6546b64;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
segment = 0;
|
|
64
|
-
for (size_t index = length & 3; index; index--) {
|
|
65
|
-
segment <<= 8;
|
|
66
|
-
segment |= key[index - 1];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
hash ^= murmur_scramble(segment);
|
|
70
|
-
hash ^= (uint32_t) length;
|
|
71
|
-
hash ^= hash >> 16;
|
|
72
|
-
hash *= 0x85ebca6b;
|
|
73
|
-
hash ^= hash >> 13;
|
|
74
|
-
hash *= 0xc2b2ae35;
|
|
75
|
-
hash ^= hash >> 16;
|
|
76
|
-
return hash;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Hash the value of an integer and return it.
|
|
81
|
-
*/
|
|
82
|
-
static uint32_t
|
|
39
|
+
static uint64_t
|
|
83
40
|
integer_hash(const pm_integer_t *integer) {
|
|
84
|
-
uint32_t
|
|
85
|
-
|
|
86
|
-
hash = murmur_hash((const uint8_t *) integer->values, sizeof(uint32_t) * integer->length);
|
|
87
|
-
} else {
|
|
88
|
-
hash = murmur_hash((const uint8_t *) &integer->value, sizeof(uint32_t));
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (integer->negative) {
|
|
92
|
-
hash ^= murmur_scramble((uint32_t) 1);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return hash;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Return the flags of the given node that determine the encoding of its string.
|
|
100
|
-
*
|
|
101
|
-
* The FORCED_* flags record how a literal was written. An escape that locks the
|
|
102
|
-
* encoding to UTF-8 (a `\u` escape above U+007F, or any `\u` in a character
|
|
103
|
-
* literal) sets FORCED_UTF8 whether or not the file is already UTF-8. So in a
|
|
104
|
-
* UTF-8 file a `\u00E9` escape and a literal e-acute carry different flags
|
|
105
|
-
* while being the same string, and the flag has to be dropped for them to
|
|
106
|
-
* compare equally.
|
|
107
|
-
*/
|
|
108
|
-
static pm_node_flags_t
|
|
109
|
-
node_encoding_flags(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) {
|
|
110
|
-
switch (PM_NODE_TYPE(node)) {
|
|
111
|
-
case PM_STRING_NODE: {
|
|
112
|
-
pm_node_flags_t mask = PM_STRING_FLAGS_FORCED_BINARY_ENCODING;
|
|
113
|
-
if (metadata->encoding != PM_ENCODING_UTF_8_ENTRY) mask |= PM_STRING_FLAGS_FORCED_UTF8_ENCODING;
|
|
114
|
-
return node->flags & mask;
|
|
115
|
-
}
|
|
116
|
-
case PM_SYMBOL_NODE: {
|
|
117
|
-
pm_node_flags_t mask = PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING;
|
|
118
|
-
if (metadata->encoding != PM_ENCODING_UTF_8_ENTRY) mask |= PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING;
|
|
119
|
-
return node->flags & mask;
|
|
120
|
-
}
|
|
121
|
-
case PM_SOURCE_FILE_NODE:
|
|
122
|
-
/* __FILE__ takes the encoding of the filepath, and every instance
|
|
123
|
-
* of it in a parse resolves the same way. */
|
|
124
|
-
return 0;
|
|
125
|
-
default:
|
|
126
|
-
assert(false && "unreachable");
|
|
127
|
-
return 0;
|
|
128
|
-
}
|
|
41
|
+
uint64_t magnitude = integer->values ? pm_xxh3((const uint8_t *) integer->values, sizeof(uint32_t) * integer->length) : integer->value;
|
|
42
|
+
return key_hash(PM_INTEGER_NODE, integer->negative, magnitude);
|
|
129
43
|
}
|
|
130
44
|
|
|
131
45
|
/**
|
|
132
|
-
* Return the hash of the given node. It is important that nodes that
|
|
133
|
-
* equivalent static literal values have the same
|
|
134
|
-
* these
|
|
46
|
+
* Return the hash of the key of the given node. It is important that nodes that
|
|
47
|
+
* have equivalent static literal values have the same key. This is because we
|
|
48
|
+
* hash these keys to look for duplicates.
|
|
135
49
|
*/
|
|
136
|
-
static
|
|
137
|
-
node_hash(const
|
|
50
|
+
static uint64_t
|
|
51
|
+
node_hash(const pm_parser_t *parser, const pm_node_t *node) {
|
|
138
52
|
switch (PM_NODE_TYPE(node)) {
|
|
139
|
-
case PM_INTEGER_NODE:
|
|
140
|
-
|
|
141
|
-
const pm_integer_node_t *cast = (const pm_integer_node_t *) node;
|
|
142
|
-
return integer_hash(&cast->value);
|
|
143
|
-
}
|
|
53
|
+
case PM_INTEGER_NODE:
|
|
54
|
+
return integer_hash(&((const pm_integer_node_t *) node)->value);
|
|
144
55
|
case PM_SOURCE_LINE_NODE: {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const int32_t *value = &line_column.line;
|
|
148
|
-
return murmur_hash((const uint8_t *) value, sizeof(int32_t));
|
|
56
|
+
int64_t line = (int64_t) pm_line_offset_list_line_column(&parser->line_offsets, node->location.start, parser->start_line).line;
|
|
57
|
+
return key_hash(PM_INTEGER_NODE, line < 0, (uint64_t) (line < 0 ? -line : line));
|
|
149
58
|
}
|
|
150
59
|
case PM_FLOAT_NODE: {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
60
|
+
const pm_float_node_t *cast = (const pm_float_node_t *) node;
|
|
61
|
+
double value = cast->value;
|
|
62
|
+
if (value == 0.0) value = 0.0; /* Normalize -0.0 to 0.0. */
|
|
63
|
+
|
|
64
|
+
uint64_t bits;
|
|
65
|
+
memcpy(&bits, &value, sizeof(bits));
|
|
66
|
+
return key_hash(PM_FLOAT_NODE, bits, 0);
|
|
154
67
|
}
|
|
155
68
|
case PM_RATIONAL_NODE: {
|
|
156
|
-
// Rationals hash their numerator and denominator.
|
|
157
69
|
const pm_rational_node_t *cast = (const pm_rational_node_t *) node;
|
|
158
|
-
return integer_hash(&cast->numerator)
|
|
70
|
+
return key_hash(PM_RATIONAL_NODE, integer_hash(&cast->numerator), integer_hash(&cast->denominator));
|
|
159
71
|
}
|
|
160
72
|
case PM_IMAGINARY_NODE: {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// fact that this is an imaginary node.
|
|
164
|
-
const pm_node_t *numeric = ((const pm_imaginary_node_t *) node)->numeric;
|
|
165
|
-
return node_hash(metadata, numeric) ^ murmur_scramble((uint32_t) node->type);
|
|
73
|
+
const pm_imaginary_node_t *cast = (const pm_imaginary_node_t *) node;
|
|
74
|
+
return key_hash(PM_IMAGINARY_NODE, node_hash(parser, cast->numeric), 0);
|
|
166
75
|
}
|
|
167
76
|
case PM_STRING_NODE: {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
77
|
+
const pm_string_node_t *cast = (const pm_string_node_t *) node;
|
|
78
|
+
|
|
79
|
+
pm_node_flags_t mask = PM_STRING_FLAGS_FORCED_BINARY_ENCODING;
|
|
80
|
+
if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) mask |= PM_STRING_FLAGS_FORCED_UTF8_ENCODING;
|
|
81
|
+
|
|
82
|
+
return key_hash(PM_STRING_NODE, pm_xxh3(pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped)), node->flags & mask);
|
|
172
83
|
}
|
|
173
84
|
case PM_SOURCE_FILE_NODE: {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const pm_string_t *value = &((const pm_source_file_node_t *) node)->filepath;
|
|
177
|
-
return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t));
|
|
85
|
+
const pm_source_file_node_t *cast = (const pm_source_file_node_t *) node;
|
|
86
|
+
return key_hash(PM_STRING_NODE, pm_xxh3(pm_string_source(&cast->filepath), pm_string_length(&cast->filepath)), 0);
|
|
178
87
|
}
|
|
179
88
|
case PM_REGULAR_EXPRESSION_NODE: {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const pm_string_t *value = &((const pm_regular_expression_node_t *) node)->unescaped;
|
|
183
|
-
return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) node->flags);
|
|
89
|
+
const pm_regular_expression_node_t *cast = (const pm_regular_expression_node_t *) node;
|
|
90
|
+
return key_hash(PM_REGULAR_EXPRESSION_NODE, pm_xxh3(pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped)), node->flags & (pm_node_flags_t) ~PM_REGULAR_EXPRESSION_FLAGS_ONCE);
|
|
184
91
|
}
|
|
185
92
|
case PM_SYMBOL_NODE: {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
93
|
+
const pm_symbol_node_t *cast = (const pm_symbol_node_t *) node;
|
|
94
|
+
|
|
95
|
+
pm_node_flags_t mask = PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING;
|
|
96
|
+
if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) mask |= PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING;
|
|
97
|
+
|
|
98
|
+
return key_hash(PM_SYMBOL_NODE, pm_xxh3(pm_string_source(&cast->unescaped), pm_string_length(&cast->unescaped)), node->flags & mask);
|
|
190
99
|
}
|
|
100
|
+
case PM_FALSE_NODE:
|
|
101
|
+
case PM_NIL_NODE:
|
|
102
|
+
case PM_SOURCE_ENCODING_NODE:
|
|
103
|
+
case PM_TRUE_NODE:
|
|
104
|
+
return key_hash(PM_NODE_TYPE(node), 0, 0);
|
|
191
105
|
default:
|
|
192
106
|
assert(false && "unreachable");
|
|
193
107
|
return 0;
|
|
194
108
|
}
|
|
195
109
|
}
|
|
196
110
|
|
|
197
|
-
/**
|
|
198
|
-
* Insert a node into the node hash. It accepts the hash that should hold the
|
|
199
|
-
* new node, the parser that generated the node, the node to insert, and a
|
|
200
|
-
* comparison function. The comparison function is used for collision detection,
|
|
201
|
-
* and must be able to compare all node types that will be stored in this hash.
|
|
202
|
-
*/
|
|
203
|
-
static pm_node_t *
|
|
204
|
-
pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *metadata, pm_node_t *node, bool replace, int (*compare)(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right)) {
|
|
205
|
-
// If we are out of space, we need to resize the hash. This will cause all
|
|
206
|
-
// of the nodes to be rehashed and reinserted into the new hash.
|
|
207
|
-
if (hash->size * 2 >= hash->capacity) {
|
|
208
|
-
// First, allocate space for the new node list.
|
|
209
|
-
uint32_t new_capacity = hash->capacity == 0 ? 4 : hash->capacity * 2;
|
|
210
|
-
pm_node_t **new_nodes = xcalloc(new_capacity, sizeof(pm_node_t *));
|
|
211
|
-
if (new_nodes == NULL) abort();
|
|
212
|
-
|
|
213
|
-
// It turns out to be more efficient to mask the hash value than to use
|
|
214
|
-
// the modulo operator. Because our capacities are always powers of two,
|
|
215
|
-
// we can use a bitwise AND to get the same result as the modulo
|
|
216
|
-
// operator.
|
|
217
|
-
uint32_t mask = new_capacity - 1;
|
|
218
|
-
|
|
219
|
-
// Now, rehash all of the nodes into the new list.
|
|
220
|
-
for (uint32_t index = 0; index < hash->capacity; index++) {
|
|
221
|
-
pm_node_t *node = hash->nodes[index];
|
|
222
|
-
|
|
223
|
-
if (node != NULL) {
|
|
224
|
-
uint32_t new_index = node_hash(metadata, node) & mask;
|
|
225
|
-
while (new_nodes[new_index] != NULL) {
|
|
226
|
-
new_index = (new_index + 1) & mask;
|
|
227
|
-
}
|
|
228
|
-
new_nodes[new_index] = node;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Finally, free the old node list and update the hash.
|
|
233
|
-
xfree_sized(hash->nodes, hash->capacity * sizeof(pm_node_t *));
|
|
234
|
-
hash->nodes = new_nodes;
|
|
235
|
-
hash->capacity = new_capacity;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Now, insert the node into the hash.
|
|
239
|
-
uint32_t mask = hash->capacity - 1;
|
|
240
|
-
uint32_t index = node_hash(metadata, node) & mask;
|
|
241
|
-
|
|
242
|
-
// We use linear probing to resolve collisions. This means that if the
|
|
243
|
-
// current index is occupied, we will move to the next index and try again.
|
|
244
|
-
// We are guaranteed that this will eventually find an empty slot because we
|
|
245
|
-
// resize the hash when it gets too full.
|
|
246
|
-
while (hash->nodes[index] != NULL) {
|
|
247
|
-
if (compare(metadata, hash->nodes[index], node) == 0) break;
|
|
248
|
-
index = (index + 1) & mask;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// If the current index is occupied, we need to return the node that was
|
|
252
|
-
// already in the hash. Otherwise, we can just increment the size and insert
|
|
253
|
-
// the new node.
|
|
254
|
-
pm_node_t *result = hash->nodes[index];
|
|
255
|
-
|
|
256
|
-
if (result == NULL) {
|
|
257
|
-
hash->size++;
|
|
258
|
-
hash->nodes[index] = node;
|
|
259
|
-
} else if (replace) {
|
|
260
|
-
hash->nodes[index] = node;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return result;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Free the internal memory associated with the given node hash.
|
|
268
|
-
*/
|
|
269
|
-
static void
|
|
270
|
-
pm_node_hash_free(pm_node_hash_t *hash) {
|
|
271
|
-
if (hash->capacity > 0) xfree_sized(hash->nodes, hash->capacity * sizeof(pm_node_t *));
|
|
272
|
-
}
|
|
273
|
-
|
|
274
111
|
/**
|
|
275
112
|
* Compare two values that can be compared with a simple numeric comparison.
|
|
276
113
|
*/
|
|
277
|
-
#define
|
|
114
|
+
#define COMPARE(left_, right_) (((left_) < (right_)) ? -1 : ((left_) > (right_)) ? 1 : 0)
|
|
278
115
|
|
|
279
116
|
/**
|
|
280
117
|
* Return the integer value of the given node as an int64_t.
|
|
281
118
|
*/
|
|
282
119
|
static int64_t
|
|
283
|
-
|
|
120
|
+
int64_value(const pm_parser_t *parser, const pm_node_t *node) {
|
|
284
121
|
switch (PM_NODE_TYPE(node)) {
|
|
285
122
|
case PM_INTEGER_NODE: {
|
|
286
123
|
const pm_integer_t *integer = &((const pm_integer_node_t *) node)->value;
|
|
@@ -290,7 +127,7 @@ pm_int64_value(const pm_static_literals_metadata_t *metadata, const pm_node_t *n
|
|
|
290
127
|
return integer->negative ? -value : value;
|
|
291
128
|
}
|
|
292
129
|
case PM_SOURCE_LINE_NODE:
|
|
293
|
-
return (int64_t) pm_line_offset_list_line_column(
|
|
130
|
+
return (int64_t) pm_line_offset_list_line_column(&parser->line_offsets, node->location.start, parser->start_line).line;
|
|
294
131
|
default:
|
|
295
132
|
assert(false && "unreachable");
|
|
296
133
|
return 0;
|
|
@@ -302,11 +139,9 @@ pm_int64_value(const pm_static_literals_metadata_t *metadata, const pm_node_t *n
|
|
|
302
139
|
* instances.
|
|
303
140
|
*/
|
|
304
141
|
static int
|
|
305
|
-
|
|
142
|
+
compare_integer_nodes(const pm_parser_t *parser, const pm_node_t *left, const pm_node_t *right) {
|
|
306
143
|
if (PM_NODE_TYPE_P(left, PM_SOURCE_LINE_NODE) || PM_NODE_TYPE_P(right, PM_SOURCE_LINE_NODE)) {
|
|
307
|
-
|
|
308
|
-
int64_t right_value = pm_int64_value(metadata, right);
|
|
309
|
-
return PM_NUMERIC_COMPARISON(left_value, right_value);
|
|
144
|
+
return COMPARE(int64_value(parser, left), int64_value(parser, right));
|
|
310
145
|
}
|
|
311
146
|
|
|
312
147
|
const pm_integer_t *left_integer = &((const pm_integer_node_t *) left)->value;
|
|
@@ -318,48 +153,15 @@ pm_compare_integer_nodes(const pm_static_literals_metadata_t *metadata, const pm
|
|
|
318
153
|
* A comparison function for comparing two FloatNode instances.
|
|
319
154
|
*/
|
|
320
155
|
static int
|
|
321
|
-
|
|
322
|
-
const
|
|
323
|
-
const double right_value = ((const pm_float_node_t *) right)->value;
|
|
324
|
-
return PM_NUMERIC_COMPARISON(left_value, right_value);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* A comparison function for comparing two nodes that have attached numbers.
|
|
329
|
-
*/
|
|
330
|
-
static int
|
|
331
|
-
pm_compare_number_nodes(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
|
|
332
|
-
if (PM_NODE_TYPE(left) != PM_NODE_TYPE(right)) {
|
|
333
|
-
return PM_NUMERIC_COMPARISON(PM_NODE_TYPE(left), PM_NODE_TYPE(right));
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
switch (PM_NODE_TYPE(left)) {
|
|
337
|
-
case PM_IMAGINARY_NODE:
|
|
338
|
-
return pm_compare_number_nodes(metadata, ((const pm_imaginary_node_t *) left)->numeric, ((const pm_imaginary_node_t *) right)->numeric);
|
|
339
|
-
case PM_RATIONAL_NODE: {
|
|
340
|
-
const pm_rational_node_t *left_rational = (const pm_rational_node_t *) left;
|
|
341
|
-
const pm_rational_node_t *right_rational = (const pm_rational_node_t *) right;
|
|
342
|
-
|
|
343
|
-
int result = pm_integer_compare(&left_rational->denominator, &right_rational->denominator);
|
|
344
|
-
if (result != 0) return result;
|
|
345
|
-
|
|
346
|
-
return pm_integer_compare(&left_rational->numerator, &right_rational->numerator);
|
|
347
|
-
}
|
|
348
|
-
case PM_INTEGER_NODE:
|
|
349
|
-
return pm_compare_integer_nodes(metadata, left, right);
|
|
350
|
-
case PM_FLOAT_NODE:
|
|
351
|
-
return pm_compare_float_nodes(metadata, left, right);
|
|
352
|
-
default:
|
|
353
|
-
assert(false && "unreachable");
|
|
354
|
-
return 0;
|
|
355
|
-
}
|
|
156
|
+
compare_float_nodes(PRISM_UNUSED const pm_parser_t *parser, const pm_node_t *left, const pm_node_t *right) {
|
|
157
|
+
return COMPARE(((const pm_float_node_t *) left)->value, ((const pm_float_node_t *) right)->value);
|
|
356
158
|
}
|
|
357
159
|
|
|
358
160
|
/**
|
|
359
161
|
* Return a pointer to the string value of the given node.
|
|
360
162
|
*/
|
|
361
163
|
static const pm_string_t *
|
|
362
|
-
|
|
164
|
+
string_value(const pm_node_t *node) {
|
|
363
165
|
switch (PM_NODE_TYPE(node)) {
|
|
364
166
|
case PM_STRING_NODE:
|
|
365
167
|
return &((const pm_string_node_t *) node)->unescaped;
|
|
@@ -375,11 +177,20 @@ pm_string_value(const pm_node_t *node) {
|
|
|
375
177
|
|
|
376
178
|
/**
|
|
377
179
|
* A comparison function for comparing two nodes that have attached strings.
|
|
180
|
+
* Only the flags in the given mask take part in the comparison.
|
|
181
|
+
*
|
|
182
|
+
* The FORCED_* flags record how a literal was written. An escape that locks the
|
|
183
|
+
* encoding to UTF-8 (a `\u` escape above U+007F, or any `\u` in a character
|
|
184
|
+
* literal) sets FORCED_UTF8 whether or not the file is already UTF-8. So in a
|
|
185
|
+
* UTF-8 file a `\u00E9` escape and a literal e-acute carry different flags
|
|
186
|
+
* while being the same string, and the flag is left out of the mask for them
|
|
187
|
+
* to compare equally. A source file node never carries a FORCED_* flag, because
|
|
188
|
+
* every instance of __FILE__ in a parse resolves to the same encoding.
|
|
378
189
|
*/
|
|
379
190
|
static int
|
|
380
|
-
|
|
381
|
-
const pm_string_t *left_string =
|
|
382
|
-
const pm_string_t *right_string =
|
|
191
|
+
compare_string_nodes(const pm_node_t *left, const pm_node_t *right, pm_node_flags_t mask) {
|
|
192
|
+
const pm_string_t *left_string = string_value(left);
|
|
193
|
+
const pm_string_t *right_string = string_value(right);
|
|
383
194
|
|
|
384
195
|
int result = pm_string_compare(left_string, right_string);
|
|
385
196
|
if (result != 0) return result;
|
|
@@ -389,137 +200,223 @@ pm_compare_string_nodes(const pm_static_literals_metadata_t *metadata, const pm_
|
|
|
389
200
|
* as `"\xC3\xA9"` stay BINARY while a `"\u00E9"` escape is forced to UTF-8,
|
|
390
201
|
* so those are distinct keys even though the bytes match.
|
|
391
202
|
*/
|
|
392
|
-
|
|
393
|
-
pm_node_flags_t right_flags = node_encoding_flags(metadata, right);
|
|
394
|
-
return PM_NUMERIC_COMPARISON(left_flags, right_flags);
|
|
203
|
+
return COMPARE(left->flags & mask, right->flags & mask);
|
|
395
204
|
}
|
|
396
205
|
|
|
397
206
|
/**
|
|
398
|
-
* A comparison function for comparing two RegularExpressionNode instances.
|
|
207
|
+
* A comparison function for comparing two RegularExpressionNode instances. The
|
|
208
|
+
* once flag is left out because it only controls how many times an
|
|
209
|
+
* interpolated regular expression is compiled, not its value.
|
|
399
210
|
*/
|
|
400
211
|
static int
|
|
401
|
-
|
|
212
|
+
compare_regular_expression_nodes(PRISM_UNUSED const pm_parser_t *parser, const pm_node_t *left, const pm_node_t *right) {
|
|
402
213
|
const pm_regular_expression_node_t *left_regexp = (const pm_regular_expression_node_t *) left;
|
|
403
214
|
const pm_regular_expression_node_t *right_regexp = (const pm_regular_expression_node_t *) right;
|
|
404
215
|
|
|
405
216
|
int result = pm_string_compare(&left_regexp->unescaped, &right_regexp->unescaped);
|
|
406
217
|
if (result != 0) return result;
|
|
407
218
|
|
|
408
|
-
|
|
219
|
+
pm_node_flags_t mask = (pm_node_flags_t) ~PM_REGULAR_EXPRESSION_FLAGS_ONCE;
|
|
220
|
+
return COMPARE(left->flags & mask, right->flags & mask);
|
|
409
221
|
}
|
|
410
222
|
|
|
411
|
-
#undef PM_NUMERIC_COMPARISON
|
|
412
|
-
|
|
413
223
|
/**
|
|
414
|
-
*
|
|
224
|
+
* Return the type that the given node is compared as. A source line node can
|
|
225
|
+
* equal an integer node and a source file node can equal a string node, so
|
|
226
|
+
* each takes the type of the node it can equal.
|
|
415
227
|
*/
|
|
416
|
-
|
|
417
|
-
|
|
228
|
+
static enum pm_node_type
|
|
229
|
+
static_literal_type(const pm_node_t *node) {
|
|
418
230
|
switch (PM_NODE_TYPE(node)) {
|
|
419
|
-
case PM_INTEGER_NODE:
|
|
420
231
|
case PM_SOURCE_LINE_NODE:
|
|
421
|
-
return
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
232
|
+
return PM_INTEGER_NODE;
|
|
233
|
+
case PM_SOURCE_FILE_NODE:
|
|
234
|
+
return PM_STRING_NODE;
|
|
235
|
+
default:
|
|
236
|
+
return PM_NODE_TYPE(node);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A comparison function for comparing any two static literal nodes. Nodes that
|
|
242
|
+
* are compared as different types are never equal.
|
|
243
|
+
*/
|
|
244
|
+
static int
|
|
245
|
+
compare_nodes(const pm_parser_t *parser, const pm_node_t *left, const pm_node_t *right) {
|
|
246
|
+
enum pm_node_type left_type = static_literal_type(left);
|
|
247
|
+
enum pm_node_type right_type = static_literal_type(right);
|
|
248
|
+
if (left_type != right_type) return COMPARE(left_type, right_type);
|
|
249
|
+
|
|
250
|
+
switch (left_type) {
|
|
251
|
+
case PM_INTEGER_NODE:
|
|
252
|
+
return compare_integer_nodes(parser, left, right);
|
|
433
253
|
case PM_FLOAT_NODE:
|
|
434
|
-
return
|
|
435
|
-
&literals->float_nodes,
|
|
436
|
-
&(pm_static_literals_metadata_t) {
|
|
437
|
-
.line_offsets = line_offsets,
|
|
438
|
-
.start = start,
|
|
439
|
-
.start_line = start_line,
|
|
440
|
-
.encoding = encoding
|
|
441
|
-
},
|
|
442
|
-
node,
|
|
443
|
-
replace,
|
|
444
|
-
pm_compare_float_nodes
|
|
445
|
-
);
|
|
446
|
-
case PM_RATIONAL_NODE:
|
|
254
|
+
return compare_float_nodes(parser, left, right);
|
|
447
255
|
case PM_IMAGINARY_NODE:
|
|
448
|
-
return
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
return pm_node_hash_insert(
|
|
463
|
-
&literals->string_nodes,
|
|
464
|
-
&(pm_static_literals_metadata_t) {
|
|
465
|
-
.line_offsets = line_offsets,
|
|
466
|
-
.start = start,
|
|
467
|
-
.start_line = start_line,
|
|
468
|
-
.encoding = encoding
|
|
469
|
-
},
|
|
470
|
-
node,
|
|
471
|
-
replace,
|
|
472
|
-
pm_compare_string_nodes
|
|
473
|
-
);
|
|
474
|
-
case PM_REGULAR_EXPRESSION_NODE:
|
|
475
|
-
return pm_node_hash_insert(
|
|
476
|
-
&literals->regexp_nodes,
|
|
477
|
-
&(pm_static_literals_metadata_t) {
|
|
478
|
-
.line_offsets = line_offsets,
|
|
479
|
-
.start = start,
|
|
480
|
-
.start_line = start_line,
|
|
481
|
-
.encoding = encoding
|
|
482
|
-
},
|
|
483
|
-
node,
|
|
484
|
-
replace,
|
|
485
|
-
pm_compare_regular_expression_nodes
|
|
486
|
-
);
|
|
487
|
-
case PM_SYMBOL_NODE:
|
|
488
|
-
return pm_node_hash_insert(
|
|
489
|
-
&literals->symbol_nodes,
|
|
490
|
-
&(pm_static_literals_metadata_t) {
|
|
491
|
-
.line_offsets = line_offsets,
|
|
492
|
-
.start = start,
|
|
493
|
-
.start_line = start_line,
|
|
494
|
-
.encoding = encoding
|
|
495
|
-
},
|
|
496
|
-
node,
|
|
497
|
-
replace,
|
|
498
|
-
pm_compare_string_nodes
|
|
499
|
-
);
|
|
500
|
-
case PM_TRUE_NODE: {
|
|
501
|
-
pm_node_t *duplicated = literals->true_node;
|
|
502
|
-
if ((duplicated == NULL) || replace) literals->true_node = node;
|
|
503
|
-
return duplicated;
|
|
256
|
+
return compare_nodes(parser, ((const pm_imaginary_node_t *) left)->numeric, ((const pm_imaginary_node_t *) right)->numeric);
|
|
257
|
+
case PM_RATIONAL_NODE: {
|
|
258
|
+
const pm_rational_node_t *left_rational = (const pm_rational_node_t *) left;
|
|
259
|
+
const pm_rational_node_t *right_rational = (const pm_rational_node_t *) right;
|
|
260
|
+
|
|
261
|
+
int result = pm_integer_compare(&left_rational->denominator, &right_rational->denominator);
|
|
262
|
+
if (result != 0) return result;
|
|
263
|
+
|
|
264
|
+
return pm_integer_compare(&left_rational->numerator, &right_rational->numerator);
|
|
265
|
+
}
|
|
266
|
+
case PM_STRING_NODE: {
|
|
267
|
+
pm_node_flags_t mask = PM_STRING_FLAGS_FORCED_BINARY_ENCODING;
|
|
268
|
+
if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) mask |= PM_STRING_FLAGS_FORCED_UTF8_ENCODING;
|
|
269
|
+
return compare_string_nodes(left, right, mask);
|
|
504
270
|
}
|
|
505
|
-
case
|
|
506
|
-
|
|
507
|
-
if (
|
|
508
|
-
return
|
|
271
|
+
case PM_SYMBOL_NODE: {
|
|
272
|
+
pm_node_flags_t mask = PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING;
|
|
273
|
+
if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) mask |= PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING;
|
|
274
|
+
return compare_string_nodes(left, right, mask);
|
|
509
275
|
}
|
|
510
|
-
case
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
276
|
+
case PM_REGULAR_EXPRESSION_NODE:
|
|
277
|
+
return compare_regular_expression_nodes(parser, left, right);
|
|
278
|
+
case PM_FALSE_NODE:
|
|
279
|
+
case PM_NIL_NODE:
|
|
280
|
+
case PM_SOURCE_ENCODING_NODE:
|
|
281
|
+
case PM_TRUE_NODE:
|
|
282
|
+
return 0;
|
|
283
|
+
default:
|
|
284
|
+
assert(false && "unreachable");
|
|
285
|
+
return 0;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
#undef COMPARE
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Place an entry into a hash table that is known not to hold an equal node, so
|
|
293
|
+
* no comparisons are needed to find its slot.
|
|
294
|
+
*/
|
|
295
|
+
static void
|
|
296
|
+
pm_static_literals_place(pm_static_literals_entry_t *entries, uint32_t mask, pm_static_literals_entry_t entry) {
|
|
297
|
+
uint32_t index = (uint32_t) (entry.hash & mask);
|
|
298
|
+
while (entries[index].node != NULL) {
|
|
299
|
+
index = (index + 1) & mask;
|
|
300
|
+
}
|
|
301
|
+
entries[index] = entry;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Move the nodes of the set into a new hash table with the given capacity. A
|
|
306
|
+
* set that has no hash table yet hashes its inline nodes. A set that already
|
|
307
|
+
* has one moves its entries, which keep their hashes.
|
|
308
|
+
*/
|
|
309
|
+
static void
|
|
310
|
+
pm_static_literals_resize(const pm_parser_t *parser, pm_static_literals_t *literals, uint32_t capacity) {
|
|
311
|
+
pm_static_literals_entry_t *entries = xcalloc(capacity, sizeof(pm_static_literals_entry_t));
|
|
312
|
+
if (entries == NULL) abort();
|
|
313
|
+
|
|
314
|
+
/* It turns out to be more efficient to mask the hash value than to use the
|
|
315
|
+
* modulo operator. Because our capacities are always powers of two, we can
|
|
316
|
+
* use a bitwise AND to get the same result as the modulo operator. */
|
|
317
|
+
uint32_t mask = capacity - 1;
|
|
318
|
+
|
|
319
|
+
if (literals->capacity == 0) {
|
|
320
|
+
for (uint32_t index = 0; index < literals->size; index++) {
|
|
321
|
+
pm_node_t *node = literals->nodes[index];
|
|
322
|
+
pm_static_literals_place(entries, mask, (pm_static_literals_entry_t) { .node = node, .hash = node_hash(parser, node) });
|
|
514
323
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (
|
|
518
|
-
|
|
324
|
+
} else {
|
|
325
|
+
for (uint32_t index = 0; index < literals->capacity; index++) {
|
|
326
|
+
if (literals->entries[index].node != NULL) {
|
|
327
|
+
pm_static_literals_place(entries, mask, literals->entries[index]);
|
|
328
|
+
}
|
|
519
329
|
}
|
|
330
|
+
|
|
331
|
+
xfree_sized(literals->entries, literals->capacity * sizeof(pm_static_literals_entry_t));
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
literals->entries = entries;
|
|
335
|
+
literals->capacity = capacity;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Add a node to the set of static literals. If an equal node is already in the
|
|
340
|
+
* set, it is returned (and replaced by the new node if replace is set).
|
|
341
|
+
* Otherwise the new node is inserted and NULL is returned. Nodes that are not
|
|
342
|
+
* static literals are ignored.
|
|
343
|
+
*/
|
|
344
|
+
pm_node_t *
|
|
345
|
+
pm_static_literals_add(const pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node, bool replace) {
|
|
346
|
+
switch (PM_NODE_TYPE(node)) {
|
|
347
|
+
case PM_FALSE_NODE:
|
|
348
|
+
case PM_FLOAT_NODE:
|
|
349
|
+
case PM_IMAGINARY_NODE:
|
|
350
|
+
case PM_INTEGER_NODE:
|
|
351
|
+
case PM_NIL_NODE:
|
|
352
|
+
case PM_RATIONAL_NODE:
|
|
353
|
+
case PM_REGULAR_EXPRESSION_NODE:
|
|
354
|
+
case PM_SOURCE_ENCODING_NODE:
|
|
355
|
+
case PM_SOURCE_FILE_NODE:
|
|
356
|
+
case PM_SOURCE_LINE_NODE:
|
|
357
|
+
case PM_STRING_NODE:
|
|
358
|
+
case PM_SYMBOL_NODE:
|
|
359
|
+
case PM_TRUE_NODE:
|
|
360
|
+
break;
|
|
520
361
|
default:
|
|
521
362
|
return NULL;
|
|
522
363
|
}
|
|
364
|
+
|
|
365
|
+
if (literals->capacity == 0) {
|
|
366
|
+
/* A set without a hash table compares the new node against each of
|
|
367
|
+
* its nodes directly. Comparing nodes of different types returns
|
|
368
|
+
* early, so this costs about as much as hashing the new node. */
|
|
369
|
+
for (uint32_t index = 0; index < literals->size; index++) {
|
|
370
|
+
pm_node_t *existing = literals->nodes[index];
|
|
371
|
+
|
|
372
|
+
if (compare_nodes(parser, existing, node) == 0) {
|
|
373
|
+
if (replace) literals->nodes[index] = node;
|
|
374
|
+
return existing;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (literals->size < PM_STATIC_LITERALS_INLINE_CAPACITY) {
|
|
379
|
+
literals->nodes[literals->size++] = node;
|
|
380
|
+
return NULL;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* The set is full, so its nodes move into a hash table. The table is
|
|
384
|
+
* sized to stay at most half full after this node is added, with room
|
|
385
|
+
* to spare before its first resize. */
|
|
386
|
+
pm_static_literals_resize(parser, literals, PM_STATIC_LITERALS_INLINE_CAPACITY * 4);
|
|
387
|
+
} else if (literals->size * 2 >= literals->capacity) {
|
|
388
|
+
/* If we are out of space, we need to resize the hash. */
|
|
389
|
+
pm_static_literals_resize(parser, literals, literals->capacity * 2);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
uint64_t hash = node_hash(parser, node);
|
|
393
|
+
uint32_t mask = literals->capacity - 1;
|
|
394
|
+
uint32_t index = (uint32_t) (hash & mask);
|
|
395
|
+
|
|
396
|
+
/* We use linear probing to resolve collisions. This means that if the
|
|
397
|
+
* current index is occupied, we will move to the next index and try again.
|
|
398
|
+
* We are guaranteed that this will eventually find an empty slot because we
|
|
399
|
+
* resize the hash when it gets too full. Entries whose hash differs cannot
|
|
400
|
+
* be equal, so only entries with a matching hash are compared. */
|
|
401
|
+
while (literals->entries[index].node != NULL) {
|
|
402
|
+
const pm_static_literals_entry_t *entry = &literals->entries[index];
|
|
403
|
+
if (entry->hash == hash && compare_nodes(parser, entry->node, node) == 0) break;
|
|
404
|
+
index = (index + 1) & mask;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/* If the current index is occupied, we need to return the node that was
|
|
408
|
+
* already in the hash. Otherwise, we can just increment the size and insert
|
|
409
|
+
* the new node. */
|
|
410
|
+
pm_node_t *result = literals->entries[index].node;
|
|
411
|
+
|
|
412
|
+
if (result == NULL) {
|
|
413
|
+
literals->size++;
|
|
414
|
+
literals->entries[index] = (pm_static_literals_entry_t) { .node = node, .hash = hash };
|
|
415
|
+
} else if (replace) {
|
|
416
|
+
literals->entries[index].node = node;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return result;
|
|
523
420
|
}
|
|
524
421
|
|
|
525
422
|
/**
|
|
@@ -527,12 +424,7 @@ pm_static_literals_add(const pm_line_offset_list_t *line_offsets, const uint8_t
|
|
|
527
424
|
*/
|
|
528
425
|
void
|
|
529
426
|
pm_static_literals_free(pm_static_literals_t *literals) {
|
|
530
|
-
|
|
531
|
-
pm_node_hash_free(&literals->float_nodes);
|
|
532
|
-
pm_node_hash_free(&literals->number_nodes);
|
|
533
|
-
pm_node_hash_free(&literals->string_nodes);
|
|
534
|
-
pm_node_hash_free(&literals->regexp_nodes);
|
|
535
|
-
pm_node_hash_free(&literals->symbol_nodes);
|
|
427
|
+
if (literals->capacity > 0) xfree_sized(literals->entries, literals->capacity * sizeof(pm_static_literals_entry_t));
|
|
536
428
|
}
|
|
537
429
|
|
|
538
430
|
/**
|
|
@@ -559,8 +451,8 @@ pm_static_literal_positive_p(const pm_node_t *node) {
|
|
|
559
451
|
/**
|
|
560
452
|
* Create a string-based representation of the given static literal.
|
|
561
453
|
*/
|
|
562
|
-
|
|
563
|
-
|
|
454
|
+
void
|
|
455
|
+
pm_static_literal_inspect(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *node) {
|
|
564
456
|
switch (PM_NODE_TYPE(node)) {
|
|
565
457
|
case PM_FALSE_NODE:
|
|
566
458
|
pm_buffer_append_string(buffer, "false", 5);
|
|
@@ -569,21 +461,21 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
|
|
|
569
461
|
const double value = ((const pm_float_node_t *) node)->value;
|
|
570
462
|
|
|
571
463
|
if (PRISM_ISINF(value)) {
|
|
572
|
-
if (
|
|
464
|
+
if (parser->start[node->location.start] == '-') {
|
|
573
465
|
pm_buffer_append_byte(buffer, '-');
|
|
574
466
|
}
|
|
575
467
|
pm_buffer_append_string(buffer, "Infinity", 8);
|
|
576
468
|
} else if (value == 0.0) {
|
|
577
|
-
if (
|
|
469
|
+
if (parser->start[node->location.start] == '-') {
|
|
578
470
|
pm_buffer_append_byte(buffer, '-');
|
|
579
471
|
}
|
|
580
472
|
pm_buffer_append_string(buffer, "0.0", 3);
|
|
581
473
|
} else {
|
|
582
474
|
pm_buffer_append_format(buffer, "%g", value);
|
|
583
475
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
476
|
+
/* %g will not insert a .0 for 1e100 (we'll get back 1e+100). So
|
|
477
|
+
* we check for the decimal point and add it in here if it's not
|
|
478
|
+
* present. */
|
|
587
479
|
if (pm_buffer_index(buffer, '.') == SIZE_MAX) {
|
|
588
480
|
size_t exponent_index = pm_buffer_index(buffer, 'e');
|
|
589
481
|
size_t index = exponent_index == SIZE_MAX ? pm_buffer_length(buffer) : exponent_index;
|
|
@@ -597,7 +489,7 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
|
|
|
597
489
|
const pm_node_t *numeric = ((const pm_imaginary_node_t *) node)->numeric;
|
|
598
490
|
pm_buffer_append_string(buffer, "(0", 2);
|
|
599
491
|
if (pm_static_literal_positive_p(numeric)) pm_buffer_append_byte(buffer, '+');
|
|
600
|
-
|
|
492
|
+
pm_static_literal_inspect(buffer, parser, numeric);
|
|
601
493
|
if (PM_NODE_TYPE_P(numeric, PM_RATIONAL_NODE)) {
|
|
602
494
|
pm_buffer_append_byte(buffer, '*');
|
|
603
495
|
}
|
|
@@ -633,7 +525,7 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
|
|
|
633
525
|
break;
|
|
634
526
|
}
|
|
635
527
|
case PM_SOURCE_ENCODING_NODE:
|
|
636
|
-
pm_buffer_append_format(buffer, "#<Encoding:%s>",
|
|
528
|
+
pm_buffer_append_format(buffer, "#<Encoding:%s>", parser->encoding->name);
|
|
637
529
|
break;
|
|
638
530
|
case PM_SOURCE_FILE_NODE: {
|
|
639
531
|
const pm_string_t *filepath = &((const pm_source_file_node_t *) node)->filepath;
|
|
@@ -643,7 +535,7 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
|
|
|
643
535
|
break;
|
|
644
536
|
}
|
|
645
537
|
case PM_SOURCE_LINE_NODE:
|
|
646
|
-
pm_buffer_append_format(buffer, "%
|
|
538
|
+
pm_buffer_append_format(buffer, "%" PRIi32, pm_line_offset_list_line_column(&parser->line_offsets, node->location.start, parser->start_line).line);
|
|
647
539
|
break;
|
|
648
540
|
case PM_STRING_NODE: {
|
|
649
541
|
const pm_string_t *unescaped = &((const pm_string_node_t *) node)->unescaped;
|
|
@@ -666,20 +558,3 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
|
|
|
666
558
|
break;
|
|
667
559
|
}
|
|
668
560
|
}
|
|
669
|
-
|
|
670
|
-
/**
|
|
671
|
-
* Create a string-based representation of the given static literal.
|
|
672
|
-
*/
|
|
673
|
-
void
|
|
674
|
-
pm_static_literal_inspect(pm_buffer_t *buffer, const pm_line_offset_list_t *line_offsets, const uint8_t *start, int32_t start_line, const pm_encoding_t *encoding, const pm_node_t *node) {
|
|
675
|
-
pm_static_literal_inspect_node(
|
|
676
|
-
buffer,
|
|
677
|
-
&(pm_static_literals_metadata_t) {
|
|
678
|
-
.line_offsets = line_offsets,
|
|
679
|
-
.start = start,
|
|
680
|
-
.start_line = start_line,
|
|
681
|
-
.encoding = encoding
|
|
682
|
-
},
|
|
683
|
-
node
|
|
684
|
-
);
|
|
685
|
-
}
|