summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKazuki Tsujimoto <kazuki@callcc.net>2021-03-21 15:12:54 +0900
committerKazuki Tsujimoto <kazuki@callcc.net>2021-03-21 15:14:31 +0900
commit21863470d965b8cc299b1f82417c70d5d26f8ab2 (patch)
tree727faf202cf818d796602b526e17fb9bfea0cb29
parent232433f22423fb6a3ff7a610140c711a964d3b3d (diff)
Pattern matching pin operator against expression [Feature #17411]
This commit is based on the patch by @nobu.
-rw-r--r--NEWS.md7
-rw-r--r--compile.c1
-rw-r--r--doc/syntax/pattern_matching.rdoc1
-rw-r--r--parse.y18
-rw-r--r--test/ripper/test_sexp.rb8
-rw-r--r--test/ruby/test_pattern_matching.rb23
6 files changed, 54 insertions, 4 deletions
diff --git a/NEWS.md b/NEWS.md
index d6208199be..f8d2179a88 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -6,6 +6,12 @@ since the **3.0.0** release, except for bug fixes.
Note that each entry is kept to a minimum, see links for details.
## Language changes
+* Pin operator now takes an expression. [[Feature #17411]]
+
+ ```ruby
+ Prime.each_cons(2).lazy.find_all{_1 in [n, ^(n + 2)]}.take(3).to_a
+ #=> [[3, 5], [5, 7], [11, 13]]
+ ```
## Command line options
@@ -90,5 +96,6 @@ Excluding feature bug fixes.
[Feature #16806]: https://bugs.ruby-lang.org/issues/16806
[Feature #17312]: https://bugs.ruby-lang.org/issues/17312
[Feature #17327]: https://bugs.ruby-lang.org/issues/17327
+[Feature #17411]: https://bugs.ruby-lang.org/issues/17411
[Bug #17423]: https://bugs.ruby-lang.org/issues/17423
[Feature #17479]: https://bugs.ruby-lang.org/issues/17479
diff --git a/compile.c b/compile.c
index 78870ee051..230b800529 100644
--- a/compile.c
+++ b/compile.c
@@ -6211,6 +6211,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
case NODE_NIL:
case NODE_COLON2:
case NODE_COLON3:
+ case NODE_BEGIN:
CHECK(COMPILE(ret, "case in literal", node));
ADD_INSN1(ret, line, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
ADD_INSNL(ret, line, branchif, matched);
diff --git a/doc/syntax/pattern_matching.rdoc b/doc/syntax/pattern_matching.rdoc
index 9f6954f1cb..69756369fb 100644
--- a/doc/syntax/pattern_matching.rdoc
+++ b/doc/syntax/pattern_matching.rdoc
@@ -450,6 +450,7 @@ Approximate syntax is:
value_pattern: literal
| Constant
| ^variable
+ | ^(expression)
variable_pattern: variable
diff --git a/parse.y b/parse.y
index 10cd1000d9..e24d26e6e1 100644
--- a/parse.y
+++ b/parse.y
@@ -1196,7 +1196,7 @@ static int looking_at_eol_p(struct parser_params *p);
%type <node> p_case_body p_cases p_top_expr p_top_expr_body
%type <node> p_expr p_as p_alt p_expr_basic p_find
%type <node> p_args p_args_head p_args_tail p_args_post p_arg
-%type <node> p_value p_primitive p_variable p_var_ref p_const
+%type <node> p_value p_primitive p_variable p_var_ref p_expr_ref p_const
%type <node> p_kwargs p_kwarg p_kw
%type <id> keyword_variable user_variable sym operation operation2 operation3
%type <id> cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_bad_arg
@@ -3994,7 +3994,7 @@ p_top_expr : p_top_expr_body
| p_top_expr_body modifier_if expr_value
{
/*%%%*/
- $$ = new_if(p, $3, remove_begin($1), 0, &@$);
+ $$ = new_if(p, $3, $1, 0, &@$);
fixpos($$, $3);
/*% %*/
/*% ripper: if_mod!($3, $1) %*/
@@ -4002,7 +4002,7 @@ p_top_expr : p_top_expr_body
| p_top_expr_body modifier_unless expr_value
{
/*%%%*/
- $$ = new_unless(p, $3, remove_begin($1), 0, &@$);
+ $$ = new_unless(p, $3, $1, 0, &@$);
fixpos($$, $3);
/*% %*/
/*% ripper: unless_mod!($3, $1) %*/
@@ -4066,6 +4066,7 @@ p_lparen : '(' {$<tbl>$ = push_pktbl(p);};
p_lbracket : '[' {$<tbl>$ = push_pktbl(p);};
p_expr_basic : p_value
+ | p_variable
| p_const p_lparen p_args rparen
{
pop_pktbl(p, $<tbl>2);
@@ -4400,8 +4401,8 @@ p_value : p_primitive
/*% %*/
/*% ripper: dot3!($1, Qnil) %*/
}
- | p_variable
| p_var_ref
+ | p_expr_ref
| p_const
| tBDOT2 p_primitive
{
@@ -4462,6 +4463,15 @@ p_var_ref : '^' tIDENTIFIER
}
;
+p_expr_ref : '^' tLPAREN expr_value ')'
+ {
+ /*%%%*/
+ $$ = NEW_BEGIN($3, &@$);
+ /*% %*/
+ /*% ripper: begin!($3) %*/
+ }
+ ;
+
p_const : tCOLON3 cname
{
/*%%%*/
diff --git a/test/ripper/test_sexp.rb b/test/ripper/test_sexp.rb
index 22ee418abb..b4e70fa4f5 100644
--- a/test/ripper/test_sexp.rb
+++ b/test/ripper/test_sexp.rb
@@ -478,6 +478,14 @@ eot
[__LINE__, %q{ case 0; in "a\x0":a1, "a\0":a2; end }] =>
nil, # duplicated key name
+
+ [__LINE__, %q{ case 0; in ^(0+0); end } ] =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:begin, [:binary, [:@int, "0", [1, 13]], :+, [:@int, "0", [1, 15]]]],
+ [[:void_stmt]],
+ nil]],
}
pattern_matching_data.each do |(i, src), expected|
define_method(:"test_pattern_matching_#{i}") do
diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb
index b18ef68316..c494550574 100644
--- a/test/ruby/test_pattern_matching.rb
+++ b/test/ruby/test_pattern_matching.rb
@@ -402,6 +402,29 @@ END
end
end
+ def test_pin_operator_expr_pattern
+ assert_block do
+ case 'abc'
+ in ^(/a/)
+ true
+ end
+ end
+
+ assert_block do
+ case {name: '2.6', released_at: Time.new(2018, 12, 25)}
+ in {released_at: ^(Time.new(2010)..Time.new(2020))}
+ true
+ end
+ end
+
+ assert_block do
+ case 0
+ in ^(0+0)
+ true
+ end
+ end
+ end
+
def test_array_pattern
assert_block do
[[0], C.new([0])].all? do |i|