prism 1.9.0 → 2.0.0.rc1

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 (238) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +84 -0
  3. data/Makefile +41 -12
  4. data/README.md +35 -97
  5. data/config.yml +174 -161
  6. data/docs/build_system.md +2 -2
  7. data/docs/configuration.md +5 -4
  8. data/docs/encoding.md +2 -2
  9. data/docs/fuzzing.md +4 -11
  10. data/docs/javascript.md +19 -4
  11. data/docs/mapping.md +1 -1
  12. data/docs/releasing.md +77 -5
  13. data/docs/relocation.md +2 -4
  14. data/docs/serialization.md +21 -24
  15. data/docs/testing.md +46 -29
  16. data/ext/prism/api_node.c +1461 -1401
  17. data/ext/prism/extconf.rb +3 -4
  18. data/ext/prism/extension.c +778 -422
  19. data/ext/prism/extension.h +3 -3
  20. data/include/prism/arena.h +37 -0
  21. data/include/prism/ast.h +2727 -610
  22. data/include/prism/buffer.h +52 -0
  23. data/include/prism/comments.h +55 -0
  24. data/include/prism/compiler/accel.h +24 -0
  25. data/include/prism/compiler/align.h +36 -0
  26. data/include/prism/compiler/assume.h +25 -0
  27. data/include/prism/compiler/exported.h +24 -0
  28. data/include/prism/compiler/fallthrough.h +22 -0
  29. data/include/prism/compiler/filesystem.h +32 -0
  30. data/include/prism/compiler/flex_array.h +19 -0
  31. data/include/prism/compiler/force_inline.h +21 -0
  32. data/include/prism/compiler/format.h +25 -0
  33. data/include/prism/compiler/inline.h +17 -0
  34. data/include/prism/compiler/nodiscard.h +22 -0
  35. data/include/prism/compiler/nonnull.h +18 -0
  36. data/include/prism/compiler/unused.h +18 -0
  37. data/include/prism/constant_pool.h +81 -0
  38. data/include/prism/diagnostic.h +38 -403
  39. data/include/prism/errors_format.h +53 -0
  40. data/include/prism/excludes.h +29 -0
  41. data/include/prism/integer.h +41 -0
  42. data/include/prism/internal/allocator.h +68 -0
  43. data/include/prism/internal/allocator_debug.h +88 -0
  44. data/include/prism/internal/arena.h +108 -0
  45. data/include/prism/internal/bit.h +42 -0
  46. data/include/prism/internal/buffer.h +91 -0
  47. data/include/prism/internal/char.h +145 -0
  48. data/include/prism/internal/comments.h +53 -0
  49. data/include/prism/internal/constant_pool.h +152 -0
  50. data/include/prism/internal/diagnostic.h +389 -0
  51. data/include/prism/{encoding.h → internal/encoding.h} +27 -68
  52. data/include/prism/internal/integer.h +68 -0
  53. data/include/prism/internal/isinf.h +16 -0
  54. data/include/prism/internal/line_offset_list.h +34 -0
  55. data/include/prism/{util/pm_list.h → internal/list.h} +11 -52
  56. data/include/prism/internal/magic_comments.h +23 -0
  57. data/include/prism/internal/memchr.h +15 -0
  58. data/include/prism/internal/node.h +38 -0
  59. data/include/prism/internal/options.h +232 -0
  60. data/include/prism/internal/parser.h +984 -0
  61. data/include/prism/internal/regexp.h +26 -0
  62. data/include/prism/internal/serialize.h +39 -0
  63. data/include/prism/internal/source.h +72 -0
  64. data/include/prism/{static_literals.h → internal/static_literals.h} +26 -47
  65. data/include/prism/internal/stringy.h +35 -0
  66. data/include/prism/{util/pm_strncasecmp.h → internal/strncasecmp.h} +3 -17
  67. data/include/prism/{util/pm_strpbrk.h → internal/strpbrk.h} +7 -20
  68. data/include/prism/internal/tokens.h +11 -0
  69. data/include/prism/json.h +32 -0
  70. data/include/prism/line_offset_list.h +61 -0
  71. data/include/prism/magic_comments.h +35 -0
  72. data/include/prism/node.h +18 -53
  73. data/include/prism/options.h +146 -280
  74. data/include/prism/parser.h +336 -868
  75. data/include/prism/prettyprint.h +6 -9
  76. data/include/prism/serialize.h +115 -0
  77. data/include/prism/source.h +155 -0
  78. data/include/prism/stream.h +28 -0
  79. data/include/prism/string_query.h +63 -0
  80. data/include/prism/stringy.h +72 -0
  81. data/include/prism/version.h +12 -3
  82. data/include/prism.h +43 -320
  83. data/lib/prism/compiler.rb +319 -305
  84. data/lib/prism/desugar_compiler.rb +105 -34
  85. data/lib/prism/dispatcher.rb +646 -473
  86. data/lib/prism/dot_visitor.rb +592 -511
  87. data/lib/prism/dsl.rb +391 -25
  88. data/lib/prism/ffi.rb +252 -90
  89. data/lib/prism/inspect_visitor.rb +357 -325
  90. data/lib/prism/lex_compat.rb +254 -212
  91. data/lib/prism/mutation_compiler.rb +313 -305
  92. data/lib/prism/node.rb +20268 -7030
  93. data/lib/prism/node_ext.rb +180 -187
  94. data/lib/prism/node_find.rb +215 -0
  95. data/lib/prism/parse_result/errors.rb +7 -1
  96. data/lib/prism/parse_result/newlines.rb +276 -8
  97. data/lib/prism/parse_result.rb +453 -139
  98. data/lib/prism/pattern.rb +65 -20
  99. data/lib/prism/reflection.rb +23 -12
  100. data/lib/prism/relocation.rb +149 -87
  101. data/lib/prism/serialize.rb +3443 -535
  102. data/lib/prism/string_query.rb +16 -1
  103. data/lib/prism/translation/parser/builder.rb +10 -2
  104. data/lib/prism/translation/parser/compiler.rb +44 -52
  105. data/lib/prism/translation/parser/lexer.rb +41 -92
  106. data/lib/prism/translation/parser.rb +1 -1
  107. data/lib/prism/translation/parser_current.rb +1 -1
  108. data/lib/prism/translation/ripper/lexer.rb +8 -10
  109. data/lib/prism/translation/ripper/sexp.rb +2 -10
  110. data/lib/prism/translation/ripper/shim.rb +2 -0
  111. data/lib/prism/translation/ripper.rb +967 -209
  112. data/lib/prism/translation/ruby_parser.rb +73 -316
  113. data/lib/prism/translation.rb +2 -0
  114. data/lib/prism/visitor.rb +632 -5
  115. data/lib/prism.rb +79 -9
  116. data/prism.gemspec +122 -60
  117. data/rbi/generated/prism/compiler.rbi +489 -0
  118. data/rbi/generated/prism/desugar_compiler.rbi +306 -0
  119. data/rbi/generated/prism/dispatcher.rbi +1138 -0
  120. data/rbi/generated/prism/dot_visitor.rbi +618 -0
  121. data/rbi/generated/prism/dsl.rbi +751 -0
  122. data/rbi/generated/prism/inspect_visitor.rbi +503 -0
  123. data/rbi/generated/prism/lex_compat.rbi +162 -0
  124. data/rbi/generated/prism/mutation_compiler.rbi +464 -0
  125. data/rbi/generated/prism/node.rbi +13681 -0
  126. data/rbi/generated/prism/node_ext.rbi +259 -0
  127. data/rbi/generated/prism/node_find.rbi +79 -0
  128. data/rbi/generated/prism/parse_result/errors.rbi +21 -0
  129. data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
  130. data/rbi/generated/prism/parse_result.rbi +764 -0
  131. data/rbi/generated/prism/pattern.rbi +134 -0
  132. data/rbi/generated/prism/reflection.rbi +89 -0
  133. data/rbi/generated/prism/relocation.rbi +329 -0
  134. data/rbi/generated/prism/serialize.rbi +181 -0
  135. data/rbi/generated/prism/string_query.rbi +36 -0
  136. data/rbi/generated/prism/translation.rbi +7 -0
  137. data/rbi/generated/prism/visitor.rbi +650 -0
  138. data/rbi/generated/prism.rbi +86 -0
  139. data/rbi/prism/translation/parser_versions.rbi +5 -0
  140. data/rbi/rubyvm/node_find.rbi +14 -0
  141. data/sig/generated/prism/compiler.rbs +492 -0
  142. data/sig/generated/prism/desugar_compiler.rbs +305 -0
  143. data/sig/generated/prism/dispatcher.rbs +1292 -0
  144. data/sig/generated/prism/dot_visitor.rbs +635 -0
  145. data/sig/generated/prism/dsl.rbs +928 -0
  146. data/sig/generated/prism/inspect_visitor.rbs +509 -0
  147. data/sig/generated/prism/lex_compat.rbs +166 -0
  148. data/sig/{prism → generated/prism}/mutation_compiler.rbs +309 -4
  149. data/sig/generated/prism/node.rbs +20160 -0
  150. data/sig/generated/prism/node_ext.rbs +290 -0
  151. data/sig/generated/prism/node_find.rbs +90 -0
  152. data/sig/generated/prism/parse_result/errors.rbs +22 -0
  153. data/sig/generated/prism/parse_result/newlines.rbs +223 -0
  154. data/sig/generated/prism/parse_result.rbs +859 -0
  155. data/sig/generated/prism/pattern.rbs +156 -0
  156. data/sig/generated/prism/reflection.rbs +101 -0
  157. data/sig/generated/prism/relocation.rbs +428 -0
  158. data/sig/generated/prism/serialize.rbs +195 -0
  159. data/sig/generated/prism/string_query.rbs +36 -0
  160. data/sig/generated/prism/translation.rbs +8 -0
  161. data/sig/generated/prism/visitor.rbs +1111 -0
  162. data/sig/generated/prism.rbs +84 -0
  163. data/sig/manifest.yaml +2 -0
  164. data/src/arena.c +117 -0
  165. data/src/{util/pm_buffer.c → buffer.c} +44 -21
  166. data/src/{util/pm_char.c → char.c} +28 -57
  167. data/src/comments.c +454 -0
  168. data/src/constant_pool.c +406 -0
  169. data/src/diagnostic.c +97 -65
  170. data/src/encoding.c +8 -3
  171. data/src/errors_format.c +519 -0
  172. data/src/{util/pm_integer.c → integer.c} +33 -22
  173. data/src/json.c +5733 -0
  174. data/src/line_offset_list.c +100 -0
  175. data/src/list.c +24 -0
  176. data/src/{util/pm_memchr.c → memchr.c} +8 -6
  177. data/src/node.c +3504 -7674
  178. data/src/options.c +162 -49
  179. data/src/parser.c +349 -0
  180. data/src/prettyprint.c +421 -354
  181. data/src/prism.c +8030 -6970
  182. data/src/regexp.c +961 -45
  183. data/src/serialize.c +459 -424
  184. data/src/source.c +518 -0
  185. data/src/static_literals.c +111 -43
  186. data/src/string_query.c +166 -0
  187. data/src/stringy.c +91 -0
  188. data/src/{util/pm_strncasecmp.c → strncasecmp.c} +5 -4
  189. data/src/strpbrk.c +447 -0
  190. data/src/{token_type.c → tokens.c} +39 -18
  191. metadata +122 -61
  192. data/ext/prism/api_pack.c +0 -276
  193. data/include/prism/defines.h +0 -260
  194. data/include/prism/pack.h +0 -163
  195. data/include/prism/regexp.h +0 -43
  196. data/include/prism/util/pm_buffer.h +0 -236
  197. data/include/prism/util/pm_char.h +0 -204
  198. data/include/prism/util/pm_constant_pool.h +0 -218
  199. data/include/prism/util/pm_integer.h +0 -130
  200. data/include/prism/util/pm_memchr.h +0 -29
  201. data/include/prism/util/pm_newline_list.h +0 -113
  202. data/include/prism/util/pm_string.h +0 -200
  203. data/lib/prism/pack.rb +0 -230
  204. data/lib/prism/parse_result/comments.rb +0 -188
  205. data/rbi/prism/compiler.rbi +0 -12
  206. data/rbi/prism/dsl.rbi +0 -524
  207. data/rbi/prism/inspect_visitor.rbi +0 -12
  208. data/rbi/prism/node.rbi +0 -8750
  209. data/rbi/prism/node_ext.rbi +0 -107
  210. data/rbi/prism/parse_result.rbi +0 -404
  211. data/rbi/prism/reflection.rbi +0 -58
  212. data/rbi/prism/string_query.rbi +0 -12
  213. data/rbi/prism/visitor.rbi +0 -473
  214. data/rbi/prism.rbi +0 -63
  215. data/sig/prism/compiler.rbs +0 -9
  216. data/sig/prism/dispatcher.rbs +0 -19
  217. data/sig/prism/dot_visitor.rbs +0 -6
  218. data/sig/prism/dsl.rbs +0 -351
  219. data/sig/prism/inspect_visitor.rbs +0 -22
  220. data/sig/prism/lex_compat.rbs +0 -10
  221. data/sig/prism/node.rbs +0 -4042
  222. data/sig/prism/node_ext.rbs +0 -149
  223. data/sig/prism/pack.rbs +0 -43
  224. data/sig/prism/parse_result/comments.rbs +0 -38
  225. data/sig/prism/parse_result.rbs +0 -197
  226. data/sig/prism/pattern.rbs +0 -13
  227. data/sig/prism/reflection.rbs +0 -50
  228. data/sig/prism/relocation.rbs +0 -185
  229. data/sig/prism/serialize.rbs +0 -8
  230. data/sig/prism/string_query.rbs +0 -11
  231. data/sig/prism/visitor.rbs +0 -169
  232. data/sig/prism.rbs +0 -272
  233. data/src/pack.c +0 -509
  234. data/src/util/pm_constant_pool.c +0 -342
  235. data/src/util/pm_list.c +0 -49
  236. data/src/util/pm_newline_list.c +0 -125
  237. data/src/util/pm_string.c +0 -381
  238. data/src/util/pm_strpbrk.c +0 -206
