Get the optree almost right
Florian Ragwitz [Wed, 1 Aug 2012 15:07:21 +0000 (11:07 -0400)]
Once.xs
t/basic.t

diff --git a/Once.xs b/Once.xs
index 7d14c37..0ec8704 100644 (file)
--- a/Once.xs
+++ b/Once.xs
@@ -58,83 +58,10 @@ THX_demand_unichar (pTHX_ I32 c, U32 flags)
     lex_read_unichar(0);
 }
 
-#define CXt_MOO 12
-#define CXt_KOOH 13
-
 static OP *
-pp_entertake (pTHX)
-{
-  dSP;
-  register PERL_CONTEXT *cx;
-  const I32 gimme = GIMME_V;
-
-  if ((0 == (PL_op->op_flags & OPf_SPECIAL)) && !SvTRUEx(POPs))
-    RETURNOP(cLOGOP->op_other->op_next);
-
-  ENTER;
-
-  PUSHBLOCK(cx, CXt_KOOH, SP);
-  cx->blk_givwhen.leave_op = cLOGOP->op_other;
-
-  RETURN;
-}
-
-STATIC I32
-S_dopoptogather(pTHX_ I32 startingblock)
+pp_stub_marker (pTHX)
 {
-    dVAR;
-    I32 i;
-    for (i = startingblock; i >= 0; i--) {
-  register const PERL_CONTEXT *cx = &cxstack[i];
-  switch (CxTYPE(cx)) {
-  default:
-      continue;
-  case CXt_MOO:
-      DEBUG_l( Perl_deb(aTHX_ "(dopoptogiven(): found given at cx=%ld)\n", (long)i));
-      return i;
-
-  case CXt_LOOP_PLAIN:
-      assert(!CxFOREACHDEF(cx));
-      break;
-  case CXt_LOOP_LAZYIV:
-  case CXt_LOOP_LAZYSV:
-  case CXt_LOOP_FOR:/* FIXME */
-      if (CxFOREACHDEF(cx)) {
-    DEBUG_l( Perl_deb(aTHX_ "(dopoptogiven(): found foreach at cx=%ld)\n", (long)i));
-    return i;
-      }
-  }
-    }
-    return i;
-}
-
-static OP *
-pp_leavetake (pTHX)
-{
-  dSP;
-  I32 cxix;
-  register PERL_CONTEXT *cx;
-  I32 gimme;
-  SV **newsp;
-  PMOP *newpm;
-
-  cxix = S_dopoptogather(aTHX_ cxstack_ix);
-  if (cxix < 0)
-  /* diag_listed_as: Can't "when" outside a topicalizer */
-  DIE(aTHX_ "Can't \"%s\" outside a topicalizer",
-      PL_op->op_flags & OPf_SPECIAL ? "default" : "when");
-
-  POPBLOCK(cx,newpm);
-  assert(CxTYPE(cx) == CXt_KOOH);
-
-  LEAVE;
-
-  if (cxix < cxstack_ix)
-    dounwind(cxix);
-
-  cx = &cxstack[cxix];
-
-  RETURNOP(cx->blk_givwhen.leave_op);
+  croak("FAIL");
 }
 
 static OP *
@@ -158,23 +85,12 @@ myparse_args_take (pTHX_ GV *namegv, SV *args, U32 *flagsp)
   blkop = parse_block(0);
   blkop = Perl_block_end(aTHX_ blk_floor, blkop);
 
