diff --git a/include/prism/options.h b/include/prism/options.h index 2f64701b0c..092fda4f07 100644 --- a/include/prism/options.h +++ b/include/prism/options.h @@ -82,14 +82,20 @@ typedef void (*pm_options_shebang_callback_t)(struct pm_options *options, const * parse in the same way as a specific version of CRuby would have. */ typedef enum { - /** The current version of prism. */ - PM_OPTIONS_VERSION_LATEST = 0, + /** If an explicit version is not provided, the current version of prism will be used. */ + PM_OPTIONS_VERSION_UNSET = 0, /** The vendored version of prism in CRuby 3.3.x. */ PM_OPTIONS_VERSION_CRUBY_3_3 = 1, /** The vendored version of prism in CRuby 3.4.x. */ - PM_OPTIONS_VERSION_CRUBY_3_4 = 2 + PM_OPTIONS_VERSION_CRUBY_3_4 = 2, + + /** The vendored version of prism in CRuby 3.5.x. */ + PM_OPTIONS_VERSION_CRUBY_3_5 = 3, + + /** The current version of prism. */ + PM_OPTIONS_VERSION_LATEST = PM_OPTIONS_VERSION_CRUBY_3_5 } pm_options_version_t; /** diff --git a/java/org/prism/ParsingOptions.java b/java/org/prism/ParsingOptions.java index d4652f069a..4aff7dbe35 100644 --- a/java/org/prism/ParsingOptions.java +++ b/java/org/prism/ParsingOptions.java @@ -12,9 +12,10 @@ public abstract class ParsingOptions { * See pm_options_version_t in include/prism/options.h. */ public enum SyntaxVersion { - LATEST(0), + LATEST(0), // Handled in pm_parser_init V3_3(1), - V3_4(2); + V3_4(2), + V3_5(3); private final int value; diff --git a/javascript/src/parsePrism.js b/javascript/src/parsePrism.js index 535e6e9a0f..d021c63a9b 100644 --- a/javascript/src/parsePrism.js +++ b/javascript/src/parsePrism.js @@ -140,12 +140,14 @@ function dumpOptions(options) { values.push(dumpCommandLineOptions(options)); template.push("C"); - if (!options.version || options.version === "latest" || options.version.match(/^3\.5(\.\d+)?$/)) { - values.push(0); + if (!options.version || options.version === "latest") { + values.push(0); // Handled in pm_parser_init } else if (options.version.match(/^3\.3(\.\d+)?$/)) { values.push(1); } else if (options.version.match(/^3\.4(\.\d+)?$/)) { values.push(2); + } else if (options.version.match(/^3\.5(\.\d+)?$/)) { + values.push(3); } else { throw new Error(`Unsupported version '${options.version}' in compiler options`); } diff --git a/lib/prism/ffi.rb b/lib/prism/ffi.rb index 5a4ba09a4f..1e1bf8b1c8 100644 --- a/lib/prism/ffi.rb +++ b/lib/prism/ffi.rb @@ -422,13 +422,13 @@ def dump_options_command_line(options) def dump_options_version(version) case version when nil, "latest" - 0 + 0 # Handled in pm_parser_init when /\A3\.3(\.\d+)?\z/ 1 when /\A3\.4(\.\d+)?\z/ 2 when /\A3\.5(\.\d+)?\z/ - 0 + 3 else raise ArgumentError, "invalid version: #{version}" end diff --git a/src/options.c b/src/options.c index a457178ce8..1b5c022cf5 100644 --- a/src/options.c +++ b/src/options.c @@ -89,7 +89,7 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length } if (strncmp(version, "3.5", 3) == 0) { - options->version = PM_OPTIONS_VERSION_LATEST; + options->version = PM_OPTIONS_VERSION_CRUBY_3_5; return true; } @@ -108,7 +108,7 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length } if (strncmp(version, "3.5.", 4) == 0 && is_number(version + 4, length - 4)) { - options->version = PM_OPTIONS_VERSION_LATEST; + options->version = PM_OPTIONS_VERSION_CRUBY_3_5; return true; } } diff --git a/src/prism.c b/src/prism.c index 8707330c95..e071199590 100644 --- a/src/prism.c +++ b/src/prism.c @@ -1409,7 +1409,7 @@ pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) { static inline void pm_conditional_predicate_warn_write_literal(pm_parser_t *parser, const pm_node_t *node) { if (pm_conditional_predicate_warn_write_literal_p(node)) { - pm_parser_warn_node(parser, node, parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_WARN_EQUAL_IN_CONDITIONAL_3_3 : PM_WARN_EQUAL_IN_CONDITIONAL); + pm_parser_warn_node(parser, node, parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_WARN_EQUAL_IN_CONDITIONAL_3_3 : PM_WARN_EQUAL_IN_CONDITIONAL); } } @@ -2976,7 +2976,7 @@ pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const */ static void pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *arguments, const pm_node_t *block) { - if (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) { + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) { if (arguments != NULL && PM_NODE_FLAG_P(arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) { pm_node_t *node; PM_NODE_LIST_FOREACH(&arguments->arguments, index, node) { @@ -9094,7 +9094,7 @@ lex_global_variable(pm_parser_t *parser) { } while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0); // $0 isn't allowed to be followed by anything. - pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; + pm_diagnostic_id_t diag_id = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, diag_id); } @@ -9131,7 +9131,7 @@ lex_global_variable(pm_parser_t *parser) { } else { // If we get here, then we have a $ followed by something that // isn't recognized as a global variable. - pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; + pm_diagnostic_id_t diag_id = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; const uint8_t *end = parser->current.end + parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); PM_PARSER_ERR_FORMAT(parser, parser->current.start, end, diag_id, (int) (end - parser->current.start), (const char *) parser->current.start); } @@ -10158,7 +10158,7 @@ lex_at_variable(pm_parser_t *parser) { } } else if (parser->current.end < end && pm_char_is_decimal_digit(*parser->current.end)) { pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE; - if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3) { + if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) { diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3; } @@ -14648,7 +14648,7 @@ parse_parameters( parser_lex(parser); pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name); - uint32_t reads = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; + uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true); pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT, (uint16_t) (depth + 1)); @@ -14664,7 +14664,7 @@ parse_parameters( // If the value of the parameter increased the number of // reads of that parameter, then we need to warn that we // have a circular definition. - if ((parser->version == PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { + if ((parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, name, PM_ERR_PARAMETER_CIRCULAR); } @@ -14749,13 +14749,13 @@ parse_parameters( if (token_begins_expression_p(parser->current.type)) { pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local); - uint32_t reads = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; + uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true); pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT_KW, (uint16_t) (depth + 1)); if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser); - if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { + if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_PARAMETER_CIRCULAR); } @@ -16459,7 +16459,7 @@ parse_variable(pm_parser_t *parser) { pm_node_list_append(¤t_scope->implicit_parameters, node); return node; - } else if ((parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) && pm_token_is_it(parser->previous.start, parser->previous.end)) { + } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) { pm_node_t *node = (pm_node_t *) pm_it_local_variable_read_node_create(parser, &parser->previous); pm_node_list_append(¤t_scope->implicit_parameters, node); @@ -22628,6 +22628,12 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm } } + // Now that we have established the user-provided options, check if + // a version was given and parse as the latest version otherwise. + if (parser->version == PM_OPTIONS_VERSION_UNSET) { + parser->version = PM_OPTIONS_VERSION_LATEST; + } + pm_accepts_block_stack_push(parser, true); // Skip past the UTF-8 BOM if it exists.