ruby-changes:61706
From: Kazuki <ko1@a...>
Date: Sun, 14 Jun 2020 09:28:10 +0900 (JST)
Subject: [ruby-changes:61706] ddded1157a (master): Introduce find pattern [Feature #16828]
https://git.ruby-lang.org/ruby.git/commit/?id=ddded1157a From ddded1157a90d21cb54b9f07de35ab9b4cc472e1 Mon Sep 17 00:00:00 2001 From: Kazuki Tsujimoto <kazuki@c...> Date: Sun, 14 Jun 2020 09:24:36 +0900 Subject: Introduce find pattern [Feature #16828] diff --git a/NEWS.md b/NEWS.md index 6856a29..8b92aa4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -38,6 +38,18 @@ sufficient information, see the ChangeLog file or Redmine https://github.com/ruby/ruby/blob/trunk/NEWS.md#L38 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 18a9723..44fec16 100644 --- a/ast.c +++ b/ast.c @@ -599,6 +599,19 @@ node_children(rb_ast_t *ast, NODE *node) https://github.com/ruby/ruby/blob/trunk/ast.c#L599 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 5a7a088..9fba24f 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 https://github.com/ruby/ruby/blob/trunk/compile.c#L5752 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 0799944..a3f3ef4 100644 --- a/ext/objspace/objspace.c +++ b/ext/objspace/objspace.c @@ -479,6 +479,7 @@ count_nodes(int argc, VALUE *argv, VALUE os) https://github.com/ruby/ruby/blob/trunk/ext/objspace/objspace.c#L479 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 9decd80..4ef18db 100644 --- a/node.c +++ b/node.c @@ -1053,6 +1053,27 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node) https://github.com/ruby/ruby/blob/trunk/node.c#L1053 F_NODE(nd_apinfo->post_args (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/