This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
[perl #77096] Deparse return and do without llafr
[perl5.git] / dist / B-Deparse / Deparse.pm
index a9b139c..7731e2a 100644 (file)
@@ -19,32 +19,21 @@ use B qw(class main_root main_start main_cv svref_2object opnumber perlstring
         SVf_IOK SVf_NOK SVf_ROK SVf_POK SVpad_OUR SVf_FAKE SVs_RMG SVs_SMG
          CVf_METHOD CVf_LVALUE
         PMf_KEEP PMf_GLOBAL PMf_CONTINUE PMf_EVAL PMf_ONCE
-        PMf_MULTILINE PMf_SINGLELINE PMf_FOLD PMf_EXTENDED),
-        ($] < 5.008004 ? () : 'OPpSORT_INPLACE'),
-        ($] < 5.008006 ? () : qw(OPpSORT_DESCEND OPpITER_REVERSED)),
-        ($] < 5.008009 ? () : qw(OPpCONST_NOVER OPpPAD_STATE)),
-        ($] < 5.009 ? 'PMf_SKIPWHITE' : qw(RXf_SKIPWHITE)),
-        ($] < 5.011 ? 'CVf_LOCKED' : 'OPpREVERSE_INPLACE'),
-        ($] < 5.013 ? () : 'PMf_NONDESTRUCT'),
-        ($] < 5.015003 &&
-            # This empirical feature test is required during the
-            # transitional phase where blead still identifies itself
-            # as 5.15.2 but has had $[ removed.  After blead has its
-            # version number bumped to 5.15.3, this can be reduced to
-            # just test $] < 5.015003.
-            ($] < 5.015002 || do { require B; exists(&B::OPpCONST_ARYBASE) })
-            ? qw(OPpCONST_ARYBASE) : ());
-$VERSION = "1.09";
+        PMf_MULTILINE PMf_SINGLELINE PMf_FOLD PMf_EXTENDED);
+$VERSION = "1.10";
 use strict;
 use vars qw/$AUTOLOAD/;
 use warnings ();
 
 BEGIN {
+    # List version-specific constants here.
     # Easiest way to keep this code portable between version looks to
     # be to fake up a dummy constant that will never actually be true.
     foreach (qw(OPpSORT_INPLACE OPpSORT_DESCEND OPpITER_REVERSED OPpCONST_NOVER
-               OPpPAD_STATE RXf_SKIPWHITE CVf_LOCKED OPpREVERSE_INPLACE
-               PMf_NONDESTRUCT OPpCONST_ARYBASE)) {
+               OPpPAD_STATE PMf_SKIPWHITE RXf_SKIPWHITE
+               CVf_LOCKED OPpREVERSE_INPLACE OPpSUBSTR_REPL_FIRST
+               PMf_NONDESTRUCT OPpCONST_ARYBASE OPpEVAL_BYTES)) {
+       eval { import B $_ };
        no strict 'refs';
        *{$_} = sub () {0} unless *{$_}{CODE};
     }
