[ruby-dev:28217] ANDCALL operator (Re: [ruby-list:41768] Re: nilの扱い)
From:
nobuyoshi nakada <nobuyoshi.nakada@...>
Date:
2006-01-13 09:17:21 UTC
List:
ruby-dev #28217
なかだです。
At Fri, 6 Jan 2006 19:14:10 +0900,
Kazuhiro Yoshida wrote in [ruby-list:41768]:
> ほかのパターンとしては、array, hash の入れ子のアクセスがあると思います。
> 例えば、入れ子の hash として扱う h があるとき、h[:a] が nil である場合を
> 考慮して
> if h[:a] && h[:a][:b]
> と書かないといけないのが面倒です。
要するに (tmp = h[:a] and tmp[:b]) を一時変数を使わずに書きたい
ということだろうと思いますが、
> これは
> h = Hash.new {|hash, key| hash[key] = {} }
調べただけで新しいエントリができてしまうのはどうかというのは置
いておくとしても、Hash以外のオブジェクトに使える方法ではないと
いうのが難点です。
そこで、左辺が真ならそれをレシーバとして右辺の式を実行するとい
う演算子を考えてみました。(見た目はよくないですが)仮に &? とす
ると上の例は h[:a]&?[:b]、h.a and h.a.b のようなメソッドチェー
ンなら h.a&?b という形になります。
Index: eval.c
===================================================================
RCS file: /cvs/ruby/src/ruby/eval.c,v
retrieving revision 1.869
diff -U2 -p -r1.869 eval.c
--- eval.c 8 Jan 2006 19:24:21 -0000 1.869
+++ eval.c 13 Jan 2006 08:12:08 -0000
@@ -3263,4 +3263,22 @@ rb_eval(VALUE self, NODE *n)
break;
+ case NODE_ANDCALL:
+ result = rb_eval(self, node->nd_recv);
+ if (!RTEST(result)) break;
+ {
+ VALUE recv = result;
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ TMP_PROTECT;
+
+ BEGIN_CALLARGS;
+ SETUP_ARGS(node->nd_args);
+ END_CALLARGS;
+
+ ruby_current_node = node;
+ SET_CURRENT_SOURCE();
+ result = rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,CALLING_NORMAL);
+ }
+ break;
+
case NODE_CALL:
{
Index: node.h
===================================================================
RCS file: /cvs/ruby/src/ruby/node.h,v
retrieving revision 1.72
diff -U2 -p -r1.72 node.h
--- node.h 29 Nov 2005 14:57:18 -0000 1.72
+++ node.h 13 Jan 2006 08:12:08 -0000
@@ -125,4 +125,5 @@ enum node_type {
NODE_PRELUDE,
NODE_LAMBDA,
+ NODE_ANDCALL,
NODE_LAST
};
@@ -308,4 +309,5 @@ typedef struct RNode {
#define NEW_FCALL(m,a) NEW_NODE(NODE_FCALL,0,m,a)
#define NEW_VCALL(m) NEW_NODE(NODE_VCALL,0,m,0)
+#define NEW_ANDCALL(r,m,a) NEW_NODE(NODE_ANDCALL,r,m,a)
#define NEW_SUPER(a) NEW_NODE(NODE_SUPER,0,0,a)
#define NEW_ZSUPER() NEW_NODE(NODE_ZSUPER,0,0,0)
Index: parse.y
===================================================================
RCS file: /cvs/ruby/src/ruby/parse.y,v
retrieving revision 1.429
diff -U2 -p -r1.429 parse.y
--- parse.y 7 Jan 2006 06:40:37 -0000 1.429
+++ parse.y 13 Jan 2006 08:18:06 -0000
@@ -280,6 +280,6 @@ static NODE *ret_args(NODE*);
static NODE *arg_blk_pass(NODE*,NODE*);
static NODE *new_call(NODE*,ID,NODE*);
-static NODE *new_fcall_gen(struct parser_params*,ID,NODE*);
-#define new_fcall(id,args) new_fcall_gen(parser, id, args)
+static NODE *new_andcall(NODE*,ID,NODE*);
+static NODE *new_fcall(ID,NODE*);
static NODE *new_super(NODE*);
static NODE *new_yield(NODE*);
@@ -562,4 +562,5 @@ static void ripper_compile_error(struct
%token tGEQ /* >= */
%token tLEQ /* <= */
+%token tANDCALL /* &? */
%token tANDOP tOROP /* && and || */
%token tMATCH tNMATCH /* =~ and !~ */
@@ -601,4 +602,5 @@ static void ripper_compile_error(struct
%left tOROP
%left tANDOP
+%left tANDCALL
%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
%left '>' tGEQ '<' tLEQ
@@ -1229,4 +1231,28 @@ command : operation command_args
%*/
}
+ | primary_value tANDCALL operation2 command_args %prec tLOWEST
+ {
+ /*%%%*/
+ $$ = new_andcall($1, $3, $4);
+ fixpos($$, $1);
+ /*%
+ $$ = dispatch4(command_call, $1, ripper_intern("::"), $3, $4);
+ %*/
+ }
+ | primary_value tANDCALL operation2 command_args cmd_brace_block
+ {
+ /*%%%*/
+ $$ = new_andcall($1, $3, $4);
+ if ($5) {
+ block_dup_check($$);
+ $5->nd_iter = $$;
+ $$ = $5;
+ }
+ fixpos($$, $1);
+ /*%
+ $$ = dispatch4(command_call, $1, ripper_intern("::"), $3, $4);
+ $$ = dispatch2(iter_block, $$, $5);
+ %*/
+ }
| kSUPER command_args
{
@@ -3348,4 +3374,14 @@ method_call : operation paren_args
%*/
}
+ | primary_value tANDCALL operation2 opt_paren_args
+ {
+ /*%%%*/
+ $$ = new_andcall($1, $3, $4);
+ fixpos($$, $1);
+ /*%
+ $$ = dispatch3(call, $1, ripper_id2sym('.'), $3);
+ $$ = method_optarg($$, $4);
+ %*/
+ }
| kSUPER paren_args
{
@@ -3388,4 +3424,16 @@ method_call : operation paren_args
%*/
}
+ | primary_value tANDCALL '[' aref_args ']'
+ {
+ /*%%%*/
+ if ($1 && nd_type($1) == NODE_SELF)
+ $$ = logop(NODE_AND, $1, NEW_FCALL(tAREF, $4));
+ else
+ $$ = NEW_ANDCALL($1, tAREF, $4);
+ fixpos($$, $1);
+ /*%
+ $$ = dispatch2(aref, $1, escape_Qundef($4));
+ %*/
+ }
;
@@ -5934,5 +5982,6 @@ parser_yylex(struct parser_params *parse
case '&':
- if ((c = nextc()) == '&') {
+ switch (c = nextc()) {
+ case '&':
lex_state = EXPR_BEG;
if ((c = nextc()) == '=') {
@@ -5943,6 +5992,7 @@ parser_yylex(struct parser_params *parse
pushback(c);
return tANDOP;
- }
- else if (c == '=') {
+ case '?':
+ return tANDCALL;
+ case '=':
set_yylval_id('&');
lex_state = EXPR_BEG;
@@ -7959,5 +8009,15 @@ new_call(NODE *r, ID m, NODE *a)
static NODE*
-new_fcall_gen(struct parser_params *parser, ID m, NODE *a)
+new_andcall(NODE *r, ID m, NODE *a)
+{
+ if (a && nd_type(a) == NODE_BLOCK_PASS) {
+ a->nd_iter = NEW_ANDCALL(r,m,a->nd_head);
+ return a;
+ }
+ return NEW_ANDCALL(r,m,a);
+}
+
+static NODE*
+new_fcall(ID m, NODE *a)
{
if (a && nd_type(a) == NODE_BLOCK_PASS) {
--
--- 僕の前にBugはない。
--- 僕の後ろにBugはできる。
中田 伸悦