Ruby 3.5.0dev (2025-11-03 revision 4a3d8346a6d0e068508631541f6bc43e8b154ea1)
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
10VALUE rb_cPrism;
11VALUE rb_cPrismNode;
12VALUE rb_cPrismSource;
13VALUE rb_cPrismToken;
14VALUE rb_cPrismLocation;
15
16VALUE rb_cPrismComment;
17VALUE rb_cPrismInlineComment;
18VALUE rb_cPrismEmbDocComment;
19VALUE rb_cPrismMagicComment;
20VALUE rb_cPrismParseError;
21VALUE rb_cPrismParseWarning;
22VALUE rb_cPrismResult;
23VALUE rb_cPrismParseResult;
24VALUE rb_cPrismLexResult;
25VALUE rb_cPrismParseLexResult;
26VALUE rb_cPrismStringQuery;
27VALUE rb_cPrismScope;
28VALUE rb_cPrismCurrentVersionError;
29
30VALUE rb_cPrismDebugEncoding;
31
32ID rb_id_option_command_line;
33ID rb_id_option_encoding;
34ID rb_id_option_filepath;
35ID rb_id_option_freeze;
36ID rb_id_option_frozen_string_literal;
37ID rb_id_option_line;
38ID rb_id_option_main_script;
39ID rb_id_option_partial_script;
40ID rb_id_option_scopes;
41ID rb_id_option_version;
42ID rb_id_source_for;
43ID rb_id_forwarding_positionals;
44ID rb_id_forwarding_keywords;
45ID rb_id_forwarding_block;
46ID rb_id_forwarding_all;
47
48/******************************************************************************/
49/* IO of Ruby code */
50/******************************************************************************/
51
56static const char *
57check_string(VALUE value) {
58 // Check if the value is a string. If it's not, then raise a type error.
59 if (!RB_TYPE_P(value, T_STRING)) {
60 rb_raise(rb_eTypeError, "wrong argument type %" PRIsVALUE " (expected String)", rb_obj_class(value));
61 }
62
63 // Otherwise, return the value as a C string.
64 return RSTRING_PTR(value);
65}
66
70static void
71input_load_string(pm_string_t *input, VALUE string) {
72 // Check if the string is a string. If it's not, then raise a type error.
73 if (!RB_TYPE_P(string, T_STRING)) {
74 rb_raise(rb_eTypeError, "wrong argument type %" PRIsVALUE " (expected String)", rb_obj_class(string));
75 }
76
77 pm_string_constant_init(input, RSTRING_PTR(string), RSTRING_LEN(string));
78}
79
80/******************************************************************************/
81/* Building C options from Ruby options */
82/******************************************************************************/
83
87static void
88build_options_scopes(pm_options_t *options, VALUE scopes) {
89 // Check if the value is an array. If it's not, then raise a type error.
90 if (!RB_TYPE_P(scopes, T_ARRAY)) {
91 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(scopes));
92 }
93
94 // Initialize the scopes array.
95 size_t scopes_count = RARRAY_LEN(scopes);
96 if (!pm_options_scopes_init(options, scopes_count)) {
97 rb_raise(rb_eNoMemError, "failed to allocate memory");
98 }
99
100 // Iterate over the scopes and add them to the options.
101 for (size_t scope_index = 0; scope_index < scopes_count; scope_index++) {
102 VALUE scope = rb_ary_entry(scopes, scope_index);
103
104 // The scope can be either an array or it can be a Prism::Scope object.
105 // Parse out the correct values here from either.
106 VALUE locals;
107 uint8_t forwarding = PM_OPTIONS_SCOPE_FORWARDING_NONE;
108
109 if (RB_TYPE_P(scope, T_ARRAY)) {
110 locals = scope;
111 } else if (rb_obj_is_kind_of(scope, rb_cPrismScope)) {
112 locals = rb_ivar_get(scope, rb_intern("@locals"));
113 if (!RB_TYPE_P(locals, T_ARRAY)) {
114 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(locals));
115 }
116
117 VALUE names = rb_ivar_get(scope, rb_intern("@forwarding"));
118 if (!RB_TYPE_P(names, T_ARRAY)) {
119 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(names));
120 }
121
122 size_t names_count = RARRAY_LEN(names);
123 for (size_t name_index = 0; name_index < names_count; name_index++) {
124 VALUE name = rb_ary_entry(names, name_index);
125
126 // Check that the name is a symbol. If it's not, then raise
127 // a type error.
128 if (!RB_TYPE_P(name, T_SYMBOL)) {
129 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Symbol)", rb_obj_class(name));
130 }
131
132 ID id = SYM2ID(name);
133 if (id == rb_id_forwarding_positionals) {
135 } else if (id == rb_id_forwarding_keywords) {
137 } else if (id == rb_id_forwarding_block) {
139 } else if (id == rb_id_forwarding_all) {
141 } else {
142 rb_raise(rb_eArgError, "invalid forwarding value: %" PRIsVALUE, name);
143 }
144 }
145 } else {
146 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array or Prism::Scope)", rb_obj_class(scope));
147 }
148
149 // Initialize the scope array.
150 size_t locals_count = RARRAY_LEN(locals);
151 pm_options_scope_t *options_scope = &options->scopes[scope_index];
152 if (!pm_options_scope_init(options_scope, locals_count)) {
153 rb_raise(rb_eNoMemError, "failed to allocate memory");
154 }
155
156 // Iterate over the locals and add them to the scope.
157 for (size_t local_index = 0; local_index < locals_count; local_index++) {
158 VALUE local = rb_ary_entry(locals, local_index);
159
160 // Check that the local is a symbol. If it's not, then raise a
161 // type error.
162 if (!RB_TYPE_P(local, T_SYMBOL)) {
163 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Symbol)", rb_obj_class(local));
164 }
165
166 // Add the local to the scope.
167 pm_string_t *scope_local = &options_scope->locals[local_index];
168 const char *name = rb_id2name(SYM2ID(local));
169 pm_string_constant_init(scope_local, name, strlen(name));
170 }
171
172 // Now set the forwarding options.
173 pm_options_scope_forwarding_set(options_scope, forwarding);
174 }
175}
176
180static int
181build_options_i(VALUE key, VALUE value, VALUE argument) {
182 pm_options_t *options = (pm_options_t *) argument;
183 ID key_id = SYM2ID(key);
184
185 if (key_id == rb_id_option_filepath) {
186 if (!NIL_P(value)) pm_options_filepath_set(options, check_string(value));
187 } else if (key_id == rb_id_option_encoding) {
188 if (!NIL_P(value)) {
189 if (value == Qfalse) {
190 pm_options_encoding_locked_set(options, true);
191 } else {
192 pm_options_encoding_set(options, rb_enc_name(rb_to_encoding(value)));
193 }
194 }
195 } else if (key_id == rb_id_option_line) {
196 if (!NIL_P(value)) pm_options_line_set(options, NUM2INT(value));
197 } else if (key_id == rb_id_option_frozen_string_literal) {
198 if (!NIL_P(value)) pm_options_frozen_string_literal_set(options, RTEST(value));
199 } else if (key_id == rb_id_option_version) {
200 if (!NIL_P(value)) {
201 const char *version = check_string(value);
202
203 if (RSTRING_LEN(value) == 7 && strncmp(version, "current", 7) == 0) {
204 const char *current_version = RSTRING_PTR(rb_const_get(rb_cObject, rb_intern("RUBY_VERSION")));
205 if (!pm_options_version_set(options, current_version, 3)) {
206 rb_exc_raise(rb_exc_new_cstr(rb_cPrismCurrentVersionError, current_version));
207 }
208 } else if (!pm_options_version_set(options, version, RSTRING_LEN(value))) {
209 rb_raise(rb_eArgError, "invalid version: %" PRIsVALUE, value);
210 }
211 }
212 } else if (key_id == rb_id_option_scopes) {
213 if (!NIL_P(value)) build_options_scopes(options, value);
214 } else if (key_id == rb_id_option_command_line) {
215 if (!NIL_P(value)) {
216 const char *string = check_string(value);
217 uint8_t command_line = 0;
218
219 for (size_t index = 0; index < strlen(string); index++) {
220 switch (string[index]) {
221 case 'a': command_line |= PM_OPTIONS_COMMAND_LINE_A; break;
222 case 'e': command_line |= PM_OPTIONS_COMMAND_LINE_E; break;
223 case 'l': command_line |= PM_OPTIONS_COMMAND_LINE_L; break;
224 case 'n': command_line |= PM_OPTIONS_COMMAND_LINE_N; break;
225 case 'p': command_line |= PM_OPTIONS_COMMAND_LINE_P; break;
226 case 'x': command_line |= PM_OPTIONS_COMMAND_LINE_X; break;
227 default: rb_raise(rb_eArgError, "invalid command line flag: '%c'", string[index]); break;
228 }
229 }
230
231 pm_options_command_line_set(options, command_line);
232 }
233 } else if (key_id == rb_id_option_main_script) {
234 if (!NIL_P(value)) pm_options_main_script_set(options, RTEST(value));
235 } else if (key_id == rb_id_option_partial_script) {
236 if (!NIL_P(value)) pm_options_partial_script_set(options, RTEST(value));
237 } else if (key_id == rb_id_option_freeze) {
238 if (!NIL_P(value)) pm_options_freeze_set(options, RTEST(value));
239 } else {
240 rb_raise(rb_eArgError, "unknown keyword: %" PRIsVALUE, key);
241 }
242
243 return ST_CONTINUE;
244}
245
252 pm_options_t *options;
253 VALUE keywords;
254};
255
260static VALUE
261build_options(VALUE argument) {
262 struct build_options_data *data = (struct build_options_data *) argument;
263 rb_hash_foreach(data->keywords, build_options_i, (VALUE) data->options);
264 return Qnil;
265}
266
270static void
271extract_options(pm_options_t *options, VALUE filepath, VALUE keywords) {
272 options->line = 1; // default
273
274 if (!NIL_P(keywords)) {
275 struct build_options_data data = { .options = options, .keywords = keywords };
276 struct build_options_data *argument = &data;
277
278 int state = 0;
279 rb_protect(build_options, (VALUE) argument, &state);
280
281 if (state != 0) {
282 pm_options_free(options);
283 rb_jump_tag(state);
284 }
285 }
286
287 if (!NIL_P(filepath)) {
288 if (!RB_TYPE_P(filepath, T_STRING)) {
289 pm_options_free(options);
290 rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected String)", rb_obj_class(filepath));
291 }
292
293 pm_options_filepath_set(options, RSTRING_PTR(filepath));
294 }
295}
296
300static void
301string_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options) {
302 VALUE string;
303 VALUE keywords;
304 rb_scan_args(argc, argv, "1:", &string, &keywords);
305
306 extract_options(options, Qnil, keywords);
307 input_load_string(input, string);
308}
309
313static void
314file_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options, VALUE *encoded_filepath) {
315 VALUE filepath;
316 VALUE keywords;
317 rb_scan_args(argc, argv, "1:", &filepath, &keywords);
318
319 Check_Type(filepath, T_STRING);
320 *encoded_filepath = rb_str_encode_ospath(filepath);
321 extract_options(options, *encoded_filepath, keywords);
322
323 const char *source = (const char *) pm_string_source(&options->filepath);
325
326 switch (result = pm_string_file_init(input, source)) {
328 break;
330 pm_options_free(options);
331
332#ifdef _WIN32
333 int e = rb_w32_map_errno(GetLastError());
334#else
335 int e = errno;
336#endif
337
338 rb_syserr_fail(e, source);
339 break;
340 }
342 pm_options_free(options);
343 rb_syserr_fail(EISDIR, source);
344 break;
345 default:
346 pm_options_free(options);
347 rb_raise(rb_eRuntimeError, "Unknown error (%d) initializing file: %s", result, source);
348 break;
349 }
350}
351
352#ifndef PRISM_EXCLUDE_SERIALIZATION
353
354/******************************************************************************/
355/* Serializing the AST */
356/******************************************************************************/
357
361static VALUE
362dump_input(pm_string_t *input, const pm_options_t *options) {
363 pm_buffer_t buffer;
364 if (!pm_buffer_init(&buffer)) {
365 rb_raise(rb_eNoMemError, "failed to allocate memory");
366 }
367
368 pm_parser_t parser;
369 pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
370
371 pm_node_t *node = pm_parse(&parser);
372 pm_serialize(&parser, node, &buffer);
373
374 VALUE result = rb_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer));
375 pm_node_destroy(&parser, node);
376 pm_buffer_free(&buffer);
377 pm_parser_free(&parser);
378
379 return result;
380}
381
389static VALUE
390dump(int argc, VALUE *argv, VALUE self) {
391 pm_string_t input;
392 pm_options_t options = { 0 };
393 string_options(argc, argv, &input, &options);
394
395#ifdef PRISM_BUILD_DEBUG
396 size_t length = pm_string_length(&input);
397 char* dup = xmalloc(length);
398 memcpy(dup, pm_string_source(&input), length);
399 pm_string_constant_init(&input, dup, length);
400#endif
401
402 VALUE value = dump_input(&input, &options);
403 if (options.freeze) rb_obj_freeze(value);
404
405#ifdef PRISM_BUILD_DEBUG
406 xfree(dup);
407#endif
408
409 pm_string_free(&input);
410 pm_options_free(&options);
411
412 return value;
413}
414
422static VALUE
423dump_file(int argc, VALUE *argv, VALUE self) {
424 pm_string_t input;
425 pm_options_t options = { 0 };
426
427 VALUE encoded_filepath;
428 file_options(argc, argv, &input, &options, &encoded_filepath);
429
430 VALUE value = dump_input(&input, &options);
431 pm_string_free(&input);
432 pm_options_free(&options);
433
434 return value;
435}
436
437#endif
438
439/******************************************************************************/
440/* Extracting values for the parse result */
441/******************************************************************************/
442
447static inline VALUE
448rb_class_new_instance_freeze(int argc, const VALUE *argv, VALUE klass, bool freeze) {
449 VALUE value = rb_class_new_instance(argc, argv, klass);
450 if (freeze) rb_obj_freeze(value);
451 return value;
452}
453
457static inline VALUE
458parser_location(const pm_parser_t *parser, VALUE source, bool freeze, const uint8_t *start, size_t length) {
459 VALUE argv[] = { source, LONG2FIX(start - parser->start), LONG2FIX(length) };
460 return rb_class_new_instance_freeze(3, argv, rb_cPrismLocation, freeze);
461}
462
466#define PARSER_LOCATION_LOC(parser, source, freeze, loc) \
467 parser_location(parser, source, freeze, loc.start, (size_t) (loc.end - loc.start))
468
472static inline VALUE
473parser_comment(const pm_parser_t *parser, VALUE source, bool freeze, const pm_comment_t *comment) {
474 VALUE argv[] = { PARSER_LOCATION_LOC(parser, source, freeze, comment->location) };
475 VALUE type = (comment->type == PM_COMMENT_EMBDOC) ? rb_cPrismEmbDocComment : rb_cPrismInlineComment;
476 return rb_class_new_instance_freeze(1, argv, type, freeze);
477}
478
482static VALUE
483parser_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
484 VALUE comments = rb_ary_new_capa(parser->comment_list.size);
485
486 for (
487 const pm_comment_t *comment = (const pm_comment_t *) parser->comment_list.head;
488 comment != NULL;
489 comment = (const pm_comment_t *) comment->node.next
490 ) {
491 VALUE value = parser_comment(parser, source, freeze, comment);
492 rb_ary_push(comments, value);
493 }
494
495 if (freeze) rb_obj_freeze(comments);
496 return comments;
497}
498
502static inline VALUE
503parser_magic_comment(const pm_parser_t *parser, VALUE source, bool freeze, const pm_magic_comment_t *magic_comment) {
504 VALUE key_loc = parser_location(parser, source, freeze, magic_comment->key_start, magic_comment->key_length);
505 VALUE value_loc = parser_location(parser, source, freeze, magic_comment->value_start, magic_comment->value_length);
506 VALUE argv[] = { key_loc, value_loc };
507 return rb_class_new_instance_freeze(2, argv, rb_cPrismMagicComment, freeze);
508}
509
513static VALUE
514parser_magic_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
515 VALUE magic_comments = rb_ary_new_capa(parser->magic_comment_list.size);
516
517 for (
519 magic_comment != NULL;
520 magic_comment = (const pm_magic_comment_t *) magic_comment->node.next
521 ) {
522 VALUE value = parser_magic_comment(parser, source, freeze, magic_comment);
523 rb_ary_push(magic_comments, value);
524 }
525
526 if (freeze) rb_obj_freeze(magic_comments);
527 return magic_comments;
528}
529
534static VALUE
535parser_data_loc(const pm_parser_t *parser, VALUE source, bool freeze) {
536 if (parser->data_loc.end == NULL) {
537 return Qnil;
538 } else {
539 return PARSER_LOCATION_LOC(parser, source, freeze, parser->data_loc);
540 }
541}
542
546static VALUE
547parser_errors(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bool freeze) {
548 VALUE errors = rb_ary_new_capa(parser->error_list.size);
549
550 for (
551 const pm_diagnostic_t *error = (const pm_diagnostic_t *) parser->error_list.head;
552 error != NULL;
553 error = (const pm_diagnostic_t *) error->node.next
554 ) {
555 VALUE type = ID2SYM(rb_intern(pm_diagnostic_id_human(error->diag_id)));
556 VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(error->message, encoding));
557 VALUE location = PARSER_LOCATION_LOC(parser, source, freeze, error->location);
558
559 VALUE level = Qnil;
560 switch (error->level) {
562 level = ID2SYM(rb_intern("syntax"));
563 break;
565 level = ID2SYM(rb_intern("argument"));
566 break;
568 level = ID2SYM(rb_intern("load"));
569 break;
570 default:
571 rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, error->level);
572 }
573
574 VALUE argv[] = { type, message, location, level };
575 VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseError, freeze);
576 rb_ary_push(errors, value);
577 }
578
579 if (freeze) rb_obj_freeze(errors);
580 return errors;
581}
582
586static VALUE
587parser_warnings(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bool freeze) {
588 VALUE warnings = rb_ary_new_capa(parser->warning_list.size);
589
590 for (
591 const pm_diagnostic_t *warning = (const pm_diagnostic_t *) parser->warning_list.head;
592 warning != NULL;
593 warning = (const pm_diagnostic_t *) warning->node.next
594 ) {
595 VALUE type = ID2SYM(rb_intern(pm_diagnostic_id_human(warning->diag_id)));
596 VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(warning->message, encoding));
597 VALUE location = PARSER_LOCATION_LOC(parser, source, freeze, warning->location);
598
599 VALUE level = Qnil;
600 switch (warning->level) {
602 level = ID2SYM(rb_intern("default"));
603 break;
605 level = ID2SYM(rb_intern("verbose"));
606 break;
607 default:
608 rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, warning->level);
609 }
610
611 VALUE argv[] = { type, message, location, level };
612 VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseWarning, freeze);
613 rb_ary_push(warnings, value);
614 }
615
616 if (freeze) rb_obj_freeze(warnings);
617 return warnings;
618}
619
623static VALUE
624parse_result_create(VALUE class, const pm_parser_t *parser, VALUE value, rb_encoding *encoding, VALUE source, bool freeze) {
625 VALUE result_argv[] = {
626 value,
627 parser_comments(parser, source, freeze),
628 parser_magic_comments(parser, source, freeze),
629 parser_data_loc(parser, source, freeze),
630 parser_errors(parser, encoding, source, freeze),
631 parser_warnings(parser, encoding, source, freeze),
632 source
633 };
634
635 return rb_class_new_instance_freeze(7, result_argv, class, freeze);
636}
637
638/******************************************************************************/
639/* Lexing Ruby code */
640/******************************************************************************/
641
647typedef struct {
648 VALUE source;
649 VALUE tokens;
650 rb_encoding *encoding;
651 bool freeze;
653
659static void
660parse_lex_token(void *data, pm_parser_t *parser, pm_token_t *token) {
661 parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) parser->lex_callback->data;
662
663 VALUE value = pm_token_new(parser, token, parse_lex_data->encoding, parse_lex_data->source, parse_lex_data->freeze);
664 VALUE yields = rb_assoc_new(value, INT2FIX(parser->lex_state));
665
666 if (parse_lex_data->freeze) {
667 rb_obj_freeze(value);
668 rb_obj_freeze(yields);
669 }
670
671 rb_ary_push(parse_lex_data->tokens, yields);
672}
673
679static void
680parse_lex_encoding_changed_callback(pm_parser_t *parser) {
681 parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) parser->lex_callback->data;
682 parse_lex_data->encoding = rb_enc_find(parser->encoding->name);
683
684 // Since the encoding changed, we need to go back and change the encoding of
685 // the tokens that were already lexed. This is only going to end up being
686 // one or two tokens, since the encoding can only change at the top of the
687 // file.
688 VALUE tokens = parse_lex_data->tokens;
689 VALUE next_tokens = rb_ary_new();
690
691 for (long index = 0; index < RARRAY_LEN(tokens); index++) {
692 VALUE yields = rb_ary_entry(tokens, index);
693 VALUE token = rb_ary_entry(yields, 0);
694
695 VALUE value = rb_ivar_get(token, rb_intern("@value"));
696 VALUE next_value = rb_str_dup(value);
697
698 rb_enc_associate(next_value, parse_lex_data->encoding);
699 if (parse_lex_data->freeze) rb_obj_freeze(next_value);
700
701 VALUE next_token_argv[] = {
702 parse_lex_data->source,
703 rb_ivar_get(token, rb_intern("@type")),
704 next_value,
705 rb_ivar_get(token, rb_intern("@location"))
706 };
707
708 VALUE next_token = rb_class_new_instance(4, next_token_argv, rb_cPrismToken);
709 VALUE next_yields = rb_assoc_new(next_token, rb_ary_entry(yields, 1));
710
711 if (parse_lex_data->freeze) {
712 rb_obj_freeze(next_token);
713 rb_obj_freeze(next_yields);
714 }
715
716 rb_ary_push(next_tokens, next_yields);
717 }
718
719 rb_ary_replace(parse_lex_data->tokens, next_tokens);
720}
721
726static VALUE
727parse_lex_input(pm_string_t *input, const pm_options_t *options, bool return_nodes) {
728 pm_parser_t parser;
729 pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
730 pm_parser_register_encoding_changed_callback(&parser, parse_lex_encoding_changed_callback);
731
732 VALUE source_string = rb_str_new((const char *) pm_string_source(input), pm_string_length(input));
733 VALUE offsets = rb_ary_new_capa(parser.newline_list.size);
734 VALUE source = rb_funcall(rb_cPrismSource, rb_id_source_for, 3, source_string, LONG2NUM(parser.start_line), offsets);
735
736 parse_lex_data_t parse_lex_data = {
737 .source = source,
738 .tokens = rb_ary_new(),
739 .encoding = rb_utf8_encoding(),
740 .freeze = options->freeze,
741 };
742
743 parse_lex_data_t *data = &parse_lex_data;
744 pm_lex_callback_t lex_callback = (pm_lex_callback_t) {
745 .data = (void *) data,
746 .callback = parse_lex_token,
747 };
748
749 parser.lex_callback = &lex_callback;
750 pm_node_t *node = pm_parse(&parser);
751
752 // Here we need to update the Source object to have the correct
753 // encoding for the source string and the correct newline offsets.
754 // We do it here because we've already created the Source object and given
755 // it over to all of the tokens, and both of these are only set after pm_parse().
756 rb_encoding *encoding = rb_enc_find(parser.encoding->name);
757 rb_enc_associate(source_string, encoding);
758
759 for (size_t index = 0; index < parser.newline_list.size; index++) {
760 rb_ary_push(offsets, ULONG2NUM(parser.newline_list.offsets[index]));
761 }
762
763 if (options->freeze) {
764 rb_obj_freeze(source_string);
765 rb_obj_freeze(offsets);
766 rb_obj_freeze(source);
767 rb_obj_freeze(parse_lex_data.tokens);
768 }
769
770 VALUE result;
771 if (return_nodes) {
772 VALUE value = rb_ary_new_capa(2);
773 rb_ary_push(value, pm_ast_new(&parser, node, parse_lex_data.encoding, source, options->freeze));
774 rb_ary_push(value, parse_lex_data.tokens);
775 if (options->freeze) rb_obj_freeze(value);
776 result = parse_result_create(rb_cPrismParseLexResult, &parser, value, parse_lex_data.encoding, source, options->freeze);
777 } else {
778 result = parse_result_create(rb_cPrismLexResult, &parser, parse_lex_data.tokens, parse_lex_data.encoding, source, options->freeze);
779 }
780
781 pm_node_destroy(&parser, node);
782 pm_parser_free(&parser);
783
784 return result;
785}
786
794static VALUE
795lex(int argc, VALUE *argv, VALUE self) {
796 pm_string_t input;
797 pm_options_t options = { 0 };
798 string_options(argc, argv, &input, &options);
799
800 VALUE result = parse_lex_input(&input, &options, false);
801 pm_string_free(&input);
802 pm_options_free(&options);
803
804 return result;
805}
806
814static VALUE
815lex_file(int argc, VALUE *argv, VALUE self) {
816 pm_string_t input;
817 pm_options_t options = { 0 };
818
819 VALUE encoded_filepath;
820 file_options(argc, argv, &input, &options, &encoded_filepath);
821
822 VALUE value = parse_lex_input(&input, &options, false);
823 pm_string_free(&input);
824 pm_options_free(&options);
825
826 return value;
827}
828
829/******************************************************************************/
830/* Parsing Ruby code */
831/******************************************************************************/
832
836static VALUE
837parse_input(pm_string_t *input, const pm_options_t *options) {
838 pm_parser_t parser;
839 pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
840
841 pm_node_t *node = pm_parse(&parser);
842 rb_encoding *encoding = rb_enc_find(parser.encoding->name);
843
844 VALUE source = pm_source_new(&parser, encoding, options->freeze);
845 VALUE value = pm_ast_new(&parser, node, encoding, source, options->freeze);
846 VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source, options->freeze);
847
848 if (options->freeze) {
849 rb_obj_freeze(source);
850 }
851
852 pm_node_destroy(&parser, node);
853 pm_parser_free(&parser);
854
855 return result;
856}
857
900static VALUE
901parse(int argc, VALUE *argv, VALUE self) {
902 pm_string_t input;
903 pm_options_t options = { 0 };
904 string_options(argc, argv, &input, &options);
905
906#ifdef PRISM_BUILD_DEBUG
907 size_t length = pm_string_length(&input);
908 char* dup = xmalloc(length);
909 memcpy(dup, pm_string_source(&input), length);
910 pm_string_constant_init(&input, dup, length);
911#endif
912
913 VALUE value = parse_input(&input, &options);
914
915#ifdef PRISM_BUILD_DEBUG
916 xfree(dup);
917#endif
918
919 pm_string_free(&input);
920 pm_options_free(&options);
921 return value;
922}
923
931static VALUE
932parse_file(int argc, VALUE *argv, VALUE self) {
933 pm_string_t input;
934 pm_options_t options = { 0 };
935
936 VALUE encoded_filepath;
937 file_options(argc, argv, &input, &options, &encoded_filepath);
938
939 VALUE value = parse_input(&input, &options);
940 pm_string_free(&input);
941 pm_options_free(&options);
942
943 return value;
944}
945
949static void
950profile_input(pm_string_t *input, const pm_options_t *options) {
951 pm_parser_t parser;
952 pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
953
954 pm_node_t *node = pm_parse(&parser);
955 pm_node_destroy(&parser, node);
956 pm_parser_free(&parser);
957}
958
967static VALUE
968profile(int argc, VALUE *argv, VALUE self) {
969 pm_string_t input;
970 pm_options_t options = { 0 };
971
972 string_options(argc, argv, &input, &options);
973 profile_input(&input, &options);
974 pm_string_free(&input);
975 pm_options_free(&options);
976
977 return Qnil;
978}
979
988static VALUE
989profile_file(int argc, VALUE *argv, VALUE self) {
990 pm_string_t input;
991 pm_options_t options = { 0 };
992
993 VALUE encoded_filepath;
994 file_options(argc, argv, &input, &options, &encoded_filepath);
995
996 profile_input(&input, &options);
997 pm_string_free(&input);
998 pm_options_free(&options);
999
1000 return Qnil;
1001}
1002
1003static int
1004parse_stream_eof(void *stream) {
1005 if (rb_funcall((VALUE) stream, rb_intern("eof?"), 0)) {
1006 return 1;
1007 }
1008 return 0;
1009}
1010
1014static char *
1015parse_stream_fgets(char *string, int size, void *stream) {
1016 RUBY_ASSERT(size > 0);
1017
1018 VALUE line = rb_funcall((VALUE) stream, rb_intern("gets"), 1, INT2FIX(size - 1));
1019 if (NIL_P(line)) {
1020 return NULL;
1021 }
1022
1023 const char *cstr = RSTRING_PTR(line);
1024 long length = RSTRING_LEN(line);
1025
1026 memcpy(string, cstr, length);
1027 string[length] = '\0';
1028
1029 return string;
1030}
1031
1039static VALUE
1040parse_stream(int argc, VALUE *argv, VALUE self) {
1041 VALUE stream;
1042 VALUE keywords;
1043 rb_scan_args(argc, argv, "1:", &stream, &keywords);
1044
1045 pm_options_t options = { 0 };
1046 extract_options(&options, Qnil, keywords);
1047
1048 pm_parser_t parser;
1049 pm_buffer_t buffer;
1050
1051 pm_node_t *node = pm_parse_stream(&parser, &buffer, (void *) stream, parse_stream_fgets, parse_stream_eof, &options);
1052 rb_encoding *encoding = rb_enc_find(parser.encoding->name);
1053
1054 VALUE source = pm_source_new(&parser, encoding, options.freeze);
1055 VALUE value = pm_ast_new(&parser, node, encoding, source, options.freeze);
1056 VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source, options.freeze);
1057
1058 pm_node_destroy(&parser, node);
1059 pm_buffer_free(&buffer);
1060 pm_parser_free(&parser);
1061
1062 return result;
1063}
1064
1068static VALUE
1069parse_input_comments(pm_string_t *input, const pm_options_t *options) {
1070 pm_parser_t parser;
1071 pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
1072
1073 pm_node_t *node = pm_parse(&parser);
1074 rb_encoding *encoding = rb_enc_find(parser.encoding->name);
1075
1076 VALUE source = pm_source_new(&parser, encoding, options->freeze);
1077 VALUE comments = parser_comments(&parser, source, options->freeze);
1078
1079 pm_node_destroy(&parser, node);
1080 pm_parser_free(&parser);
1081
1082 return comments;
1083}
1084
1092static VALUE
1093parse_comments(int argc, VALUE *argv, VALUE self) {
1094 pm_string_t input;
1095 pm_options_t options = { 0 };
1096 string_options(argc, argv, &input, &options);
1097
1098 VALUE result = parse_input_comments(&input, &options);
1099 pm_string_free(&input);
1100 pm_options_free(&options);
1101
1102 return result;
1103}
1104
1112static VALUE
1113parse_file_comments(int argc, VALUE *argv, VALUE self) {
1114 pm_string_t input;
1115 pm_options_t options = { 0 };
1116
1117 VALUE encoded_filepath;
1118 file_options(argc, argv, &input, &options, &encoded_filepath);
1119
1120 VALUE value = parse_input_comments(&input, &options);
1121 pm_string_free(&input);
1122 pm_options_free(&options);
1123
1124 return value;
1125}
1126
1141static VALUE
1142parse_lex(int argc, VALUE *argv, VALUE self) {
1143 pm_string_t input;
1144 pm_options_t options = { 0 };
1145 string_options(argc, argv, &input, &options);
1146
1147 VALUE value = parse_lex_input(&input, &options, true);
1148 pm_string_free(&input);
1149 pm_options_free(&options);
1150
1151 return value;
1152}
1153
1168static VALUE
1169parse_lex_file(int argc, VALUE *argv, VALUE self) {
1170 pm_string_t input;
1171 pm_options_t options = { 0 };
1172
1173 VALUE encoded_filepath;
1174 file_options(argc, argv, &input, &options, &encoded_filepath);
1175
1176 VALUE value = parse_lex_input(&input, &options, true);
1177 pm_string_free(&input);
1178 pm_options_free(&options);
1179
1180 return value;
1181}
1182
1186static VALUE
1187parse_input_success_p(pm_string_t *input, const pm_options_t *options) {
1188 pm_parser_t parser;
1189 pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
1190
1191 pm_node_t *node = pm_parse(&parser);
1192 pm_node_destroy(&parser, node);
1193
1194 VALUE result = parser.error_list.size == 0 ? Qtrue : Qfalse;
1195 pm_parser_free(&parser);
1196
1197 return result;
1198}
1199
1207static VALUE
1208parse_success_p(int argc, VALUE *argv, VALUE self) {
1209 pm_string_t input;
1210 pm_options_t options = { 0 };
1211 string_options(argc, argv, &input, &options);
1212
1213 VALUE result = parse_input_success_p(&input, &options);
1214 pm_string_free(&input);
1215 pm_options_free(&options);
1216
1217 return result;
1218}
1219
1227static VALUE
1228parse_failure_p(int argc, VALUE *argv, VALUE self) {
1229 return RTEST(parse_success_p(argc, argv, self)) ? Qfalse : Qtrue;
1230}
1231
1239static VALUE
1240parse_file_success_p(int argc, VALUE *argv, VALUE self) {
1241 pm_string_t input;
1242 pm_options_t options = { 0 };
1243
1244 VALUE encoded_filepath;
1245 file_options(argc, argv, &input, &options, &encoded_filepath);
1246
1247 VALUE result = parse_input_success_p(&input, &options);
1248 pm_string_free(&input);
1249 pm_options_free(&options);
1250
1251 return result;
1252}
1253
1261static VALUE
1262parse_file_failure_p(int argc, VALUE *argv, VALUE self) {
1263 return RTEST(parse_file_success_p(argc, argv, self)) ? Qfalse : Qtrue;
1264}
1265
1266/******************************************************************************/
1267/* String query methods */
1268/******************************************************************************/
1269
1274static VALUE
1275string_query(pm_string_query_t result) {
1276 switch (result) {
1278 rb_raise(rb_eArgError, "Invalid or non ascii-compatible encoding");
1279 return Qfalse;
1281 return Qfalse;
1283 return Qtrue;
1284 }
1285 return Qfalse;
1286}
1287
1296static VALUE
1297string_query_local_p(VALUE self, VALUE string) {
1298 const uint8_t *source = (const uint8_t *) check_string(string);
1299 return string_query(pm_string_query_local(source, RSTRING_LEN(string), rb_enc_get(string)->name));
1300}
1301
1310static VALUE
1311string_query_constant_p(VALUE self, VALUE string) {
1312 const uint8_t *source = (const uint8_t *) check_string(string);
1313 return string_query(pm_string_query_constant(source, RSTRING_LEN(string), rb_enc_get(string)->name));
1314}
1315
1322static VALUE
1323string_query_method_name_p(VALUE self, VALUE string) {
1324 const uint8_t *source = (const uint8_t *) check_string(string);
1325 return string_query(pm_string_query_method_name(source, RSTRING_LEN(string), rb_enc_get(string)->name));
1326}
1327
1328/******************************************************************************/
1329/* Initialization of the extension */
1330/******************************************************************************/
1331
1335RUBY_FUNC_EXPORTED void
1336Init_prism(void) {
1337 // Make sure that the prism library version matches the expected version.
1338 // Otherwise something was compiled incorrectly.
1339 if (strcmp(pm_version(), EXPECTED_PRISM_VERSION) != 0) {
1340 rb_raise(
1342 "The prism library version (%s) does not match the expected version (%s)",
1343 pm_version(),
1344 EXPECTED_PRISM_VERSION
1345 );
1346 }
1347
1348#ifdef HAVE_RB_EXT_RACTOR_SAFE
1349 // Mark this extension as Ractor-safe.
1350 rb_ext_ractor_safe(true);
1351#endif
1352
1353 // Grab up references to all of the constants that we're going to need to
1354 // reference throughout this extension.
1355 rb_cPrism = rb_define_module("Prism");
1356 rb_cPrismNode = rb_define_class_under(rb_cPrism, "Node", rb_cObject);
1357 rb_cPrismSource = rb_define_class_under(rb_cPrism, "Source", rb_cObject);
1358 rb_cPrismToken = rb_define_class_under(rb_cPrism, "Token", rb_cObject);
1359 rb_cPrismLocation = rb_define_class_under(rb_cPrism, "Location", rb_cObject);
1360 rb_cPrismComment = rb_define_class_under(rb_cPrism, "Comment", rb_cObject);
1361 rb_cPrismInlineComment = rb_define_class_under(rb_cPrism, "InlineComment", rb_cPrismComment);
1362 rb_cPrismEmbDocComment = rb_define_class_under(rb_cPrism, "EmbDocComment", rb_cPrismComment);
1363 rb_cPrismMagicComment = rb_define_class_under(rb_cPrism, "MagicComment", rb_cObject);
1364 rb_cPrismParseError = rb_define_class_under(rb_cPrism, "ParseError", rb_cObject);
1365 rb_cPrismParseWarning = rb_define_class_under(rb_cPrism, "ParseWarning", rb_cObject);
1366 rb_cPrismResult = rb_define_class_under(rb_cPrism, "Result", rb_cObject);
1367 rb_cPrismParseResult = rb_define_class_under(rb_cPrism, "ParseResult", rb_cPrismResult);
1368 rb_cPrismLexResult = rb_define_class_under(rb_cPrism, "LexResult", rb_cPrismResult);
1369 rb_cPrismParseLexResult = rb_define_class_under(rb_cPrism, "ParseLexResult", rb_cPrismResult);
1370 rb_cPrismStringQuery = rb_define_class_under(rb_cPrism, "StringQuery", rb_cObject);
1371 rb_cPrismScope = rb_define_class_under(rb_cPrism, "Scope", rb_cObject);
1372
1373 rb_cPrismCurrentVersionError = rb_const_get(rb_cPrism, rb_intern("CurrentVersionError"));
1374
1375 // Intern all of the IDs eagerly that we support so that we don't have to do
1376 // it every time we parse.
1377 rb_id_option_command_line = rb_intern_const("command_line");
1378 rb_id_option_encoding = rb_intern_const("encoding");
1379 rb_id_option_filepath = rb_intern_const("filepath");
1380 rb_id_option_freeze = rb_intern_const("freeze");
1381 rb_id_option_frozen_string_literal = rb_intern_const("frozen_string_literal");
1382 rb_id_option_line = rb_intern_const("line");
1383 rb_id_option_main_script = rb_intern_const("main_script");
1384 rb_id_option_partial_script = rb_intern_const("partial_script");
1385 rb_id_option_scopes = rb_intern_const("scopes");
1386 rb_id_option_version = rb_intern_const("version");
1387 rb_id_source_for = rb_intern("for");
1388 rb_id_forwarding_positionals = rb_intern("*");
1389 rb_id_forwarding_keywords = rb_intern("**");
1390 rb_id_forwarding_block = rb_intern("&");
1391 rb_id_forwarding_all = rb_intern("...");
1392
1396 rb_define_const(rb_cPrism, "VERSION", rb_str_freeze(rb_str_new_cstr(EXPECTED_PRISM_VERSION)));
1397
1398 // First, the functions that have to do with lexing and parsing.
1399 rb_define_singleton_method(rb_cPrism, "lex", lex, -1);
1400 rb_define_singleton_method(rb_cPrism, "lex_file", lex_file, -1);
1401 rb_define_singleton_method(rb_cPrism, "parse", parse, -1);
1402 rb_define_singleton_method(rb_cPrism, "parse_file", parse_file, -1);
1403 rb_define_singleton_method(rb_cPrism, "profile", profile, -1);
1404 rb_define_singleton_method(rb_cPrism, "profile_file", profile_file, -1);
1405 rb_define_singleton_method(rb_cPrism, "parse_stream", parse_stream, -1);
1406 rb_define_singleton_method(rb_cPrism, "parse_comments", parse_comments, -1);
1407 rb_define_singleton_method(rb_cPrism, "parse_file_comments", parse_file_comments, -1);
1408 rb_define_singleton_method(rb_cPrism, "parse_lex", parse_lex, -1);
1409 rb_define_singleton_method(rb_cPrism, "parse_lex_file", parse_lex_file, -1);
1410 rb_define_singleton_method(rb_cPrism, "parse_success?", parse_success_p, -1);
1411 rb_define_singleton_method(rb_cPrism, "parse_failure?", parse_failure_p, -1);
1412 rb_define_singleton_method(rb_cPrism, "parse_file_success?", parse_file_success_p, -1);
1413 rb_define_singleton_method(rb_cPrism, "parse_file_failure?", parse_file_failure_p, -1);
1414
1415#ifndef PRISM_EXCLUDE_SERIALIZATION
1416 rb_define_singleton_method(rb_cPrism, "dump", dump, -1);
1417 rb_define_singleton_method(rb_cPrism, "dump_file", dump_file, -1);
1418#endif
1419
1420 rb_define_singleton_method(rb_cPrismStringQuery, "local?", string_query_local_p, 1);
1421 rb_define_singleton_method(rb_cPrismStringQuery, "constant?", string_query_constant_p, 1);
1422 rb_define_singleton_method(rb_cPrismStringQuery, "method_name?", string_query_method_name_p, 1);
1423
1424 // Next, initialize the other APIs.
1425 Init_prism_api_node();
1426 Init_prism_pack();
1427}
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
@ PM_WARNING_LEVEL_DEFAULT
For warnings which should be emitted if $VERBOSE != nil.
Definition diagnostic.h:410
@ PM_WARNING_LEVEL_VERBOSE
For warnings which should be emitted if $VERBOSE == true.
Definition diagnostic.h:413
@ PM_ERROR_LEVEL_ARGUMENT
For errors that should raise an argument error.
Definition diagnostic.h:399
@ PM_ERROR_LEVEL_LOAD
For errors that should raise a load error.
Definition diagnostic.h:402
@ PM_ERROR_LEVEL_SYNTAX
For errors that should raise a syntax error.
Definition diagnostic.h:396
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1634
VALUE rb_define_module(const char *name)
Defines a top-level module.
Definition class.c:1716
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:3252
#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
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:683
void rb_syserr_fail(int e, const char *mesg)
Raises appropriate exception that represents a C errno.
Definition error.c:3908
VALUE rb_eNoMemError
NoMemoryError exception.
Definition error.c:1441
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1430
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1428
VALUE rb_class_new_instance(int argc, const VALUE *argv, VALUE klass)
Allocates, then initialises an instance of the given class.
Definition object.c:2205
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:265
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:910
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1329
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:1149
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1117
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.
VALUE rb_assoc_new(VALUE car, VALUE cdr)
Identical to rb_ary_new_from_values(), except it expects exactly two parameters.
void rb_ext_ractor_safe(bool flag)
Asserts that the extension library that calls this function is aware of Ractor.
Definition load.c:1277
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1497
#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:1669
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition string.c:1990
VALUE rb_str_freeze(VALUE str)
This is the implementation of String#freeze.
Definition string.c:3272
#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:1513
VALUE rb_const_get(VALUE space, ID name)
Identical to rb_const_defined(), except it returns the actual defined value.
Definition variable.c:3412
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:1461
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:284
VALUE type(ANYARGS)
ANYARGS-ed function type.
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
PRISM_EXPORTED_FUNCTION void pm_options_encoding_set(pm_options_t *options, const char *encoding)
Set the encoding option on the given options struct.
Definition options.c:24
PRISM_EXPORTED_FUNCTION void pm_options_free(pm_options_t *options)
Free the internal memory associated with the options.
Definition options.c:208
PRISM_EXPORTED_FUNCTION void pm_options_partial_script_set(pm_options_t *options, bool partial_script)
Set the partial script option on the given options struct.
Definition options.c:138
PRISM_EXPORTED_FUNCTION void pm_options_main_script_set(pm_options_t *options, bool main_script)
Set the main script option on the given options struct.
Definition options.c:130
PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length)
Set the version option on the given options struct by parsing the given string.
Definition options.c:74
PRISM_EXPORTED_FUNCTION bool pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count)
Create a new options scope struct.
Definition options.c:181
PRISM_EXPORTED_FUNCTION void pm_options_line_set(pm_options_t *options, int32_t line)
Set the line option on the given options struct.
Definition options.c:40
PRISM_EXPORTED_FUNCTION void pm_options_freeze_set(pm_options_t *options, bool freeze)
Set the freeze option on the given options struct.
Definition options.c:146
PRISM_EXPORTED_FUNCTION void pm_options_encoding_locked_set(pm_options_t *options, bool encoding_locked)
Set the encoding_locked option on the given options struct.
Definition options.c:32
PRISM_EXPORTED_FUNCTION void pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal)
Set the frozen string literal option on the given options struct.
Definition options.c:48
PRISM_EXPORTED_FUNCTION void pm_options_command_line_set(pm_options_t *options, uint8_t command_line)
Sets the command line option on the given options struct.
Definition options.c:56
PRISM_EXPORTED_FUNCTION void pm_options_filepath_set(pm_options_t *options, const char *filepath)
Set the filepath option on the given options struct.
Definition options.c:16
PRISM_EXPORTED_FUNCTION bool pm_options_scopes_init(pm_options_t *options, size_t scopes_count)
Allocate and zero out the scopes array on the given options struct.
Definition options.c:162
PRISM_EXPORTED_FUNCTION void pm_options_scope_forwarding_set(pm_options_scope_t *scope, uint8_t forwarding)
Set the forwarding option on the given scope struct.
Definition options.c:200
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
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
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
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_NONE
The default value for parameters.
Definition options.h:48
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_ALL
When the scope is fowarding with the ... parameter.
Definition options.h:60
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS
When the scope is fowarding with the * parameter.
Definition options.h:51
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS
When the scope is fowarding with the ** parameter.
Definition options.h:54
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
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
static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_BLOCK
When the scope is fowarding with the & parameter.
Definition options.h:57
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
void pm_buffer_free(pm_buffer_t *buffer)
Free the memory associated with the buffer.
Definition pm_buffer.c:355
bool pm_buffer_init(pm_buffer_t *buffer)
Initialize a pm_buffer_t with its default values.
Definition pm_buffer.c:27
size_t pm_buffer_length(const pm_buffer_t *buffer)
Return the length of the buffer.
Definition pm_buffer.c:43
char * pm_buffer_value(const pm_buffer_t *buffer)
Return the value of the buffer.
Definition pm_buffer.c:35
PRISM_EXPORTED_FUNCTION size_t pm_string_length(const pm_string_t *string)
Returns the length associated with the string.
Definition pm_string.c:351
PRISM_EXPORTED_FUNCTION const uint8_t * pm_string_source(const pm_string_t *string)
Returns the start pointer associated with the string.
Definition pm_string.c:359
PRISM_EXPORTED_FUNCTION void pm_string_free(pm_string_t *string)
Free the associated memory of the given string.
Definition pm_string.c:367
PRISM_EXPORTED_FUNCTION pm_string_init_result_t pm_string_file_init(pm_string_t *string, const char *filepath)
Read the file indicated by the filepath parameter into source and load its contents and size into the...
Definition pm_string.c:210
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
Indicates that the string was successfully initialized.
Definition pm_string.h:107
@ 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
Indicates that the file that was attempted to be opened was a directory.
Definition pm_string.h:116
PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse(pm_parser_t *parser)
Parse the Ruby source associated with the given parser and return the tree.
Definition prism.c:22979
PRISM_EXPORTED_FUNCTION void pm_parser_register_encoding_changed_callback(pm_parser_t *parser, pm_encoding_changed_callback_t callback)
Register a callback that will be called whenever prism changes the encoding it is using to parse base...
Definition prism.c:22915
PRISM_EXPORTED_FUNCTION void pm_parser_free(pm_parser_t *parser)
Free any memory associated with the given parser.
Definition prism.c:22953
PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, const pm_options_t *options)
Parse a stream of Ruby source and return the tree.
Definition prism.c:23066
PRISM_EXPORTED_FUNCTION void pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options)
Initialize a parser with the given start and end pointers.
Definition prism.c:22654
pm_string_query_t
Represents the results of a slice query.
Definition prism.h:265
@ PM_STRING_QUERY_TRUE
Returned if the result of the slice query is true.
Definition prism.h:273
@ PM_STRING_QUERY_ERROR
Returned if the encoding given to a slice query was invalid.
Definition prism.h:267
@ PM_STRING_QUERY_FALSE
Returned if the result of the slice query is false.
Definition prism.h:270
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
#define RTEST
This is an old name of RB_TEST.
We need a struct here to pass through rb_protect and it has to be a single value.
Definition extension.c:251
This struct gets stored in the parser and passed in to the lex callback any time a new token is found...
Definition extension.c:647
A pm_buffer_t is a simple memory buffer that stores data in a contiguous block of memory.
Definition pm_buffer.h:22
This is a node in the linked list of comments that we've found while parsing.
Definition parser.h:458
pm_comment_type_t type
The type of comment that we've found.
Definition parser.h:466
pm_location_t location
The location of the comment in the source.
Definition parser.h:463
This struct represents a diagnostic generated during parsing.
Definition diagnostic.h:364
const char * name
The name of the encoding.
Definition encoding.h:56
When you are lexing through a file, the lexer needs all of the information that the parser additional...
Definition parser.h:506
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_t * head
A pointer to the head of the list.
Definition pm_list.h:60
size_t size
The size of the list.
Definition pm_list.h:57
const uint8_t * end
A pointer to the end location of the range in the source.
Definition ast.h:549
This is a node in the linked list of magic comments that we've found while parsing.
Definition parser.h:475
size_t * offsets
The list of offsets.
size_t size
The number of offsets in the list.
This is the base structure that represents a node in the syntax tree.
Definition ast.h:1068
A scope of locals surrounding the code that is being parsed.
Definition options.h:36
pm_string_t * locals
The names of the locals in the scope.
Definition options.h:41
The options that can be passed to the parser.
Definition options.h:104
bool freeze
Whether or not the parser should freeze the nodes that it creates.
Definition options.h:193
pm_options_scope_t * scopes
The scopes surrounding the code that is being parsed.
Definition options.h:143
int32_t line
The line within the file that the parse starts on.
Definition options.h:124
pm_string_t filepath
The name of the file that is currently being parsed.
Definition options.h:118
This struct represents the overall parser.
Definition parser.h:640
pm_lex_state_t lex_state
The current state of the lexer.
Definition parser.h:649
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_list_t magic_comment_list
The list of magic comments that have been found while parsing.
Definition parser.h:721
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_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
const uint8_t * start
The pointer to the start of the source.
Definition parser.h:691
pm_list_t error_list
The list of errors that have been found while parsing.
Definition parser.h:734
pm_list_t warning_list
The list of warnings that have been found while parsing.
Definition parser.h:731
int32_t start_line
The line number at the start of the parse.
Definition parser.h:809
pm_list_t comment_list
The list of comments that have been found while parsing.
Definition parser.h:718
pm_newline_list_t newline_list
This is the list of newline offsets in the source file.
Definition parser.h:789
A generic string type that can have various ownership semantics.
Definition pm_string.h:33
This struct represents a token in the Ruby source.
Definition ast.h:529
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 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
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