@@ -4,6 +4,8 @@
4
4
  #include <ruby/win32.h>
5
5
  #endif
6
6
 
7
+ #include <errno.h>
8
+
7
9
  // NOTE: this file should contain only bindings. All non-trivial logic should be
8
10
  // in libprism so it can be shared its the various callers.
9
11
 
@@ -29,6 +31,7 @@ VALUE rb_cPrismCurrentVersionError;
29
31
 
30
32
  VALUE rb_cPrismDebugEncoding;
31
33
 
34
+ ID rb_id_option_attach_comments;
32
35
  ID rb_id_option_command_line;
33
36
  ID rb_id_option_encoding;
34
37
  ID rb_id_option_filepath;
@@ -37,14 +40,52 @@ ID rb_id_option_frozen_string_literal;
37
40
  ID rb_id_option_line;
38
41
  ID rb_id_option_main_script;
39
42
  ID rb_id_option_partial_script;
43
+ ID rb_id_option_raise_error;
40
44
  ID rb_id_option_scopes;
41
45
  ID rb_id_option_version;
46
+
42
47
  ID rb_id_source_for;
48
+
43
49
  ID rb_id_forwarding_positionals;
44
50
  ID rb_id_forwarding_keywords;
45
51
  ID rb_id_forwarding_block;
46
52
  ID rb_id_forwarding_all;
47
53
 
54
+ ID rb_id_raise_error_plain;
55
+ ID rb_id_raise_error_style;
56
+ ID rb_id_raise_error_color;
57
+
58
+ /******************************************************************************/
59
+ /* Result struct for working with functions that may need to raise errors. */
60
+ /******************************************************************************/
61
+
62
+ typedef struct {
63
+ enum {
64
+ RESULT_OK,
65
+ RESULT_ERR
66
+ } type;
67
+ VALUE value;
68
+ } result_t;
69
+
70
+ static result_t
71
+ result_ok(VALUE value) {
72
+ return (result_t) { .type = RESULT_OK, .value = value };
73
+ }
74
+
75
+ static result_t
76
+ result_err(VALUE value) {
77
+ return (result_t) { .type = RESULT_ERR, .value = value };
78
+ }
79
+
80
+ static VALUE
81
+ result_get(result_t result) {
82
+ if (result.type == RESULT_OK) {
83
+ return result.value;
84
+ } else {
85
+ rb_exc_raise(result.value);
86
+ }
87
+ }
88
+
48
89
  /******************************************************************************/
49
90
  /* IO of Ruby code */
50
91
  /******************************************************************************/
@@ -64,19 +105,6 @@ check_string(VALUE value) {
64
105
  return RSTRING_PTR(value);
65
106
  }
66
107
 
67
- /**
68
- * Load the contents and size of the given string into the given pm_string_t.
69
- */
70
- static void
71
- input_load_string(pm_string_t *input, VALUE string) {
72
- // Check if the string is a string. If it's not, then raise a type error.
73
- if (!RB_TYPE_P(string, T_STRING)) {
74
- rb_raise(rb_eTypeError, "wrong argument type %" PRIsVALUE " (expected String)", rb_obj_class(string));
75
- }
76
-
77
- pm_string_constant_init(input, RSTRING_PTR(string), RSTRING_LEN(string));
78
- }
79
-
80
108
  /******************************************************************************/
81
109
  /* Building C options from Ruby options */
82
110
  /******************************************************************************/
@@ -148,10 +176,8 @@ build_options_scopes(pm_options_t *options, VALUE scopes) {
148
176
 
149
177
  // Initialize the scope array.
150
178
  size_t locals_count = RARRAY_LEN(locals);
151
- pm_options_scope_t *options_scope = &options->scopes[scope_index];
152
- if (!pm_options_scope_init(options_scope, locals_count)) {
153
- rb_raise(rb_eNoMemError, "failed to allocate memory");
154
- }
179
+ pm_options_scope_t *options_scope = pm_options_scope_mut(options, scope_index);
180
+ pm_options_scope_init(options_scope, locals_count);
155
181
 
156
182
  // Iterate over the locals and add them to the scope.
157
183
  for (size_t local_index = 0; local_index < locals_count; local_index++) {
@@ -164,7 +190,7 @@ build_options_scopes(pm_options_t *options, VALUE scopes) {
164
190
  }
165
191
 
166
192
  // Add the local to the scope.
167
- pm_string_t *scope_local = &options_scope->locals[local_index];
193
+ pm_string_t *scope_local = pm_options_scope_local_mut(options_scope, local_index);
168
194
  const char *name = rb_id2name(SYM2ID(local));
169
195
  pm_string_constant_init(scope_local, name, strlen(name));
170
196
  }
@@ -174,6 +200,23 @@ build_options_scopes(pm_options_t *options, VALUE scopes) {
174
200
  }
175
201
  }
176
202
 
203
+ /**
204
+ * Set the version option to the syntax version of the currently running Ruby,
205
+ * or to the closest version prism supports if it does not know that one.
206
+ */
207
+ static void
208
+ options_version_set_nearest(pm_options_t *options) {
209
+ if (!pm_options_version_set(options, ruby_version, 3)) {
210
+ // Prism doesn't know this specific version. Is it lower?
211
+ if (ruby_version[0] < '3' || (ruby_version[0] == '3' && ruby_version[2] < '3')) {
212
+ pm_options_version_set_lowest(options);
213
+ } else {
214
+ // Must be higher.
215
+ pm_options_version_set_highest(options);
216
+ }
217
+ }
218
+ }
219
+
177
220
  /**
178
221
  * An iterator function that is called for each key-value in the keywords hash.
179
222
  */
@@ -201,10 +244,11 @@ build_options_i(VALUE key, VALUE value, VALUE argument) {
201
244
  const char *version = check_string(value);
202
245
 
203
246
  if (RSTRING_LEN(value) == 7 && strncmp(version, "current", 7) == 0) {
204
- const char *current_version = RSTRING_PTR(rb_const_get(rb_cObject, rb_intern("RUBY_VERSION")));
205
- if (!pm_options_version_set(options, current_version, 3)) {
206
- rb_exc_raise(rb_exc_new_cstr(rb_cPrismCurrentVersionError, current_version));
247
+ if (!pm_options_version_set(options, ruby_version, 3)) {
248
+ rb_exc_raise(rb_exc_new_cstr(rb_cPrismCurrentVersionError, ruby_version));
207
249
  }
250
+ } else if (RSTRING_LEN(value) == 7 && strncmp(version, "nearest", 7) == 0) {
251
+ options_version_set_nearest(options);
208
252
  } else if (!pm_options_version_set(options, version, RSTRING_LEN(value))) {
209
253
  rb_raise(rb_eArgError, "invalid version: %" PRIsVALUE, value);
210
254
  }
@@ -236,6 +280,46 @@ build_options_i(VALUE key, VALUE value, VALUE argument) {
236
280
  if (!NIL_P(value)) pm_options_partial_script_set(options, RTEST(value));
237
281
  } else if (key_id == rb_id_option_freeze) {
238
282
  if (!NIL_P(value)) pm_options_freeze_set(options, RTEST(value));
283
+ } else if (key_id == rb_id_option_attach_comments) {
284
+ if (!NIL_P(value)) pm_options_attach_comments_set(options, RTEST(value));
285
+ } else if (key_id == rb_id_option_raise_error) {
286
+ if (!NIL_P(value)) {
287
+ if (value == Qtrue) {
288
+ /* When given true, color when $stderr is a terminal (unless
289
+ * NO_COLOR is set), bold styling when NO_COLOR is set, plain
290
+ * otherwise.
291
+ *
292
+ * rb_stderr_tty_p is not available to extensions, so instead
293
+ * we call the tty? method on $stderr, guarding against it
294
+ * having been replaced by an object that does not respond to
295
+ * tty?. */
296
+ if (rb_respond_to(rb_stderr, rb_intern_const("tty?")) && RTEST(rb_funcall(rb_stderr, rb_intern_const("tty?"), 0))) {
297
+ const char *no_color = getenv("NO_COLOR");
298
+ if (no_color == NULL || no_color[0] == '\0') {
299
+ pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_COLOR);
300
+ } else {
301
+ pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_STYLE);
302
+ }
303
+ } else {
304
+ pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_PLAIN);
305
+ }
306
+ } else {
307
+ if (!SYMBOL_P(value)) {
308
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Symbol)", rb_obj_class(value));
309
+ }
310
+
311
+ ID value_id = SYM2ID(value);
312
+ if (value_id == rb_id_raise_error_plain) {
313
+ pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_PLAIN);
314
+ } else if (value_id == rb_id_raise_error_style) {
315
+ pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_STYLE);
316
+ } else if (value_id == rb_id_raise_error_color) {
317
+ pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_COLOR);
318
+ } else {
319
+ rb_raise(rb_eArgError, "invalid raise_error value: %" PRIsVALUE, value);
320
+ }
321
+ }
322
+ }
239
323
  } else {
240
324
  rb_raise(rb_eArgError, "unknown keyword: %" PRIsVALUE, key);
241
325
  }
