Ruby 4.1.0dev (2026-09-21 revision 61de3dd727146cfcf24e8051595bb2fb842f37aa)
extension.c
1#include "prism/extension.h"
2
3#ifdef _WIN32
4#include <ruby/win32.h>
5#endif
6
7#include <errno.h>
8
9// NOTE: this file should contain only bindings. All non-trivial logic should be
10// in libprism so it can be shared its the various callers.
11
12VALUE rb_cPrism;
13VALUE rb_cPrismNode;
14VALUE rb_cPrismSource;
15VALUE rb_cPrismToken;
16VALUE rb_cPrismLocation;
17
18VALUE rb_cPrismComment;
19VALUE rb_cPrismInlineComment;
20VALUE rb_cPrismEmbDocComment;
21VALUE rb_cPrismMagicComment;
22VALUE rb_cPrismParseError;
23VALUE rb_cPrismParseWarning;
24VALUE rb_cPrismResult;
25VALUE rb_cPrismParseResult;
26VALUE rb_cPrismLexResult;
27VALUE rb_cPrismParseLexResult;
28VALUE rb_cPrismStringQuery;
29VALUE rb_cPrismScope;
30VALUE rb_cPrismCurrentVersionError;
31
32VALUE rb_cPrismDebugEncoding;
33
34ID rb_id_option_command_line;
35ID rb_id_option_encoding;
36ID rb_id_option_filepath;
37ID rb_id_option_freeze;
38ID rb_id_option_frozen_string_literal;
39ID rb_id_option_line;
40ID rb_id_option_main_script;
41ID rb_id_option_partial_script;
42ID rb_id_option_raise_error;
43ID rb_id_option_scopes;
44ID rb_id_option_version;
45
46ID rb_id_source_for;
47
48ID rb_id_forwarding_positionals;
49ID rb_id_forwarding_keywords;
50ID rb_id_forwarding_block;
51ID rb_id_forwarding_all;
52
53ID rb_id_raise_error_plain;
54ID rb_id_raise_error_style;
55ID rb_id_raise_error_color;
56
57/******************************************************************************/
58/* Result struct for working with functions that may need to raise errors. */
59/******************************************************************************/
60
61typedef struct {
62 enum {
63 RESULT_OK,
64 RESULT_ERR
65 } type;
66 VALUE value;
67} result_t;
68
69static result_t
70result_ok(VALUE value) {
71 return (result_t) { .type = RESULT_OK, .value = value };
72}
73
74static result_t
75result_err(VALUE value) {
76 return (result_t) { .type = RESULT_ERR, .value = value };
77}
78
79static VALUE
80result_get(result_t result) {
81 if (result.type == RESULT_OK) {
82 return result.value;
83 } else {
84 rb_exc_raise(result.value);
85 }
86}
87
88/******************************************************************************/
89/* IO of Ruby code */
90/******************************************************************************/
91
96static const char *
97check_string(VALUE value) {
98 // Check if the value is a string. If it's not, then raise a type error.
99 if (!RB_TYPE_P(value, T_STRING)) {
100 rb_raise(rb_eTypeError, "wrong argument type %" PRIsVALUE " (expected String)", rb_obj_class(value));
101 }
102
103 // Otherwise, return the value as a C string.
104 return RSTRING_PTR(value);
105}
106
107/******************************************************************************/
108/* Building C options from Ruby options */
109/******************************************************************************/
110
114static void
115build_options_scopes(pm_options_t *options, VALUE scopes) {
116 // Check if the value is an array. If it's not, then raise a type error.
117 if (!RB_TYPE_P(scopes, T_ARRAY)) {
118 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(scopes));
119 }
120
121 // Initialize the scopes array.
122 size_t scopes_count = RARRAY_LEN(scopes);
123 if (!pm_options_scopes_init(options, scopes_count)) {
124 rb_raise(rb_eNoMemError, "failed to allocate memory");
125 }
126
127 // Iterate over the scopes and add them to the options.
128 for (size_t scope_index = 0; scope_index < scopes_count; scope_index++) {
129 VALUE scope = rb_ary_entry(scopes, scope_index);
130
131 // The scope can be either an array or it can be a Prism::Scope object.
132 // Parse out the correct values here from either.
133 VALUE locals;
134 uint8_t forwarding = PM_OPTIONS_SCOPE_FORWARDING_NONE;
135
136 if (RB_TYPE_P(scope, T_ARRAY)) {
137 locals = scope;
138 } else if (rb_obj_is_kind_of(scope, rb_cPrismScope)) {
139 locals = rb_ivar_get(scope, rb_intern("@locals"));
140 if (!RB_TYPE_P(locals, T_ARRAY)) {
141 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(locals));
142 }
143
144 VALUE names = rb_ivar_get(scope, rb_intern("@forwarding"));
145 if (!RB_TYPE_P(names, T_ARRAY)) {
146 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(names));
147 }
148
149 size_t names_count = RARRAY_LEN(names);
150 for (size_t name_index = 0; name_index < names_count; name_index++) {
151 VALUE name = rb_ary_entry(names, name_index);
152
153 // Check that the name is a symbol. If it's not, then raise
154 // a type error.
155 if (!RB_TYPE_P(name, T_SYMBOL)) {
156 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Symbol)", rb_obj_class(name));
157 }
158
159 ID id = SYM2ID(name);
160 if (id == rb_id_forwarding_positionals) {
162 } else if (id == rb_id_forwarding_keywords) {
164 } else if (id == rb_id_forwarding_block) {
166 } else if (id == rb_id_forwarding_all) {
168 } else {
169 rb_raise(rb_eArgError, "invalid forwarding value: %" PRIsVALUE, name);
170 }
171 }
172 } else {
173 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array or Prism::Scope)", rb_obj_class(scope));
174 }
175
176 // Initialize the scope array.
177 size_t locals_count = RARRAY_LEN(locals);
178 pm_options_scope_t *options_scope = pm_options_scope_mut(options, scope_index);
179 pm_options_scope_init(options_scope, locals_count);
180
181 // Iterate over the locals and add them to the scope.
182 for (size_t local_index = 0; local_index < locals_count; local_index++) {
183 VALUE local = rb_ary_entry(locals, local_index);
184
185 // Check that the local is a symbol. If it's not, then raise a
186 // type error.
187 if (!RB_TYPE_P(local, T_SYMBOL)) {
188 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Symbol)", rb_obj_class(local));
189 }
190
191 // Add the local to the scope.
192 pm_string_t *scope_local = pm_options_scope_local_mut(options_scope, local_index);
193 const char *name = rb_id2name(SYM2ID(local));
194 pm_string_constant_init(scope_local, name, strlen(name));
195 }
196
197 // Now set the forwarding options.
198 pm_options_scope_forwarding_set(options_scope, forwarding);
199 }
200}
201
205static int
206build_options_i(VALUE key, VALUE value, VALUE argument) {
207 pm_options_t *options = (pm_options_t *) argument;
208 ID key_id = SYM2ID(key);
209
210 if (key_id == rb_id_option_filepath) {
211 if (!NIL_P(value)) pm_options_filepath_set(options, check_string(value));
212 } else if (key_id == rb_id_option_encoding) {
213 if (!NIL_P(value)) {
214 if (value == Qfalse) {
215 pm_options_encoding_locked_set(options, true);
216 } else {
217 pm_options_encoding_set(options, rb_enc_name(rb_to_encoding(value)));
218 }
219 }
220 } else if (key_id == rb_id_option_line) {
221 if (!NIL_P(value)) pm_options_line_set(options, NUM2INT(value));
222 } else if (key_id == rb_id_option_frozen_string_literal) {
223 if (!NIL_P(value)) pm_options_frozen_string_literal_set(options, RTEST(value));
224 } else if (key_id == rb_id_option_version) {
225 if (!NIL_P(value)) {
226 const char *version = check_string(value);
227
228 if (RSTRING_LEN(value) == 7 && strncmp(version, "current", 7) == 0) {
229 if (!pm_options_version_set(options, ruby_version, 3)) {
230 rb_exc_raise(rb_exc_new_cstr(rb_cPrismCurrentVersionError, ruby_version));
231 }
232 } else if (RSTRING_LEN(value) == 7 && strncmp(version, "nearest", 7) == 0) {
233 if (!pm_options_version_set(options, ruby_version, 3)) {
234 // Prism doesn't know this specific version. Is it lower?
235 if (ruby_version[0] < '3' || (ruby_version[0] == '3' && ruby_version[2] < '3')) {
236 pm_options_version_set_lowest(options);
237 } else {
238 // Must be higher.
239 pm_options_version_set_highest(options);
240 }
241 }
242 } else if (!pm_options_version_set(options, version, RSTRING_LEN(value))) {
243 rb_raise(rb_eArgError, "invalid version: %" PRIsVALUE, value);
244 }
245 }
246 } else if (key_id == rb_id_option_scopes) {
247 if (!NIL_P(value)) build_options_scopes(options, value);
248 } else if (key_id == rb_id_option_command_line) {
249 if (!NIL_P(value)) {
250 const char *string = check_string(value);
251 uint8_t command_line = 0;
252
253 for (size_t index = 0; index < strlen(string); index++) {
254 switch (string[index]) {
255 case 'a': command_line |= PM_OPTIONS_COMMAND_LINE_A; break;
256 case 'e': command_line |= PM_OPTIONS_COMMAND_LINE_E; break;
257 case 'l': command_line |= PM_OPTIONS_COMMAND_LINE_L; break;
258 case 'n': command_line |= PM_OPTIONS_COMMAND_LINE_N; break;
259 case 'p': command_line |= PM_OPTIONS_COMMAND_LINE_P; break;
260 case 'x': command_line |= PM_OPTIONS_COMMAND_LINE_X; break;
261 default: rb_raise(rb_eArgError, "invalid command line flag: '%c'", string[index]); break;
262 }
263 }
264
265 pm_options_command_line_set(options, command_line);
266 }
267 } else if (key_id == rb_id_option_main_script) {
268 if (!NIL_P(value)) pm_options_main_script_set(options, RTEST(value));
269 } else if (key_id == rb_id_option_partial_script) {
270 if (!NIL_P(value)) pm_options_partial_script_set(options, RTEST(value));
271 } else if (key_id == rb_id_option_freeze) {
272 if (!NIL_P(value)) pm_options_freeze_set(options, RTEST(value));
273 } else if (key_id == rb_id_option_raise_error) {
274 if (!NIL_P(value)) {
275 if (value == Qtrue) {
276 /* When given true, color when $stderr is a terminal (unless
277 * NO_COLOR is set), bold styling when NO_COLOR is set, plain
278 * otherwise.
279 *
280 * rb_stderr_tty_p is not available to extensions, so instead
281 * we call the tty? method on $stderr, guarding against it
282 * having been replaced by an object that does not respond to
283 * tty?. */
285 const char *no_color = getenv("NO_COLOR");
286 if (no_color == NULL || no_color[0] == '\0') {
287 pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_COLOR);
288 } else {
289 pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_STYLE);
290 }
291 } else {
292 pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_PLAIN);
293 }
294 } else {
295 if (!SYMBOL_P(value)) {
296 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Symbol)", rb_obj_class(value));
297 }
298
299 ID value_id = SYM2ID(value);
300 if (value_id == rb_id_raise_error_plain) {
301 pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_PLAIN);
302 } else if (value_id == rb_id_raise_error_style) {
303 pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_STYLE);
304 } else if (value_id == rb_id_raise_error_color) {
305 pm_options_raise_error_set(options, (uint8_t) PM_ERRORS_FORMAT_COLOR);
306 } else {
307 rb_raise(rb_eArgError, "invalid raise_error value: %" PRIsVALUE, value);
308 }
309 }
310 }
311 } else {
312 rb_raise(rb_eArgError, "unknown keyword: %" PRIsVALUE, key);
313 }
314
315 return ST_CONTINUE;
316}
317
324 pm_options_t *options;
325 VALUE keywords;
326};
327
332static VALUE
333build_options(VALUE argument) {
334 struct build_options_data *data = (struct build_options_data *) argument;
335 rb_hash_foreach(data->keywords, build_options_i, (VALUE) data->options);
336 return Qnil;
337}
338
342static void
343extract_options(pm_options_t *options, VALUE filepath, VALUE keywords) {
344 pm_options_line_set(options, 1); /* default */
345
346 if (!NIL_P(keywords)) {
347 struct build_options_data data = { .options = options, .keywords = keywords };
348 struct build_options_data *argument = &data;
349
350 int state = 0;
351 rb_protect(build_options, (VALUE) argument, &state);
352
353 if (state != 0) {
354 pm_options_free(options);
355 rb_jump_tag(state);
356 }
357 }
358
359 if (!NIL_P(filepath)) {
360 if (!RB_TYPE_P(filepath, T_STRING)) {
361 pm_options_free(options);
362 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected String)", rb_obj_class(filepath));
363 }
364
365 pm_options_filepath_set(options, RSTRING_PTR(filepath));
366 }
367}
368
372static VALUE
373string_options(int argc, VALUE *argv, pm_options_t *options) {
374 VALUE string;
375 VALUE keywords;
376 rb_scan_args(argc, argv, "1:", &string, &keywords);
377
378 if (!RB_TYPE_P(string, T_STRING)) {
379 pm_options_free(options);
380 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected String)", rb_obj_class(string));
381 }
382
383 extract_options(options, Qnil, keywords);
384 return string;
385}
386
390static pm_source_t *
391file_options(int argc, VALUE *argv, pm_options_t *options, VALUE *encoded_filepath) {
392 VALUE filepath;
393 VALUE keywords;
394 rb_scan_args(argc, argv, "1:", &filepath, &keywords);
395
396 int state = 0;
397 filepath = rb_protect(rb_get_path, filepath, &state);
398 if (state != 0) {
399 pm_options_free(options);
400 rb_jump_tag(state);
401 }
402
403 *encoded_filepath = rb_str_encode_ospath(filepath);
404 extract_options(options, *encoded_filepath, keywords);
405
406 const char *source = (const char *) pm_string_source(pm_options_filepath(options));
408 pm_source_t *pm_src = pm_source_file_new(source, &result);
409
410 switch (result) {
412 break;
414 pm_options_free(options);
415
416#ifdef _WIN32
417 int e = rb_w32_map_errno(GetLastError());
418#else
419 int e = errno;
420#endif
421
422 rb_syserr_fail(e, source);
423 break;
424 }
426 pm_options_free(options);
427 rb_syserr_fail(EISDIR, source);
428 break;
429 default:
430 pm_options_free(options);
431 rb_raise(rb_eRuntimeError, "Unknown error (%d) initializing file: %s", result, source);
432 break;
433 }
434
435 return pm_src;
436}
437
443static result_t
444check_raise_error_option(pm_parser_t *parser, const pm_options_t *options, rb_encoding *path_encoding) {
445 if (pm_parser_errors_size(parser) == 0) return result_ok(Qnil);
446
447 uint8_t raise_error = pm_options_raise_error(options);
448 if (raise_error == 0) return result_ok(Qnil);
449
450 pm_buffer_t *buffer = pm_buffer_new();
451 if (buffer == NULL) {
452 return result_err(rb_exc_new_cstr(rb_eNoMemError, "failed to allocate memory"));
453 }
454
455 pm_error_level_t error_level = pm_errors_format(parser, buffer, (pm_errors_format_type_t) raise_error);
456
457 /* Copy the message out of the buffer and free it before building any
458 * other Ruby objects so that the buffer cannot be leaked if building the
459 * objects raises. */
460 rb_encoding *message_encoding = (error_level == PM_ERROR_LEVEL_LOAD) ? rb_locale_encoding() : rb_enc_find(pm_parser_encoding_name(parser));
461 VALUE message = rb_enc_str_new(pm_buffer_value(buffer), (long) pm_buffer_length(buffer), message_encoding);
462 pm_buffer_free(buffer);
463
464 VALUE error = Qnil;
465 switch (error_level) {
467 error = rb_exc_new_str(rb_eSyntaxError, message);
468
469 rb_encoding *path_encoding_normal = path_encoding == NULL ? rb_utf8_encoding() : path_encoding;
470 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);
471 rb_ivar_set(error, rb_intern_const("@path"), path);
472 break;
473 }
475 error = rb_exc_new_str(rb_eArgError, message);
476 break;
477 }
478 case PM_ERROR_LEVEL_LOAD: {
479 error = rb_exc_new_str(rb_eLoadError, message);
480 rb_ivar_set(error, rb_intern_const("@path"), Qnil);
481 break;
482 }
483 }
484
485 return result_err(error);
486}
487
488#ifndef PRISM_EXCLUDE_SERIALIZATION
489
490/******************************************************************************/
491/* Serializing the AST */
492/******************************************************************************/
493
497static result_t
498dump_input(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
499 pm_arena_t *arena = pm_arena_new();
500 pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
501 pm_node_t *node = pm_parse(parser);
502
503 result_t result = check_raise_error_option(parser, options, path_encoding);
504 if (result.type == RESULT_OK) {
505 pm_buffer_t *buffer = pm_buffer_new();
506 if (buffer) {
507 pm_serialize(parser, node, buffer);
508 result = result_ok(rb_str_new(pm_buffer_value(buffer), pm_buffer_length(buffer)));
509 pm_buffer_free(buffer);
510
511 if (pm_options_freeze(options)) rb_obj_freeze(result.value);
512 } else {
513 result = result_err(rb_exc_new_cstr(rb_eNoMemError, "failed to allocate memory"));
514 }
515 }
516
517 pm_parser_free(parser);
518 pm_arena_free(arena);
519
520 return result;
521}
522
531static VALUE
532dump(int argc, VALUE *argv, VALUE self) {
533 pm_options_t *options = pm_options_new();
534 VALUE string = string_options(argc, argv, options);
535
536 const uint8_t *source = (const uint8_t *) RSTRING_PTR(string);
537 size_t length = RSTRING_LEN(string);
538
539#ifdef PRISM_BUILD_DEBUG
540 char* dup = xmalloc(length);
541 memcpy(dup, source, length);
542 source = (const uint8_t *) dup;
543#endif
544
545 result_t result = dump_input(source, length, options, NULL);
546
547#ifdef PRISM_BUILD_DEBUG
548#ifdef xfree_sized
549 xfree_sized(dup, length);
550#else
551 xfree(dup);
552#endif
553#endif
554
555 pm_options_free(options);
556 return result_get(result);
557}
558
567static VALUE
568dump_file(int argc, VALUE *argv, VALUE self) {
569 pm_options_t *options = pm_options_new();
570
571 VALUE encoded_filepath;
572 pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
573
574 result_t result = dump_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
575 pm_source_free(src);
576 pm_options_free(options);
577
578 return result_get(result);
579}
580
581#endif
582
583/******************************************************************************/
584/* Extracting values for the parse result */
585/******************************************************************************/
586
591static inline VALUE
592rb_class_new_instance_freeze(int argc, const VALUE *argv, VALUE klass, bool freeze) {
593 VALUE value = rb_class_new_instance(argc, argv, klass);
594 if (freeze) rb_obj_freeze(value);
595 return value;
596}
597
601static inline VALUE
602parser_location(VALUE source, bool freeze, uint32_t start, uint32_t length) {
603 VALUE argv[] = { source, LONG2FIX(start), LONG2FIX(length) };
604 return rb_class_new_instance_freeze(3, argv, rb_cPrismLocation, freeze);
605}
606
610#define PARSER_LOCATION(source, freeze, location) \
611 parser_location(source, freeze, location.start, location.length)
612
616static inline VALUE
617parser_comment(VALUE source, bool freeze, const pm_comment_t *comment) {
618 VALUE argv[] = { PARSER_LOCATION(source, freeze, pm_comment_location(comment)) };
619 VALUE type = (pm_comment_type(comment) == PM_COMMENT_EMBDOC) ? rb_cPrismEmbDocComment : rb_cPrismInlineComment;
620 return rb_class_new_instance_freeze(1, argv, type, freeze);
621}
622
623typedef struct {
624 VALUE comments;
625 VALUE source;
626 bool freeze;
628
629static void
630parser_comments_each(const pm_comment_t *comment, void *data) {
632 VALUE value = parser_comment(each_data->source, each_data->freeze, comment);
633 rb_ary_push(each_data->comments, value);
634}
635
639static VALUE
640parser_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
641 VALUE comments = rb_ary_new_capa(pm_parser_comments_size(parser));
642
643 parser_comments_each_data_t each_data = { comments, source, freeze };
644 pm_parser_comments_each(parser, parser_comments_each, &each_data);
645
646 if (freeze) rb_obj_freeze(comments);
647 return comments;
648}
649
653static inline VALUE
654parser_magic_comment(VALUE source, bool freeze, const pm_magic_comment_t *magic_comment) {
657
658 VALUE key_loc = parser_location(source, freeze, key.start, key.length);
659 VALUE value_loc = parser_location(source, freeze, value.start, value.length);
660
661 VALUE argv[] = { key_loc, value_loc };
662 return rb_class_new_instance_freeze(2, argv, rb_cPrismMagicComment, freeze);
663}
664
665typedef struct {
666 VALUE magic_comments;
667 VALUE source;
668 bool freeze;
670
671static void
672parser_magic_comments_each(const pm_magic_comment_t *magic_comment, void *data) {
674 VALUE value = parser_magic_comment(each_data->source, each_data->freeze, magic_comment);
675 rb_ary_push(each_data->magic_comments, value);
676}
677
681static VALUE
682parser_magic_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
683 VALUE magic_comments = rb_ary_new_capa(pm_parser_magic_comments_size(parser));
684
685 parser_magic_comments_each_data_t each_data = { magic_comments, source, freeze };
686 pm_parser_magic_comments_each(parser, parser_magic_comments_each, &each_data);
687
688 if (freeze) rb_obj_freeze(magic_comments);
689 return magic_comments;
690}
691
696static VALUE
697parser_data_loc(const pm_parser_t *parser, VALUE source, bool freeze) {
698 const pm_location_t *data_loc = pm_parser_data_loc(parser);
699
700 if (data_loc->length == 0) {
701 return Qnil;
702 } else {
703 return parser_location(source, freeze, data_loc->start, data_loc->length);
704 }
705}
706
707typedef struct {
708 VALUE errors;
709 rb_encoding *encoding;
710 VALUE source;
711 bool freeze;
713
714static void
715parser_errors_each(const pm_diagnostic_t *diagnostic, void *data) {
717
718 VALUE type = ID2SYM(rb_intern(pm_diagnostic_type(diagnostic)));
719 VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(pm_diagnostic_message(diagnostic), each_data->encoding));
720 VALUE location = PARSER_LOCATION(each_data->source, each_data->freeze, pm_diagnostic_location(diagnostic));
721
722 pm_error_level_t error_level = pm_diagnostic_error_level(diagnostic);
723 VALUE level = Qnil;
724
725 switch (error_level) {
727 level = ID2SYM(rb_intern("syntax"));
728 break;
730 level = ID2SYM(rb_intern("argument"));
731 break;
733 level = ID2SYM(rb_intern("load"));
734 break;
735 default:
736 rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, error_level);
737 }
738
739 VALUE argv[] = { type, message, location, level };
740 VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseError, each_data->freeze);
741 rb_ary_push(each_data->errors, value);
742}
743
747static VALUE
748parser_errors(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bool freeze) {
749 VALUE errors = rb_ary_new_capa(pm_parser_errors_size(parser));
750
751 parser_errors_each_data_t each_data = { errors, encoding, source, freeze };
752 pm_parser_errors_each(parser, parser_errors_each, &each_data);
753
754 if (freeze) rb_obj_freeze(errors);
755 return errors;
756}
757
758typedef struct {
759 VALUE warnings;
760 rb_encoding *encoding;
761 VALUE source;
762 bool freeze;
764
765static void
766parser_warnings_each(const pm_diagnostic_t *diagnostic, void *data) {
768
769 VALUE type = ID2SYM(rb_intern(pm_diagnostic_type(diagnostic)));
770 VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(pm_diagnostic_message(diagnostic), each_data->encoding));
771 VALUE location = PARSER_LOCATION(each_data->source, each_data->freeze, pm_diagnostic_location(diagnostic));
772
773 pm_warning_level_t warning_level = pm_diagnostic_warning_level(diagnostic);
774 VALUE level = Qnil;
775
776 switch (warning_level) {
778 level = ID2SYM(rb_intern("default"));
779 break;
781 level = ID2SYM(rb_intern("verbose"));
782 break;
783 default:
784 rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, warning_level);
785 }
786
787 VALUE argv[] = { type, message, location, level };
788 VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseWarning, each_data->freeze);
789 rb_ary_push(each_data->warnings, value);
790}
791
795static VALUE
796parser_warnings(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bool freeze) {
797 VALUE warnings = rb_ary_new_capa(pm_parser_warnings_size(parser));
798
799 parser_warnings_each_data_t each_data = { warnings, encoding, source, freeze };
800 pm_parser_warnings_each(parser, parser_warnings_each, &each_data);
801
802 if (freeze) rb_obj_freeze(warnings);
803 return warnings;
804}
805
809static VALUE
810parse_result_create(VALUE class, const pm_parser_t *parser, VALUE value, rb_encoding *encoding, VALUE source, bool freeze) {
811 VALUE result_argv[] = {
812 value,
813 parser_comments(parser, source, freeze),
814 parser_magic_comments(parser, source, freeze),
815 parser_data_loc(parser, source, freeze),
816 parser_errors(parser, encoding, source, freeze),
817 parser_warnings(parser, encoding, source, freeze),
818 pm_parser_continuable(parser) ? Qtrue : Qfalse,
819 source
820 };
821
822 return rb_class_new_instance_freeze(8, result_argv, class, freeze);
823}
824
825/******************************************************************************/
826/* Lexing Ruby code */
827/******************************************************************************/
828
834typedef struct {
835 VALUE source;
836 VALUE tokens;
837 rb_encoding *encoding;
838 bool freeze;
840
846static void
847parse_lex_token(pm_parser_t *parser, pm_token_t *token, void *data) {
848 parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) data;
849
850 VALUE value = pm_token_new(parser, token, parse_lex_data->encoding, parse_lex_data->source, parse_lex_data->freeze);
851 rb_ary_push(parse_lex_data->tokens, value);
852}
853
859static void
860parse_lex_encoding_changed_callback(pm_parser_t *parser) {
861 parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) pm_parser_lex_callback_data(parser);
862 parse_lex_data->encoding = rb_enc_find(pm_parser_encoding_name(parser));
863
864 // Since the encoding changed, we need to go back and change the encoding of
865 // the tokens that were already lexed. This is only going to end up being
866 // one or two tokens, since the encoding can only change at the top of the
867 // file.
868 VALUE tokens = parse_lex_data->tokens;
869 VALUE next_tokens = rb_ary_new();
870
871 for (long index = 0; index < RARRAY_LEN(tokens); index++) {
872 VALUE token = rb_ary_entry(tokens, index);
873 VALUE value = rb_ivar_get(token, rb_intern("@value"));
874 VALUE next_value = rb_str_dup(value);
875
876 rb_enc_associate(next_value, parse_lex_data->encoding);
877 if (parse_lex_data->freeze) rb_obj_freeze(next_value);
878
879 VALUE next_token_argv[] = {
880 parse_lex_data->source,
881 rb_ivar_get(token, rb_intern("@type")),
882 next_value,
883 rb_ivar_get(token, rb_intern("@location")),
884 rb_ivar_get(token, rb_intern("@state")),
885 };
886
887 VALUE next_token = rb_class_new_instance(5, next_token_argv, rb_cPrismToken);
888
889 if (parse_lex_data->freeze) {
890 rb_obj_freeze(next_token);
891 }
892
893 rb_ary_push(next_tokens, next_token);
894 }
895
896 rb_ary_replace(parse_lex_data->tokens, next_tokens);
897}
898
903static result_t
904parse_lex_input(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding, bool return_nodes) {
905 pm_arena_t *arena = pm_arena_new();
906 pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
907 pm_parser_encoding_changed_callback_set(parser, parse_lex_encoding_changed_callback);
908
909 VALUE source_string = rb_str_new((const char *) input, input_length);
910 VALUE offsets = rb_ary_new_capa(pm_parser_line_offsets(parser)->size);
911 VALUE source = rb_funcall(rb_cPrismSource, rb_id_source_for, 3, source_string, LONG2NUM(pm_parser_start_line(parser)), offsets);
912
913 parse_lex_data_t parse_lex_data = {
914 .source = source,
915 .tokens = rb_ary_new(),
916 .encoding = rb_enc_find(pm_parser_encoding_name(parser)),
917 .freeze = pm_options_freeze(options),
918 };
919
920 parse_lex_data_t *data = &parse_lex_data;
921 pm_parser_lex_callback_set(parser, parse_lex_token, data);
922
923 pm_node_t *node = pm_parse(parser);
924
925 result_t result = check_raise_error_option(parser, options, path_encoding);
926 if (result.type == RESULT_OK) {
927 /* Update the Source object with the correct encoding and line offsets,
928 * which are only available after pm_parse() completes. */
929 rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser));
930 rb_enc_associate(source_string, encoding);
931
932 const pm_line_offset_list_t *line_offsets = pm_parser_line_offsets(parser);
933 for (size_t index = 0; index < line_offsets->size; index++) {
934 rb_ary_store(offsets, (long) index, ULONG2NUM(line_offsets->offsets[index]));
935 }
936
937 if (pm_options_freeze(options)) {
938 rb_obj_freeze(source_string);
939 rb_obj_freeze(offsets);
940 rb_obj_freeze(source);
941 rb_obj_freeze(parse_lex_data.tokens);
942 }
943
944 if (return_nodes) {
945 VALUE value = rb_ary_new_capa(2);
946 rb_ary_push(value, pm_ast_new(parser, node, parse_lex_data.encoding, source, pm_options_freeze(options)));
947 rb_ary_push(value, parse_lex_data.tokens);
948 if (pm_options_freeze(options)) rb_obj_freeze(value);
949 result = result_ok(parse_result_create(rb_cPrismParseLexResult, parser, value, parse_lex_data.encoding, source, pm_options_freeze(options)));
950 } else {
951 result = result_ok(parse_result_create(rb_cPrismLexResult, parser, parse_lex_data.tokens, parse_lex_data.encoding, source, pm_options_freeze(options)));
952 }
953 }
954
955 pm_parser_free(parser);
956 pm_arena_free(arena);
957
958 return result;
959}
960
969static VALUE
970lex(int argc, VALUE *argv, VALUE self) {
971 pm_options_t *options = pm_options_new();
972 VALUE string = string_options(argc, argv, options);
973
974 result_t result = parse_lex_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL, false);
975 pm_options_free(options);
976
977 return result_get(result);
978}
979
988static VALUE
989lex_file(int argc, VALUE *argv, VALUE self) {
990 pm_options_t *options = pm_options_new();
991
992 VALUE encoded_filepath;
993 pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
994
995 result_t result = parse_lex_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath), false);
996 pm_source_free(src);
997 pm_options_free(options);
998
999 return result_get(result);
1000}
1001
1002/******************************************************************************/
1003/* Parsing Ruby code */
1004/******************************************************************************/
1005
1009static result_t
1010parse_input(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
1011 pm_arena_t *arena = pm_arena_new();
1012 pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
1013
1014 pm_node_t *node = pm_parse(parser);
1015
1016 result_t result = check_raise_error_option(parser, options, path_encoding);
1017 if (result.type == RESULT_OK) {
1018 rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser));
1019
1020 bool freeze = pm_options_freeze(options);
1021 VALUE source = pm_source_new(parser, encoding, freeze);
1022 VALUE value = pm_ast_new(parser, node, encoding, source, freeze);
1023 result = result_ok(parse_result_create(rb_cPrismParseResult, parser, value, encoding, source, freeze));
1024
1025 if (freeze) {
1026 rb_obj_freeze(source);
1027 }
1028 }
1029
1030 pm_parser_free(parser);
1031 pm_arena_free(arena);
1032
1033 return result;
1034}
1035
1095static VALUE
1096parse(int argc, VALUE *argv, VALUE self) {
1097 pm_options_t *options = pm_options_new();
1098 VALUE string = string_options(argc, argv, options);
1099
1100 const uint8_t *source = (const uint8_t *) RSTRING_PTR(string);
1101 size_t length = RSTRING_LEN(string);
1102
1103#ifdef PRISM_BUILD_DEBUG
1104 char* dup = xmalloc(length);
1105 memcpy(dup, source, length);
1106 source = (const uint8_t *) dup;
1107#endif
1108
1109 result_t result = parse_input(source, length, options, NULL);
1110
1111#ifdef PRISM_BUILD_DEBUG
1112#ifdef xfree_sized
1113 xfree_sized(dup, length);
1114#else
1115 xfree(dup);
1116#endif
1117#endif
1118
1119 pm_options_free(options);
1120 return result_get(result);
1121}
1122
1131static VALUE
1132parse_file(int argc, VALUE *argv, VALUE self) {
1133 pm_options_t *options = pm_options_new();
1134
1135 VALUE encoded_filepath;
1136 pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
1137
1138 result_t result = parse_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
1139 pm_source_free(src);
1140 pm_options_free(options);
1141
1142 return result_get(result);
1143}
1144
1148static result_t
1149profile_input(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
1150 pm_arena_t *arena = pm_arena_new();
1151 pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
1152
1153 pm_parse(parser);
1154
1155 result_t result = check_raise_error_option(parser, options, path_encoding);
1156 pm_parser_free(parser);
1157 pm_arena_free(arena);
1158
1159 return result;
1160}
1161
1171static VALUE
1172profile(int argc, VALUE *argv, VALUE self) {
1173 pm_options_t *options = pm_options_new();
1174 VALUE string = string_options(argc, argv, options);
1175
1176 result_t result = profile_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL);
1177 pm_options_free(options);
1178 result_get(result);
1179
1180 return Qnil;
1181}
1182
1192static VALUE
1193profile_file(int argc, VALUE *argv, VALUE self) {
1194 pm_options_t *options = pm_options_new();
1195
1196 VALUE encoded_filepath;
1197 pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
1198
1199 result_t result = profile_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
1200 pm_source_free(src);
1201 pm_options_free(options);
1202 result_get(result);
1203
1204 return Qnil;
1205}
1206
1207static int
1208parse_stream_eof(void *stream) {
1209 if (rb_funcall((VALUE) stream, rb_intern("eof?"), 0)) {
1210 return 1;
1211 }
1212 return 0;
1213}
1214
1227#define MAX_ENC_LEN 6
1228
1234static char *
1235parse_stream_fgets(char *string, int size, void *stream) {
1236 RUBY_ASSERT(size > MAX_ENC_LEN);
1237
1238 /*
1239 * Request fewer bytes than the buffer can hold. `gets` may return more
1240 * bytes than requested when the limit falls in the middle of a multi-byte
1241 * character, and the reserved headroom guarantees the result still fits.
1242 */
1243 VALUE line = rb_funcall((VALUE) stream, rb_intern("gets"), 1, INT2FIX(size - MAX_ENC_LEN));
1244
1245 /*
1246 * A well-behaved stream returns a String, or nil at EOF. Coerce anything
1247 * else to nil so that we never treat a non-String as a byte buffer.
1248 */
1249 line = rb_check_string_type(line);
1250 if (NIL_P(line)) {
1251 return NULL;
1252 }
1253
1254 const char *cstr = RSTRING_PTR(line);
1255 long length = RSTRING_LEN(line);
1256
1257 /*
1258 * Defensively clamp the copy. A misbehaving `gets` may ignore the limit
1259 * entirely and return an arbitrarily long string; we must never write past
1260 * the caller's buffer. One byte is reserved for the NUL terminator.
1261 */
1262 if (length > (long) (size - 1)) {
1263 length = (long) (size - 1);
1264 }
1265
1266 memcpy(string, cstr, (size_t) length);
1267 string[length] = '\0';
1268
1269 return string;
1270}
1271
1272#undef MAX_ENC_LEN
1273
1282static VALUE
1283parse_stream(int argc, VALUE *argv, VALUE self) {
1284 VALUE stream;
1285 VALUE keywords;
1286 rb_scan_args(argc, argv, "1:", &stream, &keywords);
1287
1288 pm_options_t *options = pm_options_new();
1289 extract_options(options, Qnil, keywords);
1290
1291 pm_source_t *src = pm_source_stream_new((void *) stream, parse_stream_fgets, parse_stream_eof);
1292 pm_arena_t *arena = pm_arena_new();
1293 pm_parser_t *parser;
1294
1295 pm_node_t *node = pm_parse_stream(&parser, arena, src, options);
1296
1297 result_t result = check_raise_error_option(parser, options, NULL);
1298 if (result.type == RESULT_OK) {
1299 rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser));
1300
1301 VALUE source = pm_source_new(parser, encoding, pm_options_freeze(options));
1302 VALUE value = pm_ast_new(parser, node, encoding, source, pm_options_freeze(options));
1303 result = result_ok(parse_result_create(rb_cPrismParseResult, parser, value, encoding, source, pm_options_freeze(options)));
1304 }
1305
1306 pm_source_free(src);
1307 pm_parser_free(parser);
1308 pm_arena_free(arena);
1309 pm_options_free(options);
1310
1311 return result_get(result);
1312}
1313
1317static result_t
1318parse_input_comments(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
1319 pm_arena_t *arena = pm_arena_new();
1320 pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
1321
1322 pm_parse(parser);
1323
1324 result_t result = check_raise_error_option(parser, options, path_encoding);
1325 if (result.type == RESULT_OK) {
1326 rb_encoding *encoding = rb_enc_find(pm_parser_encoding_name(parser));
1327
1328 VALUE source = pm_source_new(parser, encoding, pm_options_freeze(options));
1329 result = result_ok(parser_comments(parser, source, pm_options_freeze(options)));
1330 }
1331
1332 pm_parser_free(parser);
1333 pm_arena_free(arena);
1334
1335 return result;
1336}
1337
1346static VALUE
1347parse_comments(int argc, VALUE *argv, VALUE self) {
1348 pm_options_t *options = pm_options_new();
1349 VALUE string = string_options(argc, argv, options);
1350
1351 result_t result = parse_input_comments((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL);
1352 pm_options_free(options);
1353
1354 return result_get(result);
1355}
1356
1365static VALUE
1366parse_file_comments(int argc, VALUE *argv, VALUE self) {
1367 pm_options_t *options = pm_options_new();
1368
1369 VALUE encoded_filepath;
1370 pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
1371
1372 result_t result = parse_input_comments(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
1373 pm_source_free(src);
1374 pm_options_free(options);
1375
1376 return result_get(result);
1377}
1378
1394static VALUE
1395parse_lex(int argc, VALUE *argv, VALUE self) {
1396 pm_options_t *options = pm_options_new();
1397 VALUE string = string_options(argc, argv, options);
1398
1399 result_t result = parse_lex_input((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL, true);
1400 pm_options_free(options);
1401
1402 return result_get(result);
1403}
1404
1420static VALUE
1421parse_lex_file(int argc, VALUE *argv, VALUE self) {
1422 pm_options_t *options = pm_options_new();
1423
1424 VALUE encoded_filepath;
1425 pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
1426
1427 result_t result = parse_lex_input(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath), true);
1428 pm_source_free(src);
1429 pm_options_free(options);
1430
1431 return result_get(result);
1432}
1433
1437static result_t
1438parse_input_success_p(const uint8_t *input, size_t input_length, const pm_options_t *options, rb_encoding *path_encoding) {
1439 pm_arena_t *arena = pm_arena_new();
1440 pm_parser_t *parser = pm_parser_new(arena, input, input_length, options);
1441 pm_parse(parser);
1442
1443 result_t result = check_raise_error_option(parser, options, path_encoding);
1444 if (result.type == RESULT_OK) {
1445 result = result_ok(pm_parser_errors_size(parser) == 0 ? Qtrue : Qfalse);
1446 }
1447
1448 pm_parser_free(parser);
1449 pm_arena_free(arena);
1450
1451 return result;
1452}
1453
1462static VALUE
1463parse_success_p(int argc, VALUE *argv, VALUE self) {
1464 pm_options_t *options = pm_options_new();
1465 VALUE string = string_options(argc, argv, options);
1466
1467 result_t result = parse_input_success_p((const uint8_t *) RSTRING_PTR(string), RSTRING_LEN(string), options, NULL);
1468 pm_options_free(options);
1469
1470 return result_get(result);
1471}
1472
1481static VALUE
1482parse_failure_p(int argc, VALUE *argv, VALUE self) {
1483 return RTEST(parse_success_p(argc, argv, self)) ? Qfalse : Qtrue;
1484}
1485
1494static VALUE
1495parse_file_success_p(int argc, VALUE *argv, VALUE self) {
1496 pm_options_t *options = pm_options_new();
1497
1498 VALUE encoded_filepath;
1499 pm_source_t *src = file_options(argc, argv, options, &encoded_filepath);
1500
1501 result_t result = parse_input_success_p(pm_source_source(src), pm_source_length(src), options, rb_enc_get(encoded_filepath));
1502 pm_source_free(src);
1503 pm_options_free(options);
1504
1505 return result_get(result);
1506}
1507
1516static VALUE
1517parse_file_failure_p(int argc, VALUE *argv, VALUE self) {
1518 return RTEST(parse_file_success_p(argc, argv, self)) ? Qfalse : Qtrue;
1519}
1520
1521/******************************************************************************/
1522/* String query methods */
1523/******************************************************************************/
1524
1529static VALUE
1530string_query(pm_string_query_t result) {
1531 switch (result) {
1533 rb_raise(rb_eArgError, "Invalid or non ascii-compatible encoding");
1534 return Qfalse;
1536 return Qfalse;
1538 return Qtrue;
1539 }
1540 return Qfalse;
1541}
1542
1552static VALUE
1553string_query_local_p(VALUE self, VALUE string) {
1554 const uint8_t *source = (const uint8_t *) check_string(string);
1555 return string_query(pm_string_query_local(source, RSTRING_LEN(string), rb_enc_get(string)->name));
1556}
1557
1567static VALUE
1568string_query_constant_p(VALUE self, VALUE string) {
1569 const uint8_t *source = (const uint8_t *) check_string(string);
1570 return string_query(pm_string_query_constant(source, RSTRING_LEN(string), rb_enc_get(string)->name));
1571}
1572
1580static VALUE
1581string_query_method_name_p(VALUE self, VALUE string) {
1582 const uint8_t *source = (const uint8_t *) check_string(string);
1583 return string_query(pm_string_query_method_name(source, RSTRING_LEN(string), rb_enc_get(string)->name));
1584}
1585
1586/******************************************************************************/
1587/* Initialization of the extension */
1588/******************************************************************************/
1589
1593RUBY_FUNC_EXPORTED void
1594Init_prism(void) {
1595 /* Make sure that the prism library version matches the expected version.
1596 * Otherwise something was compiled incorrectly. */
1597 if (strcmp(pm_version(), EXPECTED_PRISM_VERSION) != 0) {
1598 rb_raise(
1600 "The prism library version (%s) does not match the expected version (%s)",
1601 pm_version(),
1602 EXPECTED_PRISM_VERSION
1603 );
1604 }
1605
1606#ifdef HAVE_RB_EXT_RACTOR_SAFE
1607 /* Mark this extension as Ractor-safe. */
1608 rb_ext_ractor_safe(true);
1609#endif
1610
1611 rb_cPrism = rb_define_module("Prism");
1612 rb_cPrismNode = rb_define_class_under(rb_cPrism, "Node", rb_cObject);
1613 rb_cPrismSource = rb_define_class_under(rb_cPrism, "Source", rb_cObject);
1614 rb_cPrismToken = rb_define_class_under(rb_cPrism, "Token", rb_cObject);
1615 rb_cPrismLocation = rb_define_class_under(rb_cPrism, "Location", rb_cObject);
1616 rb_cPrismComment = rb_define_class_under(rb_cPrism, "Comment", rb_cObject);
1617 rb_cPrismInlineComment = rb_define_class_under(rb_cPrism, "InlineComment", rb_cPrismComment);
1618 rb_cPrismEmbDocComment = rb_define_class_under(rb_cPrism, "EmbDocComment", rb_cPrismComment);
1619 rb_cPrismMagicComment = rb_define_class_under(rb_cPrism, "MagicComment", rb_cObject);
1620 rb_cPrismParseError = rb_define_class_under(rb_cPrism, "ParseError", rb_cObject);
1621 rb_cPrismParseWarning = rb_define_class_under(rb_cPrism, "ParseWarning", rb_cObject);
1622 rb_cPrismResult = rb_define_class_under(rb_cPrism, "Result", rb_cObject);
1623 rb_cPrismParseResult = rb_define_class_under(rb_cPrism, "ParseResult", rb_cPrismResult);
1624 rb_cPrismLexResult = rb_define_class_under(rb_cPrism, "LexResult", rb_cPrismResult);
1625 rb_cPrismParseLexResult = rb_define_class_under(rb_cPrism, "ParseLexResult", rb_cPrismResult);
1626 rb_cPrismStringQuery = rb_define_class_under(rb_cPrism, "StringQuery", rb_cObject);
1627 rb_cPrismScope = rb_define_class_under(rb_cPrism, "Scope", rb_cObject);
1628
1629 rb_cPrismCurrentVersionError = rb_const_get(rb_cPrism, rb_intern("CurrentVersionError"));
1630
1631 /* Intern all of the IDs eagerly that we support so that we do not have to
1632 * do it every time we parse. */
1633 rb_id_option_command_line = rb_intern_const("command_line");
1634 rb_id_option_encoding = rb_intern_const("encoding");
1635 rb_id_option_filepath = rb_intern_const("filepath");
1636 rb_id_option_freeze = rb_intern_const("freeze");
1637 rb_id_option_frozen_string_literal = rb_intern_const("frozen_string_literal");
1638 rb_id_option_line = rb_intern_const("line");
1639 rb_id_option_main_script = rb_intern_const("main_script");
1640 rb_id_option_partial_script = rb_intern_const("partial_script");
1641 rb_id_option_raise_error = rb_intern_const("raise_error");
1642 rb_id_option_scopes = rb_intern_const("scopes");
1643 rb_id_option_version = rb_intern_const("version");
1644
1645 rb_id_source_for = rb_intern("for");
1646
1647 rb_id_forwarding_positionals = rb_intern("*");
1648 rb_id_forwarding_keywords = rb_intern("**");
1649 rb_id_forwarding_block = rb_intern("&");
1650 rb_id_forwarding_all = rb_intern("...");
1651
1652 rb_id_raise_error_plain = rb_intern_const("plain");
1653 rb_id_raise_error_style = rb_intern_const("style");
1654 rb_id_raise_error_color = rb_intern_const("color");
1655
1659 rb_define_const(rb_cPrism, "VERSION", rb_str_freeze(rb_str_new_cstr(EXPECTED_PRISM_VERSION)));
1660
1661 rb_define_singleton_method(rb_cPrism, "lex", lex, -1);
1662 rb_define_singleton_method(rb_cPrism, "lex_file", lex_file, -1);
1663 rb_define_singleton_method(rb_cPrism, "parse", parse, -1);
1664 rb_define_singleton_method(rb_cPrism, "parse_file", parse_file, -1);
1665 rb_define_singleton_method(rb_cPrism, "profile", profile, -1);
1666 rb_define_singleton_method(rb_cPrism, "profile_file", profile_file, -1);
1667 rb_define_singleton_method(rb_cPrism, "parse_stream", parse_stream, -1);
1668 rb_define_singleton_method(rb_cPrism, "parse_comments", parse_comments, -1);
1669 rb_define_singleton_method(rb_cPrism, "parse_file_comments", parse_file_comments, -1);
1670 rb_define_singleton_method(rb_cPrism, "parse_lex", parse_lex, -1);
1671 rb_define_singleton_method(rb_cPrism, "parse_lex_file", parse_lex_file, -1);
1672 rb_define_singleton_method(rb_cPrism, "parse_success?", parse_success_p, -1);
1673 rb_define_singleton_method(rb_cPrism, "parse_failure?", parse_failure_p, -1);
1674 rb_define_singleton_method(rb_cPrism, "parse_file_success?", parse_file_success_p, -1);
1675 rb_define_singleton_method(rb_cPrism, "parse_file_failure?", parse_file_failure_p, -1);
1676
1677#ifndef PRISM_EXCLUDE_SERIALIZATION
1678 rb_define_singleton_method(rb_cPrism, "dump", dump, -1);
1679 rb_define_singleton_method(rb_cPrism, "dump_file", dump_file, -1);
1680#endif
1681
1682 rb_define_singleton_method(rb_cPrismStringQuery, "local?", string_query_local_p, 1);
1683 rb_define_singleton_method(rb_cPrismStringQuery, "constant?", string_query_constant_p, 1);
1684 rb_define_singleton_method(rb_cPrismStringQuery, "method_name?", string_query_method_name_p, 1);
1685
1686 Init_prism_api_node();
1687}
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
PRISM_EXPORTED_FUNCTION pm_location_t pm_comment_location(const pm_comment_t *comment) PRISM_NONNULL(1)
Returns the location associated with the given comment.
Definition parser.c:158
PRISM_EXPORTED_FUNCTION pm_comment_type_t pm_comment_type(const pm_comment_t *comment) PRISM_NONNULL(1)
Returns the type associated with the given comment.
Definition parser.c:166
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
pm_warning_level_t
The levels of warnings generated during parsing.
Definition diagnostic.h:37
@ PM_WARNING_LEVEL_DEFAULT
For warnings which should be emitted if $VERBOSE != nil.
Definition diagnostic.h:39
@ PM_WARNING_LEVEL_VERBOSE
For warnings which should be emitted if $VERBOSE == true.
Definition diagnostic.h:42
pm_error_level_t
The levels of errors generated during parsing.
Definition diagnostic.h:23
@ PM_ERROR_LEVEL_ARGUMENT
For errors that should raise an argument error.
Definition diagnostic.h:28
@ PM_ERROR_LEVEL_LOAD
For errors that should raise a load error.
Definition diagnostic.h:31
@ PM_ERROR_LEVEL_SYNTAX
For errors that should raise a syntax error.
Definition diagnostic.h:25
pm_errors_format_type_t
The type of formatting to use when formatting errors.
@ PM_ERRORS_FORMAT_PLAIN
Format errors in a plain format with no colors or styles.
@ PM_ERRORS_FORMAT_COLOR
Format errors in a color format with bold and colors.
@ PM_ERRORS_FORMAT_STYLE
Format errors in a style format with bold only.
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Retrieves argument from argc and argv to given VALUE references according to the format string.
Definition class.c:3376
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define ULONG2NUM
Old name of RB_ULONG2NUM.
Definition long.h:60
#define SYM2ID
Old name of RB_SYM2ID.
Definition symbol.h:45
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define LONG2NUM
Old name of RB_LONG2NUM.
Definition long.h:50
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define NIL_P
Old name of RB_NIL_P.
#define T_SYMBOL
Old name of RUBY_T_SYMBOL.
Definition value_type.h:80
#define SYMBOL_P
Old name of RB_SYMBOL_P.
Definition value_type.h:88
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:678
void rb_syserr_fail(int e, const char *mesg)
Raises appropriate exception that represents a C errno.
Definition error.c:4074
VALUE rb_eNoMemError
NoMemoryError exception.
Definition error.c:1474
VALUE rb_eLoadError
LoadError exception.
Definition error.c:1481
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1463
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1461
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Identical to rb_exc_new_cstr(), except it takes a Ruby's string instead of C's.
Definition error.c:1514
VALUE rb_eSyntaxError
SyntaxError exception.
Definition error.c:1480
VALUE rb_cObject
Object class.
Definition object.c:60
VALUE rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
Allocates, then initialises an instance of the given class.
Definition object.c:2292
VALUE rb_stderr
STDERR constant.
Definition io.c:207
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:234
VALUE rb_obj_is_kind_of(VALUE obj, VALUE klass)
Queries if the given object is an instance (of possibly descendants) of the given class.
Definition object.c:905
VALUE rb_obj_freeze(VALUE obj)
Same as RB_OBJ_FREEZE(), but returns the given object.
Definition object.c:1308
VALUE rb_enc_str_new_cstr(const char *ptr, rb_encoding *enc)
Identical to rb_enc_str_new(), except it assumes the passed pointer is a pointer to a C string.
Definition string.c:1175
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1123
VALUE rb_ary_replace(VALUE copy, VALUE orig)
Replaces the contents of the former object with the contents of the latter.
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_new_capa(long capa)
Identical to rb_ary_new(), except it additionally specifies how many rooms of objects it should alloc...
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
void rb_ary_store(VALUE ary, long key, VALUE val)
Destructively stores the passed value to the passed array's passed index.
void rb_ext_ractor_safe(bool flag)
Asserts that the extension library that calls this function is aware of Ractor.
Definition load.c:1334
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1499
#define rb_exc_new_cstr(exc, str)
Identical to rb_exc_new(), except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1671
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition string.c:2023
VALUE rb_str_freeze(VALUE str)
This is the implementation of String#freeze.
Definition string.c:3376
VALUE rb_check_string_type(VALUE obj)
Try converting an object to its stringised representation using its to_str method,...
Definition string.c:3032
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1515
VALUE rb_const_get(VALUE space, ID name)
Identical to rb_const_defined(), except it returns the actual defined value.
Definition variable.c:3495
VALUE rb_ivar_set(VALUE obj, ID name, VALUE val)
Identical to rb_iv_set(), except it accepts the name as an ID instead of a C string.
Definition variable.c:2131
VALUE rb_ivar_get(VALUE obj, ID name)
Identical to rb_iv_get(), except it accepts the name as an ID instead of a C string.
Definition variable.c:1631
int rb_respond_to(VALUE obj, ID mid)
Queries if the object responds to the method.
Definition vm_method.c:3673
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:285
const char ruby_version[]
Stringised version.
Definition version.c:82
PRISM_EXPORTED_FUNCTION pm_location_t pm_magic_comment_value(const pm_magic_comment_t *magic_comment) PRISM_NONNULL(1)
Returns the location of the value associated with the given magic comment.
Definition parser.c:204
PRISM_EXPORTED_FUNCTION pm_location_t pm_magic_comment_key(const pm_magic_comment_t *magic_comment) PRISM_NONNULL(1)
Returns the location of the key associated with the given magic comment.
Definition parser.c:196
VALUE type(ANYARGS)
ANYARGS-ed function type.
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
static const uint8_t PM_OPTIONS_COMMAND_LINE_E
A bit representing whether or not the command line -e option was set.
Definition options.h:84
static const uint8_t PM_OPTIONS_COMMAND_LINE_L
A bit representing whether or not the command line -l option was set.
Definition options.h:90
static const uint8_t PM_OPTIONS_COMMAND_LINE_A
A bit representing whether or not the command line -a option was set.
Definition options.h:77
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_NONE
The default value for parameters.
Definition options.h:45
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_ALL
When the scope is forwarding with the ... parameter.
Definition options.h:57
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS
When the scope is forwarding with the * parameter.
Definition options.h:48
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS
When the scope is forwarding with the ** parameter.
Definition options.h:51
static const uint8_t PM_OPTIONS_COMMAND_LINE_N
A bit representing whether or not the command line -n option was set.
Definition options.h:96
static const uint8_t PM_OPTIONS_COMMAND_LINE_X
A bit representing whether or not the command line -x option was set.
Definition options.h:108
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_BLOCK
When the scope is forwarding with the & parameter.
Definition options.h:54
static const uint8_t PM_OPTIONS_COMMAND_LINE_P
A bit representing whether or not the command line -p option was set.
Definition options.h:102
PRISM_EXPORTED_FUNCTION PRISM_NODISCARD pm_parser_t * pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_options_t *options) PRISM_NONNULL(1)
Allocate and initialize a parser with the given start and end pointers.
Definition prism.c:23180
PRISM_EXPORTED_FUNCTION void pm_parser_free(pm_parser_t *parser) PRISM_NONNULL(1)
Free both the memory held by the given parser and the parser itself.
Definition prism.c:23213
PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse(pm_parser_t *parser) PRISM_NONNULL(1)
Initiate the parser with the given parser.
Definition prism.c:23384
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:50
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
pm_source_init_result_t
Represents the result of initializing a source from a file.
Definition source.h:46
@ PM_SOURCE_INIT_ERROR_GENERIC
Indicates a generic error from a source init function, where the type of error should be read from er...
Definition source.h:54
@ PM_SOURCE_INIT_SUCCESS
Indicates that the source was successfully initialized.
Definition source.h:48
@ PM_SOURCE_INIT_ERROR_DIRECTORY
Indicates that the file that was attempted to be opened was a directory.
Definition source.h:59
#define RTEST
This is an old name of RB_TEST.
pm_string_query_t
Represents the results of a slice query.
@ PM_STRING_QUERY_TRUE
Returned if the result of the slice query is true.
@ PM_STRING_QUERY_ERROR
Returned if the encoding given to a slice query was invalid.
@ PM_STRING_QUERY_FALSE
Returned if the result of the slice query is false.
We need a struct here to pass through rb_protect and it has to be a single value.
Definition extension.c:323
This struct gets stored in the parser and passed in to the lex callback any time a new token is found...
Definition extension.c:834
A list of offsets of the start of lines in a string.
uint32_t * offsets
The list of offsets.
size_t size
The number of offsets in the list.
This struct represents a slice in the source code, defined by an offset and a length.
Definition ast.h:572
uint32_t start
The offset of the location from the start of the source.
Definition ast.h:574
uint32_t length
The length of the location.
Definition ast.h:577
This is the base structure that represents a node in the syntax tree.
Definition ast.h:1083
A generic string type that can have various ownership semantics.
Definition stringy.h:18
This struct represents a token in the Ruby source.
Definition ast.h:544
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376