diff --git compile.c compile.c index 099c280..22ee757 100644 --- compile.c +++ compile.c @@ -3838,15 +3838,25 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) ADD_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, 0, lstart); break; } + case NODE_RESCOND: case NODE_RESBODY:{ NODE *resq = node; - NODE *narg; - LABEL *label_miss, *label_hit; + NODE *narg, *cond; + LABEL *label_miss, *label_hit, *label_pass; while (resq) { label_miss = NEW_LABEL(line); label_hit = NEW_LABEL(line); + if (nd_type(resq) == NODE_RESCOND) { + label_pass = NEW_LABEL(line); + cond = resq->nd_cond; + resq = resq->nd_resq; + } + else { + cond = 0; + } + narg = resq->nd_args; if (narg) { switch (nd_type(narg)) { @@ -3880,6 +3890,22 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) } ADD_INSNL(ret, line, jump, label_miss); ADD_LABEL(ret, label_hit); + + if (cond) { + switch (nd_type(cond)) { + case NODE_RESCOND_IF: + compile_branch_condition(iseq, ret, cond->nd_cond, label_pass, label_miss); + break; + case NODE_RESCOND_UNLESS: + compile_branch_condition(iseq, ret, cond->nd_cond, label_miss, label_pass); + break; + default: + rb_bug("NODE_RESCOND: unknown node (%s)", + ruby_node_name(nd_type(cond))); + } + ADD_LABEL(ret, label_pass); + } + COMPILE(ret, "resbody body", resq->nd_body); if (iseq->compile_data->option->tailcall_optimization) { ADD_INSN(ret, line, nop); diff --git ext/objspace/objspace.c ext/objspace/objspace.c index b1f1358..d5e2c9a 100644 --- ext/objspace/objspace.c +++ ext/objspace/objspace.c @@ -319,6 +319,9 @@ count_nodes(int argc, VALUE *argv, VALUE os) COUNT_NODE(NODE_BEGIN); COUNT_NODE(NODE_RESCUE); COUNT_NODE(NODE_RESBODY); + COUNT_NODE(NODE_RESCOND); + COUNT_NODE(NODE_RESCOND_IF); + COUNT_NODE(NODE_RESCOND_UNLESS); COUNT_NODE(NODE_ENSURE); COUNT_NODE(NODE_AND); COUNT_NODE(NODE_OR); diff --git gc.c gc.c index cc37327..f24399d 100644 --- gc.c +++ gc.c @@ -3751,6 +3751,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr) case NODE_ALIAS: case NODE_VALIAS: case NODE_ARGSCAT: + case NODE_RESCOND: gc_mark(objspace, (VALUE)obj->as.node.u1.node); /* fall through */ case NODE_GASGN: /* 2 */ @@ -3781,6 +3782,8 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr) case NODE_COLON2: case NODE_SPLAT: case NODE_TO_ARY: + case NODE_RESCOND_IF: + case NODE_RESCOND_UNLESS: ptr = (VALUE)obj->as.node.u1.node; goto again; diff --git node.c node.c index fbefb3c..0b56212 100644 --- node.c +++ node.c @@ -234,6 +234,28 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node) F_NODE(nd_head, "next rescue clause"); break; + case NODE_RESCOND: + ANN("conditional rescue"); + ANN("format: (rescue) [nd_body] (if or unless) [nd_cond]"); + F_NODE(nd_cond, "rescue condition"); + LAST_NODE; + F_NODE(nd_resq, "rescue clause"); + break; + + case NODE_RESCOND_IF: + ANN("conditional rescue if modifier"); + ANN("format: if [nd_cond]"); + LAST_NODE; + F_NODE(nd_cond, "condition"); + break; + + case NODE_RESCOND_UNLESS: + ANN("conditional rescue unless modifier"); + ANN("format: unless [nd_cond]"); + LAST_NODE; + F_NODE(nd_cond, "condition"); + break; + case NODE_ENSURE: ANN("ensure clause"); ANN("format: begin; [nd_head]; ensure; [nd_ensr]; end"); diff --git node.h node.h index 9ee0704..67ea1c6 100644 --- node.h +++ node.h @@ -54,6 +54,12 @@ enum node_type { #define NODE_RESCUE NODE_RESCUE NODE_RESBODY, #define NODE_RESBODY NODE_RESBODY + NODE_RESCOND, +#define NODE_RESCOND NODE_RESCOND + NODE_RESCOND_IF, +#define NODE_RESCOND_IF NODE_RESCOND_IF + NODE_RESCOND_UNLESS, +#define NODE_RESCOND_UNLESS NODE_RESCOND_UNLESS NODE_ENSURE, #define NODE_ENSURE NODE_ENSURE NODE_AND, @@ -385,6 +391,9 @@ typedef struct RNode { #define NEW_BEGIN(b) NEW_NODE(NODE_BEGIN,0,b,0) #define NEW_RESCUE(b,res,e) NEW_NODE(NODE_RESCUE,b,res,e) #define NEW_RESBODY(a,ex,n) NEW_NODE(NODE_RESBODY,n,ex,a) +#define NEW_RESCOND(c,b) NEW_NODE(NODE_RESCOND,c,b,0) +#define NEW_RESCOND_IF(c) NEW_NODE(NODE_RESCOND_IF,c,0,0) +#define NEW_RESCOND_UNLESS(c) NEW_NODE(NODE_RESCOND_UNLESS,c,0,0) #define NEW_ENSURE(b,en) NEW_NODE(NODE_ENSURE,b,0,en) #define NEW_RETURN(s) NEW_NODE(NODE_RETURN,s,0,0) #define NEW_YIELD(a) NEW_NODE(NODE_YIELD,a,0,0) diff --git parse.y parse.y index 5c62e36..17774dc 100644 --- parse.y +++ parse.y @@ -771,7 +771,7 @@ static void token_info_pop(struct parser_params*, const char *token); %type top_compstmt top_stmts top_stmt %type bodystmt compstmt stmts stmt_or_begin stmt expr arg primary command command_call method_call %type expr_value arg_value primary_value fcall -%type if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure +%type if_tail opt_else case_body cases opt_rescue exc_cond exc_list exc_var opt_ensure %type args call_args opt_call_args %type paren_args opt_paren_args args_tail opt_args_tail block_args_tail opt_block_args_tail %type command_args aref_args opt_block_arg block_arg var_ref var_lhs @@ -3745,23 +3745,51 @@ cases : opt_else | case_body ; -opt_rescue : keyword_rescue exc_list exc_var then +opt_rescue : keyword_rescue exc_list exc_var exc_cond then compstmt opt_rescue { /*%%%*/ if ($3) { $3 = node_assign($3, NEW_ERRINFO()); - $5 = block_append($3, $5); + if ($4) { + $4->nd_cond = block_append($3, $4->nd_cond); + } + else { + $6 = block_append($3, $6); + } + } + $$ = NEW_RESBODY($2, $6, $7); + if ($4) { + $$ = NEW_RESCOND($4, $$); } - $$ = NEW_RESBODY($2, $5, $6); - fixpos($$, $2?$2:$5); + fixpos($$, $2?$2:$6); /*% - $$ = dispatch4(rescue, + $$ = dispatch5(rescue, escape_Qundef($2), escape_Qundef($3), - escape_Qundef($5), - escape_Qundef($6)); + escape_Qundef($4), + escape_Qundef($6), + escape_Qundef($7)); + %*/ + } + | none + ; + +exc_cond : modifier_if expr_value + { + /*%%%*/ + $$ = NEW_RESCOND_IF(cond($2)); + /*% + $$ = dispatch1(rescond_if, $2); + %*/ + } + | modifier_unless expr_value + { + /*%%%*/ + $$ = NEW_RESCOND_UNLESS(cond($2)); + /*% + $$ = dispatch1(rescond_unless, $2); %*/ } | none