@@ -269,7 +353,8 @@ build_options(VALUE argument) {
269
353
  */
270
354
  static void
271
355
  extract_options(pm_options_t *options, VALUE filepath, VALUE keywords) {
272
- options->line = 1; // default
356
+ pm_options_line_set(options, 1); /* default */
357
+ options_version_set_nearest(options); /* default */
273
358
 
274
359
  if (!NIL_P(keywords)) {
275
360
  struct build_options_data data = { .options = options, .keywords = keywords };
@@ -297,36 +382,48 @@ extract_options(pm_options_t *options, VALUE filepath, VALUE keywords) {
297
382
  /**
298
383
  * Read options for methods that look like (source, **options).
299
384
  */
300
- static void
301
- string_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options) {
385
+ static VALUE
386
+ string_options(int argc, VALUE *argv, pm_options_t *options) {
302
387
  VALUE string;
303
388
  VALUE keywords;
304
389
  rb_scan_args(argc, argv, "1:", &string, &keywords);
305
390
 
391
+ if (!RB_TYPE_P(string, T_STRING)) {
392
+ pm_options_free(options);
393
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected String)", rb_obj_class(string));
394
+ }
395
+
306
396
  extract_options(options, Qnil, keywords);
307
- input_load_string(input, string);
397
+ return string;
308
398
  }
309
399
 
310
400
  /**
311
401
  * Read options for methods that look like (filepath, **options).
312
402
  */
313
- static void
314
- file_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options, VALUE *encoded_filepath) {
403
+ static pm_source_t *
404
+ file_options(int argc, VALUE *argv, pm_options_t *options, VALUE *encoded_filepath) {
315
405
  VALUE filepath;
316
406
  VALUE keywords;
317
407
  rb_scan_args(argc, argv, "1:", &filepath, &keywords);
318
408
 
319
- Check_Type(filepath, T_STRING);
409
+ int state = 0;
410
+ filepath = rb_protect(rb_get_path, filepath, &state);
411
+ if (state != 0) {
412
+ pm_options_free(options);
413
+ rb_jump_tag(state);
414
+ }
415
+
320
416
  *encoded_filepath = rb_str_encode_ospath(filepath);
321
417
  extract_options(options, *encoded_filepath, keywords);
322
418
 
323
- const char *source = (const char *) pm_string_source(&options->filepath);
324
- pm_string_init_result_t result;
419
+ const char *source = (const char *) pm_string_source(pm_options_filepath(options));
420
+ pm_source_init_result_t result;
421
+ pm_source_t *pm_src = pm_source_file_new(source, &result);
325
422
 
326
- switch (result = pm_string_file_init(input, source)) {
327
- case PM_STRING_INIT_SUCCESS:
423
+ switch (result) {
424
+ case PM_SOURCE_INIT_SUCCESS:
328
425
  break;
329
- case PM_STRING_INIT_ERROR_GENERIC: {
426
+ case PM_SOURCE_INIT_ERROR_GENERIC: {
330
427
  pm_options_free(options);
331
428
 
332
429
  #ifdef _WIN32
@@ -338,7 +435,7 @@ file_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options, V
338
435
  rb_syserr_fail(e, source);
339
436
  break;
340
437
  }
341
- case PM_STRING_INIT_ERROR_DIRECTORY:
438
+ case PM_SOURCE_INIT_ERROR_DIRECTORY:
342
439
  pm_options_free(options);
343
440
  rb_syserr_fail(EISDIR, source);
344
441
  break;
@@ -347,6 +444,58 @@ file_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options, V
347
444
  rb_raise(rb_eRuntimeError, "Unknown error (%d) initializing file: %s", result, source);
348
445
  break;
349
446
  }
447
+
448
+ return pm_src;
449
+ }
450
+
451
+ /**
452
+ * Check if the given parser has any errors and the raise option was set. If
453
+ * both are true, format them and return the formatted error wrapped in a
454
+ * result.
455
+ */
456
+ static result_t
457
+ check_raise_error_option(pm_parser_t *parser, const pm_options_t *options, rb_encoding *path_encoding) {
458
+ if (pm_parser_errors_size(parser) == 0) return result_ok(Qnil);
459
+
460
+ uint8_t raise_error = pm_options_raise_error(options);
461
+ if (raise_error == 0) return result_ok(Qnil);
462
+
463
+ pm_buffer_t *buffer = pm_buffer_new();
464
+ if (buffer == NULL) {
465
+ return result_err(rb_exc_new_cstr(rb_eNoMemError, "failed to allocate memory"));
466
+ }
467
+
468
+ pm_error_level_t error_level = pm_errors_format(parser, buffer, (pm_errors_format_type_t) raise_error);
469
+
470
+ /* Copy the message out of the buffer and free it before building any
471
+ * other Ruby objects so that the buffer cannot be leaked if building the
472
+ * objects raises. */
473
+ rb_encoding *message_encoding = (error_level == PM_ERROR_LEVEL_LOAD) ? rb_locale_encoding() : rb_enc_find(pm_parser_encoding_name(parser));
474
+ VALUE message = rb_enc_str_new(pm_buffer_value(buffer), (long) pm_buffer_length(buffer), message_encoding);
475
+ pm_buffer_free(buffer);
476
+
477
+ VALUE error = Qnil;
478
+ switch (error_level) {
479
+ case PM_ERROR_LEVEL_SYNTAX: {
480
+ error = rb_exc_new_str(rb_eSyntaxError, message);
481
+
482
+ rb_encoding *path_encoding_normal = path_encoding == NULL ? rb_utf8_encoding() : path_encoding;
483
+ VALUE path = rb_enc_str_new((const char *) pm_string_source(pm_options_filepath(options)), pm_string_length(pm_options_filepath(options)), path_encoding_normal);
484
+ rb_ivar_set(error, rb_intern_const("@path"), path);
485
+ break;
486
+ }
487
+ case PM_ERROR_LEVEL_ARGUMENT: {
488
+ error = rb_exc_new_str(rb_eArgError, message);
489
+ break;
490
+ }
491
+ case PM_ERROR_LEVEL_LOAD: {
492
+ error = rb_exc_new_str(rb_eLoadError, message);
493
+ rb_ivar_set(error, rb_intern_const("@path"), Qnil);
494
+ break;
495
+ }
496
+ }
497
+
498
+ return result_err(error);
350
499
  }
351
500
 
352
501
  #ifndef PRISM_EXCLUDE_SERIALIZATION
@@ -358,80 +507,88 @@ file_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options, V
358
507
  /**
359
508
  * Dump the AST corresponding to the given input to a string.
360
509
  */
361
- static VALUE
362
- dump_input(pm_string_t *input, const pm_options_t *options) {
363
- pm_buffer_t buffer;
364
- if (!pm_buffer_init(&buffer)) {
365
- rb_raise(rb_eNoMemError, "failed to allocate memory");
510
+ static result_t
511
+ dump_input(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
512
+ pm_arena_t *arena = pm_arena_new();
513
+ pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
514
+ pm_node_t *node = pm_parse(parser);
515
+
516
+ result_t result = check_raise_error_option(parser, options, path_encoding);
517
+ if (result.type == RESULT_OK) {
518
+ pm_buffer_t *buffer = pm_buffer_new();
519
+ if (buffer) {
520
+ pm_serialize(parser, node, buffer);
521
+ result = result_ok(rb_str_new(pm_buffer_value(buffer), pm_buffer_length(buffer)));
522
+ pm_buffer_free(buffer);
523
+
524
+ if (pm_options_freeze(options)) rb_obj_freeze(result.value);
525
+ } else {
526
+ result = result_err(rb_exc_new_cstr(rb_eNoMemError, "failed to allocate memory"));
527
+ }
366
528
  }
367
529
 
368
- pm_parser_t parser;
369
- pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
370
-
371
- pm_node_t *node = pm_parse(&parser);
372
- pm_serialize(&parser, node, &buffer);
373
-
374
- VALUE result = rb_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer));
375
- pm_node_destroy(&parser, node);
376
- pm_buffer_free(&buffer);
377
- pm_parser_free(&parser);
530
+ pm_parser_free(parser);
531
+ pm_arena_free(arena);
378
532
 
379
533
  return result;
380
534
  }
381
535
 
382
536
  /**
537
+ * :markup: markdown
383
538
  * call-seq:
384
- * Prism::dump(source, **options) -> String
539
+ * dump(source, **options) -> String
385
540
  *
386
541
  * Dump the AST corresponding to the given string to a string. For supported
387
- * options, see Prism::parse.
542
+ * options, see Prism.parse.
388
543
  */
389
544
  static VALUE
390
545
  dump(int argc, VALUE *argv, VALUE self) {
391
- pm_string_t input;
392
- pm_options_t options = { 0 };
393
- string_options(argc, argv, &input, &options);
546
+ pm_options_t *options = pm_options_new();
547
+ VALUE string = string_options(argc, argv, options);
548
+
549
+ const uint8_t *source = (const uint8_t *) RSTRING_PTR(string);
550
+ size_t length = RSTRING_LEN(string);
394
551
 
395
552
  #ifdef PRISM_BUILD_DEBUG
396
- size_t length = pm_string_length(&input);
397
553
  char* dup = xmalloc(length);
398
- memcpy(dup, pm_string_source(&input), length);
399
- pm_string_constant_init(&input, dup, length);
554
+ memcpy(dup, source, length);
555
+ source = (const uint8_t *) dup;
400
556
  #endif
401
557
 
402
- VALUE value = dump_input(&input, &options);
403
- if (options.freeze) rb_obj_freeze(value);
558
+ result_t result = dump_input(source, length, options, NULL);
404
559
 
405
560
  #ifdef PRISM_BUILD_DEBUG
561
+ #ifdef xfree_sized
562
+ xfree_sized(dup, length);
563
+ #else
406
564
  xfree(dup);
565
+ #endif
407
566
  #endif
408
567
 
409
- pm_string_free(&input);
410
- pm_options_free(&options);
411
-
412
- return value;
568
+ pm_options_free(options);
569
+ return result_get(result);
413
570
  }
414
571
 
415
572
  /**
573
+ * :markup: markdown
416
574
  * call-seq:
417
- * Prism::dump_file(filepath, **options) -> String
575
+ * dump_file(filepath, **options) -> String
418
576
  *
419
577
  * Dump the AST corresponding to the given file to a string. For supported
420
- * options, see Prism::parse.
578
+ * options, see Prism.parse.
421
579
  */
422
580
  static VALUE
423
581
  dump_file(int argc, VALUE *argv, VALUE self) {
424
- pm_string_t input;
425
- pm_options_t options = { 0 };
582
+ pm_options_t *options = pm_options_new();
426
583
 
427
584
  VALUE encoded_filepath;
428
- file_options(argc, argv, &input, &options, &encoded_filepath);
585
+ pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
429
586
 
430
- VALUE value = dump_input(&input, &options);
431
- pm_string_free(&input);
432
- pm_options_free(&options);
587
+ result_t result = dump_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
588
+ pm_source_free(src);
589
+ pm_options_free(options);
433
590
 
434
- return value;
591
+ return result_get(result);
435
592
  }
436
593
 
437
594
  #endif
@@ -455,73 +612,89 @@ rb_class_new_instance_freeze(int argc, const VALUE *argv, VALUE klass, bool free
455
612
  * Create a new Location instance from the given parser and bounds.
456
613
  */
457
614
  static inline VALUE
458
- parser_location(const pm_parser_t *parser, VALUE source, bool freeze, const uint8_t *start, size_t length) {
459
- VALUE argv[] = { source, LONG2FIX(start - parser->start), LONG2FIX(length) };
615
+ parser_location(VALUE source, bool freeze, uint32_t start, uint32_t length) {
616
+ VALUE argv[] = { source, LONG2FIX(start), LONG2FIX(length) };
460
617
  return rb_class_new_instance_freeze(3, argv, rb_cPrismLocation, freeze);
461
618
  }
462
619
 
463
620
  /**
464
621
  * Create a new Location instance from the given parser and location.
465
622
  */
466
- #define PARSER_LOCATION_LOC(parser, source, freeze, loc) \
467
- parser_location(parser, source, freeze, loc.start, (size_t) (loc.end - loc.start))
623
+ #define PARSER_LOCATION(source, freeze, location) \
624
+ parser_location(source, freeze, location.start, location.length)
468
625
 
469
626
  /**
470
627
  * Build a new Comment instance from the given parser and comment.
471
628
  */
472
629
  static inline VALUE
473
- parser_comment(const pm_parser_t *parser, VALUE source, bool freeze, const pm_comment_t *comment) {
474
- VALUE argv[] = { PARSER_LOCATION_LOC(parser, source, freeze, comment->location) };
475
- VALUE type = (comment->type == PM_COMMENT_EMBDOC) ? rb_cPrismEmbDocComment : rb_cPrismInlineComment;
630
+ parser_comment(VALUE source, bool freeze, const pm_comment_t *comment) {
631
+ VALUE argv[] = { PARSER_LOCATION(source, freeze, pm_comment_location(comment)) };
632
+ VALUE type = (pm_comment_type(comment) == PM_COMMENT_EMBDOC) ? rb_cPrismEmbDocComment : rb_cPrismInlineComment;
476
633
  return rb_class_new_instance_freeze(1, argv, type, freeze);
477
634
  }
478
635
 
636
+ typedef struct {
637
+ VALUE comments;
638
+ VALUE source;
639
+ bool freeze;
640
+ long index;
641
+ } parser_comments_each_data_t;
642
+
643
+ static void
644
+ parser_comments_each(const pm_comment_t *comment, void *data) {
645
+ parser_comments_each_data_t *each_data = (parser_comments_each_data_t *) data;
646
+ VALUE value = parser_comment(each_data->source, each_data->freeze, comment);
647
+ rb_ary_store(each_data->comments, each_data->index++, value);
648
+ }
649
+
479
650
  /**
480
- * Extract the comments out of the parser into an array.
651
+ * Extract the comments out of the parser into the given array, which was
652
+ * created before the source so that the source could hold it.
481
653
  */
482
- static VALUE
483
- parser_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
484
- VALUE comments = rb_ary_new_capa(parser->comment_list.size);
485
-
486
- for (
487
- const pm_comment_t *comment = (const pm_comment_t *) parser->comment_list.head;
488
- comment != NULL;
489
- comment = (const pm_comment_t *) comment->node.next
490
- ) {
491
- VALUE value = parser_comment(parser, source, freeze, comment);
492
- rb_ary_push(comments, value);
493
- }
494
-
654
+ static void
655
+ parser_comments(const pm_parser_t *parser, VALUE comments, VALUE source, bool freeze) {
656
+ parser_comments_each_data_t each_data = { .comments = comments, .source = source, .freeze = freeze };
657
+ pm_parser_comments_each(parser, parser_comments_each, &each_data);
495
658
  if (freeze) rb_obj_freeze(comments);
496
- return comments;
497
659
  }
498
660
 
499
661
  /**
500
662
  * Build a new MagicComment instance from the given parser and magic comment.
501
663
  */
502
664
  static inline VALUE
503
- parser_magic_comment(const pm_parser_t *parser, VALUE source, bool freeze, const pm_magic_comment_t *magic_comment) {
504
- VALUE key_loc = parser_location(parser, source, freeze, magic_comment->key_start, magic_comment->key_length);
505
- VALUE value_loc = parser_location(parser, source, freeze, magic_comment->value_start, magic_comment->value_length);
665
+ parser_magic_comment(VALUE source, bool freeze, const pm_magic_comment_t *magic_comment) {
666
+ pm_location_t key = pm_magic_comment_key(magic_comment);
667
+ pm_location_t value = pm_magic_comment_value(magic_comment);
668
+
669
+ VALUE key_loc = parser_location(source, freeze, key.start, key.length);
670
+ VALUE value_loc = parser_location(source, freeze, value.start, value.length);
671
+
506
672
  VALUE argv[] = { key_loc, value_loc };
507
673
  return rb_class_new_instance_freeze(2, argv, rb_cPrismMagicComment, freeze);
508
674
  }
509
675
 
676
+ typedef struct {
677
+ VALUE magic_comments;
678
+ VALUE source;
679
+ bool freeze;
680
+ } parser_magic_comments_each_data_t;
681
+
682
+ static void
683
+ parser_magic_comments_each(const pm_magic_comment_t *magic_comment, void *data) {
684
+ parser_magic_comments_each_data_t *each_data = (parser_magic_comments_each_data_t *) data;
685
+ VALUE value = parser_magic_comment(each_data->source, each_data->freeze, magic_comment);
686
+ rb_ary_push(each_data->magic_comments, value);
687
+ }
688
+
510
689
  /**
511
690
  * Extract the magic comments out of the parser into an array.
512
691
  */
513
692
  static VALUE
514
693
  parser_magic_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
515
- VALUE magic_comments = rb_ary_new_capa(parser->magic_comment_list.size);
516
-
517
- for (
518
- const pm_magic_comment_t *magic_comment = (const pm_magic_comment_t *) parser->magic_comment_list.head;
519
- magic_comment != NULL;
520
- magic_comment = (const pm_magic_comment_t *) magic_comment->node.next
521
- ) {
522
- VALUE value = parser_magic_comment(parser, source, freeze, magic_comment);
523
- rb_ary_push(magic_comments, value);
524
- }
694
+ VALUE magic_comments = rb_ary_new_capa(pm_parser_magic_comments_size(parser));
695
+
696
+ parser_magic_comments_each_data_t each_data = { magic_comments, source, freeze };
697
+ pm_parser_magic_comments_each(parser, parser_magic_comments_each, &each_data);
525
698
 
526
699
  if (freeze) rb_obj_freeze(magic_comments);
527
700
  return magic_comments;
@@ -533,90 +706,160 @@ parser_magic_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
533
706
  */
534
707
  static VALUE
535
708
  parser_data_loc(const pm_parser_t *parser, VALUE source, bool freeze) {
536
- if (parser->data_loc.end == NULL) {
709
+ const pm_location_t *data_loc = pm_parser_data_loc(parser);
710
+
711
+ if (data_loc->length == 0) {
537
712
  return Qnil;
538
713
  } else {
539
- return PARSER_LOCATION_LOC(parser, source, freeze, parser->data_loc);
714
+ return parser_location(source, freeze, data_loc->start, data_loc->length);
540
715
  }
541
716
  }
542
717
 
718
+ typedef struct {
719
+ VALUE errors;
720
+ rb_encoding *encoding;
721
+ VALUE source;
722
+ bool freeze;
723
+ } parser_errors_each_data_t;
724
+
725
+ static void
726
+ parser_errors_each(const pm_diagnostic_t *diagnostic, void *data) {
727
+ parser_errors_each_data_t *each_data = (parser_errors_each_data_t *) data;
728
+
729
+ VALUE type = ID2SYM(rb_intern(pm_diagnostic_type(diagnostic)));
730
+ VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(pm_diagnostic_message(diagnostic), each_data->encoding));
731
+ VALUE location = PARSER_LOCATION(each_data->source, each_data->freeze, pm_diagnostic_location(diagnostic));
732
+
733
+ pm_error_level_t error_level = pm_diagnostic_error_level(diagnostic);
734
+ VALUE level = Qnil;
735
+
736
+ switch (error_level) {
737
+ case PM_ERROR_LEVEL_SYNTAX:
738
+ level = ID2SYM(rb_intern("syntax"));
739
+ break;
740
+ case PM_ERROR_LEVEL_ARGUMENT:
741
+ level = ID2SYM(rb_intern("argument"));
742
+ break;
743
+ case PM_ERROR_LEVEL_LOAD:
744
+ level = ID2SYM(rb_intern("load"));
745
+ break;
746
+ default:
747
+ rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, error_level);
748
+ }
749
+
750
+ VALUE argv[] = { type, message, location, level };
751
+ VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseError, each_data->freeze);
752
+ rb_ary_push(each_data->errors, value);
753
+ }
754
+
543
755
  /**
544
756
  * Extract the errors out of the parser into an array.
545
757
  */
546
758
  static VALUE
547
759
  parser_errors(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bool freeze) {
548
- VALUE errors = rb_ary_new_capa(parser->error_list.size);
549
-
550
- for (
551
- const pm_diagnostic_t *error = (const pm_diagnostic_t *) parser->error_list.head;
552
- error != NULL;
553
- error = (const pm_diagnostic_t *) error->node.next
554
- ) {
555
- VALUE type = ID2SYM(rb_intern(pm_diagnostic_id_human(error->diag_id)));
556
- VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(error->message, encoding));
557
- VALUE location = PARSER_LOCATION_LOC(parser, source, freeze, error->location);
558
-
559
- VALUE level = Qnil;
560
- switch (error->level) {
561
- case PM_ERROR_LEVEL_SYNTAX:
562
- level = ID2SYM(rb_intern("syntax"));
563
- break;
564
- case PM_ERROR_LEVEL_ARGUMENT:
565
- level = ID2SYM(rb_intern("argument"));
566
- break;
567
- case PM_ERROR_LEVEL_LOAD:
568
- level = ID2SYM(rb_intern("load"));
569
- break;
570
- default:
571
- rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, error->level);
572
- }
760
+ VALUE errors = rb_ary_new_capa(pm_parser_errors_size(parser));
573
761
 
574
- VALUE argv[] = { type, message, location, level };
575
- VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseError, freeze);
576
- rb_ary_push(errors, value);
577
- }
762
+ parser_errors_each_data_t each_data = { errors, encoding, source, freeze };
763
+ pm_parser_errors_each(parser, parser_errors_each, &each_data);
578
764
 
579
765
  if (freeze) rb_obj_freeze(errors);
580
766
  return errors;
581
767
  }
582
768
 
769
+ typedef struct {
770
+ VALUE warnings;
771
+ rb_encoding *encoding;
772
+ VALUE source;
773
+ bool freeze;
774
+ } parser_warnings_each_data_t;
775
+
776
+ static void
777
+ parser_warnings_each(const pm_diagnostic_t *diagnostic, void *data) {
778
+ parser_warnings_each_data_t *each_data = (parser_warnings_each_data_t *) data;
779
+
780
+ VALUE type = ID2SYM(rb_intern(pm_diagnostic_type(diagnostic)));
781
+ VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(pm_diagnostic_message(diagnostic), each_data->encoding));
782
+ VALUE location = PARSER_LOCATION(each_data->source, each_data->freeze, pm_diagnostic_location(diagnostic));
783
+
784
+ pm_warning_level_t warning_level = pm_diagnostic_warning_level(diagnostic);
785
+ VALUE level = Qnil;
786
+
787
+ switch (warning_level) {
788
+ case PM_WARNING_LEVEL_DEFAULT:
789
+ level = ID2SYM(rb_intern("default"));
790
+ break;
791
+ case PM_WARNING_LEVEL_VERBOSE:
792
+ level = ID2SYM(rb_intern("verbose"));
793
+ break;
794
+ default:
795
+ rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, warning_level);
796
+ }
797
+
798
+ VALUE argv[] = { type, message, location, level };
799
+ VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseWarning, each_data->freeze);
800
+ rb_ary_push(each_data->warnings, value);
801
+ }
802
+
583
803
  /**
584
804
  * Extract the warnings out of the parser into an array.
585
805
  */
586
806
  static VALUE
587
807
  parser_warnings(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bool freeze) {
588
- VALUE warnings = rb_ary_new_capa(parser->warning_list.size);
589
-
590
- for (
591
- const pm_diagnostic_t *warning = (const pm_diagnostic_t *) parser->warning_list.head;
592
- warning != NULL;
593
- warning = (const pm_diagnostic_t *) warning->node.next
594
- ) {
595
- VALUE type = ID2SYM(rb_intern(pm_diagnostic_id_human(warning->diag_id)));
596
- VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(warning->message, encoding));
597
- VALUE location = PARSER_LOCATION_LOC(parser, source, freeze, warning->location);
598
-
599
- VALUE level = Qnil;
600
- switch (warning->level) {
601
- case PM_WARNING_LEVEL_DEFAULT:
602
- level = ID2SYM(rb_intern("default"));
603
- break;
604
- case PM_WARNING_LEVEL_VERBOSE:
605
- level = ID2SYM(rb_intern("verbose"));
606
- break;
607
- default:
608
- rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, warning->level);
609
- }
808
+ VALUE warnings = rb_ary_new_capa(pm_parser_warnings_size(parser));
610
809
 
611
- VALUE argv[] = { type, message, location, level };
612
- VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseWarning, freeze);
613
- rb_ary_push(warnings, value);
614
- }
810
+ parser_warnings_each_data_t each_data = { warnings, encoding, source, freeze };
811
+ pm_parser_warnings_each(parser, parser_warnings_each, &each_data);
615
812
 
