[前][次][番号順一覧][スレッド一覧]

ruby-changes:64123

From: Kazuki <ko1@a...>
Date: Sun, 13 Dec 2020 11:52:23 +0900 (JST)
Subject: [ruby-changes:64123] 88f3ce12d3 (master): Reintroduce `expr in pat` [Feature #17371]

https://git.ruby-lang.org/ruby.git/commit/?id=88f3ce12d3

From 88f3ce12d32ffbef983b0950743c20253ea2d0c6 Mon Sep 17 00:00:00 2001
From: Kazuki Tsujimoto <kazuki@c...>
Date: Sun, 13 Dec 2020 11:50:14 +0900
Subject: Reintroduce `expr in pat` [Feature #17371]


diff --git a/NEWS.md b/NEWS.md
index bb072f2..3cd98b2 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -52,19 +52,28 @@ sufficient information, see the ChangeLog file or Redmine https://github.com/ruby/ruby/blob/trunk/NEWS.md#L52
   instead of a warning. yield in a class definition outside of a method
   is now a SyntaxError instead of a LocalJumpError.  [[Feature #15575]]
 
-* Pattern matching is no longer experimental. [[Feature #17260]]
+* Pattern matching(`case/in`) is no longer experimental. [[Feature #17260]]
 
-* One-line pattern matching now uses `=>` instead of `in`.  [EXPERIMENTAL]
-  [[Feature #17260]]
+* One-line pattern matching is redesgined.  [EXPERIMENTAL]
+    * `=>` is added. It can be used as like rightward assignment.
+      [[Feature #17260]]
+
+    ```ruby
+    0 => a
+    p a #=> 0
+
+    {b: 0, c: 1} => {b:}
+    p b #=> 0
+    ```
+
+    * `in` is changed to return `true` or `false`. [[Feature #17371]]
 
     ```ruby
     # version 3.0
-    {a: 0, b: 1} => {a:}
-    p a # => 0
+    0 in 1 #=> false
 
     # version 2.7
-    {a: 0, b: 1} in {a:}
-    p a # => 0
+    0 in 1 #=> raise NoMatchingPatternError
     ```
 
 * Find pattern is added.  [EXPERIMENTAL]
@@ -639,4 +648,5 @@ end https://github.com/ruby/ruby/blob/trunk/NEWS.md#L648
 [Feature #17187]: https://bugs.ruby-lang.org/issues/17187
 [Bug #17221]:     https://bugs.ruby-lang.org/issues/17221
 [Feature #17260]: https://bugs.ruby-lang.org/issues/17260
+[Feature #17371]: https://bugs.ruby-lang.org/issues/17371
 [GH-2991]:        https://github.com/ruby/ruby/pull/2991
diff --git a/doc/syntax/pattern_matching.rdoc b/doc/syntax/pattern_matching.rdoc
index 8419af5..b4ac52e 100644
--- a/doc/syntax/pattern_matching.rdoc
+++ b/doc/syntax/pattern_matching.rdoc
@@ -15,11 +15,13 @@ Pattern matching in Ruby is implemented with the +case+/+in+ expression: https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L15
       ...
     end
 
-or with the +=>+ operator, which can be used in a standalone expression:
+(Note that +in+ and +when+ branches can *not* be mixed in one +case+ expression.)
+
+or with the +=>+ operator and the +in+ operator, which can be used in a standalone expression:
 
     <expression> => <pattern>
 
-(Note that +in+ and +when+ branches can *not* be mixed in one +case+ expression.)
+    <expression> in <pattern>
 
 Pattern matching is _exhaustive_: if variable doesn't match pattern (in a separate +in+ clause), or doesn't matches any branch of +case+ expression (and +else+ branch is absent), +NoMatchingPatternError+ is raised.
 
@@ -46,6 +48,12 @@ whilst the +=>+ operator is most useful when expected data structure is known be https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L48
   puts "Connect with user '#{user}'"
   # Prints: "Connect with user 'admin'"
 
++<expression> in <pattern>+ is the same as +case <expression>; in <pattern>; true; else false; end+.
+You can use it when you only want to know if a pattern has been matched or not:
+
+  users = [{name: "Alice", age: 12}, {name: "Bob", age: 23}]
+  users.any? {|u| u in {name: /B/, age: 20..} } #=> true
+
 See below for more examples and explanations of the syntax.
 
 == Patterns
diff --git a/parse.y b/parse.y
index bc33c6a..ff413d8 100644
--- a/parse.y
+++ b/parse.y
@@ -502,7 +502,7 @@ static NODE *new_find_pattern(struct parser_params *p, NODE *constant, NODE *fnd https://github.com/ruby/ruby/blob/trunk/parse.y#L502
 static NODE *new_find_pattern_tail(struct parser_params *p, ID pre_rest_arg, NODE *args, ID post_rest_arg, const YYLTYPE *loc);
 static NODE *new_hash_pattern(struct parser_params *p, NODE *constant, NODE *hshptn, const YYLTYPE *loc);
 static NODE *new_hash_pattern_tail(struct parser_params *p, NODE *kw_args, ID kw_rest_arg, const YYLTYPE *loc);
-static void warn_one_line_pattern_matching(struct parser_params *p, NODE *node, NODE *pattern);
+static void warn_one_line_pattern_matching(struct parser_params *p, NODE *node, NODE *pattern, bool right_assign);
 
 static NODE *new_kw_arg(struct parser_params *p, NODE *k, const YYLTYPE *loc);
 static NODE *args_with_numbered(struct parser_params*,NODE*,int);
@@ -1243,7 +1243,7 @@ static int looking_at_eol_p(struct parser_params *p); https://github.com/ruby/ruby/blob/trunk/parse.y#L1243
 %nonassoc tLOWEST
 %nonassoc tLBRACE_ARG
 
-%nonassoc  modifier_if modifier_unless modifier_while modifier_until
+%nonassoc  modifier_if modifier_unless modifier_while modifier_until keyword_in
 %left  keyword_or keyword_and
 %right keyword_not
 %nonassoc keyword_defined
@@ -1662,7 +1662,26 @@ expr		: command_call https://github.com/ruby/ruby/blob/trunk/parse.y#L1662
 			p->ctxt.in_kwarg = $<ctxt>3.in_kwarg;
 		    /*%%%*/
 			$$ = NEW_CASE3($1, NEW_IN($5, 0, 0, &@5), &@$);
-			warn_one_line_pattern_matching(p, $$, $5);
+			warn_one_line_pattern_matching(p, $$, $5, true);
+		    /*% %*/
+		    /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/
+		    }
+		| arg keyword_in
+		    {
+			value_expr($1);
+			SET_LEX_STATE(EXPR_BEG|EXPR_LABEL);
+			p->command_start = FALSE;
+			$<ctxt>$ = p->ctxt;
+			p->ctxt.in_kwarg = 1;
+		    }
+		    {$<tbl>$ = push_pvtbl(p);}
+		  p_expr
+		    {pop_pvtbl(p, $<tbl>4);}
+		    {
+			p->ctxt.in_kwarg = $<ctxt>3.in_kwarg;
+		    /*%%%*/
+			$$ = NEW_CASE3($1, NEW_IN($5, NEW_TRUE(&@5), NEW_FALSE(&@5), &@5), &@$);
+			warn_one_line_pattern_matching(p, $$, $5, false);
 		    /*% %*/
 		    /*% ripper: case!($1, in!($5, Qnil, Qnil)) %*/
 		    }
@@ -11689,13 +11708,13 @@ new_hash_pattern_tail(struct parser_params *p, NODE *kw_args, ID kw_rest_arg, co https://github.com/ruby/ruby/blob/trunk/parse.y#L11708
 }
 
 static void
-warn_one_line_pattern_matching(struct parser_params *p, NODE *node, NODE *pattern)
+warn_one_line_pattern_matching(struct parser_params *p, NODE *node, NODE *pattern, bool right_assign)
 {
     enum node_type type;
     type = nd_type(pattern);
 
     if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL) &&
-	!(type == NODE_LASGN || type == NODE_DASGN || type == NODE_DASGN_CURR))
+	!(right_assign && (type == NODE_LASGN || type == NODE_DASGN || type == NODE_DASGN_CURR)))
 	rb_warn0L(nd_line(node), "One-line pattern matching is experimental, and the behavior may change in future versions of Ruby!");
 }
 
diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb
index b155cb8..e553789 100644
--- a/test/ruby/test_pattern_matching.rb
+++ b/test/ruby/test_pattern_matching.rb
@@ -1451,7 +1451,7 @@ END https://github.com/ruby/ruby/blob/trunk/test/ruby/test_pattern_matching.rb#L1451
 
   ################################################################
 
-  def test_assoc
+  def test_one_line
     1 => a
     assert_equal 1, a
     assert_raise(NoMatchingPatternError) do
@@ -1464,6 +1464,9 @@ END https://github.com/ruby/ruby/blob/trunk/test/ruby/test_pattern_matching.rb#L1464
     assert_syntax_error(%q{
       1 => a:
     }, /unexpected/, '[ruby-core:95098]')
+
+    assert_equal true, (1 in 1)
+    assert_equal false, (1 in 2)
   end
 
   def assert_experimental_warning(code)
@@ -1481,6 +1484,7 @@ END https://github.com/ruby/ruby/blob/trunk/test/ruby/test_pattern_matching.rb#L1484
   def test_experimental_warning
     assert_experimental_warning("case [0]; in [*, 0, *]; end")
     assert_experimental_warning("0 => 0")
+    assert_experimental_warning("0 in a")
   end
 end
 END_of_GUARD
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]