diff --git a/NEWS.md b/NEWS.md index 702803eee7..5f0b5a6b2c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -14,6 +14,13 @@ Note that each entry is kept to a minimum, see links for details. #=> [[3, 5], [5, 7], [11, 13]] ``` +* Pin operator now supports instance, class, and global variables. + [[Feature #17724]] + + @n = 5 + Prime.each_cons(2).lazy.find{_1 in [n, ^@n]} + #=> [3, 5] + * Multiple assignment evaluation order has been made consistent with single assignment evaluation order. With single assignment, Ruby uses a left-to-right evaluation order. With this code: @@ -190,6 +197,7 @@ Excluding feature bug fixes. [Bug #17423]: https://bugs.ruby-lang.org/issues/17423 [Feature #17479]: https://bugs.ruby-lang.org/issues/17479 [Feature #17490]: https://bugs.ruby-lang.org/issues/17490 +[Feature #17724]: https://bugs.ruby-lang.org/issues/17724 [Feature #17744]: https://bugs.ruby-lang.org/issues/17744 [Feature #17762]: https://bugs.ruby-lang.org/issues/17762 [Bug #18003]: https://bugs.ruby-lang.org/issues/18003 diff --git a/compile.c b/compile.c index a7862194c0..38a96f165e 100644 --- a/compile.c +++ b/compile.c @@ -6462,6 +6462,9 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c case NODE_CONST: case NODE_LVAR: case NODE_DVAR: + case NODE_IVAR: + case NODE_CVAR: + case NODE_GVAR: case NODE_TRUE: case NODE_FALSE: case NODE_SELF: diff --git a/doc/syntax/pattern_matching.rdoc b/doc/syntax/pattern_matching.rdoc index 69756369fb..49835def22 100644 --- a/doc/syntax/pattern_matching.rdoc +++ b/doc/syntax/pattern_matching.rdoc @@ -312,6 +312,33 @@ One important usage of variable pinning is specifying that the same value should end #=> "not matched" +In addition to pinning local variables, you can also pin instance, global, and class variables: + + $gvar = 1 + class A + @ivar = 2 + @@cvar = 3 + case [1, 2, 3] + in ^$gvar, ^@ivar, ^@@cvar + "matched" + else + "not matched" + end + #=> "matched" + end + +You can also pin the result of arbitrary expressions using parentheses: + + a = 1 + b = 2 + case 3 + in ^(a + b) + "matched" + else + "not matched" + end + #=> "matched" + == Matching non-primitive objects: +deconstruct+ and +deconstruct_keys+ As already mentioned above, array, find, and hash patterns besides literal arrays and hashes will try to match any object implementing +deconstruct+ (for array/find patterns) or +deconstruct_keys+ (for hash patterns). @@ -449,7 +476,10 @@ Approximate syntax is: value_pattern: literal | Constant - | ^variable + | ^local_variable + | ^instance_variable + | ^class_variable + | ^global_variable | ^(expression) variable_pattern: variable diff --git a/parse.y b/parse.y index df16cf6236..dec4b7dfd9 100644 --- a/parse.y +++ b/parse.y @@ -1203,7 +1203,7 @@ static int looking_at_eol_p(struct parser_params *p); %type cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_bad_arg %type f_kwrest f_label f_arg_asgn call_op call_op2 reswords relop dot_or_colon %type p_rest p_kwrest p_kwnorest p_any_kwrest p_kw_label -%type f_no_kwarg f_any_kwrest args_forward excessed_comma +%type f_no_kwarg f_any_kwrest args_forward excessed_comma nonlocal_var %type lex_ctxt /* keep in ripper */ %token END_OF_INPUT 0 "end-of-input" %token '.' @@ -4517,6 +4517,13 @@ p_var_ref : '^' tIDENTIFIER /*% %*/ /*% ripper: var_ref!($2) %*/ } + | '^' nonlocal_var + { + /*%%%*/ + if (!($$ = gettable(p, $2, &@$))) $$ = NEW_BEGIN(0, &@$); + /*% %*/ + /*% ripper: var_ref!($2) %*/ + } ; p_expr_ref : '^' tLPAREN expr_value ')' @@ -4993,6 +5000,11 @@ simple_numeric : tINTEGER | tIMAGINARY ; +nonlocal_var : tIVAR + | tGVAR + | tCVAR + ; + user_variable : tIDENTIFIER | tIVAR | tGVAR diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb index c494550574..320c2c00c7 100644 --- a/test/ruby/test_pattern_matching.rb +++ b/test/ruby/test_pattern_matching.rb @@ -400,6 +400,30 @@ END a == 0 end end + + assert_block do + @a = /a/ + case 'abc' + in ^@a + true + end + end + + assert_block do + @@TestPatternMatching = /a/ + case 'abc' + in ^@@TestPatternMatching + true + end + end + + assert_block do + $TestPatternMatching = /a/ + case 'abc' + in ^$TestPatternMatching + true + end + end end def test_pin_operator_expr_pattern