616
813
  if (freeze) rb_obj_freeze(warnings);
617
814
  return warnings;
618
815
  }
619
816
 
817
+ /**
818
+ * Create the arrays that hold the comments attached to each node, sized for the
819
+ * parser's comment attachments. They stay empty unless the attach_comments
820
+ * option was set.
821
+ */
822
+ static void
823
+ parser_attachments_new(const pm_parser_t *parser, VALUE *attachments, VALUE *ranges) {
824
+ uint32_t ranges_size = pm_parser_comment_ranges_size(parser);
825
+ *attachments = rb_ary_new_capa(ranges_size == 0 ? 0 : (long) pm_parser_comments_size(parser));
826
+ *ranges = rb_ary_new_capa(ranges_size == 0 ? 0 : (long) ranges_size + 1);
827
+ }
828
+
829
+ /**
830
+ * Fill in the comments attached to each node. The attachments are the Comment
831
+ * objects in the order of the parser's comment attachments, which groups them
832
+ * by node in order of node id. The ranges are indexed by node id: for each node
833
+ * that owns comments, the index at which its comments begin is stored at its
834
+ * node id and the index at which they end is stored at the following node id.
835
+ */
836
+ static void
837
+ parser_attachments(const pm_parser_t *parser, VALUE comments, VALUE attachments, VALUE ranges, bool freeze) {
838
+ uint32_t ranges_size = pm_parser_comment_ranges_size(parser);
839
+
840
+ if (ranges_size > 0) {
841
+ long size = RARRAY_LEN(comments);
842
+
843
+ for (long index = 0; index < size; index++) {
844
+ rb_ary_store(attachments, index, RARRAY_AREF(comments, pm_parser_comment_attachment(parser, (uint32_t) index)));
845
+ }
846
+
847
+ for (uint32_t node_id = 0; node_id < ranges_size; node_id++) {
848
+ pm_comment_range_t range = pm_parser_comment_range(parser, node_id);
849
+
850
+ if (range.length > 0) {
851
+ rb_ary_store(ranges, (long) node_id, LONG2FIX(range.index));
852
+ rb_ary_store(ranges, (long) node_id + 1, LONG2FIX(range.index + range.length));
853
+ }
854
+ }
855
+ }
856
+
857
+ if (freeze) {
858
+ rb_obj_freeze(attachments);
859
+ rb_obj_freeze(ranges);
860
+ }
861
+ }
862
+
620
863
  /**
621
864
  * Create a new parse result from the given parser, value, encoding, and source.
622
865
  */
