diff options
author | Kevin Newton <kddnewton@gmail.com> | 2023-11-02 11:15:37 -0400 |
---|---|---|
committer | Kevin Newton <kddnewton@gmail.com> | 2023-11-03 10:13:49 -0400 |
commit | 2a0f2b776325f949ba6d311b0d90cbaaea825b63 (patch) | |
tree | 0256f169a54c47eeb1db1e8b04d1f0c6b7860d1e /prism | |
parent | a43a52d415dff3868297ab070da4d4fd820f6b37 (diff) |
[ruby/prism] Create an options struct for passing all of the possible options
https://github.com/ruby/prism/commit/99e81619de
Diffstat (limited to 'prism')
-rw-r--r-- | prism/extension.c | 92 | ||||
-rw-r--r-- | prism/options.c | 96 | ||||
-rw-r--r-- | prism/options.h | 161 | ||||
-rw-r--r-- | prism/prism.c | 58 | ||||
-rw-r--r-- | prism/prism.h | 5 | ||||
-rw-r--r-- | prism/templates/src/serialize.c.erb | 6 | ||||
-rw-r--r-- | prism/util/pm_newline_list.c | 27 | ||||
-rw-r--r-- | prism/util/pm_newline_list.h | 12 |
8 files changed, 425 insertions, 32 deletions
diff --git a/prism/extension.c b/prism/extension.c index 42268c8538..ed166a5176 100644 --- a/prism/extension.c +++ b/prism/extension.c @@ -61,14 +61,14 @@ input_load_string(pm_string_t *input, VALUE string) { * Dump the AST corresponding to the given input to a string. */ static VALUE -dump_input(pm_string_t *input, const char *filepath) { +dump_input(pm_string_t *input, const pm_options_t *options) { pm_buffer_t buffer; if (!pm_buffer_init(&buffer)) { rb_raise(rb_eNoMemError, "failed to allocate memory"); } pm_parser_t parser; - pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), filepath); + pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options); pm_node_t *node = pm_parse(&parser); pm_serialize(&parser, node, &buffer); @@ -103,7 +103,11 @@ dump(int argc, VALUE *argv, VALUE self) { pm_string_constant_init(&input, dup, length); #endif - VALUE value = dump_input(&input, check_string(filepath)); + pm_options_t options = { 0 }; + pm_options_filepath_set(&options, check_string(filepath)); + + VALUE value = dump_input(&input, &options); + pm_options_free(&options); #ifdef PRISM_DEBUG_MODE_BUILD free(dup); @@ -125,7 +129,12 @@ dump_file(VALUE self, VALUE filepath) { const char *checked = check_string(filepath); if (!pm_string_mapped_init(&input, checked)) return Qnil; - VALUE value = dump_input(&input, checked); + pm_options_t options = { 0 }; + pm_options_filepath_set(&options, checked); + + VALUE value = dump_input(&input, &options); + + pm_options_free(&options); pm_string_free(&input); return value; @@ -316,9 +325,9 @@ parse_lex_encoding_changed_callback(pm_parser_t *parser) { * the nodes and tokens. */ static VALUE -parse_lex_input(pm_string_t *input, const char *filepath, bool return_nodes) { +parse_lex_input(pm_string_t *input, const pm_options_t *options, bool return_nodes) { pm_parser_t parser; - pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), filepath); + pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options); pm_parser_register_encoding_changed_callback(&parser, parse_lex_encoding_changed_callback); VALUE offsets = rb_ary_new(); @@ -385,7 +394,13 @@ lex(int argc, VALUE *argv, VALUE self) { pm_string_t input; input_load_string(&input, string); - return parse_lex_input(&input, check_string(filepath), false); + pm_options_t options = { 0 }; + pm_options_filepath_set(&options, check_string(filepath)); + + VALUE result = parse_lex_input(&input, &options, false); + pm_options_free(&options); + + return result; } /** @@ -401,7 +416,12 @@ lex_file(VALUE self, VALUE filepath) { const char *checked = check_string(filepath); if (!pm_string_mapped_init(&input, checked)) return Qnil; - VALUE value = parse_lex_input(&input, checked, false); + pm_options_t options = { 0 }; + pm_options_filepath_set(&options, checked); + + VALUE value = parse_lex_input(&input, &options, false); + + pm_options_free(&options); pm_string_free(&input); return value; @@ -415,9 +435,9 @@ lex_file(VALUE self, VALUE filepath) { * Parse the given input and return a ParseResult instance. */ static VALUE -parse_input(pm_string_t *input, const char *filepath) { +parse_input(pm_string_t *input, const pm_options_t *options) { pm_parser_t parser; - pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), filepath); + pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options); pm_node_t *node = pm_parse(&parser); rb_encoding *encoding = rb_enc_find(parser.encoding.name); @@ -462,7 +482,11 @@ parse(int argc, VALUE *argv, VALUE self) { pm_string_constant_init(&input, dup, length); #endif - VALUE value = parse_input(&input, check_string(filepath)); + pm_options_t options = { 0 }; + pm_options_filepath_set(&options, check_string(filepath)); + + VALUE value = parse_input(&input, &options); + pm_options_free(&options); #ifdef PRISM_DEBUG_MODE_BUILD free(dup); @@ -484,7 +508,11 @@ parse_file(VALUE self, VALUE filepath) { const char *checked = check_string(filepath); if (!pm_string_mapped_init(&input, checked)) return Qnil; - VALUE value = parse_input(&input, checked); + pm_options_t options = { 0 }; + pm_options_filepath_set(&options, checked); + + VALUE value = parse_input(&input, &options); + pm_options_free(&options); pm_string_free(&input); return value; @@ -494,9 +522,9 @@ parse_file(VALUE self, VALUE filepath) { * Parse the given input and return an array of Comment objects. */ static VALUE -parse_input_comments(pm_string_t *input, const char *filepath) { +parse_input_comments(pm_string_t *input, const pm_options_t *options) { pm_parser_t parser; - pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), filepath); + pm_parser_init(&parser, pm_string_source(input), pm_string_length(input), options); pm_node_t *node = pm_parse(&parser); rb_encoding *encoding = rb_enc_find(parser.encoding.name); @@ -525,7 +553,13 @@ parse_comments(int argc, VALUE *argv, VALUE self) { pm_string_t input; input_load_string(&input, string); - return parse_input_comments(&input, check_string(filepath)); + pm_options_t options = { 0 }; + pm_options_filepath_set(&options, check_string(filepath)); + + VALUE result = parse_input_comments(&input, &options); + pm_options_free(&options); + + return result; } /** @@ -541,7 +575,12 @@ parse_file_comments(VALUE self, VALUE filepath) { const char *checked = check_string(filepath); if (!pm_string_mapped_init(&input, checked)) return Qnil; - VALUE value = parse_input_comments(&input, checked); + pm_options_t options = { 0 }; + pm_options_filepath_set(&options, checked); + + VALUE value = parse_input_comments(&input, &options); + + pm_options_free(&options); pm_string_free(&input); return value; @@ -568,7 +607,12 @@ parse_lex(int argc, VALUE *argv, VALUE self) { pm_string_t input; input_load_string(&input, string); - VALUE value = parse_lex_input(&input, check_string(filepath), true); + pm_options_t options = { 0 }; + pm_options_filepath_set(&options, check_string(filepath)); + + VALUE value = parse_lex_input(&input, &options, true); + + pm_options_free(&options); pm_string_free(&input); return value; @@ -593,7 +637,12 @@ parse_lex_file(VALUE self, VALUE filepath) { const char *checked = check_string(filepath); if (!pm_string_mapped_init(&input, checked)) return Qnil; - VALUE value = parse_lex_input(&input, checked, true); + pm_options_t options = { 0 }; + pm_options_filepath_set(&options, checked); + + VALUE value = parse_lex_input(&input, &options, true); + + pm_options_free(&options); pm_string_free(&input); return value; @@ -670,13 +719,16 @@ profile_file(VALUE self, VALUE filepath) { const char *checked = check_string(filepath); if (!pm_string_mapped_init(&input, checked)) return Qnil; + pm_options_t options = { 0 }; + pm_options_filepath_set(&options, checked); + pm_parser_t parser; - pm_parser_init(&parser, pm_string_source(&input), pm_string_length(&input), checked); + pm_parser_init(&parser, pm_string_source(&input), pm_string_length(&input), &options); pm_node_t *node = pm_parse(&parser); pm_node_destroy(&parser, node); pm_parser_free(&parser); - + pm_options_free(&options); pm_string_free(&input); return Qnil; diff --git a/prism/options.c b/prism/options.c new file mode 100644 index 0000000000..7f45c2026c --- /dev/null +++ b/prism/options.c @@ -0,0 +1,96 @@ +#include "prism/options.h" + +/** + * Set the filepath option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_filepath_set(pm_options_t *options, const char *filepath) { + options->filepath = filepath; +} + +/** + * Set the encoding option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_encoding_set(pm_options_t *options, const char *encoding) { + options->encoding = encoding; +} + +/** + * Set the line option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_line_set(pm_options_t *options, uint32_t line) { + options->line = line; +} + +/** + * Set the frozen string literal option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal) { + options->frozen_string_literal = frozen_string_literal; +} + +/** + * Set the suppress warnings option on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_suppress_warnings_set(pm_options_t *options, bool suppress_warnings) { + options->suppress_warnings = suppress_warnings; +} + +/** + * Allocate and zero out the scopes array on the given options struct. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_scopes_init(pm_options_t *options, size_t scopes_count) { + options->scopes_count = scopes_count; + options->scopes = calloc(scopes_count, sizeof(pm_options_scope_t)); + if (options->scopes == NULL) abort(); +} + +/** + * Return a pointer to the scope at the given index within the given options. + */ +PRISM_EXPORTED_FUNCTION const pm_options_scope_t * +pm_options_scope_get(const pm_options_t *options, size_t index) { + return &options->scopes[index]; +} + +/** + * Create a new options scope struct. This will hold a set of locals that are in + * scope surrounding the code that is being parsed. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count) { + scope->locals_count = locals_count; + scope->locals = calloc(locals_count, sizeof(pm_string_t)); + if (scope->locals == NULL) abort(); +} + +/** + * Return a pointer to the local at the given index within the given scope. + */ +PRISM_EXPORTED_FUNCTION const pm_string_t * +pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index) { + return &scope->locals[index]; +} + +/** + * Free the internal memory associated with the options. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_free(pm_options_t *options) { + for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) { + pm_options_scope_t *scope = &options->scopes[scope_index]; + + for (size_t local_index = 0; local_index < scope->locals_count; local_index++) { + pm_string_free(&scope->locals[local_index]); + } + + free(scope->locals); + } + + free(options->scopes); +} diff --git a/prism/options.h b/prism/options.h new file mode 100644 index 0000000000..5abf16a601 --- /dev/null +++ b/prism/options.h @@ -0,0 +1,161 @@ +/** + * @file options.h + * + * The options that can be passed to parsing. + */ +#ifndef PRISM_OPTIONS_H +#define PRISM_OPTIONS_H + +#include "prism/defines.h" +#include "prism/util/pm_string.h" + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +/** + * A scope of locals surrounding the code that is being parsed. + */ +typedef struct pm_options_scope { + /** The number of locals in the scope. */ + size_t locals_count; + + /** The names of the locals in the scope. */ + pm_string_t *locals; +} pm_options_scope_t; + +/** + * The options that can be passed to the parser. + */ +typedef struct { + /** The name of the file that is currently being parsed. */ + const char *filepath; + + /** + * The name of the encoding that the source file is in. Note that this must + * correspond to a name that can be found with Encoding.find in Ruby. + */ + const char *encoding; + + /** + * The line within the file that the parse starts on. This value is + * 0-indexed. + */ + uint32_t line; + + /** + * The number of scopes surrounding the code that is being parsed. + */ + size_t scopes_count; + + /** + * The scopes surrounding the code that is being parsed. For most parses + * this will be NULL, but for evals it will be the locals that are in scope + * surrounding the eval. + */ + pm_options_scope_t *scopes; + + /** Whether or not the frozen string literal option has been set. */ + bool frozen_string_literal; + + /** + * Whether or not we should suppress warnings. This is purposefully negated + * so that the default is to not suppress warnings, which allows us to still + * create an options struct with zeroed memory. + */ + bool suppress_warnings; +} pm_options_t; + +/** + * Set the filepath option on the given options struct. + * + * @param options The options struct to set the filepath on. + * @param filepath The filepath to set. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_filepath_set(pm_options_t *options, const char *filepath); + +/** + * Set the encoding option on the given options struct. + * + * @param options The options struct to set the encoding on. + * @param encoding The encoding to set. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_encoding_set(pm_options_t *options, const char *encoding); + +/** + * Set the line option on the given options struct. + * + * @param options The options struct to set the line on. + * @param line The line to set. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_line_set(pm_options_t *options, uint32_t line); + +/** + * Set the frozen string literal option on the given options struct. + * + * @param options The options struct to set the frozen string literal value on. + * @param frozen_string_literal The frozen string literal value to set. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal); + +/** + * Set the suppress warnings option on the given options struct. + * + * @param options The options struct to set the suppress warnings value on. + * @param suppress_warnings The suppress warnings value to set. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_suppress_warnings_set(pm_options_t *options, bool suppress_warnings); + +/** + * Allocate and zero out the scopes array on the given options struct. + * + * @param options The options struct to initialize the scopes array on. + * @param scopes_count The number of scopes to allocate. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_scopes_init(pm_options_t *options, size_t scopes_count); + +/** + * Return a pointer to the scope at the given index within the given options. + * + * @param options The options struct to get the scope from. + * @param index The index of the scope to get. + * @return A pointer to the scope at the given index. + */ +PRISM_EXPORTED_FUNCTION const pm_options_scope_t * +pm_options_scope_get(const pm_options_t *options, size_t index); + +/** + * Create a new options scope struct. This will hold a set of locals that are in + * scope surrounding the code that is being parsed. + * + * @param scope The scope struct to initialize. + * @param locals_count The number of locals to allocate. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_scope_init(pm_options_scope_t *scope, size_t locals_count); + +/** + * Return a pointer to the local at the given index within the given scope. + * + * @param scope The scope struct to get the local from. + * @param index The index of the local to get. + * @return A pointer to the local at the given index. + */ +PRISM_EXPORTED_FUNCTION const pm_string_t * +pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index); + +/** + * Free the internal memory associated with the options. + * + * @param options The options struct whose internal memory should be freed. + */ +PRISM_EXPORTED_FUNCTION void +pm_options_free(pm_options_t *options); + +#endif diff --git a/prism/prism.c b/prism/prism.c index de32ccae63..fc836a956d 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -16319,14 +16319,9 @@ pm_parser_metadata(pm_parser_t *parser, const char *metadata) { * Initialize a parser with the given start and end pointers. */ PRISM_EXPORTED_FUNCTION void -pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const char *filepath) { +pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options) { assert(source != NULL); - // Set filepath to the file that was passed - if (!filepath) filepath = ""; - pm_string_t filepath_string; - pm_string_constant_init(&filepath_string, filepath, strlen(filepath)); - *parser = (pm_parser_t) { .lex_state = PM_LEX_STATE_BEG, .enclosure_nesting = 0, @@ -16356,7 +16351,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const ch .encoding_decode_callback = NULL, .encoding_comment_start = source, .lex_callback = NULL, - .filepath_string = filepath_string, + .filepath_string = { 0 }, .constant_pool = { 0 }, .newline_list = { 0 }, .integer_base = 0, @@ -16370,8 +16365,6 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const ch .frozen_string_literal = false }; - pm_accepts_block_stack_push(parser, true); - // Initialize the constant pool. We're going to completely guess as to the // number of constants that we'll need based on the size of the input. The // ratio we chose here is actually less arbitrary than you might think. @@ -16395,6 +16388,53 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const ch size_t newline_size = size / 22; pm_newline_list_init(&parser->newline_list, source, newline_size < 4 ? 4 : newline_size); + // If options were provided to this parse, establish them here. + if (options != NULL) { + // filepath option + if (options->filepath == NULL) { + pm_string_constant_init(&parser->filepath_string, "", 0); + } else { + pm_string_constant_init(&parser->filepath_string, options->filepath, strlen(options->filepath)); + } + + // line option + if (options->line > 0) { + pm_newline_list_force(&parser->newline_list, options->line); + } + + // encoding option + // if (options->encoding != NULL) {} + + // frozen_string_literal option + if (options->frozen_string_literal) { + parser->frozen_string_literal = true; + } + + // suppress_warnings option + // if (options->suppress_warnings) {} + + // scopes option + for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) { + const pm_options_scope_t *scope = pm_options_scope_get(options, scope_index); + pm_parser_scope_push(parser, scope_index == 0); + + for (size_t local_index = 0; local_index < scope->locals_count; local_index++) { + const pm_string_t *local = pm_options_scope_local_get(scope, local_index); + + const uint8_t *source = pm_string_source(local); + size_t length = pm_string_length(local); + + uint8_t *allocated = malloc(length); + if (allocated == NULL) continue; + + memcpy((void *) allocated, source, length); + pm_parser_local_add_owned(parser, allocated, length); + } + } + } + + pm_accepts_block_stack_push(parser, true); + // Skip past the UTF-8 BOM if it exists. if (size >= 3 && source[0] == 0xef && source[1] == 0xbb && source[2] == 0xbf) { parser->current.end += 3; diff --git a/prism/prism.h b/prism/prism.h index d6b18a44a8..84bd3f84a7 100644 --- a/prism/prism.h +++ b/prism/prism.h @@ -15,6 +15,7 @@ #include "prism/ast.h" #include "prism/diagnostic.h" #include "prism/node.h" +#include "prism/options.h" #include "prism/pack.h" #include "prism/parser.h" #include "prism/prettyprint.h" @@ -47,9 +48,9 @@ PRISM_EXPORTED_FUNCTION const char * pm_version(void); * @param parser The parser to initialize. * @param source The source to parse. * @param size The size of the source. - * @param filepath The optional filepath to pass to the parser. + * @param options The optional options to use when parsing. */ -PRISM_EXPORTED_FUNCTION void pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const char *filepath); +PRISM_EXPORTED_FUNCTION void pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options); /** * Register a callback that will be called whenever prism changes the encoding diff --git a/prism/templates/src/serialize.c.erb b/prism/templates/src/serialize.c.erb index 5e166f1eef..db10fab6de 100644 --- a/prism/templates/src/serialize.c.erb +++ b/prism/templates/src/serialize.c.erb @@ -288,8 +288,11 @@ serialize_token(void *data, pm_parser_t *parser, pm_token_t *token) { */ PRISM_EXPORTED_FUNCTION void pm_lex_serialize(const uint8_t *source, size_t size, const char *filepath, pm_buffer_t *buffer) { + pm_options_t options = { 0 }; + pm_options_filepath_set(&options, filepath); + pm_parser_t parser; - pm_parser_init(&parser, source, size, filepath); + pm_parser_init(&parser, source, size, &options); pm_lex_callback_t lex_callback = (pm_lex_callback_t) { .data = (void *) buffer, @@ -310,6 +313,7 @@ pm_lex_serialize(const uint8_t *source, size_t size, const char *filepath, pm_bu pm_node_destroy(&parser, node); pm_parser_free(&parser); + pm_options_free(&options); } /** diff --git a/prism/util/pm_newline_list.c b/prism/util/pm_newline_list.c index f27bb75b63..978ebf3d0e 100644 --- a/prism/util/pm_newline_list.c +++ b/prism/util/pm_newline_list.c @@ -20,6 +20,33 @@ pm_newline_list_init(pm_newline_list_t *list, const uint8_t *start, size_t capac } /** + * Set up the newline list such that it believes it is starting on a specific + * line in the source. Basically this entails pushing on pointers to the start + * of the string until we hit the desired line. + */ +bool +pm_newline_list_force(pm_newline_list_t *list, size_t count) { + size_t next_capacity = list->capacity == 0 ? 1 : list->capacity; + while (count > next_capacity) { + next_capacity *= 2; + } + + size_t *offsets = list->offsets; + list->offsets = (size_t *) calloc(next_capacity, sizeof(size_t)); + if (list->offsets == NULL) return false; + + if (offsets != NULL) { + memcpy(list->offsets, offsets, list->size * sizeof(size_t)); + free(offsets); + } + + memset(list->offsets + list->size, 0, count * sizeof(size_t)); + list->size += count; + + return true; +} + +/** * Append a new offset to the newline list. Returns true if the reallocation of * the offsets succeeds (if one was necessary), otherwise returns false. */ diff --git a/prism/util/pm_newline_list.h b/prism/util/pm_newline_list.h index a31051f4e0..93816b0656 100644 --- a/prism/util/pm_newline_list.h +++ b/prism/util/pm_newline_list.h @@ -62,6 +62,18 @@ typedef struct { bool pm_newline_list_init(pm_newline_list_t *list, const uint8_t *start, size_t capacity); /** + * Set up the newline list such that it believes it is starting on a specific + * line in the source. Basically this entails pushing on pointers to the start + * of the string until we hit the desired line. + * + * @param list The list to set up. + * @param count The number of lines to push onto the list. + * @return True if no reallocation was needed or the reallocation of the offsets + * succeeds (if one was necessary), otherwise false. + */ +bool pm_newline_list_force(pm_newline_list_t *list, size_t count); + +/** * Append a new offset to the newline list. Returns true if the reallocation of * the offsets succeeds (if one was necessary), otherwise returns false. * |