From: Eric Mahurin Date: 2008-05-25T16:30:42+09:00 Subject: [PATCH] lambda with normal block syntax ------=_Part_4908_25005344.1211700664123 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline This patch is an independent but related one to my previous one. It can be combined to allow for normal block syntax lambdas with default arguments. This patch allows you to make lambda's more concisely by simply removing the "lambda": plus = { |a,b| a+b } # |'s are required to disambiguate with a Hash plus[1, 2] # => 3 f = do 42 end # || not required f[] # => 42 def my_while(cond) yield while cond[] end i=10 my_while({ || (i-=1).nonzero?}) { p i } 9 8 7 6 5 4 3 2 1 I wasn't able to get rid of some reduce/reduce conflict warnings (at yacc/compile time). They are come from the do/end, not the {}. I tried putting in some %prec attributes to help, but didn't get anywhere. A yacc expert may have better luck. It looks like the default precedence is correct though. I assume the problem occurs between the first argument of a no parens call arg list and a block for that call. It looks to treat it as a block in this case (instead of a lambda for the first argument), which will maintain compatibility? Here would be a ChangeLog entry: * parse.y (primary) : allow for a . The grammar is almost identical to a normal block except |'s are required in the {} form (to disambiguate from a Hash). The semantics are equivalent to preceding the block with "lambda". * sample/test.rb : tests for the above. Index: sample/test.rb =================================================================== --- sample/test.rb (revision 16585) +++ sample/test.rb (working copy) @@ -1027,6 +1027,20 @@ call_argument_test(false, lambda{|a,|}) call_argument_test(false, lambda{|a,|}, 1,2) +call_argument_test(true, {||}) +call_argument_test(false, {||}, 1) +call_argument_test(true, {|a,|}, 1) +call_argument_test(false, {|a,|}) +call_argument_test(false, {|a,|}, 1,2) + +call_argument_test(true, do end) +call_argument_test(false, do end, 1) +call_argument_test(true, do || end) +call_argument_test(false, do || end, 1) +call_argument_test(true, do |a,| end, 1) +call_argument_test(false, do |a,| end) +call_argument_test(false, do |a,| end, 1,2) + call_argument_test(true, Proc.new{||}) call_argument_test(true, Proc.new{||}, 1) call_argument_test(true, Proc.new{|a,|}, 1) Index: parse.y =================================================================== --- parse.y (revision 16585) +++ parse.y (working copy) @@ -670,6 +670,7 @@ %type assoc_list assocs assoc undef_list backref string_dvar for_var %type block_param opt_block_param block_param_def f_opt %type bv_decls opt_bv_decl bvar +%type block_lambda %type lambda f_larglist lambda_body %type brace_block cmd_brace_block do_block lhs none fitem %type mlhs mlhs_head mlhs_basic mlhs_item mlhs_node mlhs_post mlhs_inner @@ -2641,6 +2642,10 @@ $$ = dispatch1(hash, escape_Qundef($2)); %*/ } + | block_lambda + { + $$ = $1; + } | keyword_return { /*%%%*/ @@ -3327,6 +3332,56 @@ } ; +block_lambda : tLBRACE + { + /*%%%*/ + dyna_push(); + $$ = ruby_sourceline; + /*% + %*/ + } + block_param_def compstmt '}' + { + /*%%%*/ + $$ = NEW_ITER($3, $4); + nd_set_line($$, $2); + dyna_pop(); + $$->nd_iter = NEW_FCALL(rb_intern("lambda"), 0); + fixpos($$->nd_iter, $$); + /*% + $$ = method_arg( + dispatch1(fcall, rb_intern("lambda")), + arg_new()); + $$ = method_add_block($$, + dispatch2(brace_block, escape_Qundef($3), $4)); + %*/ + } + | keyword_do_block + { + /*%%%*/ + dyna_push(); + $$ = ruby_sourceline; + /*% + %*/ + } + opt_block_param compstmt keyword_end + { + /*%%%*/ + $$ = NEW_ITER($3, $4); + nd_set_line($$, $2); + dyna_pop(); + $$->nd_iter = NEW_FCALL(rb_intern("lambda"), 0); + fixpos($$->nd_iter, $$); + /*% + $$ = method_arg( + dispatch1(fcall, rb_intern("lambda")), + arg_new()); + $$ = method_add_block($$, + dispatch2(do_block, escape_Qundef($3), $4)); + %*/ + } + ; + f_larglist : '(' f_args opt_bv_decl rparen { /*%%%*/ ------=_Part_4908_25005344.1211700664123 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline This patch is an independent but related one to my previous one.  It can be combined to allow for normal block syntax lambdas with default arguments.  This patch allows you to make lambda's more concisely by simply removing the "lambda":