@@ -624,11 +867,11 @@ static VALUE
624
867
  parse_result_create(VALUE class, const pm_parser_t *parser, VALUE value, rb_encoding *encoding, VALUE source, bool freeze) {
625
868
  VALUE result_argv[] = {
626
869
  value,
627
- parser_comments(parser, source, freeze),
628
870
  parser_magic_comments(parser, source, freeze),
629
871
  parser_data_loc(parser, source, freeze),
630
872
  parser_errors(parser, encoding, source, freeze),
631
873
  parser_warnings(parser, encoding, source, freeze),
874
+ pm_parser_continuable(parser) ? Qtrue : Qfalse,
632
875
  source
633
876
  };
634
877
 
@@ -657,18 +900,11 @@ typedef struct {
657
900
  * onto the tokens array.
658
901
  */
659
902
  static void
660
- parse_lex_token(void *data, pm_parser_t *parser, pm_token_t *token) {
661
- parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) parser->lex_callback->data;
903
+ parse_lex_token(pm_parser_t *parser, pm_token_t *token, void *data) {
904
+ parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) data;
662
905
 
663
906
  VALUE value = pm_token_new(parser, token, parse_lex_data->encoding, parse_lex_data->source, parse_lex_data->freeze);
664
- VALUE yields = rb_assoc_new(value, INT2FIX(parser->lex_state));
665
-
666
- if (parse_lex_data->freeze) {
667
- rb_obj_freeze(value);
668
- rb_obj_freeze(yields);
669
- }
670
-
671
- rb_ary_push(parse_lex_data->tokens, yields);
907
+ rb_ary_push(parse_lex_data->tokens, value);
672
908
  }
673
909
 
674
910
  /**
@@ -678,8 +914,8 @@ parse_lex_token(void *data, pm_parser_t *parser, pm_token_t *token) {
678
914
  */
679
915
  static void
680
916
  parse_lex_encoding_changed_callback(pm_parser_t *parser) {
681
- parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) parser->lex_callback->data;
682
- parse_lex_data->encoding = rb_enc_find(parser->encoding->name);
917
+ parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) pm_parser_lex_callback_data(parser);
918
+ parse_lex_data->encoding = rb_enc_find(pm_parser_encoding_name(parser));
683
919
 
684
920
  // Since the encoding changed, we need to go back and change the encoding of
685
921
  // the tokens that were already lexed. This is only going to end up being
@@ -689,9 +925,7 @@ parse_lex_encoding_changed_callback(pm_parser_t *parser) {
689
925
  VALUE next_tokens = rb_ary_new();
690
926
 
691
927
  for (long index = 0; index < RARRAY_LEN(tokens); index++) {
692
- VALUE yields = rb_ary_entry(tokens, index);
693
- VALUE token = rb_ary_entry(yields, 0);
694
-
928
+ VALUE token = rb_ary_entry(tokens, index);
695
929
  VALUE value = rb_ivar_get(token, rb_intern("@value"));
696
930
  VALUE next_value = rb_str_dup(value);
697
931
 
@@ -702,18 +936,17 @@ parse_lex_encoding_changed_callback(pm_parser_t *parser) {
702
936
  parse_lex_data->source,
703
937
  rb_ivar_get(token, rb_intern("@type")),
704
938
  next_value,
705
- rb_ivar_get(token, rb_intern("@location"))
939
+ rb_ivar_get(token, rb_intern("@location")),
940
+ rb_ivar_get(token, rb_intern("@state")),
706
941
  };
707
942
 
708
- VALUE next_token = rb_class_new_instance(4, next_token_argv, rb_cPrismToken);
709
- VALUE next_yields = rb_assoc_new(next_token, rb_ary_entry(yields, 1));
943
+ VALUE next_token = rb_class_new_instance(5, next_token_argv, rb_cPrismToken);
710
944
 
711
945
  if (parse_lex_data->freeze) {
712
946
  rb_obj_freeze(next_token);
713
- rb_obj_freeze(next_yields);
714
947
  }
715
948
 
716
- rb_ary_push(next_tokens, next_yields);
949
+ rb_ary_push(next_tokens, next_token);
717
950
  }
718
951
 
719
952
  rb_ary_replace(parse_lex_data->tokens, next_tokens);
@@ -723,107 +956,119 @@ parse_lex_encoding_changed_callback(pm_parser_t *parser) {
723
956
  * Parse the given input and return a ParseResult containing just the tokens or
724
957
  * the nodes and tokens.
725
958
  */
726
- static VALUE
727
- parse_lex_input(pm_string_t *input, const pm_options_t *options, bool return_nodes) {
728
- pm_parser_t parser;
729
- pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
730
- pm_parser_register_encoding_changed_callback(&parser, parse_lex_encoding_changed_callback);
731
-
732
- VALUE source_string = rb_str_new((const char *) pm_string_source(input), pm_string_length(input));
733
- VALUE offsets = rb_ary_new_capa(parser.newline_list.size);
734
- VALUE source = rb_funcall(rb_cPrismSource, rb_id_source_for, 3, source_string, LONG2NUM(parser.start_line), offsets);
959
+ static result_t
960
+ parse_lex_input(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding, bool return_nodes) {
961
+ pm_arena_t *arena = pm_arena_new();
962
+ pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
963
+ pm_parser_encoding_changed_callback_set(parser, parse_lex_encoding_changed_callback);
964
+
965
+ VALUE source_string = rb_str_new((const char *) input, input_length);
966
+ VALUE offsets = rb_ary_new_capa(pm_parser_line_offsets(parser)->size);
967
+ VALUE comments = rb_ary_new();
968
+ VALUE attachments = rb_ary_new();
969
+ VALUE ranges = rb_ary_new();
970
+ VALUE source = rb_funcall(rb_cPrismSource, rb_id_source_for, 6, source_string, LONG2NUM(pm_parser_start_line(parser)), offsets, comments, attachments, ranges);
735
971
 
736
972
  parse_lex_data_t parse_lex_data = {
737
973
  .source = source,
738
974
  .tokens = rb_ary_new(),
739
- .encoding = rb_utf8_encoding(),
740
- .freeze = options->freeze,
975
+ .encoding = rb_enc_find(pm_parser_encoding_name(parser)),
976
+ .freeze = pm_options_freeze(options),
741
977
  };
742
978
 
743
979
  parse_lex_data_t *data = &parse_lex_data;
744
- pm_lex_callback_t lex_callback = (pm_lex_callback_t) {
745
- .data = (void *) data,
746
- .callback = parse_lex_token,
747
- };
980
+ pm_parser_lex_callback_set(parser, parse_lex_token, data);
748
981
 
749
- parser.lex_callback = &lex_callback;
750
- pm_node_t *node = pm_parse(&parser);
982
+ pm_node_t *node = pm_parse(parser);
751
983
 
752
- // Here we need to update the Source object to have the correct
753
- // encoding for the source string and the correct newline offsets.
754
- // We do it here because we've already created the Source object and given
755
- // it over to all of the tokens, and both of these are only set after pm_parse().
756
- rb_encoding *encoding = rb_enc_find(parser.encoding->name);
757
- rb_enc_associate(source_string, encoding);
984
+ result_t result = check_raise_error_option(parser, options, path_encoding);
985
+ if (result.type == RESULT_OK) {
986
+ /* Update the Source object with the correct encoding and line offsets,
987
+ * which are only available after pm_parse() completes. */
988
+ rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser));
989
+ rb_enc_associate(source_string, encoding);
758
990
 
759
- for (size_t index = 0; index < parser.newline_list.size; index++) {
760
- rb_ary_push(offsets, ULONG2NUM(parser.newline_list.offsets[index]));
761
- }
991
+ const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser);
992
+ for (size_t index = 0; index < line_offsets->size; index++) {
993
+ rb_ary_store(offsets, (long) index, ULONG2NUM(line_offsets->offsets[index]));
994
+ }
762
995
 
763
- if (options->freeze) {
764
- rb_obj_freeze(source_string);
765
- rb_obj_freeze(offsets);
766
- rb_obj_freeze(source);
767
- rb_obj_freeze(parse_lex_data.tokens);
768
- }
996
+ if (pm_options_freeze(options)) {
997
+ rb_obj_freeze(source_string);
998
+ rb_obj_freeze(offsets);
999
+ rb_obj_freeze(source);
1000
+ rb_obj_freeze(parse_lex_data.tokens);
1001
+ }
769
1002
 
770
- VALUE result;
771
- if (return_nodes) {
772
- VALUE value = rb_ary_new_capa(2);
773
- rb_ary_push(value, pm_ast_new(&parser, node, parse_lex_data.encoding, source, options->freeze));
774
- rb_ary_push(value, parse_lex_data.tokens);
775
- if (options->freeze) rb_obj_freeze(value);
776
- result = parse_result_create(rb_cPrismParseLexResult, &parser, value, parse_lex_data.encoding, source, options->freeze);
777
- } else {
778
- result = parse_result_create(rb_cPrismLexResult, &parser, parse_lex_data.tokens, parse_lex_data.encoding, source, options->freeze);
1003
+ /* The comment lists were created along with the source before parsing,
1004
+ * so they are grown to their final sizes now that those are known. */
1005
+ uint32_t ranges_size = pm_parser_comment_ranges_size(parser);
1006
+ rb_ary_resize(comments, (long) pm_parser_comments_size(parser));
1007
+
1008
+ if (ranges_size > 0) {
1009
+ rb_ary_resize(attachments, (long) pm_parser_comments_size(parser));
1010
+ rb_ary_resize(ranges, (long) ranges_size + 1);
1011
+ }
1012
+
1013
+ parser_comments(parser, comments, source, pm_options_freeze(options));
1014
+ parser_attachments(parser, comments, attachments, ranges, pm_options_freeze(options));
1015
+
1016
+ if (return_nodes) {
1017
+ VALUE value = rb_ary_new_capa(2);
1018
+ rb_ary_push(value, pm_ast_new(parser, node, parse_lex_data.encoding, source, pm_options_freeze(options)));
1019
+ rb_ary_push(value, parse_lex_data.tokens);
1020
+ if (pm_options_freeze(options)) rb_obj_freeze(value);
1021
+ result = result_ok(parse_result_create(rb_cPrismParseLexResult, parser, value, parse_lex_data.encoding, source, pm_options_freeze(options)));
1022
+ } else {
1023
+ result = result_ok(parse_result_create(rb_cPrismLexResult, parser, parse_lex_data.tokens, parse_lex_data.encoding, source, pm_options_freeze(options)));
1024
+ }
779
1025
  }
780
1026
 
781
- pm_node_destroy(&parser, node);
782
- pm_parser_free(&parser);
1027
+ pm_parser_free(parser);
1028
+ pm_arena_free(arena);
783
1029
 
784
1030
  return result;
785
1031
  }
786
1032
 
787
1033
  /**
1034
+ * :markup: markdown
788
1035
  * call-seq:
789
- * Prism::lex(source, **options) -> LexResult
1036
+ * lex(source, **options) -> LexResult
790
1037
  *
791
1038
  * Return a LexResult instance that contains an array of Token instances
792
- * corresponding to the given string. For supported options, see Prism::parse.
1039
+ * corresponding to the given string. For supported options, see Prism.parse.
793
1040
  */
794
1041
  static VALUE
795
1042
  lex(int argc, VALUE *argv, VALUE self) {
796
- pm_string_t input;
797
- pm_options_t options = { 0 };
798
- string_options(argc, argv, &input, &options);
1043
+ pm_options_t *options = pm_options_new();
1044
+ VALUE string = string_options(argc, argv, options);
799
1045
 
800
- VALUE result = parse_lex_input(&input, &options, false);
801
- pm_string_free(&input);
802
- pm_options_free(&options);
1046
+ result_t result = parse_lex_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL, false);
1047
+ pm_options_free(options);
803
1048
 
804
- return result;
1049
+ return result_get(result);
805
1050
  }
806
1051
 
