prism 2.0.0.rc1-x86_64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. checksums.yaml +7 -0
  2. data/BSDmakefile +58 -0
  3. data/CHANGELOG.md +870 -0
  4. data/CODE_OF_CONDUCT.md +76 -0
  5. data/CONTRIBUTING.md +58 -0
  6. data/LICENSE.md +7 -0
  7. data/Makefile +145 -0
  8. data/README.md +81 -0
  9. data/config.yml +4752 -0
  10. data/docs/build_system.md +119 -0
  11. data/docs/configuration.md +69 -0
  12. data/docs/cruby_compilation.md +27 -0
  13. data/docs/design.md +53 -0
  14. data/docs/encoding.md +121 -0
  15. data/docs/fuzzing.md +81 -0
  16. data/docs/heredocs.md +36 -0
  17. data/docs/javascript.md +133 -0
  18. data/docs/local_variable_depth.md +229 -0
  19. data/docs/mapping.md +117 -0
  20. data/docs/parser_translation.md +24 -0
  21. data/docs/parsing_rules.md +22 -0
  22. data/docs/releasing.md +147 -0
  23. data/docs/relocation.md +32 -0
  24. data/docs/ripper_translation.md +63 -0
  25. data/docs/ruby_api.md +45 -0
  26. data/docs/ruby_parser_translation.md +19 -0
  27. data/docs/serialization.md +230 -0
  28. data/docs/testing.md +72 -0
  29. data/ext/prism/api_node.c +7005 -0
  30. data/ext/prism/extconf.rb +126 -0
  31. data/ext/prism/extension.c +1783 -0
  32. data/ext/prism/extension.h +19 -0
  33. data/include/prism/arena.h +37 -0
  34. data/include/prism/ast.h +10371 -0
  35. data/include/prism/buffer.h +52 -0
  36. data/include/prism/comments.h +55 -0
  37. data/include/prism/compiler/accel.h +24 -0
  38. data/include/prism/compiler/align.h +36 -0
  39. data/include/prism/compiler/assume.h +25 -0
  40. data/include/prism/compiler/exported.h +24 -0
  41. data/include/prism/compiler/fallthrough.h +22 -0
  42. data/include/prism/compiler/filesystem.h +32 -0
  43. data/include/prism/compiler/flex_array.h +19 -0
  44. data/include/prism/compiler/force_inline.h +21 -0
  45. data/include/prism/compiler/format.h +25 -0
  46. data/include/prism/compiler/inline.h +17 -0
  47. data/include/prism/compiler/nodiscard.h +22 -0
  48. data/include/prism/compiler/nonnull.h +18 -0
  49. data/include/prism/compiler/unused.h +18 -0
  50. data/include/prism/constant_pool.h +81 -0
  51. data/include/prism/diagnostic.h +93 -0
  52. data/include/prism/errors_format.h +53 -0
  53. data/include/prism/excludes.h +29 -0
  54. data/include/prism/integer.h +41 -0
  55. data/include/prism/internal/allocator.h +68 -0
  56. data/include/prism/internal/allocator_debug.h +88 -0
  57. data/include/prism/internal/arena.h +108 -0
  58. data/include/prism/internal/bit.h +42 -0
  59. data/include/prism/internal/buffer.h +91 -0
  60. data/include/prism/internal/char.h +145 -0
  61. data/include/prism/internal/comments.h +53 -0
  62. data/include/prism/internal/constant_pool.h +152 -0
  63. data/include/prism/internal/diagnostic.h +389 -0
  64. data/include/prism/internal/encoding.h +242 -0
  65. data/include/prism/internal/integer.h +68 -0
  66. data/include/prism/internal/isinf.h +16 -0
  67. data/include/prism/internal/line_offset_list.h +34 -0
  68. data/include/prism/internal/list.h +62 -0
  69. data/include/prism/internal/magic_comments.h +23 -0
  70. data/include/prism/internal/memchr.h +15 -0
  71. data/include/prism/internal/node.h +38 -0
  72. data/include/prism/internal/options.h +232 -0
  73. data/include/prism/internal/parser.h +984 -0
  74. data/include/prism/internal/regexp.h +26 -0
  75. data/include/prism/internal/serialize.h +39 -0
  76. data/include/prism/internal/source.h +72 -0
  77. data/include/prism/internal/static_literals.h +100 -0
  78. data/include/prism/internal/stringy.h +35 -0
  79. data/include/prism/internal/strncasecmp.h +18 -0
  80. data/include/prism/internal/strpbrk.h +33 -0
  81. data/include/prism/internal/tokens.h +11 -0
  82. data/include/prism/json.h +32 -0
  83. data/include/prism/line_offset_list.h +61 -0
  84. data/include/prism/magic_comments.h +35 -0
  85. data/include/prism/node.h +94 -0
  86. data/include/prism/options.h +354 -0
  87. data/include/prism/parser.h +404 -0
  88. data/include/prism/prettyprint.h +31 -0
  89. data/include/prism/serialize.h +115 -0
  90. data/include/prism/source.h +155 -0
  91. data/include/prism/stream.h +28 -0
  92. data/include/prism/string_query.h +63 -0
  93. data/include/prism/stringy.h +72 -0
  94. data/include/prism/version.h +38 -0
  95. data/include/prism.h +131 -0
  96. data/lib/prism/3.3/prism.so +0 -0
  97. data/lib/prism/3.4/prism.so +0 -0
  98. data/lib/prism/4.0/prism.so +0 -0
  99. data/lib/prism/compiler.rb +815 -0
  100. data/lib/prism/desugar_compiler.rb +463 -0
  101. data/lib/prism/dispatcher.rb +2383 -0
  102. data/lib/prism/dot_visitor.rb +4848 -0
  103. data/lib/prism/dsl.rb +1369 -0
  104. data/lib/prism/ffi.rb +740 -0
  105. data/lib/prism/inspect_visitor.rb +2425 -0
  106. data/lib/prism/lex_compat.rb +953 -0
  107. data/lib/prism/mutation_compiler.rb +780 -0
  108. data/lib/prism/node.rb +33204 -0
  109. data/lib/prism/node_ext.rb +504 -0
  110. data/lib/prism/node_find.rb +215 -0
  111. data/lib/prism/parse_result/errors.rb +72 -0
  112. data/lib/prism/parse_result/newlines.rb +423 -0
  113. data/lib/prism/parse_result.rb +1221 -0
  114. data/lib/prism/pattern.rb +314 -0
  115. data/lib/prism/polyfill/append_as_bytes.rb +15 -0
  116. data/lib/prism/polyfill/byteindex.rb +13 -0
  117. data/lib/prism/polyfill/scan_byte.rb +14 -0
  118. data/lib/prism/polyfill/unpack1.rb +14 -0
  119. data/lib/prism/polyfill/warn.rb +36 -0
  120. data/lib/prism/reflection.rb +427 -0
  121. data/lib/prism/relocation.rb +567 -0
  122. data/lib/prism/serialize.rb +5308 -0
  123. data/lib/prism/string_query.rb +46 -0
  124. data/lib/prism/translation/parser/builder.rb +70 -0
  125. data/lib/prism/translation/parser/compiler.rb +2226 -0
  126. data/lib/prism/translation/parser/lexer.rb +769 -0
  127. data/lib/prism/translation/parser.rb +376 -0
  128. data/lib/prism/translation/parser_current.rb +26 -0
  129. data/lib/prism/translation/parser_versions.rb +36 -0
  130. data/lib/prism/translation/ripper/filter.rb +53 -0
  131. data/lib/prism/translation/ripper/lexer.rb +133 -0
  132. data/lib/prism/translation/ripper/sexp.rb +118 -0
  133. data/lib/prism/translation/ripper/shim.rb +7 -0
  134. data/lib/prism/translation/ripper.rb +4278 -0
  135. data/lib/prism/translation/ruby_parser.rb +1721 -0
  136. data/lib/prism/translation.rb +20 -0
  137. data/lib/prism/visitor.rb +1440 -0
  138. data/lib/prism.rb +168 -0
  139. data/prism.gemspec +234 -0
  140. data/rbi/generated/prism/compiler.rbi +489 -0
  141. data/rbi/generated/prism/desugar_compiler.rbi +306 -0
  142. data/rbi/generated/prism/dispatcher.rbi +1138 -0
  143. data/rbi/generated/prism/dot_visitor.rbi +618 -0
  144. data/rbi/generated/prism/dsl.rbi +751 -0
  145. data/rbi/generated/prism/inspect_visitor.rbi +503 -0
  146. data/rbi/generated/prism/lex_compat.rbi +162 -0
  147. data/rbi/generated/prism/mutation_compiler.rbi +464 -0
  148. data/rbi/generated/prism/node.rbi +13681 -0
  149. data/rbi/generated/prism/node_ext.rbi +259 -0
  150. data/rbi/generated/prism/node_find.rbi +79 -0
  151. data/rbi/generated/prism/parse_result/errors.rbi +21 -0
  152. data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
  153. data/rbi/generated/prism/parse_result.rbi +764 -0
  154. data/rbi/generated/prism/pattern.rbi +134 -0
  155. data/rbi/generated/prism/reflection.rbi +89 -0
  156. data/rbi/generated/prism/relocation.rbi +329 -0
  157. data/rbi/generated/prism/serialize.rbi +181 -0
  158. data/rbi/generated/prism/string_query.rbi +36 -0
  159. data/rbi/generated/prism/translation.rbi +7 -0
  160. data/rbi/generated/prism/visitor.rbi +650 -0
  161. data/rbi/generated/prism.rbi +86 -0
  162. data/rbi/prism/translation/parser.rbi +11 -0
  163. data/rbi/prism/translation/parser_versions.rbi +28 -0
  164. data/rbi/prism/translation/ripper.rbi +15 -0
  165. data/rbi/rubyvm/node_find.rbi +14 -0
  166. data/sig/generated/prism/compiler.rbs +492 -0
  167. data/sig/generated/prism/desugar_compiler.rbs +305 -0
  168. data/sig/generated/prism/dispatcher.rbs +1292 -0
  169. data/sig/generated/prism/dot_visitor.rbs +635 -0
  170. data/sig/generated/prism/dsl.rbs +928 -0
  171. data/sig/generated/prism/inspect_visitor.rbs +509 -0
  172. data/sig/generated/prism/lex_compat.rbs +166 -0
  173. data/sig/generated/prism/mutation_compiler.rbs +464 -0
  174. data/sig/generated/prism/node.rbs +20160 -0
  175. data/sig/generated/prism/node_ext.rbs +290 -0
  176. data/sig/generated/prism/node_find.rbs +90 -0
  177. data/sig/generated/prism/parse_result/errors.rbs +22 -0
  178. data/sig/generated/prism/parse_result/newlines.rbs +223 -0
  179. data/sig/generated/prism/parse_result.rbs +859 -0
  180. data/sig/generated/prism/pattern.rbs +156 -0
  181. data/sig/generated/prism/reflection.rbs +101 -0
  182. data/sig/generated/prism/relocation.rbs +428 -0
  183. data/sig/generated/prism/serialize.rbs +195 -0
  184. data/sig/generated/prism/string_query.rbs +36 -0
  185. data/sig/generated/prism/translation.rbs +8 -0
  186. data/sig/generated/prism/visitor.rbs +1111 -0
  187. data/sig/generated/prism.rbs +84 -0
  188. data/sig/manifest.yaml +2 -0
  189. data/src/arena.c +117 -0
  190. data/src/buffer.c +380 -0
  191. data/src/char.c +289 -0
  192. data/src/comments.c +454 -0
  193. data/src/constant_pool.c +406 -0
  194. data/src/diagnostic.c +886 -0
  195. data/src/encoding.c +5345 -0
  196. data/src/errors_format.c +519 -0
  197. data/src/integer.c +681 -0
  198. data/src/json.c +5733 -0
  199. data/src/line_offset_list.c +100 -0
  200. data/src/list.c +24 -0
  201. data/src/memchr.c +37 -0
  202. data/src/node.c +4515 -0
  203. data/src/options.c +451 -0
  204. data/src/parser.c +349 -0
  205. data/src/prettyprint.c +9024 -0
  206. data/src/prism.c +23739 -0
  207. data/src/regexp.c +1706 -0
  208. data/src/serialize.c +2309 -0
  209. data/src/source.c +518 -0
  210. data/src/static_literals.c +685 -0
  211. data/src/string_query.c +166 -0
  212. data/src/stringy.c +91 -0
  213. data/src/strncasecmp.c +37 -0
  214. data/src/strpbrk.c +447 -0
  215. data/src/tokens.c +724 -0
  216. metadata +259 -0
