Ruby: prism/extension.c Source File

Ruby 3.4.6p54 (2025-09-16 revision dbd83256b1cec76c69756ecb8758b9e1079833de)

extension.c
1#include "prism/extension.h"
2
3#ifdef _WIN32
4#include <ruby/win32.h>
5#endif
6
7// NOTE: this file should contain only bindings. All non-trivial logic should be
8// in libprism so it can be shared its the various callers.
9
10 VALUE rb_cPrism;
11 VALUE rb_cPrismNode;
12 VALUE rb_cPrismSource;
13 VALUE rb_cPrismToken;
14 VALUE rb_cPrismLocation;
15
16 VALUE rb_cPrismComment;
17 VALUE rb_cPrismInlineComment;
18 VALUE rb_cPrismEmbDocComment;
19 VALUE rb_cPrismMagicComment;
20 VALUE rb_cPrismParseError;
21 VALUE rb_cPrismParseWarning;
22 VALUE rb_cPrismResult;
23 VALUE rb_cPrismParseResult;
24 VALUE rb_cPrismLexResult;
25 VALUE rb_cPrismParseLexResult;
26 VALUE rb_cPrismStringQuery;
27 VALUE rb_cPrismScope;
28
29 VALUE rb_cPrismDebugEncoding;
30
31 ID rb_id_option_command_line;
32 ID rb_id_option_encoding;
33 ID rb_id_option_filepath;
34 ID rb_id_option_freeze;
35 ID rb_id_option_frozen_string_literal;
36 ID rb_id_option_line;
37 ID rb_id_option_main_script;
38 ID rb_id_option_partial_script;
39 ID rb_id_option_scopes;
40 ID rb_id_option_version;
41 ID rb_id_source_for;
42 ID rb_id_forwarding_positionals;
43 ID rb_id_forwarding_keywords;
44 ID rb_id_forwarding_block;
45 ID rb_id_forwarding_all;
46
47/******************************************************************************/
48/* IO of Ruby code */
49/******************************************************************************/
50
55static const char *
56check_string(VALUE value) {
57 // Check if the value is a string. If it's not, then raise a type error.
58 if (!RB_TYPE_P(value, T_STRING)) {
59 rb_raise(rb_eTypeError, "wrong argument type %" PRIsVALUE " (expected String)", rb_obj_class(value));
60 }
61
62 // Otherwise, return the value as a C string.
63 return RSTRING_PTR(value);
64}
65
69static void
70input_load_string(pm_string_t *input, VALUE string) {
71 // Check if the string is a string. If it's not, then raise a type error.
72 if (!RB_TYPE_P(string, T_STRING)) {
73 rb_raise(rb_eTypeError, "wrong argument type %" PRIsVALUE " (expected String)", rb_obj_class(string));
74 }
75
76 pm_string_constant_init(input, RSTRING_PTR(string), RSTRING_LEN(string));
77}
78
79/******************************************************************************/
80/* Building C options from Ruby options */
81/******************************************************************************/
82
86static void
87build_options_scopes(pm_options_t *options, VALUE scopes) {
88 // Check if the value is an array. If it's not, then raise a type error.
89 if (!RB_TYPE_P(scopes, T_ARRAY)) {
90 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(scopes));
91 }
92
93 // Initialize the scopes array.
94 size_t scopes_count = RARRAY_LEN(scopes);
95 if (!pm_options_scopes_init(options, scopes_count)) {
96 rb_raise(rb_eNoMemError, "failed to allocate memory");
97 }
98
99 // Iterate over the scopes and add them to the options.
100 for (size_t scope_index = 0; scope_index < scopes_count; scope_index++) {
101 VALUE scope = rb_ary_entry(scopes, scope_index);
102
103 // The scope can be either an array or it can be a Prism::Scope object.
104 // Parse out the correct values here from either.
105 VALUE locals;
106 uint8_t forwarding = PM_OPTIONS_SCOPE_FORWARDING_NONE;
107
108 if (RB_TYPE_P(scope, T_ARRAY)) {
109 locals = scope;
110 } else if (rb_obj_is_kind_of(scope, rb_cPrismScope)) {
111 locals = rb_ivar_get(scope, rb_intern("@locals"));
112 if (!RB_TYPE_P(locals, T_ARRAY)) {
113 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(locals));
114 }
115
116 VALUE names = rb_ivar_get(scope, rb_intern("@forwarding"));
117 if (!RB_TYPE_P(names, T_ARRAY)) {
118 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(names));
119 }
120
121 size_t names_count = RARRAY_LEN(names);
122 for (size_t name_index = 0; name_index < names_count; name_index++) {
123 VALUE name = rb_ary_entry(names, name_index);
124
125 // Check that the name is a symbol. If it's not, then raise
126 // a type error.
127 if (!RB_TYPE_P(name, T_SYMBOL)) {
128 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Symbol)", rb_obj_class(name));
129 }
130
131 ID id = SYM2ID(name);
132 if (id == rb_id_forwarding_positionals) {
133 forwarding |= PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS;
134 } else if (id == rb_id_forwarding_keywords) {
135 forwarding |= PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS;
136 } else if (id == rb_id_forwarding_block) {
137 forwarding |= PM_OPTIONS_SCOPE_FORWARDING_BLOCK;
138 } else if (id == rb_id_forwarding_all) {
139 forwarding |= PM_OPTIONS_SCOPE_FORWARDING_ALL;
140 } else {
141 rb_raise(rb_eArgError, "invalid forwarding value: %" PRIsVALUE, name);
142 }
143 }
144 } else {
145 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array or Prism::Scope)", rb_obj_class(scope));
146 }
147
148 // Initialize the scope array.
149 size_t locals_count = RARRAY_LEN(locals);
150 pm_options_scope_t *options_scope = &options->scopes[scope_index];
151 if (!pm_options_scope_init(options_scope, locals_count)) {
152 rb_raise(rb_eNoMemError, "failed to allocate memory");
153 }
154
155 // Iterate over the locals and add them to the scope.
156 for (size_t local_index = 0; local_index < locals_count; local_index++) {
157 VALUE local = rb_ary_entry(locals, local_index);
158
159 // Check that the local is a symbol. If it's not, then raise a
160 // type error.
161 if (!RB_TYPE_P(local, T_SYMBOL)) {
162 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Symbol)", rb_obj_class(local));
163 }
164
165 // Add the local to the scope.
166 pm_string_t *scope_local = &options_scope->locals[local_index];
167 const char *name = rb_id2name(SYM2ID(local));
168 pm_string_constant_init(scope_local, name, strlen(name));
169 }
170
171 // Now set the forwarding options.
172 pm_options_scope_forwarding_set(options_scope, forwarding);
173 }
174}
175
179static int
180build_options_i(VALUE key, VALUE value, VALUE argument) {
181 pm_options_t *options = (pm_options_t *) argument;
182 ID key_id = SYM2ID(key);
183
184 if (key_id == rb_id_option_filepath) {
185 if (!NIL_P(value)) pm_options_filepath_set(options, check_string(value));
186 } else if (key_id == rb_id_option_encoding) {
187 if (!NIL_P(value)) {
188 if (value == Qfalse) {
189 pm_options_encoding_locked_set(options, true);
190 } else {
191 pm_options_encoding_set(options, rb_enc_name(rb_to_encoding(value)));
192 }
193 }
194 } else if (key_id == rb_id_option_line) {
195 if (!NIL_P(value)) pm_options_line_set(options, NUM2INT(value));
196 } else if (key_id == rb_id_option_frozen_string_literal) {
197 if (!NIL_P(value)) pm_options_frozen_string_literal_set(options, RTEST(value));
198 } else if (key_id == rb_id_option_version) {
199 if (!NIL_P(value)) {
200 const char *version = check_string(value);
201
202 if (!pm_options_version_set(options, version, RSTRING_LEN(value))) {
203 rb_raise(rb_eArgError, "invalid version: %" PRIsVALUE, value);
204 }
205 }
206 } else if (key_id == rb_id_option_scopes) {
207 if (!NIL_P(value)) build_options_scopes(options, value);
208 } else if (key_id == rb_id_option_command_line) {
209 if (!NIL_P(value)) {
210 const char *string = check_string(value);
211 uint8_t command_line = 0;
212
213 for (size_t index = 0; index < strlen(string); index++) {
214 switch (string[index]) {
215 case 'a': command_line |= PM_OPTIONS_COMMAND_LINE_A; break;
216 case 'e': command_line |= PM_OPTIONS_COMMAND_LINE_E; break;
217 case 'l': command_line |= PM_OPTIONS_COMMAND_LINE_L; break;
218 case 'n': command_line |= PM_OPTIONS_COMMAND_LINE_N; break;
219 case 'p': command_line |= PM_OPTIONS_COMMAND_LINE_P; break;
220 case 'x': command_line |= PM_OPTIONS_COMMAND_LINE_X; break;
221 default: rb_raise(rb_eArgError, "invalid command line flag: '%c'", string[index]); break;
222 }
223 }
224
225 pm_options_command_line_set(options, command_line);
226 }
227 } else if (key_id == rb_id_option_main_script) {
228 if (!NIL_P(value)) pm_options_main_script_set(options, RTEST(value));
229 } else if (key_id == rb_id_option_partial_script) {
230 if (!NIL_P(value)) pm_options_partial_script_set(options, RTEST(value));
231 } else if (key_id == rb_id_option_freeze) {
232 if (!NIL_P(value)) pm_options_freeze_set(options, RTEST(value));
233 } else {
234 rb_raise(rb_eArgError, "unknown keyword: %" PRIsVALUE, key);
235 }
236
237 return ST_CONTINUE;
238}
239
245 struct build_options_data {
246 pm_options_t *options;
247 VALUE keywords;
248};
249
254static VALUE
255build_options(VALUE argument) {
256 struct build_options_data *data = (struct build_options_data *) argument;
257 rb_hash_foreach(data->keywords, build_options_i, (VALUE) data->options);
258 return Qnil;
259}
260
264static void
265extract_options(pm_options_t *options, VALUE filepath, VALUE keywords) {
266 options->line = 1; // default
267
268 if (!NIL_P(keywords)) {
269 struct build_options_data data = { .options = options, .keywords = keywords };
270 struct build_options_data *argument = &data;
271
272 int state = 0;
273 rb_protect(build_options, (VALUE) argument, &state);
274
275 if (state != 0) {
276 pm_options_free(options);
277 rb_jump_tag(state);
278 }
279 }
280
281 if (!NIL_P(filepath)) {
282 if (!RB_TYPE_P(filepath, T_STRING)) {
283 pm_options_free(options);
284 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected String)", rb_obj_class(filepath));
285 }
286
287 pm_options_filepath_set(options, RSTRING_PTR(filepath));
288 }
289}
290
294static void
295string_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options) {
296 VALUE string;
297 VALUE keywords;
298 rb_scan_args(argc, argv, "1:", &string, &keywords);
299
300 extract_options(options, Qnil, keywords);
301 input_load_string(input, string);
302}
303
307static void
308file_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options, VALUE *encoded_filepath) {
309 VALUE filepath;
310 VALUE keywords;
311 rb_scan_args(argc, argv, "1:", &filepath, &keywords);
312
313 Check_Type(filepath, T_STRING);
314 *encoded_filepath = rb_str_encode_ospath(filepath);
315 extract_options(options, *encoded_filepath, keywords);
316
317 const char *source = (const char *) pm_string_source(&options->filepath);
318 pm_string_init_result_t result;
319
320 switch (result = pm_string_file_init(input, source)) {
321 case PM_STRING_INIT_SUCCESS:
322 break;
323 case PM_STRING_INIT_ERROR_GENERIC: {
324 pm_options_free(options);
325
326#ifdef _WIN32
327 int e = rb_w32_map_errno(GetLastError());
328#else
329 int e = errno;
330#endif
331
332 rb_syserr_fail(e, source);
333 break;
334 }
335 case PM_STRING_INIT_ERROR_DIRECTORY:
336 pm_options_free(options);
337 rb_syserr_fail(EISDIR, source);
338 break;
339 default:
340 pm_options_free(options);
341 rb_raise(rb_eRuntimeError, "Unknown error (%d) initializing file: %s", result, source);
342 break;
343 }
344}
345
346#ifndef PRISM_EXCLUDE_SERIALIZATION
347
348/******************************************************************************/
349/* Serializing the AST */
350/******************************************************************************/
351
355static VALUE
356dump_input(pm_string_t *input, const pm_options_t *options) {
357 pm_buffer_t buffer;
358 if (!pm_buffer_init(&buffer)) {
359 rb_raise(rb_eNoMemError, "failed to allocate memory");
360 }
361
362 pm_parser_t parser;
363 pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
364
365 pm_node_t *node = pm_parse(&parser);
366 pm_serialize(&parser, node, &buffer);
367
368 VALUE result = rb_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer));
369 pm_node_destroy(&parser, node);
370 pm_buffer_free(&buffer);
371 pm_parser_free(&parser);
372
373 return result;
374}
375
383static VALUE
384dump(int argc, VALUE *argv, VALUE self) {
385 pm_string_t input;
386 pm_options_t options = { 0 };
387 string_options(argc, argv, &input, &options);
388
389#ifdef PRISM_BUILD_DEBUG
390 size_t length = pm_string_length(&input);
391 char* dup = xmalloc(length);
392 memcpy(dup, pm_string_source(&input), length);
393 pm_string_constant_init(&input, dup, length);
394#endif
395
396 VALUE value = dump_input(&input, &options);
397 if (options.freeze) rb_obj_freeze(value);
398
399#ifdef PRISM_BUILD_DEBUG
400 xfree(dup);
401#endif
402
403 pm_string_free(&input);
404 pm_options_free(&options);
405
406 return value;
407}
408
416static VALUE
417dump_file(int argc, VALUE *argv, VALUE self) {
418 pm_string_t input;
419 pm_options_t options = { 0 };
420
421 VALUE encoded_filepath;
422 file_options(argc, argv, &input, &options, &encoded_filepath);
423
424 VALUE value = dump_input(&input, &options);
425 pm_string_free(&input);
426 pm_options_free(&options);
427
428 return value;
429}
430
431#endif
432
433/******************************************************************************/
434/* Extracting values for the parse result */
435/******************************************************************************/
436
441static inline VALUE
442rb_class_new_instance_freeze(int argc, const VALUE *argv, VALUE klass, bool freeze) {
443 VALUE value = rb_class_new_instance(argc, argv, klass);
444 if (freeze) rb_obj_freeze(value);
445 return value;
446}
447
451static inline VALUE
452parser_location(const pm_parser_t *parser, VALUE source, bool freeze, const uint8_t *start, size_t length) {
453 VALUE argv[] = { source, LONG2FIX(start - parser->start), LONG2FIX(length) };
454 return rb_class_new_instance_freeze(3, argv, rb_cPrismLocation, freeze);
455}
456
460#define PARSER_LOCATION_LOC(parser, source, freeze, loc) \
461 parser_location(parser, source, freeze, loc.start, (size_t) (loc.end - loc.start))
462
466static inline VALUE
467parser_comment(const pm_parser_t *parser, VALUE source, bool freeze, const pm_comment_t *comment) {
468 VALUE argv[] = { PARSER_LOCATION_LOC(parser, source, freeze, comment->location) };
469 VALUE type = (comment->type == PM_COMMENT_EMBDOC) ? rb_cPrismEmbDocComment : rb_cPrismInlineComment;
470 return rb_class_new_instance_freeze(1, argv, type, freeze);
471}
472
476static VALUE
477parser_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
478 VALUE comments = rb_ary_new_capa(parser->comment_list.size);
479
480 for (
481 const pm_comment_t *comment = (const pm_comment_t *) parser->comment_list.head;
482 comment != NULL;
483 comment = (const pm_comment_t *) comment->node.next
484 ) {
485 VALUE value = parser_comment(parser, source, freeze, comment);
486 rb_ary_push(comments, value);
487 }
488
489 if (freeze) rb_obj_freeze(comments);
490 return comments;
491}
492
496static inline VALUE
497parser_magic_comment(const pm_parser_t *parser, VALUE source, bool freeze, const pm_magic_comment_t *magic_comment) {
498 VALUE key_loc = parser_location(parser, source, freeze, magic_comment->key_start, magic_comment->key_length);
499 VALUE value_loc = parser_location(parser, source, freeze, magic_comment->value_start, magic_comment->value_length);
500 VALUE argv[] = { key_loc, value_loc };
501 return rb_class_new_instance_freeze(2, argv, rb_cPrismMagicComment, freeze);
502}
503
507static VALUE
508parser_magic_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
509 VALUE magic_comments = rb_ary_new_capa(parser->magic_comment_list.size);
510
511 for (
512 const pm_magic_comment_t *magic_comment = (const pm_magic_comment_t *) parser->magic_comment_list.head;
513 magic_comment != NULL;
514 magic_comment = (const pm_magic_comment_t *) magic_comment->node.next
515 ) {
516 VALUE value = parser_magic_comment(parser, source, freeze, magic_comment);
517 rb_ary_push(magic_comments, value);
518 }
519
520 if (freeze) rb_obj_freeze(magic_comments);
521 return magic_comments;
522}
523
528static VALUE
529parser_data_loc(const pm_parser_t *parser, VALUE source, bool freeze) {
530 if (parser->data_loc.end == NULL) {
531 return Qnil;
532 } else {
533 return PARSER_LOCATION_LOC(parser, source, freeze, parser->data_loc);
534 }
535}
536
540static VALUE
541parser_errors(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bool freeze) {
542 VALUE errors = rb_ary_new_capa(parser->error_list.size);
543
544 for (
545 const pm_diagnostic_t *error = (const pm_diagnostic_t *) parser->error_list.head;
546 error != NULL;
547 error = (const pm_diagnostic_t *) error->node.next
548 ) {
549 VALUE type = ID2SYM(rb_intern(pm_diagnostic_id_human(error->diag_id)));
550 VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(error->message, encoding));
551 VALUE location = PARSER_LOCATION_LOC(parser, source, freeze, error->location);
552
553 VALUE level = Qnil;
554 switch (error->level) {
555 case PM_ERROR_LEVEL_SYNTAX:
556 level = ID2SYM(rb_intern("syntax"));
557 break;
558 case PM_ERROR_LEVEL_ARGUMENT:
559 level = ID2SYM(rb_intern("argument"));
560 break;
561 case PM_ERROR_LEVEL_LOAD:
562 level = ID2SYM(rb_intern("load"));
563 break;
564 default:
565 rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, error->level);
566 }
567
568 VALUE argv[] = { type, message, location, level };
569 VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseError, freeze);
570 rb_ary_push(errors, value);
571 }
572
573 if (freeze) rb_obj_freeze(errors);
574 return errors;
575}
576
580static VALUE
581parser_warnings(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bool freeze) {
582 VALUE warnings = rb_ary_new_capa(parser->warning_list.size);
583
584 for (
585 const pm_diagnostic_t *warning = (const pm_diagnostic_t *) parser->warning_list.head;
586 warning != NULL;
587 warning = (const pm_diagnostic_t *) warning->node.next
588 ) {
589 VALUE type = ID2SYM(rb_intern(pm_diagnostic_id_human(warning->diag_id)));
590 VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(warning->message, encoding));
591 VALUE location = PARSER_LOCATION_LOC(parser, source, freeze, warning->location);
592
593 VALUE level = Qnil;
594 switch (warning->level) {
595 case PM_WARNING_LEVEL_DEFAULT:
596 level = ID2SYM(rb_intern("default"));
597 break;
598 case PM_WARNING_LEVEL_VERBOSE:
599 level = ID2SYM(rb_intern("verbose"));
600 break;
601 default:
602 rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, warning->level);
603 }
604
605 VALUE argv[] = { type, message, location, level };
606 VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseWarning, freeze);
607 rb_ary_push(warnings, value);
608 }
609
610 if (freeze) rb_obj_freeze(warnings);
611 return warnings;
612}
613
617static VALUE
618parse_result_create(VALUE class, const pm_parser_t *parser, VALUE value, rb_encoding *encoding, VALUE source, bool freeze) {
619 VALUE result_argv[] = {
620 value,
621 parser_comments(parser, source, freeze),
622 parser_magic_comments(parser, source, freeze),
623 parser_data_loc(parser, source, freeze),
624 parser_errors(parser, encoding, source, freeze),
625 parser_warnings(parser, encoding, source, freeze),
626 source
627 };
628
629 return rb_class_new_instance_freeze(7, result_argv, class, freeze);
630}
631
632/******************************************************************************/
633/* Lexing Ruby code */
634/******************************************************************************/
635
641 typedef struct {
642 VALUE source;
643 VALUE tokens;
644 rb_encoding *encoding;
645 bool freeze;
646} parse_lex_data_t;
647
653static void
654parse_lex_token(void *data, pm_parser_t *parser, pm_token_t *token) {
655 parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) parser->lex_callback->data;
656
657 VALUE value = pm_token_new(parser, token, parse_lex_data->encoding, parse_lex_data->source, parse_lex_data->freeze);
658 VALUE yields = rb_assoc_new(value, INT2FIX(parser->lex_state));
659
660 if (parse_lex_data->freeze) {
661 rb_obj_freeze(value);
662 rb_obj_freeze(yields);
663 }
664
665 rb_ary_push(parse_lex_data->tokens, yields);
666}
667
673static void
674parse_lex_encoding_changed_callback(pm_parser_t *parser) {
675 parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) parser->lex_callback->data;
676 parse_lex_data->encoding = rb_enc_find(parser->encoding->name);
677
678 // Since the encoding changed, we need to go back and change the encoding of
679 // the tokens that were already lexed. This is only going to end up being
680 // one or two tokens, since the encoding can only change at the top of the
681 // file.
682 VALUE tokens = parse_lex_data->tokens;
683 VALUE next_tokens = rb_ary_new();
684
685 for (long index = 0; index < RARRAY_LEN(tokens); index++) {
686 VALUE yields = rb_ary_entry(tokens, index);
687 VALUE token = rb_ary_entry(yields, 0);
688
689 VALUE value = rb_ivar_get(token, rb_intern("@value"));
690 VALUE next_value = rb_str_dup(value);
691
692 rb_enc_associate(next_value, parse_lex_data->encoding);
693 if (parse_lex_data->freeze) rb_obj_freeze(next_value);
694
695 VALUE next_token_argv[] = {
696 parse_lex_data->source,
697 rb_ivar_get(token, rb_intern("@type")),
698 next_value,
699 rb_ivar_get(token, rb_intern("@location"))
700 };
701
702 VALUE next_token = rb_class_new_instance(4, next_token_argv, rb_cPrismToken);
703 VALUE next_yields = rb_assoc_new(next_token, rb_ary_entry(yields, 1));
704
705 if (parse_lex_data->freeze) {
706 rb_obj_freeze(next_token);
707 rb_obj_freeze(next_yields);
708 }
709
710 rb_ary_push(next_tokens, next_yields);
711 }
712
713 rb_ary_replace(parse_lex_data->tokens, next_tokens);
714}
715
720static VALUE
721parse_lex_input(pm_string_t *input, const pm_options_t *options, bool return_nodes) {
722 pm_parser_t parser;
723 pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
724 pm_parser_register_encoding_changed_callback(&parser, parse_lex_encoding_changed_callback);
725
726 VALUE source_string = rb_str_new((const char *) pm_string_source(input), pm_string_length(input));
727 VALUE offsets = rb_ary_new_capa(parser.newline_list.size);
728 VALUE source = rb_funcall(rb_cPrismSource, rb_id_source_for, 3, source_string, LONG2NUM(parser.start_line), offsets);
729
730 parse_lex_data_t parse_lex_data = {
731 .source = source,
732 .tokens = rb_ary_new(),
733 .encoding = rb_utf8_encoding(),
734 .freeze = options->freeze,
735 };
736
737 parse_lex_data_t *data = &parse_lex_data;
738 pm_lex_callback_t lex_callback = (pm_lex_callback_t) {
739 .data = (void *) data,
740 .callback = parse_lex_token,
741 };
742
743 parser.lex_callback = &lex_callback;
744 pm_node_t *node = pm_parse(&parser);
745
746 // Here we need to update the Source object to have the correct
747 // encoding for the source string and the correct newline offsets.
748 // We do it here because we've already created the Source object and given
749 // it over to all of the tokens, and both of these are only set after pm_parse().
750 rb_encoding *encoding = rb_enc_find(parser.encoding->name);
751 rb_enc_associate(source_string, encoding);
752
753 for (size_t index = 0; index < parser.newline_list.size; index++) {
754 rb_ary_push(offsets, ULONG2NUM(parser.newline_list.offsets[index]));
755 }
756
757 if (options->freeze) {
758 rb_obj_freeze(source_string);
759 rb_obj_freeze(offsets);
760 rb_obj_freeze(source);
761 rb_obj_freeze(parse_lex_data.tokens);
762 }
763
764 VALUE result;
765 if (return_nodes) {
766 VALUE value = rb_ary_new_capa(2);
767 rb_ary_push(value, pm_ast_new(&parser, node, parse_lex_data.encoding, source, options->freeze));
768 rb_ary_push(value, parse_lex_data.tokens);
769 if (options->freeze) rb_obj_freeze(value);
770 result = parse_result_create(rb_cPrismParseLexResult, &parser, value, parse_lex_data.encoding, source, options->freeze);
771 } else {
772 result = parse_result_create(rb_cPrismLexResult, &parser, parse_lex_data.tokens, parse_lex_data.encoding, source, options->freeze);
773 }
774
775 pm_node_destroy(&parser, node);
776 pm_parser_free(&parser);
777
778 return result;
779}
780
788static VALUE
789lex(int argc, VALUE *argv, VALUE self) {
790 pm_string_t input;
791 pm_options_t options = { 0 };
792 string_options(argc, argv, &input, &options);
793
794 VALUE result = parse_lex_input(&input, &options, false);
795 pm_string_free(&input);
796 pm_options_free(&options);
797
798 return result;
799}
800
808static VALUE
809lex_file(int argc, VALUE *argv, VALUE self) {
810 pm_string_t input;
811 pm_options_t options = { 0 };
812
813 VALUE encoded_filepath;
814 file_options(argc, argv, &input, &options, &encoded_filepath);
815
816 VALUE value = parse_lex_input(&input, &options, false);
817 pm_string_free(&input);
818 pm_options_free(&options);
819
820 return value;
821}
822
823/******************************************************************************/
824/* Parsing Ruby code */
825/******************************************************************************/
826
830static VALUE
831parse_input(pm_string_t *input, const pm_options_t *options) {
832 pm_parser_t parser;
833 pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
834
835 pm_node_t *node = pm_parse(&parser);
836 rb_encoding *encoding = rb_enc_find(parser.encoding->name);
837
838 VALUE source = pm_source_new(&parser, encoding, options->freeze);
839 VALUE value = pm_ast_new(&parser, node, encoding, source, options->freeze);
840 VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source, options->freeze);
841
842 if (options->freeze) {
843 rb_obj_freeze(source);
844 }
845
846 pm_node_destroy(&parser, node);
847 pm_parser_free(&parser);
848
849 return result;
850}
851
894static VALUE
895parse(int argc, VALUE *argv, VALUE self) {
896 pm_string_t input;
897 pm_options_t options = { 0 };
898 string_options(argc, argv, &input, &options);
899
900#ifdef PRISM_BUILD_DEBUG
901 size_t length = pm_string_length(&input);
902 char* dup = xmalloc(length);
903 memcpy(dup, pm_string_source(&input), length);
904 pm_string_constant_init(&input, dup, length);
905#endif
906
907 VALUE value = parse_input(&input, &options);
908
909#ifdef PRISM_BUILD_DEBUG
910 xfree(dup);
911#endif
912
913 pm_string_free(&input);
914 pm_options_free(&options);
915 return value;
916}
917
925static VALUE
926parse_file(int argc, VALUE *argv, VALUE self) {
927 pm_string_t input;
928 pm_options_t options = { 0 };
929
930 VALUE encoded_filepath;
931 file_options(argc, argv, &input, &options, &encoded_filepath);
932
933 VALUE value = parse_input(&input, &options);
934 pm_string_free(&input);
935 pm_options_free(&options);
936
937 return value;
938}
939
943static void
944profile_input(pm_string_t *input, const pm_options_t *options) {
945 pm_parser_t parser;
946 pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
947
948 pm_node_t *node = pm_parse(&parser);
949 pm_node_destroy(&parser, node);
950 pm_parser_free(&parser);
951}
952
961static VALUE
962profile(int argc, VALUE *argv, VALUE self) {
963 pm_string_t input;
964 pm_options_t options = { 0 };
965
966 string_options(argc, argv, &input, &options);
967 profile_input(&input, &options);
968 pm_string_free(&input);
969 pm_options_free(&options);
970
971 return Qnil;
972}
973
982static VALUE
983profile_file(int argc, VALUE *argv, VALUE self) {
984 pm_string_t input;
985 pm_options_t options = { 0 };
986
987 VALUE encoded_filepath;
988 file_options(argc, argv, &input, &options, &encoded_filepath);
989
990 profile_input(&input, &options);
991 pm_string_free(&input);
992 pm_options_free(&options);
993
994 return Qnil;
995}
996
997static int
998parse_stream_eof(void *stream) {
999 if (rb_funcall((VALUE) stream, rb_intern("eof?"), 0)) {
1000 return 1;
1001 }
1002 return 0;
1003}
1004
1008static char *
1009parse_stream_fgets(char *string, int size, void *stream) {
1010 RUBY_ASSERT(size > 0);
1011
1012 VALUE line = rb_funcall((VALUE) stream, rb_intern("gets"), 1, INT2FIX(size - 1));
1013 if (NIL_P(line)) {
1014 return NULL;
1015 }
1016
1017 const char *cstr = RSTRING_PTR(line);
1018 long length = RSTRING_LEN(line);
1019
1020 memcpy(string, cstr, length);
1021 string[length] = '\0';
1022
1023 return string;
1024}
1025
1033static VALUE
1034parse_stream(int argc, VALUE *argv, VALUE self) {
1035 VALUE stream;
1036 VALUE keywords;
1037 rb_scan_args(argc, argv, "1:", &stream, &keywords);
1038
1039 pm_options_t options = { 0 };
1040 extract_options(&options, Qnil, keywords);
1041
1042 pm_parser_t parser;
1043 pm_buffer_t buffer;
1044
1045 pm_node_t *node = pm_parse_stream(&parser, &buffer, (void *) stream, parse_stream_fgets, parse_stream_eof, &options);
1046 rb_encoding *encoding = rb_enc_find(parser.encoding->name);
1047
1048 VALUE source = pm_source_new(&parser, encoding, options.freeze);
1049 VALUE value = pm_ast_new(&parser, node, encoding, source, options.freeze);
1050 VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source, options.freeze);
1051
1052 pm_node_destroy(&parser, node);
1053 pm_buffer_free(&buffer);
1054 pm_parser_free(&parser);
1055
1056 return result;
1057}
1058
1062static VALUE
1063parse_input_comments(pm_string_t *input, const pm_options_t *options) {
1064 pm_parser_t parser;
1065 pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
1066
1067 pm_node_t *node = pm_parse(&parser);
1068 rb_encoding *encoding = rb_enc_find(parser.encoding->name);
1069
1070 VALUE source = pm_source_new(&parser, encoding, options->freeze);
1071 VALUE comments = parser_comments(&parser, source, options->freeze);
1072
1073 pm_node_destroy(&parser, node);
1074 pm_parser_free(&parser);
1075
1076 return comments;
1077}
1078
1086static VALUE
1087parse_comments(int argc, VALUE *argv, VALUE self) {
1088 pm_string_t input;
1089 pm_options_t options = { 0 };
1090 string_options(argc, argv, &input, &options);
1091
1092 VALUE result = parse_input_comments(&input, &options);
1093 pm_string_free(&input);
1094 pm_options_free(&options);
1095
1096 return result;
1097}
1098
1106static VALUE
1107parse_file_comments(int argc, VALUE *argv, VALUE self) {
1108 pm_string_t input;
1109 pm_options_t options = { 0 };
1110
1111 VALUE encoded_filepath;
1112 file_options(argc, argv, &input, &options, &encoded_filepath);
1113
1114 VALUE value = parse_input_comments(&input, &options);
1115 pm_string_free(&input);
1116 pm_options_free(&options);
1117
1118 return value;
1119}
1120
1135static VALUE
1136parse_lex(int argc, VALUE *argv, VALUE self) {
1137 pm_string_t input;
1138 pm_options_t options = { 0 };
1139 string_options(argc, argv, &input, &options);
1140
1141 VALUE value = parse_lex_input(&input, &options, true);
1142 pm_string_free(&input);
1143 pm_options_free(&options);
1144
1145 return value;
1146}
1147
1162static VALUE
1163parse_lex_file(int argc, VALUE *argv, VALUE self) {
1164 pm_string_t input;
1165 pm_options_t options = { 0 };
1166
1167 VALUE encoded_filepath;
1168 file_options(argc, argv, &input, &options, &encoded_filepath);
1169
1170 VALUE value = parse_lex_input(&input, &options, true);
1171 pm_string_free(&input);
1172 pm_options_free(&options);
1173
1174 return value;
1175}
1176
1180static VALUE
1181parse_input_success_p(pm_string_t *input, const pm_options_t *options) {
1182 pm_parser_t parser;
1183 pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
1184
1185 pm_node_t *node = pm_parse(&parser);
1186 pm_node_destroy(&parser, node);
1187
1188 VALUE result = parser.error_list.size == 0 ? Qtrue : Qfalse;
1189 pm_parser_free(&parser);
1190
1191 return result;
1192}
1193
1201static VALUE
1202parse_success_p(int argc, VALUE *argv, VALUE self) {
1203 pm_string_t input;
1204 pm_options_t options = { 0 };
1205 string_options(argc, argv, &input, &options);
1206
1207 VALUE result = parse_input_success_p(&input, &options);
1208 pm_string_free(&input);
1209 pm_options_free(&options);
1210
1211 return result;
1212}
1213
1221static VALUE
1222parse_failure_p(int argc, VALUE *argv, VALUE self) {
1223 return RTEST(parse_success_p(argc, argv, self)) ? Qfalse : Qtrue;
1224}
1225
1233static VALUE
1234parse_file_success_p(int argc, VALUE *argv, VALUE self) {
1235 pm_string_t input;
1236 pm_options_t options = { 0 };
1237
1238 VALUE encoded_filepath;
1239 file_options(argc, argv, &input, &options, &encoded_filepath);
1240
1241 VALUE result = parse_input_success_p(&input, &options);
1242 pm_string_free(&input);
1243 pm_options_free(&options);
1244
1245 return result;
1246}
1247
1255static VALUE
1256parse_file_failure_p(int argc, VALUE *argv, VALUE self) {
1257 return RTEST(parse_file_success_p(argc, argv, self)) ? Qfalse : Qtrue;
1258}
1259
1260/******************************************************************************/
1261/* String query methods */
1262/******************************************************************************/
1263
1268static VALUE
1269string_query(pm_string_query_t result) {
1270 switch (result) {
1271 case PM_STRING_QUERY_ERROR:
1272 rb_raise(rb_eArgError, "Invalid or non ascii-compatible encoding");
1273 return Qfalse;
1274 case PM_STRING_QUERY_FALSE:
1275 return Qfalse;
1276 case PM_STRING_QUERY_TRUE:
1277 return Qtrue;
1278 }
1279 return Qfalse;
1280}
1281
1290static VALUE
1291string_query_local_p(VALUE self, VALUE string) {
1292 const uint8_t *source = (const uint8_t *) check_string(string);
1293 return string_query(pm_string_query_local(source, RSTRING_LEN(string), rb_enc_get(string)->name));
1294}
1295
1304static VALUE
1305string_query_constant_p(VALUE self, VALUE string) {
1306 const uint8_t *source = (const uint8_t *) check_string(string);
1307 return string_query(pm_string_query_constant(source, RSTRING_LEN(string), rb_enc_get(string)->name));
1308}
1309
1316static VALUE
1317string_query_method_name_p(VALUE self, VALUE string) {
1318 const uint8_t *source = (const uint8_t *) check_string(string);
1319 return string_query(pm_string_query_method_name(source, RSTRING_LEN(string), rb_enc_get(string)->name));
1320}
1321
1322/******************************************************************************/
1323/* Initialization of the extension */
1324/******************************************************************************/
1325
1329RUBY_FUNC_EXPORTED void
1330Init_prism(void) {
1331 // Make sure that the prism library version matches the expected version.
1332 // Otherwise something was compiled incorrectly.
1333 if (strcmp(pm_version(), EXPECTED_PRISM_VERSION) != 0) {
1334 rb_raise(
1335 rb_eRuntimeError,
1336 "The prism library version (%s) does not match the expected version (%s)",
1337 pm_version(),
1338 EXPECTED_PRISM_VERSION
1339 );
1340 }
1341
1342#ifdef HAVE_RB_EXT_RACTOR_SAFE
1343 // Mark this extension as Ractor-safe.
1344 rb_ext_ractor_safe(true);
1345#endif
1346
1347 // Grab up references to all of the constants that we're going to need to
1348 // reference throughout this extension.
1349 rb_cPrism = rb_define_module("Prism");
1350 rb_cPrismNode = rb_define_class_under(rb_cPrism, "Node", rb_cObject);
1351 rb_cPrismSource = rb_define_class_under(rb_cPrism, "Source", rb_cObject);
1352 rb_cPrismToken = rb_define_class_under(rb_cPrism, "Token", rb_cObject);
1353 rb_cPrismLocation = rb_define_class_under(rb_cPrism, "Location", rb_cObject);
1354 rb_cPrismComment = rb_define_class_under(rb_cPrism, "Comment", rb_cObject);
1355 rb_cPrismInlineComment = rb_define_class_under(rb_cPrism, "InlineComment", rb_cPrismComment);
1356 rb_cPrismEmbDocComment = rb_define_class_under(rb_cPrism, "EmbDocComment", rb_cPrismComment);
1357 rb_cPrismMagicComment = rb_define_class_under(rb_cPrism, "MagicComment", rb_cObject);
1358 rb_cPrismParseError = rb_define_class_under(rb_cPrism, "ParseError", rb_cObject);
1359 rb_cPrismParseWarning = rb_define_class_under(rb_cPrism, "ParseWarning", rb_cObject);
1360 rb_cPrismResult = rb_define_class_under(rb_cPrism, "Result", rb_cObject);
1361 rb_cPrismParseResult = rb_define_class_under(rb_cPrism, "ParseResult", rb_cPrismResult);
1362 rb_cPrismLexResult = rb_define_class_under(rb_cPrism, "LexResult", rb_cPrismResult);
1363 rb_cPrismParseLexResult = rb_define_class_under(rb_cPrism, "ParseLexResult", rb_cPrismResult);
1364 rb_cPrismStringQuery = rb_define_class_under(rb_cPrism, "StringQuery", rb_cObject);
1365 rb_cPrismScope = rb_define_class_under(rb_cPrism, "Scope", rb_cObject);
1366
1367 // Intern all of the IDs eagerly that we support so that we don't have to do
1368 // it every time we parse.
1369 rb_id_option_command_line = rb_intern_const("command_line");
1370 rb_id_option_encoding = rb_intern_const("encoding");
1371 rb_id_option_filepath = rb_intern_const("filepath");
1372 rb_id_option_freeze = rb_intern_const("freeze");
1373 rb_id_option_frozen_string_literal = rb_intern_const("frozen_string_literal");
1374 rb_id_option_line = rb_intern_const("line");
1375 rb_id_option_main_script = rb_intern_const("main_script");
1376 rb_id_option_partial_script = rb_intern_const("partial_script");
1377 rb_id_option_scopes = rb_intern_const("scopes");
1378 rb_id_option_version = rb_intern_const("version");
1379 rb_id_source_for = rb_intern("for");
1380 rb_id_forwarding_positionals = rb_intern("*");
1381 rb_id_forwarding_keywords = rb_intern("**");
1382 rb_id_forwarding_block = rb_intern("&");
1383 rb_id_forwarding_all = rb_intern("...");
1384
1388 rb_define_const(rb_cPrism, "VERSION", rb_str_freeze(rb_str_new_cstr(EXPECTED_PRISM_VERSION)));
1389
1390 // First, the functions that have to do with lexing and parsing.
1391 rb_define_singleton_method(rb_cPrism, "lex", lex, -1);
1392 rb_define_singleton_method(rb_cPrism, "lex_file", lex_file, -1);
1393 rb_define_singleton_method(rb_cPrism, "parse", parse, -1);
1394 rb_define_singleton_method(rb_cPrism, "parse_file", parse_file, -1);
1395 rb_define_singleton_method(rb_cPrism, "profile", profile, -1);
1396 rb_define_singleton_method(rb_cPrism, "profile_file", profile_file, -1);
1397 rb_define_singleton_method(rb_cPrism, "parse_stream", parse_stream, -1);
1398 rb_define_singleton_method(rb_cPrism, "parse_comments", parse_comments, -1);
1399 rb_define_singleton_method(rb_cPrism, "parse_file_comments", parse_file_comments, -1);
1400 rb_define_singleton_method(rb_cPrism, "parse_lex", parse_lex, -1);
1401 rb_define_singleton_method(rb_cPrism, "parse_lex_file", parse_lex_file, -1);
1402 rb_define_singleton_method(rb_cPrism, "parse_success?", parse_success_p, -1);
1403 rb_define_singleton_method(rb_cPrism, "parse_failure?", parse_failure_p, -1);
1404 rb_define_singleton_method(rb_cPrism, "parse_file_success?", parse_file_success_p, -1);
1405 rb_define_singleton_method(rb_cPrism, "parse_file_failure?", parse_file_failure_p, -1);
1406
1407#ifndef PRISM_EXCLUDE_SERIALIZATION
1408 rb_define_singleton_method(rb_cPrism, "dump", dump, -1);
1409 rb_define_singleton_method(rb_cPrism, "dump_file", dump_file, -1);
1410#endif
1411
1412 rb_define_singleton_method(rb_cPrismStringQuery, "local?", string_query_local_p, 1);
1413 rb_define_singleton_method(rb_cPrismStringQuery, "constant?", string_query_constant_p, 1);
1414 rb_define_singleton_method(rb_cPrismStringQuery, "method_name?", string_query_method_name_p, 1);
1415
1416 // Next, initialize the other APIs.
1417 Init_prism_api_node();
1418 Init_prism_pack();
1419}
RUBY_ASSERT
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
rb_define_singleton_method
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
Definition cxxanyargs.hpp:685
PM_WARNING_LEVEL_DEFAULT
@ PM_WARNING_LEVEL_DEFAULT
For warnings which should be emitted if $VERBOSE != nil.
Definition diagnostic.h:409
PM_WARNING_LEVEL_VERBOSE
@ PM_WARNING_LEVEL_VERBOSE
For warnings which should be emitted if $VERBOSE == true.
Definition diagnostic.h:412
PM_ERROR_LEVEL_ARGUMENT
@ PM_ERROR_LEVEL_ARGUMENT
For errors that should raise an argument error.
Definition diagnostic.h:398
PM_ERROR_LEVEL_LOAD
@ PM_ERROR_LEVEL_LOAD
For errors that should raise a load error.
Definition diagnostic.h:401
PM_ERROR_LEVEL_SYNTAX
@ PM_ERROR_LEVEL_SYNTAX
For errors that should raise a syntax error.
Definition diagnostic.h:395
rb_define_class_under
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1012
rb_define_module
VALUE rb_define_module(const char *name)
Defines a top-level module.
Definition class.c:1095
rb_scan_args
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:2640
T_STRING
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
xfree
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
INT2FIX
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
ID2SYM
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
ULONG2NUM
#define ULONG2NUM
Old name of RB_ULONG2NUM.
Definition long.h:60
SYM2ID
#define SYM2ID
Old name of RB_SYM2ID.
Definition symbol.h:45
xmalloc
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
LONG2FIX
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
LONG2NUM
#define LONG2NUM
Old name of RB_LONG2NUM.
Definition long.h:50
Qtrue
#define Qtrue
Old name of RUBY_Qtrue.
Definition special_consts.h:61
NUM2INT
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
Qnil
#define Qnil
Old name of RUBY_Qnil.
Definition special_consts.h:60
Qfalse
#define Qfalse
Old name of RUBY_Qfalse.
Definition special_consts.h:59
T_ARRAY
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
NIL_P
#define NIL_P
Old name of RB_NIL_P.
Definition special_consts.h:55
T_SYMBOL
#define T_SYMBOL
Old name of RUBY_T_SYMBOL.
Definition value_type.h:80
rb_syserr_fail
void rb_syserr_fail(int e, const char *mesg)
Raises appropriate exception that represents a C errno.
Definition error.c:3877
rb_eNoMemError
VALUE rb_eNoMemError
NoMemoryError exception.
Definition error.c:1441
rb_eTypeError
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1430
rb_eRuntimeError
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1428
rb_class_new_instance
VALUE rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
Allocates, then initialises an instance of the given class.
Definition object.c:2138
rb_obj_class
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:247
rb_obj_is_kind_of
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:865
rb_obj_freeze
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1260
rb_utf8_encoding
rb_encoding * rb_utf8_encoding(void)
Queries the encoding that represents UTF-8.
Definition encoding.c:1475
rb_enc_str_new_cstr
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:1103
rb_funcall
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1099
rb_ary_new
VALUE rb_ary_new(void)
Allocates a new, empty array.
Definition array.c:741
rb_ext_ractor_safe
void rb_ext_ractor_safe(bool flag)
Asserts that the extension library that calls this function is aware of Ractor.
Definition load.c:1237
rb_str_new
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1498
rb_str_dup
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition string.c:1921
rb_str_freeze
VALUE rb_str_freeze(VALUE str)
This is the implementation of String#freeze.
Definition string.c:3181
rb_str_new_cstr
#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:1514
rb_ivar_get
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:1426
rb_intern_const
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:284
rb_define_const
void rb_define_const(VALUE klass, const char *name, VALUE val)
Defines a Ruby level constant under a namespace.
Definition variable.c:3797
ruby::backward::cxxanyargs::type
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition cxxanyargs.hpp:56
ruby::backward::cxxanyargs::rb_hash_foreach
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
Definition cxxanyargs.hpp:482
pm_options_scope_t
struct pm_options_scope pm_options_scope_t
A scope of locals surrounding the code that is being parsed.
PM_OPTIONS_COMMAND_LINE_E
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:207
PM_OPTIONS_COMMAND_LINE_L
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:213
pm_options_t
struct pm_options pm_options_t
The options that can be passed to the parser.
PM_OPTIONS_COMMAND_LINE_A
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:200
PM_OPTIONS_SCOPE_FORWARDING_NONE
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_NONE
The default value for parameters.
Definition options.h:48
PM_OPTIONS_SCOPE_FORWARDING_ALL
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_ALL
When the scope is fowarding with the ... parameter.
Definition options.h:60
PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS
When the scope is fowarding with the * parameter.
Definition options.h:51
PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS
When the scope is fowarding with the ** parameter.
Definition options.h:54
PM_OPTIONS_COMMAND_LINE_N
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:219
PM_OPTIONS_COMMAND_LINE_X
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:231
PM_OPTIONS_SCOPE_FORWARDING_BLOCK
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_BLOCK
When the scope is fowarding with the & parameter.
Definition options.h:57
PM_OPTIONS_COMMAND_LINE_P
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:225
pm_parser_t
struct pm_parser pm_parser_t
The parser used to parse Ruby source.
Definition parser.h:267
pm_comment_t
struct pm_comment pm_comment_t
This is a node in the linked list of comments that we've found while parsing.
pm_string_init_result_t
pm_string_init_result_t
Represents the result of calling pm_string_mapped_init or pm_string_file_init.
Definition pm_string.h:105
PM_STRING_INIT_SUCCESS
@ PM_STRING_INIT_SUCCESS
Indicates that the string was successfully initialized.
Definition pm_string.h:107
PM_STRING_INIT_ERROR_GENERIC
@ PM_STRING_INIT_ERROR_GENERIC
Indicates a generic error from a string_*_init function, where the type of error should be read from ...
Definition pm_string.h:112
PM_STRING_INIT_ERROR_DIRECTORY
@ PM_STRING_INIT_ERROR_DIRECTORY
Indicates that the file that was attempted to be opened was a directory.
Definition pm_string.h:116
pm_string_query_t
pm_string_query_t
Represents the results of a slice query.
Definition prism.h:265
PM_STRING_QUERY_TRUE
@ PM_STRING_QUERY_TRUE
Returned if the result of the slice query is true.
Definition prism.h:273
PM_STRING_QUERY_ERROR
@ PM_STRING_QUERY_ERROR
Returned if the encoding given to a slice query was invalid.
Definition prism.h:267
PM_STRING_QUERY_FALSE
@ PM_STRING_QUERY_FALSE
Returned if the result of the slice query is false.
Definition prism.h:270
pm_version
PRISM_EXPORTED_FUNCTION const char * pm_version(void)
The prism version and the serialization format.
Definition prism.c:7
RARRAY_LEN
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
errno
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
RTEST
#define RTEST
This is an old name of RB_TEST.
Definition special_consts.h:51
build_options_data
We need a struct here to pass through rb_protect and it has to be a single value.
Definition extension.c:245
magic_comment
Definition parse.c:20345
parse_lex_data_t
This struct gets stored in the parser and passed in to the lex callback any time a new token is found...
Definition extension.c:641
pm_buffer_t
A pm_buffer_t is a simple memory buffer that stores data in a contiguous block of memory.
Definition pm_buffer.h:22
pm_comment::type
pm_comment_type_t type
The type of comment that we've found.
Definition parser.h:466
pm_comment::location
pm_location_t location
The location of the comment in the source.
Definition parser.h:463
pm_diagnostic_t
This struct represents a diagnostic generated during parsing.
Definition diagnostic.h:363
pm_encoding_t::name
const char * name
The name of the encoding.
Definition encoding.h:56
pm_lex_callback_t
When you are lexing through a file, the lexer needs all of the information that the parser additional...
Definition parser.h:506
pm_lex_callback_t::data
void * data
This opaque pointer is used to provide whatever information the user deemed necessary to the callback...
Definition parser.h:512
pm_list_node::next
struct pm_list_node * next
A pointer to the next node in the list.
Definition pm_list.h:48
pm_list_t::head
pm_list_node_t * head
A pointer to the head of the list.
Definition pm_list.h:60
pm_list_t::size
size_t size
The size of the list.
Definition pm_list.h:57
pm_location_t::end
const uint8_t * end
A pointer to the end location of the range in the source.
Definition ast.h:549
pm_magic_comment_t
This is a node in the linked list of magic comments that we've found while parsing.
Definition parser.h:475
pm_newline_list_t::offsets
size_t * offsets
The list of offsets.
Definition pm_newline_list.h:39
pm_newline_list_t::size
size_t size
The number of offsets in the list.
Definition pm_newline_list.h:33
pm_options_scope::locals
pm_string_t * locals
The names of the locals in the scope.
Definition options.h:41
pm_options::freeze
bool freeze
Whether or not the parser should freeze the nodes that it creates.
Definition options.h:193
pm_options::scopes
pm_options_scope_t * scopes
The scopes surrounding the code that is being parsed.
Definition options.h:143
pm_options::line
int32_t line
The line within the file that the parse starts on.
Definition options.h:124
pm_options::filepath
pm_string_t filepath
The name of the file that is currently being parsed.
Definition options.h:118
pm_parser::lex_state
pm_lex_state_t lex_state
The current state of the lexer.
Definition parser.h:649
pm_parser::encoding
const pm_encoding_t * encoding
The encoding functions for the current file is attached to the parser as it's parsing so that it can ...
Definition parser.h:755
pm_parser::magic_comment_list
pm_list_t magic_comment_list
The list of magic comments that have been found while parsing.
Definition parser.h:721
pm_parser::lex_callback
pm_lex_callback_t * lex_callback
This is an optional callback that can be attached to the parser that will be called whenever a new to...
Definition parser.h:774
pm_parser::data_loc
pm_location_t data_loc
An optional location that represents the location of the END marker and the rest of the content of th...
Definition parser.h:728
pm_parser::start
const uint8_t * start
The pointer to the start of the source.
Definition parser.h:691
pm_parser::error_list
pm_list_t error_list
The list of errors that have been found while parsing.
Definition parser.h:734
pm_parser::warning_list
pm_list_t warning_list
The list of warnings that have been found while parsing.
Definition parser.h:731
pm_parser::start_line
int32_t start_line
The line number at the start of the parse.
Definition parser.h:809
pm_parser::comment_list
pm_list_t comment_list
The list of comments that have been found while parsing.
Definition parser.h:718
pm_parser::newline_list
pm_newline_list_t newline_list
This is the list of newline offsets in the source file.
Definition parser.h:789
pm_string_t
A generic string type that can have various ownership semantics.
Definition pm_string.h:33
pm_token_t
This struct represents a token in the Ruby source.
Definition ast.h:529
ID
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
VALUE
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
Check_Type
static void Check_Type(VALUE v, enum ruby_value_type t)
Identical to RB_TYPE_P(), except it raises exceptions on predication failure.
Definition value_type.h:433
RB_TYPE_P
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