prism 1.9.0 → 2.0.0.rc2

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 (244) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +88 -0
  3. data/CODE_OF_CONDUCT.md +1 -1
  4. data/LICENSE.md +1 -1
  5. data/Makefile +41 -12
  6. data/README.md +35 -97
  7. data/config.yml +174 -161
  8. data/docs/build_system.md +2 -2
  9. data/docs/configuration.md +5 -4
  10. data/docs/encoding.md +2 -2
  11. data/docs/fuzzing.md +4 -11
  12. data/docs/javascript.md +19 -4
  13. data/docs/mapping.md +1 -1
  14. data/docs/releasing.md +77 -5
  15. data/docs/relocation.md +2 -4
  16. data/docs/serialization.md +22 -24
  17. data/docs/testing.md +46 -29
  18. data/ext/prism/api_node.c +1461 -1401
  19. data/ext/prism/extconf.rb +3 -4
  20. data/ext/prism/extension.c +796 -422
  21. data/ext/prism/extension.h +3 -3
  22. data/include/prism/arena.h +37 -0
  23. data/include/prism/ast.h +2727 -610
  24. data/include/prism/buffer.h +52 -0
  25. data/include/prism/comments.h +55 -0
  26. data/include/prism/compiler/accel.h +26 -0
  27. data/include/prism/compiler/align.h +36 -0
  28. data/include/prism/compiler/assume.h +25 -0
  29. data/include/prism/compiler/bswap.h +46 -0
  30. data/include/prism/compiler/endianness.h +34 -0
  31. data/include/prism/compiler/exported.h +24 -0
  32. data/include/prism/compiler/fallthrough.h +22 -0
  33. data/include/prism/compiler/filesystem.h +32 -0
  34. data/include/prism/compiler/flex_array.h +19 -0
  35. data/include/prism/compiler/force_inline.h +21 -0
  36. data/include/prism/compiler/format.h +25 -0
  37. data/include/prism/compiler/inline.h +17 -0
  38. data/include/prism/compiler/nodiscard.h +22 -0
  39. data/include/prism/compiler/nonnull.h +18 -0
  40. data/include/prism/compiler/unused.h +18 -0
  41. data/include/prism/constant_pool.h +81 -0
  42. data/include/prism/diagnostic.h +38 -403
  43. data/include/prism/errors_format.h +53 -0
  44. data/include/prism/excludes.h +29 -0
  45. data/include/prism/integer.h +41 -0
  46. data/include/prism/internal/allocator.h +68 -0
  47. data/include/prism/internal/allocator_debug.h +88 -0
  48. data/include/prism/internal/arena.h +108 -0
  49. data/include/prism/internal/bit.h +42 -0
  50. data/include/prism/internal/buffer.h +94 -0
  51. data/include/prism/internal/char.h +145 -0
  52. data/include/prism/internal/comments.h +53 -0
  53. data/include/prism/internal/constant_pool.h +152 -0
  54. data/include/prism/internal/diagnostic.h +389 -0
  55. data/include/prism/{encoding.h → internal/encoding.h} +27 -68
  56. data/include/prism/internal/integer.h +68 -0
  57. data/include/prism/internal/isinf.h +16 -0
  58. data/include/prism/internal/line_offset_list.h +34 -0
  59. data/include/prism/{util/pm_list.h → internal/list.h} +11 -52
  60. data/include/prism/internal/magic_comments.h +23 -0
  61. data/include/prism/internal/memchr.h +15 -0
  62. data/include/prism/internal/node.h +38 -0
  63. data/include/prism/internal/options.h +232 -0
  64. data/include/prism/internal/parser.h +1004 -0
  65. data/include/prism/internal/regexp.h +26 -0
  66. data/include/prism/internal/serialize.h +39 -0
  67. data/include/prism/internal/source.h +72 -0
  68. data/include/prism/{static_literals.h → internal/static_literals.h} +26 -47
  69. data/include/prism/internal/stringy.h +35 -0
  70. data/include/prism/{util/pm_strncasecmp.h → internal/strncasecmp.h} +3 -17
  71. data/include/prism/{util/pm_strpbrk.h → internal/strpbrk.h} +7 -20
  72. data/include/prism/internal/tokens.h +11 -0
  73. data/include/prism/internal/xxh3.h +16 -0
  74. data/include/prism/json.h +32 -0
  75. data/include/prism/line_offset_list.h +61 -0
  76. data/include/prism/magic_comments.h +35 -0
  77. data/include/prism/node.h +18 -53
  78. data/include/prism/options.h +146 -280
  79. data/include/prism/parser.h +356 -867
  80. data/include/prism/prettyprint.h +6 -9
  81. data/include/prism/serialize.h +115 -0
  82. data/include/prism/source.h +155 -0
  83. data/include/prism/stream.h +28 -0
  84. data/include/prism/string_query.h +74 -0
  85. data/include/prism/stringy.h +72 -0
  86. data/include/prism/version.h +12 -3
  87. data/include/prism.h +43 -320
  88. data/lib/prism/compiler.rb +319 -305
  89. data/lib/prism/desugar_compiler.rb +105 -34
  90. data/lib/prism/dispatcher.rb +646 -473
  91. data/lib/prism/dot_visitor.rb +592 -511
  92. data/lib/prism/dsl.rb +391 -25
  93. data/lib/prism/ffi.rb +258 -90
  94. data/lib/prism/inspect_visitor.rb +357 -325
  95. data/lib/prism/lex_compat.rb +254 -212
  96. data/lib/prism/mutation_compiler.rb +313 -305
  97. data/lib/prism/node.rb +20966 -6992
  98. data/lib/prism/node_ext.rb +180 -187
  99. data/lib/prism/node_find.rb +215 -0
  100. data/lib/prism/parse_result/errors.rb +7 -1
  101. data/lib/prism/parse_result/newlines.rb +276 -8
  102. data/lib/prism/parse_result.rb +468 -139
  103. data/lib/prism/pattern.rb +65 -20
  104. data/lib/prism/reflection.rb +23 -12
  105. data/lib/prism/relocation.rb +149 -87
  106. data/lib/prism/serialize.rb +3460 -539
  107. data/lib/prism/string_query.rb +24 -1
  108. data/lib/prism/translation/parser/builder.rb +10 -2
  109. data/lib/prism/translation/parser/compiler.rb +44 -52
  110. data/lib/prism/translation/parser/lexer.rb +41 -92
  111. data/lib/prism/translation/parser.rb +1 -1
  112. data/lib/prism/translation/parser_current.rb +1 -1
  113. data/lib/prism/translation/ripper/lexer.rb +8 -10
  114. data/lib/prism/translation/ripper/sexp.rb +2 -10
  115. data/lib/prism/translation/ripper/shim.rb +2 -0
  116. data/lib/prism/translation/ripper.rb +967 -209
  117. data/lib/prism/translation/ruby_parser.rb +73 -316
  118. data/lib/prism/translation.rb +2 -0
  119. data/lib/prism/visitor.rb +632 -5
  120. data/lib/prism.rb +79 -9
  121. data/prism.gemspec +128 -62
  122. data/rbi/generated/prism/compiler.rbi +489 -0
  123. data/rbi/generated/prism/desugar_compiler.rbi +306 -0
  124. data/rbi/generated/prism/dispatcher.rbi +1138 -0
  125. data/rbi/generated/prism/dot_visitor.rbi +618 -0
  126. data/rbi/generated/prism/dsl.rbi +751 -0
  127. data/rbi/generated/prism/inspect_visitor.rbi +503 -0
  128. data/rbi/generated/prism/lex_compat.rbi +162 -0
  129. data/rbi/generated/prism/mutation_compiler.rbi +464 -0
  130. data/rbi/generated/prism/node.rbi +14407 -0
  131. data/rbi/generated/prism/node_ext.rbi +259 -0
  132. data/rbi/generated/prism/node_find.rbi +79 -0
  133. data/rbi/generated/prism/parse_result/errors.rbi +21 -0
  134. data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
  135. data/rbi/generated/prism/parse_result.rbi +776 -0
  136. data/rbi/generated/prism/pattern.rbi +134 -0
  137. data/rbi/generated/prism/reflection.rbi +89 -0
  138. data/rbi/generated/prism/relocation.rbi +329 -0
  139. data/rbi/generated/prism/serialize.rbi +184 -0
  140. data/rbi/generated/prism/string_query.rbi +43 -0
  141. data/rbi/generated/prism/translation.rbi +7 -0
  142. data/rbi/generated/prism/visitor.rbi +650 -0
  143. data/rbi/generated/prism.rbi +86 -0
  144. data/rbi/prism/translation/parser_versions.rbi +5 -0
  145. data/rbi/rubyvm/node_find.rbi +14 -0
  146. data/sig/generated/prism/compiler.rbs +492 -0
  147. data/sig/generated/prism/desugar_compiler.rbs +305 -0
  148. data/sig/generated/prism/dispatcher.rbs +1292 -0
  149. data/sig/generated/prism/dot_visitor.rbs +635 -0
  150. data/sig/generated/prism/dsl.rbs +928 -0
  151. data/sig/generated/prism/inspect_visitor.rbs +509 -0
  152. data/sig/generated/prism/lex_compat.rbs +166 -0
  153. data/sig/{prism → generated/prism}/mutation_compiler.rbs +309 -4
  154. data/sig/generated/prism/node.rbs +20896 -0
  155. data/sig/generated/prism/node_ext.rbs +290 -0
  156. data/sig/generated/prism/node_find.rbs +90 -0
  157. data/sig/generated/prism/parse_result/errors.rbs +22 -0
  158. data/sig/generated/prism/parse_result/newlines.rbs +223 -0
  159. data/sig/generated/prism/parse_result.rbs +871 -0
  160. data/sig/generated/prism/pattern.rbs +156 -0
  161. data/sig/generated/prism/reflection.rbs +101 -0
  162. data/sig/generated/prism/relocation.rbs +428 -0
  163. data/sig/generated/prism/serialize.rbs +198 -0
  164. data/sig/generated/prism/string_query.rbs +43 -0
  165. data/sig/generated/prism/translation.rbs +8 -0
  166. data/sig/generated/prism/visitor.rbs +1111 -0
  167. data/sig/generated/prism.rbs +84 -0
  168. data/sig/manifest.yaml +2 -0
  169. data/src/arena.c +117 -0
  170. data/src/{util/pm_buffer.c → buffer.c} +53 -21
  171. data/src/{util/pm_char.c → char.c} +28 -57
  172. data/src/comments.c +454 -0
  173. data/src/constant_pool.c +406 -0
  174. data/src/diagnostic.c +97 -65
  175. data/src/encoding.c +8 -3
  176. data/src/errors_format.c +519 -0
  177. data/src/{util/pm_integer.c → integer.c} +33 -22
  178. data/src/json.c +5733 -0
  179. data/src/line_offset_list.c +100 -0
  180. data/src/list.c +24 -0
  181. data/src/{util/pm_memchr.c → memchr.c} +8 -6
  182. data/src/node.c +3504 -7674
  183. data/src/options.c +162 -49
  184. data/src/parser.c +367 -0
  185. data/src/prettyprint.c +421 -354
  186. data/src/prism.c +8402 -7161
  187. data/src/regexp.c +961 -45
  188. data/src/serialize.c +460 -424
  189. data/src/source.c +518 -0
  190. data/src/static_literals.c +111 -43
  191. data/src/string_query.c +180 -0
  192. data/src/stringy.c +91 -0
  193. data/src/{util/pm_strncasecmp.c → strncasecmp.c} +5 -4
  194. data/src/strpbrk.c +447 -0
  195. data/src/{token_type.c → tokens.c} +39 -18
  196. data/src/xxh3.c +292 -0
  197. metadata +128 -63
  198. data/ext/prism/api_pack.c +0 -276
  199. data/include/prism/defines.h +0 -260
  200. data/include/prism/pack.h +0 -163
  201. data/include/prism/regexp.h +0 -43
  202. data/include/prism/util/pm_buffer.h +0 -236
  203. data/include/prism/util/pm_char.h +0 -204
  204. data/include/prism/util/pm_constant_pool.h +0 -218
  205. data/include/prism/util/pm_integer.h +0 -130
  206. data/include/prism/util/pm_memchr.h +0 -29
  207. data/include/prism/util/pm_newline_list.h +0 -113
  208. data/include/prism/util/pm_string.h +0 -200
  209. data/lib/prism/pack.rb +0 -230
  210. data/lib/prism/parse_result/comments.rb +0 -188
  211. data/rbi/prism/compiler.rbi +0 -12
  212. data/rbi/prism/dsl.rbi +0 -524
  213. data/rbi/prism/inspect_visitor.rbi +0 -12
  214. data/rbi/prism/node.rbi +0 -8750
  215. data/rbi/prism/node_ext.rbi +0 -107
  216. data/rbi/prism/parse_result.rbi +0 -404
  217. data/rbi/prism/reflection.rbi +0 -58
  218. data/rbi/prism/string_query.rbi +0 -12
  219. data/rbi/prism/visitor.rbi +0 -473
  220. data/rbi/prism.rbi +0 -63
  221. data/sig/prism/compiler.rbs +0 -9
  222. data/sig/prism/dispatcher.rbs +0 -19
  223. data/sig/prism/dot_visitor.rbs +0 -6
  224. data/sig/prism/dsl.rbs +0 -351
  225. data/sig/prism/inspect_visitor.rbs +0 -22
  226. data/sig/prism/lex_compat.rbs +0 -10
  227. data/sig/prism/node.rbs +0 -4042
  228. data/sig/prism/node_ext.rbs +0 -149
  229. data/sig/prism/pack.rbs +0 -43
  230. data/sig/prism/parse_result/comments.rbs +0 -38
  231. data/sig/prism/parse_result.rbs +0 -197
  232. data/sig/prism/pattern.rbs +0 -13
  233. data/sig/prism/reflection.rbs +0 -50
  234. data/sig/prism/relocation.rbs +0 -185
  235. data/sig/prism/serialize.rbs +0 -8
  236. data/sig/prism/string_query.rbs +0 -11
  237. data/sig/prism/visitor.rbs +0 -169
  238. data/sig/prism.rbs +0 -272
  239. data/src/pack.c +0 -509
  240. data/src/util/pm_constant_pool.c +0 -342
  241. data/src/util/pm_list.c +0 -49
  242. data/src/util/pm_newline_list.c +0 -125
  243. data/src/util/pm_string.c +0 -381
  244. data/src/util/pm_strpbrk.c +0 -206
