[ruby/prism] Attach magic comments to the parse result

https://github.com/ruby/prism/commit/c7ef25a79a
This commit is contained in:
Kevin Newton 2023-10-13 12:16:11 -04:00 коммит произвёл Jemma Issroff
Родитель 39dd3343d8
Коммит 5523a23469
8 изменённых файлов: 136 добавлений и 16 удалений

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

@ -234,11 +234,11 @@ module Prism
loader = Serialize::Loader.new(source, buffer.read)
tokens = loader.load_tokens
node, comments, errors, warnings = loader.load_nodes
node, comments, magic_comments, errors, warnings = loader.load_nodes
tokens.each { |token,| token.value.force_encoding(loader.encoding) }
ParseResult.new([node, tokens], comments, errors, warnings, source)
ParseResult.new([node, tokens], comments, magic_comments, errors, warnings, source)
end
end

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

@ -818,7 +818,7 @@ module Prism
# We sort by location to compare against Ripper's output
tokens.sort_by!(&:location)
ParseResult.new(tokens, result.comments, result.errors, result.warnings, [])
ParseResult.new(tokens, result.comments, result.magic_comments, result.errors, result.warnings, [])
end
end

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

@ -137,7 +137,7 @@ module Prism
end
def self.null
new(0, 0)
new(nil, 0, 0)
end
end
@ -166,6 +166,32 @@ module Prism
end
end
# This represents a magic comment that was encountered during parsing.
class MagicComment
attr_reader :key_loc, :value_loc
def initialize(key_loc, value_loc)
@key_loc = key_loc
@value_loc = value_loc
end
def key
key_loc.slice
end
def value
value_loc.slice
end
def deconstruct_keys(keys)
{ key_loc: key_loc, value_loc: value_loc }
end
def inspect
"#<Prism::MagicComment @key=#{key.inspect} @value=#{value.inspect}>"
end
end
# This represents an error that was encountered during parsing.
class ParseError
attr_reader :message, :location
@ -206,18 +232,19 @@ module Prism
# the AST, any comments that were encounters, and any errors that were
# encountered.
class ParseResult
attr_reader :value, :comments, :errors, :warnings, :source
attr_reader :value, :comments, :magic_comments, :errors, :warnings, :source
def initialize(value, comments, errors, warnings, source)
def initialize(value, comments, magic_comments, errors, warnings, source)
@value = value
@comments = comments
@magic_comments = magic_comments
@errors = errors
@warnings = warnings
@source = source
end
def deconstruct_keys(keys)
{ value: value, comments: comments, errors: errors, warnings: warnings }
{ value: value, comments: comments, magic_comments: magic_comments, errors: errors, warnings: warnings }
end
def success?

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