-  NewOp(1101, enterop, 1, LOGOP);
-  enterop->op_type = OP_ENTERWHEN;
-  enterop->op_ppaddr = pp_entertake;
-  enterop->op_flags = OPf_KIDS;
-  enterop->op_targ = -1;
-  enterop->op_private = 0;
-
-  leaveop = newUNOP(OP_LEAVEWHEN, 0, (OP *)enterop);
-  leaveop->op_ppaddr = pp_leavetake;
-
   topicalise = SvTRUE(*av_fetch((AV *)SvRV(args), 0, 0));
   if (topicalise) {
     OP *pvarop;
 
     pvarop = newOP(OP_PADSV, 0);
-    pvarop->op_targ = pad_findmy("$Gather::Once::current_topic",
-                                 sizeof("$Gather::Once::current_topic")-1, 0);
+    pvarop->op_targ = pad_findmy_pvs("$Gather::Once::current_topic", 0);
 
     if (pvarop->op_targ == NOT_IN_PAD)
       croak("outside topicaliser"); /* FIXME */
@@ -186,59 +102,44 @@ myparse_args_take (pTHX_ GV *namegv, SV *args, U32 *flagsp)
   condop = newUNOP(OP_ENTERSUB, OPf_STACKED,
                    op_append_elem(OP_LIST, condop,
                                   newCVREF(0, newSVOP(OP_CONST, 0,
-                                                      predicate_cv_ref))));
-
-  enterop->op_first = condop;
-  enterop->op_first->op_sibling = op_scope(blkop);
-  leaveop->op_next = LINKLIST(enterop->op_first);
-  enterop->op_first->op_next = (OP *)enterop;
+                                                      SvREFCNT_inc(predicate_cv_ref)))));
 
-  enterop->op_next = LINKLIST(enterop->op_first->op_sibling);
-  enterop->op_first->op_sibling->op_next = enterop->op_other = leaveop;
+  OP *stub = newSTATEOP(0, NULL, newOP(OP_NULL, 0));
+  condop = newCONDOP(0, condop, blkop, stub);
+  condop->op_ppaddr = pp_stub_marker;
 
-  return leaveop;
+  return condop;
 }
 
 static OP *
-pp_entergather (pTHX)
+is_take_stmt (pTHX_ OP *stmt)
 {
-  dSP; dTARGET;
-  register PERL_CONTEXT *cx;
-  const I32 gimme = GIMME_V;
-
-  ENTER;
-  if (PL_op->op_targ != NOT_IN_PAD)
-    sv_setsv(TARG, POPs);
-
-  PUSHBLOCK(cx, CXt_MOO, SP);
-  cx->blk_givwhen.leave_op = cLOGOP->op_other;
-
-  RETURN;
-}
-
-static OP *
-pp_leavegather (pTHX)
-{
-  dSP;
-  register PERL_CONTEXT *cx;
-  I32 gimme;
-  SV **newsp;
-  PMOP *newpm;
-
-  POPBLOCK(cx,newpm);
-  assert(CxTYPE(cx) == CXt_MOO);
-
-  //SP = adjust_stack_on_leave(newsp, SP, newsp, gimme, SVs_PADTMP|SVs_TEMP);
-  //PL_curpm = newpm; /* Don't pop $1 et al till now */
-
-  LEAVE;
-  RETURN;
+  if (stmt->op_type != OP_LINESEQ
+      || !cLISTOPx(stmt)->op_last
+      || cLISTOPx(stmt)->op_last->op_type == OP_COND_EXPR)
+    return NULL;
+
+  OP *nullop = cLISTOPx(stmt)->op_last;
+  if (nullop->op_type != OP_NULL
+      || !(nullop->op_flags & OPf_KIDS)
+      || !cLISTOPx(nullop)->op_first
+      || nullop->op_ppaddr != pp_stub_marker)
+    return NULL;
+
+  OP *condop = cLISTOPx(nullop)->op_first;
+  if (condop->op_type != OP_COND_EXPR || !(condop->op_flags & OPf_KIDS))
+    return NULL;
+
+  return condop;
+  OP *stub = condop->op_next; /* falseop */
+
+  return stub;
 }
 
 static OP *
 myparse_args_gather (pTHX_ GV *namegv, SV *topicalise, U32 *flagsp)
 {
-  OP *topicaliser, *blkop, *initop = NULL;
+  OP *topicaliser, *topblkop, *curblkop, *initop;
   int blk_floor;
 
   PERL_UNUSED_ARG(namegv);
@@ -250,41 +151,37 @@ myparse_args_gather (pTHX_ GV *namegv, SV *topicalise, U32 *flagsp)
     demand_unichar(')', 0);
   }
 
-  demand_unichar('{', DEMAND_NOCONSUME);
+  demand_unichar('{', 0);
   blk_floor = Perl_block_start(aTHX_ 1);