@@ -55,10 +44,10 @@ BEGIN {
 # - fixed reference constants (\"str")
 # - handle empty programs gracefully
 # - handle infinite loops (for (;;) {}, while (1) {})
-# - differentiate between `for my $x ...' and `my $x; for $x ...'
+# - differentiate between 'for my $x ...' and 'my $x; for $x ...'
 # - various minor cleanups
 # - moved globals into an object
-# - added `-u', like B::C
+# - added '-u', like B::C
 # - package declarations using cop_stash
 # - subs, formats and code sorted by cop_seq
 # Changes between 0.51 and 0.52:
@@ -66,10 +55,10 @@ BEGIN {
 # - added documentation
 # Changes between 0.52 and 0.53:
 # - many changes adding precedence contexts and associativity
-# - added `-p' and `-s' output style options
+# - added '-p' and '-s' output style options
 # - various other minor fixes
 # Changes between 0.53 and 0.54:
-# - added support for new `for (1..100)' optimization,
+# - added support for new 'for (1..100)' optimization,
 #   thanks to Gisle Aas
 # Changes between 0.54 and 0.55:
 # - added support for new qr// construct
@@ -78,16 +67,16 @@ BEGIN {
 # - tested on base/*.t, cmd/*.t, comp/*.t, io/*.t
 # - fixed $# on non-lexicals broken in last big rewrite
 # - added temporary fix for change in opcode of OP_STRINGIFY
-# - fixed problem in 0.54's for() patch in `for (@ary)'
+# - fixed problem in 0.54's for() patch in 'for (@ary)'
 # - fixed precedence in conditional of ?:
-# - tweaked list paren elimination in `my($x) = @_'
+# - tweaked list paren elimination in 'my($x) = @_'
 # - made continue-block detection trickier wrt. null ops
 # - fixed various prototype problems in pp_entersub
 # - added support for sub prototypes that never get GVs
 # - added unquoting for special filehandle first arg in truncate
-# - print doubled rv2gv (a bug) as `*{*GV}' instead of illegal `**GV'
+# - print doubled rv2gv (a bug) as '*{*GV}' instead of illegal '**GV'
 # - added semicolons at the ends of blocks
-# - added -l `#line' declaration option -- fixes cmd/subval.t 27,28
+# - added -l '#line' declaration option -- fixes cmd/subval.t 27,28
 # Changes between 0.56 and 0.561:
 # - fixed multiply-declared my var in pp_truncate (thanks to Sarathy)
 # - used new B.pm symbolic constants (done by Nick Ing-Simmons)
@@ -100,7 +89,7 @@ BEGIN {
 # Changes after 0.57:
 # - added parens in \&foo (patch by Albert Dvornik)
 # Changes between 0.57 and 0.58:
-# - fixed `0' statements that weren't being printed
+# - fixed '0' statements that weren't being printed
 # - added methods for use from other programs
 #   (based on patches from James Duncan and Hugo van der Sanden)
 # - added -si and -sT to control indenting (also based on a patch from Hugo)
@@ -112,7 +101,7 @@ BEGIN {
 # Changes between 0.58 and 0.59
 # - added support for Chip's OP_METHOD_NAMED
 # - added support for Ilya's OPpTARGET_MY optimization
-# - elided arrows before `()' subscripts when possible
+# - elided arrows before '()' subscripts when possible
 # Changes between 0.59 and 0.60
 # - support for method attributes was added
 # - some warnings fixed
@@ -162,7 +151,7 @@ BEGIN {
 # - configurable syntax highlighting: ANSI color, HTML, TeX, etc.
 # - more style options: brace style, hex vs. octal, quotes, ...
 # - print big ints as hex/octal instead of decimal (heuristic?)
-# - handle `my $x if 0'?
+# - handle 'my $x if 0'?
 # - version using op_next instead of op_first/sibling?
 # - avoid string copies (pass arrays, one big join?)
 # - here-docs?
@@ -260,7 +249,7 @@ BEGIN {
 # parens: -p
 # linenums: -l
 # unquote: -q
-# cuddle: ` ' or `\n', depending on -sC
+# cuddle: ' ' or '\n', depending on -sC
 # indent_size: -si
 # use_tabs: -sT
 # ex_const: -sv
@@ -274,7 +263,7 @@ BEGIN {
 # they're inside an expression or at statement level, etc.  (see
 # chart below). When ops with children call deparse on them, they pass
 # along their precedence. Fractional values are used to implement
-# associativity (`($x + $y) + $z' => `$x + $y + $y') and related
+# associativity ('($x + $y) + $z' => '$x + $y + $y') and related
 # parentheses hacks. The major disadvantage of this scheme is that
 # it doesn't know about right sides and left sides, so say if you
 # assign a listop to a variable, it can't tell it's allowed to leave
@@ -314,7 +303,7 @@ BEGIN {
 # \cS - steal parens (see maybe_parens_unop)
 # \n - newline and indent
 # \t - increase indent
-# \b - decrease indent (`outdent')
+# \b - decrease indent ('outdent')
 # \f - flush left (no indent)
 # \cK - kill following semicolon, if any
 
@@ -961,7 +950,7 @@ sub is_state {
     return $name eq "nextstate" || $name eq "dbstate" || $name eq "setstate";
 }
 
-sub is_miniwhile { # check for one-line loop (`foo() while $y--')
+sub is_miniwhile { # check for one-line loop ('foo() while $y--')
     my $op = shift;
     return (!null($op) and null($op->sibling)
            and $op->name eq "null" and class($op) eq "UNOP"
@@ -1021,7 +1010,7 @@ sub maybe_parens {
     }
 }
 
-# same as above, but get around the `if it looks like a function' rule
+# same as above, but get around the 'if it looks like a function' rule
 sub maybe_parens_unop {
     my $self = shift;
     my($name, $kid, $cx) = @_;
@@ -1042,7 +1031,7 @@ sub maybe_parens_unop {
            return $name . substr($kid, 1);
        } elsif (substr($kid, 0, 1) eq "(") {
            # avoid looks-like-a-function trap with extra parens
-           # (`+' can lead to ambiguities)
+           # ('+' can lead to ambiguities)
            return "$name(" . $kid  . ")";
        } else {
            return "$name $kid";
@@ -1462,11 +1451,12 @@ sub pp_nextstate {
        $self->{'hints'} = $hints;
     }
 
-    # hack to check that the hint hash hasn't changed
     if ($] > 5.009 &&
-       "@{[sort %{$self->{'hinthash'} || {}}]}"
-       ne "@{[sort %{$op->hints_hash->HASH || {}}]}") {
-       push @text, declare_hinthash($self->{'hinthash'}, $op->hints_hash->HASH, $self->{indent_size});
+       @text != push @text, declare_hinthash(
+           $self->{'hinthash'}, $op->hints_hash->HASH,
+           $self->{indent_size}
+       )
+    ) {
        $self->{'hinthash'} = $op->hints_hash->HASH;
     }
 
@@ -1521,8 +1511,15 @@ sub declare_hinthash {
     my @decls;
     for my $key (keys %$to) {
        next if $ignored_hints{$key};
-       if (!defined $from->{$key} or $from->{$key} ne $to->{$key}) {
-           push @decls, qq(\$^H{'$key'} = q($to->{$key}););
+       if (!exists $from->{$key} or $from->{$key} ne $to->{$key}) {
+           push @decls,
+               qq(\$^H{) . single_delim("q", "'", $key) . qq(} = )
+             . (
+                  defined $to->{$key}
+                       ? single_delim("q", "'", $to->{$key})
+                       : 'undef'
+               )
+             . qq(;);
        }
     }
     for my $key (keys %$from) {
@@ -1531,7 +1528,7 @@ sub declare_hinthash {
            push @decls, qq(delete \$^H{'$key'};);
        }
     }
-    @decls or return '';
+    @decls or return;
     return join("\n" . (" " x $indent), "BEGIN {", @decls) . "\n}\n";
 }
 
@@ -1557,6 +1554,8 @@ my %feature_keywords = (
     when    => 'switch',
     default => 'switch',
     break   => 'switch',
+    evalbytes=>'evalbytes',
+    __SUB__ => '__SUB__',
 );
 
 sub keyword {
@@ -1564,14 +1563,12 @@ sub keyword {
     my $name = shift;
     return $name if $name =~ /^CORE::/; # just in case
     if (exists $feature_keywords{$name}) {
-       return
-         $self->{'hinthash'}
-          && $self->{'hinthash'}{"feature_$feature_keywords{$name}"}
-           ? $name
-           : "CORE::$name";
+       return "CORE::$name"
+        if !$self->{'hinthash'}
+        || !$self->{'hinthash'}{"feature_$feature_keywords{$name}"}
     }
     if (
-      $name !~ /^(?:chom?p|exec|s(?:elect|ystem))\z/
+      $name !~ /^(?:chom?p|do|exec|glob|s(?:elect|ystem))\z/
        && !defined eval{prototype "CORE::$name"}
     ) { return $name }
     if (
@@ -1671,7 +1668,7 @@ sub pp_not {
 
 sub unop {
     my $self = shift;
-    my($op, $cx, $name) = @_;
+    my($op, $cx, $name, $nollafr) = @_;
     my $kid;
     if ($op->flags & OPf_KIDS) {
        $kid = $op->first;
@@ -1687,6 +1684,12 @@ sub unop {
            $kid = $kid->first;
        }
 
+       if ($nollafr) {
+           ($kid = $self->deparse($kid, 16)) =~ s/^\cS//;
+           return $self->maybe_parens(
+                       $self->keyword($name) . " $kid", $cx, 16
+                  );
+       }   
        return $self->maybe_parens_unop($name, $kid, $cx);
     } else {
        return $self->keyword($name)
@@ -1765,8 +1768,17 @@ sub pp_gmtime { unop(@_, "gmtime") }
 sub pp_alarm { unop(@_, "alarm") }
 sub pp_sleep { maybe_targmy(@_, \&unop, "sleep") }
 
-sub pp_dofile { unop(@_, "do") }
-sub pp_entereval { unop(@_, "eval") }
+sub pp_dofile {
+    my $code = unop(@_, "do", 1); # llafr does not apply
+    if ($code =~ s/^do \{/do({/) { $code .= ')' }
+    $code;
+}
+sub pp_entereval {
+    unop(
+      @_,
+      $_[1]->private & OPpEVAL_BYTES ? $_[0]->keyword('evalbytes') : "eval"
+    )
+}
 
 sub pp_ghbyname { unop(@_, "gethostbyname") }
 sub pp_gnbyname { unop(@_, "getnetbyname") }
@@ -2017,7 +2029,7 @@ sub ftst {
     my $self = shift;
     my($op, $cx, $name) = @_;
     if (class($op) eq "UNOP") {
-       # Genuine `-X' filetests are exempt from the LLAFR, but not
+       # Genuine '-X' filetests are exempt from the LLAFR, but not
        # l?stat(); for the sake of clarity, give'em all parens
        return $self->maybe_parens_unop($name, $op->first, $cx);
     } elsif (class($op) =~ /^(SV|PAD)OP$/) {
@@ -2067,7 +2079,7 @@ sub assoc_class {
     my $op = shift;
     my $name = $op->name;
     if ($name eq "concat" and $op->first->name eq "concat") {
-       # avoid spurious `=' -- see comment in pp_concat
+       # avoid spurious '=' -- see comment in pp_concat
        return "concat";
     }
     if ($name eq "null" and class($op) eq "UNOP"
@@ -2084,7 +2096,7 @@ sub assoc_class {
     return $name . ($op->flags & OPf_STACKED ? "=" : "");
 }
 
-# Left associative operators, like `+', for which
+# Left associative operators, like '+', for which
 # $a + $b + $c is equivalent to ($a + $b) + $c
 
 BEGIN {
@@ -2115,7 +2127,7 @@ sub deparse_binop_left {
     }
 }
 
-# Right associative operators, like `=', for which
+# Right associative operators, like '=', for which
 # $a = $b = $c is equivalent to $a = ($b = $c)
 
 BEGIN {
@@ -2222,9 +2234,9 @@ sub pp_smartmatch {
     }
 }
 
-# `.' is special because concats-of-concats are optimized to save copying
+# '.' is special because concats-of-concats are optimized to save copying
 # by making all but the first concat stacked. The effect is as if the
-# programmer had written `($a . $b) .= $c', except legal.
+# programmer had written '($a . $b) .= $c', except legal.
 sub pp_concat { maybe_targmy(@_, \&real_concat) }
 sub real_concat {
     my $self = shift;
@@ -2242,7 +2254,7 @@ sub real_concat {
     return $self->maybe_parens("$left .$eq $right", $cx, $prec);
 }
 
-# `x' is weird when the left arg is a list
+# 'x' is weird when the left arg is a list
 sub pp_repeat {
     my $self = shift;
     my($op, $cx) = @_;
@@ -2340,10 +2352,10 @@ sub pp_dorassign { logassignop(@_, "//=") }
 
 sub listop {
     my $self = shift;
-    my($op, $cx, $name) = @_;
+    my($op, $cx, $name, $kid, $nollafr) = @_;
     my(@exprs);
     my $parens = ($cx >= 5) || $self->{'parens'};
-    my $kid = $op->first->sibling;
+    $kid ||= $op->first->sibling;
     return $self->keyword($name) if null $kid;
     my $first;
     $name = "socketpair" if $name eq "sockpair";
@@ -2360,7 +2372,8 @@ sub listop {
     if ($name eq "chmod" && $first =~ /^\d+$/) {
        $first = sprintf("%#o", $first);
     }
-    $first = "+$first" if not $parens and substr($first, 0, 1) eq "(";
+    $first = "+$first"
+       if not $parens and not $nollafr and substr($first, 0, 1) eq "(";
     push @exprs, $first;
     $kid = $kid->sibling;
     if (defined $proto && $proto =~ /^\*\*/ && $kid->name eq "rv2gv") {
@@ -2374,7 +2387,9 @@ sub listop {
        return "$exprs[0] = $fullname"
                 . ($parens ? "($exprs[0])" : " $exprs[0]");
     }
-    if ($parens) {
+    if ($parens && $nollafr) {
+       return "($fullname " . join(", ", @exprs) . ")";
+    } elsif ($parens) {
        return "$fullname(" . join(", ", @exprs) . ")";
     } else {
        return "$fullname " . join(", ", @exprs);
@@ -2383,7 +2398,16 @@ sub listop {
 
 sub pp_bless { listop(@_, "bless") }
 sub pp_atan2 { maybe_targmy(@_, \&listop, "atan2") }
-sub pp_substr { maybe_local(@_, listop(@_, "substr")) }
+sub pp_substr {
+    my ($self,$op,$cx) = @_;
+    if ($op->private & OPpSUBSTR_REPL_FIRST) {
+       return
+          listop($self, $op, 7, "substr", $op->first->sibling->sibling)
+        . " = "
+        . $self->deparse($op->first->sibling, 7);
+    }
+    maybe_local(@_, listop(@_, "substr"))
+}
 sub pp_vec { maybe_local(@_, listop(@_, "vec")) }
 sub pp_index { maybe_targmy(@_, \&listop, "index") }
 sub pp_rindex { maybe_targmy(@_, \&listop, "rindex") }
@@ -2399,9 +2423,7 @@ sub pp_unshift { maybe_targmy(@_, \&listop, "unshift") }
 sub pp_reverse { listop(@_, "reverse") }
 sub pp_warn { listop(@_, "warn") }
 sub pp_die { listop(@_, "die") }
-# Actually, return is exempt from the LLAFR (see examples in this very
-# module!), but for consistency's sake, ignore that fact
-sub pp_return { listop(@_, "return") }
+sub pp_return { listop(@_, "return", undef, 1) } # llafr does not apply
 sub pp_open { listop(@_, "open") }
 sub pp_pipe_op { listop(@_, "pipe") }
 sub pp_tie { listop(@_, "tie") }
@@ -2468,9 +2490,12 @@ sub pp_glob {
     my $self = shift;
     my($op, $cx) = @_;
     my $text = $self->dq($op->first->sibling);  # skip pushmark
+    my $keyword =
+       $op->flags & OPf_SPECIAL ? 'glob' : $self->keyword('glob');
     if ($text =~ /^\$?(\w|::|\`)+$/ # could look like a readline
-       or $text =~ /[<>]/) {
-       return 'glob(' . single_delim('qq', '"', $text) . ')';
+       or $keyword =~ /^CORE::/
+        or $text =~ /[<>]/) {
+       return "$keyword(" . single_delim('qq', '"', $text) . ')';
     } else {
        return '<' . $text . '>';
     }
@@ -2507,7 +2532,7 @@ sub indirop {
     my $self = shift;
     my($op, $cx, $name) = @_;
     my($expr, @exprs);
-    my $kid = $op->first->sibling;
+    my $firstkid = my $kid = $op->first->sibling;
     my $indir = "";
     if ($op->flags & OPf_STACKED) {
        $indir = $kid;
@@ -2531,7 +2556,7 @@ sub indirop {
        $indir = '{$b cmp $a} ';
     }
     for (; !null($kid); $kid = $kid->sibling) {
-       $expr = $self->deparse($kid, 6);
+       $expr = $self->deparse($kid, !$indir && $kid == $firstkid && $name eq "sort" && $firstkid->name eq "entersub" ? 16 : 6);
        push @exprs, $expr;
     }
     my $name2;
@@ -2544,7 +2569,7 @@ sub indirop {
     }
 
     my $args = $indir . join(", ", @exprs);
-    if ($indir ne "" and $name eq "sort") {
+    if ($indir ne "" && $name eq "sort") {
        # We don't want to say "sort(f 1, 2, 3)", since perl -w will
        # give bareword warnings in that case. Therefore if context
        # requires, we'll put parens around the outside "(sort f 1, 2,
@@ -2556,6 +2581,13 @@ sub indirop {
        } else {
            return "$name2 $args";
        }
+    } elsif (
+       !$indir && $name eq "sort"
+      && $op->first->sibling->name eq 'entersub'
+    ) {
+       # We cannot say sort foo(bar), as foo will be interpreted as a
+       # comparison routine.  We have to say sort(...) in that case.
+       return "$name2($args)";
     } else {
        return $self->maybe_parens_func($name2, $args, $cx, 5);
     }
@@ -2597,6 +2629,7 @@ sub pp_list {
     my($op, $cx) = @_;
     my($expr, @exprs);
     my $kid = $op->first->sibling; # skip pushmark
+    return '' if class($kid) eq 'NULL';
     my $lop;
     my $local = "either"; # could be local(...), my(...), state(...) or our(...)
     for ($lop = $kid; !null($lop); $lop = $lop->sibling) {
@@ -3249,7 +3282,7 @@ sub _method {
        # doesn't get flattened by the append_elem that adds the method,
        # making a (object, arg1, arg2, ...) list where the object
        # usually is. This can be distinguished from
-       # `($obj, $arg1, $arg2)->meth()' (which is legal if $arg2 is an
+       # '($obj, $arg1, $arg2)->meth()' (which is legal if $arg2 is an
        # object) because in the later the list is in scalar context
        # as the left side of -> always is, while in the former
        # the list is in list context as method arguments always are.
@@ -3283,7 +3316,8 @@ sub _method {
     }
 
     return { method => $meth, variable_method => ref($meth),
-             object => $obj, args => \@exprs  };
+             object => $obj, args => \@exprs  },
+          $cx;
 }
 
 # compat function only
@@ -3294,12 +3328,22 @@ sub method {
 }
 
 sub e_method {
-    my ($self, $info) = @_;
+    my ($self, $info, $cx) = @_;
     my $obj = $self->deparse($info->{object}, 24);
 
     my $meth = $info->{method};
     $meth = $self->deparse($meth, 1) if $info->{variable_method};
     my $args = join(", ", map { $self->deparse($_, 6) } @{$info->{args}} );
+    if ($info->{object}->name eq 'scope' && want_list $info->{object}) {
+       # method { $object }
+       # This must be deparsed this way to preserve list context
+       # of $object.
+       my $need_paren = $cx >= 6;
+       return '(' x $need_paren
+            . $meth . substr($obj,2) # chop off the "do"
+            . " $args"
+            . ')' x $need_paren;
+    }
     my $kid = $obj . "->" . $meth;
     if (length $args) {
        return $kid . "(" . $args . ")"; # parens mandatory
@@ -3382,7 +3426,7 @@ sub check_proto {
            }
        }
     }
-    return "&" if $proto and !$doneok; # too few args and no `;'
+    return "&" if $proto and !$doneok; # too few args and no ';'
     return "&" if @args;               # too many args
     return ("", join ", ", @reals);
 }
@@ -3804,6 +3848,18 @@ sub const {
            }
            return "{" . join(", ", @elts) . "}";
        } elsif (class($ref) eq "CV") {
+           BEGIN {
+# Commented out until after 5.15.6
+#              if ($] > 5.0150051) {
+                   require overloading;
+                   unimport overloading;
+#              }
+           }
+           # Remove the 1|| after 5.15.6
+           if ((1||$] > 5.0150051) && $self->{curcv} &&
+                $self->{curcv}->object_2svref == $ref->object_2svref) {
+               return $self->keyword("__SUB__");
+           }
            return "sub " . $self->deparse_sub($ref);
        }
        if ($ref->FLAGS & SVs_SMG) {
@@ -3857,7 +3913,7 @@ sub pp_const {
     if ($op->private & OPpCONST_ARYBASE) {
         return '$[';
     }
-#    if ($op->private & OPpCONST_BARE) { # trouble with `=>' autoquoting
+#    if ($op->private & OPpCONST_BARE) { # trouble with '=>' autoquoting
 #      return $self->const_sv($op)->PV;
 #    }
     my $sv = $self->const_sv($op);
@@ -3934,7 +3990,7 @@ sub double_delim {
        if (($succeed, $to) = balanced_delim($to) and $succeed) {
            return "$from$to";
        } else {
-           for $delim ('/', '"', '#') { # note no `'' -- s''' is special
+           for $delim ('/', '"', '#') { # note no "'" -- s''' is special
                return "$from$delim$to$delim" if index($to, $delim) == -1;
            }
            $to =~ s[/][\\/]g;
@@ -4357,6 +4413,8 @@ sub pp_match { matchop(@_, "m", "/") }
 sub pp_pushre { matchop(@_, "m", "/") }
 sub pp_qr { matchop(@_, "qr", "") }
 
+sub pp_runcv { unop(@_, "__SUB__"); }
+
 sub pp_split {
     my $self = shift;
     my($op, $cx) = @_;