diff options
-rw-r--r-- | lib/prism/ffi.rb | 3 | ||||
-rw-r--r-- | prism/extension.c | 10 | ||||
-rw-r--r-- | prism/options.c | 28 | ||||
-rw-r--r-- | prism/options.h | 40 | ||||
-rw-r--r-- | prism/parser.h | 10 | ||||
-rw-r--r-- | prism/prism.c | 10 | ||||
-rw-r--r-- | test/prism/snapshots/arrays.txt | 8 | ||||
-rw-r--r-- | test/prism/snapshots/unparser/corpus/literal/assignment.txt | 8 | ||||
-rw-r--r-- | test/prism/snapshots/whitequark/masgn_attr.txt | 2 |
9 files changed, 106 insertions, 13 deletions
diff --git a/lib/prism/ffi.rb b/lib/prism/ffi.rb index 8324f722a7..4c42e5061b 100644 --- a/lib/prism/ffi.rb +++ b/lib/prism/ffi.rb @@ -314,6 +314,9 @@ module Prism template << "C" values << (options.fetch(:verbose, true) ? 0 : 1) + template << "C" + values << { nil => 0, "3.3.0" => 1, "latest" => 0 }.fetch(options[:version]) + template << "L" if (scopes = options[:scopes]) values << scopes.length diff --git a/prism/extension.c b/prism/extension.c index 6cf007b4df..6dc1993657 100644 --- a/prism/extension.c +++ b/prism/extension.c @@ -22,6 +22,7 @@ ID rb_option_id_encoding; ID rb_option_id_line; ID rb_option_id_frozen_string_literal; ID rb_option_id_verbose; +ID rb_option_id_version; ID rb_option_id_scopes; /******************************************************************************/ @@ -131,6 +132,14 @@ build_options_i(VALUE key, VALUE value, VALUE argument) { if (!NIL_P(value)) pm_options_frozen_string_literal_set(options, value == Qtrue); } else if (key_id == rb_option_id_verbose) { pm_options_suppress_warnings_set(options, value != Qtrue); + } else if (key_id == rb_option_id_version) { + if (!NIL_P(value)) { + const char *version = check_string(value); + + if (!pm_options_version_set(options, version, RSTRING_LEN(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 { @@ -1013,6 +1022,7 @@ Init_prism(void) { rb_option_id_line = rb_intern_const("line"); rb_option_id_frozen_string_literal = rb_intern_const("frozen_string_literal"); rb_option_id_verbose = rb_intern_const("verbose"); + rb_option_id_version = rb_intern_const("version"); rb_option_id_scopes = rb_intern_const("scopes"); /** diff --git a/prism/options.c b/prism/options.c index 85d04d6272..0dcae0d16f 100644 --- a/prism/options.c +++ b/prism/options.c @@ -41,6 +41,33 @@ pm_options_suppress_warnings_set(pm_options_t *options, bool suppress_warnings) } /** + * Set the version option on the given options struct by parsing the given + * string. If the string contains an invalid option, this returns false. + * Otherwise, it returns true. + */ +PRISM_EXPORTED_FUNCTION bool +pm_options_version_set(pm_options_t *options, const char *version, size_t length) { + if (version == NULL && length == 0) { + options->version = PM_OPTIONS_VERSION_LATEST; + return true; + } + + if (length == 5) { + if (strncmp(version, "3.3.0", 5) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_3_3_0; + return true; + } + + if (strncmp(version, "latest", 6) == 0) { + options->version = PM_OPTIONS_VERSION_LATEST; + return true; + } + } + + return false; +} + +/** * Allocate and zero out the scopes array on the given options struct. */ PRISM_EXPORTED_FUNCTION void @@ -163,6 +190,7 @@ pm_options_read(pm_options_t *options, const char *data) { options->frozen_string_literal = *data++; options->suppress_warnings = *data++; + options->version = (pm_options_version_t) *data++; uint32_t scopes_count = pm_options_read_u32(data); data += 4; diff --git a/prism/options.h b/prism/options.h index 8608838da8..130d635b98 100644 --- a/prism/options.h +++ b/prism/options.h @@ -25,6 +25,19 @@ typedef struct pm_options_scope { } pm_options_scope_t; /** + * The version of prism that we should be parsing with. This is used to allow + * consumers to specify which behavior they want in case they need to parse + * exactly as a specific version of CRuby. + */ +typedef enum { + /** The current version of prism. */ + PM_OPTIONS_VERSION_LATEST = 0, + + /** The vendored version of prism in CRuby 3.3.0. */ + PM_OPTIONS_VERSION_CRUBY_3_3_0 = 1 +} pm_options_version_t; + +/** * The options that can be passed to the parser. */ typedef struct { @@ -55,6 +68,13 @@ typedef struct { */ pm_options_scope_t *scopes; + /** + * The version of prism that we should be parsing with. This is used to + * allow consumers to specify which behavior they want in case they need to + * parse exactly as a specific version of CRuby. + */ + pm_options_version_t version; + /** Whether or not the frozen string literal option has been set. */ bool frozen_string_literal; @@ -107,6 +127,18 @@ PRISM_EXPORTED_FUNCTION void pm_options_frozen_string_literal_set(pm_options_t * PRISM_EXPORTED_FUNCTION void pm_options_suppress_warnings_set(pm_options_t *options, bool suppress_warnings); /** + * Set the version option on the given options struct by parsing the given + * string. If the string contains an invalid option, this returns false. + * Otherwise, it returns true. + * + * @param options The options struct to set the version on. + * @param version The version to set. + * @param length The length of the version string. + * @return Whether or not the version was parsed successfully. + */ +PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length); + +/** * Allocate and zero out the scopes array on the given options struct. * * @param options The options struct to initialize the scopes array on. @@ -167,9 +199,17 @@ PRISM_EXPORTED_FUNCTION void pm_options_free(pm_options_t *options); * | ... | the encoding bytes | * | `1` | frozen string literal | * | `1` | suppress warnings | + * | `1` | the version | * | `4` | the number of scopes | * | ... | the scopes | * + * The version field is an enum, so it should be one of the following values: + * + * | value | version | + * | ----- | ------------------------- | + * | `0` | use the latest version of prism | + * | `1` | use the version of prism that is vendored in CRuby 3.3.0 | + * * Each scope is layed out as follows: * * | # bytes | field | diff --git a/prism/parser.h b/prism/parser.h index 2c58131b19..9f38dc5830 100644 --- a/prism/parser.h +++ b/prism/parser.h @@ -9,6 +9,7 @@ #include "prism/ast.h" #include "prism/defines.h" #include "prism/encoding.h" +#include "prism/options.h" #include "prism/util/pm_constant_pool.h" #include "prism/util/pm_list.h" #include "prism/util/pm_newline_list.h" @@ -662,6 +663,12 @@ struct pm_parser { */ const pm_encoding_t *explicit_encoding; + /** The current parameter name id on parsing its default value. */ + pm_constant_id_t current_param_name; + + /** The version of prism that we should use to parse. */ + pm_options_version_t version; + /** Whether or not we're at the beginning of a command. */ bool command_start; @@ -684,9 +691,6 @@ struct pm_parser { /** This flag indicates that we are currently parsing a keyword argument. */ bool in_keyword_arg; - /** The current parameter name id on parsing its default value. */ - pm_constant_id_t current_param_name; - /** * Whether or not the parser has seen a token that has semantic meaning * (i.e., a token that is not a comment or whitespace). diff --git a/prism/prism.c b/prism/prism.c index ec319f2cac..03df698449 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -2172,11 +2172,16 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { static pm_index_target_node_t * pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { pm_index_target_node_t *node = PM_ALLOC_NODE(parser, pm_index_target_node_t); + pm_node_flags_t flags = target->base.flags; + + if (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3_0) { + flags |= PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE; + } *node = (pm_index_target_node_t) { { .type = PM_INDEX_TARGET_NODE, - .flags = target->base.flags, + .flags = flags, .location = target->base.location }, .receiver = target->receiver, @@ -17320,6 +17325,9 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm parser->suppress_warnings = true; } + // version option + parser->version = options->version; + // scopes option for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) { const pm_options_scope_t *scope = pm_options_scope_get(options, scope_index); diff --git a/test/prism/snapshots/arrays.txt b/test/prism/snapshots/arrays.txt index 93f5c8fbc1..40d4cbed4f 100644 --- a/test/prism/snapshots/arrays.txt +++ b/test/prism/snapshots/arrays.txt @@ -430,7 +430,7 @@ ├── @ MultiWriteNode (location: (41,0)-(41,21)) │ ├── lefts: (length: 2) │ │ ├── @ IndexTargetNode (location: (41,0)-(41,6)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: attribute_write │ │ │ ├── receiver: │ │ │ │ @ CallNode (location: (41,0)-(41,3)) │ │ │ │ ├── flags: variable_call @@ -452,7 +452,7 @@ │ │ │ ├── closing_loc: (41,5)-(41,6) = "]" │ │ │ └── block: ∅ │ │ └── @ IndexTargetNode (location: (41,8)-(41,14)) - │ │ ├── flags: ∅ + │ │ ├── flags: attribute_write │ │ ├── receiver: │ │ │ @ CallNode (location: (41,8)-(41,11)) │ │ │ ├── flags: variable_call @@ -2166,7 +2166,7 @@ │ │ │ ├── operator_loc: (140,17)-(140,19) = "=>" │ │ │ ├── reference: │ │ │ │ @ IndexTargetNode (location: (140,20)-(140,24)) - │ │ │ │ ├── flags: ∅ + │ │ │ │ ├── flags: attribute_write │ │ │ │ ├── receiver: │ │ │ │ │ @ CallNode (location: (140,20)-(140,21)) │ │ │ │ │ ├── flags: variable_call @@ -2229,7 +2229,7 @@ │ │ ├── operator_loc: (142,17)-(142,19) = "=>" │ │ ├── reference: │ │ │ @ IndexTargetNode (location: (142,20)-(142,27)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: attribute_write │ │ │ ├── receiver: │ │ │ │ @ CallNode (location: (142,20)-(142,21)) │ │ │ │ ├── flags: variable_call diff --git a/test/prism/snapshots/unparser/corpus/literal/assignment.txt b/test/prism/snapshots/unparser/corpus/literal/assignment.txt index 18edafe415..4cb19d2867 100644 --- a/test/prism/snapshots/unparser/corpus/literal/assignment.txt +++ b/test/prism/snapshots/unparser/corpus/literal/assignment.txt @@ -319,7 +319,7 @@ ├── @ MultiWriteNode (location: (15,0)-(15,24)) │ ├── lefts: (length: 2) │ │ ├── @ IndexTargetNode (location: (15,1)-(15,8)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: attribute_write │ │ │ ├── receiver: │ │ │ │ @ LocalVariableReadNode (location: (15,1)-(15,2)) │ │ │ │ ├── name: :a @@ -338,7 +338,7 @@ │ │ │ ├── closing_loc: (15,7)-(15,8) = "]" │ │ │ └── block: ∅ │ │ └── @ IndexTargetNode (location: (15,10)-(15,14)) - │ │ ├── flags: ∅ + │ │ ├── flags: attribute_write │ │ ├── receiver: │ │ │ @ LocalVariableReadNode (location: (15,10)-(15,11)) │ │ │ ├── name: :a @@ -370,7 +370,7 @@ ├── @ MultiWriteNode (location: (16,0)-(16,21)) │ ├── lefts: (length: 2) │ │ ├── @ IndexTargetNode (location: (16,1)-(16,5)) - │ │ │ ├── flags: ∅ + │ │ │ ├── flags: attribute_write │ │ │ ├── receiver: │ │ │ │ @ LocalVariableReadNode (location: (16,1)-(16,2)) │ │ │ │ ├── name: :a @@ -385,7 +385,7 @@ │ │ │ ├── closing_loc: (16,4)-(16,5) = "]" │ │ │ └── block: ∅ │ │ └── @ IndexTargetNode (location: (16,7)-(16,11)) - │ │ ├── flags: ∅ + │ │ ├── flags: attribute_write │ │ ├── receiver: │ │ │ @ LocalVariableReadNode (location: (16,7)-(16,8)) │ │ │ ├── name: :a diff --git a/test/prism/snapshots/whitequark/masgn_attr.txt b/test/prism/snapshots/whitequark/masgn_attr.txt index d87d7a6cb1..2a4dc38b7c 100644 --- a/test/prism/snapshots/whitequark/masgn_attr.txt +++ b/test/prism/snapshots/whitequark/masgn_attr.txt @@ -34,7 +34,7 @@ │ │ │ ├── name: :a= │ │ │ └── message_loc: (3,5)-(3,6) = "a" │ │ └── @ IndexTargetNode (location: (3,8)-(3,18)) - │ │ ├── flags: ∅ + │ │ ├── flags: attribute_write │ │ ├── receiver: │ │ │ @ SelfNode (location: (3,8)-(3,12)) │ │ ├── opening_loc: (3,12)-(3,13) = "[" |