Hi,

At Sun, 12 Jun 2005 00:45:56 +0900,
Gavin Kistner wrote in [ruby-talk:145166]:
> Would it be possible to have the parser recognize the single-line 'if/ 
> unless' case and process the right side before the left side (the  
> order in which the code is executed)?

Not impossible, but a dirty hack because current implementation is one
pass parser.


Index: parse.y =================================================================== RCS file: /cvs/ruby/src/ruby/parse.y,v retrieving revision 1.387 diff -U2 -p -u -r1.387 parse.y --- parse.y 12 Jun 2005 16:56:05 -0000 1.387 +++ parse.y 13 Jun 2005 09:11:49 -0000 @@ -150,4 +150,5 @@ struct parser_params { NODE *parser_eval_tree_begin; NODE *parser_eval_tree; + NODE *vcalls; #else /* Ripper only */ @@ -304,8 +305,16 @@ static void top_local_setup_gen _((struc #else #define remove_begin(node) (node) +/* FIXME */ +# define local_cnt(x) 3 +# define local_id(x) 1 +# define dyna_in_block() 1 #endif /* !RIPPER */ static int lvar_defined_gen _((struct parser_params*, ID)); #define lvar_defined(id) lvar_defined_gen(parser, id) +#define lvar_count() (dyna_in_block() ? lvtbl->dname_size : lvtbl->cnt) +static void replace_vcall _((struct parser_params *, int)); +static void merge_vcalls _((struct parser_params *)); + #define RE_OPTION_ONCE 0x80 @@ -678,4 +687,5 @@ stmts : none /*%%%*/ $$ = block_append($1, newline_node(remove_begin($3))); + merge_vcalls(parser); /*% $$ = dispatch2(stmts_add, $1, $3); @@ -733,9 +743,14 @@ stmt : kALIAS fitem {lex_state = EXPR_F %*/ } - | stmt kIF_MOD expr_value + | stmt kIF_MOD + { + $<num>$ = lvar_count(); + } + expr_value { /*%%%*/ - $$ = NEW_IF(cond($3), $1, 0); - fixpos($$, $3); + replace_vcall(parser, $<num>3); + $$ = NEW_IF(cond($4), $1, 0); + fixpos($$, $4); if (cond_negative(&$$->nd_cond)) { $$->nd_else = $$->nd_body; @@ -743,12 +758,17 @@ stmt : kALIAS fitem {lex_state = EXPR_F } /*% - $$ = dispatch2(if_mod, $3, $1); + $$ = dispatch2(if_mod, $4, $1); %*/ } - | stmt kUNLESS_MOD expr_value + | stmt kUNLESS_MOD + { + $<num>$ = lvar_count(); + } + expr_value { /*%%%*/ - $$ = NEW_UNLESS(cond($3), $1, 0); - fixpos($$, $3); + replace_vcall(parser, $<num>3); + $$ = NEW_UNLESS(cond($4), $1, 0); + fixpos($$, $4); if (cond_negative(&$$->nd_cond)) { $$->nd_body = $$->nd_else; @@ -756,15 +776,20 @@ stmt : kALIAS fitem {lex_state = EXPR_F } /*% - $$ = dispatch2(unless_mod, $3, $1); + $$ = dispatch2(unless_mod, $4, $1); %*/ } - | stmt kWHILE_MOD expr_value + | stmt kWHILE_MOD + { + $<num>$ = lvar_count(); + } + expr_value { /*%%%*/ + replace_vcall(parser, $<num>3); if ($1 && nd_type($1) == NODE_BEGIN) { - $$ = NEW_WHILE(cond($3), $1->nd_body, 0); + $$ = NEW_WHILE(cond($4), $1->nd_body, 0); } else { - $$ = NEW_WHILE(cond($3), $1, 1); + $$ = NEW_WHILE(cond($4), $1, 1); } if (cond_negative(&$$->nd_cond)) { @@ -772,15 +797,20 @@ stmt : kALIAS fitem {lex_state = EXPR_F } /*% - $$ = dispatch2(while_mod, $3, $1); + $$ = dispatch2(while_mod, $4, $1); %*/ } - | stmt kUNTIL_MOD expr_value + | stmt kUNTIL_MOD + { + $<num>$ = lvar_count(); + } + expr_value { /*%%%*/ + replace_vcall(parser, $<num>3); if ($1 && nd_type($1) == NODE_BEGIN) { - $$ = NEW_UNTIL(cond($3), $1->nd_body, 0); + $$ = NEW_UNTIL(cond($4), $1->nd_body, 0); } else { - $$ = NEW_UNTIL(cond($3), $1, 1); + $$ = NEW_UNTIL(cond($4), $1, 1); } if (cond_negative(&$$->nd_cond)) { @@ -788,5 +818,5 @@ stmt : kALIAS fitem {lex_state = EXPR_F } /*% - $$ = dispatch2(until_mod, $3, $1); + $$ = dispatch2(until_mod, $4, $1); %*/ } @@ -805,4 +835,5 @@ stmt : kALIAS fitem {lex_state = EXPR_F yyerror("BEGIN in method"); } + $<node>1 = parser->vcalls; local_push(0); /*% @@ -818,4 +849,5 @@ stmt : kALIAS fitem {lex_state = EXPR_F NEW_PREEXE($4)); local_pop(); + parser->vcalls = $<node>1; $$ = 0; /*% @@ -2732,4 +2764,5 @@ primary : literal yyerror("class definition in method body"); class_nest++; + $<node>1 = parser->vcalls; local_push(0); $<num>$ = ruby_sourceline; @@ -2747,4 +2780,5 @@ primary : literal nd_set_line($$, $<num>4); local_pop(); + parser->vcalls = $<node>1; class_nest--; /*% @@ -2768,4 +2802,5 @@ primary : literal in_single = 0; class_nest++; + $<node>1 = parser->vcalls; local_push(0); /*% @@ -2782,4 +2817,5 @@ primary : literal fixpos($$, $3); local_pop(); + parser->vcalls = $<node>1; class_nest--; in_def = $<num>4; @@ -2798,4 +2834,5 @@ primary : literal yyerror("module definition in method body"); class_nest++; + $<node>1 = parser->vcalls; local_push(0); $<num>$ = ruby_sourceline; @@ -2813,4 +2850,5 @@ primary : literal nd_set_line($$, $<num>3); local_pop(); + parser->vcalls = $<node>1; class_nest--; /*% @@ -2825,4 +2863,5 @@ primary : literal cur_mid = $2; in_def++; + $<node>1 = parser->vcalls; local_push(0); /*% @@ -2842,4 +2881,5 @@ primary : literal fixpos($$, $4); local_pop(); + parser->vcalls = $<node>1; in_def--; cur_mid = $<id>3; @@ -2854,4 +2894,5 @@ primary : literal /*%%%*/ in_single++; + $<node>1 = parser->vcalls; local_push(0); lex_state = EXPR_END; /* force for args */ @@ -2871,4 +2912,5 @@ primary : literal fixpos($$, $2); local_pop(); + parser->vcalls = $<node>1; in_single--; /*% @@ -4317,11 +4359,4 @@ static int parser_here_document _((struc # define whole_match_p(e,l,i) parser_whole_match_p(parser,e,l,i) -#ifdef RIPPER -/* FIXME */ -# define local_cnt(x) 3 -# define local_id(x) 1 -# define dyna_in_block() 1 -#endif /* RIPPER */ - #ifndef RIPPER # define set_yylval_str(x) yylval.node = NEW_STR(x) @@ -7105,5 +7140,5 @@ gettable_gen(parser, id) /* method call without arguments */ dyna_check(id); - return NEW_VCALL(id); + return parser->vcalls = NEW_NODE(NODE_VCALL,0,id,parser->vcalls); } else if (is_global_id(id)) { @@ -7915,4 +7950,6 @@ new_super(a) } +#define NEW_VCALL_LIST(n) NEW_NODE(NODE_VCALL,0,0,n) + static void local_push_gen(parser, top) @@ -7932,4 +7969,5 @@ local_push_gen(parser, top) local->dyna_vars = ruby_dyna_vars; lvtbl = local; + parser->vcalls = NEW_VCALL_LIST(parser->vcalls); if (!top) { /* preserve reference for GC, but link should be cut. */ @@ -7944,4 +7982,5 @@ local_pop_gen(parser) { struct local_vars *local = lvtbl->prev; + NODE *vcalls = parser->vcalls; if (lvtbl->tbl) { @@ -7952,4 +7991,10 @@ local_pop_gen(parser) xfree(lvtbl->dnames); } + while (vcalls) { + NODE *next = vcalls->nd_next; + vcalls->nd_next = 0; + vcalls = next; + } + parser->vcalls = 0; ruby_dyna_vars = lvtbl->dyna_vars; xfree(lvtbl); @@ -8155,4 +8200,61 @@ dyna_init_gen(parser, node, pre) } +static void +merge_vcalls(parser) + struct parser_params *parser; +{ + NODE **prev, *n; + + for (n = *(prev = &parser->vcalls); n != 0; n = n->nd_next) { + if (n->nd_mid == 0) { + *prev = n->nd_next; + n->nd_next = parser->vcalls; + parser->vcalls = n; + break; + } + } +} + +static void +replace_vcall(parser, vcnt) + struct parser_params *parser; + int vcnt; +{ + NODE **prev, *n; + ID id, *names; + int i, ncnt; + enum node_type type; + + if (dyna_in_block()) { + if (!(names = lvtbl->dnames)) return; + ncnt = lvtbl->dname_size; + type = NODE_DVAR; + } + else { + if (!(names = lvtbl->tbl)) return; + names++; + ncnt = lvtbl->cnt; + if (vcnt < 2) vcnt = 2; + type = NODE_LVAR; + } + + if (vcnt >= ncnt) return; + prev = &parser->vcalls; + while ((n = *prev) != 0 && (id = n->nd_mid) != 0) { + for (i = vcnt; i < ncnt; ++i) { + if (id == names[i]) { + *prev = n->nd_next; + nd_set_type(n, type); + n->nd_mid = 0; + n->nd_vid = id; + n->nd_cnt = (type == NODE_LVAR) ? i : 0; + goto skip; + } + } + prev = &n->nd_next; + skip:; + } +} + void rb_gc_mark_parser() @@ -8554,4 +8656,5 @@ parser_initialize(parser) parser->parser_eval_tree_begin = 0; parser->parser_eval_tree = 0; + parser->vcalls = 0; #else parser->parser_ruby_sourcefile = Qnil;
-- Nobu Nakada