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
data/src/strpbrk.c ADDED
@@ -0,0 +1,447 @@
1
+ #include "prism/internal/strpbrk.h"
2
+
3
+ #include "prism/compiler/accel.h"
4
+ #include "prism/compiler/inline.h"
5
+ #include "prism/compiler/unused.h"
6
+
7
+ #include "prism/internal/bit.h"
8
+ #include "prism/internal/diagnostic.h"
9
+ #include "prism/internal/encoding.h"
10
+ #include "prism/internal/parser.h"
11
+
12
+ #include <assert.h>
13
+ #include <stdbool.h>
14
+ #include <string.h>
15
+
16
+ /**
17
+ * Add an invalid multibyte character error to the parser.
18
+ */
19
+ static PRISM_INLINE void
20
+ pm_strpbrk_invalid_multibyte_character(pm_parser_t *parser, uint32_t start, uint32_t length) {
21
+ pm_diagnostic_list_append_format(&parser->metadata_arena, &parser->error_list, start, length, PM_ERR_INVALID_MULTIBYTE_CHARACTER, parser->start[start]);
22
+ }
23
+
24
+ /**
25
+ * Set the explicit encoding for the parser to the current encoding.
26
+ */
27
+ static PRISM_INLINE void
28
+ pm_strpbrk_explicit_encoding_set(pm_parser_t *parser, uint32_t start, uint32_t length) {
29
+ if (parser->explicit_encoding != NULL) {
30
+ if (parser->explicit_encoding == parser->encoding) {
31
+ // Okay, we already locked to this encoding.
32
+ } else if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
33
+ // Not okay, we already found a Unicode escape sequence and this
34
+ // conflicts.
35
+ pm_diagnostic_list_append_format(&parser->metadata_arena, &parser->error_list, start, length, PM_ERR_MIXED_ENCODING, parser->encoding->name);
36
+ } else {
37
+ // Should not be anything else.
38
+ assert(false && "unreachable");
39
+ }
40
+ }
41
+
42
+ parser->explicit_encoding = parser->encoding;
43
+ }
44
+
45
+ /**
46
+ * Scan forward through ASCII bytes looking for a byte that is in the given
47
+ * charset. Returns true if a match was found, storing its offset in *index.
48
+ * Returns false if no match was found, storing the number of ASCII bytes
49
+ * consumed in *index (so the caller can skip past them).
50
+ *
51
+ * All charset characters must be ASCII (< 0x80). The scanner stops at non-ASCII
52
+ * bytes, returning control to the caller's encoding-aware loop.
53
+ *
54
+ * Up to three optimized implementations are selected at compile time, with a
55
+ * no-op fallback for unsupported platforms:
56
+ * 1. NEON — processes 16 bytes per iteration on aarch64.
57
+ * 2. SSE2 — processes 16 bytes per iteration on x86-64.
58
+ * 3. SWAR — little-endian fallback, processes 8 bytes per iteration.
59
+ */
60
+
61
+ #if defined(PRISM_HAS_NEON) || defined(PRISM_HAS_SSE2) || defined(PRISM_HAS_SWAR)
62
+
63
+ /**
64
+ * Update the cached strpbrk lookup tables if the charset has changed. The
65
+ * parser caches the last charset's precomputed tables so that repeated calls
66
+ * with the same breakpoints (the common case during string/regex/list lexing)
67
+ * skip table construction entirely.
68
+ *
69
+ * Builds three structures:
70
+ * - low_lut/high_lut: nibble-based lookup tables for SIMD matching (NEON)
71
+ * - table: 256-bit bitmap for scalar fallback matching (all platforms)
72
+ */
73
+ static PRISM_INLINE void
74
+ pm_strpbrk_cache_update(pm_parser_t *parser, const uint8_t *charset) {
75
+ // The cache key is the full charset buffer (PM_STRPBRK_CACHE_SIZE bytes).
76
+ // Since it is always NUL-padded, a fixed-size comparison covers both
77
+ // content and length.
78
+ if (memcmp(parser->strpbrk_cache.charset, charset, sizeof(parser->strpbrk_cache.charset)) == 0) return;
79
+
80
+ memset(parser->strpbrk_cache.low_lut, 0, sizeof(parser->strpbrk_cache.low_lut));
81
+ memset(parser->strpbrk_cache.high_lut, 0, sizeof(parser->strpbrk_cache.high_lut));
82
+ memset(parser->strpbrk_cache.table, 0, sizeof(parser->strpbrk_cache.table));
83
+
84
+ // Always include NUL in the tables. The slow path uses strchr, which
85
+ // always matches NUL (it finds the C string terminator), so NUL is
86
+ // effectively always a breakpoint. Replicating that here lets the fast
87
+ // scanner handle NUL at full speed instead of bailing to the slow path.
88
+ parser->strpbrk_cache.low_lut[0x00] |= (uint8_t) (1 << 0);
89
+ parser->strpbrk_cache.high_lut[0x00] = (uint8_t) (1 << 0);
90
+ parser->strpbrk_cache.table[0] |= (uint64_t) 1;
91
+
92
+ size_t charset_len = 0;
93
+ for (const uint8_t *c = charset; *c != '\0'; c++) {
94
+ parser->strpbrk_cache.low_lut[*c & 0x0F] |= (uint8_t) (1 << (*c >> 4));
95
+ parser->strpbrk_cache.high_lut[*c >> 4] = (uint8_t) (1 << (*c >> 4));
96
+ parser->strpbrk_cache.table[*c >> 6] |= (uint64_t) 1 << (*c & 0x3F);
97
+ charset_len++;
98
+ }
99
+
100
+ // Store the new charset key, NUL-padded to the full buffer size.
101
+ memcpy(parser->strpbrk_cache.charset, charset, charset_len + 1);
102
+ memset(parser->strpbrk_cache.charset + charset_len + 1, 0, sizeof(parser->strpbrk_cache.charset) - charset_len - 1);
103
+ }
104
+
105
+ #endif
106
+
107
+ #if defined(PRISM_HAS_NEON)
108
+ #include <arm_neon.h>
109
+
110
+ static PRISM_INLINE bool
111
+ scan_strpbrk_ascii(pm_parser_t *parser, const uint8_t *source, size_t maximum, const uint8_t *charset, size_t *index) {
112
+ pm_strpbrk_cache_update(parser, charset);
113
+
114
+ uint8x16_t low_lut = vld1q_u8(parser->strpbrk_cache.low_lut);
115
+ uint8x16_t high_lut = vld1q_u8(parser->strpbrk_cache.high_lut);
116
+ uint8x16_t mask_0f = vdupq_n_u8(0x0F);
117
+ uint8x16_t mask_80 = vdupq_n_u8(0x80);
118
+
119
+ size_t idx = 0;
120
+
121
+ while (idx + 16 <= maximum) {
122
+ uint8x16_t v = vld1q_u8(source + idx);
123
+
124
+ // If any byte has the high bit set, we have non-ASCII data.
125
+ // Return to let the caller's encoding-aware loop handle it.
126
+ if (vmaxvq_u8(vandq_u8(v, mask_80)) != 0) break;
127
+
128
+ uint8x16_t lo_class = vqtbl1q_u8(low_lut, vandq_u8(v, mask_0f));
129
+ uint8x16_t hi_class = vqtbl1q_u8(high_lut, vshrq_n_u8(v, 4));
130
+ uint8x16_t matched = vtstq_u8(lo_class, hi_class);
131
+
132
+ if (vmaxvq_u8(matched) == 0) {
133
+ idx += 16;
134
+ continue;
135
+ }
136
+
137
+ // Find the position of the first matching byte.
138
+ uint64_t lo64 = vgetq_lane_u64(vreinterpretq_u64_u8(matched), 0);
139
+ if (lo64 != 0) {
140
+ *index = idx + pm_ctzll(lo64) / 8;
141
+ return true;
142
+ }
143
+ uint64_t hi64 = vgetq_lane_u64(vreinterpretq_u64_u8(matched), 1);
144
+ *index = idx + 8 + pm_ctzll(hi64) / 8;
145
+ return true;
146
+ }
147
+
148
+ // Scalar tail for remaining < 16 ASCII bytes.
149
+ while (idx < maximum && source[idx] < 0x80) {
150
+ uint8_t byte = source[idx];
151
+ if (parser->strpbrk_cache.table[byte >> 6] & ((uint64_t) 1 << (byte & 0x3F))) {
152
+ *index = idx;
153
+ return true;
154
+ }
155
+ idx++;
156
+ }
157
+
158
+ *index = idx;
159
+ return false;
160
+ }
161
+
162
+ #elif defined(PRISM_HAS_SSE2)
163
+ #include <emmintrin.h>
164
+
165
+ static PRISM_INLINE bool
166
+ scan_strpbrk_ascii(pm_parser_t *parser, const uint8_t *source, size_t maximum, const uint8_t *charset, size_t *index) {
167
+ pm_strpbrk_cache_update(parser, charset);
168
+
169
+ /*
170
+ * SSE2 has no byte shuffle, so the nibble lookup used by NEON is not
171
+ * available. Instead, each charset byte is broadcast into its own vector
172
+ * and compared against the input directly. Charsets are short, so this is
173
+ * a handful of compares per 16 bytes.
174
+ */
175
+ __m128i needles[sizeof(parser->strpbrk_cache.charset)];
176
+ size_t needles_size = 0;
177
+
178
+ for (size_t i = 0; i < sizeof(parser->strpbrk_cache.charset) && parser->strpbrk_cache.charset[i] != '\0'; i++) {
179
+ needles[needles_size++] = _mm_set1_epi8((char) parser->strpbrk_cache.charset[i]);
180
+ }
181
+
182
+ const __m128i zero = _mm_setzero_si128();
183
+ size_t idx = 0;
184
+
185
+ while (idx + 16 <= maximum) {
186
+ __m128i v = _mm_loadu_si128((const __m128i *) (source + idx));
187
+
188
+ /* If any byte has the high bit set, stop. */
189
+ if (_mm_movemask_epi8(v) != 0) break;
190
+
191
+ /* NUL is always a breakpoint, matching pm_strpbrk_cache_update. */
192
+ __m128i matched = _mm_cmpeq_epi8(v, zero);
193
+ for (size_t i = 0; i < needles_size; i++) {
194
+ matched = _mm_or_si128(matched, _mm_cmpeq_epi8(v, needles[i]));
195
+ }
196
+
197
+ int mask = _mm_movemask_epi8(matched);
198
+
199
+ if (mask == 0) {
200
+ idx += 16;
201
+ continue;
202
+ }
203
+
204
+ /* Find the first matching byte. */
205
+ *index = idx + pm_ctzll((uint64_t) (mask & 0xFFFF));
206
+ return true;
207
+ }
208
+
209
+ /* Scalar tail. */
210
+ while (idx < maximum && source[idx] < 0x80) {
211
+ uint8_t byte = source[idx];
212
+ if (parser->strpbrk_cache.table[byte >> 6] & ((uint64_t) 1 << (byte & 0x3F))) {
213
+ *index = idx;
214
+ return true;
215
+ }
216
+ idx++;
217
+ }
218
+
219
+ *index = idx;
220
+ return false;
221
+ }
222
+
223
+ #elif defined(PRISM_HAS_SWAR)
224
+
225
+ static PRISM_INLINE bool
226
+ scan_strpbrk_ascii(pm_parser_t *parser, const uint8_t *source, size_t maximum, const uint8_t *charset, size_t *index) {
227
+ pm_strpbrk_cache_update(parser, charset);
228
+
229
+ static const uint64_t highs = 0x8080808080808080ULL;
230
+ size_t idx = 0;
231
+
232
+ while (idx + 8 <= maximum) {
233
+ uint64_t word;
234
+ memcpy(&word, source + idx, 8);
235
+
236
+ // Bail on any non-ASCII byte.
237
+ if (word & highs) break;
238
+
239
+ // Check each byte against the charset table.
240
+ for (size_t j = 0; j < 8; j++) {
241
+ uint8_t byte = source[idx + j];
242
+ if (parser->strpbrk_cache.table[byte >> 6] & ((uint64_t) 1 << (byte & 0x3F))) {
243
+ *index = idx + j;
244
+ return true;
245
+ }
246
+ }
247
+
248
+ idx += 8;
249
+ }
250
+
251
+ // Scalar tail.
252
+ while (idx < maximum && source[idx] < 0x80) {
253
+ uint8_t byte = source[idx];
254
+ if (parser->strpbrk_cache.table[byte >> 6] & ((uint64_t) 1 << (byte & 0x3F))) {
255
+ *index = idx;
256
+ return true;
257
+ }
258
+ idx++;
259
+ }
260
+
261
+ *index = idx;
262
+ return false;
263
+ }
264
+
265
+ #else
266
+
267
+ static PRISM_INLINE bool
268
+ scan_strpbrk_ascii(PRISM_UNUSED pm_parser_t *parser, PRISM_UNUSED const uint8_t *source, PRISM_UNUSED size_t maximum, PRISM_UNUSED const uint8_t *charset, size_t *index) {
269
+ *index = 0;
270
+ return false;
271
+ }
272
+
273
+ #endif
274
+
275
+ /**
276
+ * This is the default path.
277
+ */
278
+ static PRISM_INLINE const uint8_t *
279
+ pm_strpbrk_utf8(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t index, size_t maximum, bool validate) {
280
+ while (index < maximum) {
281
+ if (strchr((const char *) charset, source[index]) != NULL) {
282
+ return source + index;
283
+ }
284
+
285
+ if (source[index] < 0x80) {
286
+ index++;
287
+ } else {
288
+ size_t width = pm_encoding_utf_8_char_width(source + index, (ptrdiff_t) (maximum - index));
289
+
290
+ if (width > 0) {
291
+ index += width;
292
+ } else if (!validate) {
293
+ index++;
294
+ } else {
295
+ // At this point we know we have an invalid multibyte character.
296
+ // We'll walk forward as far as we can until we find the next
297
+ // valid character so that we don't spam the user with a ton of
298
+ // the same kind of error.
299
+ const size_t start = index;
300
+
301
+ do {
302
+ index++;
303
+ } while (index < maximum && pm_encoding_utf_8_char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
304
+
305
+ pm_strpbrk_invalid_multibyte_character(parser, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start));
306
+ }
307
+ }
308
+ }
309
+
310
+ return NULL;
311
+ }
312
+
313
+ /**
314
+ * This is the path when the encoding is ASCII-8BIT.
315
+ */
316
+ static PRISM_INLINE const uint8_t *
317
+ pm_strpbrk_ascii_8bit(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t index, size_t maximum, bool validate) {
318
+ while (index < maximum) {
319
+ if (strchr((const char *) charset, source[index]) != NULL) {
320
+ return source + index;
321
+ }
322
+
323
+ if (validate && source[index] >= 0x80) pm_strpbrk_explicit_encoding_set(parser, (uint32_t) (source - parser->start), 1);
324
+ index++;
325
+ }
326
+
327
+ return NULL;
328
+ }
329
+
330
+ /**
331
+ * This is the slow path that does care about the encoding.
332
+ */
333
+ static PRISM_INLINE const uint8_t *
334
+ pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t index, size_t maximum, bool validate) {
335
+ const pm_encoding_t *encoding = parser->encoding;
336
+
337
+ while (index < maximum) {
338
+ if (strchr((const char *) charset, source[index]) != NULL) {
339
+ return source + index;
340
+ }
341
+
342
+ if (source[index] < 0x80) {
343
+ index++;
344
+ } else {
345
+ size_t width = encoding->char_width(source + index, (ptrdiff_t) (maximum - index));
346
+ if (validate) pm_strpbrk_explicit_encoding_set(parser, (uint32_t) (source - parser->start), (uint32_t) width);
347
+
348
+ if (width > 0) {
349
+ index += width;
350
+ } else if (!validate) {
351
+ index++;
352
+ } else {
353
+ // At this point we know we have an invalid multibyte character.
354
+ // We'll walk forward as far as we can until we find the next
355
+ // valid character so that we don't spam the user with a ton of
356
+ // the same kind of error.
357
+ const size_t start = index;
358
+
359
+ do {
360
+ index++;
361
+ } while (index < maximum && encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
362
+
363
+ pm_strpbrk_invalid_multibyte_character(parser, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start));
364
+ }
365
+ }
366
+ }
367
+
368
+ return NULL;
369
+ }
370
+
371
+ /**
372
+ * This is the fast path that does not care about the encoding because we know
373
+ * the encoding only supports single-byte characters.
374
+ */
375
+ static PRISM_INLINE const uint8_t *
376
+ pm_strpbrk_single_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t index, size_t maximum, bool validate) {
377
+ const pm_encoding_t *encoding = parser->encoding;
378
+
379
+ while (index < maximum) {
380
+ if (strchr((const char *) charset, source[index]) != NULL) {
381
+ return source + index;
382
+ }
383
+
384
+ if (source[index] < 0x80 || !validate) {
385
+ index++;
386
+ } else {
387
+ size_t width = encoding->char_width(source + index, (ptrdiff_t) (maximum - index));
388
+ pm_strpbrk_explicit_encoding_set(parser, (uint32_t) (source - parser->start), (uint32_t) width);
389
+
390
+ if (width > 0) {
391
+ index += width;
392
+ } else {
393
+ // At this point we know we have an invalid multibyte character.
394
+ // We'll walk forward as far as we can until we find the next
395
+ // valid character so that we don't spam the user with a ton of
396
+ // the same kind of error.
397
+ const size_t start = index;
398
+
399
+ do {
400
+ index++;
401
+ } while (index < maximum && encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
402
+
403
+ pm_strpbrk_invalid_multibyte_character(parser, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start));
404
+ }
405
+ }
406
+ }
407
+
408
+ return NULL;
409
+ }
410
+
411
+ /**
412
+ * Here we have rolled our own version of strpbrk. The standard library strpbrk
413
+ * has undefined behavior when the source string is not null-terminated. We want
414
+ * to support strings that are not null-terminated because pm_parse does not
415
+ * have the contract that the string is null-terminated. (This is desirable
416
+ * because it means the extension can call pm_parse with the result of a call to
417
+ * mmap).
418
+ *
419
+ * The standard library strpbrk also does not support passing a maximum length
420
+ * to search. We want to support this for the reason mentioned above, but we
421
+ * also don't want it to stop on null bytes. Ruby actually allows null bytes
422
+ * within strings, comments, regular expressions, etc. So we need to be able to
423
+ * skip past them.
424
+ *
425
+ * Finally, we want to support encodings wherein the charset could contain
426
+ * characters that are trailing bytes of multi-byte characters. For example, in
427
+ * Shift_JIS, the backslash character can be a trailing byte. In that case we
428
+ * need to take a slower path and iterate one multi-byte character at a time.
429
+ */
430
+ const uint8_t *
431
+ pm_strpbrk(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, ptrdiff_t length, bool validate) {
432
+ if (length <= 0) return NULL;
433
+
434
+ size_t maximum = (size_t) length;
435
+ size_t index = 0;
436
+ if (scan_strpbrk_ascii(parser, source, maximum, charset, &index)) return source + index;
437
+
438
+ if (!parser->encoding_changed) {
439
+ return pm_strpbrk_utf8(parser, source, charset, index, maximum, validate);
440
+ } else if (parser->encoding == PM_ENCODING_ASCII_8BIT_ENTRY) {
441
+ return pm_strpbrk_ascii_8bit(parser, source, charset, index, maximum, validate);
442
+ } else if (parser->encoding->multibyte) {
443
+ return pm_strpbrk_multi_byte(parser, source, charset, index, maximum, validate);
444
+ } else {
445
+ return pm_strpbrk_single_byte(parser, source, charset, index, maximum, validate);
446
+ }
447
+ }
@@ -1,22 +1,20 @@
1
- /* :markup: markdown */
2
-
3
1
  /*----------------------------------------------------------------------------*/
