From: Vincent Pit Date: Sun, 3 Jan 2010 17:22:38 +0000 (+0100) Subject: Make given() statements return the last evaluated expression X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=25b991bf8caa94f23a64f9568f5ceee69781aa25;p=p5sagit%2Fp5-mst-13.2.git Make given() statements return the last evaluated expression --- diff --git a/op.c b/op.c index 88a31d3..e0d7fbb 100644 --- a/op.c +++ b/op.c @@ -923,25 +923,28 @@ Perl_scalar(pTHX_ OP *o) case OP_LEAVETRY: kid = cLISTOPo->op_first; scalar(kid); - while ((kid = kid->op_sibling)) { - if (kid->op_sibling) - scalarvoid(kid); - else + kid = kid->op_sibling; + do_kids: + while (kid) { + OP *sib = kid->op_sibling; + if (sib && kid->op_type != OP_LEAVEWHEN) { + if (sib->op_type == OP_BREAK && sib->op_flags & OPf_SPECIAL) { + scalar(kid); + scalarvoid(sib); + break; + } else + scalarvoid(kid); + } else scalar(kid); + kid = sib; } PL_curcop = &PL_compiling; break; case OP_SCOPE: case OP_LINESEQ: case OP_LIST: - for (kid = cLISTOPo->op_first; kid; kid = kid->op_sibling) { - if (kid->op_sibling) - scalarvoid(kid); - else - scalar(kid); - } - PL_curcop = &PL_compiling; - break; + kid = cLISTOPo->op_first; + goto do_kids; case OP_SORT: Perl_ck_warner(aTHX_ packWARN(WARN_VOID), "Useless use of sort in scalar context"); break; @@ -985,7 +988,7 @@ Perl_scalarvoid(pTHX_ OP *o) want = o->op_flags & OPf_WANT; if ((want && want != OPf_WANT_SCALAR) || (PL_parser && PL_parser->error_count) - || o->op_type == OP_RETURN || o->op_type == OP_REQUIRE) + || o->op_type == OP_RETURN || o->op_type == OP_REQUIRE || o->op_type == OP_LEAVEWHEN) { return o; } @@ -1296,24 +1299,27 @@ Perl_list(pTHX_ OP *o) case OP_LEAVETRY: kid = cLISTOPo->op_first; list(kid); - while ((kid = kid->op_sibling)) { - if (kid->op_sibling) - scalarvoid(kid); - else + kid = kid->op_sibling; + do_kids: + while (kid) { + OP *sib = kid->op_sibling; + if (sib && kid->op_type != OP_LEAVEWHEN) { + if (sib->op_type == OP_BREAK && sib->op_flags & OPf_SPECIAL) { + list(kid); + scalarvoid(sib); + break; + } else + scalarvoid(kid); + } else list(kid); + kid = sib; } PL_curcop = &PL_compiling; break; case OP_SCOPE: case OP_LINESEQ: - for (kid = cLISTOPo->op_first; kid; kid = kid->op_sibling) { - if (kid->op_sibling) - scalarvoid(kid); - else - list(kid); - } - PL_curcop = &PL_compiling; - break; + kid = cLISTOPo->op_first; + goto do_kids; } return o; } diff --git a/pod/perlsyn.pod b/pod/perlsyn.pod index 4e1bc0a..f90b8b3 100644 --- a/pod/perlsyn.pod +++ b/pod/perlsyn.pod @@ -667,6 +667,42 @@ case to the next: default { say '$foo does not contain a y' } } +=head3 Return value + +When a C statement is also a valid expression (e.g. +when it's the last statement of a block), it returns : + +=over 4 + +=item * + +An empty list as soon as an explicit C is encountered. + +=item * + +The value of the last evaluated expression of the successful +C/C clause, if there's one. + +=item * + +The value of the last evaluated expression of the C block if no +condition was true. + +=back + +Note that, unlike C and C, both C and C always +themselves return an empty list. + + my $price = do { given ($item) { + when ([ 'pear', 'apple' ]) { 1 } + break when 'vote'; # My vote cannot be bought + 1e10 when /Mona Lisa/; + 'unknown'; + } }; + +C blocks can't currently be used as proper expressions. This +may be addressed in a future version of perl. + =head3 Switching in a loop Instead of using C, you can use a C loop. diff --git a/pp_ctl.c b/pp_ctl.c index c181d0f..ff36756 100644 --- a/pp_ctl.c +++ b/pp_ctl.c @@ -3982,14 +3982,38 @@ PP(pp_leavegiven) POPBLOCK(cx,newpm); assert(CxTYPE(cx) == CXt_GIVEN); - SP = newsp; - PUTBACK; - - PL_curpm = newpm; /* pop $1 et al */ + TAINT_NOT; + if (gimme == G_VOID) + SP = newsp; + else if (gimme == G_SCALAR) { + register SV **mark; + MARK = newsp + 1; + if (MARK <= SP) { + if (SvFLAGS(TOPs) & (SVs_PADTMP|SVs_TEMP)) + *MARK = TOPs; + else + *MARK = sv_mortalcopy(TOPs); + } + else { + MEXTEND(mark,0); + *MARK = &PL_sv_undef; + } + SP = MARK; + } + else { + /* in case LEAVE wipes old return values */ + register SV **mark; + for (mark = newsp + 1; mark <= SP; mark++) { + if (!(SvFLAGS(*mark) & (SVs_PADTMP|SVs_TEMP))) { + *mark = sv_mortalcopy(*mark); + TAINT_NOT; /* Each item is independent */ + } + } + } + PL_curpm = newpm; /* Don't pop $1 et al till now */ LEAVE_with_name("given"); - - return NORMAL; + RETURN; } /* Helper routines used by pp_smartmatch */ @@ -4529,9 +4553,10 @@ PP(pp_enterwhen) fails, we don't want to push a context and then pop it again right away, so we skip straight to the op that follows the leavewhen. + RETURNOP calls PUTBACK which restores the stack pointer after the POPs. */ if ((0 == (PL_op->op_flags & OPf_SPECIAL)) && !SvTRUEx(POPs)) - return cLOGOP->op_other->op_next; + RETURNOP(cLOGOP->op_other->op_next); ENTER_with_name("eval"); SAVETMPS; @@ -4590,7 +4615,8 @@ PP(pp_break) I32 cxix; register PERL_CONTEXT *cx; I32 inner; - + dSP; + cxix = dopoptogiven(cxstack_ix); if (cxix < 0) { if (PL_op->op_flags & OPf_SPECIAL) @@ -4614,7 +4640,8 @@ PP(pp_break) if (CxFOREACH(cx)) return CX_LOOP_NEXTOP_GET(cx); else - return cx->blk_givwhen.leave_op; + /* RETURNOP calls PUTBACK which restores the old old sp */ + RETURNOP(cx->blk_givwhen.leave_op); } STATIC OP * diff --git a/t/op/switch.t b/t/op/switch.t index 92facef..1452b78 100644 --- a/t/op/switch.t +++ b/t/op/switch.t @@ -9,7 +9,7 @@ BEGIN { use strict; use warnings; -plan tests => 132; +plan tests => 160; # The behaviour of the feature pragma should be tested by lib/switch.t # using the tests in t/lib/switch/*. This file tests the behaviour of @@ -1031,6 +1031,138 @@ unreified_check(1,2,undef); unreified_check(undef); unreified_check(undef,""); +# Test do { given } as a rvalue + +{ + # Simple scalar + my $lexical = 5; + my @things = (11 .. 26); # 16 elements + my @exp = (5, 16, 9); + no warnings 'void'; + for (0, 1, 2) { + my $scalar = do { given ($_) { + when (0) { $lexical } + when (2) { 'void'; 8, 9 } + @things; + } }; + is($scalar, shift(@exp), "rvalue given - simple scalar [$_]"); + } +} +{ + # Postfix scalar + my $lexical = 5; + my @exp = (5, 7, 9); + for (0, 1, 2) { + no warnings 'void'; + my $scalar = do { given ($_) { + $lexical when 0; + 8, 9 when 2; + 6, 7; + } }; + is($scalar, shift(@exp), "rvalue given - postfix scalar [$_]"); + } +} +{ + # Default scalar + my @exp = (5, 9, 9); + for (0, 1, 2) { + my $scalar = do { given ($_) { + no warnings 'void'; + when (0) { 5 } + default { 8, 9 } + 6, 7; + } }; + is($scalar, shift(@exp), "rvalue given - default scalar [$_]"); + } +} +{ + # Simple list + my @things = (11 .. 13); + my @exp = ('3 4 5', '11 12 13', '8 9'); + for (0, 1, 2) { + my @list = do { given ($_) { + when (0) { 3 .. 5 } + when (2) { my $fake = 'void'; 8, 9 } + @things; + } }; + is("@list", shift(@exp), "rvalue given - simple list [$_]"); + } +} +{ + # Postfix list + my @things = (12); + my @exp = ('3 4 5', '6 7', '12'); + for (0, 1, 2) { + my @list = do { given ($_) { + 3 .. 5 when 0; + @things when 2; + 6, 7; + } }; + is("@list", shift(@exp), "rvalue given - postfix list [$_]"); + } +} +{ + # Default list + my @things = (11 .. 20); # 10 elements + my @exp = ('m o o', '8 10', '8 10'); + for (0, 1, 2) { + my @list = do { given ($_) { + when (0) { "moo" =~ /(.)/g } + default { 8, scalar(@things) } + 6, 7; + } }; + is("@list", shift(@exp), "rvalue given - default list [$_]"); + } +} +{ + # Switch control + my @exp = ('6 7', '', '6 7'); + for (0, 1, 2, 3) { + my @list = do { given ($_) { + continue when $_ <= 1; + break when 1; + next when 2; + 6, 7; + } }; + is("@list", shift(@exp), "rvalue given - default list [$_]"); + } +} +{ + # Context propagation + my $smart_hash = sub { + do { given ($_[0]) { + 'undef' when undef; + when ([ 1 .. 3 ]) { 1 .. 3 } + when (4) { my $fake; do { 4, 5 } } + } }; + }; + + my $scalar; + + $scalar = $smart_hash->(); + is($scalar, 'undef', "rvalue given - scalar context propagation [undef]"); + + $scalar = $smart_hash->(4); + is($scalar, 5, "rvalue given - scalar context propagation [4]"); + + $scalar = $smart_hash->(999); + is($scalar, undef, "rvalue given - scalar context propagation [999]"); + + my @list; + + @list = $smart_hash->(); + is("@list", 'undef', "rvalue given - list context propagation [undef]"); + + @list = $smart_hash->(2); + is("@list", '1 2 3', "rvalue given - list context propagation [2]"); + + @list = $smart_hash->(4); + is("@list", '4 5', "rvalue given - list context propagation [4]"); + + @list = $smart_hash->(999); + is("@list", '', "rvalue given - list context propagation [999]"); +} + # Okay, that'll do for now. The intricacies of the smartmatch # semantics are tested in t/op/smartmatch.t __END__