Eradicate traces of 'asciirange' re subpragma.
[p5sagit/p5-mst-13.2.git] / ext / B / B / Deparse.pm
index 2f01389..1560420 100644 (file)
@@ -10,7 +10,7 @@ package B::Deparse;
 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
+        OPf_KIDS OPf_REF OPf_STACKED OPf_SPECIAL OPf_MOD
         OPpLVAL_INTRO OPpOUR_INTRO OPpENTERSUB_AMPER OPpSLICE OPpCONST_BARE
         OPpTRANS_SQUASH OPpTRANS_DELETE OPpTRANS_COMPLEMENT OPpTARGET_MY
         OPpCONST_ARYBASE OPpEXISTS_SUB OPpSORT_NUMERIC OPpSORT_INTEGER
@@ -94,6 +94,8 @@ use warnings ();
 # - added more control of expanding control structures
 
 # Todo:
+#  (See also BUGS section at the end of this file)
+#
 # - finish tr/// changes
 # - add option for even more parens (generalize \&foo change)
 # - left/right context
@@ -113,7 +115,7 @@ use warnings ();
 # - here-docs?
 
 # Tests that will always fail:
-# comp/redef.t -- all (redefinition happens at compile time)
+# (see t/TEST for the short list)
 
 # Object fields (were globals):
 #
