Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion ext/prism/extension.c
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,14 @@ profile_file(int argc, VALUE *argv, VALUE self) {
return Qnil;
}

static int
parse_stream_eof(void *stream) {
if (rb_funcall((VALUE) stream, rb_intern("eof?"), 0)) {
return 1;
}
return 0;
}

/**
* An implementation of fgets that is suitable for use with Ruby IO objects.
*/
Expand Down Expand Up @@ -1034,7 +1042,7 @@ parse_stream(int argc, VALUE *argv, VALUE self) {
pm_parser_t parser;
pm_buffer_t buffer;

pm_node_t *node = pm_parse_stream(&parser, &buffer, (void *) stream, parse_stream_fgets, &options);
pm_node_t *node = pm_parse_stream(&parser, &buffer, (void *) stream, parse_stream_fgets, parse_stream_eof, &options);
rb_encoding *encoding = rb_enc_find(parser.encoding->name);

VALUE source = pm_source_new(&parser, encoding, options.freeze);
Expand Down
13 changes: 11 additions & 2 deletions include/prism.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,25 @@ PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse(pm_parser_t *parser);
*/
typedef char * (pm_parse_stream_fgets_t)(char *string, int size, void *stream);

/**
* This function is used in pm_parse_stream to check whether a stream is EOF.
* It closely mirrors that of feof so that feof can be used as the
* default implementation.
*/
typedef int (pm_parse_stream_feof_t)(void *stream);

/**
* Parse a stream of Ruby source and return the tree.
*
* @param parser The parser to use.
* @param buffer The buffer to use.
* @param stream The stream to parse.
* @param stream_fgets The function to use to read from the stream.
* @param stream_feof The function to use to determine if the stream has hit eof.
* @param options The optional options to use when parsing.
* @return The AST representing the source.
*/
PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, const pm_options_t *options);
PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, const pm_options_t *options);

// We optionally support serializing to a binary string. For systems that don't
// want or need this functionality, it can be turned off with the
Expand All @@ -111,9 +119,10 @@ PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buff
* @param buffer The buffer to serialize to.
* @param stream The stream to parse.
* @param stream_fgets The function to use to read from the stream.
* @param stream_feof The function to use to tell if the stream has hit eof.
* @param data The optional data to pass to the parser.
*/
PRISM_EXPORTED_FUNCTION void pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, const char *data);
PRISM_EXPORTED_FUNCTION void pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, const char *data);

/**
* Serialize the given list of comments to the given buffer.
Expand Down
7 changes: 5 additions & 2 deletions lib/prism/ffi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def self.load_exported_functions_from(header, *functions, callbacks)
end

callback :pm_parse_stream_fgets_t, [:pointer, :int, :pointer], :pointer
callback :pm_parse_stream_feof_t, [:pointer], :int
enum :pm_string_init_result_t, %i[PM_STRING_INIT_SUCCESS PM_STRING_INIT_ERROR_GENERIC PM_STRING_INIT_ERROR_DIRECTORY]
enum :pm_string_query_t, [:PM_STRING_QUERY_ERROR, -1, :PM_STRING_QUERY_FALSE, :PM_STRING_QUERY_TRUE]

Expand All @@ -101,7 +102,7 @@ def self.load_exported_functions_from(header, *functions, callbacks)
"pm_string_query_local",
"pm_string_query_constant",
"pm_string_query_method_name",
[:pm_parse_stream_fgets_t]
[:pm_parse_stream_fgets_t, :pm_parse_stream_feof_t]
)

load_exported_functions_from(
Expand Down Expand Up @@ -281,12 +282,14 @@ def parse_stream(stream, **options)
end
}

eof_callback = -> (_) { stream.eof? }

# In the pm_serialize_parse_stream function it accepts a pointer to the
# IO object as a void* and then passes it through to the callback as the
# third argument, but it never touches it itself. As such, since we have
# access to the IO object already through the closure of the lambda, we
# can pass a null pointer here and not worry.
LibRubyParser.pm_serialize_parse_stream(buffer.pointer, nil, callback, dump_options(options))
LibRubyParser.pm_serialize_parse_stream(buffer.pointer, nil, callback, eof_callback, dump_options(options))
Prism.load(source, buffer.read, options.fetch(:freeze, false))
end
end
Expand Down
19 changes: 13 additions & 6 deletions src/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -22842,7 +22842,7 @@ pm_parse(pm_parser_t *parser) {
* otherwise return true.
*/
static bool
pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets) {
pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof) {
#define LINE_SIZE 4096
char line[LINE_SIZE];

Expand Down Expand Up @@ -22878,6 +22878,12 @@ pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t
if (strncmp(line, "__END__\r\n", 9) == 0) return false;
break;
}

// All data should be read via gets. If the string returned by gets
// _doesn't_ end with a newline, then we assume we hit EOF condition.
if (stream_feof(stream)) {
break;
}
}

return true;
Expand Down Expand Up @@ -22913,16 +22919,17 @@ pm_parse_stream_unterminated_heredoc_p(pm_parser_t *parser) {
* can stream stdin in to Ruby so we need to support a streaming API.
*/
PRISM_EXPORTED_FUNCTION pm_node_t *
pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, const pm_options_t *options) {
pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, const pm_options_t *options) {
pm_buffer_init(buffer);

bool eof = pm_parse_stream_read(buffer, stream, stream_fgets);
bool eof = pm_parse_stream_read(buffer, stream, stream_fgets, stream_feof);

pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options);
pm_node_t *node = pm_parse(parser);

while (!eof && parser->error_list.size > 0 && (parser->lex_modes.index > 0 || pm_parse_stream_unterminated_heredoc_p(parser))) {
pm_node_destroy(parser, node);
eof = pm_parse_stream_read(buffer, stream, stream_fgets);
eof = pm_parse_stream_read(buffer, stream, stream_fgets, stream_feof);

pm_parser_free(parser);
pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options);
Expand Down Expand Up @@ -23014,13 +23021,13 @@ pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, cons
* given stream into to the given buffer.
*/
PRISM_EXPORTED_FUNCTION void
pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, const char *data) {
pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, const char *data) {
pm_parser_t parser;
pm_options_t options = { 0 };
pm_options_read(&options, data);

pm_buffer_t parser_buffer;
pm_node_t *node = pm_parse_stream(&parser, &parser_buffer, stream, stream_fgets, &options);
pm_node_t *node = pm_parse_stream(&parser, &parser_buffer, stream, stream_fgets, stream_feof, &options);
pm_serialize_header(buffer);
pm_serialize_content(&parser, node, buffer);
pm_buffer_append_byte(buffer, '\0');
Expand Down
Loading