807
1052
  /**
1053
+ * :markup: markdown
808
1054
  * call-seq:
809
- * Prism::lex_file(filepath, **options) -> LexResult
1055
+ * lex_file(filepath, **options) -> LexResult
810
1056
  *
811
1057
  * Return a LexResult instance that contains an array of Token instances
812
- * corresponding to the given file. For supported options, see Prism::parse.
1058
+ * corresponding to the given file. For supported options, see Prism.parse.
813
1059
  */
814
1060
  static VALUE
815
1061
  lex_file(int argc, VALUE *argv, VALUE self) {
816
- pm_string_t input;
817
- pm_options_t options = { 0 };
1062
+ pm_options_t *options = pm_options_new();
818
1063
 
819
1064
  VALUE encoded_filepath;
820
- file_options(argc, argv, &input, &options, &encoded_filepath);
1065
+ pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
821
1066
 
822
- VALUE value = parse_lex_input(&input, &options, false);
823
- pm_string_free(&input);
824
- pm_options_free(&options);
1067
+ result_t result = parse_lex_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath), false);
1068
+ pm_source_free(src);
1069
+ pm_options_free(options);
825
1070
 
826
- return value;
1071
+ return result_get(result);
827
1072
  }
828
1073
 
829
1074
  /******************************************************************************/
@@ -833,35 +1078,47 @@ lex_file(int argc, VALUE *argv, VALUE self) {
833
1078
  /**
834
1079
  * Parse the given input and return a ParseResult instance.
835
1080
  */
836
- static VALUE
837
- parse_input(pm_string_t *input, const pm_options_t *options) {
838
- pm_parser_t parser;
839
- pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
1081
+ static result_t
1082
+ parse_input(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
1083
+ pm_arena_t *arena = pm_arena_new();
1084
+ pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
1085
+
1086
+ pm_node_t *node = pm_parse(parser);
1087
+
1088
+ result_t result = check_raise_error_option(parser, options, path_encoding);
1089
+ if (result.type == RESULT_OK) {
1090
+ rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser));
840
1091
 
841
- pm_node_t *node = pm_parse(&parser);
842
- rb_encoding *encoding = rb_enc_find(parser.encoding->name);
1092
+ bool freeze = pm_options_freeze(options);
1093
+ VALUE comments = rb_ary_new_capa(pm_parser_comments_size(parser));
1094
+ VALUE attachments, ranges;
1095
+ parser_attachments_new(parser, &attachments, &ranges);
843
1096
 
844
- VALUE source = pm_source_new(&parser, encoding, options->freeze);
845
- VALUE value = pm_ast_new(&parser, node, encoding, source, options->freeze);
846
- VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source, options->freeze);
1097
+ VALUE source = pm_source_new(parser, encoding, comments, attachments, ranges, freeze);
1098
+ parser_comments(parser, comments, source, freeze);
1099
+ parser_attachments(parser, comments, attachments, ranges, freeze);
847
1100
 
848
- if (options->freeze) {
849
- rb_obj_freeze(source);
1101
+ VALUE value = pm_ast_new(parser, node, encoding, source, freeze);
1102
+ result = result_ok(parse_result_create(rb_cPrismParseResult, parser, value, encoding, source, freeze));
850
1103
  }
851
1104
 
852
- pm_node_destroy(&parser, node);
853
- pm_parser_free(&parser);
1105
+ pm_parser_free(parser);
1106
+ pm_arena_free(arena);
854
1107
 
855
1108
  return result;
856
1109
  }
857
1110
 
858
1111
  /**
1112
+ * :markup: markdown
859
1113
  * call-seq:
860
- * Prism::parse(source, **options) -> ParseResult
1114
+ * parse(source, **options) -> ParseResult
861
1115
  *
862
1116
  * Parse the given string and return a ParseResult instance. The options that
863
1117
  * are supported are:
864
1118
  *
1119
+ * * `attach_comments` - whether or not to attach each comment to the node that
1120
+ * owns it. When set, the comments on the result are sorted by start
1121
+ * offset. This should be a boolean or nil.
865
1122
  * * `command_line` - either nil or a string of the various options that were
866
1123
  * set on the command line. Valid values are combinations of "a", "l",
867
1124
  * "n", "p", and "x".
@@ -886,116 +1143,143 @@ parse_input(pm_string_t *input, const pm_options_t *options) {
886
1143
  * script that you know will be embedded inside another script later, but
887
1144
  * you do not have that context yet. For example, when parsing an ERB
888
1145
  * template that will be evaluated inside another script.
1146
+ * * `raise_error` - either nil, true, or a symbol indicating that an error
1147
+ * should be raised if the source contains any errors. The message of
1148
+ * the error will include the source of the lines that contain errors
1149
+ * when possible, and the value of this option controls how those lines
1150
+ * are formatted. Valid values are `:plain` (no formatting), `:style`
1151
+ * (bold formatting), and `:color` (bold and color formatting). When
1152
+ * `true` is given, the formatting is determined by whether or not
1153
+ * `$stderr` is a terminal and whether or not the `NO_COLOR` environment
1154
+ * variable is set, mirroring the behavior of CRuby itself. Syntax-level
1155
+ * errors raise SyntaxError, argument-level errors raise ArgumentError,
1156
+ * and load-level errors raise LoadError. Note that this option is
1157
+ * honored by every API that accepts these options, including predicates
1158
+ * like Prism.parse_success?, which will raise instead of returning a
1159
+ * boolean when the source contains errors.
889
1160
  * * `scopes` - the locals that are in scope surrounding the code that is being
890
1161
  * parsed. This should be an array of arrays of symbols or nil. Scopes are
891
1162
  * ordered from the outermost scope to the innermost one.
892
1163
  * * `version` - the version of Ruby syntax that prism should used to parse Ruby
893
- * code. By default prism assumes you want to parse with the latest
894
- * version of Ruby syntax (which you can trigger with `nil` or
895
- * `"latest"`). You may also restrict the syntax to a specific version of
896
- * Ruby, e.g., with `"3.3.0"`. To parse with the same syntax version that
897
- * the current Ruby is running use `version: "current"`. Raises
898
- * ArgumentError if the version is not currently supported by Prism.
1164
+ * code. By default prism parses with the syntax version nearest to the
1165
+ * current Ruby that is running (which you can trigger with `nil` or
1166
+ * `"nearest"`): the running version if prism supports it, otherwise the
1167
+ * closest version that it does support. You may also restrict the syntax
1168
+ * to a specific version of Ruby, e.g., with `"3.3.0"`. To parse with
1169
+ * exactly the same syntax version that the current Ruby is running use
1170
+ * `version: "current"`, which raises Prism::CurrentVersionError if prism
1171
+ * does not support it. To parse with the latest version of Ruby syntax
1172
+ * that prism supports, use `version: "latest"`. Raises ArgumentError if
1173
+ * the version is not currently supported by Prism.
899
1174
  */
900
1175
  static VALUE
901
1176
  parse(int argc, VALUE *argv, VALUE self) {
902
- pm_string_t input;
903
- pm_options_t options = { 0 };
904
- string_options(argc, argv, &input, &options);
1177
+ pm_options_t *options = pm_options_new();
1178
+ VALUE string = string_options(argc, argv, options);
1179
+
1180
+ const uint8_t *source = (const uint8_t *) RSTRING_PTR(string);
1181
+ size_t length = RSTRING_LEN(string);
905
1182
 
906
1183
  #ifdef PRISM_BUILD_DEBUG
907
- size_t length = pm_string_length(&input);
908
1184
  char* dup = xmalloc(length);
909
- memcpy(dup, pm_string_source(&input), length);
910
- pm_string_constant_init(&input, dup, length);
1185
+ memcpy(dup, source, length);
1186
+ source = (const uint8_t *) dup;
911
1187
  #endif
912
1188
 
913
- VALUE value = parse_input(&input, &options);
1189
+ result_t result = parse_input(source, length, options, NULL);
914
1190
 
915
1191
  #ifdef PRISM_BUILD_DEBUG
1192
+ #ifdef xfree_sized
1193
+ xfree_sized(dup, length);
1194
+ #else
916
1195
  xfree(dup);
1196
+ #endif
917
1197
  #endif
918
1198
 
919
- pm_string_free(&input);
920
- pm_options_free(&options);
921
- return value;
1199
+ pm_options_free(options);
1200
+ return result_get(result);
922
1201
  }
923
1202
 
924
1203
  /**
1204
+ * :markup: markdown
925
1205
  * call-seq:
926
- * Prism::parse_file(filepath, **options) -> ParseResult
1206
+ * parse_file(filepath, **options) -> ParseResult
927
1207
  *
928
1208
  * Parse the given file and return a ParseResult instance. For supported
929
- * options, see Prism::parse.
1209
+ * options, see Prism.parse.
930
1210
  */
931
1211
  static VALUE
932
1212
  parse_file(int argc, VALUE *argv, VALUE self) {
933
- pm_string_t input;
934
- pm_options_t options = { 0 };
1213
+ pm_options_t *options = pm_options_new();
935
1214
 
936
1215
  VALUE encoded_filepath;
937
- file_options(argc, argv, &input, &options, &encoded_filepath);
1216
+ pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
938
1217
 
939
- VALUE value = parse_input(&input, &options);
940
- pm_string_free(&input);
941
- pm_options_free(&options);
1218
+ result_t result = parse_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
1219
+ pm_source_free(src);
1220
+ pm_options_free(options);
942
1221
 
943
- return value;
1222
+ return result_get(result);
944
1223
  }
945
1224
 
946
1225
  /**
947
1226
  * Parse the given input and return nothing.
948
1227
  */
949
- static void
950
- profile_input(pm_string_t *input, const pm_options_t *options) {
951
- pm_parser_t parser;
952
- pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
1228
+ static result_t
1229
+ profile_input(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
1230
+ pm_arena_t *arena = pm_arena_new();
1231
+ pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
1232
+
1233
+ pm_parse(parser);
953
1234
 
954
- pm_node_t *node = pm_parse(&parser);
955
- pm_node_destroy(&parser, node);
956
- pm_parser_free(&parser);
1235
+ result_t result = check_raise_error_option(parser, options, path_encoding);
1236
+ pm_parser_free(parser);
1237
+ pm_arena_free(arena);
1238
+
1239
+ return result;
957
1240
  }
958
1241
 
959
1242
  /**
1243
+ * :markup: markdown
960
1244
  * call-seq:
961
- * Prism::profile(source, **options) -> nil
1245
+ * profile(source, **options) -> nil
962
1246
  *
963
1247
  * Parse the given string and return nothing. This method is meant to allow
964
1248
  * profilers to avoid the overhead of reifying the AST to Ruby. For supported
965
- * options, see Prism::parse.
1249
+ * options, see Prism.parse.
966
1250
  */
967
1251
  static VALUE
968
1252
  profile(int argc, VALUE *argv, VALUE self) {
969
- pm_string_t input;
970
- pm_options_t options = { 0 };
1253
+ pm_options_t *options = pm_options_new();
1254
+ VALUE string = string_options(argc, argv, options);
971
1255
 
972
- string_options(argc, argv, &input, &options);
973
- profile_input(&input, &options);
974
- pm_string_free(&input);
975
- pm_options_free(&options);
1256
+ result_t result = profile_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL);
1257
+ pm_options_free(options);
1258
+ result_get(result);
976
1259
 
977
1260
  return Qnil;
978
1261
  }
979
1262
 
980
1263
  /**
1264
+ * :markup: markdown
981
1265
  * call-seq:
982
- * Prism::profile_file(filepath, **options) -> nil
1266
+ * profile_file(filepath, **options) -> nil
983
1267
  *
984
1268
  * Parse the given file and return nothing. This method is meant to allow
985
1269
  * profilers to avoid the overhead of reifying the AST to Ruby. For supported
986
- * options, see Prism::parse.
1270
+ * options, see Prism.parse.
987
1271
  */
988
1272
  static VALUE
989
1273
  profile_file(int argc, VALUE *argv, VALUE self) {
990
- pm_string_t input;
991
- pm_options_t options = { 0 };
1274
+ pm_options_t *options = pm_options_new();
992
1275
 
993
1276
  VALUE encoded_filepath;
994
- file_options(argc, argv, &input, &options, &encoded_filepath);
1277
+ pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
995
1278
 
996
- profile_input(&input, &options);
997
- pm_string_free(&input);
998
- pm_options_free(&options);
1279
+ result_t result = profile_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
1280
+ pm_source_free(src);
1281
+ pm_options_free(options);
1282
+ result_get(result);
999
1283
 
1000
1284
  return Qnil;
1001
1285
  }