4
2
  /* This file is generated by the templates/template.rb script and should not */
5
3
  /* be modified manually. See */
6
- /* templates/src/token_type.c.erb */
4
+ /* templates/src/tokens.c.erb */
7
5
  /* if you are looking to modify the */
8
6
  /* template */
9
7
  /*----------------------------------------------------------------------------*/
10
8
 
11
- #include <string.h>
12
-
13
9
  #include "prism/ast.h"
14
10
 
11
+ #include <assert.h>
12
+
15
13
  /**
16
14
  * Returns a string representation of the given token type.
17
15
  */
18
- PRISM_EXPORTED_FUNCTION const char *
19
- pm_token_type_name(pm_token_type_t token_type) {
16
+ const char *
17
+ pm_token_type(pm_token_type_t token_type) {
20
18
  switch (token_type) {
21
19
  case PM_TOKEN_EOF:
22
20
  return "EOF";
@@ -46,6 +44,8 @@ pm_token_type_name(pm_token_type_t token_type) {
46
44
  return "KEYWORD_WHEN";
47
45
  case PM_TOKEN_NEWLINE:
48
46
  return "NEWLINE";
47
+ case PM_TOKEN_NEWLINE_TERMINATOR:
48
+ return "NEWLINE_TERMINATOR";
49
49
  case PM_TOKEN_PARENTHESIS_RIGHT:
50
50
  return "PARENTHESIS_RIGHT";
51
51
  case PM_TOKEN_PIPE:
@@ -74,6 +74,10 @@ pm_token_type_name(pm_token_type_t token_type) {
74
74
  return "BANG_TILDE";
75
75
  case PM_TOKEN_BRACE_LEFT:
76
76
  return "BRACE_LEFT";
77
+ case PM_TOKEN_BRACE_LEFT_ARGUMENT:
78
+ return "BRACE_LEFT_ARGUMENT";
79
+ case PM_TOKEN_BRACE_LEFT_HASH:
80
+ return "BRACE_LEFT_HASH";
77
81
  case PM_TOKEN_BRACKET_LEFT:
78
82
  return "BRACKET_LEFT";
79
83
  case PM_TOKEN_BRACKET_LEFT_ARRAY:
@@ -180,6 +184,10 @@ pm_token_type_name(pm_token_type_t token_type) {
180
184
  return "KEYWORD_DEF";
181
185
  case PM_TOKEN_KEYWORD_DEFINED:
182
186
  return "KEYWORD_DEFINED";
187
+ case PM_TOKEN_KEYWORD_DO_BLOCK:
188
+ return "KEYWORD_DO_BLOCK";
189
+ case PM_TOKEN_KEYWORD_DO_LAMBDA:
190
+ return "KEYWORD_DO_LAMBDA";
183
191
  case PM_TOKEN_KEYWORD_DO_LOOP:
184
192
  return "KEYWORD_DO_LOOP";
185
193
  case PM_TOKEN_KEYWORD_END_UPCASE:
@@ -266,6 +274,8 @@ pm_token_type_name(pm_token_type_t token_type) {
266
274
  return "NUMBERED_REFERENCE";
267
275
  case PM_TOKEN_PARENTHESIS_LEFT:
268
276
  return "PARENTHESIS_LEFT";
277
+ case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
278
+ return "PARENTHESIS_LEFT_GROUPING";
269
279
  case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES:
270
280
  return "PARENTHESIS_LEFT_PARENTHESES";
271
281
  case PM_TOKEN_PERCENT:
@@ -340,12 +350,12 @@ pm_token_type_name(pm_token_type_t token_type) {
340
350
  return "USTAR_STAR";
341
351
  case PM_TOKEN_WORDS_SEP:
342
352
  return "WORDS_SEP";
353
+ case PM_TOKEN_WORDS_SEP_IMPLICIT:
354
+ return "WORDS_SEP_IMPLICIT";
355
+ case PM_TOKEN_XSTRING_BEGIN:
356
+ return "XSTRING_BEGIN";
343
357
  case PM_TOKEN___END__:
344
358
  return "__END__";
345
- case PM_TOKEN_MISSING:
346
- return "MISSING";
347
- case PM_TOKEN_NOT_PROVIDED:
348
- return "NOT_PROVIDED";
349
359
  case PM_TOKEN_MAXIMUM:
350
360
  assert(false && "unreachable");
351
361
  return "";
@@ -361,14 +371,10 @@ pm_token_type_name(pm_token_type_t token_type) {
361
371
  * Returns the human name of the given token type.
362
372
  */
363
373
  const char *
364
- pm_token_type_human(pm_token_type_t token_type) {
374
+ pm_token_str(pm_token_type_t token_type) {
365
375
  switch (token_type) {
366
376
  case PM_TOKEN_EOF:
367
377
  return "end-of-input";
368
- case PM_TOKEN_MISSING:
369
- return "missing token";
370
- case PM_TOKEN_NOT_PROVIDED:
371
- return "not provided token";
372
378
  case PM_TOKEN_AMPERSAND:
373
379
  return "'&'";
374
380
  case PM_TOKEN_AMPERSAND_AMPERSAND:
@@ -391,6 +397,10 @@ pm_token_type_human(pm_token_type_t token_type) {
391
397
  return "'!~'";
392
398
  case PM_TOKEN_BRACE_LEFT:
393
399
  return "'{'";
400
+ case PM_TOKEN_BRACE_LEFT_ARGUMENT:
401
+ return "'{'";
402
+ case PM_TOKEN_BRACE_LEFT_HASH:
403
+ return "'{'";
394
404
  case PM_TOKEN_BRACE_RIGHT:
395
405
  return "'}'";
396
406
  case PM_TOKEN_BRACKET_LEFT:
@@ -505,6 +515,10 @@ pm_token_type_human(pm_token_type_t token_type) {
505
515
  return "'defined?'";
506
516
  case PM_TOKEN_KEYWORD_DO:
507
517
  return "'do'";
518
+ case PM_TOKEN_KEYWORD_DO_BLOCK:
519
+ return "'do'";
520
+ case PM_TOKEN_KEYWORD_DO_LAMBDA:
521
+ return "'do'";
508
522
  case PM_TOKEN_KEYWORD_DO_LOOP:
509
523
  return "'do'";
510
524
  case PM_TOKEN_KEYWORD_ELSE:
@@ -605,10 +619,14 @@ pm_token_type_human(pm_token_type_t token_type) {
605
619
  return "'->'";
606
620
  case PM_TOKEN_NEWLINE:
607
621
  return "newline";
622
+ case PM_TOKEN_NEWLINE_TERMINATOR:
623
+ return "newline";
608
624
  case PM_TOKEN_NUMBERED_REFERENCE:
609
625
  return "numbered reference";
610
626
  case PM_TOKEN_PARENTHESIS_LEFT:
611
627
  return "'('";
628
+ case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
629
+ return "'('";
612
630
  case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES:
613
631
  return "'('";
614
632
  case PM_TOKEN_PARENTHESIS_RIGHT:
@@ -688,7 +706,10 @@ pm_token_type_human(pm_token_type_t token_type) {
688
706
  case PM_TOKEN_USTAR_STAR:
689
707
  return "**";
690
708
  case PM_TOKEN_WORDS_SEP:
709
+ case PM_TOKEN_WORDS_SEP_IMPLICIT:
691
710
  return "string separator";
711
+ case PM_TOKEN_XSTRING_BEGIN:
712
+ return "backtick string literal";
692
713
  case PM_TOKEN___END__:
693
714
  return "'__END__'";
694
715
  case PM_TOKEN_MAXIMUM:
@@ -696,8 +717,8 @@ pm_token_type_human(pm_token_type_t token_type) {
696
717
  return "";
697
718
  }
698
719
 
699
- // Provide a default, because some compilers can't determine that the above
700
- // switch is exhaustive.
720
+ /* Provide a default, because some compilers cannot determine that the above
721
+ * switch is exhaustive. */
701
722
  assert(false && "unreachable");
702
723
  return "";
703
724
  }