scoping
[p5sagit/p5-mst-13.2.git] / ext / B / B / Deparse.pm
index 7e57a58..2f01389 100644 (file)
@@ -11,10 +11,11 @@ use Carp 'cluck', 'croak';
 use B qw(class main_root main_start main_cv svref_2object opnumber cstring
         OPf_WANT OPf_WANT_VOID OPf_WANT_SCALAR OPf_WANT_LIST
         OPf_KIDS OPf_REF OPf_STACKED OPf_SPECIAL
-        OPpLVAL_INTRO OPpENTERSUB_AMPER OPpSLICE OPpCONST_BARE
+        OPpLVAL_INTRO OPpOUR_INTRO OPpENTERSUB_AMPER OPpSLICE OPpCONST_BARE
         OPpTRANS_SQUASH OPpTRANS_DELETE OPpTRANS_COMPLEMENT OPpTARGET_MY
-        OPpCONST_ARYBASE OPpEXISTS_SUB
-        SVf_IOK SVf_NOK SVf_ROK SVf_POK
+        OPpCONST_ARYBASE OPpEXISTS_SUB OPpSORT_NUMERIC OPpSORT_INTEGER
+        OPpSORT_REVERSE
+        SVf_IOK SVf_NOK SVf_ROK SVf_POK SVpad_OUR
          CVf_METHOD CVf_LOCKED CVf_LVALUE
         PMf_KEEP PMf_GLOBAL PMf_CONTINUE PMf_EVAL PMf_ONCE PMf_SKIPWHITE
         PMf_MULTILINE PMf_SINGLELINE PMf_FOLD PMf_EXTENDED);
@@ -205,6 +206,13 @@ use warnings ();
 #  1             statement modifiers
 #  0             statement level
 
+# Also, lineseq may pass a fourth parameter to the pp_ routines:
+# if present, the fourth parameter is passed on by deparse.
+#
+# If present and true, it means that the op exists directly as
+# part of a lineseq. Currently it's only used by scopeop to
+# decide whether its results need to be enclosed in a do {} block.
+
 # Nonprinting characters with special meaning:
 # \cS - steal parens (see maybe_parens_unop)
 # \n - newline and indent