@@ -1009,13 +1293,40 @@ parse_stream_eof(void *stream) {
1009
1293
  }
1010
1294
 
1011
1295
  /**
1012
- * An implementation of fgets that is suitable for use with Ruby IO objects.
1296
+ * The largest number of bytes a single character can occupy in any encoding
1297
+ * that Ruby supports (CESU-8). `IO#gets(limit)` treats its argument as a soft
1298
+ * limit: to avoid splitting a multi-byte character it may return up to
1299
+ * `MAX_ENC_LEN - 1` more bytes than requested. Reserving `MAX_ENC_LEN` bytes of
1300
+ * headroom therefore leaves room for both that overshoot and the terminating
1301
+ * NUL byte.
1302
+ *
1303
+ * The value can be re-derived from the Ruby source tree with:
1304
+ *
1305
+ * grep -Erh "max (enc|byte) length" enc | grep -Eo '[0-9]+' | sort | tail -n1
1306
+ */
1307
+ #define MAX_ENC_LEN 6
1308
+
1309
+ /**
1310
+ * An implementation of fgets that is suitable for use with Ruby IO objects. As
1311
+ * required by pm_source_stream_fgets_t, this never writes more than `size`
1312
+ * bytes into `string` (including the terminating NUL byte).
1013
1313
  */
1014
1314
  static char *
1015
1315
  parse_stream_fgets(char *string, int size, void *stream) {
1016
- RUBY_ASSERT(size > 0);
1316
+ RUBY_ASSERT(size > MAX_ENC_LEN);
1017
1317
 
1018
- VALUE line = rb_funcall((VALUE) stream, rb_intern("gets"), 1, INT2FIX(size - 1));
1318
+ /*
1319
+ * Request fewer bytes than the buffer can hold. `gets` may return more
1320
+ * bytes than requested when the limit falls in the middle of a multi-byte
1321
+ * character, and the reserved headroom guarantees the result still fits.
1322
+ */
1323
+ VALUE line = rb_funcall((VALUE) stream, rb_intern("gets"), 1, INT2FIX(size - MAX_ENC_LEN));
1324
+
1325
+ /*
1326
+ * A well-behaved stream returns a String, or nil at EOF. Coerce anything
1327
+ * else to nil so that we never treat a non-String as a byte buffer.
1328
+ */
1329
+ line = rb_check_string_type(line);
1019
1330
  if (NIL_P(line)) {
1020
1331
  return NULL;
1021
1332
  }
@@ -1023,18 +1334,30 @@ parse_stream_fgets(char *string, int size, void *stream) {
1023
1334
  const char *cstr = RSTRING_PTR(line);
1024
1335
  long length = RSTRING_LEN(line);
1025
1336
 
1026
- memcpy(string, cstr, length);
1337
+ /*
1338
+ * Defensively clamp the copy. A misbehaving `gets` may ignore the limit
1339
+ * entirely and return an arbitrarily long string; we must never write past
1340
+ * the caller's buffer. One byte is reserved for the NUL terminator.
1341
+ */
1342
+ if (length > (long) (size - 1)) {
1343
+ length = (long) (size - 1);
1344
+ }
1345
+
1346
+ memcpy(string, cstr, (size_t) length);
1027
1347
  string[length] = '\0';
1028
1348
 
1029
1349
  return string;
1030
1350
  }
1031
1351
 
1352
+ #undef MAX_ENC_LEN
1353
+
1032
1354
  /**
1355
+ * :markup: markdown
1033
1356
  * call-seq:
1034
- * Prism::parse_stream(stream, **options) -> ParseResult
1357
+ * parse_stream(stream, **options) -> ParseResult
1035
1358
  *
1036
1359
  * Parse the given object that responds to `gets` and return a ParseResult
1037
- * instance. The options that are supported are the same as Prism::parse.
1360
+ * instance. The options that are supported are the same as Prism.parse.
1038
1361
  */
1039
1362
  static VALUE
1040
1363
  parse_stream(int argc, VALUE *argv, VALUE self) {
@@ -1042,187 +1365,213 @@ parse_stream(int argc, VALUE *argv, VALUE self) {
1042
1365
  VALUE keywords;
1043
1366
  rb_scan_args(argc, argv, "1:", &stream, &keywords);
1044
1367
 
1045
- pm_options_t options = { 0 };
1046
- extract_options(&options, Qnil, keywords);
1368
+ pm_options_t *options = pm_options_new();
1369
+ extract_options(options, Qnil, keywords);
1047
1370
 
1048
- pm_parser_t parser;
1049
- pm_buffer_t buffer;
1371
+ pm_source_t *src = pm_source_stream_new((void *) stream, parse_stream_fgets, parse_stream_eof);
1372
+ pm_arena_t *arena = pm_arena_new();
1373
+ pm_parser_t *parser;
1050
1374
 
1051
- pm_node_t *node = pm_parse_stream(&parser, &buffer, (void *) stream, parse_stream_fgets, parse_stream_eof, &options);
1052
- rb_encoding *encoding = rb_enc_find(parser.encoding->name);
1375
+ pm_node_t *node = pm_parse_stream(&parser, arena, src, options);
1053
1376
 
1054
- VALUE source = pm_source_new(&parser, encoding, options.freeze);
1055
- VALUE value = pm_ast_new(&parser, node, encoding, source, options.freeze);
1056
- VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source, options.freeze);
1377
+ result_t result = check_raise_error_option(parser, options, NULL);
1378
+ if (result.type == RESULT_OK) {
1379
+ rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser));
1057
1380
 
1058
- pm_node_destroy(&parser, node);
1059
- pm_buffer_free(&buffer);
1060
- pm_parser_free(&parser);
1381
+ VALUE comments = rb_ary_new_capa(pm_parser_comments_size(parser));
1382
+ VALUE attachments, ranges;
1383
+ parser_attachments_new(parser, &attachments, &ranges);
1061
1384
 
1062
- return result;
1385
+ VALUE source = pm_source_new(parser, encoding, comments, attachments, ranges, pm_options_freeze(options));
1386
+ parser_comments(parser, comments, source, pm_options_freeze(options));
1387
+ parser_attachments(parser, comments, attachments, ranges, pm_options_freeze(options));
1388
+
1389
+ VALUE value = pm_ast_new(parser, node, encoding, source, pm_options_freeze(options));
1390
+
1391
+ result = result_ok(parse_result_create(rb_cPrismParseResult, parser, value, encoding, source, pm_options_freeze(options)));
1392
+ }
1393
+
1394
+ pm_source_free(src);
1395
+ pm_parser_free(parser);
1396
+ pm_arena_free(arena);
1397
+ pm_options_free(options);
1398
+
1399
+ return result_get(result);
1063
1400
  }
1064
1401
 
1065
1402
  /**
1066
1403
  * Parse the given input and return an array of Comment objects.
1067
1404
  */
1068
- static VALUE
1069
- parse_input_comments(pm_string_t *input, const pm_options_t *options) {
1070
- pm_parser_t parser;
1071
- pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
1405
+ static result_t
1406
+ parse_input_comments(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
1407
+ pm_arena_t *arena = pm_arena_new();
1408
+ pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
1072
1409
 
1073
- pm_node_t *node = pm_parse(&parser);
1074
- rb_encoding *encoding = rb_enc_find(parser.encoding->name);
1410
+ pm_parse(parser);
1075
1411
 
1076
- VALUE source = pm_source_new(&parser, encoding, options->freeze);
1077
- VALUE comments = parser_comments(&parser, source, options->freeze);
1412
+ result_t result = check_raise_error_option(parser, options, path_encoding);
1413
+ if (result.type == RESULT_OK) {
1414
+ rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser));
1078
1415
 
1079
- pm_node_destroy(&parser, node);
1080
- pm_parser_free(&parser);
1416
+ VALUE comments = rb_ary_new_capa(pm_parser_comments_size(parser));
1417
+ VALUE attachments, ranges;
1418
+ parser_attachments_new(parser, &attachments, &ranges);
1419
+
1420
+ VALUE source = pm_source_new(parser, encoding, comments, attachments, ranges, pm_options_freeze(options));
1421
+ parser_comments(parser, comments, source, pm_options_freeze(options));
1422
+ parser_attachments(parser, comments, attachments, ranges, pm_options_freeze(options));
1423
+
1424
+ result = result_ok(comments);
1425
+ }
1081
1426
 
1082
- return comments;
1427
+ pm_parser_free(parser);
1428
+ pm_arena_free(arena);
1429
+
1430
+ return result;
1083
1431
  }
1084
1432
 
1085
1433
  /**
1434
+ * :markup: markdown
1086
1435
  * call-seq:
1087
- * Prism::parse_comments(source, **options) -> Array
1436
+ * parse_comments(source, **options) -> Array
1088
1437
  *
1089
1438
  * Parse the given string and return an array of Comment objects. For supported
1090
- * options, see Prism::parse.
1439
+ * options, see Prism.parse.
1091
1440
  */
1092
1441
  static VALUE
1093
1442
  parse_comments(int argc, VALUE *argv, VALUE self) {
1094
- pm_string_t input;
1095
- pm_options_t options = { 0 };
1096
- string_options(argc, argv, &input, &options);
1443
+ pm_options_t *options = pm_options_new();
1444
+ VALUE string = string_options(argc, argv, options);
1097
1445
 
1098
- VALUE result = parse_input_comments(&input, &options);
1099
- pm_string_free(&input);
1100
- pm_options_free(&options);
1446
+ result_t result = parse_input_comments((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL);
1447
+ pm_options_free(options);
1101
1448
 
1102
- return result;
1449
+ return result_get(result);
1103
1450
  }
1104
1451
 
1105
1452
  /**
1453
+ * :markup: markdown
1106
1454
  * call-seq:
1107
- * Prism::parse_file_comments(filepath, **options) -> Array
1455
+ * parse_file_comments(filepath, **options) -> Array
1108
1456
  *
1109
1457
  * Parse the given file and return an array of Comment objects. For supported
1110
- * options, see Prism::parse.
1458
+ * options, see Prism.parse.
1111
1459
  */
1112
1460
  static VALUE
1113
1461
  parse_file_comments(int argc, VALUE *argv, VALUE self) {
1114
- pm_string_t input;
1115
- pm_options_t options = { 0 };
1462
+ pm_options_t *options = pm_options_new();
1116
1463
 
1117
1464
  VALUE encoded_filepath;
1118
- file_options(argc, argv, &input, &options, &encoded_filepath);
1465
+ pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
1119
1466
 
1120
- VALUE value = parse_input_comments(&input, &options);
1121
- pm_string_free(&input);
1122
- pm_options_free(&options);
1467
+ result_t result = parse_input_comments(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
1468
+ pm_source_free(src);
1469
+ pm_options_free(options);
1123
1470
 
1124
- return value;
1471
+ return result_get(result);
1125
1472
  }
1126
1473
 
1127
1474
  /**
1475
+ * :markup: markdown
1128
1476
  * call-seq:
1129
- * Prism::parse_lex(source, **options) -> ParseLexResult
1477
+ * parse_lex(source, **options) -> ParseLexResult
1130
1478
  *
1131
1479
  * Parse the given string and return a ParseLexResult instance that contains a
1132
1480
  * 2-element array, where the first element is the AST and the second element is
1133
1481
  * an array of Token instances.
1134
1482
  *
1135
1483
  * This API is only meant to be used in the case where you need both the AST and
1136
- * the tokens. If you only need one or the other, use either Prism::parse or
1137
- * Prism::lex.
1484
+ * the tokens. If you only need one or the other, use either Prism.parse or
1485
+ * Prism.lex.
1138
1486
  *
1139
- * For supported options, see Prism::parse.
1487
+ * For supported options, see Prism.parse.
1140
1488
  */
1141
1489
  static VALUE
1142
1490
  parse_lex(int argc, VALUE *argv, VALUE self) {
1143
- pm_string_t input;
1144
- pm_options_t options = { 0 };
1145
- string_options(argc, argv, &input, &options);
1491
+ pm_options_t *options = pm_options_new();
1492
+ VALUE string = string_options(argc, argv, options);
1146
1493
 
1147
- VALUE value = parse_lex_input(&input, &options, true);
1148
- pm_string_free(&input);
1149
- pm_options_free(&options);
1494
+ result_t result = parse_lex_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL, true);
1495
+ pm_options_free(options);
1150
1496
 
1151
- return value;
1497
+ return result_get(result);
1152
1498
  }
1153
1499
 
1154
1500
  /**
1501
+ * :markup: markdown
1155
1502
  * call-seq:
1156
- * Prism::parse_lex_file(filepath, **options) -> ParseLexResult
1503
+ * parse_lex_file(filepath, **options) -> ParseLexResult
1157
1504
  *
1158
1505
  * Parse the given file and return a ParseLexResult instance that contains a
1159
1506
  * 2-element array, where the first element is the AST and the second element is
1160
1507
  * an array of Token instances.
1161
1508
  *
1162
1509
  * This API is only meant to be used in the case where you need both the AST and
1163
- * the tokens. If you only need one or the other, use either Prism::parse_file
1164
- * or Prism::lex_file.
1510
+ * the tokens. If you only need one or the other, use either Prism.parse_file
1511
+ * or Prism.lex_file.
1165
1512
  *
1166
- * For supported options, see Prism::parse.
1513
+ * For supported options, see Prism.parse.
1167
1514
  */
1168
1515
  static VALUE
1169
1516
  parse_lex_file(int argc, VALUE *argv, VALUE self) {
1170
- pm_string_t input;
1171
- pm_options_t options = { 0 };
1517
+ pm_options_t *options = pm_options_new();
1172
1518
 
1173
1519
  VALUE encoded_filepath;
1174
- file_options(argc, argv, &input, &options, &encoded_filepath);
1520
+ pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
1175
1521
 
1176
- VALUE value = parse_lex_input(&input, &options, true);
1177
- pm_string_free(&input);
1178
- pm_options_free(&options);
1522
+ result_t result = parse_lex_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath), true);
1523
+ pm_source_free(src);
1524
+ pm_options_free(options);
1179
1525
 