plus = { |a,b| a+b }  # |'s are required to disambiguate with a Hash
plus[1, 2] # => 3

f = do 42 end   # || not required
f[] # => 42

def my_while(cond)
    yield while cond[]
end
i=10
my_while({ || (i-=1).nonzero?}) { p i }

9
8
7
6
5
4
3
2
1


I wasn't able to get rid of some reduce/reduce conflict warnings (at yacc/compile time).  They are come from the do/end, not the {}.  I tried putting in some %prec attributes to help, but didn't get anywhere.  A yacc expert may have better luck.  It looks like the default precedence is correct though.  I assume the problem occurs between the first argument of a no parens call arg list and a block for that call.  It looks to treat it as a block in this case (instead of a lambda for the first argument), which will maintain compatibility?

Here would be a ChangeLog entry:

* parse.y (primary) : allow for a <block_lambda>.  The <block_lambda> grammar is almost identical to a normal block except |'s are required in the {} form (to disambiguate from a Hash).  The semantics are equivalent to preceding the block with "lambda".
* sample/test.rb : tests for the above.


Index: sample/test.rb
===================================================================
--- sample/test.rb    (revision 16585)
+++ sample/test.rb    (working copy)
@@ -1027,6 +1027,20 @@
 call_argument_test(false, lambda{|a,|})
 call_argument_test(false, lambda{|a,|}, 1,2)
 
+call_argument_test(true, {||})
+call_argument_test(false, {||}, 1)
+call_argument_test(true, {|a,|}, 1)
+call_argument_test(false, {|a,|})
+call_argument_test(false, {|a,|}, 1,2)
+
+call_argument_test(true, do end)
+call_argument_test(false, do end, 1)
+call_argument_test(true, do || end)
+call_argument_test(false, do || end, 1)
+call_argument_test(true, do |a,| end, 1)
+call_argument_test(false, do |a,| end)
+call_argument_test(false, do |a,| end, 1,2)
+
 call_argument_test(true, Proc.new{||})
 call_argument_test(true, Proc.new{||}, 1)
 call_argument_test(true, Proc.new{|a,|}, 1)
Index: parse.y
===================================================================
--- parse.y    (revision 16585)
+++ parse.y    (working copy)
@@ -670,6 +670,7 @@
 %type <node> assoc_list assocs assoc undef_list backref string_dvar for_var
 %type <node> block_param opt_block_param block_param_def f_opt
 %type <node> bv_decls opt_bv_decl bvar
+%type <node> block_lambda
 %type <node> lambda f_larglist lambda_body
 %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
@@ -2641,6 +2642,10 @@
             $$ = dispatch1(hash, escape_Qundef($2));
             %*/
             }
+                | block_lambda
+            {
+            $$ = $1;
+            }
         | keyword_return
             {
             /*%%%*/
@@ -3327,6 +3332,56 @@
             }
         ;
 
+block_lambda    : tLBRACE
+            {
+            /*%%%*/
+            dyna_push();
+            $<num>$ = ruby_sourceline;
+            /*%
+                    %*/
+            }
+                  block_param_def compstmt '}'
+            {
+            /*%%%*/
+                        $$ = NEW_ITER($3, $4);
+            nd_set_line($$, $<num>2);
+            dyna_pop();
+            $$->nd_iter = NEW_FCALL(rb_intern("lambda"), 0);
+            fixpos($$->nd_iter, $$);
+            /*%
+            $$ = method_arg(
+                            dispatch1(fcall, rb_intern("lambda")),
+                            arg_new());
+            $$ = method_add_block($$,
+                            dispatch2(brace_block, escape_Qundef($3), $4));
+            %*/
+            }
+        | keyword_do_block
+            {
+            /*%%%*/
+            dyna_push();
+            $<num>$ = ruby_sourceline;
+            /*%
+                    %*/
+            }
+                  opt_block_param compstmt keyword_end
+            {
+            /*%%%*/
+                        $$ = NEW_ITER($3, $4);
+            nd_set_line($$, $<num>2);
+            dyna_pop();
+            $$->nd_iter = NEW_FCALL(rb_intern("lambda"), 0);
+            fixpos($$->nd_iter, $$);
+            /*%
+            $$ = method_arg(
+                            dispatch1(fcall, rb_intern("lambda")),
+                            arg_new());
+            $$ = method_add_block($$,
+                            dispatch2(do_block, escape_Qundef($3), $4));
+            %*/
+            }
+                ;
+
 f_larglist    : '(' f_args opt_bv_decl rparen
             {
             /*%%%*/
------=_Part_4908_25005344.1211700664123--