@@ -1,4 +1,19 @@
1
- #include "prism/static_literals.h"
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>
2
17
 
3
18
  /**
4
19
  * A small struct used for passing around a subset of the information that is
@@ -7,16 +22,19 @@
7
22
  */
8
23
  typedef struct {
9
24
  /** The list of newline offsets to use to calculate line numbers. */
10
- const pm_newline_list_t *newline_list;
25
+ const pm_line_offset_list_t *line_offsets;
26
+
27
+ /** The start of the source being parsed. */
28
+ const uint8_t *start;
11
29
 
12
30
  /** The line number that the parser starts on. */
13
31
  int32_t start_line;
14
32
 
15
- /** The name of the encoding that the parser is using. */
16
- const char *encoding_name;
33
+ /** The encoding that the parser is using. */
34
+ const pm_encoding_t *encoding;
17
35
  } pm_static_literals_metadata_t;
18
36
 
19
- static inline uint32_t
37
+ static PRISM_INLINE uint32_t
20
38
  murmur_scramble(uint32_t value) {
21
39
  value *= 0xcc9e2d51;
22
40
  value = (value << 15) | (value >> 17);
@@ -77,6 +95,39 @@ integer_hash(const pm_integer_t *integer) {
77
95
  return hash;
78
96
  }
79
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
+
80
131
  /**
81
132
  * Return the hash of the given node. It is important that nodes that have
82
133
  * equivalent static literal values have the same hash. This is because we use
@@ -92,7 +143,7 @@ node_hash(const pm_static_literals_metadata_t *metadata, const pm_node_t *node)
92
143
  }
93
144
  case PM_SOURCE_LINE_NODE: {
94
145
  // Source lines hash their line number.
95
- const pm_line_column_t line_column = pm_newline_list_line_column(metadata->newline_list, node->location.start, metadata->start_line);
146
+ const pm_line_column_t line_column = pm_line_offset_list_line_column(metadata->line_offsets, node->location.start, metadata->start_line);
96
147
  const int32_t *value = &line_column.line;
97
148
  return murmur_hash((const uint8_t *) value, sizeof(int32_t));
98
149
  }
@@ -117,11 +168,7 @@ node_hash(const pm_static_literals_metadata_t *metadata, const pm_node_t *node)
117
168
  // Strings hash their value and mix in their flags so that different
118
169
  // encodings are not considered equal.
119
170
  const pm_string_t *value = &((const pm_string_node_t *) node)->unescaped;
120
-
121
- pm_node_flags_t flags = node->flags;
122
- flags &= (PM_STRING_FLAGS_FORCED_BINARY_ENCODING | PM_STRING_FLAGS_FORCED_UTF8_ENCODING);
123
-
124
- return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) flags);
171
+ return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) node_encoding_flags(metadata, node));
125
172
  }
126
173
  case PM_SOURCE_FILE_NODE: {
127
174
  // Source files hash their value and mix in their flags so that
@@ -139,7 +186,7 @@ node_hash(const pm_static_literals_metadata_t *metadata, const pm_node_t *node)
139
186
  // Symbols hash their value and mix in their flags so that different
140
187
  // encodings are not considered equal.
141
188
  const pm_string_t *value = &((const pm_symbol_node_t *) node)->unescaped;
142
- return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) node->flags);
189
+ return murmur_hash(pm_string_source(value), pm_string_length(value) * sizeof(uint8_t)) ^ murmur_scramble((uint32_t) node_encoding_flags(metadata, node));
143
190
  }
144
191
  default:
145
192
  assert(false && "unreachable");
@@ -161,7 +208,7 @@ pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *m
161
208
  // First, allocate space for the new node list.
162
209
  uint32_t new_capacity = hash->capacity == 0 ? 4 : hash->capacity * 2;
163
210
  pm_node_t **new_nodes = xcalloc(new_capacity, sizeof(pm_node_t *));
164
- if (new_nodes == NULL) return NULL;
211
+ if (new_nodes == NULL) abort();
165
212
 
166
213
  // It turns out to be more efficient to mask the hash value than to use
167
214
  // the modulo operator. Because our capacities are always powers of two,
@@ -174,13 +221,16 @@ pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *m
174
221
  pm_node_t *node = hash->nodes[index];
175
222
 
176
223
  if (node != NULL) {
177
- uint32_t index = node_hash(metadata, node) & mask;
178
- new_nodes[index] = node;
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;
179
229
  }
180
230
  }
181
231
 
182
232
  // Finally, free the old node list and update the hash.
183
- xfree(hash->nodes);
233
+ xfree_sized(hash->nodes, hash->capacity * sizeof(pm_node_t *));
184
234
  hash->nodes = new_nodes;
185
235
  hash->capacity = new_capacity;
186
236
  }
@@ -218,7 +268,7 @@ pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *m
218
268
  */
219
269
  static void
220
270
  pm_node_hash_free(pm_node_hash_t *hash) {
221
- if (hash->capacity > 0) xfree(hash->nodes);
271
+ if (hash->capacity > 0) xfree_sized(hash->nodes, hash->capacity * sizeof(pm_node_t *));
222
272
  }
223
273
 
224
274
  /**
@@ -240,7 +290,7 @@ pm_int64_value(const pm_static_literals_metadata_t *metadata, const pm_node_t *n
240
290
  return integer->negative ? -value : value;
241
291
  }
242
292
  case PM_SOURCE_LINE_NODE:
243
- return (int64_t) pm_newline_list_line_column(metadata->newline_list, node->location.start, metadata->start_line).line;
293
+ return (int64_t) pm_line_offset_list_line_column(metadata->line_offsets, node->location.start, metadata->start_line).line;
244
294
  default:
245
295
  assert(false && "unreachable");
246
296
  return 0;
@@ -268,7 +318,7 @@ pm_compare_integer_nodes(const pm_static_literals_metadata_t *metadata, const pm
268
318
  * A comparison function for comparing two FloatNode instances.
269
319
  */
270
320
  static int
271
- pm_compare_float_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
321
+ pm_compare_float_nodes(PRISM_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
272
322
  const double left_value = ((const pm_float_node_t *) left)->value;
273
323
  const double right_value = ((const pm_float_node_t *) right)->value;
274
324
  return PM_NUMERIC_COMPARISON(left_value, right_value);
@@ -327,17 +377,28 @@ pm_string_value(const pm_node_t *node) {
327
377
  * A comparison function for comparing two nodes that have attached strings.
328
378
  */
329
379
  static int
330
- pm_compare_string_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
380
+ pm_compare_string_nodes(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
331
381
  const pm_string_t *left_string = pm_string_value(left);
332
382
  const pm_string_t *right_string = pm_string_value(right);
333
- return pm_string_compare(left_string, right_string);
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);
334
395
  }
335
396
 
336
397
  /**
337
398
  * A comparison function for comparing two RegularExpressionNode instances.
338
399
  */
339
400
  static int
340
- pm_compare_regular_expression_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right) {
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) {
341
402
  const pm_regular_expression_node_t *left_regexp = (const pm_regular_expression_node_t *) left;
342
403
  const pm_regular_expression_node_t *right_regexp = (const pm_regular_expression_node_t *) right;
343
404
 
@@ -353,16 +414,17 @@ pm_compare_regular_expression_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_liter
353
414
  * Add a node to the set of static literals.
354
415
  */
355
416
  pm_node_t *
356
- pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line, pm_static_literals_t *literals, pm_node_t *node, bool replace) {
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) {
357
418
  switch (PM_NODE_TYPE(node)) {
358
419
  case PM_INTEGER_NODE:
359
420
  case PM_SOURCE_LINE_NODE:
360
421
  return pm_node_hash_insert(
361
422
  &literals->integer_nodes,
362
423
  &(pm_static_literals_metadata_t) {
363
- .newline_list = newline_list,
424
+ .line_offsets = line_offsets,
425
+ .start = start,
364
426
  .start_line = start_line,
365
- .encoding_name = NULL
427
+ .encoding = encoding
366
428
  },
367
429
  node,
368
430
  replace,
@@ -372,9 +434,10 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line
372
434
  return pm_node_hash_insert(
373
435
  &literals->float_nodes,
374
436
  &(pm_static_literals_metadata_t) {
375
- .newline_list = newline_list,
437
+ .line_offsets = line_offsets,
438
+ .start = start,
376
439
  .start_line = start_line,
377
- .encoding_name = NULL
440
+ .encoding = encoding
378
441
  },
379
442
  node,
380
443
  replace,
@@ -385,9 +448,10 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line
385
448
  return pm_node_hash_insert(
386
449
  &literals->number_nodes,
387
450
  &(pm_static_literals_metadata_t) {
388
- .newline_list = newline_list,
451
+ .line_offsets = line_offsets,
452
+ .start = start,
389
453
  .start_line = start_line,
390
- .encoding_name = NULL
454
+ .encoding = encoding
391
455
  },
392
456
  node,
393
457
  replace,
@@ -398,9 +462,10 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line
398
462
  return pm_node_hash_insert(
399
463
  &literals->string_nodes,
400
464
  &(pm_static_literals_metadata_t) {
401
- .newline_list = newline_list,
465
+ .line_offsets = line_offsets,
466
+ .start = start,
402
467
  .start_line = start_line,
403
- .encoding_name = NULL
468
+ .encoding = encoding
404
469
  },
405
470
  node,
406
471
  replace,
@@ -410,9 +475,10 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line
410
475
  return pm_node_hash_insert(
411
476
  &literals->regexp_nodes,
412
477
  &(pm_static_literals_metadata_t) {
413
- .newline_list = newline_list,
478
+ .line_offsets = line_offsets,
479
+ .start = start,
414
480
  .start_line = start_line,
415
- .encoding_name = NULL
481
+ .encoding = encoding
416
482
  },
417
483
  node,
418
484
  replace,
@@ -422,9 +488,10 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line
422
488
  return pm_node_hash_insert(
423
489
  &literals->symbol_nodes,
424
490
  &(pm_static_literals_metadata_t) {
425
- .newline_list = newline_list,
491
+ .line_offsets = line_offsets,
492
+ .start = start,
426
493
  .start_line = start_line,
427
- .encoding_name = NULL
494
+ .encoding = encoding
428
495
  },
429
496
  node,
430
497
  replace,
@@ -492,7 +559,7 @@ pm_static_literal_positive_p(const pm_node_t *node) {
492
559
  /**
493
560
  * Create a string-based representation of the given static literal.
494
561
  */
495
- static inline void
562
+ static PRISM_INLINE void
496
563
  pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_metadata_t *metadata, const pm_node_t *node) {
497
564
  switch (PM_NODE_TYPE(node)) {
498
565
  case PM_FALSE_NODE:
@@ -502,12 +569,12 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
502
569
  const double value = ((const pm_float_node_t *) node)->value;
503
570
 
504
571
  if (PRISM_ISINF(value)) {
505
- if (*node->location.start == '-') {
572
+ if (metadata->start[node->location.start] == '-') {
506
573
  pm_buffer_append_byte(buffer, '-');
507
574
  }
508
575
  pm_buffer_append_string(buffer, "Infinity", 8);
509
576
  } else if (value == 0.0) {
510
- if (*node->location.start == '-') {
577
+ if (metadata->start[node->location.start] == '-') {
511
578
  pm_buffer_append_byte(buffer, '-');
512
579
  }
513
580
  pm_buffer_append_string(buffer, "0.0", 3);
@@ -566,7 +633,7 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
566
633
  break;
567
634
  }
568
635
  case PM_SOURCE_ENCODING_NODE:
569
- pm_buffer_append_format(buffer, "#<Encoding:%s>", metadata->encoding_name);
636
+ pm_buffer_append_format(buffer, "#<Encoding:%s>", metadata->encoding->name);
570
637
  break;
571
638
  case PM_SOURCE_FILE_NODE: {
572
639
  const pm_string_t *filepath = &((const pm_source_file_node_t *) node)->filepath;
@@ -576,7 +643,7 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
576
643
  break;
577
644
  }
578
645
  case PM_SOURCE_LINE_NODE:
579
- pm_buffer_append_format(buffer, "%d", pm_newline_list_line_column(metadata->newline_list, node->location.start, metadata->start_line).line);
646
+ pm_buffer_append_format(buffer, "%d", pm_line_offset_list_line_column(metadata->line_offsets, node->location.start, metadata->start_line).line);
580
647
  break;
581
648
  case PM_STRING_NODE: {
582
649
  const pm_string_t *unescaped = &((const pm_string_node_t *) node)->unescaped;
@@ -604,13 +671,14 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
604
671
  * Create a string-based representation of the given static literal.
605
672
  */
606
673
  void
607
- pm_static_literal_inspect(pm_buffer_t *buffer, const pm_newline_list_t *newline_list, int32_t start_line, const char *encoding_name, const pm_node_t *node) {
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) {
608
675
  pm_static_literal_inspect_node(
609
676
  buffer,
610
677
  &(pm_static_literals_metadata_t) {
611
- .newline_list = newline_list,
678
+ .line_offsets = line_offsets,
679
+ .start = start,
612
680
  .start_line = start_line,
613
- .encoding_name = encoding_name
681
+ .encoding = encoding
614
682
  },
615
683
  node
616
684
  );
@@ -0,0 +1,180 @@
1
+ #include "prism/string_query.h"
2
+
3
+ #include "prism/internal/char.h"
4
+ #include "prism/internal/encoding.h"
5
+ #include "prism/internal/parser.h"
6
+
7
+ #include <assert.h>
8
+ #include <string.h>
9
+
10
+ /** The category of slice returned from pm_slice_type. */
11
+ typedef enum {
12
+ /** Returned when the given encoding name is invalid. */
13
+ PM_SLICE_TYPE_ERROR = -1,
14
+
15
+ /** Returned when no other types apply to the slice. */
16
+ PM_SLICE_TYPE_NONE,
17
+
18
+ /** Returned when the slice is a valid local variable name. */
19
+ PM_SLICE_TYPE_LOCAL,
20
+
21
+ /** Returned when the slice is a valid constant name. */
22
+ PM_SLICE_TYPE_CONSTANT,
23
+
24
+ /** Returned when the slice is a valid method name. */
25
+ PM_SLICE_TYPE_METHOD_NAME
26
+ } pm_slice_type_t;
27
+
28
+ /**
29
+ * Check that the slice is a valid local variable name or constant.
30
+ */
31
+ static pm_slice_type_t
32
+ pm_slice_type(const uint8_t *source, size_t length, const char *encoding_name) {
33
+ // first, get the right encoding object
34
+ const pm_encoding_t *encoding = pm_encoding_find((const uint8_t *) encoding_name, (const uint8_t *) (encoding_name + strlen(encoding_name)));
35
+ if (encoding == NULL) return PM_SLICE_TYPE_ERROR;
36
+
37
+ // check that there is at least one character
38
+ if (length == 0) return PM_SLICE_TYPE_NONE;
39
+
40
+ size_t width;
41
+ if ((width = encoding->alpha_char(source, (ptrdiff_t) length)) != 0) {
42
+ // valid because alphabetical
43
+ } else if (*source == '_') {
44
+ // valid because underscore
45
+ width = 1;
46
+ } else if ((*source >= 0x80) && ((width = encoding->char_width(source, (ptrdiff_t) length)) > 0)) {
47
+ // valid because multibyte
48
+ } else {
49
+ // invalid because no match
50
+ return PM_SLICE_TYPE_NONE;
51
+ }
52
+
53
+ // determine the type of the slice based on the first character
54
+ const uint8_t *end = source + length;
55
+ pm_slice_type_t result = encoding->isupper_char(source, end - source) ? PM_SLICE_TYPE_CONSTANT : PM_SLICE_TYPE_LOCAL;
56
+
57
+ // next, iterate through all of the bytes of the string to ensure that they
58
+ // are all valid identifier characters
59
+ source += width;
60
+
61
+ while (source < end) {
62
+ if ((width = encoding->alnum_char(source, end - source)) != 0) {
63
+ // valid because alphanumeric
64
+ source += width;
65
+ } else if (*source == '_') {
66
+ // valid because underscore
67
+ source++;
68
+ } else if ((*source >= 0x80) && ((width = encoding->char_width(source, end - source)) > 0)) {
69
+ // valid because multibyte
70
+ source += width;
71
+ } else {
72
+ // invalid because no match
73
+ break;
74
+ }
75
+ }
76
+
77
+ // accept a ! or ? at the end of the slice as a method name
78
+ if (*source == '!' || *source == '?' || *source == '=') {
79
+ source++;
80
+ result = PM_SLICE_TYPE_METHOD_NAME;
81
+ }
82
+
83
+ // valid if we are at the end of the slice
84
+ return source == end ? result : PM_SLICE_TYPE_NONE;
85
+ }
86
+
87
+ /**
88
+ * Check that the slice is a valid local variable name.
89
+ */
90
+ pm_string_query_t
91
+ pm_string_query_local(const uint8_t *source, size_t length, const char *encoding_name) {
92
+ switch (pm_slice_type(source, length, encoding_name)) {
93
+ case PM_SLICE_TYPE_ERROR:
94
+ return PM_STRING_QUERY_ERROR;
95
+ case PM_SLICE_TYPE_NONE:
96
+ case PM_SLICE_TYPE_CONSTANT:
97
+ case PM_SLICE_TYPE_METHOD_NAME:
98
+ return PM_STRING_QUERY_FALSE;
99
+ case PM_SLICE_TYPE_LOCAL:
100
+ return PM_STRING_QUERY_TRUE;
101
+ }
102
+
103
+ assert(false && "unreachable");
104
+ return PM_STRING_QUERY_FALSE;
105
+ }
106
+
107
+ /**
108
+ * Check that the slice is a valid constant name.
109
+ */
110
+ pm_string_query_t
111
+ pm_string_query_constant(const uint8_t *source, size_t length, const char *encoding_name) {
112
+ switch (pm_slice_type(source, length, encoding_name)) {
113
+ case PM_SLICE_TYPE_ERROR:
114
+ return PM_STRING_QUERY_ERROR;
115
+ case PM_SLICE_TYPE_NONE:
116
+ case PM_SLICE_TYPE_LOCAL:
117
+ case PM_SLICE_TYPE_METHOD_NAME:
118
+ return PM_STRING_QUERY_FALSE;
119
+ case PM_SLICE_TYPE_CONSTANT:
120
+ return PM_STRING_QUERY_TRUE;
121
+ }
122
+
123
+ assert(false && "unreachable");
124
+ return PM_STRING_QUERY_FALSE;
125
+ }
126
+
127
+ /**
128
+ * Check that the slice is a valid method name.
129
+ */
130
+ pm_string_query_t
131
+ pm_string_query_method_name(const uint8_t *source, size_t length, const char *encoding_name) {
132
+ #define B(p) ((p) ? PM_STRING_QUERY_TRUE : PM_STRING_QUERY_FALSE)
133
+ #define C1(c) (*source == c)
134
+ #define C2(s) (memcmp(source, s, 2) == 0)
135
+ #define C3(s) (memcmp(source, s, 3) == 0)
136
+
137
+ switch (pm_slice_type(source, length, encoding_name)) {
138
+ case PM_SLICE_TYPE_ERROR:
139
+ return PM_STRING_QUERY_ERROR;
140
+ case PM_SLICE_TYPE_NONE:
141
+ break;
142
+ case PM_SLICE_TYPE_LOCAL:
143
+ // numbered parameters are not valid method names
144
+ return B((length != 2) || (source[0] != '_') || (source[1] == '0') || !pm_char_is_decimal_digit(source[1]));
145
+ case PM_SLICE_TYPE_CONSTANT:
146
+ // all constants are valid method names
147
+ case PM_SLICE_TYPE_METHOD_NAME:
148
+ // all method names are valid method names
149
+ return PM_STRING_QUERY_TRUE;
150
+ }
151
+
152
+ switch (length) {
153
+ case 1:
154
+ return B(C1('&') || C1('`') || C1('!') || C1('^') || C1('>') || C1('<') || C1('-') || C1('%') || C1('|') || C1('+') || C1('/') || C1('*') || C1('~'));
155
+ case 2:
156
+ return B(C2("!=") || C2("!~") || C2("[]") || C2("==") || C2("=~") || C2(">=") || C2(">>") || C2("<=") || C2("<<") || C2("**"));
157
+ case 3:
158
+ return B(C3("===") || C3("<=>") || C3("[]="));
159
+ default:
160
+ return PM_STRING_QUERY_FALSE;
161
+ }
162
+
163
+ #undef B
164
+ #undef C1
165
+ #undef C2
166
+ #undef C3
167
+ }
168
+
169
+ /**
170
+ * Check that the slice is a keyword. Keywords are spelled in ASCII, so any
171
+ * ASCII-compatible encoding spells them with the same bytes.
172
+ */
173
+ pm_string_query_t
174
+ pm_string_query_keyword(const uint8_t *source, size_t length, const char *encoding_name) {
175
+ if (pm_encoding_find((const uint8_t *) encoding_name, (const uint8_t *) (encoding_name + strlen(encoding_name))) == NULL) {
176
+ return PM_STRING_QUERY_ERROR;
177
+ }
178
+
179
+ return pm_keyword_p(source, source + length) ? PM_STRING_QUERY_TRUE : PM_STRING_QUERY_FALSE;
180
+ }
data/src/stringy.c ADDED
@@ -0,0 +1,91 @@
1
+ #include "prism/internal/stringy.h"
2
+
3
+ #include "prism/internal/allocator.h"
4
+
5
+ #include <assert.h>
6
+ #include <stdlib.h>
7
+ #include <string.h>
8
+
9
+ /**
10
+ * Initialize a shared string that is based on initial input.
11
+ */
12
+ void
13
+ pm_string_shared_init(pm_string_t *string, const uint8_t *start, const uint8_t *end) {
14
+ assert(start <= end);
15
+
16
+ *string = (pm_string_t) {
17
+ .type = PM_STRING_SHARED,
18
+ .source = start,
19
+ .length = (size_t) (end - start)
20
+ };
21
+ }
22
+
23
+ /**
24
+ * Initialize an owned string that is responsible for freeing allocated memory.
25
+ */
26
+ void
27
+ pm_string_owned_init(pm_string_t *string, uint8_t *source, size_t length) {
28
+ *string = (pm_string_t) {
29
+ .type = PM_STRING_OWNED,
30
+ .source = source,
31
+ .length = length
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Initialize a constant string that doesn't own its memory source.
37
+ */
38
+ void
39
+ pm_string_constant_init(pm_string_t *string, const char *source, size_t length) {
40
+ *string = (pm_string_t) {
41
+ .type = PM_STRING_CONSTANT,
42
+ .source = (const uint8_t *) source,
43
+ .length = length
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Compare the underlying lengths and bytes of two strings. Returns 0 if the
49
+ * strings are equal, a negative number if the left string is less than the
50
+ * right string, and a positive number if the left string is greater than the
51
+ * right string.
52
+ */
53
+ int
54
+ pm_string_compare(const pm_string_t *left, const pm_string_t *right) {
55
+ size_t left_length = pm_string_length(left);
56
+ size_t right_length = pm_string_length(right);
57
+
58
+ if (left_length < right_length) {
59
+ return -1;
60
+ } else if (left_length > right_length) {
61
+ return 1;
62
+ }
63
+
64
+ return memcmp(pm_string_source(left), pm_string_source(right), left_length);
65
+ }
66
+
67
+ /**
68
+ * Returns the length associated with the string.
69
+ */
70
+ size_t
71
+ pm_string_length(const pm_string_t *string) {
72
+ return string->length;
73
+ }
74
+
75
+ /**
76
+ * Returns the start pointer associated with the string.
77
+ */
78
+ const uint8_t *
79
+ pm_string_source(const pm_string_t *string) {
80
+ return string->source;
81
+ }
82
+
83
+ /**
84
+ * Free the associated memory of the given string.
85
+ */
86
+ void
87
+ pm_string_cleanup(pm_string_t *string) {
88
+ if (string->type == PM_STRING_OWNED) {
89
+ xfree_sized((void *) string->source, string->length);
90
+ }
91
+ }
@@ -1,11 +1,12 @@
1
- #include "prism/util/pm_strncasecmp.h"
1
+ #include "prism/internal/strncasecmp.h"
2
+
3
+ #include "prism/compiler/inline.h"
2
4
 
3
5
  /**
4
6
  * A locale-insensitive version of `tolower(3)`
5
7
  */
6
- static inline int
7
- pm_tolower(int c)
8
- {
8
+ static PRISM_INLINE int
9
+ pm_tolower(int c) {
9
10
  if ('A' <= c && c <= 'Z') {
10
11
  return c | 0x20;
11
12
  }