1180
- return value;
1526
+ return result_get(result);
1181
1527
  }
1182
1528
 
1183
1529
  /**
1184
1530
  * Parse the given input and return true if it parses without errors.
1185
1531
  */
1186
- static VALUE
1187
- parse_input_success_p(pm_string_t *input, const pm_options_t *options) {
1188
- pm_parser_t parser;
1189
- pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
1190
-
1191
- pm_node_t *node = pm_parse(&parser);
1192
- pm_node_destroy(&parser, node);
1532
+ static result_t
1533
+ parse_input_success_p(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
1534
+ pm_arena_t *arena = pm_arena_new();
1535
+ pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
1536
+ pm_parse(parser);
1537
+
1538
+ result_t result = check_raise_error_option(parser, options, path_encoding);
1539
+ if (result.type == RESULT_OK) {
1540
+ result = result_ok(pm_parser_errors_size(parser) == 0 ? Qtrue : Qfalse);
1541
+ }
1193
1542
 
1194
- VALUE result = parser.error_list.size == 0 ? Qtrue : Qfalse;
1195
- pm_parser_free(&parser);
1543
+ pm_parser_free(parser);
1544
+ pm_arena_free(arena);
1196
1545
 
1197
1546
  return result;
1198
1547
  }
1199
1548
 
1200
1549
  /**
1550
+ * :markup: markdown
1201
1551
  * call-seq:
1202
- * Prism::parse_success?(source, **options) -> bool
1552
+ * parse_success?(source, **options) -> bool
1203
1553
  *
1204
1554
  * Parse the given string and return true if it parses without errors. For
1205
- * supported options, see Prism::parse.
1555
+ * supported options, see Prism.parse.
1206
1556
  */
1207
1557
  static VALUE
1208
1558
  parse_success_p(int argc, VALUE *argv, VALUE self) {
1209
- pm_string_t input;
1210
- pm_options_t options = { 0 };
1211
- string_options(argc, argv, &input, &options);
1559
+ pm_options_t *options = pm_options_new();
1560
+ VALUE string = string_options(argc, argv, options);
1212
1561
 
1213
- VALUE result = parse_input_success_p(&input, &options);
1214
- pm_string_free(&input);
1215
- pm_options_free(&options);
1562
+ result_t result = parse_input_success_p((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL);
1563
+ pm_options_free(options);
1216
1564
 
1217
- return result;
1565
+ return result_get(result);
1218
1566
  }
1219
1567
 
1220
1568
  /**
1569
+ * :markup: markdown
1221
1570
  * call-seq:
1222
- * Prism::parse_failure?(source, **options) -> bool
1571
+ * parse_failure?(source, **options) -> bool
1223
1572
  *
1224
1573
  * Parse the given string and return true if it parses with errors. For
1225
- * supported options, see Prism::parse.
1574
+ * supported options, see Prism.parse.
1226
1575
  */
1227
1576
  static VALUE
1228
1577
  parse_failure_p(int argc, VALUE *argv, VALUE self) {
@@ -1230,33 +1579,34 @@ parse_failure_p(int argc, VALUE *argv, VALUE self) {
1230
1579
  }
1231
1580
 
1232
1581
  /**
1582
+ * :markup: markdown
1233
1583
  * call-seq:
1234
- * Prism::parse_file_success?(filepath, **options) -> bool
1584
+ * parse_file_success?(filepath, **options) -> bool
1235
1585
  *
1236
1586
  * Parse the given file and return true if it parses without errors. For
1237
- * supported options, see Prism::parse.
1587
+ * supported options, see Prism.parse.
1238
1588
  */
1239
1589
  static VALUE
1240
1590
  parse_file_success_p(int argc, VALUE *argv, VALUE self) {
1241
- pm_string_t input;
1242
- pm_options_t options = { 0 };
1591
+ pm_options_t *options = pm_options_new();
1243
1592
 
1244
1593
  VALUE encoded_filepath;
1245
- file_options(argc, argv, &input, &options, &encoded_filepath);
1594
+ pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
1246
1595
 
1247
- VALUE result = parse_input_success_p(&input, &options);
1248
- pm_string_free(&input);
1249
- pm_options_free(&options);
1596
+ result_t result = parse_input_success_p(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
1597
+ pm_source_free(src);
1598
+ pm_options_free(options);
1250
1599
 
1251
- return result;
1600
+ return result_get(result);
1252
1601
  }
1253
1602
 
1254
1603
  /**
1604
+ * :markup: markdown
1255
1605
  * call-seq:
1256
- * Prism::parse_file_failure?(filepath, **options) -> bool
1606
+ * parse_file_failure?(filepath, **options) -> bool
1257
1607
  *
1258
1608
  * Parse the given file and return true if it parses with errors. For
1259
- * supported options, see Prism::parse.
1609
+ * supported options, see Prism.parse.
1260
1610
  */
1261
1611
  static VALUE
1262
1612
  parse_file_failure_p(int argc, VALUE *argv, VALUE self) {
@@ -1286,8 +1636,9 @@ string_query(pm_string_query_t result) {
1286
1636
  }
1287
1637
 
1288
1638
  /**
1639
+ * :markup: markdown
1289
1640
  * call-seq:
1290
- * Prism::StringQuery::local?(string) -> bool
1641
+ * local?(string) -> bool
1291
1642
  *
1292
1643
  * Returns true if the string constitutes a valid local variable name. Note that
1293
1644
  * this means the names that can be set through Binding#local_variable_set, not
@@ -1300,8 +1651,9 @@ string_query_local_p(VALUE self, VALUE string) {
1300
1651
  }
1301
1652
 
1302
1653
  /**
1654
+ * :markup: markdown
1303
1655
  * call-seq:
1304
- * Prism::StringQuery::constant?(string) -> bool
1656
+ * constant?(string) -> bool
1305
1657
  *
1306
1658
  * Returns true if the string constitutes a valid constant name. Note that this
1307
1659
  * means the names that can be set through Module#const_set, not necessarily the
@@ -1314,8 +1666,9 @@ string_query_constant_p(VALUE self, VALUE string) {
1314
1666
  }
1315
1667
 
1316
1668
  /**
1669
+ * :markup: markdown
1317
1670
  * call-seq:
1318
- * Prism::StringQuery::method_name?(string) -> bool
1671
+ * method_name?(string) -> bool
1319
1672
  *
1320
1673
  * Returns true if the string constitutes a valid method name.
1321
1674
  */
@@ -1334,8 +1687,8 @@ string_query_method_name_p(VALUE self, VALUE string) {
1334
1687
  */
1335
1688
  RUBY_FUNC_EXPORTED void
1336
1689
  Init_prism(void) {
1337
- // Make sure that the prism library version matches the expected version.
1338
- // Otherwise something was compiled incorrectly.
1690
+ /* Make sure that the prism library version matches the expected version.
1691
+ * Otherwise something was compiled incorrectly. */
1339
1692
  if (strcmp(pm_version(), EXPECTED_PRISM_VERSION) != 0) {
1340
1693
  rb_raise(
1341
1694
  rb_eRuntimeError,
@@ -1346,12 +1699,10 @@ Init_prism(void) {
1346
1699
  }
1347
1700
 
1348
1701
  #ifdef HAVE_RB_EXT_RACTOR_SAFE
1349
- // Mark this extension as Ractor-safe.
1702
+ /* Mark this extension as Ractor-safe. */
1350
1703
  rb_ext_ractor_safe(true);
1351
1704
  #endif
1352
1705
 
1353
- // Grab up references to all of the constants that we're going to need to
1354
- // reference throughout this extension.
1355
1706
  rb_cPrism = rb_define_module("Prism");
1356
1707
  rb_cPrismNode = rb_define_class_under(rb_cPrism, "Node", rb_cObject);
1357
1708
  rb_cPrismSource = rb_define_class_under(rb_cPrism, "Source", rb_cObject);
@@ -1372,8 +1723,9 @@ Init_prism(void) {
1372
1723
 
1373
1724
  rb_cPrismCurrentVersionError = rb_const_get(rb_cPrism, rb_intern("CurrentVersionError"));
1374
1725
 
1375
- // Intern all of the IDs eagerly that we support so that we don't have to do
1376
- // it every time we parse.
1726
+ /* Intern all of the IDs eagerly that we support so that we do not have to
1727
+ * do it every time we parse. */
1728
+ rb_id_option_attach_comments = rb_intern_const("attach_comments");
1377
1729
  rb_id_option_command_line = rb_intern_const("command_line");
1378
1730
  rb_id_option_encoding = rb_intern_const("encoding");
1379
1731
  rb_id_option_filepath = rb_intern_const("filepath");
@@ -1382,20 +1734,26 @@ Init_prism(void) {
1382
1734
  rb_id_option_line = rb_intern_const("line");
1383
1735
  rb_id_option_main_script = rb_intern_const("main_script");
1384
1736
  rb_id_option_partial_script = rb_intern_const("partial_script");
1737
+ rb_id_option_raise_error = rb_intern_const("raise_error");
1385
1738
  rb_id_option_scopes = rb_intern_const("scopes");
1386
1739
  rb_id_option_version = rb_intern_const("version");
1740
+
1387
1741
  rb_id_source_for = rb_intern("for");
1742
+
1388
1743
  rb_id_forwarding_positionals = rb_intern("*");
1389
1744
  rb_id_forwarding_keywords = rb_intern("**");
1390
1745
  rb_id_forwarding_block = rb_intern("&");
1391
1746
  rb_id_forwarding_all = rb_intern("...");
1392
1747
 
1748
+ rb_id_raise_error_plain = rb_intern_const("plain");
1749
+ rb_id_raise_error_style = rb_intern_const("style");
1750
+ rb_id_raise_error_color = rb_intern_const("color");
1751
+
1393
1752
  /**
1394
1753
  * The version of the prism library.
1395
1754
  */
1396
1755
  rb_define_const(rb_cPrism, "VERSION", rb_str_freeze(rb_str_new_cstr(EXPECTED_PRISM_VERSION)));
1397
1756
 
1398
- // First, the functions that have to do with lexing and parsing.
1399
1757
  rb_define_singleton_method(rb_cPrism, "lex", lex, -1);
1400
1758
  rb_define_singleton_method(rb_cPrism, "lex_file", lex_file, -1);
1401
1759
  rb_define_singleton_method(rb_cPrism, "parse", parse, -1);
@@ -1421,7 +1779,5 @@ Init_prism(void) {
1421
1779
  rb_define_singleton_method(rb_cPrismStringQuery, "constant?", string_query_constant_p, 1);
1422
1780
  rb_define_singleton_method(rb_cPrismStringQuery, "method_name?", string_query_method_name_p, 1);
1423
1781
 
1424
- // Next, initialize the other APIs.
1425
1782
  Init_prism_api_node();
1426
- Init_prism_pack();
1427
1783
  }