diff options
-rw-r--r-- | lib/prism/ffi.rb | 29 | ||||
-rw-r--r-- | prism/extension.c | 63 | ||||
-rw-r--r-- | prism/options.c | 35 | ||||
-rw-r--r-- | prism/options.h | 78 | ||||
-rw-r--r-- | prism/parser.h | 27 | ||||
-rw-r--r-- | prism/prism.c | 20 | ||||
-rw-r--r-- | test/prism/command_line_test.rb | 8 |
7 files changed, 117 insertions, 143 deletions
diff --git a/lib/prism/ffi.rb b/lib/prism/ffi.rb index 347c35d26f..2aecd4df86 100644 --- a/lib/prism/ffi.rb +++ b/lib/prism/ffi.rb @@ -313,6 +313,24 @@ module Prism LibRubyParser.pm_parse_success_p(string.pointer, string.length, dump_options(options)) end + # Return the value that should be dumped for the command_line option. + def dump_options_command_line(options) + command_line = options.fetch(:command_line, "") + raise ArgumentError, "command_line must be a string" unless command_line.is_a?(String) + + command_line.each_char.inject(0) do |value, char| + case char + when "a" then value | 0b000001 + when "e" then value | 0b000010 + when "l" then value | 0b000100 + when "n" then value | 0b001000 + when "p" then value | 0b010000 + when "x" then value | 0b100000 + else raise ArgumentError, "invalid command_line option: #{char}" + end + end + end + # Convert the given options into a serialized options string. def dump_options(options) template = +"" @@ -342,16 +360,7 @@ module Prism values << (options.fetch(:frozen_string_literal, false) ? 1 : 0) template << "C" - values << (options.fetch(:command_line_p, false) ? 1 : 0) - - template << "C" - values << (options.fetch(:command_line_n, false) ? 1 : 0) - - template << "C" - values << (options.fetch(:command_line_l, false) ? 1 : 0) - - template << "C" - values << (options.fetch(:command_line_a, false) ? 1 : 0) + values << dump_options_command_line(options) template << "C" values << { nil => 0, "3.3.0" => 1, "3.4.0" => 0, "latest" => 0 }.fetch(options[:version]) diff --git a/prism/extension.c b/prism/extension.c index ed56a6c513..81a60e44a9 100644 --- a/prism/extension.c +++ b/prism/extension.c @@ -29,10 +29,7 @@ ID rb_option_id_line; ID rb_option_id_frozen_string_literal; ID rb_option_id_version; ID rb_option_id_scopes; -ID rb_option_id_command_line_p; -ID rb_option_id_command_line_n; -ID rb_option_id_command_line_l; -ID rb_option_id_command_line_a; +ID rb_option_id_command_line; /******************************************************************************/ /* IO of Ruby code */ @@ -148,21 +145,32 @@ build_options_i(VALUE key, VALUE value, VALUE argument) { const char *version = check_string(value); if (!pm_options_version_set(options, version, RSTRING_LEN(value))) { - rb_raise(rb_eArgError, "invalid version: %"PRIsVALUE, value); + rb_raise(rb_eArgError, "invalid version: %" PRIsVALUE, value); } } } else if (key_id == rb_option_id_scopes) { if (!NIL_P(value)) build_options_scopes(options, value); - } else if (key_id == rb_option_id_command_line_p) { - if (!NIL_P(value)) pm_options_command_line_p_set(options, value == Qtrue); - } else if (key_id == rb_option_id_command_line_n) { - if (!NIL_P(value)) pm_options_command_line_n_set(options, value == Qtrue); - } else if (key_id == rb_option_id_command_line_l) { - if (!NIL_P(value)) pm_options_command_line_l_set(options, value == Qtrue); - } else if (key_id == rb_option_id_command_line_a) { - if (!NIL_P(value)) pm_options_command_line_a_set(options, value == Qtrue); + } else if (key_id == rb_option_id_command_line) { + if (!NIL_P(value)) { + const char *string = check_string(value); + uint8_t command_line = 0; + + for (size_t index = 0; index < strlen(string); index++) { + switch (string[index]) { + case 'a': command_line |= PM_OPTIONS_COMMAND_LINE_A; break; + case 'e': command_line |= PM_OPTIONS_COMMAND_LINE_E; break; + case 'l': command_line |= PM_OPTIONS_COMMAND_LINE_L; break; + case 'n': command_line |= PM_OPTIONS_COMMAND_LINE_N; break; + case 'p': command_line |= PM_OPTIONS_COMMAND_LINE_P; break; + case 'x': command_line |= PM_OPTIONS_COMMAND_LINE_X; break; + default: rb_raise(rb_eArgError, "invalid command line flag: '%c'", string[index]); break; + } + } + + pm_options_command_line_set(options, command_line); + } } else { - rb_raise(rb_eArgError, "unknown keyword: %"PRIsVALUE, key); + rb_raise(rb_eArgError, "unknown keyword: %" PRIsVALUE, key); } return ST_CONTINUE; @@ -697,21 +705,25 @@ parse_input(pm_string_t *input, const pm_options_t *options) { * Parse the given string and return a ParseResult instance. The options that * are supported are: * - * * `filepath` - the filepath of the source being parsed. This should be a - * string or nil + * * `command_line` - either nil or a string of the various options that were + * set on the command line. Valid values are combinations of "a", "l", + * "n", "p", and "x". * * `encoding` - the encoding of the source being parsed. This should be an - * encoding or nil - * * `line` - the line number that the parse starts on. This should be an - * integer or nil. Note that this is 1-indexed. + * encoding or nil. + * * `filepath` - the filepath of the source being parsed. This should be a + * string or nil. * * `frozen_string_literal` - whether or not the frozen string literal pragma * has been set. This should be a boolean or nil. - * * `version` - the version of prism that should be used to parse Ruby code. By - * default prism assumes you want to parse with the latest vesion of - * prism (which you can trigger with `nil` or `"latest"`). If you want to - * parse exactly as CRuby 3.3.0 would, then you can pass `"3.3.0"`. + * * `line` - the line number that the parse starts on. This should be an + * integer or nil. Note that this is 1-indexed. * * `scopes` - the locals that are in scope surrounding the code that is being * parsed. This should be an array of arrays of symbols or nil. Scopes are * ordered from the outermost scope to the innermost one. + * * `version` - the version of Ruby syntax that prism should used to parse Ruby + * code. By default prism assumes you want to parse with the latest vesion + * of Ruby syntax (which you can trigger with `nil` or `"latest"`). You + * may also restrict the syntax to a specific version of Ruby. The + * supported values are `"3.3.0"` and `"3.4.0"`. */ static VALUE parse(int argc, VALUE *argv, VALUE self) { @@ -1244,10 +1256,7 @@ Init_prism(void) { rb_option_id_frozen_string_literal = rb_intern_const("frozen_string_literal"); rb_option_id_version = rb_intern_const("version"); rb_option_id_scopes = rb_intern_const("scopes"); - rb_option_id_command_line_p = rb_intern_const("command_line_p"); - rb_option_id_command_line_n = rb_intern_const("command_line_n"); - rb_option_id_command_line_l = rb_intern_const("command_line_l"); - rb_option_id_command_line_a = rb_intern_const("command_line_a"); + rb_option_id_command_line = rb_intern_const("command_line"); /** * The version of the prism library. diff --git a/prism/options.c b/prism/options.c index 54fd1bc014..ca0f150107 100644 --- a/prism/options.c +++ b/prism/options.c @@ -33,35 +33,11 @@ pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_l } /** - * Sets the -p command line option on the given options struct. + * Sets the command line option on the given options struct. */ PRISM_EXPORTED_FUNCTION void -pm_options_command_line_p_set(pm_options_t *options, bool command_line_p) { - options->command_line_p = command_line_p; -} - -/** - * Sets the -n command line option on the given options struct. - */ -PRISM_EXPORTED_FUNCTION void -pm_options_command_line_n_set(pm_options_t *options, bool command_line_n) { - options->command_line_n = command_line_n; -} - -/** - * Sets the -l command line option on the given options struct. - */ -PRISM_EXPORTED_FUNCTION void -pm_options_command_line_l_set(pm_options_t *options, bool command_line_l) { - options->command_line_l = command_line_l; -} - -/** - * Sets the -a command line option on the given options struct. - */ -PRISM_EXPORTED_FUNCTION void -pm_options_command_line_a_set(pm_options_t *options, bool command_line_a) { - options->command_line_a = command_line_a; +pm_options_command_line_set(pm_options_t *options, uint8_t command_line) { + options->command_line = command_line; } /** @@ -226,10 +202,7 @@ pm_options_read(pm_options_t *options, const char *data) { } options->frozen_string_literal = (*data++) ? true : false; - options->command_line_p = (*data++) ? true : false; - options->command_line_n = (*data++) ? true : false; - options->command_line_l = (*data++) ? true : false; - options->command_line_a = (*data++) ? true : false; + options->command_line = (uint8_t) *data++; options->version = (pm_options_version_t) *data++; uint32_t scopes_count = pm_options_read_u32(data); diff --git a/prism/options.h b/prism/options.h index 4a60fc266a..ce979656a5 100644 --- a/prism/options.h +++ b/prism/options.h @@ -76,21 +76,49 @@ typedef struct { */ pm_options_version_t version; + /** A bitset of the various options that were set on the command line. */ + uint8_t command_line; + /** Whether or not the frozen string literal option has been set. */ bool frozen_string_literal; +} pm_options_t; - /** Whether or not the -p command line option has been set. */ - bool command_line_p; +/** + * A bit representing whether or not the command line -a option was set. -a + * splits the input line $_ into $F. + */ +static const uint8_t PM_OPTIONS_COMMAND_LINE_A = 0x1; - /** Whether or not the -n command line option has been set. */ - bool command_line_n; +/** + * A bit representing whether or not the command line -e option was set. -e + * allow the user to specify a script to be executed. This is necessary for + * prism to know because certain warnings are not generated when -e is used. + */ +static const uint8_t PM_OPTIONS_COMMAND_LINE_E = 0x2; - /** Whether or not the -l command line option has been set. */ - bool command_line_l; +/** + * A bit representing whether or not the command line -l option was set. -l + * chomps the input line by default. + */ +static const uint8_t PM_OPTIONS_COMMAND_LINE_L = 0x4; - /** Whether or not the -a command line option has been set. */ - bool command_line_a; -} pm_options_t; +/** + * A bit representing whether or not the command line -n option was set. -n + * wraps the script in a while gets loop. + */ +static const uint8_t PM_OPTIONS_COMMAND_LINE_N = 0x8; + +/** + * A bit representing whether or not the command line -p option was set. -p + * prints the value of $_ at the end of each loop. + */ +static const uint8_t PM_OPTIONS_COMMAND_LINE_P = 0x10; + +/** + * A bit representing whether or not the command line -x option was set. -x + * searches the input file for a shebang that matches the current Ruby engine. + */ +static const uint8_t PM_OPTIONS_COMMAND_LINE_X = 0x20; /** * Set the filepath option on the given options struct. @@ -125,36 +153,12 @@ PRISM_EXPORTED_FUNCTION void pm_options_encoding_set(pm_options_t *options, cons PRISM_EXPORTED_FUNCTION void pm_options_frozen_string_literal_set(pm_options_t *options, bool frozen_string_literal); /** - * Sets the -p command line option on the given options struct. - * - * @param options The options struct to set the -p command line option on. - * @param command_line_p The -p command line option to set. - */ -PRISM_EXPORTED_FUNCTION void pm_options_command_line_p_set(pm_options_t *options, bool command_line_p); - -/** - * Sets the -n command line option on the given options struct. - * - * @param options The options struct to set the -n command line option on. - * @param command_line_n The -n command line option to set. - */ -PRISM_EXPORTED_FUNCTION void pm_options_command_line_n_set(pm_options_t *options, bool command_line_n); - -/** - * Sets the -l command line option on the given options struct. - * - * @param options The options struct to set the -l command line option on. - * @param command_line_l The -l command line option to set. - */ -PRISM_EXPORTED_FUNCTION void pm_options_command_line_l_set(pm_options_t *options, bool command_line_l); - -/** - * Sets the -a command line option on the given options struct. + * Sets the command line option on the given options struct. * - * @param options The options struct to set the -a command line option on. - * @param command_line_a The -a command line option to set. + * @param options The options struct to set the command line option on. + * @param command_line The command_line value to set. */ -PRISM_EXPORTED_FUNCTION void pm_options_command_line_a_set(pm_options_t *options, bool command_line_a); +PRISM_EXPORTED_FUNCTION void pm_options_command_line_set(pm_options_t *options, uint8_t command_line); /** * Set the version option on the given options struct by parsing the given diff --git a/prism/parser.h b/prism/parser.h index b790489d7d..80521e4ad9 100644 --- a/prism/parser.h +++ b/prism/parser.h @@ -703,6 +703,9 @@ struct pm_parser { /** The version of prism that we should use to parse. */ pm_options_version_t version; + /** The command line flags given from the options. */ + uint8_t command_line; + /** Whether or not we're at the beginning of a command. */ bool command_start; @@ -736,30 +739,6 @@ struct pm_parser { * a true value. */ bool frozen_string_literal; - - /** - * Whether or not -p was present on the command line that invoked the - * parser. -p prints the value of $_ at the end of each loop. - */ - bool command_line_p; - - /** - * Whether or not -n was present on the command line that invoked the - * parser. -n wraps the script in a while gets loop. - */ - bool command_line_n; - - /** - * Whether or not -l was present on the command line that invoked the - * parser. -l chomps the input line by default. - */ - bool command_line_l; - - /** - * Whether or not -a was present on the command line that invoked the - * parser. -a splits the input line $_ into $F. - */ - bool command_line_a; }; #endif diff --git a/prism/prism.c b/prism/prism.c index d6a8d50680..7394786162 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -18273,7 +18273,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc */ static pm_statements_node_t * wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { - if (parser->command_line_p) { + if (parser->command_line & PM_OPTIONS_COMMAND_LINE_P) { pm_arguments_node_t *arguments = pm_arguments_node_create(parser); pm_arguments_node_arguments_append( arguments, @@ -18287,8 +18287,8 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { )); } - if (parser->command_line_n) { - if (parser->command_line_a) { + if (parser->command_line & PM_OPTIONS_COMMAND_LINE_N) { + if (parser->command_line & PM_OPTIONS_COMMAND_LINE_A) { pm_arguments_node_t *arguments = pm_arguments_node_create(parser); pm_arguments_node_arguments_append( arguments, @@ -18313,7 +18313,7 @@ wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) { (pm_node_t *) pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$/", 2)) ); - if (parser->command_line_l) { + if (parser->command_line & PM_OPTIONS_COMMAND_LINE_L) { pm_keyword_hash_node_t *keywords = pm_keyword_hash_node_create(parser); pm_keyword_hash_node_elements_append(keywords, (pm_node_t *) pm_assoc_node_create( parser, @@ -18367,7 +18367,7 @@ parse_program(pm_parser_t *parser) { // At the top level, see if we need to wrap the statements in a program // node with a while loop based on the options. - if (parser->command_line_p || parser->command_line_n) { + if (parser->command_line & (PM_OPTIONS_COMMAND_LINE_P | PM_OPTIONS_COMMAND_LINE_N)) { statements = wrap_statements(parser, statements); } @@ -18421,6 +18421,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm .current_string = PM_STRING_EMPTY, .start_line = 1, .explicit_encoding = NULL, + .command_line = 0, .command_start = true, .recovering = false, .encoding_changed = false, @@ -18428,11 +18429,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm .in_keyword_arg = false, .current_param_name = 0, .semantic_token_seen = false, - .frozen_string_literal = false, - .command_line_p = options != NULL && options->command_line_p, - .command_line_n = options != NULL && options->command_line_n, - .command_line_l = options != NULL && options->command_line_l, - .command_line_a = options != NULL && options->command_line_a + .frozen_string_literal = false }; // Initialize the constant pool. We're going to completely guess as to the @@ -18478,6 +18475,9 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm parser->frozen_string_literal = true; } + // command_line option + parser->command_line = options->command_line; + // version option parser->version = options->version; diff --git a/test/prism/command_line_test.rb b/test/prism/command_line_test.rb index f83110a612..f5438bd0ad 100644 --- a/test/prism/command_line_test.rb +++ b/test/prism/command_line_test.rb @@ -5,7 +5,7 @@ require_relative "test_helper" module Prism class CommandLineTest < TestCase def test_command_line_p - program = Prism.parse("1", command_line_p: true).value + program = Prism.parse("1", command_line: "p").value statements = program.statements.body assert_equal 2, statements.length @@ -14,7 +14,7 @@ module Prism end def test_command_line_n - program = Prism.parse("1", command_line_n: true).value + program = Prism.parse("1", command_line: "n").value statements = program.statements.body assert_equal 1, statements.length @@ -30,7 +30,7 @@ module Prism end def test_command_line_a - program = Prism.parse("1", command_line_n: true, command_line_a: true).value + program = Prism.parse("1", command_line: "na").value statements = program.statements.body assert_equal 1, statements.length @@ -42,7 +42,7 @@ module Prism end def test_command_line_l - program = Prism.parse("1", command_line_n: true, command_line_l: true).value + program = Prism.parse("1", command_line: "nl").value statements = program.statements.body assert_equal 1, statements.length |