mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Reintroduce expr in pat
[Feature #17371]
This commit is contained in:
parent
a8cf526ae9
commit
88f3ce12d3
4 changed files with 56 additions and 15 deletions
24
NEWS.md
24
NEWS.md
|
@ -52,19 +52,28 @@ sufficient information, see the ChangeLog file or Redmine
|
|||
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
|
|||
[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
|
||||
|
|
|
@ -15,11 +15,13 @@ Pattern matching in Ruby is implemented with the +case+/+in+ expression:
|
|||
...
|
||||
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
|
|||
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
|
||||
|
|
29
parse.y
29
parse.y
|
@ -502,7 +502,7 @@ static NODE *new_find_pattern(struct parser_params *p, NODE *constant, NODE *fnd
|
|||
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);
|
|||
%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
|
|||
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
|
|||
}
|
||||
|
||||
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!");
|
||||
}
|
||||
|
||||
|
|
|
@ -1451,7 +1451,7 @@ END
|
|||
|
||||
################################################################
|
||||
|
||||
def test_assoc
|
||||
def test_one_line
|
||||
1 => a
|
||||
assert_equal 1, a
|
||||
assert_raise(NoMatchingPatternError) do
|
||||
|
@ -1464,6 +1464,9 @@ END
|
|||
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
|
|||
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
|
||||
|
|
Loading…
Reference in a new issue