Skip to content

When reading from stdin, put a wrapper around the IO object #13944

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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 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
19 changes: 13 additions & 6 deletions prism/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -22830,7 +22830,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 @@ -22866,6 +22866,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 @@ -22901,16 +22907,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 @@ -23002,13 +23009,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
12 changes: 10 additions & 2 deletions prism/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 @@ -113,7 +121,7 @@ PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buff
* @param stream_fgets The function to use to read from the stream.
* @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
30 changes: 28 additions & 2 deletions prism_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -11503,6 +11503,18 @@ pm_parse_string(pm_parse_result_t *result, VALUE source, VALUE filepath, VALUE *
return pm_parse_process(result, node, script_lines);
}

struct rb_stdin_wrapper {
VALUE rb_stdin;
int eof_seen;
};

static int
pm_parse_stdin_eof(void *stream)
{
struct rb_stdin_wrapper * wrapped_stdin = (struct rb_stdin_wrapper *)stream;
return wrapped_stdin->eof_seen;
}

/**
* An implementation of fgets that is suitable for use with Ruby IO objects.
*/
Expand All @@ -11511,7 +11523,9 @@ pm_parse_stdin_fgets(char *string, int size, void *stream)
{
RUBY_ASSERT(size > 0);

VALUE line = rb_funcall((VALUE) stream, rb_intern("gets"), 1, INT2FIX(size - 1));
struct rb_stdin_wrapper * wrapped_stdin = (struct rb_stdin_wrapper *)stream;

VALUE line = rb_funcall(wrapped_stdin->rb_stdin, rb_intern("gets"), 1, INT2FIX(size - 1));
if (NIL_P(line)) {
return NULL;
}
Expand All @@ -11522,6 +11536,13 @@ pm_parse_stdin_fgets(char *string, int size, void *stream)
memcpy(string, cstr, length);
string[length] = '\0';

// We're reading strings from stdin via gets. We'll assume that if the
// string is smaller than the requested length, and doesn't end with a
// newline, that we hit EOF.
if (length < (size - 1) && string[length - 1] != '\n') {
wrapped_stdin->eof_seen = 1;
}

return string;
}

Expand All @@ -11538,8 +11559,13 @@ pm_parse_stdin(pm_parse_result_t *result)
{
pm_options_frozen_string_literal_init(&result->options);

struct rb_stdin_wrapper wrapped_stdin = {
rb_stdin,
0
};

pm_buffer_t buffer;
pm_node_t *node = pm_parse_stream(&result->parser, &buffer, (void *) rb_stdin, pm_parse_stdin_fgets, &result->options);
pm_node_t *node = pm_parse_stream(&result->parser, &buffer, (void *) &wrapped_stdin, pm_parse_stdin_fgets, pm_parse_stdin_eof, &result->options);

// Copy the allocated buffer contents into the input string so that it gets
// freed. At this point we've handed over ownership, so we don't need to
Expand Down
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy