aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Tsujimoto <kazuki@callcc.net>2020-06-14 09:24:36 +0900
committerKazuki Tsujimoto <kazuki@callcc.net>2020-06-14 09:24:36 +0900
commitddded1157a90d21cb54b9f07de35ab9b4cc472e1 (patch)
tree315005f4eead6840846e6ce4bbd4dfa44c3bfce7
parentf7906a7e31e6b1cfa135ecea69deb8827e8c8803 (diff)
downloadruby-ddded1157a90d21cb54b9f07de35ab9b4cc472e1.tar.gz
Introduce find pattern [Feature #16828]
-rw-r--r--NEWS.md12
-rw-r--r--ast.c13
-rw-r--r--compile.c167
-rw-r--r--ext/objspace/objspace.c1
-rw-r--r--node.c34
-rw-r--r--node.h11
-rw-r--r--parse.y121
-rw-r--r--test/ripper/test_parser_events.rb6
-rw-r--r--test/ripper/test_sexp.rb37
-rw-r--r--test/ruby/test_pattern_matching.rb57
10 files changed, 449 insertions, 10 deletions
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 <node> brace_block cmd_brace_block do_block lhs none fitem
%type <node> mlhs mlhs_head mlhs_basic mlhs_item mlhs_node mlhs_post mlhs_inner
%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
+%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_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
%type <id> f_kwrest f_label f_arg_asgn call_op call_op2 reswords relop dot_or_colon
-%type <id> p_kwrest p_kwnorest p_any_kwrest p_kw_label
+%type <id> p_rest p_kwrest p_kwnorest p_any_kwrest p_kw_label
%type <id> f_no_kwarg f_any_kwrest args_forward excessed_comma
%token END_OF_INPUT 0 "end-of-input"
%token <id> '.'
@@ -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, $<tbl>2);
+ $$ = new_find_pattern(p, $1, $3, &@$);
+ /*%%%*/
+ nd_set_first_loc($$, @1.beg_pos);
+ /*%
+ %*/
+ }
| p_const p_lparen p_kwargs rparen
{
pop_pktbl(p, $<tbl>2);
@@ -4034,6 +4085,15 @@ p_expr_basic : p_value
/*%
%*/
}
+ | p_const p_lbracket p_find rbracket
+ {
+ pop_pktbl(p, $<tbl>2);
+ $$ = new_find_pattern(p, $1, $3, &@$);
+ /*%%%*/
+ nd_set_first_loc($$, @1.beg_pos);
+ /*%
+ %*/
+ }
| p_const p_lbracket p_kwargs rbracket
{
pop_pktbl(p, $<tbl>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, &@$);
}
- | tSTAR
+ ;
+
+p_find : p_rest ',' p_args_post ',' p_rest
{
- $$ = new_array_pattern_tail(p, Qnone, 1, 0, Qnone, &@$);
+ $$ = new_find_pattern_tail(p, $1, $3, $5, &@$);
+ }
+ ;
+
+
+p_rest : tSTAR tIDENTIFIER
+ {
+ $$ = $2;
}
- | tSTAR ',' p_args_post
+ | tSTAR
{
- $$ = new_array_pattern_tail(p, Qnone, 1, 0, $3, &@$);
+ $$ = 0;
}
;
@@ -11537,6 +11610,34 @@ new_array_pattern_tail(struct parser_params *p, NODE *pre_args, int has_rest, ID
}
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)
{
hshptn->nd_pconst = constant;
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|