Ruby 3.5.0dev (2025-02-22 revision b17f984e4e903d3ece3013c1488279d1947dfc39)
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;
28
29VALUE rb_cPrismDebugEncoding;
30
31ID rb_id_option_command_line;
32ID rb_id_option_encoding;
33ID rb_id_option_filepath;
34ID rb_id_option_freeze;
35ID rb_id_option_frozen_string_literal;
36ID rb_id_option_line;
37ID rb_id_option_main_script;
38ID rb_id_option_partial_script;
39ID rb_id_option_scopes;
40ID rb_id_option_version;
41ID rb_id_source_for;
42ID rb_id_forwarding_positionals;
43ID rb_id_forwarding_keywords;
44ID rb_id_forwarding_block;
45ID 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) {
134 } else if (id == rb_id_forwarding_keywords) {
136 } else if (id == rb_id_forwarding_block) {
138 } else if (id == rb_id_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
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);
319
320 switch (result = pm_string_file_init(input, source)) {
322 break;
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 }
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 (
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) {
556 level = ID2SYM(rb_intern("syntax"));
557 break;
559 level = ID2SYM(rb_intern("argument"));
560 break;
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) {
596 level = ID2SYM(rb_intern("default"));
597 break;
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
641typedef struct {
642 VALUE source;
643 VALUE tokens;
644 rb_encoding *encoding;
645 bool freeze;
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
1000static char *
1001parse_stream_fgets(char *string, int size, void *stream) {
1002 RUBY_ASSERT(size > 0);
1003
1004 VALUE line = rb_funcall((VALUE) stream, rb_intern("gets"), 1, INT2FIX(size - 1));
1005 if (NIL_P(line)) {
1006 return NULL;
1007 }
1008
1009 const char *cstr = RSTRING_PTR(line);
1010 long length = RSTRING_LEN(line);
1011
1012 memcpy(string, cstr, length);
1013 string[length] = '\0';
1014
1015 return string;
1016}
1017
1025static VALUE
1026parse_stream(int argc, VALUE *argv, VALUE self) {
1027 VALUE stream;
1028 VALUE keywords;
1029 rb_scan_args(argc, argv, "1:", &stream, &keywords);
1030
1031 pm_options_t options = { 0 };
1032 extract_options(&options, Qnil, keywords);
1033
1034 pm_parser_t parser;
1035 pm_buffer_t buffer;
1036
1037 pm_node_t *node = pm_parse_stream(&parser, &buffer, (void *) stream, parse_stream_fgets, &options);
1038 rb_encoding *encoding = rb_enc_find(parser.encoding->name);
1039
1040 VALUE source = pm_source_new(&parser, encoding, options.freeze);
1041 VALUE value = pm_ast_new(&parser, node, encoding, source, options.freeze);
1042 VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source, options.freeze);
1043
1044 pm_node_destroy(&parser, node);
1045 pm_buffer_free(&buffer);
1046 pm_parser_free(&parser);
1047
1048 return result;
1049}
1050
1054static VALUE
1055parse_input_comments(pm_string_t *input, const pm_options_t *options) {
1056 pm_parser_t parser;
1057 pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
1058
1059 pm_node_t *node = pm_parse(&parser);
1060 rb_encoding *encoding = rb_enc_find(parser.encoding->name);
1061
1062 VALUE source = pm_source_new(&parser, encoding, options->freeze);
1063 VALUE comments = parser_comments(&parser, source, options->freeze);
1064
1065 pm_node_destroy(&parser, node);
1066 pm_parser_free(&parser);
1067
1068 return comments;
1069}
1070
1078static VALUE
1079parse_comments(int argc, VALUE *argv, VALUE self) {
1080 pm_string_t input;
1081 pm_options_t options = { 0 };
1082 string_options(argc, argv, &input, &options);
1083
1084 VALUE result = parse_input_comments(&input, &options);
1085 pm_string_free(&input);
1086 pm_options_free(&options);
1087
1088 return result;
1089}
1090
1098static VALUE
1099parse_file_comments(int argc, VALUE *argv, VALUE self) {
1100 pm_string_t input;
1101 pm_options_t options = { 0 };
1102
1103 VALUE encoded_filepath;
1104 file_options(argc, argv, &input, &options, &encoded_filepath);
1105
1106 VALUE value = parse_input_comments(&input, &options);
1107 pm_string_free(&input);
1108 pm_options_free(&options);
1109
1110 return value;
1111}
1112
1127static VALUE
1128parse_lex(int argc, VALUE *argv, VALUE self) {
1129 pm_string_t input;
1130 pm_options_t options = { 0 };
1131 string_options(argc, argv, &input, &options);
1132
1133 VALUE value = parse_lex_input(&input, &options, true);
1134 pm_string_free(&input);
1135 pm_options_free(&options);
1136
1137 return value;
1138}
1139
1154static VALUE
1155parse_lex_file(int argc, VALUE *argv, VALUE self) {
1156 pm_string_t input;
1157 pm_options_t options = { 0 };
1158
1159 VALUE encoded_filepath;
1160 file_options(argc, argv, &input, &options, &encoded_filepath);
1161
1162 VALUE value = parse_lex_input(&input, &options, true);
1163 pm_string_free(&input);
1164 pm_options_free(&options);
1165
1166 return value;
1167}
1168
1172static VALUE
1173parse_input_success_p(pm_string_t *input, const pm_options_t *options) {
1174 pm_parser_t parser;
1175 pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options);
1176
1177 pm_node_t *node = pm_parse(&parser);
1178 pm_node_destroy(&parser, node);
1179
1180 VALUE result = parser.error_list.size == 0 ? Qtrue : Qfalse;
1181 pm_parser_free(&parser);
1182
1183 return result;
1184}
1185
1193static VALUE
1194parse_success_p(int argc, VALUE *argv, VALUE self) {
1195 pm_string_t input;
1196 pm_options_t options = { 0 };
1197 string_options(argc, argv, &input, &options);
1198
1199 VALUE result = parse_input_success_p(&input, &options);
1200 pm_string_free(&input);
1201 pm_options_free(&options);
1202
1203 return result;
1204}
1205
1213static VALUE
1214parse_failure_p(int argc, VALUE *argv, VALUE self) {
1215 return RTEST(parse_success_p(argc, argv, self)) ? Qfalse : Qtrue;
1216}
1217
1225static VALUE
1226parse_file_success_p(int argc, VALUE *argv, VALUE self) {
1227 pm_string_t input;
1228 pm_options_t options = { 0 };
1229
1230 VALUE encoded_filepath;
1231 file_options(argc, argv, &input, &options, &encoded_filepath);
1232
1233 VALUE result = parse_input_success_p(&input, &options);
1234 pm_string_free(&input);
1235 pm_options_free(&options);
1236
1237 return result;
1238}
1239
1247static VALUE
1248parse_file_failure_p(int argc, VALUE *argv, VALUE self) {
1249 return RTEST(parse_file_success_p(argc, argv, self)) ? Qfalse : Qtrue;
1250}
1251
1252/******************************************************************************/
1253/* String query methods */
1254/******************************************************************************/
1255
1260static VALUE
1261string_query(pm_string_query_t result) {
1262 switch (result) {
1264 rb_raise(rb_eArgError, "Invalid or non ascii-compatible encoding");
1265 return Qfalse;
1267 return Qfalse;
1269 return Qtrue;
1270 }
1271 return Qfalse;
1272}
1273
1282static VALUE
1283string_query_local_p(VALUE self, VALUE string) {
1284 const uint8_t *source = (const uint8_t *) check_string(string);
1285 return string_query(pm_string_query_local(source, RSTRING_LEN(string), rb_enc_get(string)->name));
1286}
1287
1296static VALUE
1297string_query_constant_p(VALUE self, VALUE string) {
1298 const uint8_t *source = (const uint8_t *) check_string(string);
1299 return string_query(pm_string_query_constant(source, RSTRING_LEN(string), rb_enc_get(string)->name));
1300}
1301
1308static VALUE
1309string_query_method_name_p(VALUE self, VALUE string) {
1310 const uint8_t *source = (const uint8_t *) check_string(string);
1311 return string_query(pm_string_query_method_name(source, RSTRING_LEN(string), rb_enc_get(string)->name));
1312}
1313
1314/******************************************************************************/
1315/* Initialization of the extension */
1316/******************************************************************************/
1317
1321RUBY_FUNC_EXPORTED void
1322Init_prism(void) {
1323 // Make sure that the prism library version matches the expected version.
1324 // Otherwise something was compiled incorrectly.
1325 if (strcmp(pm_version(), EXPECTED_PRISM_VERSION) != 0) {
1326 rb_raise(
1328 "The prism library version (%s) does not match the expected version (%s)",
1329 pm_version(),
1330 EXPECTED_PRISM_VERSION
1331 );
1332 }
1333
1334 // Grab up references to all of the constants that we're going to need to
1335 // reference throughout this extension.
1336 rb_cPrism = rb_define_module("Prism");
1337 rb_cPrismNode = rb_define_class_under(rb_cPrism, "Node", rb_cObject);
1338 rb_cPrismSource = rb_define_class_under(rb_cPrism, "Source", rb_cObject);
1339 rb_cPrismToken = rb_define_class_under(rb_cPrism, "Token", rb_cObject);
1340 rb_cPrismLocation = rb_define_class_under(rb_cPrism, "Location", rb_cObject);
1341 rb_cPrismComment = rb_define_class_under(rb_cPrism, "Comment", rb_cObject);
1342 rb_cPrismInlineComment = rb_define_class_under(rb_cPrism, "InlineComment", rb_cPrismComment);
1343 rb_cPrismEmbDocComment = rb_define_class_under(rb_cPrism, "EmbDocComment", rb_cPrismComment);
1344 rb_cPrismMagicComment = rb_define_class_under(rb_cPrism, "MagicComment", rb_cObject);
1345 rb_cPrismParseError = rb_define_class_under(rb_cPrism, "ParseError", rb_cObject);
1346 rb_cPrismParseWarning = rb_define_class_under(rb_cPrism, "ParseWarning", rb_cObject);
1347 rb_cPrismResult = rb_define_class_under(rb_cPrism, "Result", rb_cObject);
1348 rb_cPrismParseResult = rb_define_class_under(rb_cPrism, "ParseResult", rb_cPrismResult);
1349 rb_cPrismLexResult = rb_define_class_under(rb_cPrism, "LexResult", rb_cPrismResult);
1350 rb_cPrismParseLexResult = rb_define_class_under(rb_cPrism, "ParseLexResult", rb_cPrismResult);
1351 rb_cPrismStringQuery = rb_define_class_under(rb_cPrism, "StringQuery", rb_cObject);
1352 rb_cPrismScope = rb_define_class_under(rb_cPrism, "Scope", rb_cObject);
1353
1354 // Intern all of the IDs eagerly that we support so that we don't have to do
1355 // it every time we parse.
1356 rb_id_option_command_line = rb_intern_const("command_line");
1357 rb_id_option_encoding = rb_intern_const("encoding");
1358 rb_id_option_filepath = rb_intern_const("filepath");
1359 rb_id_option_freeze = rb_intern_const("freeze");
1360 rb_id_option_frozen_string_literal = rb_intern_const("frozen_string_literal");
1361 rb_id_option_line = rb_intern_const("line");
1362 rb_id_option_main_script = rb_intern_const("main_script");
1363 rb_id_option_partial_script = rb_intern_const("partial_script");
1364 rb_id_option_scopes = rb_intern_const("scopes");
1365 rb_id_option_version = rb_intern_const("version");
1366 rb_id_source_for = rb_intern("for");
1367 rb_id_forwarding_positionals = rb_intern("*");
1368 rb_id_forwarding_keywords = rb_intern("**");
1369 rb_id_forwarding_block = rb_intern("&");
1370 rb_id_forwarding_all = rb_intern("...");
1371
1375 rb_define_const(rb_cPrism, "VERSION", rb_str_freeze(rb_str_new_cstr(EXPECTED_PRISM_VERSION)));
1376
1377 // First, the functions that have to do with lexing and parsing.
1378 rb_define_singleton_method(rb_cPrism, "lex", lex, -1);
1379 rb_define_singleton_method(rb_cPrism, "lex_file", lex_file, -1);
1380 rb_define_singleton_method(rb_cPrism, "parse", parse, -1);
1381 rb_define_singleton_method(rb_cPrism, "parse_file", parse_file, -1);
1382 rb_define_singleton_method(rb_cPrism, "profile", profile, -1);
1383 rb_define_singleton_method(rb_cPrism, "profile_file", profile_file, -1);
1384 rb_define_singleton_method(rb_cPrism, "parse_stream", parse_stream, -1);
1385 rb_define_singleton_method(rb_cPrism, "parse_comments", parse_comments, -1);
1386 rb_define_singleton_method(rb_cPrism, "parse_file_comments", parse_file_comments, -1);
1387 rb_define_singleton_method(rb_cPrism, "parse_lex", parse_lex, -1);
1388 rb_define_singleton_method(rb_cPrism, "parse_lex_file", parse_lex_file, -1);
1389 rb_define_singleton_method(rb_cPrism, "parse_success?", parse_success_p, -1);
1390 rb_define_singleton_method(rb_cPrism, "parse_failure?", parse_failure_p, -1);
1391 rb_define_singleton_method(rb_cPrism, "parse_file_success?", parse_file_success_p, -1);
1392 rb_define_singleton_method(rb_cPrism, "parse_file_failure?", parse_file_failure_p, -1);
1393
1394#ifndef PRISM_EXCLUDE_SERIALIZATION
1395 rb_define_singleton_method(rb_cPrism, "dump", dump, -1);
1396 rb_define_singleton_method(rb_cPrism, "dump_file", dump_file, -1);
1397#endif
1398
1399 rb_define_singleton_method(rb_cPrismStringQuery, "local?", string_query_local_p, 1);
1400 rb_define_singleton_method(rb_cPrismStringQuery, "constant?", string_query_constant_p, 1);
1401 rb_define_singleton_method(rb_cPrismStringQuery, "method_name?", string_query_method_name_p, 1);
1402
1403 // Next, initialize the other APIs.
1404 Init_prism_api_node();
1405 Init_prism_pack();
1406}
#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:405
@ PM_WARNING_LEVEL_VERBOSE
For warnings which should be emitted if $VERBOSE == true.
Definition diagnostic.h:408
@ PM_ERROR_LEVEL_ARGUMENT
For errors that should raise an argument error.
Definition diagnostic.h:394
@ PM_ERROR_LEVEL_LOAD
For errors that should raise a load error.
Definition diagnostic.h:397
@ PM_ERROR_LEVEL_SYNTAX
For errors that should raise a syntax error.
Definition diagnostic.h:391
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1012
VALUE rb_define_module(const char *name)
Defines a top-level module.
Definition class.c:1095
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:2635
#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_syserr_fail(int e, const char *mesg)
Raises appropriate exception that represents a C errno.
Definition error.c:3905
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:2138
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:247
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
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1260
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:1099
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1099
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1498
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition string.c:1917
VALUE rb_str_freeze(VALUE str)
This is the implementation of String#freeze.
Definition string.c:3177
#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
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:1335
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.
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:201
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:207
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:194
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:213
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:225
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:219
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
pm_string_query_t
Represents the results of a slice query.
Definition prism.h:240
@ PM_STRING_QUERY_TRUE
Returned if the result of the slice query is true.
Definition prism.h:248
@ PM_STRING_QUERY_ERROR
Returned if the encoding given to a slice query was invalid.
Definition prism.h:242
@ PM_STRING_QUERY_FALSE
Returned if the result of the slice query is false.
Definition prism.h:245
#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:245
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
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:359
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:550
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:1069
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:98
bool freeze
Whether or not the parser should freeze the nodes that it creates.
Definition options.h:187
pm_options_scope_t * scopes
The scopes surrounding the code that is being parsed.
Definition options.h:137
int32_t line
The line within the file that the parse starts on.
Definition options.h:118
pm_string_t filepath
The name of the file that is currently being parsed.
Definition options.h:112
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:530
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