@@ -271,6 +273,7 @@ sub next_todo {
 sub begin_is_use {
     my ($self, $cv) = @_;
     my $root = $cv->ROOT;
+    local @$self{qw'curcv curcvlex'} = ($cv);
 #require B::Debug;
 #B::walkoptree($cv->ROOT, "debug");
     my $lineseq = $root->first;
@@ -591,13 +594,13 @@ sub ambient_pragmas {
        elsif ($name eq 're') {
            require re;
            if ($val eq 'none') {
-               $hint_bits &= ~re::bits(qw/taint eval asciirange/);
+               $hint_bits &= ~re::bits(qw/taint eval/);
                next();
            }
 
            my @names;
            if ($val eq 'all') {
-               @names = qw/taint eval asciirange/;
+               @names = qw/taint eval/;
            }
            elsif (ref $val) {
                @names = @$val;
@@ -958,6 +961,11 @@ sub pp_mapstart { # see also mapwhile
     return "XXX";
 }
 
+sub pp_method_named {
+    cluck "unexpected OP_METHOD_NAMED";
+    return "XXX";
+}
+
 sub pp_flip { # see also flop
     cluck "unexpected OP_FLIP";
     return "XXX";
@@ -1817,7 +1825,8 @@ sub binop {
        ($left, $right) = ($right, $left);
     }
     $left = $self->deparse_binop_left($op, $left, $prec);
-    $left = "($left)" if $flags & LIST_CONTEXT && $left =~ /^\$/;
+    $left = "($left)" if $flags & LIST_CONTEXT
+               && $left !~ /^(my|our|local|)[\@\(]/;
     $right = $self->deparse_binop_right($op, $right, $prec);
     return $self->maybe_parens("$left $opname$eq $right", $cx, $prec);
 }
@@ -2205,8 +2214,15 @@ sub pp_list {
        # 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
+       #
+       # OP_ENTERSUB can break this logic, so check for it.
+       # I suspect that open and exit can too.
+
+       if (!($lop->private & (OPpLVAL_INTRO|OPpOUR_INTRO)
                or $lop->name eq "undef")
+           or $lop->name eq "entersub"
+           or $lop->name eq "exit"
+           or $lop->name eq "open")
        {
            $local = ""; # or not
            last;
@@ -2214,8 +2230,10 @@ sub pp_list {
        if ($lop->name =~ /^pad[ash]v$/) { # my()
            ($local = "", last) if $local eq "local" || $local eq "our";
            $local = "my";
-       } elsif ($op->name =~ /^(gv|rv2)[ash]v$/
-                       && $op->private & OPpOUR_INTRO) { # our()
+       } elsif ($lop->name =~ /^(gv|rv2)[ash]v$/
+                       && $lop->private & OPpOUR_INTRO
+               or $lop->name eq "null" && $lop->first->name eq "gvsv"
+                       && $lop->first->private & OPpOUR_INTRO) { # our()
            ($local = "", last) if $local eq "my" || $local eq "local";
            $local = "our";
        } elsif ($lop->name ne "undef") { # local()
@@ -2411,6 +2429,8 @@ sub pp_leavetry {
 
 BEGIN { eval "sub OP_CONST () {" . opnumber("const") . "}" }
 BEGIN { eval "sub OP_STRINGIFY () {" . opnumber("stringify") . "}" }
+BEGIN { eval "sub OP_RV2SV () {" . opnumber("rv2sv") . "}" }
+BEGIN { eval "sub OP_LIST () {" . opnumber("list") . "}" }
 
 sub pp_null {
     my $self = shift;
@@ -2519,6 +2539,11 @@ sub pp_aelemfast {
 sub rv2x {
     my $self = shift;
     my($op, $cx, $type) = @_;
+
+    if (class($op) eq 'NULL' || !$op->can("first")) {
+       Carp::cluck("Unexpected op in pp_rv2x");
+       return 'XXX';
+    }
     my $kid = $op->first;
     my $str = $self->deparse($kid, 0);
     return $self->stash_variable($type, $str) if is_scalar($kid);
@@ -2542,7 +2567,17 @@ sub pp_av2arylen {
 }
 
 # skip down to the old, ex-rv2cv
-sub pp_rv2cv { $_[0]->rv2x($_[1]->first->first->sibling, $_[2], "&") }
+sub pp_rv2cv {
+    my ($self, $op, $cx) = @_;
+    if (!null($op->first) && $op->first->name eq 'null' &&
+       $op->first->targ eq OP_LIST)
+    {
+       return $self->rv2x($op->first->first->sibling, $cx, "&")
+    }
+    else {
+       return $self->rv2x($op, $cx, "")
+    }
+}
 
 sub pp_rv2av {
     my $self = shift;
@@ -2724,7 +2759,8 @@ sub method {
     } else {
        $obj = $kid;
        $kid = $kid->sibling;
-       for (; not null $kid->sibling; $kid = $kid->sibling) {
+       for (; !null ($kid->sibling) && $kid->name ne "method_named";
+             $kid = $kid->sibling) {
            push @exprs, $self->deparse($kid, 6);
        }
        $meth = $kid;
@@ -2744,7 +2780,7 @@ sub method {
     }
     my $args = join(", ", @exprs);     
     $kid = $obj . "->" . $meth;
-    if ($args) {
+    if (length $args) {
        return $kid . "(" . $args . ")"; # parens mandatory
     } else {
        return $kid;
@@ -2834,7 +2870,7 @@ sub pp_entersub {
     my $prefix = "";
     my $amper = "";
     my($kid, @exprs);
-    if ($op->flags & OPf_SPECIAL) {
+    if ($op->flags & OPf_SPECIAL && !($op->flags & OPf_MOD)) {
        $prefix = "do ";
     } elsif ($op->private & OPpENTERSUB_AMPER) {
        $amper = "&";
@@ -2856,7 +2892,7 @@ sub pp_entersub {
        }
        $simple = 1; # only calls of named functions can be prototyped
        $kid = $self->deparse($kid, 24);
-    } elsif (is_scalar $kid->first) {
+    } elsif (is_scalar ($kid->first) && $kid->first->name ne 'rv2cv') {
        $amper = "&";
        $kid = $self->deparse($kid, 24);
     } else {
@@ -2926,13 +2962,75 @@ sub uninterp {
     return $str;
 }
 
+{
+my $bal;
+BEGIN {
+    use re "eval";
+    # Matches any string which is balanced with respect to {braces}
+    $bal = qr(
+      (?:
+       [^\\{}]
+      | \\\\
+      | \\[{}]
+      | \{(??{$bal})\}
+      )*
+    )x;
+}
+
 # the same, but treat $|, $), $( and $ at the end of the string differently
 sub re_uninterp {
     my($str) = @_;
-    $str =~ s/(^|\G|[^\\])((?:\\\\)*)([\$\@](?!\||\)|\(|$)|\\[uUlLQE])/$1$2\\$3/g;
+
+    $str =~ s/
+         ( ^|\G                  # $1
+          | [^\\]
+          )
+
+          (                       # $2
+            (?:\\\\)*
+          )
+
+          (                       # $3
+            (\(\?\??\{$bal\}\))   # $4
+          | [\$\@]
+            (?!\||\)|\(|$)
+          | \\[uUlLQE]
+          )
+
+       /length($4) ? "$1$2$4" : "$1$2\\$3"/xeg;
+
     return $str;
 }
 
+# This is for regular expressions with the /x modifier
+# We have to leave comments unmangled.
+sub re_uninterp_extended {
+    my($str) = @_;
+
+    $str =~ s/
+         ( ^|\G                  # $1
+          | [^\\]
+          )
+
+          (                       # $2
+            (?:\\\\)*
+          )
+
+          (                       # $3
+            ( \(\?\??\{$bal\}\)   # $4  (skip over (?{}) and (??{}) blocks)
+            | \#[^\n]*            #     (skip over comments)
+            )
+          | [\$\@]
+            (?!\||\)|\(|$)
+          | \\[uUlLQE]
+          )
+
+       /length($4) ? "$1$2$4" : "$1$2\\$3"/xeg;
+
+    return $str;
+}
+}
+
 # character escapes, but not delimiters that might need to be escaped
 sub escape_str { # ASCII, UTF8
     my($str) = @_;
@@ -2949,6 +3047,16 @@ sub escape_str { # ASCII, UTF8
     return $str;
 }
 
+# For regexes with the /x modifier.
+# Leave whitespace unmangled.
+sub escape_extended_re {
+    my($str) = @_;
+    $str =~ s/(.)/ord($1)>255 ? sprintf("\\x{%x}", ord($1)) : $1/eg;
+    $str =~ s/([\0\033-\037\177-\377])/'\\' . sprintf("%03o", ord($1))/ge;
+    $str =~ s/\n/\n\f/g;
+    return $str;
+}
+
 # Don't do this for regexen
 sub unback {
     my($str) = @_;
@@ -3018,16 +3126,24 @@ sub const {
     } elsif ($sv->FLAGS & SVf_IOK) {
        return $sv->int_value;
     } elsif ($sv->FLAGS & SVf_NOK) {
-       return $sv->NV;
+       # try the default stringification
+       my $r = "".$sv->NV;
+       if ($r =~ /e/) {
+           # If it's in scientific notation, we might have lost information
+           return sprintf("%.20e", $sv->NV);
+       }
+       return $r;
     } elsif ($sv->FLAGS & SVf_ROK && $sv->can("RV")) {
        return "\\(" . const($sv->RV) . ")"; # constant folded
-    } else {
+    } elsif ($sv->FLAGS & SVf_POK) {
        my $str = $sv->PV;
        if ($str =~ /[^ -~]/) { # ASCII for non-printing
            return single_delim("qq", '"', uninterp escape_str unback $str);
        } else {
            return single_delim("q", "'", unback $str);
        }
+    } else {
+       return "undef";
     }
 }
 
@@ -3065,13 +3181,13 @@ sub dq {
     } elsif ($type eq "concat") {
        my $first = $self->dq($op->first);
        my $last  = $self->dq($op->last);
+
        # Disambiguate "${foo}bar", "${foo}{bar}", "${foo}[1]"
-       if ($last =~ /^[A-Z\\\^\[\]_?]/) {
-           $first =~ s/([\$@])\^$/${1}{^}/;  # "${^}W" etc
-        }
-       elsif ($last =~ /^[{\[\w]/) {
-           $first =~ s/([\$@])([A-Za-z_]\w*)$/${1}{$2}/;
-       }
+       ($last =~ /^[A-Z\\\^\[\]_?]/ &&
+           $first =~ s/([\$@])\^$/${1}{^}/)  # "${^}W" etc
+           || ($last =~ /^[{\[\w_]/ &&
+               $first =~ s/([\$@])([A-Za-z_]\w*)$/${1}{$2}/);
+
        return $first . $last;
     } elsif ($type eq "uc") {
        return '\U' . $self->dq($op->first->sibling) . '\E';
@@ -3137,10 +3253,13 @@ sub double_delim {
     }
 }
 
+# Only used by tr///, so backslashes hyphens
 sub pchr { # ASCII
     my($n) = @_;
     if ($n == ord '\\') {
        return '\\\\';
+    } elsif ($n == ord "-") {
+       return "\\-";
     } elsif ($n >= ord(' ') and $n <= ord('~')) {
        return chr($n);
     } elsif ($n == ord "\a") {
@@ -3183,12 +3302,10 @@ sub collapse {
     return $str;
 }
 
-# XXX This has trouble with hyphens in the replacement (tr/bac/-AC/),
-# and backslashes.
-
 sub tr_decode_byte {
     my($table, $flags) = @_;
-    my(@table) = unpack("s256", $table);
+    my(@table) = unpack("s*", $table);
+    splice @table, 0x100, 1;   # Number of subsequent elements
     my($c, $tr, @from, @to, @delfrom, $delhyphen);
     if ($table[ord "-"] != -1 and 
        $table[ord("-") - 1] == -1 || $table[ord("-") + 1] == -1)
@@ -3202,7 +3319,7 @@ sub tr_decode_byte {
            $delhyphen = 1;
        }
     }
-    for ($c = 0; $c < 256; $c++) {
+    for ($c = 0; $c < @table; $c++) {
        $tr = $table[$c];
        if ($tr >= 0) {
            push @from, $c; push @to, $tr;
@@ -3234,6 +3351,8 @@ sub tr_chr {
     my $x = shift;
     if ($x == ord "-") {
        return "\\-";
+    } elsif ($x == ord "\\") {
+       return "\\\\";
     } else {
        return chr $x;
     }
@@ -3356,32 +3475,36 @@ sub pp_trans {
 # Like dq(), but different
 sub re_dq {
     my $self = shift;
-    my $op = shift;
+    my ($op, $extended) = @_;
+
     my $type = $op->name;
     if ($type eq "const") {
        return '$[' if $op->private & OPpCONST_ARYBASE;
-       return re_uninterp(escape_str(re_unback($self->const_sv($op)->as_string)));
+       my $unbacked = re_unback($self->const_sv($op)->as_string);
+       return re_uninterp_extended(escape_extended_re($unbacked))
+           if $extended;
+       return re_uninterp(escape_str($unbacked));
     } elsif ($type eq "concat") {
-       my $first = $self->re_dq($op->first);
-       my $last  = $self->re_dq($op->last);
+       my $first = $self->re_dq($op->first, $extended);
+       my $last  = $self->re_dq($op->last,  $extended);
+
        # Disambiguate "${foo}bar", "${foo}{bar}", "${foo}[1]"
-       if ($last =~ /^[A-Z\\\^\[\]_?]/) {
-           $first =~ s/([\$@])\^$/${1}{^}/;
-       }
-       elsif ($last =~ /^[{\[\w]/) {
-           $first =~ s/([\$@])([A-Za-z_]\w*)$/${1}{$2}/;
-       }
+       ($last =~ /^[A-Z\\\^\[\]_?]/ &&
+           $first =~ s/([\$@])\^$/${1}{^}/)  # "${^}W" etc
+           || ($last =~ /^[{\[\w_]/ &&
+               $first =~ s/([\$@])([A-Za-z_]\w*)$/${1}{$2}/);
+
        return $first . $last;
     } elsif ($type eq "uc") {
-       return '\U' . $self->re_dq($op->first->sibling) . '\E';
+       return '\U' . $self->re_dq($op->first->sibling, $extended) . '\E';
     } elsif ($type eq "lc") {
-       return '\L' . $self->re_dq($op->first->sibling) . '\E';
+       return '\L' . $self->re_dq($op->first->sibling, $extended) . '\E';
     } elsif ($type eq "ucfirst") {
-       return '\u' . $self->re_dq($op->first->sibling);
+       return '\u' . $self->re_dq($op->first->sibling, $extended);
     } elsif ($type eq "lcfirst") {
-       return '\l' . $self->re_dq($op->first->sibling);
+       return '\l' . $self->re_dq($op->first->sibling, $extended);
     } elsif ($type eq "quotemeta") {
-       return '\Q' . $self->re_dq($op->first->sibling) . '\E';
+       return '\Q' . $self->re_dq($op->first->sibling, $extended) . '\E';
     } elsif ($type eq "join") {
        return $self->deparse($op->last, 26); # was join($", @ary)
     } else {
@@ -3389,13 +3512,54 @@ sub re_dq {
     }
 }
 
-sub pp_regcomp {
+sub pure_string {
+    my ($self, $op) = @_;
+    my $type = $op->name;
+
+    if ($type eq 'const') {
+       return 1;
+    }
+    elsif ($type =~ /^[ul]c(first)?$/ || $type eq 'quotemeta') {
+       return $self->pure_string($op->first->sibling);
+    }
+    elsif ($type eq 'join') {
+       my $join_op = $op->first->sibling;  # Skip pushmark
+       return 0 unless $join_op->name eq 'null' && $join_op->targ eq OP_RV2SV;
+
+       my $gvop = $join_op->first;
+       return 0 unless $gvop->name eq 'gvsv';
+        return 0 unless '"' eq $self->gv_name($self->gv_or_padgv($gvop));
+
+       return 0 unless ${$join_op->sibling} eq ${$op->last};
+       return 0 unless $op->last->name =~ /^(rv2|pad)av$/;
+    }
+    elsif ($type eq 'concat') {
+       return $self->pure_string($op->first)
+            && $self->pure_string($op->last);
+    }
+    elsif (is_scalar($op) || $type =~ /^[ah]elem(fast)?$/) {
+       return 1;
+    }
+    else {
+       return 0;
+    }
+
+    return 1;
+}
+
+sub regcomp {
     my $self = shift;
-    my($op, $cx) = @_;
+    my($op, $cx, $extended) = @_;
     my $kid = $op->first;
     $kid = $kid->first if $kid->name eq "regcmaybe";
     $kid = $kid->first if $kid->name eq "regcreset";
-    return $self->re_dq($kid);
+    return ($self->re_dq($kid, $extended), 1) if $self->pure_string($kid);
+    return ($self->deparse($kid, $cx), 0);
+}
+
+sub pp_regcomp {
+    my ($self, $op, $cx) = @_;
+    return (($self->regcomp($op, $cx, 0))[0]);
 }
 
 # osmic acid -- see osmium tetroxide
@@ -3415,10 +3579,19 @@ sub matchop {
        $var = $self->deparse($kid, 20);
        $kid = $kid->sibling;
     }
+    my $quote = 1;
+    my $extended = ($op->pmflags & PMf_EXTENDED);
     if (null $kid) {
-       $re = re_uninterp(escape_str(re_unback($op->precomp)));
+       my $unbacked = re_unback($op->precomp);
+       if ($extended) {
+           $re = re_uninterp_extended(escape_extended_re($unbacked));
+       } else {
+           $re = re_uninterp(escape_str(re_unback($op->precomp)));
+       }
+    } elsif ($kid->name ne 'regcomp') {
+       Carp::cluck("found ".$kid->name." where regcomp expected");
     } else {
-       $re = $self->deparse($kid, 1);
+       ($re, $quote) = $self->regcomp($kid, 1, $extended);
     }
     my $flags = "";
     $flags .= "c" if $op->pmflags & PMf_CONTINUE;
@@ -3432,10 +3605,10 @@ sub matchop {
     if ($op->pmflags & PMf_ONCE) { # only one kind of delimiter works here
        $re =~ s/\?/\\?/g;
        $re = "?$re?";
-    } else {
+    } elsif ($quote) {
        $re = single_delim($name, $delim, $re);
     }
-    $re = $re . $flags;
+    $re = $re . $flags if $quote;
     if ($binop) {
        return $self->maybe_parens("$var =~ $re", $cx, 20);
     } else {
@@ -3511,10 +3684,17 @@ sub pp_subst {
            $repl = $self->dq($repl);   
        }
     }
+    my $extended = ($op->pmflags & PMf_EXTENDED);
     if (null $kid) {
-       $re = re_uninterp(escape_str(re_unback($op->precomp)));
+       my $unbacked = re_unback($op->precomp);
+       if ($extended) {
+           $re = re_uninterp_extended(escape_extended_re($unbacked));
+       }
+       else {
+           $re = re_uninterp(escape_str($unbacked));
+       }
     } else {
-       $re = $self->deparse($kid, 1);
+       ($re) = $self->regcomp($kid, 1, $extended);
     }
     $flags .= "e" if $op->pmflags & PMf_EVAL;
     $flags .= "g" if $op->pmflags & PMf_GLOBAL;
@@ -3522,7 +3702,7 @@ sub pp_subst {
     $flags .= "m" if $op->pmflags & PMf_MULTILINE;
     $flags .= "o" if $op->pmflags & PMf_KEEP;
     $flags .= "s" if $op->pmflags & PMf_SINGLELINE;
-    $flags .= "x" if $op->pmflags & PMf_EXTENDED;
+    $flags .= "x" if $extended;
     $flags = $substwords{$flags} if $substwords{$flags};
     if ($binop) {
        return $self->maybe_parens("$var =~ s"
@@ -3809,6 +3989,9 @@ particular context, where some pragmas are already in scope. In
 this case, you use the B<ambient_pragmas> method to describe the
 assumptions you wish to make.
 
+Not all of the options currently have any useful effect. See
+L</BUGS> for more details.
+
 The parameters it accepts are:
 
 =over 4
@@ -3901,13 +4084,67 @@ the main:: package, the code will include a package declaration.
 
 =head1 BUGS
 
-See the 'to do' list at the beginning of the module file.
+=over 4
+
+=item *
+
+The only pragmas to be completely supported are: C<use warnings>,
+C<use strict 'refs'>, C<use bytes>, and C<use integer>. (C<$[>, which
+behaves like a pragma, is also supported.)
+
+Excepting those listed above, we're currently unable to guarantee that
+B::Deparse will produce a pragma at the correct point in the program.
+Since the effects of pragmas are often lexically scoped, this can mean
+that the pragma holds sway over a different portion of the program
+than in the input file.
+
+=item *
+
+If a keyword is over-ridden, and your program explicitly calls
+the built-in version by using CORE::keyword, the output of B::Deparse
+will not reflect this.
+
+=item *
+
+C<sort foo (1, 2, 3)> comes out as C<sort (foo 1, 2, 3)>, which
+causes perl to issue a warning.
+
+The obvious fix doesn't work, because these are different:
+
+    print (FOO 1, 2, 3), 4, 5, 6;
+    print FOO (1, 2, 3), 4, 5, 6;
+
+=item *
+
+Constants (other than simple strings or numbers) don't work properly.
+Pathological examples that fail (and probably always will) include:
+
+    use constant E2BIG => ($!=7);
+    use constant x=>\$x; print x
+
+The following could (and should) be made to work:
+
+    use constant regex => qr/blah/;
+    print regex;
+
+=item *
+
+An input file that uses source filtering probably won't be deparsed into
+runnable code, because it will still include the B<use> declaration
+for the source filtering module, even though the code that is
+produced is already ordinary Perl which shouldn't be filtered again.
+
+=item *
+
+There are probably many more bugs on non-ASCII platforms (EBCDIC).
+
+=back
 
 =head1 AUTHOR
 
 Stephen McCamant <smcc@CSUA.Berkeley.EDU>, based on an earlier
 version by Malcolm Beattie <mbeattie@sable.ox.ac.uk>, with
-contributions from Gisle Aas, James Duncan, Albert Dvornik, Hugo van
-der Sanden, Gurusamy Sarathy, and Nick Ing-Simmons.
+contributions from Gisle Aas, James Duncan, Albert Dvornik, Robin
+Houston, Hugo van der Sanden, Gurusamy Sarathy, and Nick Ing-Simmons.
 
 =cut