diff --git a/NEWS.md b/NEWS.md index 6856a29517..8b92aa45b0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -38,6 +38,18 @@ 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]] +* Find pattern is added. [[Feature #16828]] + + ```ruby + case ["a", 1, "b", "c", 2, "d", "e", "f", 3] + in [*pre, String => x, String => y, *post] + p pre #=> ["a", 1] + p x #=> "b" + p y #=> "c" + p post #=> [2, "d", "e", "f", 3] + end + ``` + * Rightward assignment statement is added. [EXPERIMENTAL] [[Feature #15921]] diff --git a/ast.c b/ast.c index 18a97239ff..44fec1645c 100644 --- a/ast.c +++ b/ast.c @@ -599,6 +599,19 @@ node_children(rb_ast_t *ast, NODE *node) rest, NEW_CHILD(ast, apinfo->post_args)); } + case NODE_FNDPTN: + { + struct rb_fnd_pattern_info *fpinfo = node->nd_fpinfo; + VALUE pre_rest = NODE_NAMED_REST_P(fpinfo->pre_rest_arg) ? NEW_CHILD(ast, fpinfo->pre_rest_arg) : + ID2SYM(rb_intern("NODE_SPECIAL_NO_NAME_REST")); + VALUE post_rest = NODE_NAMED_REST_P(fpinfo->post_rest_arg) ? NEW_CHILD(ast, fpinfo->post_rest_arg) : + ID2SYM(rb_intern("NODE_SPECIAL_NO_NAME_REST")); + return rb_ary_new_from_args(4, + NEW_CHILD(ast, node->nd_pconst), + pre_rest, + NEW_CHILD(ast, fpinfo->args), + post_rest); + } case NODE_HSHPTN: { VALUE kwrest = node->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD ? ID2SYM(rb_intern("NODE_SPECIAL_NO_REST_KEYWORD")) : diff --git a/compile.c b/compile.c index 5a7a088f61..9fba24f0fa 100644 --- a/compile.c +++ b/compile.c @@ -5752,6 +5752,173 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c break; } + case NODE_FNDPTN: { + /* + * if pattern.has_constant_node? + * unless pattern.constant === obj + * goto match_failed + * end + * end + * unless obj.respond_to?(:deconstruct) + * goto match_failed + * end + * d = obj.deconstruct + * unless Array === d + * goto type_error + * end + * unless d.length >= pattern.args_num + * goto match_failed + * end + * + * begin + * len = d.length + * limit = d.length - pattern.args_num + * i = 0 + * while i <= limit + * if pattern.args_num.times.all? {|j| pattern.args[j].match?(d[i+j]) } + * if pattern.has_pre_rest_arg_id + * unless pattern.pre_rest_arg.match?(d[0, i]) + * goto find_failed + * end + * end + * if pattern.has_post_rest_arg_id + * unless pattern.post_rest_arg.match?(d[i+pattern.args_num, len]) + * goto find_failed + * end + * end + * goto find_succeeded + * end + * i+=1 + * end + * find_failed: + * goto match_failed + * find_succeeded: + * end + * + * goto matched + * type_error: + * FrozenCore.raise TypeError + * match_failed: + * goto unmatched + */ + struct rb_fnd_pattern_info *fpinfo = node->nd_fpinfo; + const NODE *args = fpinfo->args; + const int args_num = fpinfo->args ? rb_long2int(fpinfo->args->nd_alen) : 0; + + LABEL *match_failed, *type_error; + match_failed = NEW_LABEL(line); + type_error = NEW_LABEL(line); + + if (node->nd_pconst) { + ADD_INSN(ret, line, dup); + CHECK(COMPILE(ret, "constant", node->nd_pconst)); + ADD_INSN1(ret, line, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE)); + ADD_INSNL(ret, line, branchunless, match_failed); + } + + ADD_INSN(ret, line, dup); + ADD_INSN1(ret, line, putobject, ID2SYM(rb_intern("deconstruct"))); + ADD_SEND(ret, line, idRespond_to, INT2FIX(1)); + ADD_INSNL(ret, line, branchunless, match_failed); + + ADD_SEND(ret, line, rb_intern("deconstruct"), INT2FIX(0)); + + ADD_INSN(ret, line, dup); + ADD_INSN1(ret, line, checktype, INT2FIX(T_ARRAY)); + ADD_INSNL(ret, line, branchunless, type_error); + + ADD_INSN(ret, line, dup); + ADD_SEND(ret, line, idLength, INT2FIX(0)); + ADD_INSN1(ret, line, putobject, INT2FIX(args_num)); + ADD_SEND(ret, line, idGE, INT2FIX(1)); + ADD_INSNL(ret, line, branchunless, match_failed); + + { + LABEL *while_begin = NEW_LABEL(nd_line(node)); + LABEL *next_loop = NEW_LABEL(nd_line(node)); + LABEL *find_succeeded = NEW_LABEL(line); + LABEL *find_failed = NEW_LABEL(nd_line(node)); + int j; + + ADD_INSN(ret, line, dup); /* allocate stack for len */ + ADD_SEND(ret, line, idLength, INT2FIX(0)); + + ADD_INSN(ret, line, dup); /* allocate stack for limit */ + ADD_INSN1(ret, line, putobject, INT2FIX(args_num)); + ADD_SEND(ret, line, idMINUS, INT2FIX(1)); + + ADD_INSN1(ret, line, putobject, INT2FIX(0)); /* allocate stack for i */ + + ADD_LABEL(ret, while_begin); + + ADD_INSN(ret, line, dup); + ADD_INSN1(ret, line, topn, INT2FIX(2)); + ADD_SEND(ret, line, idLE, INT2FIX(1)); + ADD_INSNL(ret, line, branchunless, find_failed); + + for (j = 0; j < args_num; j++) { + ADD_INSN1(ret, line, topn, INT2FIX(3)); + ADD_INSN1(ret, line, topn, INT2FIX(1)); + if (j != 0) { + ADD_INSN1(ret, line, putobject, INT2FIX(j)); + ADD_SEND(ret, line, idPLUS, INT2FIX(1)); + } + ADD_SEND(ret, line, idAREF, INT2FIX(1)); + + CHECK(iseq_compile_pattern_match(iseq, ret, args->nd_head, next_loop, in_alt_pattern)); + args = args->nd_next; + } + + if (NODE_NAMED_REST_P(fpinfo->pre_rest_arg)) { + ADD_INSN1(ret, line, topn, INT2FIX(3)); + ADD_INSN1(ret, line, putobject, INT2FIX(0)); + ADD_INSN1(ret, line, topn, INT2FIX(2)); + ADD_SEND(ret, line, idAREF, INT2FIX(2)); + CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->pre_rest_arg, find_failed, in_alt_pattern)); + } + if (NODE_NAMED_REST_P(fpinfo->post_rest_arg)) { + ADD_INSN1(ret, line, topn, INT2FIX(3)); + ADD_INSN1(ret, line, topn, INT2FIX(1)); + ADD_INSN1(ret, line, putobject, INT2FIX(args_num)); + ADD_SEND(ret, line, idPLUS, INT2FIX(1)); + ADD_INSN1(ret, line, topn, INT2FIX(3)); + ADD_SEND(ret, line, idAREF, INT2FIX(2)); + CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->post_rest_arg, find_failed, in_alt_pattern)); + } + ADD_INSNL(ret, line, jump, find_succeeded); + + ADD_LABEL(ret, next_loop); + ADD_INSN1(ret, line, putobject, INT2FIX(1)); + ADD_SEND(ret, line, idPLUS, INT2FIX(1)); + ADD_INSNL(ret, line, jump, while_begin); + + ADD_LABEL(ret, find_failed); + ADD_INSN(ret, line, pop); + ADD_INSN(ret, line, pop); + ADD_INSN(ret, line, pop); + ADD_INSNL(ret, line, jump, match_failed); + + ADD_LABEL(ret, find_succeeded); + ADD_INSN(ret, line, pop); + ADD_INSN(ret, line, pop); + ADD_INSN(ret, line, pop); + } + + ADD_INSN(ret, line, pop); + ADD_INSNL(ret, line, jump, matched); + + ADD_LABEL(ret, type_error); + ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); + ADD_INSN1(ret, line, putobject, rb_eTypeError); + ADD_INSN1(ret, line, putobject, rb_fstring_lit("deconstruct must return Array")); + ADD_SEND(ret, line, id_core_raise, INT2FIX(2)); + + ADD_LABEL(ret, match_failed); + ADD_INSN(ret, line, pop); + ADD_INSNL(ret, line, jump, unmatched); + + break; + } case NODE_HSHPTN: { /* * keys = nil diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c index 0799944a5b..a3f3ef4845 100644 --- a/ext/objspace/objspace.c +++ b/ext/objspace/objspace.c @@ -479,6 +479,7 @@ count_nodes(int argc, VALUE *argv, VALUE os) COUNT_NODE(NODE_ATTRASGN); COUNT_NODE(NODE_LAMBDA); COUNT_NODE(NODE_ARYPTN); + COUNT_NODE(NODE_FNDPTN); COUNT_NODE(NODE_HSHPTN); #undef COUNT_NODE case NODE_LAST: break; diff --git a/node.c b/node.c index 9decd803ce..4ef18db544 100644 --- a/node.c +++ b/node.c @@ -1053,6 +1053,27 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node) F_NODE(nd_apinfo->post_args, "post arguments"); return; + case NODE_FNDPTN: + ANN("find pattern"); + ANN("format: [nd_pconst](*[pre_rest_arg], args, ..., *[post_rest_arg])"); + F_NODE(nd_pconst, "constant"); + if (NODE_NAMED_REST_P(node->nd_fpinfo->pre_rest_arg)) { + F_NODE(nd_fpinfo->pre_rest_arg, "pre rest argument"); + } + else { + F_MSG(nd_fpinfo->pre_rest_arg, "pre rest argument", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)"); + } + F_NODE(nd_fpinfo->args, "arguments"); + + LAST_NODE; + if (NODE_NAMED_REST_P(node->nd_fpinfo->post_rest_arg)) { + F_NODE(nd_fpinfo->post_rest_arg, "post rest argument"); + } + else { + F_MSG(nd_fpinfo->post_rest_arg, "post rest argument", "NODE_SPECIAL_NO_NAME_REST (rest argument without name)"); + } + return; + case NODE_HSHPTN: ANN("hash pattern"); ANN("format: [nd_pconst]([nd_pkwargs], ..., **[nd_pkwrestarg])"); @@ -1204,6 +1225,7 @@ rb_ast_newnode(rb_ast_t *ast, enum node_type type) case NODE_ARGS: case NODE_SCOPE: case NODE_ARYPTN: + case NODE_FNDPTN: return ast_newnode_in_bucket(&nb->markable); default: return ast_newnode_in_bucket(&nb->unmarkable); @@ -1271,6 +1293,12 @@ mark_ast_value(void *ctx, NODE * node) rb_gc_mark_movable(apinfo->imemo); break; } + case NODE_FNDPTN: + { + struct rb_fnd_pattern_info *fpinfo = node->nd_fpinfo; + rb_gc_mark_movable(fpinfo->imemo); + break; + } case NODE_ARGS: { struct rb_args_info *args = node->nd_ainfo; @@ -1311,6 +1339,12 @@ update_ast_value(void *ctx, NODE * node) apinfo->imemo = rb_gc_location(apinfo->imemo); break; } + case NODE_FNDPTN: + { + struct rb_fnd_pattern_info *fpinfo = node->nd_fpinfo; + fpinfo->imemo = rb_gc_location(fpinfo->imemo); + break; + } case NODE_ARGS: { struct rb_args_info *args = node->nd_ainfo; diff --git a/node.h b/node.h index 805491b87e..60e9604f46 100644 --- a/node.h +++ b/node.h @@ -123,6 +123,7 @@ enum node_type { NODE_LAMBDA, NODE_ARYPTN, NODE_HSHPTN, + NODE_FNDPTN, NODE_LAST }; @@ -166,6 +167,7 @@ typedef struct RNode { struct rb_global_entry *entry; struct rb_args_info *args; struct rb_ary_pattern_info *apinfo; + struct rb_fnd_pattern_info *fpinfo; VALUE value; } u3; rb_code_location_t nd_loc; @@ -278,6 +280,8 @@ typedef struct RNode { #define nd_apinfo u3.apinfo +#define nd_fpinfo u3.fpinfo + #define NEW_NODE(t,a0,a1,a2,loc) rb_node_newnode((t),(VALUE)(a0),(VALUE)(a1),(VALUE)(a2),loc) #define NEW_NODE_WITH_LOCALS(t,a1,a2,loc) node_newnode_with_locals(p, (t),(VALUE)(a1),(VALUE)(a2),loc) @@ -456,6 +460,13 @@ struct rb_ary_pattern_info { VALUE imemo; }; +struct rb_fnd_pattern_info { + NODE *pre_rest_arg; + NODE *args; + NODE *post_rest_arg; + VALUE imemo; +}; + struct parser_params; void *rb_parser_malloc(struct parser_params *, size_t); void *rb_parser_realloc(struct parser_params *, void *, size_t); diff --git a/parse.y b/parse.y index 38640664d0..2c8b1ab97a 100644 --- a/parse.y +++ b/parse.y @@ -496,6 +496,8 @@ static NODE *new_args(struct parser_params*,NODE*,NODE*,ID,NODE*,NODE*,const YYL static NODE *new_args_tail(struct parser_params*,NODE*,ID,ID,const YYLTYPE*); static NODE *new_array_pattern(struct parser_params *p, NODE *constant, NODE *pre_arg, NODE *aryptn, const YYLTYPE *loc); static NODE *new_array_pattern_tail(struct parser_params *p, NODE *pre_args, int has_rest, ID rest_arg, NODE *post_args, const YYLTYPE *loc); +static NODE *new_find_pattern(struct parser_params *p, NODE *constant, NODE *fndptn, const YYLTYPE *loc); +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 NODE *new_case3(struct parser_params *p, NODE *val, NODE *pat, const YYLTYPE *loc); @@ -871,6 +873,42 @@ new_array_pattern_tail(struct parser_params *p, VALUE pre_args, VALUE has_rest, return (VALUE)t; } +static VALUE +new_find_pattern(struct parser_params *p, VALUE constant, VALUE fndptn, const YYLTYPE *loc) +{ + NODE *t = (NODE *)fndptn; + struct rb_fnd_pattern_info *fpinfo = t->nd_fpinfo; + VALUE pre_rest_arg = Qnil, args = Qnil, post_rest_arg = Qnil; + + if (fpinfo) { + pre_rest_arg = rb_ary_entry(fpinfo->imemo, 0); + args = rb_ary_entry(fpinfo->imemo, 1); + post_rest_arg = rb_ary_entry(fpinfo->imemo, 2); + } + + return dispatch4(fndptn, constant, pre_rest_arg, args, post_rest_arg); +} + +static VALUE +new_find_pattern_tail(struct parser_params *p, VALUE pre_rest_arg, VALUE args, VALUE post_rest_arg, const YYLTYPE *loc) +{ + NODE *t; + struct rb_fnd_pattern_info *fpinfo; + + pre_rest_arg = dispatch1(var_field, pre_rest_arg ? pre_rest_arg : Qnil); + post_rest_arg = dispatch1(var_field, post_rest_arg ? post_rest_arg : Qnil); + + VALUE tmpbuf = rb_imemo_tmpbuf_auto_free_pointer(); + fpinfo = ZALLOC(struct rb_fnd_pattern_info); + rb_imemo_tmpbuf_set_ptr(tmpbuf, fpinfo); + fpinfo->imemo = rb_ary_new_from_args(4, pre_rest_arg, args, post_rest_arg, tmpbuf); + + t = rb_node_newnode(NODE_FNDPTN, Qnil, Qnil, (VALUE)fpinfo, &NULL_LOC); + RB_OBJ_WRITTEN(p->ast, Qnil, fpinfo->imemo); + + return (VALUE)t; +} + #define new_hash(p,h,l) rb_ary_new_from_args(0) static VALUE @@ -1139,14 +1177,14 @@ static int looking_at_eol_p(struct parser_params *p); %type brace_block cmd_brace_block do_block lhs none fitem %type mlhs mlhs_head mlhs_basic mlhs_item mlhs_node mlhs_post mlhs_inner %type p_case_body p_cases p_top_expr p_top_expr_body -%type p_expr p_as p_alt p_expr_basic +%type p_expr p_as p_alt p_expr_basic p_find %type p_args p_args_head p_args_tail p_args_post p_arg %type p_value p_primitive p_variable p_var_ref p_const %type p_kwargs p_kwarg p_kw %type keyword_variable user_variable sym operation operation2 operation3 %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_kwrest p_kwnorest p_any_kwrest p_kw_label +%type p_rest p_kwrest p_kwnorest p_any_kwrest p_kw_label %type f_no_kwarg f_any_kwrest args_forward excessed_comma %token END_OF_INPUT 0 "end-of-input" %token '.' @@ -3963,6 +4001,10 @@ p_top_expr_body : p_expr /*% %*/ } + | p_find + { + $$ = new_find_pattern(p, Qnone, $1, &@$); + } | p_args_tail { $$ = new_array_pattern(p, Qnone, Qnone, $1, &@$); @@ -4011,6 +4053,15 @@ p_expr_basic : p_value /*% %*/ } + | p_const p_lparen p_find rparen + { + pop_pktbl(p, $2); + $$ = new_find_pattern(p, $1, $3, &@$); + /*%%%*/ + nd_set_first_loc($$, @1.beg_pos); + /*% + %*/ + } | p_const p_lparen p_kwargs rparen { pop_pktbl(p, $2); @@ -4034,6 +4085,15 @@ p_expr_basic : p_value /*% %*/ } + | p_const p_lbracket p_find rbracket + { + pop_pktbl(p, $2); + $$ = new_find_pattern(p, $1, $3, &@$); + /*%%%*/ + nd_set_first_loc($$, @1.beg_pos); + /*% + %*/ + } | p_const p_lbracket p_kwargs rbracket { pop_pktbl(p, $2); @@ -4052,6 +4112,10 @@ p_expr_basic : p_value { $$ = new_array_pattern(p, Qnone, Qnone, $2, &@$); } + | tLBRACK p_find rbracket + { + $$ = new_find_pattern(p, Qnone, $2, &@$); + } | tLBRACK rbracket { $$ = new_array_pattern_tail(p, Qnone, 0, 0, Qnone, &@$); @@ -4135,21 +4199,30 @@ p_args_head : p_arg ',' } ; -p_args_tail : tSTAR tIDENTIFIER +p_args_tail : p_rest { - $$ = new_array_pattern_tail(p, Qnone, 1, $2, Qnone, &@$); + $$ = new_array_pattern_tail(p, Qnone, 1, $1, Qnone, &@$); } - | tSTAR tIDENTIFIER ',' p_args_post + | p_rest ',' p_args_post { - $$ = new_array_pattern_tail(p, Qnone, 1, $2, $4, &@$); + $$ = new_array_pattern_tail(p, Qnone, 1, $1, $3, &@$); + } + ; + +p_find : p_rest ',' p_args_post ',' p_rest + { + $$ = new_find_pattern_tail(p, $1, $3, $5, &@$); + } + ; + + +p_rest : tSTAR tIDENTIFIER + { + $$ = $2; } | tSTAR { - $$ = new_array_pattern_tail(p, Qnone, 1, 0, Qnone, &@$); - } - | tSTAR ',' p_args_post - { - $$ = new_array_pattern_tail(p, Qnone, 1, 0, $3, &@$); + $$ = 0; } ; @@ -11536,6 +11609,34 @@ new_array_pattern_tail(struct parser_params *p, NODE *pre_args, int has_rest, ID return node; } +static NODE* +new_find_pattern(struct parser_params *p, NODE *constant, NODE *fndptn, const YYLTYPE *loc) +{ + fndptn->nd_pconst = constant; + + return fndptn; +} + +static NODE* +new_find_pattern_tail(struct parser_params *p, ID pre_rest_arg, NODE *args, ID post_rest_arg, const YYLTYPE *loc) +{ + int saved_line = p->ruby_sourceline; + NODE *node; + VALUE tmpbuf = rb_imemo_tmpbuf_auto_free_pointer(); + struct rb_fnd_pattern_info *fpinfo = ZALLOC(struct rb_fnd_pattern_info); + rb_imemo_tmpbuf_set_ptr(tmpbuf, fpinfo); + node = NEW_NODE(NODE_FNDPTN, 0, 0, fpinfo, loc); + fpinfo->imemo = tmpbuf; + RB_OBJ_WRITTEN(p->ast, Qnil, tmpbuf); + + fpinfo->pre_rest_arg = pre_rest_arg ? assignable(p, pre_rest_arg, 0, loc) : NODE_SPECIAL_NO_NAME_REST; + fpinfo->args = args; + fpinfo->post_rest_arg = post_rest_arg ? assignable(p, post_rest_arg, 0, loc) : NODE_SPECIAL_NO_NAME_REST; + + p->ruby_sourceline = saved_line; + return node; +} + static NODE* new_hash_pattern(struct parser_params *p, NODE *constant, NODE *hshptn, const YYLTYPE *loc) { diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb index 544896b15f..d61dba340d 100644 --- a/test/ripper/test_parser_events.rb +++ b/test/ripper/test_parser_events.rb @@ -1548,6 +1548,12 @@ class TestRipper::ParserEvents < Test::Unit::TestCase assert_equal true, thru_aryptn end + def test_fndptn + thru_fndptn = false + parse('case 0; in [*,0,*]; end', :on_fndptn) {thru_fndptn = true} + assert_equal true, thru_fndptn + end + def test_hshptn thru_hshptn = false parse('case 0; in {a:}; end', :on_hshptn) {thru_hshptn = true} diff --git a/test/ripper/test_sexp.rb b/test/ripper/test_sexp.rb index 89b45941a3..47339ba496 100644 --- a/test/ripper/test_sexp.rb +++ b/test/ripper/test_sexp.rb @@ -412,6 +412,43 @@ eot [:@int, "0", [1, 5]], [:in, [:aryptn, nil, nil, nil, nil], [[:void_stmt]], nil]], + [__LINE__, %q{ 0 in [*, a, *] }] => + [:case, + [:@int, "0", [1, 0]], + [:in, + [:fndptn, + nil, + [:var_field, nil], + [[:var_field, [:@ident, "a", [1, 9]]]], + [:var_field, nil]], + nil, + nil]], + + [__LINE__, %q{ 0 in [*a, b, *c] }] => + [:case, + [:@int, "0", [1, 0]], + [:in, + [:fndptn, + nil, + [:var_field, [:@ident, "a", [1, 7]]], + [[:var_field, [:@ident, "b", [1, 10]]]], + [:var_field, [:@ident, "c", [1, 14]]]], + nil, + nil]], + + [__LINE__, %q{ 0 in A(*a, b, c, *d) }] => + [:case, + [:@int, "0", [1, 0]], + [:in, + [:fndptn, + [:var_ref, [:@const, "A", [1, 5]]], + [:var_field, [:@ident, "a", [1, 8]]], + [[:var_field, [:@ident, "b", [1, 11]]], + [:var_field, [:@ident, "c", [1, 14]]]], + [:var_field, [:@ident, "d", [1, 18]]]], + nil, + nil]], + [__LINE__, %q{ case 0; in {a: 0}; end }] => [:case, [:@int, "0", [1, 5]], diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb index 2afa823d53..de8515b397 100644 --- a/test/ruby/test_pattern_matching.rb +++ b/test/ruby/test_pattern_matching.rb @@ -736,6 +736,63 @@ END end end + def test_find_pattern + [0, 1, 2] in [*, 1 => a, *] + assert_equal(1, a) + + [0, 1, 2] in [*a, 1 => b, *c] + assert_equal([0], a) + assert_equal(1, b) + assert_equal([2], c) + + assert_block do + case [0, 1, 2] + in [*, 9, *] + false + else + true + end + end + + assert_block do + case [0, 1, 2] + in [*, Integer, String, *] + false + else + true + end + end + + [0, 1, 2] in [*a, 1 => b, 2 => c, *d] + assert_equal([0], a) + assert_equal(1, b) + assert_equal(2, c) + assert_equal([], d) + + case [0, 1, 2] + in *, 1 => a, *; + assert_equal(1, a) + end + + assert_block do + case [0, 1, 2] + in String(*, 1, *) + false + in Array(*, 1, *) + true + end + end + + assert_block do + case [0, 1, 2] + in String[*, 1, *] + false + in Array[*, 1, *] + true + end + end + end + def test_hash_pattern assert_block do [{}, C.new({})].all? do |i|