-  if (SvTRUE(topicalise)) {
-    initop = newOP(OP_PADSV, (OPpLVAL_INTRO<<8));
-    initop->op_targ = pad_add_my_scalar_pvn("$Gather::Once::current_topic",
-                                            sizeof("$Gather::Once::current_topic")-1);
+
+  initop = newOP(OP_PADSV, (OPpLVAL_INTRO<<8));
+  initop->op_targ = pad_add_my_scalar_pvn("$Gather::Once::current_topic",
+                                          sizeof("$Gather::Once::current_topic")-1);
+
+  topblkop = curblkop = parse_fullstmt(0);
+  lex_read_space(0);
+  while (lex_peek_unichar(0) != '}') {
+    OP *stub, *stmt = parse_fullstmt(0);
+
+    curblkop = op_append_elem(OP_LINESEQ, curblkop, stmt);
+
+    if ((stub = is_take_stmt(aTHX_ stmt))) {
+      curblkop = stub->op_next;
+    }
+
+    //op_dump(curblkop);
+    lex_read_space(0);
   }
 
-  blkop = parse_block(0);
-  if (initop)
-    blkop = op_prepend_elem(OP_LINESEQ, initop, blkop);
+  demand_unichar('}', DEMAND_IMMEDIATE);
 
-  blkop = Perl_block_end(aTHX_ blk_floor, blkop);
+  topblkop = op_prepend_elem(OP_LINESEQ, initop, topblkop);
+  topblkop = op_scope(Perl_block_end(aTHX_ blk_floor, topblkop));
 
-  LOGOP *enterop;
-  NewOp(1101, enterop, 1, LOGOP);
-  enterop->op_type = OP_ENTERGIVEN;
-  enterop->op_ppaddr = pp_entergather;
-  enterop->op_flags = OPf_KIDS;
-  enterop->op_targ = SvTRUE(topicalise) ? initop->op_targ : NOT_IN_PAD;
-  enterop->op_private = 0;
-
-  OP *leaveop;
-  leaveop = newUNOP(OP_LEAVEGIVEN, 0, (OP *)enterop);
-  leaveop->op_ppaddr = pp_leavegather;
-
-  enterop->op_first = SvTRUE(topicalise) ? topicaliser : newOP(OP_NULL, 0);
-  enterop->op_first->op_sibling = op_scope(blkop);
-  leaveop->op_next = LINKLIST(enterop->op_first);
-  enterop->op_first->op_next = (OP *)enterop;
-
-  enterop->op_next = LINKLIST(enterop->op_first->op_sibling);
-  enterop->op_first->op_sibling->op_next = enterop->op_other = leaveop;
-
-  return leaveop;
+  //return newGIVENOP(SvTRUE(topicalise) ? topicaliser : newOP(OP_NULL, 0),
+  //blkop, initop->op_targ);
+
+  return topblkop;
 }
 
 static OP *
index ecaa468..c68402e 100644 (file)
--- a/t/basic.t
+++ b/t/basic.t
@@ -3,11 +3,11 @@ use warnings;
 use Test::More 0.89;
 
 use Gather::Once
-    block => 'with',
-    take  => 'iff',
+    block      => 'with',
+    take       => 'iff',
     topicalise => 1,
     predicate  => sub {
-        warn "$_[0] == $_[1]";
+        diag explain \@_;
         $_[0] == $_[1];
     };
 
@@ -15,6 +15,7 @@ my $n = 42;
 
 my @ret = with ($n) {
     warn 42;
+    iff (23) { 42 };
     iff (42) { 23 };
     warn 23;
     42;
@@ -22,6 +23,25 @@ my @ret = with ($n) {
 
 diag explain \@ret;
 
+done_testing;
+
+__END__
+
+my $pred = sub {};
+my @ret___ = do {
+    warn 42;
+    ($pred->($n, 23))
+        ? do { 42 }
+            : ($pred->($n, 42))
+                ? do { 23 }
+                    : do {
+                        warn 23;
+                        42;
+                    };
+};
+
+diag explain \@ret;
+
 use Gather::Once
     block => 'moo',
     take  => 'iff_',
@@ -39,6 +59,10 @@ my @ret_ = moo {
     iff_ (42) { 1, 2, 3 };
 };
 
+my @ret__ = do {
+    $pred->(42) ? do { 1, 2, 3 } : ()
+};
+
 diag explain \@ret_;
 
 done_testing;