@ -10,6 +10,7 @@ VALUE rb_cPrismToken;
VALUE rb_cPrismLocation;
VALUE rb_cPrismComment;
VALUE rb_cPrismMagicComment;
VALUE rb_cPrismParseError;
VALUE rb_cPrismParseWarning;
VALUE rb_cPrismParseResult;
@ -153,6 +154,35 @@ parser_comments(pm_parser_t *parser, VALUE source) {
return comments;
}
// Extract the magic comments out of the parser into an array.
static VALUE
parser_magic_comments(pm_parser_t *parser, VALUE source) {
VALUE magic_comments = rb_ary_new();
for (pm_magic_comment_t *magic_comment = (pm_magic_comment_t *) parser->magic_comment_list.head; magic_comment != NULL; magic_comment = (pm_magic_comment_t *) magic_comment->node.next) {
VALUE key_loc_argv[] = {
source,
LONG2FIX(magic_comment->key_start - parser->start),
LONG2FIX(magic_comment->key_length)
};
VALUE value_loc_argv[] = {
source,
LONG2FIX(magic_comment->value_start - parser->start),
LONG2FIX(magic_comment->value_length)
};
VALUE magic_comment_argv[] = {
rb_class_new_instance(3, key_loc_argv, rb_cPrismLocation),
rb_class_new_instance(3, value_loc_argv, rb_cPrismLocation)
};
rb_ary_push(magic_comments, rb_class_new_instance(2, magic_comment_argv, rb_cPrismMagicComment));
}
return magic_comments;
}
// Extract the errors out of the parser into an array.
static VALUE
parser_errors(pm_parser_t *parser, rb_encoding *encoding, VALUE source) {
@ -297,6 +327,7 @@ parse_lex_input(pm_string_t *input, const char *filepath, bool return_nodes) {
VALUE result_argv[] = {
value,
parser_comments(&parser, source),
parser_magic_comments(&parser, source),
parser_errors(&parser, parse_lex_data.encoding, source),
parser_warnings(&parser, parse_lex_data.encoding, source),
source
@ -304,7 +335,7 @@ parse_lex_input(pm_string_t *input, const char *filepath, bool return_nodes) {
pm_node_destroy(&parser, node);
pm_parser_free(&parser);
return rb_class_new_instance(5, result_argv, rb_cPrismParseResult);
return rb_class_new_instance(6, result_argv, rb_cPrismParseResult);
}
// Return an array of tokens corresponding to the given string.
@ -351,12 +382,13 @@ parse_input(pm_string_t *input, const char *filepath) {
VALUE result_argv[] = {
pm_ast_new(&parser, node, encoding),
parser_comments(&parser, source),
parser_magic_comments(&parser, source),
parser_errors(&parser, encoding, source),
parser_warnings(&parser, encoding, source),
source
};
VALUE result = rb_class_new_instance(5, result_argv, rb_cPrismParseResult);
VALUE result = rb_class_new_instance(6, result_argv, rb_cPrismParseResult);
pm_node_destroy(&parser, node);
pm_parser_free(&parser);
@ -547,6 +579,7 @@ Init_prism(void) {
rb_cPrismToken = rb_define_class_under(rb_cPrism, "Token", rb_cObject);
rb_cPrismLocation = rb_define_class_under(rb_cPrism, "Location", rb_cObject);
rb_cPrismComment = rb_define_class_under(rb_cPrism, "Comment", rb_cObject);
rb_cPrismMagicComment = rb_define_class_under(rb_cPrism, "MagicComment", rb_cObject);
rb_cPrismParseError = rb_define_class_under(rb_cPrism, "ParseError", rb_cObject);
rb_cPrismParseWarning = rb_define_class_under(rb_cPrism, "ParseWarning", rb_cObject);
rb_cPrismParseResult = rb_define_class_under(rb_cPrism, "ParseResult", rb_cObject);

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

@ -250,6 +250,16 @@ typedef struct pm_comment {
pm_comment_type_t type;
} pm_comment_t;
// This is a node in the linked list of magic comments that we've found while
// parsing.
typedef struct {
pm_list_node_t node;
const uint8_t *key_start;
const uint8_t *value_start;
uint32_t key_length;
uint32_t value_length;
} pm_magic_comment_t;
// When the encoding that is being used to parse the source is changed by prism,
// we provide the ability here to call out to a user-defined function.
typedef void (*pm_encoding_changed_callback_t)(pm_parser_t *parser);
@ -353,6 +363,7 @@ struct pm_parser {
const uint8_t *heredoc_end;
pm_list_t comment_list; // the list of comments that have been found while parsing
pm_list_t magic_comment_list; // the list of magic comments that have been found while parsing.
pm_list_t warning_list; // the list of warnings that have been found while parsing
pm_list_t error_list; // the list of errors that have been found while parsing
pm_scope_t *current_scope; // the current local scope

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

@ -5448,6 +5448,16 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
// When we're done, we want to free the string in case we had to
// allocate memory for it.
pm_string_free(&key);
// Allocate a new magic comment node to append to the parser's list.
pm_magic_comment_t *magic_comment;
if ((magic_comment = (pm_magic_comment_t *) malloc(sizeof(pm_magic_comment_t))) != NULL) {
magic_comment->key_start = key_start;
magic_comment->value_start = value_start;
magic_comment->key_length = (uint32_t) key_length;
magic_comment->value_length = (uint32_t) (value_end - value_start);
pm_list_append(&parser->magic_comment_list, (pm_list_node_t *) magic_comment);
}
}
}
@ -15262,6 +15272,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const ch
.next_start = NULL,
.heredoc_end = NULL,
.comment_list = PM_LIST_EMPTY,
.magic_comment_list = PM_LIST_EMPTY,
.warning_list = PM_LIST_EMPTY,
.error_list = PM_LIST_EMPTY,
.current_scope = NULL,
@ -15356,6 +15367,19 @@ pm_comment_list_free(pm_list_t *list) {
}
}
// Free all of the memory associated with the magic comment list.
static inline void
pm_magic_comment_list_free(pm_list_t *list) {
pm_list_node_t *node, *next;
for (node = list->head; node != NULL; node = next) {
next = node->next;
pm_magic_comment_t *magic_comment = (pm_magic_comment_t *) node;
free(magic_comment);
}
}
// Free any memory associated with the given parser.
PRISM_EXPORTED_FUNCTION void
pm_parser_free(pm_parser_t *parser) {
@ -15363,6 +15387,7 @@ pm_parser_free(pm_parser_t *parser) {
pm_diagnostic_list_free(&parser->error_list);
pm_diagnostic_list_free(&parser->warning_list);
pm_comment_list_free(&parser->comment_list);
pm_magic_comment_list_free(&parser->magic_comment_list);
pm_constant_pool_free(&parser->constant_pool);
pm_newline_list_free(&parser->newline_list);

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

@ -55,9 +55,10 @@ module Prism
def load_metadata
comments = load_varint.times.map { Comment.new(Comment::TYPES.fetch(load_varint), load_location) }
magic_comments = load_varint.times.map { MagicComment.new(load_location, load_location) }
errors = load_varint.times.map { ParseError.new(load_embedded_string, load_location) }
warnings = load_varint.times.map { ParseWarning.new(load_embedded_string, load_location) }
[comments, errors, warnings]
[comments, magic_comments, errors, warnings]
end
def load_tokens
@ -76,14 +77,14 @@ module Prism
def load_tokens_result
tokens = load_tokens
encoding = load_encoding
comments, errors, warnings = load_metadata
comments, magic_comments, errors, warnings = load_metadata
if encoding != @encoding
tokens.each { |token,| token.value.force_encoding(encoding) }
end
raise "Expected to consume all bytes while deserializing" unless @io.eof?
Prism::ParseResult.new(tokens, comments, errors, warnings, @source)
Prism::ParseResult.new(tokens, comments, magic_comments, errors, warnings, @source)
end
def load_nodes
@ -97,17 +98,17 @@ module Prism
@encoding = load_encoding
@input = input.force_encoding(@encoding).freeze
comments, errors, warnings = load_metadata
comments, magic_comments, errors, warnings = load_metadata
@constant_pool_offset = io.read(4).unpack1("L")
@constant_pool = Array.new(load_varint, nil)
[load_node, comments, errors, warnings]
[load_node, comments, magic_comments, errors, warnings]
end
def load_result
node, comments, errors, warnings = load_nodes
Prism::ParseResult.new(node, comments, errors, warnings, @source)
node, comments, magic_comments, errors, warnings = load_nodes
Prism::ParseResult.new(node, comments, magic_comments, errors, warnings, @source)
end
private

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

@ -146,6 +146,27 @@ pm_serialize_comment_list(pm_parser_t *parser, pm_list_t *list, pm_buffer_t *buf
}
}
static void
pm_serialize_magic_comment(pm_parser_t *parser, pm_magic_comment_t *magic_comment, pm_buffer_t *buffer) {
// serialize key location
pm_buffer_append_u32(buffer, pm_ptrdifft_to_u32(magic_comment->key_start - parser->start));
pm_buffer_append_u32(buffer, pm_ptrdifft_to_u32(magic_comment->key_length));
// serialize value location
pm_buffer_append_u32(buffer, pm_ptrdifft_to_u32(magic_comment->value_start - parser->start));
pm_buffer_append_u32(buffer, pm_ptrdifft_to_u32(magic_comment->value_length));
}
static void
pm_serialize_magic_comment_list(pm_parser_t *parser, pm_list_t *list, pm_buffer_t *buffer) {
pm_buffer_append_u32(buffer, pm_sizet_to_u32(pm_list_size(list)));
pm_magic_comment_t *magic_comment;
for (magic_comment = (pm_magic_comment_t *) list->head; magic_comment != NULL; magic_comment = (pm_magic_comment_t *) magic_comment->node.next) {
pm_serialize_magic_comment(parser, magic_comment, buffer);
}
}
static void
pm_serialize_diagnostic(pm_parser_t *parser, pm_diagnostic_t *diagnostic, pm_buffer_t *buffer) {
// serialize message
@ -180,6 +201,7 @@ void
pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) {
pm_serialize_encoding(&parser->encoding, buffer);
pm_serialize_comment_list(parser, &parser->comment_list, buffer);
pm_serialize_magic_comment_list(parser, &parser->magic_comment_list, buffer);
pm_serialize_diagnostic_list(parser, &parser->error_list, buffer);
pm_serialize_diagnostic_list(parser, &parser->warning_list, buffer);
@ -268,6 +290,7 @@ pm_lex_serialize(const uint8_t *source, size_t size, const char *filepath, pm_bu
pm_serialize_encoding(&parser.encoding, buffer);
pm_serialize_comment_list(&parser, &parser.comment_list, buffer);
pm_serialize_magic_comment_list(&parser, &parser.magic_comment_list, buffer);
pm_serialize_diagnostic_list(&parser, &parser.error_list, buffer);
pm_serialize_diagnostic_list(&parser, &parser.warning_list, buffer);