@@ -291,7 +299,6 @@ sub begin_is_use {
 
        return unless $self->const_sv($constop)->PV eq $module;
        $constop = $constop->sibling;
-
        $version = $self->const_sv($constop)->int_value;
        $constop = $constop->sibling;
        return if $constop->name ne "method_named";
@@ -310,18 +317,18 @@ sub begin_is_use {
     # See if there are import arguments
     my $args = '';
 
-    my $constop = $entersub->first->sibling; # Skip over pushmark
-    return unless $self->const_sv($constop)->PV eq $module;
+    my $svop = $entersub->first->sibling; # Skip over pushmark
+    return unless $self->const_sv($svop)->PV eq $module;
 
     # Pull out the arguments
-    for ($constop=$constop->sibling; $constop->name eq "const";
-               $constop = $constop->sibling) {
+    for ($svop=$svop->sibling; $svop->name ne "method_named";
+               $svop = $svop->sibling) {
        $args .= ", " if length($args);
-       $args .= $self->deparse($constop, 6);
+       $args .= $self->deparse($svop, 6);
     }
 
     my $use = 'use';
-    my $method_named = $constop;
+    my $method_named = $svop;
     return if $method_named->name ne "method_named";
     my $method_name = $self->const_sv($method_named)->PV;
 
@@ -642,11 +649,14 @@ sub ambient_pragmas {
 
 sub deparse {
     my $self = shift;
-    my($op, $cx) = @_;
+    my($op, $cx, $flags) = @_;
 
     Carp::confess("Null op in deparse") if !defined($op)
                                        || class($op) eq "NULL";
     my $meth = "pp_" . $op->name;
+    if (is_scope($op)) {
+       return $self->$meth($op, $cx, $flags);
+    }
     return $self->$meth($op, $cx);
 }
 
@@ -682,6 +692,7 @@ sub deparse_sub {
     my $self = shift;
     my $cv = shift;
     my $proto = "";
+Carp::confess("NULL in deparse_sub") if !defined($cv) || $cv->isa("B::NULL");
 Carp::confess("SPECIAL in deparse_sub") if $cv->isa("B::SPECIAL");
     local $self->{'curcop'} = $self->{'curcop'};
     if ($cv->FLAGS & SVf_POK) {
@@ -698,18 +709,35 @@ Carp::confess("SPECIAL in deparse_sub") if $cv->isa("B::SPECIAL");
     local($self->{'curcvlex'});
     local(@$self{qw'curstash warnings hints'})
                = @$self{qw'curstash warnings hints'};
+    my $body;
     if (not null $cv->ROOT) {
-       # skip leavesub
-       return $proto . "{\n\t" . 
-           $self->deparse($cv->ROOT->first, 0) . "\n\b}\n"; 
+       my $lineseq = $cv->ROOT->first;
+       if ($lineseq->name eq "lineseq") {
+           my @ops;
+           for(my$o=$lineseq->first; $$o; $o=$o->sibling) {
+               push @ops, $o;
+           }
+           $body = $self->lineseq(undef, @ops).";";
+           my $scope_en = $self->find_scope_en($lineseq);
+           if (defined $scope_en) {
+               my $subs = join"", $self->seq_subs($scope_en);
+               $body .= ";\n$subs" if length($subs);
+           }
+       }
+       else {
+           $body = $self->deparse($cv->ROOT->first, 0);
+       }
     }
-    my $sv = $cv->const_sv;
-    if ($$sv) {
-       # uh-oh. inlinable sub... format it differently
-       return $proto . "{ " . const($sv) . " }\n";
-    } else { # XSUB? (or just a declaration)
-       return "$proto;\n";
+    else {
+       my $sv = $cv->const_sv;
+       if ($$sv) {
+           # uh-oh. inlinable sub... format it differently
+           return $proto . "{ " . const($sv) . " }\n";
+       } else { # XSUB? (or just a declaration)
+           return "$proto;\n";
+       }
     }
+    return $proto ."{\n\t$body\n\b}" ."\n";
 }
 
 sub deparse_format {
@@ -829,11 +857,14 @@ sub maybe_parens_func {
 sub maybe_local {
     my $self = shift;
     my($op, $cx, $text) = @_;
-    if ($op->private & OPpLVAL_INTRO and not $self->{'avoid_local'}{$$op}) {
+    my $our_intro = ($op->name =~ /^(gv|rv2)[ash]v$/) ? OPpOUR_INTRO : 0;
+    if ($op->private & (OPpLVAL_INTRO|$our_intro)
+       and not $self->{'avoid_local'}{$$op}) {
+       my $our_local = ($op->private & OPpLVAL_INTRO) ? "local" : "our";
         if (want_scalar($op)) {
-           return "local $text";
+           return "$our_local $text";
        } else {
-           return $self->maybe_parens_func("local", $text, $cx, 16);
+           return $self->maybe_parens_func("$our_local", $text, $cx, 16);
        }
     } else {
        return $text;
@@ -957,10 +988,27 @@ sub pp_entertry { # see also leavetry
     return "XXX";
 }
 
+# $root should be the op which represents the root of whatever
+# we're sequencing here. If it's undefined, then we don't append
+# any subroutine declarations to the deparsed ops, otherwise we
+# append appropriate declarations.
 sub lineseq {
-    my $self = shift;
-    my(@ops) = @_;
+    my($self, $root, @ops) = @_;
     my($expr, @exprs);
+
+    my $out_cop = $self->{'curcop'};
+    my $out_seq = defined($out_cop) ? $out_cop->cop_seq : undef;
+    my $limit_seq;
+    if (defined $root) {
+       $limit_seq = $out_seq;
+       my $nseq = $self->find_scope_st($root->sibling) if ${$root->sibling};
+       $limit_seq = $nseq if !defined($limit_seq)
+                          or defined($nseq) && $nseq < $limit_seq;
+    }
+    $limit_seq = $self->{'limit_seq'}
+       if defined($self->{'limit_seq'})
+       && (!defined($limit_seq) || $self->{'limit_seq'} < $limit_seq);
+    local $self->{'limit_seq'} = $limit_seq;
     for (my $i = 0; $i < @ops; $i++) {
        $expr = "";
        if (is_state $ops[$i]) {
@@ -971,22 +1019,32 @@ sub lineseq {
                last;
            }
        }
-       if (!is_state $ops[$i] and $ops[$i+1] and !null($ops[$i+1]) and
-           $ops[$i+1]->name eq "leaveloop" and $self->{'expand'} < 3)
+       if (!is_state $ops[$i] and (my $ls = $ops[$i+1]) and
+           !null($ops[$i+1]) and $ops[$i+1]->name eq "lineseq")
        {
-           push @exprs, $expr . $self->for_loop($ops[$i], 0);
-           $i++;
-           next;
+           if ($ls->first && !null($ls->first) && is_state($ls->first)
+               && (my $sib = $ls->first->sibling)) {
+               if (!null($sib) && $sib->name eq "leaveloop") {
+                   push @exprs, $expr . $self->for_loop($ops[$i], 0);
+                   $i++;
+                   next;
+               }
+           }
        }
-       $expr .= $self->deparse($ops[$i], 0);
+       $expr .= $self->deparse($ops[$i], 0, (@ops != 1));
        $expr =~ s/;\n?\z//;
        push @exprs, $expr;
     }
-    return join(";\n", grep {length} @exprs);
+    my $body = join(";\n", grep {length} @exprs);
+    my $subs = "";
+    if (defined $root && defined $limit_seq) {
+       $subs = join "\n", $self->seq_subs($limit_seq);
+    }
+    return join(";\n", grep {length} $body, $subs);
 }
 
 sub scopeop {
-    my($real_block, $self, $op, $cx) = @_;
+    my($real_block, $self, $op, $cx, $flags) = @_;
     my $kid;
     my @kids;
 
@@ -1016,30 +1074,15 @@ sub scopeop {
     for (; !null($kid); $kid = $kid->sibling) {
        push @kids, $kid;
     }
-    if ($cx > 0) { # inside an expression, (a do {} while for lineseq)
-       return "do { " . $self->lineseq(@kids) . " }";
+    if ($flags || $cx > 0) { # inside an expression, (a do {} while for lineseq)
+       return "do {\n\t" . $self->lineseq($op, @kids) . "\n\b}";
     } else {
-       my $lineseq = $self->lineseq(@kids);
+       my $lineseq = $self->lineseq($op, @kids);
        return (length ($lineseq) ? "$lineseq;" : "");
     }
 }
 
-sub invoker {
-    my $caller = (caller(2))[3];
-    if ($caller eq "B::Deparse::deparse") {
-       return (caller(3))[3];
-    }
-    else {
-       return $caller;
-    }
-}
-
-sub pp_scope {
-    my ($self, $op, $cx) = @_;
-    my $body = scopeop(0, @_);
-    return $body if $cx > 0 || invoker() ne "B::Deparse::lineseq";
-    return "do {\n\t$body\n\b};";
-}
+sub pp_scope { scopeop(0, @_); }
 sub pp_lineseq { scopeop(0, @_); }
 sub pp_leave { scopeop(1, @_); }
 
@@ -1090,6 +1133,7 @@ sub lex_in_scope {
     my ($self, $name) = @_;
     $self->populate_curcvlex() if !defined $self->{'curcvlex'};
 
+    return 0 if !defined($self->{'curcop'});
     my $seq = $self->{'curcop'}->cop_seq;
     return 0 if !exists $self->{'curcvlex'}{$name};
     for my $a (@{$self->{'curcvlex'}{$name}}) {
@@ -1101,12 +1145,13 @@ sub lex_in_scope {
 
 sub populate_curcvlex {
     my $self = shift;
-    for (my $cv = $self->{'curcv'}; $$cv; $cv = $cv->OUTSIDE) {
+    for (my $cv = $self->{'curcv'}; class($cv) eq "CV"; $cv = $cv->OUTSIDE) {
        my @padlist = $cv->PADLIST->ARRAY;
        my @ns = $padlist[0]->ARRAY;
 
        for (my $i=0; $i<@ns; ++$i) {
            next if class($ns[$i]) eq "SPECIAL";
+           next if $ns[$i]->FLAGS & SVpad_OUR;  # Skip "our" vars
            if (class($ns[$i]) eq "PV") {
                # Probably that pesky lexical @_
                next;
@@ -1120,17 +1165,34 @@ sub populate_curcvlex {
     }
 }
 
-# Recurses down the tree, looking for a COP
-sub find_cop {
-    my ($self, $op) = @_;
-    if ($op->flags & OPf_KIDS) {
-       for (my $o=$op->first; $$o; $o=$o->sibling) {
-           return $o if is_state($o);
-           my $r = $self->find_cop($o);
-           return $r if defined $r;
+sub find_scope_st { ((find_scope(@_))[0]); }
+sub find_scope_en { ((find_scope(@_))[1]); }
+
+# Recurses down the tree, looking for pad variable introductions and COPs
+sub find_scope {
+    my ($self, $op, $scope_st, $scope_en) = @_;
+Carp::cluck() if !defined $op;
+    return ($scope_st, $scope_en) unless $op->flags & OPf_KIDS;
+
+    for (my $o=$op->first; $$o; $o=$o->sibling) {
+       if ($o->name =~ /^pad.v$/ && $o->private & OPpLVAL_INTRO) {
+           my $s = int($self->padname_sv($o->targ)->NVX);
+           my $e = $self->padname_sv($o->targ)->IVX;
+           $scope_st = $s if !defined($scope_st) || $s < $scope_st;
+           $scope_en = $e if !defined($scope_en) || $e > $scope_en;
+       }
+       elsif (is_state($o)) {
+           my $c = $o->cop_seq;
+           $scope_st = $c if !defined($scope_st) || $c < $scope_st;
+           $scope_en = $c if !defined($scope_en) || $c > $scope_en;
+       }
+       elsif ($o->flags & OPf_KIDS) {
+           ($scope_st, $scope_en) =
+               $self->find_scope($o, $scope_st, $scope_en)
        }
     }
-    return undef;
+
+    return ($scope_st, $scope_en);
 }
 
 # Returns a list of subs which should be inserted before the COP
@@ -1140,8 +1202,8 @@ sub cop_subs {
     # If we have nephews, then our sequence number indicates
     # the cop_seq of the end of some sort of scope.
     if (class($op->sibling) ne "NULL" && $op->sibling->flags & OPf_KIDS
-       and my $ncop = $self->find_cop($op->sibling)) {
-       $seq = $ncop->cop_seq;
+       and my $nseq = $self->find_scope_st($op->sibling) ) {
+       $seq = $nseq;
     }
     $seq = $out_seq if defined($out_seq) && $out_seq < $seq;
     return $self->seq_subs($seq);
@@ -1152,6 +1214,7 @@ sub seq_subs {
     my @text;
 #push @text, "# ($seq)\n";
 
+    return "" if !defined $seq;
     while (scalar(@{$self->{'subs_todo'}})
           and $seq > $self->{'subs_todo'}[0][0]) {
        push @text, $self->next_todo;
@@ -1166,7 +1229,6 @@ sub pp_nextstate {
     my($op, $cx) = @_;
     $self->{'curcop'} = $op;
     my @text;
-#push @text, "# ", $op->cop_seq, "\n";
     push @text, $self->cop_subs($op);
     push @text, $op->label . ": " if $op->label;
     my $stash = $op->stashpv;
@@ -1215,10 +1277,10 @@ sub pp_nextstate {
 
 sub declare_warnings {
     my ($from, $to) = @_;
-    if ($to eq warnings::bits("all")) {
+    if (($to & WARN_MASK) eq warnings::bits("all")) {
        return "use warnings;\n";
     }
-    elsif ($to eq "\0"x12) {
+    elsif (($to & WARN_MASK) eq "\0"x length($to)) {
        return "no warnings;\n";
     }
     return "BEGIN {\${^WARNING_BITS} = ".cstring($to)."}\n";
@@ -2082,12 +2144,21 @@ sub indirop {
        $indir = $indir->first; # skip rv2gv
        if (is_scope($indir)) {
            $indir = "{" . $self->deparse($indir, 0) . "}";
+       } elsif ($indir->name eq "const" && $indir->private & OPpCONST_BARE) {
+           $indir = $self->const_sv($indir)->PV;
        } else {
            $indir = $self->deparse($indir, 24);
        }
        $indir = $indir . " ";
        $kid = $kid->sibling;
     }
+    if ($name eq "sort" && $op->private & (OPpSORT_NUMERIC | OPpSORT_INTEGER)) {
+       $indir = ($op->private & OPpSORT_REVERSE) ? '{$b <=> $a} '
+                                                 : '{$a <=> $b} ';
+    }
+    elsif ($name eq "sort" && $op->private & OPpSORT_REVERSE) {
+       $indir = '{$b cmp $a} ';
+    }
     for (; !null($kid); $kid = $kid->sibling) {
        $expr = $self->deparse($kid, 6);
        push @exprs, $expr;
@@ -2129,21 +2200,26 @@ sub pp_list {
     my($expr, @exprs);
     my $kid = $op->first->sibling; # skip pushmark
     my $lop;
-    my $local = "either"; # could be local(...) or my(...)
+    my $local = "either"; # could be local(...), my(...) or our(...)
     for ($lop = $kid; !null($lop); $lop = $lop->sibling) {
        # This assumes that no other private flags equal 128, and that
        # OPs that store things other than flags in their op_private,
        # like OP_AELEMFAST, won't be immediate children of a list.
-       unless ($lop->private & OPpLVAL_INTRO or $lop->name eq "undef")
+       unless ($lop->private & OPpLVAL_INTRO
+               or $lop->name eq "undef")
        {
            $local = ""; # or not
            last;
        }
        if ($lop->name =~ /^pad[ash]v$/) { # my()
-           ($local = "", last) if $local eq "local";
+           ($local = "", last) if $local eq "local" || $local eq "our";
            $local = "my";
+       } elsif ($op->name =~ /^(gv|rv2)[ash]v$/
+                       && $op->private & OPpOUR_INTRO) { # our()
+           ($local = "", last) if $local eq "my" || $local eq "local";
+           $local = "our";
        } elsif ($lop->name ne "undef") { # local()
-           ($local = "", last) if $local eq "my";
+           ($local = "", last) if $local eq "my" || $local eq "our";
            $local = "local";
        }
     }
@@ -2227,7 +2303,6 @@ sub loop_common {
     my $bare = 0;
     my $body;
     my $cond = undef;
-    my $out_seq = $self->{'curcop'}->cop_seq;;
     if ($kid->name eq "lineseq") { # bare or infinite loop 
        if (is_state $kid->last) { # infinite
            $head = "while (1) "; # Can't use for(;;) if there's a continue
@@ -2284,7 +2359,7 @@ sub loop_common {
     # block (or the last in a bare loop).
     my $cont_start = $enter->nextop;
     my $cont;
-    if ($$cont_start != $$op and $ {$cont_start->sibling} != $ {$body->last}) {
+    if ($$cont_start != $$op && ${$cont_start->sibling} != ${$body->last}) {
        if ($bare) {
            $cont = $body->last;
        } else {
@@ -2299,7 +2374,7 @@ sub loop_common {
        for (; $$state != $$cont; $state = $state->sibling) {
            push @states, $state;
        }
-       $body = $self->lineseq(@states);
+       $body = $self->lineseq(undef, @states);
        if (defined $cond and not is_scope $cont and $self->{'expand'} < 3) {
            $head = "for ($init; $cond; " . $self->deparse($cont, 1) .") ";
            $cont = "\cK";
@@ -2309,14 +2384,13 @@ sub loop_common {
        }
     } else {
        return "" if !defined $body;
+       if (length $init) {
+           $head = "for ($init; $cond;) ";
+       }
        $cont = "\cK";
        $body = $self->deparse($body, 0);
     }
-    $body =~ s/;?$/;/;
-    $body .= "\n";
-    # If we have say C<{my $x=2; sub x{$x}}>, the sub must go inside
-    # the loop. So we insert any subs which are due here.
-    $body .= join"", $self->seq_subs($out_seq);
+    $body =~ s/;?$/;\n/;
 
     return $head . "{\n\t" . $body . "\b}" . $cont;
 }
@@ -2327,7 +2401,7 @@ sub for_loop {
     my $self = shift;
     my($op, $cx) = @_;
     my $init = $self->deparse($op, 1);
-    return $self->loop_common($op->sibling, $cx, $init);
+    return $self->loop_common($op->sibling->first->sibling, $cx, $init);
 }
 
 sub pp_leavetry {
@@ -2434,7 +2508,11 @@ sub pp_aelemfast {
     my $self = shift;
     my($op, $cx) = @_;
     my $gv = $self->gv_or_padgv($op);
-    return "\$" . $self->gv_name($gv) . "[" .
+    my $name = $self->gv_name($gv);
+    $name = $self->{'curstash'}."::$name"
+       if $name !~ /::/ && $self->lex_in_scope('@'.$name);
+
+    return "\$" . $name . "[" .
                  ($op->private + $self->{'arybase'}) . "]";
 }
 
@@ -2508,6 +2586,13 @@ sub elem {
        $array = $self->padany($array);
     } elsif (is_scope($array)) { # ${expr}[0]
        $array = "{" . $self->deparse($array, 0) . "}";
+    } elsif ($array->name eq "gv") {
+       $array = $self->gv_name($self->gv_or_padgv($array));
+       if ($array !~ /::/) {
+           my $prefix = ($left eq '[' ? '@' : '%');
+           $array = $self->{curstash}.'::'.$array
+               if $self->lex_in_scope($prefix . $array);
+       }
     } elsif (is_scalar $array) { # $x[0], $$x[0], ...
        $array = $self->deparse($array, 24);
     } else {
@@ -2821,7 +2906,7 @@ sub pp_entersub {
            return "$kid(" . $args . ")";
        } elsif (defined $proto and $proto eq "") {
            return $kid;
-       } elsif (defined $proto and $proto eq "\$") {
+       } elsif (defined $proto and $proto eq "\$" and is_scalar($exprs[0])) {
            return $self->maybe_parens_func($kid, $args, $cx, 16);
        } elsif (defined($proto) && $proto or $simple) {
            return $self->maybe_parens_func($kid, $args, $cx, 5);
@@ -3368,7 +3453,7 @@ sub pp_split {
     my($kid, @exprs, $ary, $expr);
     $kid = $op->first;
     if ($ {$kid->pmreplroot}) {
-       $ary = '@' . $self->gv_name($kid->pmreplroot);
+       $ary = $self->stash_variable('@', $self->gv_name($kid->pmreplroot));
     }
     for (; !null($kid); $kid = $kid->sibling) {
        push @exprs, $self->deparse($kid, 6);