[ruby/prism] Create an options struct for passing all of the possible options

https://github.com/ruby/prism/commit/99e81619de
This commit is contained in:
Kevin Newton 2023-11-02 11:15:37 -04:00
Родитель a43a52d415
Коммит 2a0f2b7763
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 0EAD74C79EC73F26
9 изменённых файлов: 427 добавлений и 32 удалений

Просмотреть файл

@ -45,6 +45,7 @@ Gem::Specification.new do |spec|
"include/prism/diagnostic.h",
"include/prism/enc/pm_encoding.h",
"include/prism/node.h",
"include/prism/options.h",
"include/prism/pack.h",
"include/prism/parser.h",
"include/prism/prettyprint.h",
@ -106,6 +107,7 @@ Gem::Specification.new do |spec|
"src/util/pm_string_list.c",
"src/util/pm_strncasecmp.c",
"src/util/pm_strpbrk.c",
"src/options.c",
"src/prism.c",
"prism.gemspec",
"sig/prism.rbs",

Просмотреть файл

@ -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;

96
prism/options.c Normal file
Просмотреть файл

@ -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);
}

161
prism/options.h Normal file
Просмотреть файл

@ -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

Просмотреть файл

@ -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;

Просмотреть файл

@ -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

Просмотреть файл

@ -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);
}
/**

Просмотреть файл

@ -19,6 +19,33 @@ pm_newline_list_init(pm_newline_list_t *list, const uint8_t *start, size_t capac
return true;
}
/**
* 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.

Просмотреть файл

@ -61,6 +61,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.