@@ -0,0 +1,685 @@
1
+ #include "prism/internal/static_literals.h"
2
+
3
+ #include "prism/compiler/inline.h"
4
+ #include "prism/compiler/unused.h"
5
+
6
+ #include "prism/internal/allocator.h"
7
+ #include "prism/internal/buffer.h"
8
+ #include "prism/internal/integer.h"
9
+ #include "prism/internal/isinf.h"
10
+ #include "prism/internal/stringy.h"
11
+
12
+ #include <assert.h>
13
+ #include <math.h>
14
+ #include <stdio.h>
15
+ #include <stdlib.h>
16
+ #include <string.h>
17
+
18
+ /**
19
+ * A small struct used for passing around a subset of the information that is
20
+ * stored on the parser. We use this to avoid having static literals explicitly
21
+ * depend on the parser struct.
22
+ */
23
+ typedef struct {
24
+ /** The list of newline offsets to use to calculate line numbers. */
25
+ const pm_line_offset_list_t *line_offsets;
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;
43
+ }
44
+
45
+ /**
46
+ * Murmur hash (https://en.wikipedia.org/wiki/MurmurHash) is a non-cryptographic
47
+ * general-purpose hash function. It is fast, which is what we care about in
48
+ * this case.
49
+ */
50
+ static uint32_t
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
83
+ integer_hash(const pm_integer_t *integer) {
84
+ uint32_t hash;
85
+ if (integer->values) {
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
+ }
129
+ }
130
+
131
+ /**
132
+ * Return the hash of the given node. It is important that nodes that have
133
+ * equivalent static literal values have the same hash. This is because we use
134
+ * these hashes to look for duplicates.
135
+ */
136
+ static uint32_t
137
+ node_hash(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) {
138
+ switch (PM_NODE_TYPE(node)) {
139
+ case PM_INTEGER_NODE: {
140
+ // Integers hash their value.
141
+ const pm_integer_node_t *cast = (const pm_integer_node_t *) node;
142
+ return integer_hash(&cast->value);
143
+ }
144
+ case PM_SOURCE_LINE_NODE: {
145
+ // Source lines hash their line number.
146
+ const pm_line_column_t line_column = pm_line_offset_list_line_column(metadata->line_offsets, node->location.start, metadata->start_line);
147
+ const int32_t *value = &line_column.line;
148
+ return murmur_hash((const uint8_t *) value, sizeof(int32_t));
149
+ }
150
+ case PM_FLOAT_NODE: {
151
+ // Floats hash their value.
152
+ const double *value = &((const pm_float_node_t *) node)->value;
153
+ return murmur_hash((const uint8_t *) value, sizeof(double));
154
+ }
155
+ case PM_RATIONAL_NODE: {
156
+ // Rationals hash their numerator and denominator.
157
+ const pm_rational_node_t *cast = (const pm_rational_node_t *) node;
158
+ return integer_hash(&cast->numerator) ^ integer_hash(&cast->denominator) ^ murmur_scramble((uint32_t) cast->base.type);
159
+ }
160
+ case PM_IMAGINARY_NODE: {
161
+ // Imaginaries hash their numeric value. Because their numeric value
162
+ // is stored as a subnode, we hash that node and then mix in the
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);
166
+ }
167
+ case PM_STRING_NODE: {
168
+ // Strings hash their value and mix in their flags so that different
169
+ // encodings are not considered equal.
170
+ const pm_string_t *value = &((const pm_string_node_t *) node)->unescaped;
171
+ return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) node_encoding_flags(metadata, node));
172
+ }
173
+ case PM_SOURCE_FILE_NODE: {
174
+ // Source files hash their value and mix in their flags so that
175
+ // different encodings are not considered equal.
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));
178
+ }
179
+ case PM_REGULAR_EXPRESSION_NODE: {
180
+ // Regular expressions hash their value and mix in their flags so
181
+ // that different encodings are not considered equal.
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);
184
+ }
185
+ case PM_SYMBOL_NODE: {
186
+ // Symbols hash their value and mix in their flags so that different
187
+ // encodings are not considered equal.
188
+ const pm_string_t *value = &((const pm_symbol_node_t *) node)->unescaped;
189
+ return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) node_encoding_flags(metadata, node));
190
+ }
191
+ default:
192
+ assert(false && "unreachable");
193
+ return 0;
194
+ }
195
+ }
196
+
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
+ /**
275
+ * Compare two values that can be compared with a simple numeric comparison.
276
+ */
277
+ #define PM_NUMERIC_COMPARISON(left, right) ((left < right) ? -1 : (left > right) ? 1 : 0)
278
+
279
+ /**
280
+ * Return the integer value of the given node as an int64_t.
281
+ */
282
+ static int64_t
283
+ pm_int64_value(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) {
284
+ switch (PM_NODE_TYPE(node)) {
285
+ case PM_INTEGER_NODE: {
286
+ const pm_integer_t *integer = &((const pm_integer_node_t *) node)->value;
287
+ if (integer->values) return integer->negative ? INT64_MIN : INT64_MAX;
288
+
289
+ int64_t value = (int64_t) integer->value;
290
+ return integer->negative ? -value : value;
291
+ }
292
+ case PM_SOURCE_LINE_NODE:
293
+ return (int64_t) pm_line_offset_list_line_column(metadata->line_offsets, node->location.start, metadata->start_line).line;
294
+ default:
295
+ assert(false && "unreachable");
296
+ return 0;
297
+ }
298
+ }
299
+
300
+ /**
301
+ * A comparison function for comparing two IntegerNode or SourceLineNode
302
+ * instances.
303
+ */
304
+ static int
305
+ pm_compare_integer_nodes(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
306
+ if (PM_NODE_TYPE_P(left, PM_SOURCE_LINE_NODE) || PM_NODE_TYPE_P(right, PM_SOURCE_LINE_NODE)) {
307
+ int64_t left_value = pm_int64_value(metadata, left);
308
+ int64_t right_value = pm_int64_value(metadata, right);
309
+ return PM_NUMERIC_COMPARISON(left_value, right_value);
310
+ }
311
+
312
+ const pm_integer_t *left_integer = &((const pm_integer_node_t *) left)->value;
313
+ const pm_integer_t *right_integer = &((const pm_integer_node_t *) right)->value;
314
+ return pm_integer_compare(left_integer, right_integer);
315
+ }
316
+
317
+ /**
318
+ * A comparison function for comparing two FloatNode instances.
319
+ */
320
+ static int
321
+ pm_compare_float_nodes(PRISM_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
322
+ const double left_value = ((const pm_float_node_t *) left)->value;
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
+ }
356
+ }
357
+
358
+ /**
359
+ * Return a pointer to the string value of the given node.
360
+ */
361
+ static const pm_string_t *
362
+ pm_string_value(const pm_node_t *node) {
363
+ switch (PM_NODE_TYPE(node)) {
364
+ case PM_STRING_NODE:
365
+ return &((const pm_string_node_t *) node)->unescaped;
366
+ case PM_SOURCE_FILE_NODE:
367
+ return &((const pm_source_file_node_t *) node)->filepath;
368
+ case PM_SYMBOL_NODE:
369
+ return &((const pm_symbol_node_t *) node)->unescaped;
370
+ default:
371
+ assert(false && "unreachable");
372
+ return NULL;
373
+ }
374
+ }
375
+
376
+ /**
377
+ * A comparison function for comparing two nodes that have attached strings.
378
+ */
379
+ static int
380
+ pm_compare_string_nodes(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
381
+ const pm_string_t *left_string = pm_string_value(left);
382
+ const pm_string_t *right_string = pm_string_value(right);
383
+
384
+ int result = pm_string_compare(left_string, right_string);
385
+ if (result != 0) return result;
386
+
387
+ /*
388
+ * Equal bytes are not enough. In a binary source file the two bytes written
389
+ * as `"\xC3\xA9"` stay BINARY while a `"\u00E9"` escape is forced to UTF-8,
390
+ * so those are distinct keys even though the bytes match.
391
+ */
392
+ pm_node_flags_t left_flags = node_encoding_flags(metadata, left);
393
+ pm_node_flags_t right_flags = node_encoding_flags(metadata, right);
394
+ return PM_NUMERIC_COMPARISON(left_flags, right_flags);
395
+ }
396
+
397
+ /**
398
+ * A comparison function for comparing two RegularExpressionNode instances.
399
+ */
400
+ static int
401
+ pm_compare_regular_expression_nodes(PRISM_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
402
+ const pm_regular_expression_node_t *left_regexp = (const pm_regular_expression_node_t *) left;
403
+ const pm_regular_expression_node_t *right_regexp = (const pm_regular_expression_node_t *) right;
404
+
405
+ int result = pm_string_compare(&left_regexp->unescaped, &right_regexp->unescaped);
406
+ if (result != 0) return result;
407
+
408
+ return PM_NUMERIC_COMPARISON(left_regexp->base.flags, right_regexp->base.flags);
409
+ }
410
+
411
+ #undef PM_NUMERIC_COMPARISON
412
+
413
+ /**
414
+ * Add a node to the set of static literals.
415
+ */
416
+ pm_node_t *
417
+ pm_static_literals_add(const pm_line_offset_list_t *line_offsets, const uint8_t *start, int32_t start_line, const pm_encoding_t *encoding, pm_static_literals_t *literals, pm_node_t *node, bool replace) {
418
+ switch (PM_NODE_TYPE(node)) {
419
+ case PM_INTEGER_NODE:
420
+ case PM_SOURCE_LINE_NODE:
421
+ return pm_node_hash_insert(
422
+ &literals->integer_nodes,
423
+ &(pm_static_literals_metadata_t) {
424
+ .line_offsets = line_offsets,
425
+ .start = start,
426
+ .start_line = start_line,
427
+ .encoding = encoding
428
+ },
429
+ node,
430
+ replace,
431
+ pm_compare_integer_nodes
432
+ );
433
+ case PM_FLOAT_NODE:
434
+ return pm_node_hash_insert(
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:
447
+ case PM_IMAGINARY_NODE:
448
+ return pm_node_hash_insert(
449
+ &literals->number_nodes,
450
+ &(pm_static_literals_metadata_t) {
451
+ .line_offsets = line_offsets,
452
+ .start = start,
453
+ .start_line = start_line,
454
+ .encoding = encoding
455
+ },
456
+ node,
457
+ replace,
458
+ pm_compare_number_nodes
459
+ );
460
+ case PM_STRING_NODE:
461
+ case PM_SOURCE_FILE_NODE:
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;
504
+ }
505
+ case PM_FALSE_NODE: {
506
+ pm_node_t *duplicated = literals->false_node;
507
+ if ((duplicated == NULL) || replace) literals->false_node = node;
508
+ return duplicated;
509
+ }
510
+ case PM_NIL_NODE: {
511
+ pm_node_t *duplicated = literals->nil_node;
512
+ if ((duplicated == NULL) || replace) literals->nil_node = node;
513
+ return duplicated;
514
+ }
515
+ case PM_SOURCE_ENCODING_NODE: {
516
+ pm_node_t *duplicated = literals->source_encoding_node;
517
+ if ((duplicated == NULL) || replace) literals->source_encoding_node = node;
518
+ return duplicated;
519
+ }
520
+ default:
521
+ return NULL;
522
+ }
523
+ }
524
+
525
+ /**
526
+ * Free the internal memory associated with the given static literals set.
527
+ */
528
+ void
529
+ pm_static_literals_free(pm_static_literals_t *literals) {
530
+ pm_node_hash_free(&literals->integer_nodes);
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);
536
+ }
537
+
538
+ /**
539
+ * A helper to determine if the given node is a static literal that is positive.
540
+ * This is used for formatting imaginary nodes.
541
+ */
542
+ static bool
543
+ pm_static_literal_positive_p(const pm_node_t *node) {
544
+ switch (PM_NODE_TYPE(node)) {
545
+ case PM_FLOAT_NODE:
546
+ return ((const pm_float_node_t *) node)->value > 0;
547
+ case PM_INTEGER_NODE:
548
+ return !((const pm_integer_node_t *) node)->value.negative;
549
+ case PM_RATIONAL_NODE:
550
+ return !((const pm_rational_node_t *) node)->numerator.negative;
551
+ case PM_IMAGINARY_NODE:
552
+ return pm_static_literal_positive_p(((const pm_imaginary_node_t *) node)->numeric);
553
+ default:
554
+ assert(false && "unreachable");
555
+ return false;
556
+ }
557
+ }
558
+
559
+ /**
560
+ * Create a string-based representation of the given static literal.
561
+ */
562
+ static PRISM_INLINE void
563
+ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_metadata_t *metadata, const pm_node_t *node) {
564
+ switch (PM_NODE_TYPE(node)) {
565
+ case PM_FALSE_NODE:
566
+ pm_buffer_append_string(buffer, "false", 5);
567
+ break;
568
+ case PM_FLOAT_NODE: {
569
+ const double value = ((const pm_float_node_t *) node)->value;
570
+
571
+ if (PRISM_ISINF(value)) {
572
+ if (metadata->start[node->location.start] == '-') {
573
+ pm_buffer_append_byte(buffer, '-');
574
+ }
575
+ pm_buffer_append_string(buffer, "Infinity", 8);
576
+ } else if (value == 0.0) {
577
+ if (metadata->start[node->location.start] == '-') {
578
+ pm_buffer_append_byte(buffer, '-');
579
+ }
580
+ pm_buffer_append_string(buffer, "0.0", 3);
581
+ } else {
582
+ pm_buffer_append_format(buffer, "%g", value);
583
+
584
+ // %g will not insert a .0 for 1e100 (we'll get back 1e+100). So
585
+ // we check for the decimal point and add it in here if it's not
586
+ // present.
587
+ if (pm_buffer_index(buffer, '.') == SIZE_MAX) {
588
+ size_t exponent_index = pm_buffer_index(buffer, 'e');
589
+ size_t index = exponent_index == SIZE_MAX ? pm_buffer_length(buffer) : exponent_index;
590
+ pm_buffer_insert(buffer, index, ".0", 2);
591
+ }
592
+ }
593
+
594
+ break;
595
+ }
596
+ case PM_IMAGINARY_NODE: {
597
+ const pm_node_t *numeric = ((const pm_imaginary_node_t *) node)->numeric;
598
+ pm_buffer_append_string(buffer, "(0", 2);
599
+ if (pm_static_literal_positive_p(numeric)) pm_buffer_append_byte(buffer, '+');
600
+ pm_static_literal_inspect_node(buffer, metadata, numeric);
601
+ if (PM_NODE_TYPE_P(numeric, PM_RATIONAL_NODE)) {
602
+ pm_buffer_append_byte(buffer, '*');
603
+ }
604
+ pm_buffer_append_string(buffer, "i)", 2);
605
+ break;
606
+ }
607
+ case PM_INTEGER_NODE:
608
+ pm_integer_string(buffer, &((const pm_integer_node_t *) node)->value);
609
+ break;
610
+ case PM_NIL_NODE:
611
+ pm_buffer_append_string(buffer, "nil", 3);
612
+ break;
613
+ case PM_RATIONAL_NODE: {
614
+ const pm_rational_node_t *rational = (const pm_rational_node_t *) node;
615
+ pm_buffer_append_byte(buffer, '(');
616
+ pm_integer_string(buffer, &rational->numerator);
617
+ pm_buffer_append_byte(buffer, '/');
618
+ pm_integer_string(buffer, &rational->denominator);
619
+ pm_buffer_append_byte(buffer, ')');
620
+ break;
621
+ }
622
+ case PM_REGULAR_EXPRESSION_NODE: {
623
+ const pm_string_t *unescaped = &((const pm_regular_expression_node_t *) node)->unescaped;
624
+ pm_buffer_append_byte(buffer, '/');
625
+ pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_RUBY);
626
+ pm_buffer_append_byte(buffer, '/');
627
+
628
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE)) pm_buffer_append_string(buffer, "m", 1);
629
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE)) pm_buffer_append_string(buffer, "i", 1);
630
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED)) pm_buffer_append_string(buffer, "x", 1);
631
+ if (PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT)) pm_buffer_append_string(buffer, "n", 1);
632
+
633
+ break;
634
+ }
635
+ case PM_SOURCE_ENCODING_NODE:
636
+ pm_buffer_append_format(buffer, "#<Encoding:%s>", metadata->encoding->name);
637
+ break;
638
+ case PM_SOURCE_FILE_NODE: {
639
+ const pm_string_t *filepath = &((const pm_source_file_node_t *) node)->filepath;
640
+ pm_buffer_append_byte(buffer, '"');
641
+ pm_buffer_append_source(buffer, pm_string_source(filepath), pm_string_length(filepath), PM_BUFFER_ESCAPING_RUBY);
642
+ pm_buffer_append_byte(buffer, '"');
643
+ break;
644
+ }
645
+ case PM_SOURCE_LINE_NODE:
646
+ pm_buffer_append_format(buffer, "%d", pm_line_offset_list_line_column(metadata->line_offsets, node->location.start, metadata->start_line).line);
647
+ break;
648
+ case PM_STRING_NODE: {
649
+ const pm_string_t *unescaped = &((const pm_string_node_t *) node)->unescaped;
650
+ pm_buffer_append_byte(buffer, '"');
651
+ pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_RUBY);
652
+ pm_buffer_append_byte(buffer, '"');
653
+ break;
654
+ }
655
+ case PM_SYMBOL_NODE: {
656
+ const pm_string_t *unescaped = &((const pm_symbol_node_t *) node)->unescaped;
657
+ pm_buffer_append_byte(buffer, ':');
658
+ pm_buffer_append_source(buffer, pm_string_source(unescaped), pm_string_length(unescaped), PM_BUFFER_ESCAPING_RUBY);
659
+ break;
660
+ }
661
+ case PM_TRUE_NODE:
662
+ pm_buffer_append_string(buffer, "true", 4);
663
+ break;
664
+ default:
665
+ assert(false && "unreachable");
666
+ break;
667
+ }
668
+ }
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
+ }