xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/ext/B/B/Deparse.pm (revision 0:68f95e015346)
1*0Sstevel@tonic-gate# B::Deparse.pm
2*0Sstevel@tonic-gate# Copyright (c) 1998-2000, 2002, 2003 Stephen McCamant. All rights reserved.
3*0Sstevel@tonic-gate# This module is free software; you can redistribute and/or modify
4*0Sstevel@tonic-gate# it under the same terms as Perl itself.
5*0Sstevel@tonic-gate
6*0Sstevel@tonic-gate# This is based on the module of the same name by Malcolm Beattie,
7*0Sstevel@tonic-gate# but essentially none of his code remains.
8*0Sstevel@tonic-gate
9*0Sstevel@tonic-gatepackage B::Deparse;
10*0Sstevel@tonic-gateuse Carp;
11*0Sstevel@tonic-gateuse B qw(class main_root main_start main_cv svref_2object opnumber perlstring
12*0Sstevel@tonic-gate	 OPf_WANT OPf_WANT_VOID OPf_WANT_SCALAR OPf_WANT_LIST
13*0Sstevel@tonic-gate	 OPf_KIDS OPf_REF OPf_STACKED OPf_SPECIAL OPf_MOD
14*0Sstevel@tonic-gate	 OPpLVAL_INTRO OPpOUR_INTRO OPpENTERSUB_AMPER OPpSLICE OPpCONST_BARE
15*0Sstevel@tonic-gate	 OPpTRANS_SQUASH OPpTRANS_DELETE OPpTRANS_COMPLEMENT OPpTARGET_MY
16*0Sstevel@tonic-gate	 OPpCONST_ARYBASE OPpEXISTS_SUB OPpSORT_NUMERIC OPpSORT_INTEGER
17*0Sstevel@tonic-gate	 OPpSORT_REVERSE OPpSORT_INPLACE
18*0Sstevel@tonic-gate	 SVf_IOK SVf_NOK SVf_ROK SVf_POK SVpad_OUR SVf_FAKE SVs_RMG SVs_SMG
19*0Sstevel@tonic-gate         CVf_METHOD CVf_LOCKED CVf_LVALUE
20*0Sstevel@tonic-gate	 PMf_KEEP PMf_GLOBAL PMf_CONTINUE PMf_EVAL PMf_ONCE PMf_SKIPWHITE
21*0Sstevel@tonic-gate	 PMf_MULTILINE PMf_SINGLELINE PMf_FOLD PMf_EXTENDED);
22*0Sstevel@tonic-gate# Not sure if I really should have this as maint's version, given that Deparse
23*0Sstevel@tonic-gate# differs from blead. (latter has // support)
24*0Sstevel@tonic-gate$VERSION = 0.66;
25*0Sstevel@tonic-gateuse strict;
26*0Sstevel@tonic-gateuse vars qw/$AUTOLOAD/;
27*0Sstevel@tonic-gateuse warnings ();
28*0Sstevel@tonic-gate
29*0Sstevel@tonic-gate# Changes between 0.50 and 0.51:
30*0Sstevel@tonic-gate# - fixed nulled leave with live enter in sort { }
31*0Sstevel@tonic-gate# - fixed reference constants (\"str")
32*0Sstevel@tonic-gate# - handle empty programs gracefully
33*0Sstevel@tonic-gate# - handle infinte loops (for (;;) {}, while (1) {})
34*0Sstevel@tonic-gate# - differentiate between `for my $x ...' and `my $x; for $x ...'
35*0Sstevel@tonic-gate# - various minor cleanups
36*0Sstevel@tonic-gate# - moved globals into an object
37*0Sstevel@tonic-gate# - added `-u', like B::C
38*0Sstevel@tonic-gate# - package declarations using cop_stash
39*0Sstevel@tonic-gate# - subs, formats and code sorted by cop_seq
40*0Sstevel@tonic-gate# Changes between 0.51 and 0.52:
41*0Sstevel@tonic-gate# - added pp_threadsv (special variables under USE_5005THREADS)
42*0Sstevel@tonic-gate# - added documentation
43*0Sstevel@tonic-gate# Changes between 0.52 and 0.53:
44*0Sstevel@tonic-gate# - many changes adding precedence contexts and associativity
45*0Sstevel@tonic-gate# - added `-p' and `-s' output style options
46*0Sstevel@tonic-gate# - various other minor fixes
47*0Sstevel@tonic-gate# Changes between 0.53 and 0.54:
48*0Sstevel@tonic-gate# - added support for new `for (1..100)' optimization,
49*0Sstevel@tonic-gate#   thanks to Gisle Aas
50*0Sstevel@tonic-gate# Changes between 0.54 and 0.55:
51*0Sstevel@tonic-gate# - added support for new qr// construct
52*0Sstevel@tonic-gate# - added support for new pp_regcreset OP
53*0Sstevel@tonic-gate# Changes between 0.55 and 0.56:
54*0Sstevel@tonic-gate# - tested on base/*.t, cmd/*.t, comp/*.t, io/*.t
55*0Sstevel@tonic-gate# - fixed $# on non-lexicals broken in last big rewrite
56*0Sstevel@tonic-gate# - added temporary fix for change in opcode of OP_STRINGIFY
57*0Sstevel@tonic-gate# - fixed problem in 0.54's for() patch in `for (@ary)'
58*0Sstevel@tonic-gate# - fixed precedence in conditional of ?:
59*0Sstevel@tonic-gate# - tweaked list paren elimination in `my($x) = @_'
60*0Sstevel@tonic-gate# - made continue-block detection trickier wrt. null ops
61*0Sstevel@tonic-gate# - fixed various prototype problems in pp_entersub
62*0Sstevel@tonic-gate# - added support for sub prototypes that never get GVs
63*0Sstevel@tonic-gate# - added unquoting for special filehandle first arg in truncate
64*0Sstevel@tonic-gate# - print doubled rv2gv (a bug) as `*{*GV}' instead of illegal `**GV'
65*0Sstevel@tonic-gate# - added semicolons at the ends of blocks
66*0Sstevel@tonic-gate# - added -l `#line' declaration option -- fixes cmd/subval.t 27,28
67*0Sstevel@tonic-gate# Changes between 0.56 and 0.561:
68*0Sstevel@tonic-gate# - fixed multiply-declared my var in pp_truncate (thanks to Sarathy)
69*0Sstevel@tonic-gate# - used new B.pm symbolic constants (done by Nick Ing-Simmons)
70*0Sstevel@tonic-gate# Changes between 0.561 and 0.57:
71*0Sstevel@tonic-gate# - stylistic changes to symbolic constant stuff
72*0Sstevel@tonic-gate# - handled scope in s///e replacement code
73*0Sstevel@tonic-gate# - added unquote option for expanding "" into concats, etc.
74*0Sstevel@tonic-gate# - split method and proto parts of pp_entersub into separate functions
75*0Sstevel@tonic-gate# - various minor cleanups
76*0Sstevel@tonic-gate# Changes after 0.57:
77*0Sstevel@tonic-gate# - added parens in \&foo (patch by Albert Dvornik)
78*0Sstevel@tonic-gate# Changes between 0.57 and 0.58:
79*0Sstevel@tonic-gate# - fixed `0' statements that weren't being printed
80*0Sstevel@tonic-gate# - added methods for use from other programs
81*0Sstevel@tonic-gate#   (based on patches from James Duncan and Hugo van der Sanden)
82*0Sstevel@tonic-gate# - added -si and -sT to control indenting (also based on a patch from Hugo)
83*0Sstevel@tonic-gate# - added -sv to print something else instead of '???'
84*0Sstevel@tonic-gate# - preliminary version of utf8 tr/// handling
85*0Sstevel@tonic-gate# Changes after 0.58:
86*0Sstevel@tonic-gate# - uses of $op->ppaddr changed to new $op->name (done by Sarathy)
87*0Sstevel@tonic-gate# - added support for Hugo's new OP_SETSTATE (like nextstate)
88*0Sstevel@tonic-gate# Changes between 0.58 and 0.59
89*0Sstevel@tonic-gate# - added support for Chip's OP_METHOD_NAMED
90*0Sstevel@tonic-gate# - added support for Ilya's OPpTARGET_MY optimization
91*0Sstevel@tonic-gate# - elided arrows before `()' subscripts when possible
92*0Sstevel@tonic-gate# Changes between 0.59 and 0.60
93*0Sstevel@tonic-gate# - support for method attribues was added
94*0Sstevel@tonic-gate# - some warnings fixed
95*0Sstevel@tonic-gate# - separate recognition of constant subs
96*0Sstevel@tonic-gate# - rewrote continue block handling, now recoginizing for loops
97*0Sstevel@tonic-gate# - added more control of expanding control structures
98*0Sstevel@tonic-gate# Changes between 0.60 and 0.61 (mostly by Robin Houston)
99*0Sstevel@tonic-gate# - many bug-fixes
100*0Sstevel@tonic-gate# - support for pragmas and 'use'
101*0Sstevel@tonic-gate# - support for the little-used $[ variable
102*0Sstevel@tonic-gate# - support for __DATA__ sections
103*0Sstevel@tonic-gate# - UTF8 support
104*0Sstevel@tonic-gate# - BEGIN, CHECK, INIT and END blocks
105*0Sstevel@tonic-gate# - scoping of subroutine declarations fixed
106*0Sstevel@tonic-gate# - compile-time output from the input program can be suppressed, so that the
107*0Sstevel@tonic-gate#   output is just the deparsed code. (a change to O.pm in fact)
108*0Sstevel@tonic-gate# - our() declarations
109*0Sstevel@tonic-gate# - *all* the known bugs are now listed in the BUGS section
110*0Sstevel@tonic-gate# - comprehensive test mechanism (TEST -deparse)
111*0Sstevel@tonic-gate# Changes between 0.62 and 0.63 (mostly by Rafael Garcia-Suarez)
112*0Sstevel@tonic-gate# - bug-fixes
113*0Sstevel@tonic-gate# - new switch -P
114*0Sstevel@tonic-gate# - support for command-line switches (-l, -0, etc.)
115*0Sstevel@tonic-gate# Changes between 0.63 and 0.64
116*0Sstevel@tonic-gate# - support for //, CHECK blocks, and assertions
117*0Sstevel@tonic-gate# - improved handling of foreach loops and lexicals
118*0Sstevel@tonic-gate# - option to use Data::Dumper for constants
119*0Sstevel@tonic-gate# - more bug fixes
120*0Sstevel@tonic-gate# - discovered lots more bugs not yet fixed
121*0Sstevel@tonic-gate
122*0Sstevel@tonic-gate# Todo:
123*0Sstevel@tonic-gate#  (See also BUGS section at the end of this file)
124*0Sstevel@tonic-gate#
125*0Sstevel@tonic-gate# - finish tr/// changes
126*0Sstevel@tonic-gate# - add option for even more parens (generalize \&foo change)
127*0Sstevel@tonic-gate# - left/right context
128*0Sstevel@tonic-gate# - copy comments (look at real text with $^P?)
129*0Sstevel@tonic-gate# - avoid semis in one-statement blocks
130*0Sstevel@tonic-gate# - associativity of &&=, ||=, ?:
131*0Sstevel@tonic-gate# - ',' => '=>' (auto-unquote?)
132*0Sstevel@tonic-gate# - break long lines ("\r" as discretionary break?)
133*0Sstevel@tonic-gate# - configurable syntax highlighting: ANSI color, HTML, TeX, etc.
134*0Sstevel@tonic-gate# - more style options: brace style, hex vs. octal, quotes, ...
135*0Sstevel@tonic-gate# - print big ints as hex/octal instead of decimal (heuristic?)
136*0Sstevel@tonic-gate# - handle `my $x if 0'?
137*0Sstevel@tonic-gate# - version using op_next instead of op_first/sibling?
138*0Sstevel@tonic-gate# - avoid string copies (pass arrays, one big join?)
139*0Sstevel@tonic-gate# - here-docs?
140*0Sstevel@tonic-gate
141*0Sstevel@tonic-gate# Current test.deparse failures
142*0Sstevel@tonic-gate# comp/assertions 38 - disabled assertions should be like "my($x) if 0"
143*0Sstevel@tonic-gate#    'sub f : assertion {}; no assertions; my $x=1; {f(my $x=2); print "$x\n"}'
144*0Sstevel@tonic-gate# comp/hints 6 - location of BEGIN blocks wrt. block openings
145*0Sstevel@tonic-gate# run/switchI 1 - missing -I switches entirely
146*0Sstevel@tonic-gate#    perl -Ifoo -e 'print @INC'
147*0Sstevel@tonic-gate# op/caller 2 - warning mask propagates backwards before warnings::register
148*0Sstevel@tonic-gate#    'use warnings; BEGIN {${^WARNING_BITS} eq "U"x12;} use warnings::register'
149*0Sstevel@tonic-gate# op/getpid 2 - can't assign to shared my() declaration (threads only)
150*0Sstevel@tonic-gate#    'my $x : shared = 5'
151*0Sstevel@tonic-gate# op/override 7 - parens on overriden require change v-string interpretation
152*0Sstevel@tonic-gate#    'BEGIN{*CORE::GLOBAL::require=sub {}} require v5.6'
153*0Sstevel@tonic-gate#    c.f. 'BEGIN { *f = sub {0} }; f 2'
154*0Sstevel@tonic-gate# op/pat 774 - losing Unicode-ness of Latin1-only strings
155*0Sstevel@tonic-gate#    'use charnames ":short"; $x="\N{latin:a with acute}"'
156*0Sstevel@tonic-gate# op/recurse 12 - missing parens on recursive call makes it look like method
157*0Sstevel@tonic-gate#    'sub f { f($x) }'
158*0Sstevel@tonic-gate# op/subst 90 - inconsistent handling of utf8 under "use utf8"
159*0Sstevel@tonic-gate# op/taint 29 - "use re 'taint'" deparsed in the wrong place wrt. block open
160*0Sstevel@tonic-gate# op/tiehandle compile - "use strict" deparsed in the wrong place
161*0Sstevel@tonic-gate# uni/tr_ several
162*0Sstevel@tonic-gate# ext/B/t/xref 11 - line numbers when we add newlines to one-line subs
163*0Sstevel@tonic-gate# ext/Data/Dumper/t/dumper compile
164*0Sstevel@tonic-gate# ext/DB_file/several
165*0Sstevel@tonic-gate# ext/Encode/several
166*0Sstevel@tonic-gate# ext/Ernno/Errno warnings
167*0Sstevel@tonic-gate# ext/IO/lib/IO/t/io_sel 23
168*0Sstevel@tonic-gate# ext/PerlIO/t/encoding compile
169*0Sstevel@tonic-gate# ext/POSIX/t/posix 6
170*0Sstevel@tonic-gate# ext/Socket/Socket 8
171*0Sstevel@tonic-gate# ext/Storable/t/croak compile
172*0Sstevel@tonic-gate# lib/Attribute/Handlers/t/multi compile
173*0Sstevel@tonic-gate# lib/bignum/ several
174*0Sstevel@tonic-gate# lib/charnames 35
175*0Sstevel@tonic-gate# lib/constant 32
176*0Sstevel@tonic-gate# lib/English 40
177*0Sstevel@tonic-gate# lib/ExtUtils/t/bytes 4
178*0Sstevel@tonic-gate# lib/File/DosGlob compile
179*0Sstevel@tonic-gate# lib/Filter/Simple/t/data 1
180*0Sstevel@tonic-gate# lib/Math/BigInt/t/constant 1
181*0Sstevel@tonic-gate# lib/Net/t/config Deparse-warning
182*0Sstevel@tonic-gate# lib/overload compile
183*0Sstevel@tonic-gate# lib/Switch/ several
184*0Sstevel@tonic-gate# lib/Symbol 4
185*0Sstevel@tonic-gate# lib/Test/Simple several
186*0Sstevel@tonic-gate# lib/Term/Complete
187*0Sstevel@tonic-gate# lib/Tie/File/t/29_downcopy 5
188*0Sstevel@tonic-gate# lib/vars 22
189*0Sstevel@tonic-gate
190*0Sstevel@tonic-gate# Object fields (were globals):
191*0Sstevel@tonic-gate#
192*0Sstevel@tonic-gate# avoid_local:
193*0Sstevel@tonic-gate# (local($a), local($b)) and local($a, $b) have the same internal
194*0Sstevel@tonic-gate# representation but the short form looks better. We notice we can
195*0Sstevel@tonic-gate# use a large-scale local when checking the list, but need to prevent
196*0Sstevel@tonic-gate# individual locals too. This hash holds the addresses of OPs that
197*0Sstevel@tonic-gate# have already had their local-ness accounted for. The same thing
198*0Sstevel@tonic-gate# is done with my().
199*0Sstevel@tonic-gate#
200*0Sstevel@tonic-gate# curcv:
201*0Sstevel@tonic-gate# CV for current sub (or main program) being deparsed
202*0Sstevel@tonic-gate#
203*0Sstevel@tonic-gate# curcvlex:
204*0Sstevel@tonic-gate# Cached hash of lexical variables for curcv: keys are names,
205*0Sstevel@tonic-gate# each value is an array of pairs, indicating the cop_seq of scopes
206*0Sstevel@tonic-gate# in which a var of that name is valid.
207*0Sstevel@tonic-gate#
208*0Sstevel@tonic-gate# curcop:
209*0Sstevel@tonic-gate# COP for statement being deparsed
210*0Sstevel@tonic-gate#
211*0Sstevel@tonic-gate# curstash:
212*0Sstevel@tonic-gate# name of the current package for deparsed code
213*0Sstevel@tonic-gate#
214*0Sstevel@tonic-gate# subs_todo:
215*0Sstevel@tonic-gate# array of [cop_seq, CV, is_format?] for subs and formats we still
216*0Sstevel@tonic-gate# want to deparse
217*0Sstevel@tonic-gate#
218*0Sstevel@tonic-gate# protos_todo:
219*0Sstevel@tonic-gate# as above, but [name, prototype] for subs that never got a GV
220*0Sstevel@tonic-gate#
221*0Sstevel@tonic-gate# subs_done, forms_done:
222*0Sstevel@tonic-gate# keys are addresses of GVs for subs and formats we've already
223*0Sstevel@tonic-gate# deparsed (or at least put into subs_todo)
224*0Sstevel@tonic-gate#
225*0Sstevel@tonic-gate# subs_declared
226*0Sstevel@tonic-gate# keys are names of subs for which we've printed declarations.
227*0Sstevel@tonic-gate# That means we can omit parentheses from the arguments.
228*0Sstevel@tonic-gate#
229*0Sstevel@tonic-gate# subs_deparsed
230*0Sstevel@tonic-gate# Keeps track of fully qualified names of all deparsed subs.
231*0Sstevel@tonic-gate#
232*0Sstevel@tonic-gate# parens: -p
233*0Sstevel@tonic-gate# linenums: -l
234*0Sstevel@tonic-gate# unquote: -q
235*0Sstevel@tonic-gate# cuddle: ` ' or `\n', depending on -sC
236*0Sstevel@tonic-gate# indent_size: -si
237*0Sstevel@tonic-gate# use_tabs: -sT
238*0Sstevel@tonic-gate# ex_const: -sv
239*0Sstevel@tonic-gate
240*0Sstevel@tonic-gate# A little explanation of how precedence contexts and associativity
241*0Sstevel@tonic-gate# work:
242*0Sstevel@tonic-gate#
243*0Sstevel@tonic-gate# deparse() calls each per-op subroutine with an argument $cx (short
244*0Sstevel@tonic-gate# for context, but not the same as the cx* in the perl core), which is
245*0Sstevel@tonic-gate# a number describing the op's parents in terms of precedence, whether
246*0Sstevel@tonic-gate# they're inside an expression or at statement level, etc.  (see
247*0Sstevel@tonic-gate# chart below). When ops with children call deparse on them, they pass
248*0Sstevel@tonic-gate# along their precedence. Fractional values are used to implement
249*0Sstevel@tonic-gate# associativity (`($x + $y) + $z' => `$x + $y + $y') and related
250*0Sstevel@tonic-gate# parentheses hacks. The major disadvantage of this scheme is that
251*0Sstevel@tonic-gate# it doesn't know about right sides and left sides, so say if you
252*0Sstevel@tonic-gate# assign a listop to a variable, it can't tell it's allowed to leave
253*0Sstevel@tonic-gate# the parens off the listop.
254*0Sstevel@tonic-gate
255*0Sstevel@tonic-gate# Precedences:
256*0Sstevel@tonic-gate# 26             [TODO] inside interpolation context ("")
257*0Sstevel@tonic-gate# 25 left        terms and list operators (leftward)
258*0Sstevel@tonic-gate# 24 left        ->
259*0Sstevel@tonic-gate# 23 nonassoc    ++ --
260*0Sstevel@tonic-gate# 22 right       **
261*0Sstevel@tonic-gate# 21 right       ! ~ \ and unary + and -
262*0Sstevel@tonic-gate# 20 left        =~ !~
263*0Sstevel@tonic-gate# 19 left        * / % x
264*0Sstevel@tonic-gate# 18 left        + - .
265*0Sstevel@tonic-gate# 17 left        << >>
266*0Sstevel@tonic-gate# 16 nonassoc    named unary operators
267*0Sstevel@tonic-gate# 15 nonassoc    < > <= >= lt gt le ge
268*0Sstevel@tonic-gate# 14 nonassoc    == != <=> eq ne cmp
269*0Sstevel@tonic-gate# 13 left        &
270*0Sstevel@tonic-gate# 12 left        | ^
271*0Sstevel@tonic-gate# 11 left        &&
272*0Sstevel@tonic-gate# 10 left        ||
273*0Sstevel@tonic-gate#  9 nonassoc    ..  ...
274*0Sstevel@tonic-gate#  8 right       ?:
275*0Sstevel@tonic-gate#  7 right       = += -= *= etc.
276*0Sstevel@tonic-gate#  6 left        , =>
277*0Sstevel@tonic-gate#  5 nonassoc    list operators (rightward)
278*0Sstevel@tonic-gate#  4 right       not
279*0Sstevel@tonic-gate#  3 left        and
280*0Sstevel@tonic-gate#  2 left        or xor
281*0Sstevel@tonic-gate#  1             statement modifiers
282*0Sstevel@tonic-gate#  0.5           statements, but still print scopes as do { ... }
283*0Sstevel@tonic-gate#  0             statement level
284*0Sstevel@tonic-gate
285*0Sstevel@tonic-gate# Nonprinting characters with special meaning:
286*0Sstevel@tonic-gate# \cS - steal parens (see maybe_parens_unop)
287*0Sstevel@tonic-gate# \n - newline and indent
288*0Sstevel@tonic-gate# \t - increase indent
289*0Sstevel@tonic-gate# \b - decrease indent (`outdent')
290*0Sstevel@tonic-gate# \f - flush left (no indent)
291*0Sstevel@tonic-gate# \cK - kill following semicolon, if any
292*0Sstevel@tonic-gate
293*0Sstevel@tonic-gatesub null {
294*0Sstevel@tonic-gate    my $op = shift;
295*0Sstevel@tonic-gate    return class($op) eq "NULL";
296*0Sstevel@tonic-gate}
297*0Sstevel@tonic-gate
298*0Sstevel@tonic-gatesub todo {
299*0Sstevel@tonic-gate    my $self = shift;
300*0Sstevel@tonic-gate    my($cv, $is_form) = @_;
301*0Sstevel@tonic-gate    return unless ($cv->FILE eq $0 || exists $self->{files}{$cv->FILE});
302*0Sstevel@tonic-gate    my $seq;
303*0Sstevel@tonic-gate    if ($cv->OUTSIDE_SEQ) {
304*0Sstevel@tonic-gate	$seq = $cv->OUTSIDE_SEQ;
305*0Sstevel@tonic-gate    } elsif (!null($cv->START) and is_state($cv->START)) {
306*0Sstevel@tonic-gate	$seq = $cv->START->cop_seq;
307*0Sstevel@tonic-gate    } else {
308*0Sstevel@tonic-gate	$seq = 0;
309*0Sstevel@tonic-gate    }
310*0Sstevel@tonic-gate    push @{$self->{'subs_todo'}}, [$seq, $cv, $is_form];
311*0Sstevel@tonic-gate    unless ($is_form || class($cv->STASH) eq 'SPECIAL') {
312*0Sstevel@tonic-gate	$self->{'subs_deparsed'}{$cv->STASH->NAME."::".$cv->GV->NAME} = 1;
313*0Sstevel@tonic-gate    }
314*0Sstevel@tonic-gate}
315*0Sstevel@tonic-gate
316*0Sstevel@tonic-gatesub next_todo {
317*0Sstevel@tonic-gate    my $self = shift;
318*0Sstevel@tonic-gate    my $ent = shift @{$self->{'subs_todo'}};
319*0Sstevel@tonic-gate    my $cv = $ent->[1];
320*0Sstevel@tonic-gate    my $gv = $cv->GV;
321*0Sstevel@tonic-gate    my $name = $self->gv_name($gv);
322*0Sstevel@tonic-gate    if ($ent->[2]) {
323*0Sstevel@tonic-gate	return "format $name =\n"
324*0Sstevel@tonic-gate	    . $self->deparse_format($ent->[1]). "\n";
325*0Sstevel@tonic-gate    } else {
326*0Sstevel@tonic-gate	$self->{'subs_declared'}{$name} = 1;
327*0Sstevel@tonic-gate	if ($name eq "BEGIN") {
328*0Sstevel@tonic-gate	    my $use_dec = $self->begin_is_use($cv);
329*0Sstevel@tonic-gate	    if (defined ($use_dec) and $self->{'expand'} < 5) {
330*0Sstevel@tonic-gate		return () if 0 == length($use_dec);
331*0Sstevel@tonic-gate		return $use_dec;
332*0Sstevel@tonic-gate	    }
333*0Sstevel@tonic-gate	}
334*0Sstevel@tonic-gate	my $l = '';
335*0Sstevel@tonic-gate	if ($self->{'linenums'}) {
336*0Sstevel@tonic-gate	    my $line = $gv->LINE;
337*0Sstevel@tonic-gate	    my $file = $gv->FILE;
338*0Sstevel@tonic-gate	    $l = "\n\f#line $line \"$file\"\n";
339*0Sstevel@tonic-gate	}
340*0Sstevel@tonic-gate	my $p = '';
341*0Sstevel@tonic-gate	if (class($cv->STASH) ne "SPECIAL") {
342*0Sstevel@tonic-gate	    my $stash = $cv->STASH->NAME;
343*0Sstevel@tonic-gate	    if ($stash ne $self->{'curstash'}) {
344*0Sstevel@tonic-gate		$p = "package $stash;\n";
345*0Sstevel@tonic-gate		$name = "$self->{'curstash'}::$name" unless $name =~ /::/;
346*0Sstevel@tonic-gate		$self->{'curstash'} = $stash;
347*0Sstevel@tonic-gate	    }
348*0Sstevel@tonic-gate	    $name =~ s/^\Q$stash\E:://;
349*0Sstevel@tonic-gate	}
350*0Sstevel@tonic-gate        return "${p}${l}sub $name " . $self->deparse_sub($cv);
351*0Sstevel@tonic-gate    }
352*0Sstevel@tonic-gate}
353*0Sstevel@tonic-gate
354*0Sstevel@tonic-gate# Return a "use" declaration for this BEGIN block, if appropriate
355*0Sstevel@tonic-gatesub begin_is_use {
356*0Sstevel@tonic-gate    my ($self, $cv) = @_;
357*0Sstevel@tonic-gate    my $root = $cv->ROOT;
358*0Sstevel@tonic-gate    local @$self{qw'curcv curcvlex'} = ($cv);
359*0Sstevel@tonic-gate#require B::Debug;
360*0Sstevel@tonic-gate#B::walkoptree($cv->ROOT, "debug");
361*0Sstevel@tonic-gate    my $lineseq = $root->first;
362*0Sstevel@tonic-gate    return if $lineseq->name ne "lineseq";
363*0Sstevel@tonic-gate
364*0Sstevel@tonic-gate    my $req_op = $lineseq->first->sibling;
365*0Sstevel@tonic-gate    return if $req_op->name ne "require";
366*0Sstevel@tonic-gate
367*0Sstevel@tonic-gate    my $module;
368*0Sstevel@tonic-gate    if ($req_op->first->private & OPpCONST_BARE) {
369*0Sstevel@tonic-gate	# Actually it should always be a bareword
370*0Sstevel@tonic-gate	$module = $self->const_sv($req_op->first)->PV;
371*0Sstevel@tonic-gate	$module =~ s[/][::]g;
372*0Sstevel@tonic-gate	$module =~ s/.pm$//;
373*0Sstevel@tonic-gate    }
374*0Sstevel@tonic-gate    else {
375*0Sstevel@tonic-gate	$module = $self->const($self->const_sv($req_op->first), 6);
376*0Sstevel@tonic-gate    }
377*0Sstevel@tonic-gate
378*0Sstevel@tonic-gate    my $version;
379*0Sstevel@tonic-gate    my $version_op = $req_op->sibling;
380*0Sstevel@tonic-gate    return if class($version_op) eq "NULL";
381*0Sstevel@tonic-gate    if ($version_op->name eq "lineseq") {
382*0Sstevel@tonic-gate	# We have a version parameter; skip nextstate & pushmark
383*0Sstevel@tonic-gate	my $constop = $version_op->first->next->next;
384*0Sstevel@tonic-gate
385*0Sstevel@tonic-gate	return unless $self->const_sv($constop)->PV eq $module;
386*0Sstevel@tonic-gate	$constop = $constop->sibling;
387*0Sstevel@tonic-gate	$version = $self->const_sv($constop);
388*0Sstevel@tonic-gate	if (class($version) eq "IV") {
389*0Sstevel@tonic-gate	    $version = $version->int_value;
390*0Sstevel@tonic-gate	} elsif (class($version) eq "NV") {
391*0Sstevel@tonic-gate	    $version = $version->NV;
392*0Sstevel@tonic-gate	} elsif (class($version) ne "PVMG") {
393*0Sstevel@tonic-gate	    # Includes PVIV and PVNV
394*0Sstevel@tonic-gate	    $version = $version->PV;
395*0Sstevel@tonic-gate	} else {
396*0Sstevel@tonic-gate	    # version specified as a v-string
397*0Sstevel@tonic-gate	    $version = 'v'.join '.', map ord, split //, $version->PV;
398*0Sstevel@tonic-gate	}
399*0Sstevel@tonic-gate	$constop = $constop->sibling;
400*0Sstevel@tonic-gate	return if $constop->name ne "method_named";
401*0Sstevel@tonic-gate	return if $self->const_sv($constop)->PV ne "VERSION";
402*0Sstevel@tonic-gate    }
403*0Sstevel@tonic-gate
404*0Sstevel@tonic-gate    $lineseq = $version_op->sibling;
405*0Sstevel@tonic-gate    return if $lineseq->name ne "lineseq";
406*0Sstevel@tonic-gate    my $entersub = $lineseq->first->sibling;
407*0Sstevel@tonic-gate    if ($entersub->name eq "stub") {
408*0Sstevel@tonic-gate	return "use $module $version ();\n" if defined $version;
409*0Sstevel@tonic-gate	return "use $module ();\n";
410*0Sstevel@tonic-gate    }
411*0Sstevel@tonic-gate    return if $entersub->name ne "entersub";
412*0Sstevel@tonic-gate
413*0Sstevel@tonic-gate    # See if there are import arguments
414*0Sstevel@tonic-gate    my $args = '';
415*0Sstevel@tonic-gate
416*0Sstevel@tonic-gate    my $svop = $entersub->first->sibling; # Skip over pushmark
417*0Sstevel@tonic-gate    return unless $self->const_sv($svop)->PV eq $module;
418*0Sstevel@tonic-gate
419*0Sstevel@tonic-gate    # Pull out the arguments
420*0Sstevel@tonic-gate    for ($svop=$svop->sibling; $svop->name ne "method_named";
421*0Sstevel@tonic-gate		$svop = $svop->sibling) {
422*0Sstevel@tonic-gate	$args .= ", " if length($args);
423*0Sstevel@tonic-gate	$args .= $self->deparse($svop, 6);
424*0Sstevel@tonic-gate    }
425*0Sstevel@tonic-gate
426*0Sstevel@tonic-gate    my $use = 'use';
427*0Sstevel@tonic-gate    my $method_named = $svop;
428*0Sstevel@tonic-gate    return if $method_named->name ne "method_named";
429*0Sstevel@tonic-gate    my $method_name = $self->const_sv($method_named)->PV;
430*0Sstevel@tonic-gate
431*0Sstevel@tonic-gate    if ($method_name eq "unimport") {
432*0Sstevel@tonic-gate	$use = 'no';
433*0Sstevel@tonic-gate    }
434*0Sstevel@tonic-gate
435*0Sstevel@tonic-gate    # Certain pragmas are dealt with using hint bits,
436*0Sstevel@tonic-gate    # so we ignore them here
437*0Sstevel@tonic-gate    if ($module eq 'strict' || $module eq 'integer'
438*0Sstevel@tonic-gate	|| $module eq 'bytes' || $module eq 'warnings') {
439*0Sstevel@tonic-gate	return "";
440*0Sstevel@tonic-gate    }
441*0Sstevel@tonic-gate
442*0Sstevel@tonic-gate    if (defined $version && length $args) {
443*0Sstevel@tonic-gate	return "$use $module $version ($args);\n";
444*0Sstevel@tonic-gate    } elsif (defined $version) {
445*0Sstevel@tonic-gate	return "$use $module $version;\n";
446*0Sstevel@tonic-gate    } elsif (length $args) {
447*0Sstevel@tonic-gate	return "$use $module ($args);\n";
448*0Sstevel@tonic-gate    } else {
449*0Sstevel@tonic-gate	return "$use $module;\n";
450*0Sstevel@tonic-gate    }
451*0Sstevel@tonic-gate}
452*0Sstevel@tonic-gate
453*0Sstevel@tonic-gatesub stash_subs {
454*0Sstevel@tonic-gate    my ($self, $pack) = @_;
455*0Sstevel@tonic-gate    my (@ret, $stash);
456*0Sstevel@tonic-gate    if (!defined $pack) {
457*0Sstevel@tonic-gate	$pack = '';
458*0Sstevel@tonic-gate	$stash = \%::;
459*0Sstevel@tonic-gate    }
460*0Sstevel@tonic-gate    else {
461*0Sstevel@tonic-gate	$pack =~ s/(::)?$/::/;
462*0Sstevel@tonic-gate	no strict 'refs';
463*0Sstevel@tonic-gate	$stash = \%$pack;
464*0Sstevel@tonic-gate    }
465*0Sstevel@tonic-gate    my %stash = svref_2object($stash)->ARRAY;
466*0Sstevel@tonic-gate    while (my ($key, $val) = each %stash) {
467*0Sstevel@tonic-gate	next if $key eq 'main::';	# avoid infinite recursion
468*0Sstevel@tonic-gate	my $class = class($val);
469*0Sstevel@tonic-gate	if ($class eq "PV") {
470*0Sstevel@tonic-gate	    # Just a prototype. As an ugly but fairly effective way
471*0Sstevel@tonic-gate	    # to find out if it belongs here is to see if the AUTOLOAD
472*0Sstevel@tonic-gate	    # (if any) for the stash was defined in one of our files.
473*0Sstevel@tonic-gate	    my $A = $stash{"AUTOLOAD"};
474*0Sstevel@tonic-gate	    if (defined ($A) && class($A) eq "GV" && defined($A->CV)
475*0Sstevel@tonic-gate		&& class($A->CV) eq "CV") {
476*0Sstevel@tonic-gate		my $AF = $A->FILE;
477*0Sstevel@tonic-gate		next unless $AF eq $0 || exists $self->{'files'}{$AF};
478*0Sstevel@tonic-gate	    }
479*0Sstevel@tonic-gate	    push @{$self->{'protos_todo'}}, [$pack . $key, $val->PV];
480*0Sstevel@tonic-gate	} elsif ($class eq "IV") {
481*0Sstevel@tonic-gate	    # Just a name. As above.
482*0Sstevel@tonic-gate	    my $A = $stash{"AUTOLOAD"};
483*0Sstevel@tonic-gate	    if (defined ($A) && class($A) eq "GV" && defined($A->CV)
484*0Sstevel@tonic-gate		&& class($A->CV) eq "CV") {
485*0Sstevel@tonic-gate		my $AF = $A->FILE;
486*0Sstevel@tonic-gate		next unless $AF eq $0 || exists $self->{'files'}{$AF};
487*0Sstevel@tonic-gate	    }
488*0Sstevel@tonic-gate	    push @{$self->{'protos_todo'}}, [$pack . $key, undef];
489*0Sstevel@tonic-gate	} elsif ($class eq "GV") {
490*0Sstevel@tonic-gate	    if (class(my $cv = $val->CV) ne "SPECIAL") {
491*0Sstevel@tonic-gate		next if $self->{'subs_done'}{$$val}++;
492*0Sstevel@tonic-gate		next if $$val != ${$cv->GV};   # Ignore imposters
493*0Sstevel@tonic-gate		$self->todo($cv, 0);
494*0Sstevel@tonic-gate	    }
495*0Sstevel@tonic-gate	    if (class(my $cv = $val->FORM) ne "SPECIAL") {
496*0Sstevel@tonic-gate		next if $self->{'forms_done'}{$$val}++;
497*0Sstevel@tonic-gate		next if $$val != ${$cv->GV};   # Ignore imposters
498*0Sstevel@tonic-gate		$self->todo($cv, 1);
499*0Sstevel@tonic-gate	    }
500*0Sstevel@tonic-gate	    if (class($val->HV) ne "SPECIAL" && $key =~ /::$/) {
501*0Sstevel@tonic-gate		$self->stash_subs($pack . $key);
502*0Sstevel@tonic-gate	    }
503*0Sstevel@tonic-gate	}
504*0Sstevel@tonic-gate    }
505*0Sstevel@tonic-gate}
506*0Sstevel@tonic-gate
507*0Sstevel@tonic-gatesub print_protos {
508*0Sstevel@tonic-gate    my $self = shift;
509*0Sstevel@tonic-gate    my $ar;
510*0Sstevel@tonic-gate    my @ret;
511*0Sstevel@tonic-gate    foreach $ar (@{$self->{'protos_todo'}}) {
512*0Sstevel@tonic-gate	my $proto = (defined $ar->[1] ? " (". $ar->[1] . ")" : "");
513*0Sstevel@tonic-gate	push @ret, "sub " . $ar->[0] .  "$proto;\n";
514*0Sstevel@tonic-gate    }
515*0Sstevel@tonic-gate    delete $self->{'protos_todo'};
516*0Sstevel@tonic-gate    return @ret;
517*0Sstevel@tonic-gate}
518*0Sstevel@tonic-gate
519*0Sstevel@tonic-gatesub style_opts {
520*0Sstevel@tonic-gate    my $self = shift;
521*0Sstevel@tonic-gate    my $opts = shift;
522*0Sstevel@tonic-gate    my $opt;
523*0Sstevel@tonic-gate    while (length($opt = substr($opts, 0, 1))) {
524*0Sstevel@tonic-gate	if ($opt eq "C") {
525*0Sstevel@tonic-gate	    $self->{'cuddle'} = " ";
526*0Sstevel@tonic-gate	    $opts = substr($opts, 1);
527*0Sstevel@tonic-gate	} elsif ($opt eq "i") {
528*0Sstevel@tonic-gate	    $opts =~ s/^i(\d+)//;
529*0Sstevel@tonic-gate	    $self->{'indent_size'} = $1;
530*0Sstevel@tonic-gate	} elsif ($opt eq "T") {
531*0Sstevel@tonic-gate	    $self->{'use_tabs'} = 1;
532*0Sstevel@tonic-gate	    $opts = substr($opts, 1);
533*0Sstevel@tonic-gate	} elsif ($opt eq "v") {
534*0Sstevel@tonic-gate	    $opts =~ s/^v([^.]*)(.|$)//;
535*0Sstevel@tonic-gate	    $self->{'ex_const'} = $1;
536*0Sstevel@tonic-gate	}
537*0Sstevel@tonic-gate    }
538*0Sstevel@tonic-gate}
539*0Sstevel@tonic-gate
540*0Sstevel@tonic-gatesub new {
541*0Sstevel@tonic-gate    my $class = shift;
542*0Sstevel@tonic-gate    my $self = bless {}, $class;
543*0Sstevel@tonic-gate    $self->{'cuddle'} = "\n";
544*0Sstevel@tonic-gate    $self->{'curcop'} = undef;
545*0Sstevel@tonic-gate    $self->{'curstash'} = "main";
546*0Sstevel@tonic-gate    $self->{'ex_const'} = "'???'";
547*0Sstevel@tonic-gate    $self->{'expand'} = 0;
548*0Sstevel@tonic-gate    $self->{'files'} = {};
549*0Sstevel@tonic-gate    $self->{'indent_size'} = 4;
550*0Sstevel@tonic-gate    $self->{'linenums'} = 0;
551*0Sstevel@tonic-gate    $self->{'parens'} = 0;
552*0Sstevel@tonic-gate    $self->{'subs_todo'} = [];
553*0Sstevel@tonic-gate    $self->{'unquote'} = 0;
554*0Sstevel@tonic-gate    $self->{'use_dumper'} = 0;
555*0Sstevel@tonic-gate    $self->{'use_tabs'} = 0;
556*0Sstevel@tonic-gate
557*0Sstevel@tonic-gate    $self->{'ambient_arybase'} = 0;
558*0Sstevel@tonic-gate    $self->{'ambient_warnings'} = undef; # Assume no lexical warnings
559*0Sstevel@tonic-gate    $self->{'ambient_hints'} = 0;
560*0Sstevel@tonic-gate    $self->init();
561*0Sstevel@tonic-gate
562*0Sstevel@tonic-gate    while (my $arg = shift @_) {
563*0Sstevel@tonic-gate	if ($arg eq "-d") {
564*0Sstevel@tonic-gate	    $self->{'use_dumper'} = 1;
565*0Sstevel@tonic-gate	    require Data::Dumper;
566*0Sstevel@tonic-gate	} elsif ($arg =~ /^-f(.*)/) {
567*0Sstevel@tonic-gate	    $self->{'files'}{$1} = 1;
568*0Sstevel@tonic-gate	} elsif ($arg eq "-l") {
569*0Sstevel@tonic-gate	    $self->{'linenums'} = 1;
570*0Sstevel@tonic-gate	} elsif ($arg eq "-p") {
571*0Sstevel@tonic-gate	    $self->{'parens'} = 1;
572*0Sstevel@tonic-gate	} elsif ($arg eq "-P") {
573*0Sstevel@tonic-gate	    $self->{'noproto'} = 1;
574*0Sstevel@tonic-gate	} elsif ($arg eq "-q") {
575*0Sstevel@tonic-gate	    $self->{'unquote'} = 1;
576*0Sstevel@tonic-gate	} elsif (substr($arg, 0, 2) eq "-s") {
577*0Sstevel@tonic-gate	    $self->style_opts(substr $arg, 2);
578*0Sstevel@tonic-gate	} elsif ($arg =~ /^-x(\d)$/) {
579*0Sstevel@tonic-gate	    $self->{'expand'} = $1;
580*0Sstevel@tonic-gate	}
581*0Sstevel@tonic-gate    }
582*0Sstevel@tonic-gate    return $self;
583*0Sstevel@tonic-gate}
584*0Sstevel@tonic-gate
585*0Sstevel@tonic-gate{
586*0Sstevel@tonic-gate    # Mask out the bits that L<warnings::register> uses
587*0Sstevel@tonic-gate    my $WARN_MASK;
588*0Sstevel@tonic-gate    BEGIN {
589*0Sstevel@tonic-gate	$WARN_MASK = $warnings::Bits{all} | $warnings::DeadBits{all};
590*0Sstevel@tonic-gate    }
591*0Sstevel@tonic-gate    sub WARN_MASK () {
592*0Sstevel@tonic-gate	return $WARN_MASK;
593*0Sstevel@tonic-gate    }
594*0Sstevel@tonic-gate}
595*0Sstevel@tonic-gate
596*0Sstevel@tonic-gate# Initialise the contextual information, either from
597*0Sstevel@tonic-gate# defaults provided with the ambient_pragmas method,
598*0Sstevel@tonic-gate# or from perl's own defaults otherwise.
599*0Sstevel@tonic-gatesub init {
600*0Sstevel@tonic-gate    my $self = shift;
601*0Sstevel@tonic-gate
602*0Sstevel@tonic-gate    $self->{'arybase'}  = $self->{'ambient_arybase'};
603*0Sstevel@tonic-gate    $self->{'warnings'} = defined ($self->{'ambient_warnings'})
604*0Sstevel@tonic-gate				? $self->{'ambient_warnings'} & WARN_MASK
605*0Sstevel@tonic-gate				: undef;
606*0Sstevel@tonic-gate    $self->{'hints'}    = $self->{'ambient_hints'} & 0xFF;
607*0Sstevel@tonic-gate
608*0Sstevel@tonic-gate    # also a convenient place to clear out subs_declared
609*0Sstevel@tonic-gate    delete $self->{'subs_declared'};
610*0Sstevel@tonic-gate}
611*0Sstevel@tonic-gate
612*0Sstevel@tonic-gatesub compile {
613*0Sstevel@tonic-gate    my(@args) = @_;
614*0Sstevel@tonic-gate    return sub {
615*0Sstevel@tonic-gate	my $self = B::Deparse->new(@args);
616*0Sstevel@tonic-gate	# First deparse command-line args
617*0Sstevel@tonic-gate	if (defined $^I) { # deparse -i
618*0Sstevel@tonic-gate	    print q(BEGIN { $^I = ).perlstring($^I).qq(; }\n);
619*0Sstevel@tonic-gate	}
620*0Sstevel@tonic-gate	if ($^W) { # deparse -w
621*0Sstevel@tonic-gate	    print qq(BEGIN { \$^W = $^W; }\n);
622*0Sstevel@tonic-gate	}
623*0Sstevel@tonic-gate	if ($/ ne "\n" or defined $O::savebackslash) { # deparse -l and -0
624*0Sstevel@tonic-gate	    my $fs = perlstring($/) || 'undef';
625*0Sstevel@tonic-gate	    my $bs = perlstring($O::savebackslash) || 'undef';
626*0Sstevel@tonic-gate	    print qq(BEGIN { \$/ = $fs; \$\\ = $bs; }\n);
627*0Sstevel@tonic-gate	}
628*0Sstevel@tonic-gate	my @BEGINs  = B::begin_av->isa("B::AV") ? B::begin_av->ARRAY : ();
629*0Sstevel@tonic-gate	my @CHECKs  = B::check_av->isa("B::AV") ? B::check_av->ARRAY : ();
630*0Sstevel@tonic-gate	my @INITs   = B::init_av->isa("B::AV") ? B::init_av->ARRAY : ();
631*0Sstevel@tonic-gate	my @ENDs    = B::end_av->isa("B::AV") ? B::end_av->ARRAY : ();
632*0Sstevel@tonic-gate	for my $block (@BEGINs, @CHECKs, @INITs, @ENDs) {
633*0Sstevel@tonic-gate	    $self->todo($block, 0);
634*0Sstevel@tonic-gate	}
635*0Sstevel@tonic-gate	$self->stash_subs();
636*0Sstevel@tonic-gate	local($SIG{"__DIE__"}) =
637*0Sstevel@tonic-gate	  sub {
638*0Sstevel@tonic-gate	      if ($self->{'curcop'}) {
639*0Sstevel@tonic-gate		  my $cop = $self->{'curcop'};
640*0Sstevel@tonic-gate		  my($line, $file) = ($cop->line, $cop->file);
641*0Sstevel@tonic-gate		  print STDERR "While deparsing $file near line $line,\n";
642*0Sstevel@tonic-gate	      }
643*0Sstevel@tonic-gate	    };
644*0Sstevel@tonic-gate	$self->{'curcv'} = main_cv;
645*0Sstevel@tonic-gate	$self->{'curcvlex'} = undef;
646*0Sstevel@tonic-gate	print $self->print_protos;
647*0Sstevel@tonic-gate	@{$self->{'subs_todo'}} =
648*0Sstevel@tonic-gate	  sort {$a->[0] <=> $b->[0]} @{$self->{'subs_todo'}};
649*0Sstevel@tonic-gate	print $self->indent($self->deparse_root(main_root)), "\n"
650*0Sstevel@tonic-gate	  unless null main_root;
651*0Sstevel@tonic-gate	my @text;
652*0Sstevel@tonic-gate	while (scalar(@{$self->{'subs_todo'}})) {
653*0Sstevel@tonic-gate	    push @text, $self->next_todo;
654*0Sstevel@tonic-gate	}
655*0Sstevel@tonic-gate	print $self->indent(join("", @text)), "\n" if @text;
656*0Sstevel@tonic-gate
657*0Sstevel@tonic-gate	# Print __DATA__ section, if necessary
658*0Sstevel@tonic-gate	no strict 'refs';
659*0Sstevel@tonic-gate	my $laststash = defined $self->{'curcop'}
660*0Sstevel@tonic-gate	    ? $self->{'curcop'}->stash->NAME : $self->{'curstash'};
661*0Sstevel@tonic-gate	if (defined *{$laststash."::DATA"}{IO}) {
662*0Sstevel@tonic-gate	    print "package $laststash;\n"
663*0Sstevel@tonic-gate		unless $laststash eq $self->{'curstash'};
664*0Sstevel@tonic-gate	    print "__DATA__\n";
665*0Sstevel@tonic-gate	    print readline(*{$laststash."::DATA"});
666*0Sstevel@tonic-gate	}
667*0Sstevel@tonic-gate    }
668*0Sstevel@tonic-gate}
669*0Sstevel@tonic-gate
670*0Sstevel@tonic-gatesub coderef2text {
671*0Sstevel@tonic-gate    my $self = shift;
672*0Sstevel@tonic-gate    my $sub = shift;
673*0Sstevel@tonic-gate    croak "Usage: ->coderef2text(CODEREF)" unless UNIVERSAL::isa($sub, "CODE");
674*0Sstevel@tonic-gate
675*0Sstevel@tonic-gate    $self->init();
676*0Sstevel@tonic-gate    return $self->indent($self->deparse_sub(svref_2object($sub)));
677*0Sstevel@tonic-gate}
678*0Sstevel@tonic-gate
679*0Sstevel@tonic-gatesub ambient_pragmas {
680*0Sstevel@tonic-gate    my $self = shift;
681*0Sstevel@tonic-gate    my ($arybase, $hint_bits, $warning_bits) = (0, 0);
682*0Sstevel@tonic-gate
683*0Sstevel@tonic-gate    while (@_ > 1) {
684*0Sstevel@tonic-gate	my $name = shift();
685*0Sstevel@tonic-gate	my $val  = shift();
686*0Sstevel@tonic-gate
687*0Sstevel@tonic-gate	if ($name eq 'strict') {
688*0Sstevel@tonic-gate	    require strict;
689*0Sstevel@tonic-gate
690*0Sstevel@tonic-gate	    if ($val eq 'none') {
691*0Sstevel@tonic-gate		$hint_bits &= ~strict::bits(qw/refs subs vars/);
692*0Sstevel@tonic-gate		next();
693*0Sstevel@tonic-gate	    }
694*0Sstevel@tonic-gate
695*0Sstevel@tonic-gate	    my @names;
696*0Sstevel@tonic-gate	    if ($val eq "all") {
697*0Sstevel@tonic-gate		@names = qw/refs subs vars/;
698*0Sstevel@tonic-gate	    }
699*0Sstevel@tonic-gate	    elsif (ref $val) {
700*0Sstevel@tonic-gate		@names = @$val;
701*0Sstevel@tonic-gate	    }
702*0Sstevel@tonic-gate	    else {
703*0Sstevel@tonic-gate		@names = split' ', $val;
704*0Sstevel@tonic-gate	    }
705*0Sstevel@tonic-gate	    $hint_bits |= strict::bits(@names);
706*0Sstevel@tonic-gate	}
707*0Sstevel@tonic-gate
708*0Sstevel@tonic-gate	elsif ($name eq '$[') {
709*0Sstevel@tonic-gate	    $arybase = $val;
710*0Sstevel@tonic-gate	}
711*0Sstevel@tonic-gate
712*0Sstevel@tonic-gate	elsif ($name eq 'integer'
713*0Sstevel@tonic-gate	    || $name eq 'bytes'
714*0Sstevel@tonic-gate	    || $name eq 'utf8') {
715*0Sstevel@tonic-gate	    require "$name.pm";
716*0Sstevel@tonic-gate	    if ($val) {
717*0Sstevel@tonic-gate		$hint_bits |= ${$::{"${name}::"}{"hint_bits"}};
718*0Sstevel@tonic-gate	    }
719*0Sstevel@tonic-gate	    else {
720*0Sstevel@tonic-gate		$hint_bits &= ~${$::{"${name}::"}{"hint_bits"}};
721*0Sstevel@tonic-gate	    }
722*0Sstevel@tonic-gate	}
723*0Sstevel@tonic-gate
724*0Sstevel@tonic-gate	elsif ($name eq 're') {
725*0Sstevel@tonic-gate	    require re;
726*0Sstevel@tonic-gate	    if ($val eq 'none') {
727*0Sstevel@tonic-gate		$hint_bits &= ~re::bits(qw/taint eval/);
728*0Sstevel@tonic-gate		next();
729*0Sstevel@tonic-gate	    }
730*0Sstevel@tonic-gate
731*0Sstevel@tonic-gate	    my @names;
732*0Sstevel@tonic-gate	    if ($val eq 'all') {
733*0Sstevel@tonic-gate		@names = qw/taint eval/;
734*0Sstevel@tonic-gate	    }
735*0Sstevel@tonic-gate	    elsif (ref $val) {
736*0Sstevel@tonic-gate		@names = @$val;
737*0Sstevel@tonic-gate	    }
738*0Sstevel@tonic-gate	    else {
739*0Sstevel@tonic-gate		@names = split' ',$val;
740*0Sstevel@tonic-gate	    }
741*0Sstevel@tonic-gate	    $hint_bits |= re::bits(@names);
742*0Sstevel@tonic-gate	}
743*0Sstevel@tonic-gate
744*0Sstevel@tonic-gate	elsif ($name eq 'warnings') {
745*0Sstevel@tonic-gate	    if ($val eq 'none') {
746*0Sstevel@tonic-gate		$warning_bits = $warnings::NONE;
747*0Sstevel@tonic-gate		next();
748*0Sstevel@tonic-gate	    }
749*0Sstevel@tonic-gate
750*0Sstevel@tonic-gate	    my @names;
751*0Sstevel@tonic-gate	    if (ref $val) {
752*0Sstevel@tonic-gate		@names = @$val;
753*0Sstevel@tonic-gate	    }
754*0Sstevel@tonic-gate	    else {
755*0Sstevel@tonic-gate		@names = split/\s+/, $val;
756*0Sstevel@tonic-gate	    }
757*0Sstevel@tonic-gate
758*0Sstevel@tonic-gate	    $warning_bits = $warnings::NONE if !defined ($warning_bits);
759*0Sstevel@tonic-gate	    $warning_bits |= warnings::bits(@names);
760*0Sstevel@tonic-gate	}
761*0Sstevel@tonic-gate
762*0Sstevel@tonic-gate	elsif ($name eq 'warning_bits') {
763*0Sstevel@tonic-gate	    $warning_bits = $val;
764*0Sstevel@tonic-gate	}
765*0Sstevel@tonic-gate
766*0Sstevel@tonic-gate	elsif ($name eq 'hint_bits') {
767*0Sstevel@tonic-gate	    $hint_bits = $val;
768*0Sstevel@tonic-gate	}
769*0Sstevel@tonic-gate
770*0Sstevel@tonic-gate	else {
771*0Sstevel@tonic-gate	    croak "Unknown pragma type: $name";
772*0Sstevel@tonic-gate	}
773*0Sstevel@tonic-gate    }
774*0Sstevel@tonic-gate    if (@_) {
775*0Sstevel@tonic-gate	croak "The ambient_pragmas method expects an even number of args";
776*0Sstevel@tonic-gate    }
777*0Sstevel@tonic-gate
778*0Sstevel@tonic-gate    $self->{'ambient_arybase'} = $arybase;
779*0Sstevel@tonic-gate    $self->{'ambient_warnings'} = $warning_bits;
780*0Sstevel@tonic-gate    $self->{'ambient_hints'} = $hint_bits;
781*0Sstevel@tonic-gate}
782*0Sstevel@tonic-gate
783*0Sstevel@tonic-gate# This method is the inner loop, so try to keep it simple
784*0Sstevel@tonic-gatesub deparse {
785*0Sstevel@tonic-gate    my $self = shift;
786*0Sstevel@tonic-gate    my($op, $cx) = @_;
787*0Sstevel@tonic-gate
788*0Sstevel@tonic-gate    Carp::confess("Null op in deparse") if !defined($op)
789*0Sstevel@tonic-gate					|| class($op) eq "NULL";
790*0Sstevel@tonic-gate    my $meth = "pp_" . $op->name;
791*0Sstevel@tonic-gate    return $self->$meth($op, $cx);
792*0Sstevel@tonic-gate}
793*0Sstevel@tonic-gate
794*0Sstevel@tonic-gatesub indent {
795*0Sstevel@tonic-gate    my $self = shift;
796*0Sstevel@tonic-gate    my $txt = shift;
797*0Sstevel@tonic-gate    my @lines = split(/\n/, $txt);
798*0Sstevel@tonic-gate    my $leader = "";
799*0Sstevel@tonic-gate    my $level = 0;
800*0Sstevel@tonic-gate    my $line;
801*0Sstevel@tonic-gate    for $line (@lines) {
802*0Sstevel@tonic-gate	my $cmd = substr($line, 0, 1);
803*0Sstevel@tonic-gate	if ($cmd eq "\t" or $cmd eq "\b") {
804*0Sstevel@tonic-gate	    $level += ($cmd eq "\t" ? 1 : -1) * $self->{'indent_size'};
805*0Sstevel@tonic-gate	    if ($self->{'use_tabs'}) {
806*0Sstevel@tonic-gate		$leader = "\t" x ($level / 8) . " " x ($level % 8);
807*0Sstevel@tonic-gate	    } else {
808*0Sstevel@tonic-gate		$leader = " " x $level;
809*0Sstevel@tonic-gate	    }
810*0Sstevel@tonic-gate	    $line = substr($line, 1);
811*0Sstevel@tonic-gate	}
812*0Sstevel@tonic-gate	if (substr($line, 0, 1) eq "\f") {
813*0Sstevel@tonic-gate	    $line = substr($line, 1); # no indent
814*0Sstevel@tonic-gate	} else {
815*0Sstevel@tonic-gate	    $line = $leader . $line;
816*0Sstevel@tonic-gate	}
817*0Sstevel@tonic-gate	$line =~ s/\cK;?//g;
818*0Sstevel@tonic-gate    }
819*0Sstevel@tonic-gate    return join("\n", @lines);
820*0Sstevel@tonic-gate}
821*0Sstevel@tonic-gate
822*0Sstevel@tonic-gatesub deparse_sub {
823*0Sstevel@tonic-gate    my $self = shift;
824*0Sstevel@tonic-gate    my $cv = shift;
825*0Sstevel@tonic-gate    my $proto = "";
826*0Sstevel@tonic-gateCarp::confess("NULL in deparse_sub") if !defined($cv) || $cv->isa("B::NULL");
827*0Sstevel@tonic-gateCarp::confess("SPECIAL in deparse_sub") if $cv->isa("B::SPECIAL");
828*0Sstevel@tonic-gate    local $self->{'curcop'} = $self->{'curcop'};
829*0Sstevel@tonic-gate    if ($cv->FLAGS & SVf_POK) {
830*0Sstevel@tonic-gate	$proto = "(". $cv->PV . ") ";
831*0Sstevel@tonic-gate    }
832*0Sstevel@tonic-gate    if ($cv->CvFLAGS & (CVf_METHOD|CVf_LOCKED|CVf_LVALUE)) {
833*0Sstevel@tonic-gate        $proto .= ": ";
834*0Sstevel@tonic-gate        $proto .= "lvalue " if $cv->CvFLAGS & CVf_LVALUE;
835*0Sstevel@tonic-gate        $proto .= "locked " if $cv->CvFLAGS & CVf_LOCKED;
836*0Sstevel@tonic-gate        $proto .= "method " if $cv->CvFLAGS & CVf_METHOD;
837*0Sstevel@tonic-gate    }
838*0Sstevel@tonic-gate
839*0Sstevel@tonic-gate    local($self->{'curcv'}) = $cv;
840*0Sstevel@tonic-gate    local($self->{'curcvlex'});
841*0Sstevel@tonic-gate    local(@$self{qw'curstash warnings hints'})
842*0Sstevel@tonic-gate		= @$self{qw'curstash warnings hints'};
843*0Sstevel@tonic-gate    my $body;
844*0Sstevel@tonic-gate    if (not null $cv->ROOT) {
845*0Sstevel@tonic-gate	my $lineseq = $cv->ROOT->first;
846*0Sstevel@tonic-gate	if ($lineseq->name eq "lineseq") {
847*0Sstevel@tonic-gate	    my @ops;
848*0Sstevel@tonic-gate	    for(my$o=$lineseq->first; $$o; $o=$o->sibling) {
849*0Sstevel@tonic-gate		push @ops, $o;
850*0Sstevel@tonic-gate	    }
851*0Sstevel@tonic-gate	    $body = $self->lineseq(undef, @ops).";";
852*0Sstevel@tonic-gate	    my $scope_en = $self->find_scope_en($lineseq);
853*0Sstevel@tonic-gate	    if (defined $scope_en) {
854*0Sstevel@tonic-gate		my $subs = join"", $self->seq_subs($scope_en);
855*0Sstevel@tonic-gate		$body .= ";\n$subs" if length($subs);
856*0Sstevel@tonic-gate	    }
857*0Sstevel@tonic-gate	}
858*0Sstevel@tonic-gate	else {
859*0Sstevel@tonic-gate	    $body = $self->deparse($cv->ROOT->first, 0);
860*0Sstevel@tonic-gate	}
861*0Sstevel@tonic-gate    }
862*0Sstevel@tonic-gate    else {
863*0Sstevel@tonic-gate	my $sv = $cv->const_sv;
864*0Sstevel@tonic-gate	if ($$sv) {
865*0Sstevel@tonic-gate	    # uh-oh. inlinable sub... format it differently
866*0Sstevel@tonic-gate	    return $proto . "{ " . $self->const($sv, 0) . " }\n";
867*0Sstevel@tonic-gate	} else { # XSUB? (or just a declaration)
868*0Sstevel@tonic-gate	    return "$proto;\n";
869*0Sstevel@tonic-gate	}
870*0Sstevel@tonic-gate    }
871*0Sstevel@tonic-gate    return $proto ."{\n\t$body\n\b}" ."\n";
872*0Sstevel@tonic-gate}
873*0Sstevel@tonic-gate
874*0Sstevel@tonic-gatesub deparse_format {
875*0Sstevel@tonic-gate    my $self = shift;
876*0Sstevel@tonic-gate    my $form = shift;
877*0Sstevel@tonic-gate    my @text;
878*0Sstevel@tonic-gate    local($self->{'curcv'}) = $form;
879*0Sstevel@tonic-gate    local($self->{'curcvlex'});
880*0Sstevel@tonic-gate    local($self->{'in_format'}) = 1;
881*0Sstevel@tonic-gate    local(@$self{qw'curstash warnings hints'})
882*0Sstevel@tonic-gate		= @$self{qw'curstash warnings hints'};
883*0Sstevel@tonic-gate    my $op = $form->ROOT;
884*0Sstevel@tonic-gate    my $kid;
885*0Sstevel@tonic-gate    return "\f." if $op->first->name eq 'stub'
886*0Sstevel@tonic-gate                || $op->first->name eq 'nextstate';
887*0Sstevel@tonic-gate    $op = $op->first->first; # skip leavewrite, lineseq
888*0Sstevel@tonic-gate    while (not null $op) {
889*0Sstevel@tonic-gate	$op = $op->sibling; # skip nextstate
890*0Sstevel@tonic-gate	my @exprs;
891*0Sstevel@tonic-gate	$kid = $op->first->sibling; # skip pushmark
892*0Sstevel@tonic-gate	push @text, "\f".$self->const_sv($kid)->PV;
893*0Sstevel@tonic-gate	$kid = $kid->sibling;
894*0Sstevel@tonic-gate	for (; not null $kid; $kid = $kid->sibling) {
895*0Sstevel@tonic-gate	    push @exprs, $self->deparse($kid, 0);
896*0Sstevel@tonic-gate	}
897*0Sstevel@tonic-gate	push @text, "\f".join(", ", @exprs)."\n" if @exprs;
898*0Sstevel@tonic-gate	$op = $op->sibling;
899*0Sstevel@tonic-gate    }
900*0Sstevel@tonic-gate    return join("", @text) . "\f.";
901*0Sstevel@tonic-gate}
902*0Sstevel@tonic-gate
903*0Sstevel@tonic-gatesub is_scope {
904*0Sstevel@tonic-gate    my $op = shift;
905*0Sstevel@tonic-gate    return $op->name eq "leave" || $op->name eq "scope"
906*0Sstevel@tonic-gate      || $op->name eq "lineseq"
907*0Sstevel@tonic-gate	|| ($op->name eq "null" && class($op) eq "UNOP"
908*0Sstevel@tonic-gate	    && (is_scope($op->first) || $op->first->name eq "enter"));
909*0Sstevel@tonic-gate}
910*0Sstevel@tonic-gate
911*0Sstevel@tonic-gatesub is_state {
912*0Sstevel@tonic-gate    my $name = $_[0]->name;
913*0Sstevel@tonic-gate    return $name eq "nextstate" || $name eq "dbstate" || $name eq "setstate";
914*0Sstevel@tonic-gate}
915*0Sstevel@tonic-gate
916*0Sstevel@tonic-gatesub is_miniwhile { # check for one-line loop (`foo() while $y--')
917*0Sstevel@tonic-gate    my $op = shift;
918*0Sstevel@tonic-gate    return (!null($op) and null($op->sibling)
919*0Sstevel@tonic-gate	    and $op->name eq "null" and class($op) eq "UNOP"
920*0Sstevel@tonic-gate	    and (($op->first->name =~ /^(and|or)$/
921*0Sstevel@tonic-gate		  and $op->first->first->sibling->name eq "lineseq")
922*0Sstevel@tonic-gate		 or ($op->first->name eq "lineseq"
923*0Sstevel@tonic-gate		     and not null $op->first->first->sibling
924*0Sstevel@tonic-gate		     and $op->first->first->sibling->name eq "unstack")
925*0Sstevel@tonic-gate		 ));
926*0Sstevel@tonic-gate}
927*0Sstevel@tonic-gate
928*0Sstevel@tonic-gate# Check if the op and its sibling are the initialization and the rest of a
929*0Sstevel@tonic-gate# for (..;..;..) { ... } loop
930*0Sstevel@tonic-gatesub is_for_loop {
931*0Sstevel@tonic-gate    my $op = shift;
932*0Sstevel@tonic-gate    # This OP might be almost anything, though it won't be a
933*0Sstevel@tonic-gate    # nextstate. (It's the initialization, so in the canonical case it
934*0Sstevel@tonic-gate    # will be an sassign.) The sibling is a lineseq whose first child
935*0Sstevel@tonic-gate    # is a nextstate and whose second is a leaveloop.
936*0Sstevel@tonic-gate    my $lseq = $op->sibling;
937*0Sstevel@tonic-gate    if (!is_state $op and !null($lseq) and $lseq->name eq "lineseq") {
938*0Sstevel@tonic-gate	if ($lseq->first && !null($lseq->first) && is_state($lseq->first)
939*0Sstevel@tonic-gate	    && (my $sib = $lseq->first->sibling)) {
940*0Sstevel@tonic-gate	    return (!null($sib) && $sib->name eq "leaveloop");
941*0Sstevel@tonic-gate	}
942*0Sstevel@tonic-gate    }
943*0Sstevel@tonic-gate    return 0;
944*0Sstevel@tonic-gate}
945*0Sstevel@tonic-gate
946*0Sstevel@tonic-gatesub is_scalar {
947*0Sstevel@tonic-gate    my $op = shift;
948*0Sstevel@tonic-gate    return ($op->name eq "rv2sv" or
949*0Sstevel@tonic-gate	    $op->name eq "padsv" or
950*0Sstevel@tonic-gate	    $op->name eq "gv" or # only in array/hash constructs
951*0Sstevel@tonic-gate	    $op->flags & OPf_KIDS && !null($op->first)
952*0Sstevel@tonic-gate	      && $op->first->name eq "gvsv");
953*0Sstevel@tonic-gate}
954*0Sstevel@tonic-gate
955*0Sstevel@tonic-gatesub maybe_parens {
956*0Sstevel@tonic-gate    my $self = shift;
957*0Sstevel@tonic-gate    my($text, $cx, $prec) = @_;
958*0Sstevel@tonic-gate    if ($prec < $cx              # unary ops nest just fine
959*0Sstevel@tonic-gate	or $prec == $cx and $cx != 4 and $cx != 16 and $cx != 21
960*0Sstevel@tonic-gate	or $self->{'parens'})
961*0Sstevel@tonic-gate    {
962*0Sstevel@tonic-gate	$text = "($text)";
963*0Sstevel@tonic-gate	# In a unop, let parent reuse our parens; see maybe_parens_unop
964*0Sstevel@tonic-gate	$text = "\cS" . $text if $cx == 16;
965*0Sstevel@tonic-gate	return $text;
966*0Sstevel@tonic-gate    } else {
967*0Sstevel@tonic-gate	return $text;
968*0Sstevel@tonic-gate    }
969*0Sstevel@tonic-gate}
970*0Sstevel@tonic-gate
971*0Sstevel@tonic-gate# same as above, but get around the `if it looks like a function' rule
972*0Sstevel@tonic-gatesub maybe_parens_unop {
973*0Sstevel@tonic-gate    my $self = shift;
974*0Sstevel@tonic-gate    my($name, $kid, $cx) = @_;
975*0Sstevel@tonic-gate    if ($cx > 16 or $self->{'parens'}) {
976*0Sstevel@tonic-gate	$kid =  $self->deparse($kid, 1);
977*0Sstevel@tonic-gate 	if ($name eq "umask" && $kid =~ /^\d+$/) {
978*0Sstevel@tonic-gate	    $kid = sprintf("%#o", $kid);
979*0Sstevel@tonic-gate	}
980*0Sstevel@tonic-gate	return "$name($kid)";
981*0Sstevel@tonic-gate    } else {
982*0Sstevel@tonic-gate	$kid = $self->deparse($kid, 16);
983*0Sstevel@tonic-gate 	if ($name eq "umask" && $kid =~ /^\d+$/) {
984*0Sstevel@tonic-gate	    $kid = sprintf("%#o", $kid);
985*0Sstevel@tonic-gate	}
986*0Sstevel@tonic-gate	if (substr($kid, 0, 1) eq "\cS") {
987*0Sstevel@tonic-gate	    # use kid's parens
988*0Sstevel@tonic-gate	    return $name . substr($kid, 1);
989*0Sstevel@tonic-gate	} elsif (substr($kid, 0, 1) eq "(") {
990*0Sstevel@tonic-gate	    # avoid looks-like-a-function trap with extra parens
991*0Sstevel@tonic-gate	    # (`+' can lead to ambiguities)
992*0Sstevel@tonic-gate	    return "$name(" . $kid  . ")";
993*0Sstevel@tonic-gate	} else {
994*0Sstevel@tonic-gate	    return "$name $kid";
995*0Sstevel@tonic-gate	}
996*0Sstevel@tonic-gate    }
997*0Sstevel@tonic-gate}
998*0Sstevel@tonic-gate
999*0Sstevel@tonic-gatesub maybe_parens_func {
1000*0Sstevel@tonic-gate    my $self = shift;
1001*0Sstevel@tonic-gate    my($func, $text, $cx, $prec) = @_;
1002*0Sstevel@tonic-gate    if ($prec <= $cx or substr($text, 0, 1) eq "(" or $self->{'parens'}) {
1003*0Sstevel@tonic-gate	return "$func($text)";
1004*0Sstevel@tonic-gate    } else {
1005*0Sstevel@tonic-gate	return "$func $text";
1006*0Sstevel@tonic-gate    }
1007*0Sstevel@tonic-gate}
1008*0Sstevel@tonic-gate
1009*0Sstevel@tonic-gatesub maybe_local {
1010*0Sstevel@tonic-gate    my $self = shift;
1011*0Sstevel@tonic-gate    my($op, $cx, $text) = @_;
1012*0Sstevel@tonic-gate    my $our_intro = ($op->name =~ /^(gv|rv2)[ash]v$/) ? OPpOUR_INTRO : 0;
1013*0Sstevel@tonic-gate    if ($op->private & (OPpLVAL_INTRO|$our_intro)
1014*0Sstevel@tonic-gate	and not $self->{'avoid_local'}{$$op}) {
1015*0Sstevel@tonic-gate	my $our_local = ($op->private & OPpLVAL_INTRO) ? "local" : "our";
1016*0Sstevel@tonic-gate	if( $our_local eq 'our' ) {
1017*0Sstevel@tonic-gate	    die "Unexpected our($text)\n" unless $text =~ /^\W(\w+::)*\w+\z/;
1018*0Sstevel@tonic-gate	    $text =~ s/(\w+::)+//;
1019*0Sstevel@tonic-gate	}
1020*0Sstevel@tonic-gate        if (want_scalar($op)) {
1021*0Sstevel@tonic-gate	    return "$our_local $text";
1022*0Sstevel@tonic-gate	} else {
1023*0Sstevel@tonic-gate	    return $self->maybe_parens_func("$our_local", $text, $cx, 16);
1024*0Sstevel@tonic-gate	}
1025*0Sstevel@tonic-gate    } else {
1026*0Sstevel@tonic-gate	return $text;
1027*0Sstevel@tonic-gate    }
1028*0Sstevel@tonic-gate}
1029*0Sstevel@tonic-gate
1030*0Sstevel@tonic-gatesub maybe_targmy {
1031*0Sstevel@tonic-gate    my $self = shift;
1032*0Sstevel@tonic-gate    my($op, $cx, $func, @args) = @_;
1033*0Sstevel@tonic-gate    if ($op->private & OPpTARGET_MY) {
1034*0Sstevel@tonic-gate	my $var = $self->padname($op->targ);
1035*0Sstevel@tonic-gate	my $val = $func->($self, $op, 7, @args);
1036*0Sstevel@tonic-gate	return $self->maybe_parens("$var = $val", $cx, 7);
1037*0Sstevel@tonic-gate    } else {
1038*0Sstevel@tonic-gate	return $func->($self, $op, $cx, @args);
1039*0Sstevel@tonic-gate    }
1040*0Sstevel@tonic-gate}
1041*0Sstevel@tonic-gate
1042*0Sstevel@tonic-gatesub padname_sv {
1043*0Sstevel@tonic-gate    my $self = shift;
1044*0Sstevel@tonic-gate    my $targ = shift;
1045*0Sstevel@tonic-gate    return $self->{'curcv'}->PADLIST->ARRAYelt(0)->ARRAYelt($targ);
1046*0Sstevel@tonic-gate}
1047*0Sstevel@tonic-gate
1048*0Sstevel@tonic-gatesub maybe_my {
1049*0Sstevel@tonic-gate    my $self = shift;
1050*0Sstevel@tonic-gate    my($op, $cx, $text) = @_;
1051*0Sstevel@tonic-gate    if ($op->private & OPpLVAL_INTRO and not $self->{'avoid_local'}{$$op}) {
1052*0Sstevel@tonic-gate	if (want_scalar($op)) {
1053*0Sstevel@tonic-gate	    return "my $text";
1054*0Sstevel@tonic-gate	} else {
1055*0Sstevel@tonic-gate	    return $self->maybe_parens_func("my", $text, $cx, 16);
1056*0Sstevel@tonic-gate	}
1057*0Sstevel@tonic-gate    } else {
1058*0Sstevel@tonic-gate	return $text;
1059*0Sstevel@tonic-gate    }
1060*0Sstevel@tonic-gate}
1061*0Sstevel@tonic-gate
1062*0Sstevel@tonic-gate# The following OPs don't have functions:
1063*0Sstevel@tonic-gate
1064*0Sstevel@tonic-gate# pp_padany -- does not exist after parsing
1065*0Sstevel@tonic-gate
1066*0Sstevel@tonic-gatesub AUTOLOAD {
1067*0Sstevel@tonic-gate    if ($AUTOLOAD =~ s/^.*::pp_//) {
1068*0Sstevel@tonic-gate	warn "unexpected OP_".uc $AUTOLOAD;
1069*0Sstevel@tonic-gate	return "XXX";
1070*0Sstevel@tonic-gate    } else {
1071*0Sstevel@tonic-gate	die "Undefined subroutine $AUTOLOAD called";
1072*0Sstevel@tonic-gate    }
1073*0Sstevel@tonic-gate}
1074*0Sstevel@tonic-gate
1075*0Sstevel@tonic-gatesub DESTROY {}	#	Do not AUTOLOAD
1076*0Sstevel@tonic-gate
1077*0Sstevel@tonic-gate# $root should be the op which represents the root of whatever
1078*0Sstevel@tonic-gate# we're sequencing here. If it's undefined, then we don't append
1079*0Sstevel@tonic-gate# any subroutine declarations to the deparsed ops, otherwise we
1080*0Sstevel@tonic-gate# append appropriate declarations.
1081*0Sstevel@tonic-gatesub lineseq {
1082*0Sstevel@tonic-gate    my($self, $root, @ops) = @_;
1083*0Sstevel@tonic-gate    my($expr, @exprs);
1084*0Sstevel@tonic-gate
1085*0Sstevel@tonic-gate    my $out_cop = $self->{'curcop'};
1086*0Sstevel@tonic-gate    my $out_seq = defined($out_cop) ? $out_cop->cop_seq : undef;
1087*0Sstevel@tonic-gate    my $limit_seq;
1088*0Sstevel@tonic-gate    if (defined $root) {
1089*0Sstevel@tonic-gate	$limit_seq = $out_seq;
1090*0Sstevel@tonic-gate	my $nseq;
1091*0Sstevel@tonic-gate	$nseq = $self->find_scope_st($root->sibling) if ${$root->sibling};
1092*0Sstevel@tonic-gate	$limit_seq = $nseq if !defined($limit_seq)
1093*0Sstevel@tonic-gate			   or defined($nseq) && $nseq < $limit_seq;
1094*0Sstevel@tonic-gate    }
1095*0Sstevel@tonic-gate    $limit_seq = $self->{'limit_seq'}
1096*0Sstevel@tonic-gate	if defined($self->{'limit_seq'})
1097*0Sstevel@tonic-gate	&& (!defined($limit_seq) || $self->{'limit_seq'} < $limit_seq);
1098*0Sstevel@tonic-gate    local $self->{'limit_seq'} = $limit_seq;
1099*0Sstevel@tonic-gate    for (my $i = 0; $i < @ops; $i++) {
1100*0Sstevel@tonic-gate	$expr = "";
1101*0Sstevel@tonic-gate	if (is_state $ops[$i]) {
1102*0Sstevel@tonic-gate	    $expr = $self->deparse($ops[$i], 0);
1103*0Sstevel@tonic-gate	    $i++;
1104*0Sstevel@tonic-gate	    if ($i > $#ops) {
1105*0Sstevel@tonic-gate		push @exprs, $expr;
1106*0Sstevel@tonic-gate		last;
1107*0Sstevel@tonic-gate	    }
1108*0Sstevel@tonic-gate	}
1109*0Sstevel@tonic-gate	if (!is_state $ops[$i] and (my $ls = $ops[$i+1]) and
1110*0Sstevel@tonic-gate	    !null($ops[$i+1]) and $ops[$i+1]->name eq "lineseq")
1111*0Sstevel@tonic-gate	{
1112*0Sstevel@tonic-gate	    if ($ls->first && !null($ls->first) && is_state($ls->first)
1113*0Sstevel@tonic-gate		&& (my $sib = $ls->first->sibling)) {
1114*0Sstevel@tonic-gate		if (!null($sib) && $sib->name eq "leaveloop") {
1115*0Sstevel@tonic-gate		    push @exprs, $expr . $self->for_loop($ops[$i], 0);
1116*0Sstevel@tonic-gate		    $i++;
1117*0Sstevel@tonic-gate		    next;
1118*0Sstevel@tonic-gate		}
1119*0Sstevel@tonic-gate	    }
1120*0Sstevel@tonic-gate	}
1121*0Sstevel@tonic-gate	$expr .= $self->deparse($ops[$i], (@ops != 1)/2);
1122*0Sstevel@tonic-gate	$expr =~ s/;\n?\z//;
1123*0Sstevel@tonic-gate	push @exprs, $expr;
1124*0Sstevel@tonic-gate    }
1125*0Sstevel@tonic-gate    my $body = join(";\n", grep {length} @exprs);
1126*0Sstevel@tonic-gate    my $subs = "";
1127*0Sstevel@tonic-gate    if (defined $root && defined $limit_seq && !$self->{'in_format'}) {
1128*0Sstevel@tonic-gate	$subs = join "\n", $self->seq_subs($limit_seq);
1129*0Sstevel@tonic-gate    }
1130*0Sstevel@tonic-gate    return join(";\n", grep {length} $body, $subs);
1131*0Sstevel@tonic-gate}
1132*0Sstevel@tonic-gate
1133*0Sstevel@tonic-gatesub scopeop {
1134*0Sstevel@tonic-gate    my($real_block, $self, $op, $cx) = @_;
1135*0Sstevel@tonic-gate    my $kid;
1136*0Sstevel@tonic-gate    my @kids;
1137*0Sstevel@tonic-gate
1138*0Sstevel@tonic-gate    local(@$self{qw'curstash warnings hints'})
1139*0Sstevel@tonic-gate		= @$self{qw'curstash warnings hints'} if $real_block;
1140*0Sstevel@tonic-gate    if ($real_block) {
1141*0Sstevel@tonic-gate	$kid = $op->first->sibling; # skip enter
1142*0Sstevel@tonic-gate	if (is_miniwhile($kid)) {
1143*0Sstevel@tonic-gate	    my $top = $kid->first;
1144*0Sstevel@tonic-gate	    my $name = $top->name;
1145*0Sstevel@tonic-gate	    if ($name eq "and") {
1146*0Sstevel@tonic-gate		$name = "while";
1147*0Sstevel@tonic-gate	    } elsif ($name eq "or") {
1148*0Sstevel@tonic-gate		$name = "until";
1149*0Sstevel@tonic-gate	    } else { # no conditional -> while 1 or until 0
1150*0Sstevel@tonic-gate		return $self->deparse($top->first, 1) . " while 1";
1151*0Sstevel@tonic-gate	    }
1152*0Sstevel@tonic-gate	    my $cond = $top->first;
1153*0Sstevel@tonic-gate	    my $body = $cond->sibling->first; # skip lineseq
1154*0Sstevel@tonic-gate	    $cond = $self->deparse($cond, 1);
1155*0Sstevel@tonic-gate	    $body = $self->deparse($body, 1);
1156*0Sstevel@tonic-gate	    return "$body $name $cond";
1157*0Sstevel@tonic-gate	}
1158*0Sstevel@tonic-gate    } else {
1159*0Sstevel@tonic-gate	$kid = $op->first;
1160*0Sstevel@tonic-gate    }
1161*0Sstevel@tonic-gate    for (; !null($kid); $kid = $kid->sibling) {
1162*0Sstevel@tonic-gate	push @kids, $kid;
1163*0Sstevel@tonic-gate    }
1164*0Sstevel@tonic-gate    if ($cx > 0) { # inside an expression, (a do {} while for lineseq)
1165*0Sstevel@tonic-gate	return "do {\n\t" . $self->lineseq($op, @kids) . "\n\b}";
1166*0Sstevel@tonic-gate    } else {
1167*0Sstevel@tonic-gate	my $lineseq = $self->lineseq($op, @kids);
1168*0Sstevel@tonic-gate	return (length ($lineseq) ? "$lineseq;" : "");
1169*0Sstevel@tonic-gate    }
1170*0Sstevel@tonic-gate}
1171*0Sstevel@tonic-gate
1172*0Sstevel@tonic-gatesub pp_scope { scopeop(0, @_); }
1173*0Sstevel@tonic-gatesub pp_lineseq { scopeop(0, @_); }
1174*0Sstevel@tonic-gatesub pp_leave { scopeop(1, @_); }
1175*0Sstevel@tonic-gate
1176*0Sstevel@tonic-gate# This is a special case of scopeop and lineseq, for the case of the
1177*0Sstevel@tonic-gate# main_root. The difference is that we print the output statements as
1178*0Sstevel@tonic-gate# soon as we get them, for the sake of impatient users.
1179*0Sstevel@tonic-gatesub deparse_root {
1180*0Sstevel@tonic-gate    my $self = shift;
1181*0Sstevel@tonic-gate    my($op) = @_;
1182*0Sstevel@tonic-gate    local(@$self{qw'curstash warnings hints'})
1183*0Sstevel@tonic-gate      = @$self{qw'curstash warnings hints'};
1184*0Sstevel@tonic-gate    my @kids;
1185*0Sstevel@tonic-gate    for (my $kid = $op->first->sibling; !null($kid); $kid = $kid->sibling) {
1186*0Sstevel@tonic-gate	push @kids, $kid;
1187*0Sstevel@tonic-gate    }
1188*0Sstevel@tonic-gate    for (my $i = 0; $i < @kids; $i++) {
1189*0Sstevel@tonic-gate	my $expr = "";
1190*0Sstevel@tonic-gate	if (is_state $kids[$i]) {
1191*0Sstevel@tonic-gate	    $expr = $self->deparse($kids[$i], 0);
1192*0Sstevel@tonic-gate	    $i++;
1193*0Sstevel@tonic-gate	    if ($i > $#kids) {
1194*0Sstevel@tonic-gate		print $self->indent($expr);
1195*0Sstevel@tonic-gate		last;
1196*0Sstevel@tonic-gate	    }
1197*0Sstevel@tonic-gate	}
1198*0Sstevel@tonic-gate	if (is_for_loop($kids[$i])) {
1199*0Sstevel@tonic-gate	    $expr .= $self->for_loop($kids[$i], 0);
1200*0Sstevel@tonic-gate	    $expr .= ";\n" unless $i == $#kids;
1201*0Sstevel@tonic-gate	    print $self->indent($expr);
1202*0Sstevel@tonic-gate	    $i++;
1203*0Sstevel@tonic-gate	    next;
1204*0Sstevel@tonic-gate	}
1205*0Sstevel@tonic-gate	$expr .= $self->deparse($kids[$i], (@kids != 1)/2);
1206*0Sstevel@tonic-gate	$expr =~ s/;\n?\z//;
1207*0Sstevel@tonic-gate	$expr .= ";";
1208*0Sstevel@tonic-gate	print $self->indent($expr);
1209*0Sstevel@tonic-gate	print "\n" unless $i == $#kids;
1210*0Sstevel@tonic-gate    }
1211*0Sstevel@tonic-gate}
1212*0Sstevel@tonic-gate
1213*0Sstevel@tonic-gate# The BEGIN {} is used here because otherwise this code isn't executed
1214*0Sstevel@tonic-gate# when you run B::Deparse on itself.
1215*0Sstevel@tonic-gatemy %globalnames;
1216*0Sstevel@tonic-gateBEGIN { map($globalnames{$_}++, "SIG", "STDIN", "STDOUT", "STDERR", "INC",
1217*0Sstevel@tonic-gate	    "ENV", "ARGV", "ARGVOUT", "_"); }
1218*0Sstevel@tonic-gate
1219*0Sstevel@tonic-gatesub gv_name {
1220*0Sstevel@tonic-gate    my $self = shift;
1221*0Sstevel@tonic-gate    my $gv = shift;
1222*0Sstevel@tonic-gateCarp::confess() unless ref($gv) eq "B::GV";
1223*0Sstevel@tonic-gate    my $stash = $gv->STASH->NAME;
1224*0Sstevel@tonic-gate    my $name = $gv->SAFENAME;
1225*0Sstevel@tonic-gate    if (($stash eq 'main' && $globalnames{$name})
1226*0Sstevel@tonic-gate	or ($stash eq $self->{'curstash'} && !$globalnames{$name})
1227*0Sstevel@tonic-gate	or $name =~ /^[^A-Za-z_]/)
1228*0Sstevel@tonic-gate    {
1229*0Sstevel@tonic-gate	$stash = "";
1230*0Sstevel@tonic-gate    } else {
1231*0Sstevel@tonic-gate	$stash = $stash . "::";
1232*0Sstevel@tonic-gate    }
1233*0Sstevel@tonic-gate    if ($name =~ /^(\^..|{)/) {
1234*0Sstevel@tonic-gate        $name = "{$name}";       # ${^WARNING_BITS}, etc and ${
1235*0Sstevel@tonic-gate    }
1236*0Sstevel@tonic-gate    return $stash . $name;
1237*0Sstevel@tonic-gate}
1238*0Sstevel@tonic-gate
1239*0Sstevel@tonic-gate# Return the name to use for a stash variable.
1240*0Sstevel@tonic-gate# If a lexical with the same name is in scope, it may need to be
1241*0Sstevel@tonic-gate# fully-qualified.
1242*0Sstevel@tonic-gatesub stash_variable {
1243*0Sstevel@tonic-gate    my ($self, $prefix, $name) = @_;
1244*0Sstevel@tonic-gate
1245*0Sstevel@tonic-gate    return "$prefix$name" if $name =~ /::/;
1246*0Sstevel@tonic-gate
1247*0Sstevel@tonic-gate    unless ($prefix eq '$' || $prefix eq '@' || #'
1248*0Sstevel@tonic-gate	    $prefix eq '%' || $prefix eq '$#') {
1249*0Sstevel@tonic-gate	return "$prefix$name";
1250*0Sstevel@tonic-gate    }
1251*0Sstevel@tonic-gate
1252*0Sstevel@tonic-gate    my $v = ($prefix eq '$#' ? '@' : $prefix) . $name;
1253*0Sstevel@tonic-gate    return $prefix .$self->{'curstash'}.'::'. $name if $self->lex_in_scope($v);
1254*0Sstevel@tonic-gate    return "$prefix$name";
1255*0Sstevel@tonic-gate}
1256*0Sstevel@tonic-gate
1257*0Sstevel@tonic-gatesub lex_in_scope {
1258*0Sstevel@tonic-gate    my ($self, $name) = @_;
1259*0Sstevel@tonic-gate    $self->populate_curcvlex() if !defined $self->{'curcvlex'};
1260*0Sstevel@tonic-gate
1261*0Sstevel@tonic-gate    return 0 if !defined($self->{'curcop'});
1262*0Sstevel@tonic-gate    my $seq = $self->{'curcop'}->cop_seq;
1263*0Sstevel@tonic-gate    return 0 if !exists $self->{'curcvlex'}{$name};
1264*0Sstevel@tonic-gate    for my $a (@{$self->{'curcvlex'}{$name}}) {
1265*0Sstevel@tonic-gate	my ($st, $en) = @$a;
1266*0Sstevel@tonic-gate	return 1 if $seq > $st && $seq <= $en;
1267*0Sstevel@tonic-gate    }
1268*0Sstevel@tonic-gate    return 0;
1269*0Sstevel@tonic-gate}
1270*0Sstevel@tonic-gate
1271*0Sstevel@tonic-gatesub populate_curcvlex {
1272*0Sstevel@tonic-gate    my $self = shift;
1273*0Sstevel@tonic-gate    for (my $cv = $self->{'curcv'}; class($cv) eq "CV"; $cv = $cv->OUTSIDE) {
1274*0Sstevel@tonic-gate	my $padlist = $cv->PADLIST;
1275*0Sstevel@tonic-gate	# an undef CV still in lexical chain
1276*0Sstevel@tonic-gate	next if class($padlist) eq "SPECIAL";
1277*0Sstevel@tonic-gate	my @padlist = $padlist->ARRAY;
1278*0Sstevel@tonic-gate	my @ns = $padlist[0]->ARRAY;
1279*0Sstevel@tonic-gate
1280*0Sstevel@tonic-gate	for (my $i=0; $i<@ns; ++$i) {
1281*0Sstevel@tonic-gate	    next if class($ns[$i]) eq "SPECIAL";
1282*0Sstevel@tonic-gate	    next if $ns[$i]->FLAGS & SVpad_OUR;  # Skip "our" vars
1283*0Sstevel@tonic-gate	    if (class($ns[$i]) eq "PV") {
1284*0Sstevel@tonic-gate		# Probably that pesky lexical @_
1285*0Sstevel@tonic-gate		next;
1286*0Sstevel@tonic-gate	    }
1287*0Sstevel@tonic-gate            my $name = $ns[$i]->PVX;
1288*0Sstevel@tonic-gate	    my ($seq_st, $seq_en) =
1289*0Sstevel@tonic-gate		($ns[$i]->FLAGS & SVf_FAKE)
1290*0Sstevel@tonic-gate		    ? (0, 999999)
1291*0Sstevel@tonic-gate		    : ($ns[$i]->NVX, $ns[$i]->IVX);
1292*0Sstevel@tonic-gate
1293*0Sstevel@tonic-gate	    push @{$self->{'curcvlex'}{$name}}, [$seq_st, $seq_en];
1294*0Sstevel@tonic-gate	}
1295*0Sstevel@tonic-gate    }
1296*0Sstevel@tonic-gate}
1297*0Sstevel@tonic-gate
1298*0Sstevel@tonic-gatesub find_scope_st { ((find_scope(@_))[0]); }
1299*0Sstevel@tonic-gatesub find_scope_en { ((find_scope(@_))[1]); }
1300*0Sstevel@tonic-gate
1301*0Sstevel@tonic-gate# Recurses down the tree, looking for pad variable introductions and COPs
1302*0Sstevel@tonic-gatesub find_scope {
1303*0Sstevel@tonic-gate    my ($self, $op, $scope_st, $scope_en) = @_;
1304*0Sstevel@tonic-gate    carp("Undefined op in find_scope") if !defined $op;
1305*0Sstevel@tonic-gate    return ($scope_st, $scope_en) unless $op->flags & OPf_KIDS;
1306*0Sstevel@tonic-gate
1307*0Sstevel@tonic-gate    for (my $o=$op->first; $$o; $o=$o->sibling) {
1308*0Sstevel@tonic-gate	if ($o->name =~ /^pad.v$/ && $o->private & OPpLVAL_INTRO) {
1309*0Sstevel@tonic-gate	    my $s = int($self->padname_sv($o->targ)->NVX);
1310*0Sstevel@tonic-gate	    my $e = $self->padname_sv($o->targ)->IVX;
1311*0Sstevel@tonic-gate	    $scope_st = $s if !defined($scope_st) || $s < $scope_st;
1312*0Sstevel@tonic-gate	    $scope_en = $e if !defined($scope_en) || $e > $scope_en;
1313*0Sstevel@tonic-gate	}
1314*0Sstevel@tonic-gate	elsif (is_state($o)) {
1315*0Sstevel@tonic-gate	    my $c = $o->cop_seq;
1316*0Sstevel@tonic-gate	    $scope_st = $c if !defined($scope_st) || $c < $scope_st;
1317*0Sstevel@tonic-gate	    $scope_en = $c if !defined($scope_en) || $c > $scope_en;
1318*0Sstevel@tonic-gate	}
1319*0Sstevel@tonic-gate	elsif ($o->flags & OPf_KIDS) {
1320*0Sstevel@tonic-gate	    ($scope_st, $scope_en) =
1321*0Sstevel@tonic-gate		$self->find_scope($o, $scope_st, $scope_en)
1322*0Sstevel@tonic-gate	}
1323*0Sstevel@tonic-gate    }
1324*0Sstevel@tonic-gate
1325*0Sstevel@tonic-gate    return ($scope_st, $scope_en);
1326*0Sstevel@tonic-gate}
1327*0Sstevel@tonic-gate
1328*0Sstevel@tonic-gate# Returns a list of subs which should be inserted before the COP
1329*0Sstevel@tonic-gatesub cop_subs {
1330*0Sstevel@tonic-gate    my ($self, $op, $out_seq) = @_;
1331*0Sstevel@tonic-gate    my $seq = $op->cop_seq;
1332*0Sstevel@tonic-gate    # If we have nephews, then our sequence number indicates
1333*0Sstevel@tonic-gate    # the cop_seq of the end of some sort of scope.
1334*0Sstevel@tonic-gate    if (class($op->sibling) ne "NULL" && $op->sibling->flags & OPf_KIDS
1335*0Sstevel@tonic-gate	and my $nseq = $self->find_scope_st($op->sibling) ) {
1336*0Sstevel@tonic-gate	$seq = $nseq;
1337*0Sstevel@tonic-gate    }
1338*0Sstevel@tonic-gate    $seq = $out_seq if defined($out_seq) && $out_seq < $seq;
1339*0Sstevel@tonic-gate    return $self->seq_subs($seq);
1340*0Sstevel@tonic-gate}
1341*0Sstevel@tonic-gate
1342*0Sstevel@tonic-gatesub seq_subs {
1343*0Sstevel@tonic-gate    my ($self, $seq) = @_;
1344*0Sstevel@tonic-gate    my @text;
1345*0Sstevel@tonic-gate#push @text, "# ($seq)\n";
1346*0Sstevel@tonic-gate
1347*0Sstevel@tonic-gate    return "" if !defined $seq;
1348*0Sstevel@tonic-gate    while (scalar(@{$self->{'subs_todo'}})
1349*0Sstevel@tonic-gate	   and $seq > $self->{'subs_todo'}[0][0]) {
1350*0Sstevel@tonic-gate	push @text, $self->next_todo;
1351*0Sstevel@tonic-gate    }
1352*0Sstevel@tonic-gate    return @text;
1353*0Sstevel@tonic-gate}
1354*0Sstevel@tonic-gate
1355*0Sstevel@tonic-gate# Notice how subs and formats are inserted between statements here;
1356*0Sstevel@tonic-gate# also $[ assignments and pragmas.
1357*0Sstevel@tonic-gatesub pp_nextstate {
1358*0Sstevel@tonic-gate    my $self = shift;
1359*0Sstevel@tonic-gate    my($op, $cx) = @_;
1360*0Sstevel@tonic-gate    $self->{'curcop'} = $op;
1361*0Sstevel@tonic-gate    my @text;
1362*0Sstevel@tonic-gate    push @text, $self->cop_subs($op);
1363*0Sstevel@tonic-gate    push @text, $op->label . ": " if $op->label;
1364*0Sstevel@tonic-gate    my $stash = $op->stashpv;
1365*0Sstevel@tonic-gate    if ($stash ne $self->{'curstash'}) {
1366*0Sstevel@tonic-gate	push @text, "package $stash;\n";
1367*0Sstevel@tonic-gate	$self->{'curstash'} = $stash;
1368*0Sstevel@tonic-gate    }
1369*0Sstevel@tonic-gate
1370*0Sstevel@tonic-gate    if ($self->{'arybase'} != $op->arybase) {
1371*0Sstevel@tonic-gate	push @text, '$[ = '. $op->arybase .";\n";
1372*0Sstevel@tonic-gate	$self->{'arybase'} = $op->arybase;
1373*0Sstevel@tonic-gate    }
1374*0Sstevel@tonic-gate
1375*0Sstevel@tonic-gate    my $warnings = $op->warnings;
1376*0Sstevel@tonic-gate    my $warning_bits;
1377*0Sstevel@tonic-gate    if ($warnings->isa("B::SPECIAL") && $$warnings == 4) {
1378*0Sstevel@tonic-gate	$warning_bits = $warnings::Bits{"all"} & WARN_MASK;
1379*0Sstevel@tonic-gate    }
1380*0Sstevel@tonic-gate    elsif ($warnings->isa("B::SPECIAL") && $$warnings == 5) {
1381*0Sstevel@tonic-gate        $warning_bits = $warnings::NONE;
1382*0Sstevel@tonic-gate    }
1383*0Sstevel@tonic-gate    elsif ($warnings->isa("B::SPECIAL")) {
1384*0Sstevel@tonic-gate	$warning_bits = undef;
1385*0Sstevel@tonic-gate    }
1386*0Sstevel@tonic-gate    else {
1387*0Sstevel@tonic-gate	$warning_bits = $warnings->PV & WARN_MASK;
1388*0Sstevel@tonic-gate    }
1389*0Sstevel@tonic-gate
1390*0Sstevel@tonic-gate    if (defined ($warning_bits) and
1391*0Sstevel@tonic-gate       !defined($self->{warnings}) || $self->{'warnings'} ne $warning_bits) {
1392*0Sstevel@tonic-gate	push @text, declare_warnings($self->{'warnings'}, $warning_bits);
1393*0Sstevel@tonic-gate	$self->{'warnings'} = $warning_bits;
1394*0Sstevel@tonic-gate    }
1395*0Sstevel@tonic-gate
1396*0Sstevel@tonic-gate    if ($self->{'hints'} != $op->private) {
1397*0Sstevel@tonic-gate	push @text, declare_hints($self->{'hints'}, $op->private);
1398*0Sstevel@tonic-gate	$self->{'hints'} = $op->private;
1399*0Sstevel@tonic-gate    }
1400*0Sstevel@tonic-gate
1401*0Sstevel@tonic-gate    # This should go after of any branches that add statements, to
1402*0Sstevel@tonic-gate    # increase the chances that it refers to the same line it did in
1403*0Sstevel@tonic-gate    # the original program.
1404*0Sstevel@tonic-gate    if ($self->{'linenums'}) {
1405*0Sstevel@tonic-gate	push @text, "\f#line " . $op->line .
1406*0Sstevel@tonic-gate	  ' "' . $op->file, qq'"\n';
1407*0Sstevel@tonic-gate    }
1408*0Sstevel@tonic-gate
1409*0Sstevel@tonic-gate    return join("", @text);
1410*0Sstevel@tonic-gate}
1411*0Sstevel@tonic-gate
1412*0Sstevel@tonic-gatesub declare_warnings {
1413*0Sstevel@tonic-gate    my ($from, $to) = @_;
1414*0Sstevel@tonic-gate    if (($to & WARN_MASK) eq warnings::bits("all")) {
1415*0Sstevel@tonic-gate	return "use warnings;\n";
1416*0Sstevel@tonic-gate    }
1417*0Sstevel@tonic-gate    elsif (($to & WARN_MASK) eq "\0"x length($to)) {
1418*0Sstevel@tonic-gate	return "no warnings;\n";
1419*0Sstevel@tonic-gate    }
1420*0Sstevel@tonic-gate    return "BEGIN {\${^WARNING_BITS} = ".perlstring($to)."}\n";
1421*0Sstevel@tonic-gate}
1422*0Sstevel@tonic-gate
1423*0Sstevel@tonic-gatesub declare_hints {
1424*0Sstevel@tonic-gate    my ($from, $to) = @_;
1425*0Sstevel@tonic-gate    my $use = $to   & ~$from;
1426*0Sstevel@tonic-gate    my $no  = $from & ~$to;
1427*0Sstevel@tonic-gate    my $decls = "";
1428*0Sstevel@tonic-gate    for my $pragma (hint_pragmas($use)) {
1429*0Sstevel@tonic-gate	$decls .= "use $pragma;\n";
1430*0Sstevel@tonic-gate    }
1431*0Sstevel@tonic-gate    for my $pragma (hint_pragmas($no)) {
1432*0Sstevel@tonic-gate        $decls .= "no $pragma;\n";
1433*0Sstevel@tonic-gate    }
1434*0Sstevel@tonic-gate    return $decls;
1435*0Sstevel@tonic-gate}
1436*0Sstevel@tonic-gate
1437*0Sstevel@tonic-gatesub hint_pragmas {
1438*0Sstevel@tonic-gate    my ($bits) = @_;
1439*0Sstevel@tonic-gate    my @pragmas;
1440*0Sstevel@tonic-gate    push @pragmas, "integer" if $bits & 0x1;
1441*0Sstevel@tonic-gate    push @pragmas, "strict 'refs'" if $bits & 0x2;
1442*0Sstevel@tonic-gate    push @pragmas, "bytes" if $bits & 0x8;
1443*0Sstevel@tonic-gate    return @pragmas;
1444*0Sstevel@tonic-gate}
1445*0Sstevel@tonic-gate
1446*0Sstevel@tonic-gatesub pp_dbstate { pp_nextstate(@_) }
1447*0Sstevel@tonic-gatesub pp_setstate { pp_nextstate(@_) }
1448*0Sstevel@tonic-gate
1449*0Sstevel@tonic-gatesub pp_unstack { return "" } # see also leaveloop
1450*0Sstevel@tonic-gate
1451*0Sstevel@tonic-gatesub baseop {
1452*0Sstevel@tonic-gate    my $self = shift;
1453*0Sstevel@tonic-gate    my($op, $cx, $name) = @_;
1454*0Sstevel@tonic-gate    return $name;
1455*0Sstevel@tonic-gate}
1456*0Sstevel@tonic-gate
1457*0Sstevel@tonic-gatesub pp_stub {
1458*0Sstevel@tonic-gate    my $self = shift;
1459*0Sstevel@tonic-gate    my($op, $cx, $name) = @_;
1460*0Sstevel@tonic-gate    if ($cx >= 1) {
1461*0Sstevel@tonic-gate	return "()";
1462*0Sstevel@tonic-gate    }
1463*0Sstevel@tonic-gate    else {
1464*0Sstevel@tonic-gate	return "();";
1465*0Sstevel@tonic-gate    }
1466*0Sstevel@tonic-gate}
1467*0Sstevel@tonic-gatesub pp_wantarray { baseop(@_, "wantarray") }
1468*0Sstevel@tonic-gatesub pp_fork { baseop(@_, "fork") }
1469*0Sstevel@tonic-gatesub pp_wait { maybe_targmy(@_, \&baseop, "wait") }
1470*0Sstevel@tonic-gatesub pp_getppid { maybe_targmy(@_, \&baseop, "getppid") }
1471*0Sstevel@tonic-gatesub pp_time { maybe_targmy(@_, \&baseop, "time") }
1472*0Sstevel@tonic-gatesub pp_tms { baseop(@_, "times") }
1473*0Sstevel@tonic-gatesub pp_ghostent { baseop(@_, "gethostent") }
1474*0Sstevel@tonic-gatesub pp_gnetent { baseop(@_, "getnetent") }
1475*0Sstevel@tonic-gatesub pp_gprotoent { baseop(@_, "getprotoent") }
1476*0Sstevel@tonic-gatesub pp_gservent { baseop(@_, "getservent") }
1477*0Sstevel@tonic-gatesub pp_ehostent { baseop(@_, "endhostent") }
1478*0Sstevel@tonic-gatesub pp_enetent { baseop(@_, "endnetent") }
1479*0Sstevel@tonic-gatesub pp_eprotoent { baseop(@_, "endprotoent") }
1480*0Sstevel@tonic-gatesub pp_eservent { baseop(@_, "endservent") }
1481*0Sstevel@tonic-gatesub pp_gpwent { baseop(@_, "getpwent") }
1482*0Sstevel@tonic-gatesub pp_spwent { baseop(@_, "setpwent") }
1483*0Sstevel@tonic-gatesub pp_epwent { baseop(@_, "endpwent") }
1484*0Sstevel@tonic-gatesub pp_ggrent { baseop(@_, "getgrent") }
1485*0Sstevel@tonic-gatesub pp_sgrent { baseop(@_, "setgrent") }
1486*0Sstevel@tonic-gatesub pp_egrent { baseop(@_, "endgrent") }
1487*0Sstevel@tonic-gatesub pp_getlogin { baseop(@_, "getlogin") }
1488*0Sstevel@tonic-gate
1489*0Sstevel@tonic-gatesub POSTFIX () { 1 }
1490*0Sstevel@tonic-gate
1491*0Sstevel@tonic-gate# I couldn't think of a good short name, but this is the category of
1492*0Sstevel@tonic-gate# symbolic unary operators with interesting precedence
1493*0Sstevel@tonic-gate
1494*0Sstevel@tonic-gatesub pfixop {
1495*0Sstevel@tonic-gate    my $self = shift;
1496*0Sstevel@tonic-gate    my($op, $cx, $name, $prec, $flags) = (@_, 0);
1497*0Sstevel@tonic-gate    my $kid = $op->first;
1498*0Sstevel@tonic-gate    $kid = $self->deparse($kid, $prec);
1499*0Sstevel@tonic-gate    return $self->maybe_parens(($flags & POSTFIX) ? "$kid$name" : "$name$kid",
1500*0Sstevel@tonic-gate			       $cx, $prec);
1501*0Sstevel@tonic-gate}
1502*0Sstevel@tonic-gate
1503*0Sstevel@tonic-gatesub pp_preinc { pfixop(@_, "++", 23) }
1504*0Sstevel@tonic-gatesub pp_predec { pfixop(@_, "--", 23) }
1505*0Sstevel@tonic-gatesub pp_postinc { maybe_targmy(@_, \&pfixop, "++", 23, POSTFIX) }
1506*0Sstevel@tonic-gatesub pp_postdec { maybe_targmy(@_, \&pfixop, "--", 23, POSTFIX) }
1507*0Sstevel@tonic-gatesub pp_i_preinc { pfixop(@_, "++", 23) }
1508*0Sstevel@tonic-gatesub pp_i_predec { pfixop(@_, "--", 23) }
1509*0Sstevel@tonic-gatesub pp_i_postinc { maybe_targmy(@_, \&pfixop, "++", 23, POSTFIX) }
1510*0Sstevel@tonic-gatesub pp_i_postdec { maybe_targmy(@_, \&pfixop, "--", 23, POSTFIX) }
1511*0Sstevel@tonic-gatesub pp_complement { maybe_targmy(@_, \&pfixop, "~", 21) }
1512*0Sstevel@tonic-gate
1513*0Sstevel@tonic-gatesub pp_negate { maybe_targmy(@_, \&real_negate) }
1514*0Sstevel@tonic-gatesub real_negate {
1515*0Sstevel@tonic-gate    my $self = shift;
1516*0Sstevel@tonic-gate    my($op, $cx) = @_;
1517*0Sstevel@tonic-gate    if ($op->first->name =~ /^(i_)?negate$/) {
1518*0Sstevel@tonic-gate	# avoid --$x
1519*0Sstevel@tonic-gate	$self->pfixop($op, $cx, "-", 21.5);
1520*0Sstevel@tonic-gate    } else {
1521*0Sstevel@tonic-gate	$self->pfixop($op, $cx, "-", 21);
1522*0Sstevel@tonic-gate    }
1523*0Sstevel@tonic-gate}
1524*0Sstevel@tonic-gatesub pp_i_negate { pp_negate(@_) }
1525*0Sstevel@tonic-gate
1526*0Sstevel@tonic-gatesub pp_not {
1527*0Sstevel@tonic-gate    my $self = shift;
1528*0Sstevel@tonic-gate    my($op, $cx) = @_;
1529*0Sstevel@tonic-gate    if ($cx <= 4) {
1530*0Sstevel@tonic-gate	$self->pfixop($op, $cx, "not ", 4);
1531*0Sstevel@tonic-gate    } else {
1532*0Sstevel@tonic-gate	$self->pfixop($op, $cx, "!", 21);
1533*0Sstevel@tonic-gate    }
1534*0Sstevel@tonic-gate}
1535*0Sstevel@tonic-gate
1536*0Sstevel@tonic-gatesub unop {
1537*0Sstevel@tonic-gate    my $self = shift;
1538*0Sstevel@tonic-gate    my($op, $cx, $name) = @_;
1539*0Sstevel@tonic-gate    my $kid;
1540*0Sstevel@tonic-gate    if ($op->flags & OPf_KIDS) {
1541*0Sstevel@tonic-gate	$kid = $op->first;
1542*0Sstevel@tonic-gate	if (defined prototype("CORE::$name")
1543*0Sstevel@tonic-gate	   && prototype("CORE::$name") =~ /^;?\*/
1544*0Sstevel@tonic-gate	   && $kid->name eq "rv2gv") {
1545*0Sstevel@tonic-gate	    $kid = $kid->first;
1546*0Sstevel@tonic-gate	}
1547*0Sstevel@tonic-gate
1548*0Sstevel@tonic-gate	return $self->maybe_parens_unop($name, $kid, $cx);
1549*0Sstevel@tonic-gate    } else {
1550*0Sstevel@tonic-gate	return $name .  ($op->flags & OPf_SPECIAL ? "()" : "");
1551*0Sstevel@tonic-gate    }
1552*0Sstevel@tonic-gate}
1553*0Sstevel@tonic-gate
1554*0Sstevel@tonic-gatesub pp_chop { maybe_targmy(@_, \&unop, "chop") }
1555*0Sstevel@tonic-gatesub pp_chomp { maybe_targmy(@_, \&unop, "chomp") }
1556*0Sstevel@tonic-gatesub pp_schop { maybe_targmy(@_, \&unop, "chop") }
1557*0Sstevel@tonic-gatesub pp_schomp { maybe_targmy(@_, \&unop, "chomp") }
1558*0Sstevel@tonic-gatesub pp_defined { unop(@_, "defined") }
1559*0Sstevel@tonic-gatesub pp_undef { unop(@_, "undef") }
1560*0Sstevel@tonic-gatesub pp_study { unop(@_, "study") }
1561*0Sstevel@tonic-gatesub pp_ref { unop(@_, "ref") }
1562*0Sstevel@tonic-gatesub pp_pos { maybe_local(@_, unop(@_, "pos")) }
1563*0Sstevel@tonic-gate
1564*0Sstevel@tonic-gatesub pp_sin { maybe_targmy(@_, \&unop, "sin") }
1565*0Sstevel@tonic-gatesub pp_cos { maybe_targmy(@_, \&unop, "cos") }
1566*0Sstevel@tonic-gatesub pp_rand { maybe_targmy(@_, \&unop, "rand") }
1567*0Sstevel@tonic-gatesub pp_srand { unop(@_, "srand") }
1568*0Sstevel@tonic-gatesub pp_exp { maybe_targmy(@_, \&unop, "exp") }
1569*0Sstevel@tonic-gatesub pp_log { maybe_targmy(@_, \&unop, "log") }
1570*0Sstevel@tonic-gatesub pp_sqrt { maybe_targmy(@_, \&unop, "sqrt") }
1571*0Sstevel@tonic-gatesub pp_int { maybe_targmy(@_, \&unop, "int") }
1572*0Sstevel@tonic-gatesub pp_hex { maybe_targmy(@_, \&unop, "hex") }
1573*0Sstevel@tonic-gatesub pp_oct { maybe_targmy(@_, \&unop, "oct") }
1574*0Sstevel@tonic-gatesub pp_abs { maybe_targmy(@_, \&unop, "abs") }
1575*0Sstevel@tonic-gate
1576*0Sstevel@tonic-gatesub pp_length { maybe_targmy(@_, \&unop, "length") }
1577*0Sstevel@tonic-gatesub pp_ord { maybe_targmy(@_, \&unop, "ord") }
1578*0Sstevel@tonic-gatesub pp_chr { maybe_targmy(@_, \&unop, "chr") }
1579*0Sstevel@tonic-gate
1580*0Sstevel@tonic-gatesub pp_each { unop(@_, "each") }
1581*0Sstevel@tonic-gatesub pp_values { unop(@_, "values") }
1582*0Sstevel@tonic-gatesub pp_keys { unop(@_, "keys") }
1583*0Sstevel@tonic-gatesub pp_pop { unop(@_, "pop") }
1584*0Sstevel@tonic-gatesub pp_shift { unop(@_, "shift") }
1585*0Sstevel@tonic-gate
1586*0Sstevel@tonic-gatesub pp_caller { unop(@_, "caller") }
1587*0Sstevel@tonic-gatesub pp_reset { unop(@_, "reset") }
1588*0Sstevel@tonic-gatesub pp_exit { unop(@_, "exit") }
1589*0Sstevel@tonic-gatesub pp_prototype { unop(@_, "prototype") }
1590*0Sstevel@tonic-gate
1591*0Sstevel@tonic-gatesub pp_close { unop(@_, "close") }
1592*0Sstevel@tonic-gatesub pp_fileno { unop(@_, "fileno") }
1593*0Sstevel@tonic-gatesub pp_umask { unop(@_, "umask") }
1594*0Sstevel@tonic-gatesub pp_untie { unop(@_, "untie") }
1595*0Sstevel@tonic-gatesub pp_tied { unop(@_, "tied") }
1596*0Sstevel@tonic-gatesub pp_dbmclose { unop(@_, "dbmclose") }
1597*0Sstevel@tonic-gatesub pp_getc { unop(@_, "getc") }
1598*0Sstevel@tonic-gatesub pp_eof { unop(@_, "eof") }
1599*0Sstevel@tonic-gatesub pp_tell { unop(@_, "tell") }
1600*0Sstevel@tonic-gatesub pp_getsockname { unop(@_, "getsockname") }
1601*0Sstevel@tonic-gatesub pp_getpeername { unop(@_, "getpeername") }
1602*0Sstevel@tonic-gate
1603*0Sstevel@tonic-gatesub pp_chdir { maybe_targmy(@_, \&unop, "chdir") }
1604*0Sstevel@tonic-gatesub pp_chroot { maybe_targmy(@_, \&unop, "chroot") }
1605*0Sstevel@tonic-gatesub pp_readlink { unop(@_, "readlink") }
1606*0Sstevel@tonic-gatesub pp_rmdir { maybe_targmy(@_, \&unop, "rmdir") }
1607*0Sstevel@tonic-gatesub pp_readdir { unop(@_, "readdir") }
1608*0Sstevel@tonic-gatesub pp_telldir { unop(@_, "telldir") }
1609*0Sstevel@tonic-gatesub pp_rewinddir { unop(@_, "rewinddir") }
1610*0Sstevel@tonic-gatesub pp_closedir { unop(@_, "closedir") }
1611*0Sstevel@tonic-gatesub pp_getpgrp { maybe_targmy(@_, \&unop, "getpgrp") }
1612*0Sstevel@tonic-gatesub pp_localtime { unop(@_, "localtime") }
1613*0Sstevel@tonic-gatesub pp_gmtime { unop(@_, "gmtime") }
1614*0Sstevel@tonic-gatesub pp_alarm { unop(@_, "alarm") }
1615*0Sstevel@tonic-gatesub pp_sleep { maybe_targmy(@_, \&unop, "sleep") }
1616*0Sstevel@tonic-gate
1617*0Sstevel@tonic-gatesub pp_dofile { unop(@_, "do") }
1618*0Sstevel@tonic-gatesub pp_entereval { unop(@_, "eval") }
1619*0Sstevel@tonic-gate
1620*0Sstevel@tonic-gatesub pp_ghbyname { unop(@_, "gethostbyname") }
1621*0Sstevel@tonic-gatesub pp_gnbyname { unop(@_, "getnetbyname") }
1622*0Sstevel@tonic-gatesub pp_gpbyname { unop(@_, "getprotobyname") }
1623*0Sstevel@tonic-gatesub pp_shostent { unop(@_, "sethostent") }
1624*0Sstevel@tonic-gatesub pp_snetent { unop(@_, "setnetent") }
1625*0Sstevel@tonic-gatesub pp_sprotoent { unop(@_, "setprotoent") }
1626*0Sstevel@tonic-gatesub pp_sservent { unop(@_, "setservent") }
1627*0Sstevel@tonic-gatesub pp_gpwnam { unop(@_, "getpwnam") }
1628*0Sstevel@tonic-gatesub pp_gpwuid { unop(@_, "getpwuid") }
1629*0Sstevel@tonic-gatesub pp_ggrnam { unop(@_, "getgrnam") }
1630*0Sstevel@tonic-gatesub pp_ggrgid { unop(@_, "getgrgid") }
1631*0Sstevel@tonic-gate
1632*0Sstevel@tonic-gatesub pp_lock { unop(@_, "lock") }
1633*0Sstevel@tonic-gate
1634*0Sstevel@tonic-gatesub pp_exists {
1635*0Sstevel@tonic-gate    my $self = shift;
1636*0Sstevel@tonic-gate    my($op, $cx) = @_;
1637*0Sstevel@tonic-gate    my $arg;
1638*0Sstevel@tonic-gate    if ($op->private & OPpEXISTS_SUB) {
1639*0Sstevel@tonic-gate	# Checking for the existence of a subroutine
1640*0Sstevel@tonic-gate	return $self->maybe_parens_func("exists",
1641*0Sstevel@tonic-gate				$self->pp_rv2cv($op->first, 16), $cx, 16);
1642*0Sstevel@tonic-gate    }
1643*0Sstevel@tonic-gate    if ($op->flags & OPf_SPECIAL) {
1644*0Sstevel@tonic-gate	# Array element, not hash element
1645*0Sstevel@tonic-gate	return $self->maybe_parens_func("exists",
1646*0Sstevel@tonic-gate				$self->pp_aelem($op->first, 16), $cx, 16);
1647*0Sstevel@tonic-gate    }
1648*0Sstevel@tonic-gate    return $self->maybe_parens_func("exists", $self->pp_helem($op->first, 16),
1649*0Sstevel@tonic-gate				    $cx, 16);
1650*0Sstevel@tonic-gate}
1651*0Sstevel@tonic-gate
1652*0Sstevel@tonic-gatesub pp_delete {
1653*0Sstevel@tonic-gate    my $self = shift;
1654*0Sstevel@tonic-gate    my($op, $cx) = @_;
1655*0Sstevel@tonic-gate    my $arg;
1656*0Sstevel@tonic-gate    if ($op->private & OPpSLICE) {
1657*0Sstevel@tonic-gate	if ($op->flags & OPf_SPECIAL) {
1658*0Sstevel@tonic-gate	    # Deleting from an array, not a hash
1659*0Sstevel@tonic-gate	    return $self->maybe_parens_func("delete",
1660*0Sstevel@tonic-gate					$self->pp_aslice($op->first, 16),
1661*0Sstevel@tonic-gate					$cx, 16);
1662*0Sstevel@tonic-gate	}
1663*0Sstevel@tonic-gate	return $self->maybe_parens_func("delete",
1664*0Sstevel@tonic-gate					$self->pp_hslice($op->first, 16),
1665*0Sstevel@tonic-gate					$cx, 16);
1666*0Sstevel@tonic-gate    } else {
1667*0Sstevel@tonic-gate	if ($op->flags & OPf_SPECIAL) {
1668*0Sstevel@tonic-gate	    # Deleting from an array, not a hash
1669*0Sstevel@tonic-gate	    return $self->maybe_parens_func("delete",
1670*0Sstevel@tonic-gate					$self->pp_aelem($op->first, 16),
1671*0Sstevel@tonic-gate					$cx, 16);
1672*0Sstevel@tonic-gate	}
1673*0Sstevel@tonic-gate	return $self->maybe_parens_func("delete",
1674*0Sstevel@tonic-gate					$self->pp_helem($op->first, 16),
1675*0Sstevel@tonic-gate					$cx, 16);
1676*0Sstevel@tonic-gate    }
1677*0Sstevel@tonic-gate}
1678*0Sstevel@tonic-gate
1679*0Sstevel@tonic-gatesub pp_require {
1680*0Sstevel@tonic-gate    my $self = shift;
1681*0Sstevel@tonic-gate    my($op, $cx) = @_;
1682*0Sstevel@tonic-gate    if (class($op) eq "UNOP" and $op->first->name eq "const"
1683*0Sstevel@tonic-gate	and $op->first->private & OPpCONST_BARE)
1684*0Sstevel@tonic-gate    {
1685*0Sstevel@tonic-gate	my $name = $self->const_sv($op->first)->PV;
1686*0Sstevel@tonic-gate	$name =~ s[/][::]g;
1687*0Sstevel@tonic-gate	$name =~ s/\.pm//g;
1688*0Sstevel@tonic-gate	return "require $name";
1689*0Sstevel@tonic-gate    } else {
1690*0Sstevel@tonic-gate	$self->unop($op, $cx, "require");
1691*0Sstevel@tonic-gate    }
1692*0Sstevel@tonic-gate}
1693*0Sstevel@tonic-gate
1694*0Sstevel@tonic-gatesub pp_scalar {
1695*0Sstevel@tonic-gate    my $self = shift;
1696*0Sstevel@tonic-gate    my($op, $cv) = @_;
1697*0Sstevel@tonic-gate    my $kid = $op->first;
1698*0Sstevel@tonic-gate    if (not null $kid->sibling) {
1699*0Sstevel@tonic-gate	# XXX Was a here-doc
1700*0Sstevel@tonic-gate	return $self->dquote($op);
1701*0Sstevel@tonic-gate    }
1702*0Sstevel@tonic-gate    $self->unop(@_, "scalar");
1703*0Sstevel@tonic-gate}
1704*0Sstevel@tonic-gate
1705*0Sstevel@tonic-gate
1706*0Sstevel@tonic-gatesub padval {
1707*0Sstevel@tonic-gate    my $self = shift;
1708*0Sstevel@tonic-gate    my $targ = shift;
1709*0Sstevel@tonic-gate    return $self->{'curcv'}->PADLIST->ARRAYelt(1)->ARRAYelt($targ);
1710*0Sstevel@tonic-gate}
1711*0Sstevel@tonic-gate
1712*0Sstevel@tonic-gatesub pp_refgen {
1713*0Sstevel@tonic-gate    my $self = shift;
1714*0Sstevel@tonic-gate    my($op, $cx) = @_;
1715*0Sstevel@tonic-gate    my $kid = $op->first;
1716*0Sstevel@tonic-gate    if ($kid->name eq "null") {
1717*0Sstevel@tonic-gate	$kid = $kid->first;
1718*0Sstevel@tonic-gate	if ($kid->name eq "anonlist" || $kid->name eq "anonhash") {
1719*0Sstevel@tonic-gate	    my($pre, $post) = @{{"anonlist" => ["[","]"],
1720*0Sstevel@tonic-gate				 "anonhash" => ["{","}"]}->{$kid->name}};
1721*0Sstevel@tonic-gate	    my($expr, @exprs);
1722*0Sstevel@tonic-gate	    $kid = $kid->first->sibling; # skip pushmark
1723*0Sstevel@tonic-gate	    for (; !null($kid); $kid = $kid->sibling) {
1724*0Sstevel@tonic-gate		$expr = $self->deparse($kid, 6);
1725*0Sstevel@tonic-gate		push @exprs, $expr;
1726*0Sstevel@tonic-gate	    }
1727*0Sstevel@tonic-gate	    return $pre . join(", ", @exprs) . $post;
1728*0Sstevel@tonic-gate	} elsif (!null($kid->sibling) and
1729*0Sstevel@tonic-gate		 $kid->sibling->name eq "anoncode") {
1730*0Sstevel@tonic-gate	    return "sub " .
1731*0Sstevel@tonic-gate		$self->deparse_sub($self->padval($kid->sibling->targ));
1732*0Sstevel@tonic-gate	} elsif ($kid->name eq "pushmark") {
1733*0Sstevel@tonic-gate            my $sib_name = $kid->sibling->name;
1734*0Sstevel@tonic-gate            if ($sib_name =~ /^(pad|rv2)[ah]v$/
1735*0Sstevel@tonic-gate                and not $kid->sibling->flags & OPf_REF)
1736*0Sstevel@tonic-gate            {
1737*0Sstevel@tonic-gate                # The @a in \(@a) isn't in ref context, but only when the
1738*0Sstevel@tonic-gate                # parens are there.
1739*0Sstevel@tonic-gate		return "\\(" . $self->pp_list($op->first) . ")";
1740*0Sstevel@tonic-gate            } elsif ($sib_name eq 'entersub') {
1741*0Sstevel@tonic-gate                my $text = $self->deparse($kid->sibling, 1);
1742*0Sstevel@tonic-gate                # Always show parens for \(&func()), but only with -p otherwise
1743*0Sstevel@tonic-gate                $text = "($text)" if $self->{'parens'}
1744*0Sstevel@tonic-gate                                 or $kid->sibling->private & OPpENTERSUB_AMPER;
1745*0Sstevel@tonic-gate                return "\\$text";
1746*0Sstevel@tonic-gate            }
1747*0Sstevel@tonic-gate        }
1748*0Sstevel@tonic-gate    }
1749*0Sstevel@tonic-gate    $self->pfixop($op, $cx, "\\", 20);
1750*0Sstevel@tonic-gate}
1751*0Sstevel@tonic-gate
1752*0Sstevel@tonic-gatesub pp_srefgen { pp_refgen(@_) }
1753*0Sstevel@tonic-gate
1754*0Sstevel@tonic-gatesub pp_readline {
1755*0Sstevel@tonic-gate    my $self = shift;
1756*0Sstevel@tonic-gate    my($op, $cx) = @_;
1757*0Sstevel@tonic-gate    my $kid = $op->first;
1758*0Sstevel@tonic-gate    $kid = $kid->first if $kid->name eq "rv2gv"; # <$fh>
1759*0Sstevel@tonic-gate    return "<" . $self->deparse($kid, 1) . ">" if is_scalar($kid);
1760*0Sstevel@tonic-gate    return $self->unop($op, $cx, "readline");
1761*0Sstevel@tonic-gate}
1762*0Sstevel@tonic-gate
1763*0Sstevel@tonic-gatesub pp_rcatline {
1764*0Sstevel@tonic-gate    my $self = shift;
1765*0Sstevel@tonic-gate    my($op) = @_;
1766*0Sstevel@tonic-gate    return "<" . $self->gv_name($self->gv_or_padgv($op)) . ">";
1767*0Sstevel@tonic-gate}
1768*0Sstevel@tonic-gate
1769*0Sstevel@tonic-gate# Unary operators that can occur as pseudo-listops inside double quotes
1770*0Sstevel@tonic-gatesub dq_unop {
1771*0Sstevel@tonic-gate    my $self = shift;
1772*0Sstevel@tonic-gate    my($op, $cx, $name, $prec, $flags) = (@_, 0, 0);
1773*0Sstevel@tonic-gate    my $kid;
1774*0Sstevel@tonic-gate    if ($op->flags & OPf_KIDS) {
1775*0Sstevel@tonic-gate       $kid = $op->first;
1776*0Sstevel@tonic-gate       # If there's more than one kid, the first is an ex-pushmark.
1777*0Sstevel@tonic-gate       $kid = $kid->sibling if not null $kid->sibling;
1778*0Sstevel@tonic-gate       return $self->maybe_parens_unop($name, $kid, $cx);
1779*0Sstevel@tonic-gate    } else {
1780*0Sstevel@tonic-gate       return $name .  ($op->flags & OPf_SPECIAL ? "()" : "");
1781*0Sstevel@tonic-gate    }
1782*0Sstevel@tonic-gate}
1783*0Sstevel@tonic-gate
1784*0Sstevel@tonic-gatesub pp_ucfirst { dq_unop(@_, "ucfirst") }
1785*0Sstevel@tonic-gatesub pp_lcfirst { dq_unop(@_, "lcfirst") }
1786*0Sstevel@tonic-gatesub pp_uc { dq_unop(@_, "uc") }
1787*0Sstevel@tonic-gatesub pp_lc { dq_unop(@_, "lc") }
1788*0Sstevel@tonic-gatesub pp_quotemeta { maybe_targmy(@_, \&dq_unop, "quotemeta") }
1789*0Sstevel@tonic-gate
1790*0Sstevel@tonic-gatesub loopex {
1791*0Sstevel@tonic-gate    my $self = shift;
1792*0Sstevel@tonic-gate    my ($op, $cx, $name) = @_;
1793*0Sstevel@tonic-gate    if (class($op) eq "PVOP") {
1794*0Sstevel@tonic-gate	return "$name " . $op->pv;
1795*0Sstevel@tonic-gate    } elsif (class($op) eq "OP") {
1796*0Sstevel@tonic-gate	return $name;
1797*0Sstevel@tonic-gate    } elsif (class($op) eq "UNOP") {
1798*0Sstevel@tonic-gate	# Note -- loop exits are actually exempt from the
1799*0Sstevel@tonic-gate	# looks-like-a-func rule, but a few extra parens won't hurt
1800*0Sstevel@tonic-gate	return $self->maybe_parens_unop($name, $op->first, $cx);
1801*0Sstevel@tonic-gate    }
1802*0Sstevel@tonic-gate}
1803*0Sstevel@tonic-gate
1804*0Sstevel@tonic-gatesub pp_last { loopex(@_, "last") }
1805*0Sstevel@tonic-gatesub pp_next { loopex(@_, "next") }
1806*0Sstevel@tonic-gatesub pp_redo { loopex(@_, "redo") }
1807*0Sstevel@tonic-gatesub pp_goto { loopex(@_, "goto") }
1808*0Sstevel@tonic-gatesub pp_dump { loopex(@_, "dump") }
1809*0Sstevel@tonic-gate
1810*0Sstevel@tonic-gatesub ftst {
1811*0Sstevel@tonic-gate    my $self = shift;
1812*0Sstevel@tonic-gate    my($op, $cx, $name) = @_;
1813*0Sstevel@tonic-gate    if (class($op) eq "UNOP") {
1814*0Sstevel@tonic-gate	# Genuine `-X' filetests are exempt from the LLAFR, but not
1815*0Sstevel@tonic-gate	# l?stat(); for the sake of clarity, give'em all parens
1816*0Sstevel@tonic-gate	return $self->maybe_parens_unop($name, $op->first, $cx);
1817*0Sstevel@tonic-gate    } elsif (class($op) =~ /^(SV|PAD)OP$/) {
1818*0Sstevel@tonic-gate	return $self->maybe_parens_func($name, $self->pp_gv($op, 1), $cx, 16);
1819*0Sstevel@tonic-gate    } else { # I don't think baseop filetests ever survive ck_ftst, but...
1820*0Sstevel@tonic-gate	return $name;
1821*0Sstevel@tonic-gate    }
1822*0Sstevel@tonic-gate}
1823*0Sstevel@tonic-gate
1824*0Sstevel@tonic-gatesub pp_lstat    { ftst(@_, "lstat") }
1825*0Sstevel@tonic-gatesub pp_stat     { ftst(@_, "stat") }
1826*0Sstevel@tonic-gatesub pp_ftrread  { ftst(@_, "-R") }
1827*0Sstevel@tonic-gatesub pp_ftrwrite { ftst(@_, "-W") }
1828*0Sstevel@tonic-gatesub pp_ftrexec  { ftst(@_, "-X") }
1829*0Sstevel@tonic-gatesub pp_fteread  { ftst(@_, "-r") }
1830*0Sstevel@tonic-gatesub pp_ftewrite { ftst(@_, "-w") }
1831*0Sstevel@tonic-gatesub pp_fteexec  { ftst(@_, "-x") }
1832*0Sstevel@tonic-gatesub pp_ftis     { ftst(@_, "-e") }
1833*0Sstevel@tonic-gatesub pp_fteowned { ftst(@_, "-O") }
1834*0Sstevel@tonic-gatesub pp_ftrowned { ftst(@_, "-o") }
1835*0Sstevel@tonic-gatesub pp_ftzero   { ftst(@_, "-z") }
1836*0Sstevel@tonic-gatesub pp_ftsize   { ftst(@_, "-s") }
1837*0Sstevel@tonic-gatesub pp_ftmtime  { ftst(@_, "-M") }
1838*0Sstevel@tonic-gatesub pp_ftatime  { ftst(@_, "-A") }
1839*0Sstevel@tonic-gatesub pp_ftctime  { ftst(@_, "-C") }
1840*0Sstevel@tonic-gatesub pp_ftsock   { ftst(@_, "-S") }
1841*0Sstevel@tonic-gatesub pp_ftchr    { ftst(@_, "-c") }
1842*0Sstevel@tonic-gatesub pp_ftblk    { ftst(@_, "-b") }
1843*0Sstevel@tonic-gatesub pp_ftfile   { ftst(@_, "-f") }
1844*0Sstevel@tonic-gatesub pp_ftdir    { ftst(@_, "-d") }
1845*0Sstevel@tonic-gatesub pp_ftpipe   { ftst(@_, "-p") }
1846*0Sstevel@tonic-gatesub pp_ftlink   { ftst(@_, "-l") }
1847*0Sstevel@tonic-gatesub pp_ftsuid   { ftst(@_, "-u") }
1848*0Sstevel@tonic-gatesub pp_ftsgid   { ftst(@_, "-g") }
1849*0Sstevel@tonic-gatesub pp_ftsvtx   { ftst(@_, "-k") }
1850*0Sstevel@tonic-gatesub pp_fttty    { ftst(@_, "-t") }
1851*0Sstevel@tonic-gatesub pp_fttext   { ftst(@_, "-T") }
1852*0Sstevel@tonic-gatesub pp_ftbinary { ftst(@_, "-B") }
1853*0Sstevel@tonic-gate
1854*0Sstevel@tonic-gatesub SWAP_CHILDREN () { 1 }
1855*0Sstevel@tonic-gatesub ASSIGN () { 2 } # has OP= variant
1856*0Sstevel@tonic-gatesub LIST_CONTEXT () { 4 } # Assignment is in list context
1857*0Sstevel@tonic-gate
1858*0Sstevel@tonic-gatemy(%left, %right);
1859*0Sstevel@tonic-gate
1860*0Sstevel@tonic-gatesub assoc_class {
1861*0Sstevel@tonic-gate    my $op = shift;
1862*0Sstevel@tonic-gate    my $name = $op->name;
1863*0Sstevel@tonic-gate    if ($name eq "concat" and $op->first->name eq "concat") {
1864*0Sstevel@tonic-gate	# avoid spurious `=' -- see comment in pp_concat
1865*0Sstevel@tonic-gate	return "concat";
1866*0Sstevel@tonic-gate    }
1867*0Sstevel@tonic-gate    if ($name eq "null" and class($op) eq "UNOP"
1868*0Sstevel@tonic-gate	and $op->first->name =~ /^(and|x?or)$/
1869*0Sstevel@tonic-gate	and null $op->first->sibling)
1870*0Sstevel@tonic-gate    {
1871*0Sstevel@tonic-gate	# Like all conditional constructs, OP_ANDs and OP_ORs are topped
1872*0Sstevel@tonic-gate	# with a null that's used as the common end point of the two
1873*0Sstevel@tonic-gate	# flows of control. For precedence purposes, ignore it.
1874*0Sstevel@tonic-gate	# (COND_EXPRs have these too, but we don't bother with
1875*0Sstevel@tonic-gate	# their associativity).
1876*0Sstevel@tonic-gate	return assoc_class($op->first);
1877*0Sstevel@tonic-gate    }
1878*0Sstevel@tonic-gate    return $name . ($op->flags & OPf_STACKED ? "=" : "");
1879*0Sstevel@tonic-gate}
1880*0Sstevel@tonic-gate
1881*0Sstevel@tonic-gate# Left associative operators, like `+', for which
1882*0Sstevel@tonic-gate# $a + $b + $c is equivalent to ($a + $b) + $c
1883*0Sstevel@tonic-gate
1884*0Sstevel@tonic-gateBEGIN {
1885*0Sstevel@tonic-gate    %left = ('multiply' => 19, 'i_multiply' => 19,
1886*0Sstevel@tonic-gate	     'divide' => 19, 'i_divide' => 19,
1887*0Sstevel@tonic-gate	     'modulo' => 19, 'i_modulo' => 19,
1888*0Sstevel@tonic-gate	     'repeat' => 19,
1889*0Sstevel@tonic-gate	     'add' => 18, 'i_add' => 18,
1890*0Sstevel@tonic-gate	     'subtract' => 18, 'i_subtract' => 18,
1891*0Sstevel@tonic-gate	     'concat' => 18,
1892*0Sstevel@tonic-gate	     'left_shift' => 17, 'right_shift' => 17,
1893*0Sstevel@tonic-gate	     'bit_and' => 13,
1894*0Sstevel@tonic-gate	     'bit_or' => 12, 'bit_xor' => 12,
1895*0Sstevel@tonic-gate	     'and' => 3,
1896*0Sstevel@tonic-gate	     'or' => 2, 'xor' => 2,
1897*0Sstevel@tonic-gate	    );
1898*0Sstevel@tonic-gate}
1899*0Sstevel@tonic-gate
1900*0Sstevel@tonic-gatesub deparse_binop_left {
1901*0Sstevel@tonic-gate    my $self = shift;
1902*0Sstevel@tonic-gate    my($op, $left, $prec) = @_;
1903*0Sstevel@tonic-gate    if ($left{assoc_class($op)} && $left{assoc_class($left)}
1904*0Sstevel@tonic-gate	and $left{assoc_class($op)} == $left{assoc_class($left)})
1905*0Sstevel@tonic-gate    {
1906*0Sstevel@tonic-gate	return $self->deparse($left, $prec - .00001);
1907*0Sstevel@tonic-gate    } else {
1908*0Sstevel@tonic-gate	return $self->deparse($left, $prec);
1909*0Sstevel@tonic-gate    }
1910*0Sstevel@tonic-gate}
1911*0Sstevel@tonic-gate
1912*0Sstevel@tonic-gate# Right associative operators, like `=', for which
1913*0Sstevel@tonic-gate# $a = $b = $c is equivalent to $a = ($b = $c)
1914*0Sstevel@tonic-gate
1915*0Sstevel@tonic-gateBEGIN {
1916*0Sstevel@tonic-gate    %right = ('pow' => 22,
1917*0Sstevel@tonic-gate	      'sassign=' => 7, 'aassign=' => 7,
1918*0Sstevel@tonic-gate	      'multiply=' => 7, 'i_multiply=' => 7,
1919*0Sstevel@tonic-gate	      'divide=' => 7, 'i_divide=' => 7,
1920*0Sstevel@tonic-gate	      'modulo=' => 7, 'i_modulo=' => 7,
1921*0Sstevel@tonic-gate	      'repeat=' => 7,
1922*0Sstevel@tonic-gate	      'add=' => 7, 'i_add=' => 7,
1923*0Sstevel@tonic-gate	      'subtract=' => 7, 'i_subtract=' => 7,
1924*0Sstevel@tonic-gate	      'concat=' => 7,
1925*0Sstevel@tonic-gate	      'left_shift=' => 7, 'right_shift=' => 7,
1926*0Sstevel@tonic-gate	      'bit_and=' => 7,
1927*0Sstevel@tonic-gate	      'bit_or=' => 7, 'bit_xor=' => 7,
1928*0Sstevel@tonic-gate	      'andassign' => 7,
1929*0Sstevel@tonic-gate	      'orassign' => 7,
1930*0Sstevel@tonic-gate	     );
1931*0Sstevel@tonic-gate}
1932*0Sstevel@tonic-gate
1933*0Sstevel@tonic-gatesub deparse_binop_right {
1934*0Sstevel@tonic-gate    my $self = shift;
1935*0Sstevel@tonic-gate    my($op, $right, $prec) = @_;
1936*0Sstevel@tonic-gate    if ($right{assoc_class($op)} && $right{assoc_class($right)}
1937*0Sstevel@tonic-gate	and $right{assoc_class($op)} == $right{assoc_class($right)})
1938*0Sstevel@tonic-gate    {
1939*0Sstevel@tonic-gate	return $self->deparse($right, $prec - .00001);
1940*0Sstevel@tonic-gate    } else {
1941*0Sstevel@tonic-gate	return $self->deparse($right, $prec);
1942*0Sstevel@tonic-gate    }
1943*0Sstevel@tonic-gate}
1944*0Sstevel@tonic-gate
1945*0Sstevel@tonic-gatesub binop {
1946*0Sstevel@tonic-gate    my $self = shift;
1947*0Sstevel@tonic-gate    my ($op, $cx, $opname, $prec, $flags) = (@_, 0);
1948*0Sstevel@tonic-gate    my $left = $op->first;
1949*0Sstevel@tonic-gate    my $right = $op->last;
1950*0Sstevel@tonic-gate    my $eq = "";
1951*0Sstevel@tonic-gate    if ($op->flags & OPf_STACKED && $flags & ASSIGN) {
1952*0Sstevel@tonic-gate	$eq = "=";
1953*0Sstevel@tonic-gate	$prec = 7;
1954*0Sstevel@tonic-gate    }
1955*0Sstevel@tonic-gate    if ($flags & SWAP_CHILDREN) {
1956*0Sstevel@tonic-gate	($left, $right) = ($right, $left);
1957*0Sstevel@tonic-gate    }
1958*0Sstevel@tonic-gate    $left = $self->deparse_binop_left($op, $left, $prec);
1959*0Sstevel@tonic-gate    $left = "($left)" if $flags & LIST_CONTEXT
1960*0Sstevel@tonic-gate		&& $left !~ /^(my|our|local|)[\@\(]/;
1961*0Sstevel@tonic-gate    $right = $self->deparse_binop_right($op, $right, $prec);
1962*0Sstevel@tonic-gate    return $self->maybe_parens("$left $opname$eq $right", $cx, $prec);
1963*0Sstevel@tonic-gate}
1964*0Sstevel@tonic-gate
1965*0Sstevel@tonic-gatesub pp_add { maybe_targmy(@_, \&binop, "+", 18, ASSIGN) }
1966*0Sstevel@tonic-gatesub pp_multiply { maybe_targmy(@_, \&binop, "*", 19, ASSIGN) }
1967*0Sstevel@tonic-gatesub pp_subtract { maybe_targmy(@_, \&binop, "-",18,  ASSIGN) }
1968*0Sstevel@tonic-gatesub pp_divide { maybe_targmy(@_, \&binop, "/", 19, ASSIGN) }
1969*0Sstevel@tonic-gatesub pp_modulo { maybe_targmy(@_, \&binop, "%", 19, ASSIGN) }
1970*0Sstevel@tonic-gatesub pp_i_add { maybe_targmy(@_, \&binop, "+", 18, ASSIGN) }
1971*0Sstevel@tonic-gatesub pp_i_multiply { maybe_targmy(@_, \&binop, "*", 19, ASSIGN) }
1972*0Sstevel@tonic-gatesub pp_i_subtract { maybe_targmy(@_, \&binop, "-", 18, ASSIGN) }
1973*0Sstevel@tonic-gatesub pp_i_divide { maybe_targmy(@_, \&binop, "/", 19, ASSIGN) }
1974*0Sstevel@tonic-gatesub pp_i_modulo { maybe_targmy(@_, \&binop, "%", 19, ASSIGN) }
1975*0Sstevel@tonic-gatesub pp_pow { maybe_targmy(@_, \&binop, "**", 22, ASSIGN) }
1976*0Sstevel@tonic-gate
1977*0Sstevel@tonic-gatesub pp_left_shift { maybe_targmy(@_, \&binop, "<<", 17, ASSIGN) }
1978*0Sstevel@tonic-gatesub pp_right_shift { maybe_targmy(@_, \&binop, ">>", 17, ASSIGN) }
1979*0Sstevel@tonic-gatesub pp_bit_and { maybe_targmy(@_, \&binop, "&", 13, ASSIGN) }
1980*0Sstevel@tonic-gatesub pp_bit_or { maybe_targmy(@_, \&binop, "|", 12, ASSIGN) }
1981*0Sstevel@tonic-gatesub pp_bit_xor { maybe_targmy(@_, \&binop, "^", 12, ASSIGN) }
1982*0Sstevel@tonic-gate
1983*0Sstevel@tonic-gatesub pp_eq { binop(@_, "==", 14) }
1984*0Sstevel@tonic-gatesub pp_ne { binop(@_, "!=", 14) }
1985*0Sstevel@tonic-gatesub pp_lt { binop(@_, "<", 15) }
1986*0Sstevel@tonic-gatesub pp_gt { binop(@_, ">", 15) }
1987*0Sstevel@tonic-gatesub pp_ge { binop(@_, ">=", 15) }
1988*0Sstevel@tonic-gatesub pp_le { binop(@_, "<=", 15) }
1989*0Sstevel@tonic-gatesub pp_ncmp { binop(@_, "<=>", 14) }
1990*0Sstevel@tonic-gatesub pp_i_eq { binop(@_, "==", 14) }
1991*0Sstevel@tonic-gatesub pp_i_ne { binop(@_, "!=", 14) }
1992*0Sstevel@tonic-gatesub pp_i_lt { binop(@_, "<", 15) }
1993*0Sstevel@tonic-gatesub pp_i_gt { binop(@_, ">", 15) }
1994*0Sstevel@tonic-gatesub pp_i_ge { binop(@_, ">=", 15) }
1995*0Sstevel@tonic-gatesub pp_i_le { binop(@_, "<=", 15) }
1996*0Sstevel@tonic-gatesub pp_i_ncmp { binop(@_, "<=>", 14) }
1997*0Sstevel@tonic-gate
1998*0Sstevel@tonic-gatesub pp_seq { binop(@_, "eq", 14) }
1999*0Sstevel@tonic-gatesub pp_sne { binop(@_, "ne", 14) }
2000*0Sstevel@tonic-gatesub pp_slt { binop(@_, "lt", 15) }
2001*0Sstevel@tonic-gatesub pp_sgt { binop(@_, "gt", 15) }
2002*0Sstevel@tonic-gatesub pp_sge { binop(@_, "ge", 15) }
2003*0Sstevel@tonic-gatesub pp_sle { binop(@_, "le", 15) }
2004*0Sstevel@tonic-gatesub pp_scmp { binop(@_, "cmp", 14) }
2005*0Sstevel@tonic-gate
2006*0Sstevel@tonic-gatesub pp_sassign { binop(@_, "=", 7, SWAP_CHILDREN) }
2007*0Sstevel@tonic-gatesub pp_aassign { binop(@_, "=", 7, SWAP_CHILDREN | LIST_CONTEXT) }
2008*0Sstevel@tonic-gate
2009*0Sstevel@tonic-gate# `.' is special because concats-of-concats are optimized to save copying
2010*0Sstevel@tonic-gate# by making all but the first concat stacked. The effect is as if the
2011*0Sstevel@tonic-gate# programmer had written `($a . $b) .= $c', except legal.
2012*0Sstevel@tonic-gatesub pp_concat { maybe_targmy(@_, \&real_concat) }
2013*0Sstevel@tonic-gatesub real_concat {
2014*0Sstevel@tonic-gate    my $self = shift;
2015*0Sstevel@tonic-gate    my($op, $cx) = @_;
2016*0Sstevel@tonic-gate    my $left = $op->first;
2017*0Sstevel@tonic-gate    my $right = $op->last;
2018*0Sstevel@tonic-gate    my $eq = "";
2019*0Sstevel@tonic-gate    my $prec = 18;
2020*0Sstevel@tonic-gate    if ($op->flags & OPf_STACKED and $op->first->name ne "concat") {
2021*0Sstevel@tonic-gate	$eq = "=";
2022*0Sstevel@tonic-gate	$prec = 7;
2023*0Sstevel@tonic-gate    }
2024*0Sstevel@tonic-gate    $left = $self->deparse_binop_left($op, $left, $prec);
2025*0Sstevel@tonic-gate    $right = $self->deparse_binop_right($op, $right, $prec);
2026*0Sstevel@tonic-gate    return $self->maybe_parens("$left .$eq $right", $cx, $prec);
2027*0Sstevel@tonic-gate}
2028*0Sstevel@tonic-gate
2029*0Sstevel@tonic-gate# `x' is weird when the left arg is a list
2030*0Sstevel@tonic-gatesub pp_repeat {
2031*0Sstevel@tonic-gate    my $self = shift;
2032*0Sstevel@tonic-gate    my($op, $cx) = @_;
2033*0Sstevel@tonic-gate    my $left = $op->first;
2034*0Sstevel@tonic-gate    my $right = $op->last;
2035*0Sstevel@tonic-gate    my $eq = "";
2036*0Sstevel@tonic-gate    my $prec = 19;
2037*0Sstevel@tonic-gate    if ($op->flags & OPf_STACKED) {
2038*0Sstevel@tonic-gate	$eq = "=";
2039*0Sstevel@tonic-gate	$prec = 7;
2040*0Sstevel@tonic-gate    }
2041*0Sstevel@tonic-gate    if (null($right)) { # list repeat; count is inside left-side ex-list
2042*0Sstevel@tonic-gate	my $kid = $left->first->sibling; # skip pushmark
2043*0Sstevel@tonic-gate	my @exprs;
2044*0Sstevel@tonic-gate	for (; !null($kid->sibling); $kid = $kid->sibling) {
2045*0Sstevel@tonic-gate	    push @exprs, $self->deparse($kid, 6);
2046*0Sstevel@tonic-gate	}
2047*0Sstevel@tonic-gate	$right = $kid;
2048*0Sstevel@tonic-gate	$left = "(" . join(", ", @exprs). ")";
2049*0Sstevel@tonic-gate    } else {
2050*0Sstevel@tonic-gate	$left = $self->deparse_binop_left($op, $left, $prec);
2051*0Sstevel@tonic-gate    }
2052*0Sstevel@tonic-gate    $right = $self->deparse_binop_right($op, $right, $prec);
2053*0Sstevel@tonic-gate    return $self->maybe_parens("$left x$eq $right", $cx, $prec);
2054*0Sstevel@tonic-gate}
2055*0Sstevel@tonic-gate
2056*0Sstevel@tonic-gatesub range {
2057*0Sstevel@tonic-gate    my $self = shift;
2058*0Sstevel@tonic-gate    my ($op, $cx, $type) = @_;
2059*0Sstevel@tonic-gate    my $left = $op->first;
2060*0Sstevel@tonic-gate    my $right = $left->sibling;
2061*0Sstevel@tonic-gate    $left = $self->deparse($left, 9);
2062*0Sstevel@tonic-gate    $right = $self->deparse($right, 9);
2063*0Sstevel@tonic-gate    return $self->maybe_parens("$left $type $right", $cx, 9);
2064*0Sstevel@tonic-gate}
2065*0Sstevel@tonic-gate
2066*0Sstevel@tonic-gatesub pp_flop {
2067*0Sstevel@tonic-gate    my $self = shift;
2068*0Sstevel@tonic-gate    my($op, $cx) = @_;
2069*0Sstevel@tonic-gate    my $flip = $op->first;
2070*0Sstevel@tonic-gate    my $type = ($flip->flags & OPf_SPECIAL) ? "..." : "..";
2071*0Sstevel@tonic-gate    return $self->range($flip->first, $cx, $type);
2072*0Sstevel@tonic-gate}
2073*0Sstevel@tonic-gate
2074*0Sstevel@tonic-gate# one-line while/until is handled in pp_leave
2075*0Sstevel@tonic-gate
2076*0Sstevel@tonic-gatesub logop {
2077*0Sstevel@tonic-gate    my $self = shift;
2078*0Sstevel@tonic-gate    my ($op, $cx, $lowop, $lowprec, $highop, $highprec, $blockname) = @_;
2079*0Sstevel@tonic-gate    my $left = $op->first;
2080*0Sstevel@tonic-gate    my $right = $op->first->sibling;
2081*0Sstevel@tonic-gate    if ($cx < 1 and is_scope($right) and $blockname
2082*0Sstevel@tonic-gate	and $self->{'expand'} < 7)
2083*0Sstevel@tonic-gate    { # if ($a) {$b}
2084*0Sstevel@tonic-gate	$left = $self->deparse($left, 1);
2085*0Sstevel@tonic-gate	$right = $self->deparse($right, 0);
2086*0Sstevel@tonic-gate	return "$blockname ($left) {\n\t$right\n\b}\cK";
2087*0Sstevel@tonic-gate    } elsif ($cx < 1 and $blockname and not $self->{'parens'}
2088*0Sstevel@tonic-gate	     and $self->{'expand'} < 7) { # $b if $a
2089*0Sstevel@tonic-gate	$right = $self->deparse($right, 1);
2090*0Sstevel@tonic-gate	$left = $self->deparse($left, 1);
2091*0Sstevel@tonic-gate	return "$right $blockname $left";
2092*0Sstevel@tonic-gate    } elsif ($cx > $lowprec and $highop) { # $a && $b
2093*0Sstevel@tonic-gate	$left = $self->deparse_binop_left($op, $left, $highprec);
2094*0Sstevel@tonic-gate	$right = $self->deparse_binop_right($op, $right, $highprec);
2095*0Sstevel@tonic-gate	return $self->maybe_parens("$left $highop $right", $cx, $highprec);
2096*0Sstevel@tonic-gate    } else { # $a and $b
2097*0Sstevel@tonic-gate	$left = $self->deparse_binop_left($op, $left, $lowprec);
2098*0Sstevel@tonic-gate	$right = $self->deparse_binop_right($op, $right, $lowprec);
2099*0Sstevel@tonic-gate	return $self->maybe_parens("$left $lowop $right", $cx, $lowprec);
2100*0Sstevel@tonic-gate    }
2101*0Sstevel@tonic-gate}
2102*0Sstevel@tonic-gate
2103*0Sstevel@tonic-gatesub pp_and { logop(@_, "and", 3, "&&", 11, "if") }
2104*0Sstevel@tonic-gatesub pp_or  { logop(@_, "or",  2, "||", 10, "unless") }
2105*0Sstevel@tonic-gate
2106*0Sstevel@tonic-gate# xor is syntactically a logop, but it's really a binop (contrary to
2107*0Sstevel@tonic-gate# old versions of opcode.pl). Syntax is what matters here.
2108*0Sstevel@tonic-gatesub pp_xor { logop(@_, "xor", 2, "",   0,  "") }
2109*0Sstevel@tonic-gate
2110*0Sstevel@tonic-gatesub logassignop {
2111*0Sstevel@tonic-gate    my $self = shift;
2112*0Sstevel@tonic-gate    my ($op, $cx, $opname) = @_;
2113*0Sstevel@tonic-gate    my $left = $op->first;
2114*0Sstevel@tonic-gate    my $right = $op->first->sibling->first; # skip sassign
2115*0Sstevel@tonic-gate    $left = $self->deparse($left, 7);
2116*0Sstevel@tonic-gate    $right = $self->deparse($right, 7);
2117*0Sstevel@tonic-gate    return $self->maybe_parens("$left $opname $right", $cx, 7);
2118*0Sstevel@tonic-gate}
2119*0Sstevel@tonic-gate
2120*0Sstevel@tonic-gatesub pp_andassign { logassignop(@_, "&&=") }
2121*0Sstevel@tonic-gatesub pp_orassign { logassignop(@_, "||=") }
2122*0Sstevel@tonic-gate
2123*0Sstevel@tonic-gatesub listop {
2124*0Sstevel@tonic-gate    my $self = shift;
2125*0Sstevel@tonic-gate    my($op, $cx, $name) = @_;
2126*0Sstevel@tonic-gate    my(@exprs);
2127*0Sstevel@tonic-gate    my $parens = ($cx >= 5) || $self->{'parens'};
2128*0Sstevel@tonic-gate    my $kid = $op->first->sibling;
2129*0Sstevel@tonic-gate    return $name if null $kid;
2130*0Sstevel@tonic-gate    my $first;
2131*0Sstevel@tonic-gate    $name = "socketpair" if $name eq "sockpair";
2132*0Sstevel@tonic-gate    my $proto = prototype("CORE::$name");
2133*0Sstevel@tonic-gate    if (defined $proto
2134*0Sstevel@tonic-gate	&& $proto =~ /^;?\*/
2135*0Sstevel@tonic-gate	&& $kid->name eq "rv2gv") {
2136*0Sstevel@tonic-gate	$first = $self->deparse($kid->first, 6);
2137*0Sstevel@tonic-gate    }
2138*0Sstevel@tonic-gate    else {
2139*0Sstevel@tonic-gate	$first = $self->deparse($kid, 6);
2140*0Sstevel@tonic-gate    }
2141*0Sstevel@tonic-gate    if ($name eq "chmod" && $first =~ /^\d+$/) {
2142*0Sstevel@tonic-gate	$first = sprintf("%#o", $first);
2143*0Sstevel@tonic-gate    }
2144*0Sstevel@tonic-gate    $first = "+$first" if not $parens and substr($first, 0, 1) eq "(";
2145*0Sstevel@tonic-gate    push @exprs, $first;
2146*0Sstevel@tonic-gate    $kid = $kid->sibling;
2147*0Sstevel@tonic-gate    if (defined $proto && $proto =~ /^\*\*/ && $kid->name eq "rv2gv") {
2148*0Sstevel@tonic-gate	push @exprs, $self->deparse($kid->first, 6);
2149*0Sstevel@tonic-gate	$kid = $kid->sibling;
2150*0Sstevel@tonic-gate    }
2151*0Sstevel@tonic-gate    for (; !null($kid); $kid = $kid->sibling) {
2152*0Sstevel@tonic-gate	push @exprs, $self->deparse($kid, 6);
2153*0Sstevel@tonic-gate    }
2154*0Sstevel@tonic-gate    if ($parens) {
2155*0Sstevel@tonic-gate	return "$name(" . join(", ", @exprs) . ")";
2156*0Sstevel@tonic-gate    } else {
2157*0Sstevel@tonic-gate	return "$name " . join(", ", @exprs);
2158*0Sstevel@tonic-gate    }
2159*0Sstevel@tonic-gate}
2160*0Sstevel@tonic-gate
2161*0Sstevel@tonic-gatesub pp_bless { listop(@_, "bless") }
2162*0Sstevel@tonic-gatesub pp_atan2 { maybe_targmy(@_, \&listop, "atan2") }
2163*0Sstevel@tonic-gatesub pp_substr { maybe_local(@_, listop(@_, "substr")) }
2164*0Sstevel@tonic-gatesub pp_vec { maybe_local(@_, listop(@_, "vec")) }
2165*0Sstevel@tonic-gatesub pp_index { maybe_targmy(@_, \&listop, "index") }
2166*0Sstevel@tonic-gatesub pp_rindex { maybe_targmy(@_, \&listop, "rindex") }
2167*0Sstevel@tonic-gatesub pp_sprintf { maybe_targmy(@_, \&listop, "sprintf") }
2168*0Sstevel@tonic-gatesub pp_formline { listop(@_, "formline") } # see also deparse_format
2169*0Sstevel@tonic-gatesub pp_crypt { maybe_targmy(@_, \&listop, "crypt") }
2170*0Sstevel@tonic-gatesub pp_unpack { listop(@_, "unpack") }
2171*0Sstevel@tonic-gatesub pp_pack { listop(@_, "pack") }
2172*0Sstevel@tonic-gatesub pp_join { maybe_targmy(@_, \&listop, "join") }
2173*0Sstevel@tonic-gatesub pp_splice { listop(@_, "splice") }
2174*0Sstevel@tonic-gatesub pp_push { maybe_targmy(@_, \&listop, "push") }
2175*0Sstevel@tonic-gatesub pp_unshift { maybe_targmy(@_, \&listop, "unshift") }
2176*0Sstevel@tonic-gatesub pp_reverse { listop(@_, "reverse") }
2177*0Sstevel@tonic-gatesub pp_warn { listop(@_, "warn") }
2178*0Sstevel@tonic-gatesub pp_die { listop(@_, "die") }
2179*0Sstevel@tonic-gate# Actually, return is exempt from the LLAFR (see examples in this very
2180*0Sstevel@tonic-gate# module!), but for consistency's sake, ignore that fact
2181*0Sstevel@tonic-gatesub pp_return { listop(@_, "return") }
2182*0Sstevel@tonic-gatesub pp_open { listop(@_, "open") }
2183*0Sstevel@tonic-gatesub pp_pipe_op { listop(@_, "pipe") }
2184*0Sstevel@tonic-gatesub pp_tie { listop(@_, "tie") }
2185*0Sstevel@tonic-gatesub pp_binmode { listop(@_, "binmode") }
2186*0Sstevel@tonic-gatesub pp_dbmopen { listop(@_, "dbmopen") }
2187*0Sstevel@tonic-gatesub pp_sselect { listop(@_, "select") }
2188*0Sstevel@tonic-gatesub pp_select { listop(@_, "select") }
2189*0Sstevel@tonic-gatesub pp_read { listop(@_, "read") }
2190*0Sstevel@tonic-gatesub pp_sysopen { listop(@_, "sysopen") }
2191*0Sstevel@tonic-gatesub pp_sysseek { listop(@_, "sysseek") }
2192*0Sstevel@tonic-gatesub pp_sysread { listop(@_, "sysread") }
2193*0Sstevel@tonic-gatesub pp_syswrite { listop(@_, "syswrite") }
2194*0Sstevel@tonic-gatesub pp_send { listop(@_, "send") }
2195*0Sstevel@tonic-gatesub pp_recv { listop(@_, "recv") }
2196*0Sstevel@tonic-gatesub pp_seek { listop(@_, "seek") }
2197*0Sstevel@tonic-gatesub pp_fcntl { listop(@_, "fcntl") }
2198*0Sstevel@tonic-gatesub pp_ioctl { listop(@_, "ioctl") }
2199*0Sstevel@tonic-gatesub pp_flock { maybe_targmy(@_, \&listop, "flock") }
2200*0Sstevel@tonic-gatesub pp_socket { listop(@_, "socket") }
2201*0Sstevel@tonic-gatesub pp_sockpair { listop(@_, "sockpair") }
2202*0Sstevel@tonic-gatesub pp_bind { listop(@_, "bind") }
2203*0Sstevel@tonic-gatesub pp_connect { listop(@_, "connect") }
2204*0Sstevel@tonic-gatesub pp_listen { listop(@_, "listen") }
2205*0Sstevel@tonic-gatesub pp_accept { listop(@_, "accept") }
2206*0Sstevel@tonic-gatesub pp_shutdown { listop(@_, "shutdown") }
2207*0Sstevel@tonic-gatesub pp_gsockopt { listop(@_, "getsockopt") }
2208*0Sstevel@tonic-gatesub pp_ssockopt { listop(@_, "setsockopt") }
2209*0Sstevel@tonic-gatesub pp_chown { maybe_targmy(@_, \&listop, "chown") }
2210*0Sstevel@tonic-gatesub pp_unlink { maybe_targmy(@_, \&listop, "unlink") }
2211*0Sstevel@tonic-gatesub pp_chmod { maybe_targmy(@_, \&listop, "chmod") }
2212*0Sstevel@tonic-gatesub pp_utime { maybe_targmy(@_, \&listop, "utime") }
2213*0Sstevel@tonic-gatesub pp_rename { maybe_targmy(@_, \&listop, "rename") }
2214*0Sstevel@tonic-gatesub pp_link { maybe_targmy(@_, \&listop, "link") }
2215*0Sstevel@tonic-gatesub pp_symlink { maybe_targmy(@_, \&listop, "symlink") }
2216*0Sstevel@tonic-gatesub pp_mkdir { maybe_targmy(@_, \&listop, "mkdir") }
2217*0Sstevel@tonic-gatesub pp_open_dir { listop(@_, "opendir") }
2218*0Sstevel@tonic-gatesub pp_seekdir { listop(@_, "seekdir") }
2219*0Sstevel@tonic-gatesub pp_waitpid { maybe_targmy(@_, \&listop, "waitpid") }
2220*0Sstevel@tonic-gatesub pp_system { maybe_targmy(@_, \&listop, "system") }
2221*0Sstevel@tonic-gatesub pp_exec { maybe_targmy(@_, \&listop, "exec") }
2222*0Sstevel@tonic-gatesub pp_kill { maybe_targmy(@_, \&listop, "kill") }
2223*0Sstevel@tonic-gatesub pp_setpgrp { maybe_targmy(@_, \&listop, "setpgrp") }
2224*0Sstevel@tonic-gatesub pp_getpriority { maybe_targmy(@_, \&listop, "getpriority") }
2225*0Sstevel@tonic-gatesub pp_setpriority { maybe_targmy(@_, \&listop, "setpriority") }
2226*0Sstevel@tonic-gatesub pp_shmget { listop(@_, "shmget") }
2227*0Sstevel@tonic-gatesub pp_shmctl { listop(@_, "shmctl") }
2228*0Sstevel@tonic-gatesub pp_shmread { listop(@_, "shmread") }
2229*0Sstevel@tonic-gatesub pp_shmwrite { listop(@_, "shmwrite") }
2230*0Sstevel@tonic-gatesub pp_msgget { listop(@_, "msgget") }
2231*0Sstevel@tonic-gatesub pp_msgctl { listop(@_, "msgctl") }
2232*0Sstevel@tonic-gatesub pp_msgsnd { listop(@_, "msgsnd") }
2233*0Sstevel@tonic-gatesub pp_msgrcv { listop(@_, "msgrcv") }
2234*0Sstevel@tonic-gatesub pp_semget { listop(@_, "semget") }
2235*0Sstevel@tonic-gatesub pp_semctl { listop(@_, "semctl") }
2236*0Sstevel@tonic-gatesub pp_semop { listop(@_, "semop") }
2237*0Sstevel@tonic-gatesub pp_ghbyaddr { listop(@_, "gethostbyaddr") }
2238*0Sstevel@tonic-gatesub pp_gnbyaddr { listop(@_, "getnetbyaddr") }
2239*0Sstevel@tonic-gatesub pp_gpbynumber { listop(@_, "getprotobynumber") }
2240*0Sstevel@tonic-gatesub pp_gsbyname { listop(@_, "getservbyname") }
2241*0Sstevel@tonic-gatesub pp_gsbyport { listop(@_, "getservbyport") }
2242*0Sstevel@tonic-gatesub pp_syscall { listop(@_, "syscall") }
2243*0Sstevel@tonic-gate
2244*0Sstevel@tonic-gatesub pp_glob {
2245*0Sstevel@tonic-gate    my $self = shift;
2246*0Sstevel@tonic-gate    my($op, $cx) = @_;
2247*0Sstevel@tonic-gate    my $text = $self->dq($op->first->sibling);  # skip pushmark
2248*0Sstevel@tonic-gate    if ($text =~ /^\$?(\w|::|\`)+$/ # could look like a readline
2249*0Sstevel@tonic-gate	or $text =~ /[<>]/) {
2250*0Sstevel@tonic-gate	return 'glob(' . single_delim('qq', '"', $text) . ')';
2251*0Sstevel@tonic-gate    } else {
2252*0Sstevel@tonic-gate	return '<' . $text . '>';
2253*0Sstevel@tonic-gate    }
2254*0Sstevel@tonic-gate}
2255*0Sstevel@tonic-gate
2256*0Sstevel@tonic-gate# Truncate is special because OPf_SPECIAL makes a bareword first arg
2257*0Sstevel@tonic-gate# be a filehandle. This could probably be better fixed in the core
2258*0Sstevel@tonic-gate# by moving the GV lookup into ck_truc.
2259*0Sstevel@tonic-gate
2260*0Sstevel@tonic-gatesub pp_truncate {
2261*0Sstevel@tonic-gate    my $self = shift;
2262*0Sstevel@tonic-gate    my($op, $cx) = @_;
2263*0Sstevel@tonic-gate    my(@exprs);
2264*0Sstevel@tonic-gate    my $parens = ($cx >= 5) || $self->{'parens'};
2265*0Sstevel@tonic-gate    my $kid = $op->first->sibling;
2266*0Sstevel@tonic-gate    my $fh;
2267*0Sstevel@tonic-gate    if ($op->flags & OPf_SPECIAL) {
2268*0Sstevel@tonic-gate	# $kid is an OP_CONST
2269*0Sstevel@tonic-gate	$fh = $self->const_sv($kid)->PV;
2270*0Sstevel@tonic-gate    } else {
2271*0Sstevel@tonic-gate	$fh = $self->deparse($kid, 6);
2272*0Sstevel@tonic-gate        $fh = "+$fh" if not $parens and substr($fh, 0, 1) eq "(";
2273*0Sstevel@tonic-gate    }
2274*0Sstevel@tonic-gate    my $len = $self->deparse($kid->sibling, 6);
2275*0Sstevel@tonic-gate    if ($parens) {
2276*0Sstevel@tonic-gate	return "truncate($fh, $len)";
2277*0Sstevel@tonic-gate    } else {
2278*0Sstevel@tonic-gate	return "truncate $fh, $len";
2279*0Sstevel@tonic-gate    }
2280*0Sstevel@tonic-gate}
2281*0Sstevel@tonic-gate
2282*0Sstevel@tonic-gatesub indirop {
2283*0Sstevel@tonic-gate    my $self = shift;
2284*0Sstevel@tonic-gate    my($op, $cx, $name) = @_;
2285*0Sstevel@tonic-gate    my($expr, @exprs);
2286*0Sstevel@tonic-gate    my $kid = $op->first->sibling;
2287*0Sstevel@tonic-gate    my $indir = "";
2288*0Sstevel@tonic-gate    if ($op->flags & OPf_STACKED) {
2289*0Sstevel@tonic-gate	$indir = $kid;
2290*0Sstevel@tonic-gate	$indir = $indir->first; # skip rv2gv
2291*0Sstevel@tonic-gate	if (is_scope($indir)) {
2292*0Sstevel@tonic-gate	    $indir = "{" . $self->deparse($indir, 0) . "}";
2293*0Sstevel@tonic-gate	    $indir = "{;}" if $indir eq "{}";
2294*0Sstevel@tonic-gate	} elsif ($indir->name eq "const" && $indir->private & OPpCONST_BARE) {
2295*0Sstevel@tonic-gate	    $indir = $self->const_sv($indir)->PV;
2296*0Sstevel@tonic-gate	} else {
2297*0Sstevel@tonic-gate	    $indir = $self->deparse($indir, 24);
2298*0Sstevel@tonic-gate	}
2299*0Sstevel@tonic-gate	$indir = $indir . " ";
2300*0Sstevel@tonic-gate	$kid = $kid->sibling;
2301*0Sstevel@tonic-gate    }
2302*0Sstevel@tonic-gate    if ($name eq "sort" && $op->private & (OPpSORT_NUMERIC | OPpSORT_INTEGER)) {
2303*0Sstevel@tonic-gate	$indir = ($op->private & OPpSORT_REVERSE) ? '{$b <=> $a} '
2304*0Sstevel@tonic-gate						  : '{$a <=> $b} ';
2305*0Sstevel@tonic-gate    }
2306*0Sstevel@tonic-gate    elsif ($name eq "sort" && $op->private & OPpSORT_REVERSE) {
2307*0Sstevel@tonic-gate	$indir = '{$b cmp $a} ';
2308*0Sstevel@tonic-gate    }
2309*0Sstevel@tonic-gate    for (; !null($kid); $kid = $kid->sibling) {
2310*0Sstevel@tonic-gate	$expr = $self->deparse($kid, 6);
2311*0Sstevel@tonic-gate	push @exprs, $expr;
2312*0Sstevel@tonic-gate    }
2313*0Sstevel@tonic-gate    if ($name eq "sort" && ($op->private & OPpSORT_INPLACE)) {
2314*0Sstevel@tonic-gate	return "$exprs[0] = sort $indir $exprs[0]";
2315*0Sstevel@tonic-gate    }
2316*0Sstevel@tonic-gate
2317*0Sstevel@tonic-gate    my $args = $indir . join(", ", @exprs);
2318*0Sstevel@tonic-gate    if ($indir ne "" and $name eq "sort") {
2319*0Sstevel@tonic-gate	# We don't want to say "sort(f 1, 2, 3)", since perl -w will
2320*0Sstevel@tonic-gate	# give bareword warnings in that case. Therefore if context
2321*0Sstevel@tonic-gate	# requires, we'll put parens around the outside "(sort f 1, 2,
2322*0Sstevel@tonic-gate	# 3)". Unfortunately, we'll currently think the parens are
2323*0Sstevel@tonic-gate	# neccessary more often that they really are, because we don't
2324*0Sstevel@tonic-gate	# distinguish which side of an assignment we're on.
2325*0Sstevel@tonic-gate	if ($cx >= 5) {
2326*0Sstevel@tonic-gate	    return "($name $args)";
2327*0Sstevel@tonic-gate	} else {
2328*0Sstevel@tonic-gate	    return "$name $args";
2329*0Sstevel@tonic-gate	}
2330*0Sstevel@tonic-gate    } else {
2331*0Sstevel@tonic-gate	return $self->maybe_parens_func($name, $args, $cx, 5);
2332*0Sstevel@tonic-gate    }
2333*0Sstevel@tonic-gate
2334*0Sstevel@tonic-gate}
2335*0Sstevel@tonic-gate
2336*0Sstevel@tonic-gatesub pp_prtf { indirop(@_, "printf") }
2337*0Sstevel@tonic-gatesub pp_print { indirop(@_, "print") }
2338*0Sstevel@tonic-gatesub pp_sort { indirop(@_, "sort") }
2339*0Sstevel@tonic-gate
2340*0Sstevel@tonic-gatesub mapop {
2341*0Sstevel@tonic-gate    my $self = shift;
2342*0Sstevel@tonic-gate    my($op, $cx, $name) = @_;
2343*0Sstevel@tonic-gate    my($expr, @exprs);
2344*0Sstevel@tonic-gate    my $kid = $op->first; # this is the (map|grep)start
2345*0Sstevel@tonic-gate    $kid = $kid->first->sibling; # skip a pushmark
2346*0Sstevel@tonic-gate    my $code = $kid->first; # skip a null
2347*0Sstevel@tonic-gate    if (is_scope $code) {
2348*0Sstevel@tonic-gate	$code = "{" . $self->deparse($code, 0) . "} ";
2349*0Sstevel@tonic-gate    } else {
2350*0Sstevel@tonic-gate	$code = $self->deparse($code, 24) . ", ";
2351*0Sstevel@tonic-gate    }
2352*0Sstevel@tonic-gate    $kid = $kid->sibling;
2353*0Sstevel@tonic-gate    for (; !null($kid); $kid = $kid->sibling) {
2354*0Sstevel@tonic-gate	$expr = $self->deparse($kid, 6);
2355*0Sstevel@tonic-gate	push @exprs, $expr if defined $expr;
2356*0Sstevel@tonic-gate    }
2357*0Sstevel@tonic-gate    return $self->maybe_parens_func($name, $code . join(", ", @exprs), $cx, 5);
2358*0Sstevel@tonic-gate}
2359*0Sstevel@tonic-gate
2360*0Sstevel@tonic-gatesub pp_mapwhile { mapop(@_, "map") }
2361*0Sstevel@tonic-gatesub pp_grepwhile { mapop(@_, "grep") }
2362*0Sstevel@tonic-gate
2363*0Sstevel@tonic-gatesub pp_list {
2364*0Sstevel@tonic-gate    my $self = shift;
2365*0Sstevel@tonic-gate    my($op, $cx) = @_;
2366*0Sstevel@tonic-gate    my($expr, @exprs);
2367*0Sstevel@tonic-gate    my $kid = $op->first->sibling; # skip pushmark
2368*0Sstevel@tonic-gate    my $lop;
2369*0Sstevel@tonic-gate    my $local = "either"; # could be local(...), my(...) or our(...)
2370*0Sstevel@tonic-gate    for ($lop = $kid; !null($lop); $lop = $lop->sibling) {
2371*0Sstevel@tonic-gate	# This assumes that no other private flags equal 128, and that
2372*0Sstevel@tonic-gate	# OPs that store things other than flags in their op_private,
2373*0Sstevel@tonic-gate	# like OP_AELEMFAST, won't be immediate children of a list.
2374*0Sstevel@tonic-gate	#
2375*0Sstevel@tonic-gate	# OP_ENTERSUB can break this logic, so check for it.
2376*0Sstevel@tonic-gate	# I suspect that open and exit can too.
2377*0Sstevel@tonic-gate
2378*0Sstevel@tonic-gate	if (!($lop->private & (OPpLVAL_INTRO|OPpOUR_INTRO)
2379*0Sstevel@tonic-gate		or $lop->name eq "undef")
2380*0Sstevel@tonic-gate	    or $lop->name eq "entersub"
2381*0Sstevel@tonic-gate	    or $lop->name eq "exit"
2382*0Sstevel@tonic-gate	    or $lop->name eq "open")
2383*0Sstevel@tonic-gate	{
2384*0Sstevel@tonic-gate	    $local = ""; # or not
2385*0Sstevel@tonic-gate	    last;
2386*0Sstevel@tonic-gate	}
2387*0Sstevel@tonic-gate	if ($lop->name =~ /^pad[ash]v$/) { # my()
2388*0Sstevel@tonic-gate	    ($local = "", last) if $local eq "local" || $local eq "our";
2389*0Sstevel@tonic-gate	    $local = "my";
2390*0Sstevel@tonic-gate	} elsif ($lop->name =~ /^(gv|rv2)[ash]v$/
2391*0Sstevel@tonic-gate			&& $lop->private & OPpOUR_INTRO
2392*0Sstevel@tonic-gate		or $lop->name eq "null" && $lop->first->name eq "gvsv"
2393*0Sstevel@tonic-gate			&& $lop->first->private & OPpOUR_INTRO) { # our()
2394*0Sstevel@tonic-gate	    ($local = "", last) if $local eq "my" || $local eq "local";
2395*0Sstevel@tonic-gate	    $local = "our";
2396*0Sstevel@tonic-gate	} elsif ($lop->name ne "undef") { # local()
2397*0Sstevel@tonic-gate	    ($local = "", last) if $local eq "my" || $local eq "our";
2398*0Sstevel@tonic-gate	    $local = "local";
2399*0Sstevel@tonic-gate	}
2400*0Sstevel@tonic-gate    }
2401*0Sstevel@tonic-gate    $local = "" if $local eq "either"; # no point if it's all undefs
2402*0Sstevel@tonic-gate    return $self->deparse($kid, $cx) if null $kid->sibling and not $local;
2403*0Sstevel@tonic-gate    for (; !null($kid); $kid = $kid->sibling) {
2404*0Sstevel@tonic-gate	if ($local) {
2405*0Sstevel@tonic-gate	    if (class($kid) eq "UNOP" and $kid->first->name eq "gvsv") {
2406*0Sstevel@tonic-gate		$lop = $kid->first;
2407*0Sstevel@tonic-gate	    } else {
2408*0Sstevel@tonic-gate		$lop = $kid;
2409*0Sstevel@tonic-gate	    }
2410*0Sstevel@tonic-gate	    $self->{'avoid_local'}{$$lop}++;
2411*0Sstevel@tonic-gate	    $expr = $self->deparse($kid, 6);
2412*0Sstevel@tonic-gate	    delete $self->{'avoid_local'}{$$lop};
2413*0Sstevel@tonic-gate	} else {
2414*0Sstevel@tonic-gate	    $expr = $self->deparse($kid, 6);
2415*0Sstevel@tonic-gate	}
2416*0Sstevel@tonic-gate	push @exprs, $expr;
2417*0Sstevel@tonic-gate    }
2418*0Sstevel@tonic-gate    if ($local) {
2419*0Sstevel@tonic-gate	return "$local(" . join(", ", @exprs) . ")";
2420*0Sstevel@tonic-gate    } else {
2421*0Sstevel@tonic-gate	return $self->maybe_parens( join(", ", @exprs), $cx, 6);
2422*0Sstevel@tonic-gate    }
2423*0Sstevel@tonic-gate}
2424*0Sstevel@tonic-gate
2425*0Sstevel@tonic-gatesub is_ifelse_cont {
2426*0Sstevel@tonic-gate    my $op = shift;
2427*0Sstevel@tonic-gate    return ($op->name eq "null" and class($op) eq "UNOP"
2428*0Sstevel@tonic-gate	    and $op->first->name =~ /^(and|cond_expr)$/
2429*0Sstevel@tonic-gate	    and is_scope($op->first->first->sibling));
2430*0Sstevel@tonic-gate}
2431*0Sstevel@tonic-gate
2432*0Sstevel@tonic-gatesub pp_cond_expr {
2433*0Sstevel@tonic-gate    my $self = shift;
2434*0Sstevel@tonic-gate    my($op, $cx) = @_;
2435*0Sstevel@tonic-gate    my $cond = $op->first;
2436*0Sstevel@tonic-gate    my $true = $cond->sibling;
2437*0Sstevel@tonic-gate    my $false = $true->sibling;
2438*0Sstevel@tonic-gate    my $cuddle = $self->{'cuddle'};
2439*0Sstevel@tonic-gate    unless ($cx < 1 and (is_scope($true) and $true->name ne "null") and
2440*0Sstevel@tonic-gate	    (is_scope($false) || is_ifelse_cont($false))
2441*0Sstevel@tonic-gate	    and $self->{'expand'} < 7) {
2442*0Sstevel@tonic-gate	$cond = $self->deparse($cond, 8);
2443*0Sstevel@tonic-gate	$true = $self->deparse($true, 8);
2444*0Sstevel@tonic-gate	$false = $self->deparse($false, 8);
2445*0Sstevel@tonic-gate	return $self->maybe_parens("$cond ? $true : $false", $cx, 8);
2446*0Sstevel@tonic-gate    }
2447*0Sstevel@tonic-gate
2448*0Sstevel@tonic-gate    $cond = $self->deparse($cond, 1);
2449*0Sstevel@tonic-gate    $true = $self->deparse($true, 0);
2450*0Sstevel@tonic-gate    my $head = "if ($cond) {\n\t$true\n\b}";
2451*0Sstevel@tonic-gate    my @elsifs;
2452*0Sstevel@tonic-gate    while (!null($false) and is_ifelse_cont($false)) {
2453*0Sstevel@tonic-gate	my $newop = $false->first;
2454*0Sstevel@tonic-gate	my $newcond = $newop->first;
2455*0Sstevel@tonic-gate	my $newtrue = $newcond->sibling;
2456*0Sstevel@tonic-gate	$false = $newtrue->sibling; # last in chain is OP_AND => no else
2457*0Sstevel@tonic-gate	$newcond = $self->deparse($newcond, 1);
2458*0Sstevel@tonic-gate	$newtrue = $self->deparse($newtrue, 0);
2459*0Sstevel@tonic-gate	push @elsifs, "elsif ($newcond) {\n\t$newtrue\n\b}";
2460*0Sstevel@tonic-gate    }
2461*0Sstevel@tonic-gate    if (!null($false)) {
2462*0Sstevel@tonic-gate	$false = $cuddle . "else {\n\t" .
2463*0Sstevel@tonic-gate	  $self->deparse($false, 0) . "\n\b}\cK";
2464*0Sstevel@tonic-gate    } else {
2465*0Sstevel@tonic-gate	$false = "\cK";
2466*0Sstevel@tonic-gate    }
2467*0Sstevel@tonic-gate    return $head . join($cuddle, "", @elsifs) . $false;
2468*0Sstevel@tonic-gate}
2469*0Sstevel@tonic-gate
2470*0Sstevel@tonic-gatesub loop_common {
2471*0Sstevel@tonic-gate    my $self = shift;
2472*0Sstevel@tonic-gate    my($op, $cx, $init) = @_;
2473*0Sstevel@tonic-gate    my $enter = $op->first;
2474*0Sstevel@tonic-gate    my $kid = $enter->sibling;
2475*0Sstevel@tonic-gate    local(@$self{qw'curstash warnings hints'})
2476*0Sstevel@tonic-gate		= @$self{qw'curstash warnings hints'};
2477*0Sstevel@tonic-gate    my $head = "";
2478*0Sstevel@tonic-gate    my $bare = 0;
2479*0Sstevel@tonic-gate    my $body;
2480*0Sstevel@tonic-gate    my $cond = undef;
2481*0Sstevel@tonic-gate    if ($kid->name eq "lineseq") { # bare or infinite loop
2482*0Sstevel@tonic-gate	if ($kid->last->name eq "unstack") { # infinite
2483*0Sstevel@tonic-gate	    $head = "while (1) "; # Can't use for(;;) if there's a continue
2484*0Sstevel@tonic-gate	    $cond = "";
2485*0Sstevel@tonic-gate	} else {
2486*0Sstevel@tonic-gate	    $bare = 1;
2487*0Sstevel@tonic-gate	}
2488*0Sstevel@tonic-gate	$body = $kid;
2489*0Sstevel@tonic-gate    } elsif ($enter->name eq "enteriter") { # foreach
2490*0Sstevel@tonic-gate	my $ary = $enter->first->sibling; # first was pushmark
2491*0Sstevel@tonic-gate	my $var = $ary->sibling;
2492*0Sstevel@tonic-gate	if ($enter->flags & OPf_STACKED
2493*0Sstevel@tonic-gate	    and not null $ary->first->sibling->sibling)
2494*0Sstevel@tonic-gate	{
2495*0Sstevel@tonic-gate	    $ary = $self->deparse($ary->first->sibling, 9) . " .. " .
2496*0Sstevel@tonic-gate	      $self->deparse($ary->first->sibling->sibling, 9);
2497*0Sstevel@tonic-gate	} else {
2498*0Sstevel@tonic-gate	    $ary = $self->deparse($ary, 1);
2499*0Sstevel@tonic-gate	}
2500*0Sstevel@tonic-gate	if (null $var) {
2501*0Sstevel@tonic-gate	    if ($enter->flags & OPf_SPECIAL) { # thread special var
2502*0Sstevel@tonic-gate		$var = $self->pp_threadsv($enter, 1);
2503*0Sstevel@tonic-gate	    } else { # regular my() variable
2504*0Sstevel@tonic-gate		$var = $self->pp_padsv($enter, 1);
2505*0Sstevel@tonic-gate	    }
2506*0Sstevel@tonic-gate	} elsif ($var->name eq "rv2gv") {
2507*0Sstevel@tonic-gate	    $var = $self->pp_rv2sv($var, 1);
2508*0Sstevel@tonic-gate	    if ($enter->private & OPpOUR_INTRO) {
2509*0Sstevel@tonic-gate		# our declarations don't have package names
2510*0Sstevel@tonic-gate		$var =~ s/^(.).*::/$1/;
2511*0Sstevel@tonic-gate		$var = "our $var";
2512*0Sstevel@tonic-gate	    }
2513*0Sstevel@tonic-gate	} elsif ($var->name eq "gv") {
2514*0Sstevel@tonic-gate	    $var = "\$" . $self->deparse($var, 1);
2515*0Sstevel@tonic-gate	}
2516*0Sstevel@tonic-gate	$head = "foreach $var ($ary) ";
2517*0Sstevel@tonic-gate	$body = $kid->first->first->sibling; # skip OP_AND and OP_ITER
2518*0Sstevel@tonic-gate    } elsif ($kid->name eq "null") { # while/until
2519*0Sstevel@tonic-gate	$kid = $kid->first;
2520*0Sstevel@tonic-gate	my $name = {"and" => "while", "or" => "until"}->{$kid->name};
2521*0Sstevel@tonic-gate	$cond = $self->deparse($kid->first, 1);
2522*0Sstevel@tonic-gate	$head = "$name ($cond) ";
2523*0Sstevel@tonic-gate	$body = $kid->first->sibling;
2524*0Sstevel@tonic-gate    } elsif ($kid->name eq "stub") { # bare and empty
2525*0Sstevel@tonic-gate	return "{;}"; # {} could be a hashref
2526*0Sstevel@tonic-gate    }
2527*0Sstevel@tonic-gate    # If there isn't a continue block, then the next pointer for the loop
2528*0Sstevel@tonic-gate    # will point to the unstack, which is kid's last child, except
2529*0Sstevel@tonic-gate    # in a bare loop, when it will point to the leaveloop. When neither of
2530*0Sstevel@tonic-gate    # these conditions hold, then the second-to-last child is the continue
2531*0Sstevel@tonic-gate    # block (or the last in a bare loop).
2532*0Sstevel@tonic-gate    my $cont_start = $enter->nextop;
2533*0Sstevel@tonic-gate    my $cont;
2534*0Sstevel@tonic-gate    if ($$cont_start != $$op && ${$cont_start} != ${$body->last}) {
2535*0Sstevel@tonic-gate	if ($bare) {
2536*0Sstevel@tonic-gate	    $cont = $body->last;
2537*0Sstevel@tonic-gate	} else {
2538*0Sstevel@tonic-gate	    $cont = $body->first;
2539*0Sstevel@tonic-gate	    while (!null($cont->sibling->sibling)) {
2540*0Sstevel@tonic-gate		$cont = $cont->sibling;
2541*0Sstevel@tonic-gate	    }
2542*0Sstevel@tonic-gate	}
2543*0Sstevel@tonic-gate	my $state = $body->first;
2544*0Sstevel@tonic-gate	my $cuddle = $self->{'cuddle'};
2545*0Sstevel@tonic-gate	my @states;
2546*0Sstevel@tonic-gate	for (; $$state != $$cont; $state = $state->sibling) {
2547*0Sstevel@tonic-gate	    push @states, $state;
2548*0Sstevel@tonic-gate	}
2549*0Sstevel@tonic-gate	$body = $self->lineseq(undef, @states);
2550*0Sstevel@tonic-gate	if (defined $cond and not is_scope $cont and $self->{'expand'} < 3) {
2551*0Sstevel@tonic-gate	    $head = "for ($init; $cond; " . $self->deparse($cont, 1) .") ";
2552*0Sstevel@tonic-gate	    $cont = "\cK";
2553*0Sstevel@tonic-gate	} else {
2554*0Sstevel@tonic-gate	    $cont = $cuddle . "continue {\n\t" .
2555*0Sstevel@tonic-gate	      $self->deparse($cont, 0) . "\n\b}\cK";
2556*0Sstevel@tonic-gate	}
2557*0Sstevel@tonic-gate    } else {
2558*0Sstevel@tonic-gate	return "" if !defined $body;
2559*0Sstevel@tonic-gate	if (length $init) {
2560*0Sstevel@tonic-gate	    $head = "for ($init; $cond;) ";
2561*0Sstevel@tonic-gate	}
2562*0Sstevel@tonic-gate	$cont = "\cK";
2563*0Sstevel@tonic-gate	$body = $self->deparse($body, 0);
2564*0Sstevel@tonic-gate    }
2565*0Sstevel@tonic-gate    $body =~ s/;?$/;\n/;
2566*0Sstevel@tonic-gate
2567*0Sstevel@tonic-gate    return $head . "{\n\t" . $body . "\b}" . $cont;
2568*0Sstevel@tonic-gate}
2569*0Sstevel@tonic-gate
2570*0Sstevel@tonic-gatesub pp_leaveloop { loop_common(@_, "") }
2571*0Sstevel@tonic-gate
2572*0Sstevel@tonic-gatesub for_loop {
2573*0Sstevel@tonic-gate    my $self = shift;
2574*0Sstevel@tonic-gate    my($op, $cx) = @_;
2575*0Sstevel@tonic-gate    my $init = $self->deparse($op, 1);
2576*0Sstevel@tonic-gate    return $self->loop_common($op->sibling->first->sibling, $cx, $init);
2577*0Sstevel@tonic-gate}
2578*0Sstevel@tonic-gate
2579*0Sstevel@tonic-gatesub pp_leavetry {
2580*0Sstevel@tonic-gate    my $self = shift;
2581*0Sstevel@tonic-gate    return "eval {\n\t" . $self->pp_leave(@_) . "\n\b}";
2582*0Sstevel@tonic-gate}
2583*0Sstevel@tonic-gate
2584*0Sstevel@tonic-gateBEGIN { eval "sub OP_CONST () {" . opnumber("const") . "}" }
2585*0Sstevel@tonic-gateBEGIN { eval "sub OP_STRINGIFY () {" . opnumber("stringify") . "}" }
2586*0Sstevel@tonic-gateBEGIN { eval "sub OP_RV2SV () {" . opnumber("rv2sv") . "}" }
2587*0Sstevel@tonic-gateBEGIN { eval "sub OP_LIST () {" . opnumber("list") . "}" }
2588*0Sstevel@tonic-gate
2589*0Sstevel@tonic-gatesub pp_null {
2590*0Sstevel@tonic-gate    my $self = shift;
2591*0Sstevel@tonic-gate    my($op, $cx) = @_;
2592*0Sstevel@tonic-gate    if (class($op) eq "OP") {
2593*0Sstevel@tonic-gate	# old value is lost
2594*0Sstevel@tonic-gate	return $self->{'ex_const'} if $op->targ == OP_CONST;
2595*0Sstevel@tonic-gate    } elsif ($op->first->name eq "pushmark") {
2596*0Sstevel@tonic-gate	return $self->pp_list($op, $cx);
2597*0Sstevel@tonic-gate    } elsif ($op->first->name eq "enter") {
2598*0Sstevel@tonic-gate	return $self->pp_leave($op, $cx);
2599*0Sstevel@tonic-gate    } elsif ($op->targ == OP_STRINGIFY) {
2600*0Sstevel@tonic-gate	return $self->dquote($op, $cx);
2601*0Sstevel@tonic-gate    } elsif (!null($op->first->sibling) and
2602*0Sstevel@tonic-gate	     $op->first->sibling->name eq "readline" and
2603*0Sstevel@tonic-gate	     $op->first->sibling->flags & OPf_STACKED) {
2604*0Sstevel@tonic-gate	return $self->maybe_parens($self->deparse($op->first, 7) . " = "
2605*0Sstevel@tonic-gate				   . $self->deparse($op->first->sibling, 7),
2606*0Sstevel@tonic-gate				   $cx, 7);
2607*0Sstevel@tonic-gate    } elsif (!null($op->first->sibling) and
2608*0Sstevel@tonic-gate	     $op->first->sibling->name eq "trans" and
2609*0Sstevel@tonic-gate	     $op->first->sibling->flags & OPf_STACKED) {
2610*0Sstevel@tonic-gate	return $self->maybe_parens($self->deparse($op->first, 20) . " =~ "
2611*0Sstevel@tonic-gate				   . $self->deparse($op->first->sibling, 20),
2612*0Sstevel@tonic-gate				   $cx, 20);
2613*0Sstevel@tonic-gate    } elsif ($op->flags & OPf_SPECIAL && $cx < 1 && !$op->targ) {
2614*0Sstevel@tonic-gate	return "do {\n\t". $self->deparse($op->first, $cx) ."\n\b};";
2615*0Sstevel@tonic-gate    } elsif (!null($op->first->sibling) and
2616*0Sstevel@tonic-gate	     $op->first->sibling->name eq "null" and
2617*0Sstevel@tonic-gate	     class($op->first->sibling) eq "UNOP" and
2618*0Sstevel@tonic-gate	     $op->first->sibling->first->flags & OPf_STACKED and
2619*0Sstevel@tonic-gate	     $op->first->sibling->first->name eq "rcatline") {
2620*0Sstevel@tonic-gate	return $self->maybe_parens($self->deparse($op->first, 18) . " .= "
2621*0Sstevel@tonic-gate				   . $self->deparse($op->first->sibling, 18),
2622*0Sstevel@tonic-gate				   $cx, 18);
2623*0Sstevel@tonic-gate    } else {
2624*0Sstevel@tonic-gate	return $self->deparse($op->first, $cx);
2625*0Sstevel@tonic-gate    }
2626*0Sstevel@tonic-gate}
2627*0Sstevel@tonic-gate
2628*0Sstevel@tonic-gatesub padname {
2629*0Sstevel@tonic-gate    my $self = shift;
2630*0Sstevel@tonic-gate    my $targ = shift;
2631*0Sstevel@tonic-gate    return $self->padname_sv($targ)->PVX;
2632*0Sstevel@tonic-gate}
2633*0Sstevel@tonic-gate
2634*0Sstevel@tonic-gatesub padany {
2635*0Sstevel@tonic-gate    my $self = shift;
2636*0Sstevel@tonic-gate    my $op = shift;
2637*0Sstevel@tonic-gate    return substr($self->padname($op->targ), 1); # skip $/@/%
2638*0Sstevel@tonic-gate}
2639*0Sstevel@tonic-gate
2640*0Sstevel@tonic-gatesub pp_padsv {
2641*0Sstevel@tonic-gate    my $self = shift;
2642*0Sstevel@tonic-gate    my($op, $cx) = @_;
2643*0Sstevel@tonic-gate    return $self->maybe_my($op, $cx, $self->padname($op->targ));
2644*0Sstevel@tonic-gate}
2645*0Sstevel@tonic-gate
2646*0Sstevel@tonic-gatesub pp_padav { pp_padsv(@_) }
2647*0Sstevel@tonic-gatesub pp_padhv { pp_padsv(@_) }
2648*0Sstevel@tonic-gate
2649*0Sstevel@tonic-gatemy @threadsv_names;
2650*0Sstevel@tonic-gate
2651*0Sstevel@tonic-gateBEGIN {
2652*0Sstevel@tonic-gate    @threadsv_names = ("_", "1", "2", "3", "4", "5", "6", "7", "8", "9",
2653*0Sstevel@tonic-gate		       "&", "`", "'", "+", "/", ".", ",", "\\", '"', ";",
2654*0Sstevel@tonic-gate		       "^", "-", "%", "=", "|", "~", ":", "^A", "^E",
2655*0Sstevel@tonic-gate		       "!", "@");
2656*0Sstevel@tonic-gate}
2657*0Sstevel@tonic-gate
2658*0Sstevel@tonic-gatesub pp_threadsv {
2659*0Sstevel@tonic-gate    my $self = shift;
2660*0Sstevel@tonic-gate    my($op, $cx) = @_;
2661*0Sstevel@tonic-gate    return $self->maybe_local($op, $cx, "\$" .  $threadsv_names[$op->targ]);
2662*0Sstevel@tonic-gate}
2663*0Sstevel@tonic-gate
2664*0Sstevel@tonic-gatesub gv_or_padgv {
2665*0Sstevel@tonic-gate    my $self = shift;
2666*0Sstevel@tonic-gate    my $op = shift;
2667*0Sstevel@tonic-gate    if (class($op) eq "PADOP") {
2668*0Sstevel@tonic-gate	return $self->padval($op->padix);
2669*0Sstevel@tonic-gate    } else { # class($op) eq "SVOP"
2670*0Sstevel@tonic-gate	return $op->gv;
2671*0Sstevel@tonic-gate    }
2672*0Sstevel@tonic-gate}
2673*0Sstevel@tonic-gate
2674*0Sstevel@tonic-gatesub pp_gvsv {
2675*0Sstevel@tonic-gate    my $self = shift;
2676*0Sstevel@tonic-gate    my($op, $cx) = @_;
2677*0Sstevel@tonic-gate    my $gv = $self->gv_or_padgv($op);
2678*0Sstevel@tonic-gate    return $self->maybe_local($op, $cx, $self->stash_variable("\$",
2679*0Sstevel@tonic-gate				 $self->gv_name($gv)));
2680*0Sstevel@tonic-gate}
2681*0Sstevel@tonic-gate
2682*0Sstevel@tonic-gatesub pp_gv {
2683*0Sstevel@tonic-gate    my $self = shift;
2684*0Sstevel@tonic-gate    my($op, $cx) = @_;
2685*0Sstevel@tonic-gate    my $gv = $self->gv_or_padgv($op);
2686*0Sstevel@tonic-gate    return $self->gv_name($gv);
2687*0Sstevel@tonic-gate}
2688*0Sstevel@tonic-gate
2689*0Sstevel@tonic-gatesub pp_aelemfast {
2690*0Sstevel@tonic-gate    my $self = shift;
2691*0Sstevel@tonic-gate    my($op, $cx) = @_;
2692*0Sstevel@tonic-gate    my $name;
2693*0Sstevel@tonic-gate    if ($op->flags & OPf_SPECIAL) { # optimised PADAV
2694*0Sstevel@tonic-gate	$name = $self->padname($op->targ);
2695*0Sstevel@tonic-gate	$name =~ s/^@/\$/;
2696*0Sstevel@tonic-gate    }
2697*0Sstevel@tonic-gate    else {
2698*0Sstevel@tonic-gate	my $gv = $self->gv_or_padgv($op);
2699*0Sstevel@tonic-gate	$name = $self->gv_name($gv);
2700*0Sstevel@tonic-gate	$name = $self->{'curstash'}."::$name"
2701*0Sstevel@tonic-gate	    if $name !~ /::/ && $self->lex_in_scope('@'.$name);
2702*0Sstevel@tonic-gate	$name = '$' . $name;
2703*0Sstevel@tonic-gate    }
2704*0Sstevel@tonic-gate
2705*0Sstevel@tonic-gate    return $name . "[" .  ($op->private + $self->{'arybase'}) . "]";
2706*0Sstevel@tonic-gate}
2707*0Sstevel@tonic-gate
2708*0Sstevel@tonic-gatesub rv2x {
2709*0Sstevel@tonic-gate    my $self = shift;
2710*0Sstevel@tonic-gate    my($op, $cx, $type) = @_;
2711*0Sstevel@tonic-gate
2712*0Sstevel@tonic-gate    if (class($op) eq 'NULL' || !$op->can("first")) {
2713*0Sstevel@tonic-gate	carp("Unexpected op in pp_rv2x");
2714*0Sstevel@tonic-gate	return 'XXX';
2715*0Sstevel@tonic-gate    }
2716*0Sstevel@tonic-gate    my $kid = $op->first;
2717*0Sstevel@tonic-gate    if ($kid->name eq "gv") {
2718*0Sstevel@tonic-gate	return $self->stash_variable($type, $self->deparse($kid, 0));
2719*0Sstevel@tonic-gate    } elsif (is_scalar $kid) {
2720*0Sstevel@tonic-gate	my $str = $self->deparse($kid, 0);
2721*0Sstevel@tonic-gate	if ($str =~ /^\$([^\w\d])\z/) {
2722*0Sstevel@tonic-gate	    # "$$+" isn't a legal way to write the scalar dereference
2723*0Sstevel@tonic-gate	    # of $+, since the lexer can't tell you aren't trying to
2724*0Sstevel@tonic-gate	    # do something like "$$ + 1" to get one more than your
2725*0Sstevel@tonic-gate	    # PID. Either "${$+}" or "$${+}" are workable
2726*0Sstevel@tonic-gate	    # disambiguations, but if the programmer did the former,
2727*0Sstevel@tonic-gate	    # they'd be in the "else" clause below rather than here.
2728*0Sstevel@tonic-gate	    # It's not clear if this should somehow be unified with
2729*0Sstevel@tonic-gate	    # the code in dq and re_dq that also adds lexer
2730*0Sstevel@tonic-gate	    # disambiguation braces.
2731*0Sstevel@tonic-gate	    $str = '$' . "{$1}"; #'
2732*0Sstevel@tonic-gate	}
2733*0Sstevel@tonic-gate	return $type . $str;
2734*0Sstevel@tonic-gate    } else {
2735*0Sstevel@tonic-gate	return $type . "{" . $self->deparse($kid, 0) . "}";
2736*0Sstevel@tonic-gate    }
2737*0Sstevel@tonic-gate}
2738*0Sstevel@tonic-gate
2739*0Sstevel@tonic-gatesub pp_rv2sv { maybe_local(@_, rv2x(@_, "\$")) }
2740*0Sstevel@tonic-gatesub pp_rv2hv { maybe_local(@_, rv2x(@_, "%")) }
2741*0Sstevel@tonic-gatesub pp_rv2gv { maybe_local(@_, rv2x(@_, "*")) }
2742*0Sstevel@tonic-gate
2743*0Sstevel@tonic-gate# skip rv2av
2744*0Sstevel@tonic-gatesub pp_av2arylen {
2745*0Sstevel@tonic-gate    my $self = shift;
2746*0Sstevel@tonic-gate    my($op, $cx) = @_;
2747*0Sstevel@tonic-gate    if ($op->first->name eq "padav") {
2748*0Sstevel@tonic-gate	return $self->maybe_local($op, $cx, '$#' . $self->padany($op->first));
2749*0Sstevel@tonic-gate    } else {
2750*0Sstevel@tonic-gate	return $self->maybe_local($op, $cx,
2751*0Sstevel@tonic-gate				  $self->rv2x($op->first, $cx, '$#'));
2752*0Sstevel@tonic-gate    }
2753*0Sstevel@tonic-gate}
2754*0Sstevel@tonic-gate
2755*0Sstevel@tonic-gate# skip down to the old, ex-rv2cv
2756*0Sstevel@tonic-gatesub pp_rv2cv {
2757*0Sstevel@tonic-gate    my ($self, $op, $cx) = @_;
2758*0Sstevel@tonic-gate    if (!null($op->first) && $op->first->name eq 'null' &&
2759*0Sstevel@tonic-gate	$op->first->targ eq OP_LIST)
2760*0Sstevel@tonic-gate    {
2761*0Sstevel@tonic-gate	return $self->rv2x($op->first->first->sibling, $cx, "&")
2762*0Sstevel@tonic-gate    }
2763*0Sstevel@tonic-gate    else {
2764*0Sstevel@tonic-gate	return $self->rv2x($op, $cx, "")
2765*0Sstevel@tonic-gate    }
2766*0Sstevel@tonic-gate}
2767*0Sstevel@tonic-gate
2768*0Sstevel@tonic-gatesub list_const {
2769*0Sstevel@tonic-gate    my $self = shift;
2770*0Sstevel@tonic-gate    my($cx, @list) = @_;
2771*0Sstevel@tonic-gate    my @a = map $self->const($_, 6), @list;
2772*0Sstevel@tonic-gate    if (@a == 0) {
2773*0Sstevel@tonic-gate	return "()";
2774*0Sstevel@tonic-gate    } elsif (@a == 1) {
2775*0Sstevel@tonic-gate	return $a[0];
2776*0Sstevel@tonic-gate    } elsif ( @a > 2 and !grep(!/^-?\d+$/, @a)) {
2777*0Sstevel@tonic-gate	# collapse (-1,0,1,2) into (-1..2)
2778*0Sstevel@tonic-gate	my ($s, $e) = @a[0,-1];
2779*0Sstevel@tonic-gate	my $i = $s;
2780*0Sstevel@tonic-gate	return $self->maybe_parens("$s..$e", $cx, 9)
2781*0Sstevel@tonic-gate	  unless grep $i++ != $_, @a;
2782*0Sstevel@tonic-gate    }
2783*0Sstevel@tonic-gate    return $self->maybe_parens(join(", ", @a), $cx, 6);
2784*0Sstevel@tonic-gate}
2785*0Sstevel@tonic-gate
2786*0Sstevel@tonic-gatesub pp_rv2av {
2787*0Sstevel@tonic-gate    my $self = shift;
2788*0Sstevel@tonic-gate    my($op, $cx) = @_;
2789*0Sstevel@tonic-gate    my $kid = $op->first;
2790*0Sstevel@tonic-gate    if ($kid->name eq "const") { # constant list
2791*0Sstevel@tonic-gate	my $av = $self->const_sv($kid);
2792*0Sstevel@tonic-gate	return $self->list_const($cx, $av->ARRAY);
2793*0Sstevel@tonic-gate    } else {
2794*0Sstevel@tonic-gate	return $self->maybe_local($op, $cx, $self->rv2x($op, $cx, "\@"));
2795*0Sstevel@tonic-gate    }
2796*0Sstevel@tonic-gate }
2797*0Sstevel@tonic-gate
2798*0Sstevel@tonic-gatesub is_subscriptable {
2799*0Sstevel@tonic-gate    my $op = shift;
2800*0Sstevel@tonic-gate    if ($op->name =~ /^[ahg]elem/) {
2801*0Sstevel@tonic-gate	return 1;
2802*0Sstevel@tonic-gate    } elsif ($op->name eq "entersub") {
2803*0Sstevel@tonic-gate	my $kid = $op->first;
2804*0Sstevel@tonic-gate	return 0 unless null $kid->sibling;
2805*0Sstevel@tonic-gate	$kid = $kid->first;
2806*0Sstevel@tonic-gate	$kid = $kid->sibling until null $kid->sibling;
2807*0Sstevel@tonic-gate	return 0 if is_scope($kid);
2808*0Sstevel@tonic-gate	$kid = $kid->first;
2809*0Sstevel@tonic-gate	return 0 if $kid->name eq "gv";
2810*0Sstevel@tonic-gate	return 0 if is_scalar($kid);
2811*0Sstevel@tonic-gate	return is_subscriptable($kid);
2812*0Sstevel@tonic-gate    } else {
2813*0Sstevel@tonic-gate	return 0;
2814*0Sstevel@tonic-gate    }
2815*0Sstevel@tonic-gate}
2816*0Sstevel@tonic-gate
2817*0Sstevel@tonic-gatesub elem {
2818*0Sstevel@tonic-gate    my $self = shift;
2819*0Sstevel@tonic-gate    my ($op, $cx, $left, $right, $padname) = @_;
2820*0Sstevel@tonic-gate    my($array, $idx) = ($op->first, $op->first->sibling);
2821*0Sstevel@tonic-gate    unless ($array->name eq $padname) { # Maybe this has been fixed
2822*0Sstevel@tonic-gate	$array = $array->first; # skip rv2av (or ex-rv2av in _53+)
2823*0Sstevel@tonic-gate    }
2824*0Sstevel@tonic-gate    if ($array->name eq $padname) {
2825*0Sstevel@tonic-gate	$array = $self->padany($array);
2826*0Sstevel@tonic-gate    } elsif (is_scope($array)) { # ${expr}[0]
2827*0Sstevel@tonic-gate	$array = "{" . $self->deparse($array, 0) . "}";
2828*0Sstevel@tonic-gate    } elsif ($array->name eq "gv") {
2829*0Sstevel@tonic-gate	$array = $self->gv_name($self->gv_or_padgv($array));
2830*0Sstevel@tonic-gate	if ($array !~ /::/) {
2831*0Sstevel@tonic-gate	    my $prefix = ($left eq '[' ? '@' : '%');
2832*0Sstevel@tonic-gate	    $array = $self->{curstash}.'::'.$array
2833*0Sstevel@tonic-gate		if $self->lex_in_scope($prefix . $array);
2834*0Sstevel@tonic-gate	}
2835*0Sstevel@tonic-gate    } elsif (is_scalar $array) { # $x[0], $$x[0], ...
2836*0Sstevel@tonic-gate	$array = $self->deparse($array, 24);
2837*0Sstevel@tonic-gate    } else {
2838*0Sstevel@tonic-gate	# $x[20][3]{hi} or expr->[20]
2839*0Sstevel@tonic-gate	my $arrow = is_subscriptable($array) ? "" : "->";
2840*0Sstevel@tonic-gate	return $self->deparse($array, 24) . $arrow .
2841*0Sstevel@tonic-gate	    $left . $self->deparse($idx, 1) . $right;
2842*0Sstevel@tonic-gate    }
2843*0Sstevel@tonic-gate    $idx = $self->deparse($idx, 1);
2844*0Sstevel@tonic-gate
2845*0Sstevel@tonic-gate    # Outer parens in an array index will confuse perl
2846*0Sstevel@tonic-gate    # if we're interpolating in a regular expression, i.e.
2847*0Sstevel@tonic-gate    # /$x$foo[(-1)]/ is *not* the same as /$x$foo[-1]/
2848*0Sstevel@tonic-gate    #
2849*0Sstevel@tonic-gate    # If $self->{parens}, then an initial '(' will
2850*0Sstevel@tonic-gate    # definitely be paired with a final ')'. If
2851*0Sstevel@tonic-gate    # !$self->{parens}, the misleading parens won't
2852*0Sstevel@tonic-gate    # have been added in the first place.
2853*0Sstevel@tonic-gate    #
2854*0Sstevel@tonic-gate    # [You might think that we could get "(...)...(...)"
2855*0Sstevel@tonic-gate    # where the initial and final parens do not match
2856*0Sstevel@tonic-gate    # each other. But we can't, because the above would
2857*0Sstevel@tonic-gate    # only happen if there's an infix binop between the
2858*0Sstevel@tonic-gate    # two pairs of parens, and *that* means that the whole
2859*0Sstevel@tonic-gate    # expression would be parenthesized as well.]
2860*0Sstevel@tonic-gate    #
2861*0Sstevel@tonic-gate    $idx =~ s/^\((.*)\)$/$1/ if $self->{'parens'};
2862*0Sstevel@tonic-gate
2863*0Sstevel@tonic-gate    # Hash-element braces will autoquote a bareword inside themselves.
2864*0Sstevel@tonic-gate    # We need to make sure that C<$hash{warn()}> doesn't come out as
2865*0Sstevel@tonic-gate    # C<$hash{warn}>, which has a quite different meaning. Currently
2866*0Sstevel@tonic-gate    # B::Deparse will always quote strings, even if the string was a
2867*0Sstevel@tonic-gate    # bareword in the original (i.e. the OPpCONST_BARE flag is ignored
2868*0Sstevel@tonic-gate    # for constant strings.) So we can cheat slightly here - if we see
2869*0Sstevel@tonic-gate    # a bareword, we know that it is supposed to be a function call.
2870*0Sstevel@tonic-gate    #
2871*0Sstevel@tonic-gate    $idx =~ s/^([A-Za-z_]\w*)$/$1()/;
2872*0Sstevel@tonic-gate
2873*0Sstevel@tonic-gate    return "\$" . $array . $left . $idx . $right;
2874*0Sstevel@tonic-gate}
2875*0Sstevel@tonic-gate
2876*0Sstevel@tonic-gatesub pp_aelem { maybe_local(@_, elem(@_, "[", "]", "padav")) }
2877*0Sstevel@tonic-gatesub pp_helem { maybe_local(@_, elem(@_, "{", "}", "padhv")) }
2878*0Sstevel@tonic-gate
2879*0Sstevel@tonic-gatesub pp_gelem {
2880*0Sstevel@tonic-gate    my $self = shift;
2881*0Sstevel@tonic-gate    my($op, $cx) = @_;
2882*0Sstevel@tonic-gate    my($glob, $part) = ($op->first, $op->last);
2883*0Sstevel@tonic-gate    $glob = $glob->first; # skip rv2gv
2884*0Sstevel@tonic-gate    $glob = $glob->first if $glob->name eq "rv2gv"; # this one's a bug
2885*0Sstevel@tonic-gate    my $scope = is_scope($glob);
2886*0Sstevel@tonic-gate    $glob = $self->deparse($glob, 0);
2887*0Sstevel@tonic-gate    $part = $self->deparse($part, 1);
2888*0Sstevel@tonic-gate    return "*" . ($scope ? "{$glob}" : $glob) . "{$part}";
2889*0Sstevel@tonic-gate}
2890*0Sstevel@tonic-gate
2891*0Sstevel@tonic-gatesub slice {
2892*0Sstevel@tonic-gate    my $self = shift;
2893*0Sstevel@tonic-gate    my ($op, $cx, $left, $right, $regname, $padname) = @_;
2894*0Sstevel@tonic-gate    my $last;
2895*0Sstevel@tonic-gate    my(@elems, $kid, $array, $list);
2896*0Sstevel@tonic-gate    if (class($op) eq "LISTOP") {
2897*0Sstevel@tonic-gate	$last = $op->last;
2898*0Sstevel@tonic-gate    } else { # ex-hslice inside delete()
2899*0Sstevel@tonic-gate	for ($kid = $op->first; !null $kid->sibling; $kid = $kid->sibling) {}
2900*0Sstevel@tonic-gate	$last = $kid;
2901*0Sstevel@tonic-gate    }
2902*0Sstevel@tonic-gate    $array = $last;
2903*0Sstevel@tonic-gate    $array = $array->first
2904*0Sstevel@tonic-gate	if $array->name eq $regname or $array->name eq "null";
2905*0Sstevel@tonic-gate    if (is_scope($array)) {
2906*0Sstevel@tonic-gate	$array = "{" . $self->deparse($array, 0) . "}";
2907*0Sstevel@tonic-gate    } elsif ($array->name eq $padname) {
2908*0Sstevel@tonic-gate	$array = $self->padany($array);
2909*0Sstevel@tonic-gate    } else {
2910*0Sstevel@tonic-gate	$array = $self->deparse($array, 24);
2911*0Sstevel@tonic-gate    }
2912*0Sstevel@tonic-gate    $kid = $op->first->sibling; # skip pushmark
2913*0Sstevel@tonic-gate    if ($kid->name eq "list") {
2914*0Sstevel@tonic-gate	$kid = $kid->first->sibling; # skip list, pushmark
2915*0Sstevel@tonic-gate	for (; !null $kid; $kid = $kid->sibling) {
2916*0Sstevel@tonic-gate	    push @elems, $self->deparse($kid, 6);
2917*0Sstevel@tonic-gate	}
2918*0Sstevel@tonic-gate	$list = join(", ", @elems);
2919*0Sstevel@tonic-gate    } else {
2920*0Sstevel@tonic-gate	$list = $self->deparse($kid, 1);
2921*0Sstevel@tonic-gate    }
2922*0Sstevel@tonic-gate    return "\@" . $array . $left . $list . $right;
2923*0Sstevel@tonic-gate}
2924*0Sstevel@tonic-gate
2925*0Sstevel@tonic-gatesub pp_aslice { maybe_local(@_, slice(@_, "[", "]", "rv2av", "padav")) }
2926*0Sstevel@tonic-gatesub pp_hslice { maybe_local(@_, slice(@_, "{", "}", "rv2hv", "padhv")) }
2927*0Sstevel@tonic-gate
2928*0Sstevel@tonic-gatesub pp_lslice {
2929*0Sstevel@tonic-gate    my $self = shift;
2930*0Sstevel@tonic-gate    my($op, $cx) = @_;
2931*0Sstevel@tonic-gate    my $idx = $op->first;
2932*0Sstevel@tonic-gate    my $list = $op->last;
2933*0Sstevel@tonic-gate    my(@elems, $kid);
2934*0Sstevel@tonic-gate    $list = $self->deparse($list, 1);
2935*0Sstevel@tonic-gate    $idx = $self->deparse($idx, 1);
2936*0Sstevel@tonic-gate    return "($list)" . "[$idx]";
2937*0Sstevel@tonic-gate}
2938*0Sstevel@tonic-gate
2939*0Sstevel@tonic-gatesub want_scalar {
2940*0Sstevel@tonic-gate    my $op = shift;
2941*0Sstevel@tonic-gate    return ($op->flags & OPf_WANT) == OPf_WANT_SCALAR;
2942*0Sstevel@tonic-gate}
2943*0Sstevel@tonic-gate
2944*0Sstevel@tonic-gatesub want_list {
2945*0Sstevel@tonic-gate    my $op = shift;
2946*0Sstevel@tonic-gate    return ($op->flags & OPf_WANT) == OPf_WANT_LIST;
2947*0Sstevel@tonic-gate}
2948*0Sstevel@tonic-gate
2949*0Sstevel@tonic-gatesub method {
2950*0Sstevel@tonic-gate    my $self = shift;
2951*0Sstevel@tonic-gate    my($op, $cx) = @_;
2952*0Sstevel@tonic-gate    my $kid = $op->first->sibling; # skip pushmark
2953*0Sstevel@tonic-gate    my($meth, $obj, @exprs);
2954*0Sstevel@tonic-gate    if ($kid->name eq "list" and want_list $kid) {
2955*0Sstevel@tonic-gate	# When an indirect object isn't a bareword but the args are in
2956*0Sstevel@tonic-gate	# parens, the parens aren't part of the method syntax (the LLAFR
2957*0Sstevel@tonic-gate	# doesn't apply), but they make a list with OPf_PARENS set that
2958*0Sstevel@tonic-gate	# doesn't get flattened by the append_elem that adds the method,
2959*0Sstevel@tonic-gate	# making a (object, arg1, arg2, ...) list where the object
2960*0Sstevel@tonic-gate	# usually is. This can be distinguished from
2961*0Sstevel@tonic-gate	# `($obj, $arg1, $arg2)->meth()' (which is legal if $arg2 is an
2962*0Sstevel@tonic-gate	# object) because in the later the list is in scalar context
2963*0Sstevel@tonic-gate	# as the left side of -> always is, while in the former
2964*0Sstevel@tonic-gate	# the list is in list context as method arguments always are.
2965*0Sstevel@tonic-gate	# (Good thing there aren't method prototypes!)
2966*0Sstevel@tonic-gate	$meth = $kid->sibling;
2967*0Sstevel@tonic-gate	$kid = $kid->first->sibling; # skip pushmark
2968*0Sstevel@tonic-gate	$obj = $kid;
2969*0Sstevel@tonic-gate	$kid = $kid->sibling;
2970*0Sstevel@tonic-gate	for (; not null $kid; $kid = $kid->sibling) {
2971*0Sstevel@tonic-gate	    push @exprs, $self->deparse($kid, 6);
2972*0Sstevel@tonic-gate	}
2973*0Sstevel@tonic-gate    } else {
2974*0Sstevel@tonic-gate	$obj = $kid;
2975*0Sstevel@tonic-gate	$kid = $kid->sibling;
2976*0Sstevel@tonic-gate	for (; !null ($kid->sibling) && $kid->name ne "method_named";
2977*0Sstevel@tonic-gate	      $kid = $kid->sibling) {
2978*0Sstevel@tonic-gate	    push @exprs, $self->deparse($kid, 6);
2979*0Sstevel@tonic-gate	}
2980*0Sstevel@tonic-gate	$meth = $kid;
2981*0Sstevel@tonic-gate    }
2982*0Sstevel@tonic-gate    $obj = $self->deparse($obj, 24);
2983*0Sstevel@tonic-gate    if ($meth->name eq "method_named") {
2984*0Sstevel@tonic-gate	$meth = $self->const_sv($meth)->PV;
2985*0Sstevel@tonic-gate    } else {
2986*0Sstevel@tonic-gate	$meth = $meth->first;
2987*0Sstevel@tonic-gate	if ($meth->name eq "const") {
2988*0Sstevel@tonic-gate	    # As of 5.005_58, this case is probably obsoleted by the
2989*0Sstevel@tonic-gate	    # method_named case above
2990*0Sstevel@tonic-gate	    $meth = $self->const_sv($meth)->PV; # needs to be bare
2991*0Sstevel@tonic-gate	} else {
2992*0Sstevel@tonic-gate	    $meth = $self->deparse($meth, 1);
2993*0Sstevel@tonic-gate	}
2994*0Sstevel@tonic-gate    }
2995*0Sstevel@tonic-gate    my $args = join(", ", @exprs);
2996*0Sstevel@tonic-gate    $kid = $obj . "->" . $meth;
2997*0Sstevel@tonic-gate    if (length $args) {
2998*0Sstevel@tonic-gate	return $kid . "(" . $args . ")"; # parens mandatory
2999*0Sstevel@tonic-gate    } else {
3000*0Sstevel@tonic-gate	return $kid;
3001*0Sstevel@tonic-gate    }
3002*0Sstevel@tonic-gate}
3003*0Sstevel@tonic-gate
3004*0Sstevel@tonic-gate# returns "&" if the prototype doesn't match the args,
3005*0Sstevel@tonic-gate# or ("", $args_after_prototype_demunging) if it does.
3006*0Sstevel@tonic-gatesub check_proto {
3007*0Sstevel@tonic-gate    my $self = shift;
3008*0Sstevel@tonic-gate    return "&" if $self->{'noproto'};
3009*0Sstevel@tonic-gate    my($proto, @args) = @_;
3010*0Sstevel@tonic-gate    my($arg, $real);
3011*0Sstevel@tonic-gate    my $doneok = 0;
3012*0Sstevel@tonic-gate    my @reals;
3013*0Sstevel@tonic-gate    # An unbackslashed @ or % gobbles up the rest of the args
3014*0Sstevel@tonic-gate    1 while $proto =~ s/(?<!\\)([@%])[^\]]+$/$1/;
3015*0Sstevel@tonic-gate    while ($proto) {
3016*0Sstevel@tonic-gate	$proto =~ s/^(\\?[\$\@&%*]|\\\[[\$\@&%*]+\]|;)//;
3017*0Sstevel@tonic-gate	my $chr = $1;
3018*0Sstevel@tonic-gate	if ($chr eq "") {
3019*0Sstevel@tonic-gate	    return "&" if @args;
3020*0Sstevel@tonic-gate	} elsif ($chr eq ";") {
3021*0Sstevel@tonic-gate	    $doneok = 1;
3022*0Sstevel@tonic-gate	} elsif ($chr eq "@" or $chr eq "%") {
3023*0Sstevel@tonic-gate	    push @reals, map($self->deparse($_, 6), @args);
3024*0Sstevel@tonic-gate	    @args = ();
3025*0Sstevel@tonic-gate	} else {
3026*0Sstevel@tonic-gate	    $arg = shift @args;
3027*0Sstevel@tonic-gate	    last unless $arg;
3028*0Sstevel@tonic-gate	    if ($chr eq "\$") {
3029*0Sstevel@tonic-gate		if (want_scalar $arg) {
3030*0Sstevel@tonic-gate		    push @reals, $self->deparse($arg, 6);
3031*0Sstevel@tonic-gate		} else {
3032*0Sstevel@tonic-gate		    return "&";
3033*0Sstevel@tonic-gate		}
3034*0Sstevel@tonic-gate	    } elsif ($chr eq "&") {
3035*0Sstevel@tonic-gate		if ($arg->name =~ /^(s?refgen|undef)$/) {
3036*0Sstevel@tonic-gate		    push @reals, $self->deparse($arg, 6);
3037*0Sstevel@tonic-gate		} else {
3038*0Sstevel@tonic-gate		    return "&";
3039*0Sstevel@tonic-gate		}
3040*0Sstevel@tonic-gate	    } elsif ($chr eq "*") {
3041*0Sstevel@tonic-gate		if ($arg->name =~ /^s?refgen$/
3042*0Sstevel@tonic-gate		    and $arg->first->first->name eq "rv2gv")
3043*0Sstevel@tonic-gate		  {
3044*0Sstevel@tonic-gate		      $real = $arg->first->first; # skip refgen, null
3045*0Sstevel@tonic-gate		      if ($real->first->name eq "gv") {
3046*0Sstevel@tonic-gate			  push @reals, $self->deparse($real, 6);
3047*0Sstevel@tonic-gate		      } else {
3048*0Sstevel@tonic-gate			  push @reals, $self->deparse($real->first, 6);
3049*0Sstevel@tonic-gate		      }
3050*0Sstevel@tonic-gate		  } else {
3051*0Sstevel@tonic-gate		      return "&";
3052*0Sstevel@tonic-gate		  }
3053*0Sstevel@tonic-gate	    } elsif (substr($chr, 0, 1) eq "\\") {
3054*0Sstevel@tonic-gate		$chr =~ tr/\\[]//d;
3055*0Sstevel@tonic-gate		if ($arg->name =~ /^s?refgen$/ and
3056*0Sstevel@tonic-gate		    !null($real = $arg->first) and
3057*0Sstevel@tonic-gate		    ($chr =~ /\$/ && is_scalar($real->first)
3058*0Sstevel@tonic-gate		     or ($chr =~ /@/
3059*0Sstevel@tonic-gate			 && class($real->first->sibling) ne 'NULL'
3060*0Sstevel@tonic-gate			 && $real->first->sibling->name
3061*0Sstevel@tonic-gate			 =~ /^(rv2|pad)av$/)
3062*0Sstevel@tonic-gate		     or ($chr =~ /%/
3063*0Sstevel@tonic-gate			 && class($real->first->sibling) ne 'NULL'
3064*0Sstevel@tonic-gate			 && $real->first->sibling->name
3065*0Sstevel@tonic-gate			 =~ /^(rv2|pad)hv$/)
3066*0Sstevel@tonic-gate		     #or ($chr =~ /&/ # This doesn't work
3067*0Sstevel@tonic-gate		     #   && $real->first->name eq "rv2cv")
3068*0Sstevel@tonic-gate		     or ($chr =~ /\*/
3069*0Sstevel@tonic-gate			 && $real->first->name eq "rv2gv")))
3070*0Sstevel@tonic-gate		  {
3071*0Sstevel@tonic-gate		      push @reals, $self->deparse($real, 6);
3072*0Sstevel@tonic-gate		  } else {
3073*0Sstevel@tonic-gate		      return "&";
3074*0Sstevel@tonic-gate		  }
3075*0Sstevel@tonic-gate	    }
3076*0Sstevel@tonic-gate       }
3077*0Sstevel@tonic-gate    }
3078*0Sstevel@tonic-gate    return "&" if $proto and !$doneok; # too few args and no `;'
3079*0Sstevel@tonic-gate    return "&" if @args;               # too many args
3080*0Sstevel@tonic-gate    return ("", join ", ", @reals);
3081*0Sstevel@tonic-gate}
3082*0Sstevel@tonic-gate
3083*0Sstevel@tonic-gatesub pp_entersub {
3084*0Sstevel@tonic-gate    my $self = shift;
3085*0Sstevel@tonic-gate    my($op, $cx) = @_;
3086*0Sstevel@tonic-gate    return $self->method($op, $cx) unless null $op->first->sibling;
3087*0Sstevel@tonic-gate    my $prefix = "";
3088*0Sstevel@tonic-gate    my $amper = "";
3089*0Sstevel@tonic-gate    my($kid, @exprs);
3090*0Sstevel@tonic-gate    if ($op->flags & OPf_SPECIAL && !($op->flags & OPf_MOD)) {
3091*0Sstevel@tonic-gate	$prefix = "do ";
3092*0Sstevel@tonic-gate    } elsif ($op->private & OPpENTERSUB_AMPER) {
3093*0Sstevel@tonic-gate	$amper = "&";
3094*0Sstevel@tonic-gate    }
3095*0Sstevel@tonic-gate    $kid = $op->first;
3096*0Sstevel@tonic-gate    $kid = $kid->first->sibling; # skip ex-list, pushmark
3097*0Sstevel@tonic-gate    for (; not null $kid->sibling; $kid = $kid->sibling) {
3098*0Sstevel@tonic-gate	push @exprs, $kid;
3099*0Sstevel@tonic-gate    }
3100*0Sstevel@tonic-gate    my $simple = 0;
3101*0Sstevel@tonic-gate    my $proto = undef;
3102*0Sstevel@tonic-gate    if (is_scope($kid)) {
3103*0Sstevel@tonic-gate	$amper = "&";
3104*0Sstevel@tonic-gate	$kid = "{" . $self->deparse($kid, 0) . "}";
3105*0Sstevel@tonic-gate    } elsif ($kid->first->name eq "gv") {
3106*0Sstevel@tonic-gate	my $gv = $self->gv_or_padgv($kid->first);
3107*0Sstevel@tonic-gate	if (class($gv->CV) ne "SPECIAL") {
3108*0Sstevel@tonic-gate	    $proto = $gv->CV->PV if $gv->CV->FLAGS & SVf_POK;
3109*0Sstevel@tonic-gate	}
3110*0Sstevel@tonic-gate	$simple = 1; # only calls of named functions can be prototyped
3111*0Sstevel@tonic-gate	$kid = $self->deparse($kid, 24);
3112*0Sstevel@tonic-gate    } elsif (is_scalar ($kid->first) && $kid->first->name ne 'rv2cv') {
3113*0Sstevel@tonic-gate	$amper = "&";
3114*0Sstevel@tonic-gate	$kid = $self->deparse($kid, 24);
3115*0Sstevel@tonic-gate    } else {
3116*0Sstevel@tonic-gate	$prefix = "";
3117*0Sstevel@tonic-gate	my $arrow = is_subscriptable($kid->first) ? "" : "->";
3118*0Sstevel@tonic-gate	$kid = $self->deparse($kid, 24) . $arrow;
3119*0Sstevel@tonic-gate    }
3120*0Sstevel@tonic-gate
3121*0Sstevel@tonic-gate    # Doesn't matter how many prototypes there are, if
3122*0Sstevel@tonic-gate    # they haven't happened yet!
3123*0Sstevel@tonic-gate    my $declared;
3124*0Sstevel@tonic-gate    {
3125*0Sstevel@tonic-gate	no strict 'refs';
3126*0Sstevel@tonic-gate	no warnings 'uninitialized';
3127*0Sstevel@tonic-gate	$declared = exists $self->{'subs_declared'}{$kid}
3128*0Sstevel@tonic-gate	    || (
3129*0Sstevel@tonic-gate		 defined &{ %{$self->{'curstash'}."::"}->{$kid} }
3130*0Sstevel@tonic-gate		 && !exists
3131*0Sstevel@tonic-gate		     $self->{'subs_deparsed'}{$self->{'curstash'}."::".$kid}
3132*0Sstevel@tonic-gate		 && defined prototype $self->{'curstash'}."::".$kid
3133*0Sstevel@tonic-gate	       );
3134*0Sstevel@tonic-gate	if (!$declared && defined($proto)) {
3135*0Sstevel@tonic-gate	    # Avoid "too early to check prototype" warning
3136*0Sstevel@tonic-gate	    ($amper, $proto) = ('&');
3137*0Sstevel@tonic-gate	}
3138*0Sstevel@tonic-gate    }
3139*0Sstevel@tonic-gate
3140*0Sstevel@tonic-gate    my $args;
3141*0Sstevel@tonic-gate    if ($declared and defined $proto and not $amper) {
3142*0Sstevel@tonic-gate	($amper, $args) = $self->check_proto($proto, @exprs);
3143*0Sstevel@tonic-gate	if ($amper eq "&") {
3144*0Sstevel@tonic-gate	    $args = join(", ", map($self->deparse($_, 6), @exprs));
3145*0Sstevel@tonic-gate	}
3146*0Sstevel@tonic-gate    } else {
3147*0Sstevel@tonic-gate	$args = join(", ", map($self->deparse($_, 6), @exprs));
3148*0Sstevel@tonic-gate    }
3149*0Sstevel@tonic-gate    if ($prefix or $amper) {
3150*0Sstevel@tonic-gate	if ($op->flags & OPf_STACKED) {
3151*0Sstevel@tonic-gate	    return $prefix . $amper . $kid . "(" . $args . ")";
3152*0Sstevel@tonic-gate	} else {
3153*0Sstevel@tonic-gate	    return $prefix . $amper. $kid;
3154*0Sstevel@tonic-gate	}
3155*0Sstevel@tonic-gate    } else {
3156*0Sstevel@tonic-gate	# glob() invocations can be translated into calls of
3157*0Sstevel@tonic-gate	# CORE::GLOBAL::glob with a second parameter, a number.
3158*0Sstevel@tonic-gate	# Reverse this.
3159*0Sstevel@tonic-gate	if ($kid eq "CORE::GLOBAL::glob") {
3160*0Sstevel@tonic-gate	    $kid = "glob";
3161*0Sstevel@tonic-gate	    $args =~ s/\s*,[^,]+$//;
3162*0Sstevel@tonic-gate	}
3163*0Sstevel@tonic-gate
3164*0Sstevel@tonic-gate	# It's a syntax error to call CORE::GLOBAL::foo without a prefix,
3165*0Sstevel@tonic-gate	# so it must have been translated from a keyword call. Translate
3166*0Sstevel@tonic-gate	# it back.
3167*0Sstevel@tonic-gate	$kid =~ s/^CORE::GLOBAL:://;
3168*0Sstevel@tonic-gate
3169*0Sstevel@tonic-gate	my $dproto = defined($proto) ? $proto : "undefined";
3170*0Sstevel@tonic-gate        if (!$declared) {
3171*0Sstevel@tonic-gate	    return "$kid(" . $args . ")";
3172*0Sstevel@tonic-gate	} elsif ($dproto eq "") {
3173*0Sstevel@tonic-gate	    return $kid;
3174*0Sstevel@tonic-gate	} elsif ($dproto eq "\$" and is_scalar($exprs[0])) {
3175*0Sstevel@tonic-gate	    # is_scalar is an excessively conservative test here:
3176*0Sstevel@tonic-gate	    # really, we should be comparing to the precedence of the
3177*0Sstevel@tonic-gate	    # top operator of $exprs[0] (ala unop()), but that would
3178*0Sstevel@tonic-gate	    # take some major code restructuring to do right.
3179*0Sstevel@tonic-gate	    return $self->maybe_parens_func($kid, $args, $cx, 16);
3180*0Sstevel@tonic-gate	} elsif ($dproto ne '$' and defined($proto) || $simple) { #'
3181*0Sstevel@tonic-gate	    return $self->maybe_parens_func($kid, $args, $cx, 5);
3182*0Sstevel@tonic-gate	} else {
3183*0Sstevel@tonic-gate	    return "$kid(" . $args . ")";
3184*0Sstevel@tonic-gate	}
3185*0Sstevel@tonic-gate    }
3186*0Sstevel@tonic-gate}
3187*0Sstevel@tonic-gate
3188*0Sstevel@tonic-gatesub pp_enterwrite { unop(@_, "write") }
3189*0Sstevel@tonic-gate
3190*0Sstevel@tonic-gate# escape things that cause interpolation in double quotes,
3191*0Sstevel@tonic-gate# but not character escapes
3192*0Sstevel@tonic-gatesub uninterp {
3193*0Sstevel@tonic-gate    my($str) = @_;
3194*0Sstevel@tonic-gate    $str =~ s/(^|\G|[^\\])((?:\\\\)*)([\$\@]|\\[uUlLQE])/$1$2\\$3/g;
3195*0Sstevel@tonic-gate    return $str;
3196*0Sstevel@tonic-gate}
3197*0Sstevel@tonic-gate
3198*0Sstevel@tonic-gate{
3199*0Sstevel@tonic-gatemy $bal;
3200*0Sstevel@tonic-gateBEGIN {
3201*0Sstevel@tonic-gate    use re "eval";
3202*0Sstevel@tonic-gate    # Matches any string which is balanced with respect to {braces}
3203*0Sstevel@tonic-gate    $bal = qr(
3204*0Sstevel@tonic-gate      (?:
3205*0Sstevel@tonic-gate	[^\\{}]
3206*0Sstevel@tonic-gate      | \\\\
3207*0Sstevel@tonic-gate      | \\[{}]
3208*0Sstevel@tonic-gate      | \{(??{$bal})\}
3209*0Sstevel@tonic-gate      )*
3210*0Sstevel@tonic-gate    )x;
3211*0Sstevel@tonic-gate}
3212*0Sstevel@tonic-gate
3213*0Sstevel@tonic-gate# the same, but treat $|, $), $( and $ at the end of the string differently
3214*0Sstevel@tonic-gatesub re_uninterp {
3215*0Sstevel@tonic-gate    my($str) = @_;
3216*0Sstevel@tonic-gate
3217*0Sstevel@tonic-gate    $str =~ s/
3218*0Sstevel@tonic-gate	  ( ^|\G                  # $1
3219*0Sstevel@tonic-gate          | [^\\]
3220*0Sstevel@tonic-gate          )
3221*0Sstevel@tonic-gate
3222*0Sstevel@tonic-gate          (                       # $2
3223*0Sstevel@tonic-gate            (?:\\\\)*
3224*0Sstevel@tonic-gate          )
3225*0Sstevel@tonic-gate
3226*0Sstevel@tonic-gate          (                       # $3
3227*0Sstevel@tonic-gate            (\(\?\??\{$bal\}\))   # $4
3228*0Sstevel@tonic-gate          | [\$\@]
3229*0Sstevel@tonic-gate            (?!\||\)|\(|$)
3230*0Sstevel@tonic-gate          | \\[uUlLQE]
3231*0Sstevel@tonic-gate          )
3232*0Sstevel@tonic-gate
3233*0Sstevel@tonic-gate	/defined($4) && length($4) ? "$1$2$4" : "$1$2\\$3"/xeg;
3234*0Sstevel@tonic-gate
3235*0Sstevel@tonic-gate    return $str;
3236*0Sstevel@tonic-gate}
3237*0Sstevel@tonic-gate
3238*0Sstevel@tonic-gate# This is for regular expressions with the /x modifier
3239*0Sstevel@tonic-gate# We have to leave comments unmangled.
3240*0Sstevel@tonic-gatesub re_uninterp_extended {
3241*0Sstevel@tonic-gate    my($str) = @_;
3242*0Sstevel@tonic-gate
3243*0Sstevel@tonic-gate    $str =~ s/
3244*0Sstevel@tonic-gate	  ( ^|\G                  # $1
3245*0Sstevel@tonic-gate          | [^\\]
3246*0Sstevel@tonic-gate          )
3247*0Sstevel@tonic-gate
3248*0Sstevel@tonic-gate          (                       # $2
3249*0Sstevel@tonic-gate            (?:\\\\)*
3250*0Sstevel@tonic-gate          )
3251*0Sstevel@tonic-gate
3252*0Sstevel@tonic-gate          (                       # $3
3253*0Sstevel@tonic-gate            ( \(\?\??\{$bal\}\)   # $4  (skip over (?{}) and (??{}) blocks)
3254*0Sstevel@tonic-gate            | \#[^\n]*            #     (skip over comments)
3255*0Sstevel@tonic-gate            )
3256*0Sstevel@tonic-gate          | [\$\@]
3257*0Sstevel@tonic-gate            (?!\||\)|\(|$|\s)
3258*0Sstevel@tonic-gate          | \\[uUlLQE]
3259*0Sstevel@tonic-gate          )
3260*0Sstevel@tonic-gate
3261*0Sstevel@tonic-gate	/defined($4) && length($4) ? "$1$2$4" : "$1$2\\$3"/xeg;
3262*0Sstevel@tonic-gate
3263*0Sstevel@tonic-gate    return $str;
3264*0Sstevel@tonic-gate}
3265*0Sstevel@tonic-gate}
3266*0Sstevel@tonic-gate
3267*0Sstevel@tonic-gatemy %unctrl = # portable to to EBCDIC
3268*0Sstevel@tonic-gate    (
3269*0Sstevel@tonic-gate     "\c@" => '\c@',	# unused
3270*0Sstevel@tonic-gate     "\cA" => '\cA',
3271*0Sstevel@tonic-gate     "\cB" => '\cB',
3272*0Sstevel@tonic-gate     "\cC" => '\cC',
3273*0Sstevel@tonic-gate     "\cD" => '\cD',
3274*0Sstevel@tonic-gate     "\cE" => '\cE',
3275*0Sstevel@tonic-gate     "\cF" => '\cF',
3276*0Sstevel@tonic-gate     "\cG" => '\cG',
3277*0Sstevel@tonic-gate     "\cH" => '\cH',
3278*0Sstevel@tonic-gate     "\cI" => '\cI',
3279*0Sstevel@tonic-gate     "\cJ" => '\cJ',
3280*0Sstevel@tonic-gate     "\cK" => '\cK',
3281*0Sstevel@tonic-gate     "\cL" => '\cL',
3282*0Sstevel@tonic-gate     "\cM" => '\cM',
3283*0Sstevel@tonic-gate     "\cN" => '\cN',
3284*0Sstevel@tonic-gate     "\cO" => '\cO',
3285*0Sstevel@tonic-gate     "\cP" => '\cP',
3286*0Sstevel@tonic-gate     "\cQ" => '\cQ',
3287*0Sstevel@tonic-gate     "\cR" => '\cR',
3288*0Sstevel@tonic-gate     "\cS" => '\cS',
3289*0Sstevel@tonic-gate     "\cT" => '\cT',
3290*0Sstevel@tonic-gate     "\cU" => '\cU',
3291*0Sstevel@tonic-gate     "\cV" => '\cV',
3292*0Sstevel@tonic-gate     "\cW" => '\cW',
3293*0Sstevel@tonic-gate     "\cX" => '\cX',
3294*0Sstevel@tonic-gate     "\cY" => '\cY',
3295*0Sstevel@tonic-gate     "\cZ" => '\cZ',
3296*0Sstevel@tonic-gate     "\c[" => '\c[',	# unused
3297*0Sstevel@tonic-gate     "\c\\" => '\c\\',	# unused
3298*0Sstevel@tonic-gate     "\c]" => '\c]',	# unused
3299*0Sstevel@tonic-gate     "\c_" => '\c_',	# unused
3300*0Sstevel@tonic-gate    );
3301*0Sstevel@tonic-gate
3302*0Sstevel@tonic-gate# character escapes, but not delimiters that might need to be escaped
3303*0Sstevel@tonic-gatesub escape_str { # ASCII, UTF8
3304*0Sstevel@tonic-gate    my($str) = @_;
3305*0Sstevel@tonic-gate    $str =~ s/(.)/ord($1) > 255 ? sprintf("\\x{%x}", ord($1)) : $1/eg;
3306*0Sstevel@tonic-gate    $str =~ s/\a/\\a/g;
3307*0Sstevel@tonic-gate#    $str =~ s/\cH/\\b/g; # \b means something different in a regex
3308*0Sstevel@tonic-gate    $str =~ s/\t/\\t/g;
3309*0Sstevel@tonic-gate    $str =~ s/\n/\\n/g;
3310*0Sstevel@tonic-gate    $str =~ s/\e/\\e/g;
3311*0Sstevel@tonic-gate    $str =~ s/\f/\\f/g;
3312*0Sstevel@tonic-gate    $str =~ s/\r/\\r/g;
3313*0Sstevel@tonic-gate    $str =~ s/([\cA-\cZ])/$unctrl{$1}/ge;
3314*0Sstevel@tonic-gate    $str =~ s/([[:^print:]])/sprintf("\\%03o", ord($1))/ge;
3315*0Sstevel@tonic-gate    return $str;
3316*0Sstevel@tonic-gate}
3317*0Sstevel@tonic-gate
3318*0Sstevel@tonic-gate# For regexes with the /x modifier.
3319*0Sstevel@tonic-gate# Leave whitespace unmangled.
3320*0Sstevel@tonic-gatesub escape_extended_re {
3321*0Sstevel@tonic-gate    my($str) = @_;
3322*0Sstevel@tonic-gate    $str =~ s/(.)/ord($1) > 255 ? sprintf("\\x{%x}", ord($1)) : $1/eg;
3323*0Sstevel@tonic-gate    $str =~ s/([[:^print:]])/
3324*0Sstevel@tonic-gate	($1 =~ y! \t\n!!) ? $1 : sprintf("\\%03o", ord($1))/ge;
3325*0Sstevel@tonic-gate    $str =~ s/\n/\n\f/g;
3326*0Sstevel@tonic-gate    return $str;
3327*0Sstevel@tonic-gate}
3328*0Sstevel@tonic-gate
3329*0Sstevel@tonic-gate# Don't do this for regexen
3330*0Sstevel@tonic-gatesub unback {
3331*0Sstevel@tonic-gate    my($str) = @_;
3332*0Sstevel@tonic-gate    $str =~ s/\\/\\\\/g;
3333*0Sstevel@tonic-gate    return $str;
3334*0Sstevel@tonic-gate}
3335*0Sstevel@tonic-gate
3336*0Sstevel@tonic-gate# Remove backslashes which precede literal control characters,
3337*0Sstevel@tonic-gate# to avoid creating ambiguity when we escape the latter.
3338*0Sstevel@tonic-gatesub re_unback {
3339*0Sstevel@tonic-gate    my($str) = @_;
3340*0Sstevel@tonic-gate
3341*0Sstevel@tonic-gate    # the insane complexity here is due to the behaviour of "\c\"
3342*0Sstevel@tonic-gate    $str =~ s/(^|[^\\]|\\c\\)(?<!\\c)\\(\\\\)*(?=[[:^print:]])/$1$2/g;
3343*0Sstevel@tonic-gate    return $str;
3344*0Sstevel@tonic-gate}
3345*0Sstevel@tonic-gate
3346*0Sstevel@tonic-gatesub balanced_delim {
3347*0Sstevel@tonic-gate    my($str) = @_;
3348*0Sstevel@tonic-gate    my @str = split //, $str;
3349*0Sstevel@tonic-gate    my($ar, $open, $close, $fail, $c, $cnt);
3350*0Sstevel@tonic-gate    for $ar (['[',']'], ['(',')'], ['<','>'], ['{','}']) {
3351*0Sstevel@tonic-gate	($open, $close) = @$ar;
3352*0Sstevel@tonic-gate	$fail = 0; $cnt = 0;
3353*0Sstevel@tonic-gate	for $c (@str) {
3354*0Sstevel@tonic-gate	    if ($c eq $open) {
3355*0Sstevel@tonic-gate		$cnt++;
3356*0Sstevel@tonic-gate	    } elsif ($c eq $close) {
3357*0Sstevel@tonic-gate		$cnt--;
3358*0Sstevel@tonic-gate		if ($cnt < 0) {
3359*0Sstevel@tonic-gate		    # qq()() isn't ")("
3360*0Sstevel@tonic-gate		    $fail = 1;
3361*0Sstevel@tonic-gate		    last;
3362*0Sstevel@tonic-gate		}
3363*0Sstevel@tonic-gate	    }
3364*0Sstevel@tonic-gate	}
3365*0Sstevel@tonic-gate	$fail = 1 if $cnt != 0;
3366*0Sstevel@tonic-gate	return ($open, "$open$str$close") if not $fail;
3367*0Sstevel@tonic-gate    }
3368*0Sstevel@tonic-gate    return ("", $str);
3369*0Sstevel@tonic-gate}
3370*0Sstevel@tonic-gate
3371*0Sstevel@tonic-gatesub single_delim {
3372*0Sstevel@tonic-gate    my($q, $default, $str) = @_;
3373*0Sstevel@tonic-gate    return "$default$str$default" if $default and index($str, $default) == -1;
3374*0Sstevel@tonic-gate    if ($q ne 'qr') {
3375*0Sstevel@tonic-gate	(my $succeed, $str) = balanced_delim($str);
3376*0Sstevel@tonic-gate	return "$q$str" if $succeed;
3377*0Sstevel@tonic-gate    }
3378*0Sstevel@tonic-gate    for my $delim ('/', '"', '#') {
3379*0Sstevel@tonic-gate	return "$q$delim" . $str . $delim if index($str, $delim) == -1;
3380*0Sstevel@tonic-gate    }
3381*0Sstevel@tonic-gate    if ($default) {
3382*0Sstevel@tonic-gate	$str =~ s/$default/\\$default/g;
3383*0Sstevel@tonic-gate	return "$default$str$default";
3384*0Sstevel@tonic-gate    } else {
3385*0Sstevel@tonic-gate	$str =~ s[/][\\/]g;
3386*0Sstevel@tonic-gate	return "$q/$str/";
3387*0Sstevel@tonic-gate    }
3388*0Sstevel@tonic-gate}
3389*0Sstevel@tonic-gate
3390*0Sstevel@tonic-gatemy $max_prec;
3391*0Sstevel@tonic-gateBEGIN { $max_prec = int(0.999 + 8*length(pack("F", 42))*log(2)/log(10)); }
3392*0Sstevel@tonic-gate
3393*0Sstevel@tonic-gate# Split a floating point number into an integer mantissa and a binary
3394*0Sstevel@tonic-gate# exponent. Assumes you've already made sure the number isn't zero or
3395*0Sstevel@tonic-gate# some weird infinity or NaN.
3396*0Sstevel@tonic-gatesub split_float {
3397*0Sstevel@tonic-gate    my($f) = @_;
3398*0Sstevel@tonic-gate    my $exponent = 0;
3399*0Sstevel@tonic-gate    if ($f == int($f)) {
3400*0Sstevel@tonic-gate	while ($f % 2 == 0) {
3401*0Sstevel@tonic-gate	    $f /= 2;
3402*0Sstevel@tonic-gate	    $exponent++;
3403*0Sstevel@tonic-gate	}
3404*0Sstevel@tonic-gate    } else {
3405*0Sstevel@tonic-gate	while ($f != int($f)) {
3406*0Sstevel@tonic-gate	    $f *= 2;
3407*0Sstevel@tonic-gate	    $exponent--;
3408*0Sstevel@tonic-gate	}
3409*0Sstevel@tonic-gate    }
3410*0Sstevel@tonic-gate    my $mantissa = sprintf("%.0f", $f);
3411*0Sstevel@tonic-gate    return ($mantissa, $exponent);
3412*0Sstevel@tonic-gate}
3413*0Sstevel@tonic-gate
3414*0Sstevel@tonic-gatesub const {
3415*0Sstevel@tonic-gate    my $self = shift;
3416*0Sstevel@tonic-gate    my($sv, $cx) = @_;
3417*0Sstevel@tonic-gate    if ($self->{'use_dumper'}) {
3418*0Sstevel@tonic-gate	return $self->const_dumper($sv, $cx);
3419*0Sstevel@tonic-gate    }
3420*0Sstevel@tonic-gate    if (class($sv) eq "SPECIAL") {
3421*0Sstevel@tonic-gate	# sv_undef, sv_yes, sv_no
3422*0Sstevel@tonic-gate	return ('undef', '1', $self->maybe_parens("!1", $cx, 21))[$$sv-1];
3423*0Sstevel@tonic-gate    } elsif (class($sv) eq "NULL") {
3424*0Sstevel@tonic-gate       return 'undef';
3425*0Sstevel@tonic-gate    }
3426*0Sstevel@tonic-gate    # convert a version object into the "v1.2.3" string in its V magic
3427*0Sstevel@tonic-gate    if ($sv->FLAGS & SVs_RMG) {
3428*0Sstevel@tonic-gate	for (my $mg = $sv->MAGIC; $mg; $mg = $mg->MOREMAGIC) {
3429*0Sstevel@tonic-gate	    return $mg->PTR if $mg->TYPE eq 'V';
3430*0Sstevel@tonic-gate	}
3431*0Sstevel@tonic-gate    }
3432*0Sstevel@tonic-gate
3433*0Sstevel@tonic-gate    if ($sv->FLAGS & SVf_IOK) {
3434*0Sstevel@tonic-gate	my $str = $sv->int_value;
3435*0Sstevel@tonic-gate	$str = $self->maybe_parens($str, $cx, 21) if $str < 0;
3436*0Sstevel@tonic-gate	return $str;
3437*0Sstevel@tonic-gate    } elsif ($sv->FLAGS & SVf_NOK) {
3438*0Sstevel@tonic-gate	my $nv = $sv->NV;
3439*0Sstevel@tonic-gate	if ($nv == 0) {
3440*0Sstevel@tonic-gate	    if (pack("F", $nv) eq pack("F", 0)) {
3441*0Sstevel@tonic-gate		# positive zero
3442*0Sstevel@tonic-gate		return "0";
3443*0Sstevel@tonic-gate	    } else {
3444*0Sstevel@tonic-gate		# negative zero
3445*0Sstevel@tonic-gate		return $self->maybe_parens("-.0", $cx, 21);
3446*0Sstevel@tonic-gate	    }
3447*0Sstevel@tonic-gate	} elsif (1/$nv == 0) {
3448*0Sstevel@tonic-gate	    if ($nv > 0) {
3449*0Sstevel@tonic-gate		# positive infinity
3450*0Sstevel@tonic-gate		return $self->maybe_parens("9**9**9", $cx, 22);
3451*0Sstevel@tonic-gate	    } else {
3452*0Sstevel@tonic-gate		# negative infinity
3453*0Sstevel@tonic-gate		return $self->maybe_parens("-9**9**9", $cx, 21);
3454*0Sstevel@tonic-gate	    }
3455*0Sstevel@tonic-gate	} elsif ($nv != $nv) {
3456*0Sstevel@tonic-gate	    # NaN
3457*0Sstevel@tonic-gate	    if (pack("F", $nv) eq pack("F", sin(9**9**9))) {
3458*0Sstevel@tonic-gate		# the normal kind
3459*0Sstevel@tonic-gate		return "sin(9**9**9)";
3460*0Sstevel@tonic-gate	    } elsif (pack("F", $nv) eq pack("F", -sin(9**9**9))) {
3461*0Sstevel@tonic-gate		# the inverted kind
3462*0Sstevel@tonic-gate		return $self->maybe_parens("-sin(9**9**9)", $cx, 21);
3463*0Sstevel@tonic-gate	    } else {
3464*0Sstevel@tonic-gate		# some other kind
3465*0Sstevel@tonic-gate		my $hex = unpack("h*", pack("F", $nv));
3466*0Sstevel@tonic-gate		return qq'unpack("F", pack("h*", "$hex"))';
3467*0Sstevel@tonic-gate	    }
3468*0Sstevel@tonic-gate	}
3469*0Sstevel@tonic-gate	# first, try the default stringification
3470*0Sstevel@tonic-gate	my $str = "$nv";
3471*0Sstevel@tonic-gate	if ($str != $nv) {
3472*0Sstevel@tonic-gate	    # failing that, try using more precision
3473*0Sstevel@tonic-gate	    $str = sprintf("%.${max_prec}g", $nv);
3474*0Sstevel@tonic-gate#	    if (pack("F", $str) ne pack("F", $nv)) {
3475*0Sstevel@tonic-gate	    if ($str != $nv) {
3476*0Sstevel@tonic-gate		# not representable in decimal with whatever sprintf()
3477*0Sstevel@tonic-gate		# and atof() Perl is using here.
3478*0Sstevel@tonic-gate		my($mant, $exp) = split_float($nv);
3479*0Sstevel@tonic-gate		return $self->maybe_parens("$mant * 2**$exp", $cx, 19);
3480*0Sstevel@tonic-gate	    }
3481*0Sstevel@tonic-gate	}
3482*0Sstevel@tonic-gate	$str = $self->maybe_parens($str, $cx, 21) if $nv < 0;
3483*0Sstevel@tonic-gate	return $str;
3484*0Sstevel@tonic-gate    } elsif ($sv->FLAGS & SVf_ROK && $sv->can("RV")) {
3485*0Sstevel@tonic-gate	my $ref = $sv->RV;
3486*0Sstevel@tonic-gate	if (class($ref) eq "AV") {
3487*0Sstevel@tonic-gate	    return "[" . $self->list_const(2, $ref->ARRAY) . "]";
3488*0Sstevel@tonic-gate	} elsif (class($ref) eq "HV") {
3489*0Sstevel@tonic-gate	    my %hash = $ref->ARRAY;
3490*0Sstevel@tonic-gate	    my @elts;
3491*0Sstevel@tonic-gate	    for my $k (sort keys %hash) {
3492*0Sstevel@tonic-gate		push @elts, "$k => " . $self->const($hash{$k}, 6);
3493*0Sstevel@tonic-gate	    }
3494*0Sstevel@tonic-gate	    return "{" . join(", ", @elts) . "}";
3495*0Sstevel@tonic-gate	} elsif (class($ref) eq "CV") {
3496*0Sstevel@tonic-gate	    return "sub " . $self->deparse_sub($ref);
3497*0Sstevel@tonic-gate	}
3498*0Sstevel@tonic-gate	if ($ref->FLAGS & SVs_SMG) {
3499*0Sstevel@tonic-gate	    for (my $mg = $ref->MAGIC; $mg; $mg = $mg->MOREMAGIC) {
3500*0Sstevel@tonic-gate		if ($mg->TYPE eq 'r') {
3501*0Sstevel@tonic-gate		    my $re = re_uninterp(escape_str(re_unback($mg->precomp)));
3502*0Sstevel@tonic-gate		    return single_delim("qr", "", $re);
3503*0Sstevel@tonic-gate		}
3504*0Sstevel@tonic-gate	    }
3505*0Sstevel@tonic-gate	}
3506*0Sstevel@tonic-gate
3507*0Sstevel@tonic-gate	return $self->maybe_parens("\\" . $self->const($ref, 20), $cx, 20);
3508*0Sstevel@tonic-gate    } elsif ($sv->FLAGS & SVf_POK) {
3509*0Sstevel@tonic-gate	my $str = $sv->PV;
3510*0Sstevel@tonic-gate	if ($str =~ /[^ -~]/) { # ASCII for non-printing
3511*0Sstevel@tonic-gate	    return single_delim("qq", '"', uninterp escape_str unback $str);
3512*0Sstevel@tonic-gate	} else {
3513*0Sstevel@tonic-gate	    return single_delim("q", "'", unback $str);
3514*0Sstevel@tonic-gate	}
3515*0Sstevel@tonic-gate    } else {
3516*0Sstevel@tonic-gate	return "undef";
3517*0Sstevel@tonic-gate    }
3518*0Sstevel@tonic-gate}
3519*0Sstevel@tonic-gate
3520*0Sstevel@tonic-gatesub const_dumper {
3521*0Sstevel@tonic-gate    my $self = shift;
3522*0Sstevel@tonic-gate    my($sv, $cx) = @_;
3523*0Sstevel@tonic-gate    my $ref = $sv->object_2svref();
3524*0Sstevel@tonic-gate    my $dumper = Data::Dumper->new([$$ref], ['$v']);
3525*0Sstevel@tonic-gate    $dumper->Purity(1)->Terse(1)->Deparse(1)->Indent(0)->Useqq(1)->Sortkeys(1);
3526*0Sstevel@tonic-gate    my $str = $dumper->Dump();
3527*0Sstevel@tonic-gate    if ($str =~ /^\$v/) {
3528*0Sstevel@tonic-gate	return '${my ' . $str . ' \$v}';
3529*0Sstevel@tonic-gate    } else {
3530*0Sstevel@tonic-gate	return $str;
3531*0Sstevel@tonic-gate    }
3532*0Sstevel@tonic-gate}
3533*0Sstevel@tonic-gate
3534*0Sstevel@tonic-gatesub const_sv {
3535*0Sstevel@tonic-gate    my $self = shift;
3536*0Sstevel@tonic-gate    my $op = shift;
3537*0Sstevel@tonic-gate    my $sv = $op->sv;
3538*0Sstevel@tonic-gate    # the constant could be in the pad (under useithreads)
3539*0Sstevel@tonic-gate    $sv = $self->padval($op->targ) unless $$sv;
3540*0Sstevel@tonic-gate    return $sv;
3541*0Sstevel@tonic-gate}
3542*0Sstevel@tonic-gate
3543*0Sstevel@tonic-gatesub pp_const {
3544*0Sstevel@tonic-gate    my $self = shift;
3545*0Sstevel@tonic-gate    my($op, $cx) = @_;
3546*0Sstevel@tonic-gate    if ($op->private & OPpCONST_ARYBASE) {
3547*0Sstevel@tonic-gate        return '$[';
3548*0Sstevel@tonic-gate    }
3549*0Sstevel@tonic-gate#    if ($op->private & OPpCONST_BARE) { # trouble with `=>' autoquoting
3550*0Sstevel@tonic-gate#	return $self->const_sv($op)->PV;
3551*0Sstevel@tonic-gate#    }
3552*0Sstevel@tonic-gate    my $sv = $self->const_sv($op);
3553*0Sstevel@tonic-gate    return $self->const($sv, $cx);
3554*0Sstevel@tonic-gate}
3555*0Sstevel@tonic-gate
3556*0Sstevel@tonic-gatesub dq {
3557*0Sstevel@tonic-gate    my $self = shift;
3558*0Sstevel@tonic-gate    my $op = shift;
3559*0Sstevel@tonic-gate    my $type = $op->name;
3560*0Sstevel@tonic-gate    if ($type eq "const") {
3561*0Sstevel@tonic-gate	return '$[' if $op->private & OPpCONST_ARYBASE;
3562*0Sstevel@tonic-gate	return uninterp(escape_str(unback($self->const_sv($op)->as_string)));
3563*0Sstevel@tonic-gate    } elsif ($type eq "concat") {
3564*0Sstevel@tonic-gate	my $first = $self->dq($op->first);
3565*0Sstevel@tonic-gate	my $last  = $self->dq($op->last);
3566*0Sstevel@tonic-gate
3567*0Sstevel@tonic-gate	# Disambiguate "${foo}bar", "${foo}{bar}", "${foo}[1]", "$foo\::bar"
3568*0Sstevel@tonic-gate	($last =~ /^[A-Z\\\^\[\]_?]/ &&
3569*0Sstevel@tonic-gate	    $first =~ s/([\$@])\^$/${1}{^}/)  # "${^}W" etc
3570*0Sstevel@tonic-gate	    || ($last =~ /^[:'{\[\w_]/ && #'
3571*0Sstevel@tonic-gate		$first =~ s/([\$@])([A-Za-z_]\w*)$/${1}{$2}/);
3572*0Sstevel@tonic-gate
3573*0Sstevel@tonic-gate	return $first . $last;
3574*0Sstevel@tonic-gate    } elsif ($type eq "uc") {
3575*0Sstevel@tonic-gate	return '\U' . $self->dq($op->first->sibling) . '\E';
3576*0Sstevel@tonic-gate    } elsif ($type eq "lc") {
3577*0Sstevel@tonic-gate	return '\L' . $self->dq($op->first->sibling) . '\E';
3578*0Sstevel@tonic-gate    } elsif ($type eq "ucfirst") {
3579*0Sstevel@tonic-gate	return '\u' . $self->dq($op->first->sibling);
3580*0Sstevel@tonic-gate    } elsif ($type eq "lcfirst") {
3581*0Sstevel@tonic-gate	return '\l' . $self->dq($op->first->sibling);
3582*0Sstevel@tonic-gate    } elsif ($type eq "quotemeta") {
3583*0Sstevel@tonic-gate	return '\Q' . $self->dq($op->first->sibling) . '\E';
3584*0Sstevel@tonic-gate    } elsif ($type eq "join") {
3585*0Sstevel@tonic-gate	return $self->deparse($op->last, 26); # was join($", @ary)
3586*0Sstevel@tonic-gate    } else {
3587*0Sstevel@tonic-gate	return $self->deparse($op, 26);
3588*0Sstevel@tonic-gate    }
3589*0Sstevel@tonic-gate}
3590*0Sstevel@tonic-gate
3591*0Sstevel@tonic-gatesub pp_backtick {
3592*0Sstevel@tonic-gate    my $self = shift;
3593*0Sstevel@tonic-gate    my($op, $cx) = @_;
3594*0Sstevel@tonic-gate    # skip pushmark
3595*0Sstevel@tonic-gate    return single_delim("qx", '`', $self->dq($op->first->sibling));
3596*0Sstevel@tonic-gate}
3597*0Sstevel@tonic-gate
3598*0Sstevel@tonic-gatesub dquote {
3599*0Sstevel@tonic-gate    my $self = shift;
3600*0Sstevel@tonic-gate    my($op, $cx) = @_;
3601*0Sstevel@tonic-gate    my $kid = $op->first->sibling; # skip ex-stringify, pushmark
3602*0Sstevel@tonic-gate    return $self->deparse($kid, $cx) if $self->{'unquote'};
3603*0Sstevel@tonic-gate    $self->maybe_targmy($kid, $cx,
3604*0Sstevel@tonic-gate			sub {single_delim("qq", '"', $self->dq($_[1]))});
3605*0Sstevel@tonic-gate}
3606*0Sstevel@tonic-gate
3607*0Sstevel@tonic-gate# OP_STRINGIFY is a listop, but it only ever has one arg
3608*0Sstevel@tonic-gatesub pp_stringify { maybe_targmy(@_, \&dquote) }
3609*0Sstevel@tonic-gate
3610*0Sstevel@tonic-gate# tr/// and s/// (and tr[][], tr[]//, tr###, etc)
3611*0Sstevel@tonic-gate# note that tr(from)/to/ is OK, but not tr/from/(to)
3612*0Sstevel@tonic-gatesub double_delim {
3613*0Sstevel@tonic-gate    my($from, $to) = @_;
3614*0Sstevel@tonic-gate    my($succeed, $delim);
3615*0Sstevel@tonic-gate    if ($from !~ m[/] and $to !~ m[/]) {
3616*0Sstevel@tonic-gate	return "/$from/$to/";
3617*0Sstevel@tonic-gate    } elsif (($succeed, $from) = balanced_delim($from) and $succeed) {
3618*0Sstevel@tonic-gate	if (($succeed, $to) = balanced_delim($to) and $succeed) {
3619*0Sstevel@tonic-gate	    return "$from$to";
3620*0Sstevel@tonic-gate	} else {
3621*0Sstevel@tonic-gate	    for $delim ('/', '"', '#') { # note no `'' -- s''' is special
3622*0Sstevel@tonic-gate		return "$from$delim$to$delim" if index($to, $delim) == -1;
3623*0Sstevel@tonic-gate	    }
3624*0Sstevel@tonic-gate	    $to =~ s[/][\\/]g;
3625*0Sstevel@tonic-gate	    return "$from/$to/";
3626*0Sstevel@tonic-gate	}
3627*0Sstevel@tonic-gate    } else {
3628*0Sstevel@tonic-gate	for $delim ('/', '"', '#') { # note no '
3629*0Sstevel@tonic-gate	    return "$delim$from$delim$to$delim"
3630*0Sstevel@tonic-gate		if index($to . $from, $delim) == -1;
3631*0Sstevel@tonic-gate	}
3632*0Sstevel@tonic-gate	$from =~ s[/][\\/]g;
3633*0Sstevel@tonic-gate	$to =~ s[/][\\/]g;
3634*0Sstevel@tonic-gate	return "/$from/$to/";
3635*0Sstevel@tonic-gate    }
3636*0Sstevel@tonic-gate}
3637*0Sstevel@tonic-gate
3638*0Sstevel@tonic-gate# Only used by tr///, so backslashes hyphens
3639*0Sstevel@tonic-gatesub pchr { # ASCII
3640*0Sstevel@tonic-gate    my($n) = @_;
3641*0Sstevel@tonic-gate    if ($n == ord '\\') {
3642*0Sstevel@tonic-gate	return '\\\\';
3643*0Sstevel@tonic-gate    } elsif ($n == ord "-") {
3644*0Sstevel@tonic-gate	return "\\-";
3645*0Sstevel@tonic-gate    } elsif ($n >= ord(' ') and $n <= ord('~')) {
3646*0Sstevel@tonic-gate	return chr($n);
3647*0Sstevel@tonic-gate    } elsif ($n == ord "\a") {
3648*0Sstevel@tonic-gate	return '\\a';
3649*0Sstevel@tonic-gate    } elsif ($n == ord "\b") {
3650*0Sstevel@tonic-gate	return '\\b';
3651*0Sstevel@tonic-gate    } elsif ($n == ord "\t") {
3652*0Sstevel@tonic-gate	return '\\t';
3653*0Sstevel@tonic-gate    } elsif ($n == ord "\n") {
3654*0Sstevel@tonic-gate	return '\\n';
3655*0Sstevel@tonic-gate    } elsif ($n == ord "\e") {
3656*0Sstevel@tonic-gate	return '\\e';
3657*0Sstevel@tonic-gate    } elsif ($n == ord "\f") {
3658*0Sstevel@tonic-gate	return '\\f';
3659*0Sstevel@tonic-gate    } elsif ($n == ord "\r") {
3660*0Sstevel@tonic-gate	return '\\r';
3661*0Sstevel@tonic-gate    } elsif ($n >= ord("\cA") and $n <= ord("\cZ")) {
3662*0Sstevel@tonic-gate	return '\\c' . chr(ord("@") + $n);
3663*0Sstevel@tonic-gate    } else {
3664*0Sstevel@tonic-gate#	return '\x' . sprintf("%02x", $n);
3665*0Sstevel@tonic-gate	return '\\' . sprintf("%03o", $n);
3666*0Sstevel@tonic-gate    }
3667*0Sstevel@tonic-gate}
3668*0Sstevel@tonic-gate
3669*0Sstevel@tonic-gatesub collapse {
3670*0Sstevel@tonic-gate    my(@chars) = @_;
3671*0Sstevel@tonic-gate    my($str, $c, $tr) = ("");
3672*0Sstevel@tonic-gate    for ($c = 0; $c < @chars; $c++) {
3673*0Sstevel@tonic-gate	$tr = $chars[$c];
3674*0Sstevel@tonic-gate	$str .= pchr($tr);
3675*0Sstevel@tonic-gate	if ($c <= $#chars - 2 and $chars[$c + 1] == $tr + 1 and
3676*0Sstevel@tonic-gate	    $chars[$c + 2] == $tr + 2)
3677*0Sstevel@tonic-gate	{
3678*0Sstevel@tonic-gate	    for (; $c <= $#chars-1 and $chars[$c + 1] == $chars[$c] + 1; $c++)
3679*0Sstevel@tonic-gate	      {}
3680*0Sstevel@tonic-gate	    $str .= "-";
3681*0Sstevel@tonic-gate	    $str .= pchr($chars[$c]);
3682*0Sstevel@tonic-gate	}
3683*0Sstevel@tonic-gate    }
3684*0Sstevel@tonic-gate    return $str;
3685*0Sstevel@tonic-gate}
3686*0Sstevel@tonic-gate
3687*0Sstevel@tonic-gatesub tr_decode_byte {
3688*0Sstevel@tonic-gate    my($table, $flags) = @_;
3689*0Sstevel@tonic-gate    my(@table) = unpack("s*", $table);
3690*0Sstevel@tonic-gate    splice @table, 0x100, 1;   # Number of subsequent elements
3691*0Sstevel@tonic-gate    my($c, $tr, @from, @to, @delfrom, $delhyphen);
3692*0Sstevel@tonic-gate    if ($table[ord "-"] != -1 and
3693*0Sstevel@tonic-gate	$table[ord("-") - 1] == -1 || $table[ord("-") + 1] == -1)
3694*0Sstevel@tonic-gate    {
3695*0Sstevel@tonic-gate	$tr = $table[ord "-"];
3696*0Sstevel@tonic-gate	$table[ord "-"] = -1;
3697*0Sstevel@tonic-gate	if ($tr >= 0) {
3698*0Sstevel@tonic-gate	    @from = ord("-");
3699*0Sstevel@tonic-gate	    @to = $tr;
3700*0Sstevel@tonic-gate	} else { # -2 ==> delete
3701*0Sstevel@tonic-gate	    $delhyphen = 1;
3702*0Sstevel@tonic-gate	}
3703*0Sstevel@tonic-gate    }
3704*0Sstevel@tonic-gate    for ($c = 0; $c < @table; $c++) {
3705*0Sstevel@tonic-gate	$tr = $table[$c];
3706*0Sstevel@tonic-gate	if ($tr >= 0) {
3707*0Sstevel@tonic-gate	    push @from, $c; push @to, $tr;
3708*0Sstevel@tonic-gate	} elsif ($tr == -2) {
3709*0Sstevel@tonic-gate	    push @delfrom, $c;
3710*0Sstevel@tonic-gate	}
3711*0Sstevel@tonic-gate    }
3712*0Sstevel@tonic-gate    @from = (@from, @delfrom);
3713*0Sstevel@tonic-gate    if ($flags & OPpTRANS_COMPLEMENT) {
3714*0Sstevel@tonic-gate	my @newfrom = ();
3715*0Sstevel@tonic-gate	my %from;
3716*0Sstevel@tonic-gate	@from{@from} = (1) x @from;
3717*0Sstevel@tonic-gate	for ($c = 0; $c < 256; $c++) {
3718*0Sstevel@tonic-gate	    push @newfrom, $c unless $from{$c};
3719*0Sstevel@tonic-gate	}
3720*0Sstevel@tonic-gate	@from = @newfrom;
3721*0Sstevel@tonic-gate    }
3722*0Sstevel@tonic-gate    unless ($flags & OPpTRANS_DELETE || !@to) {
3723*0Sstevel@tonic-gate	pop @to while $#to and $to[$#to] == $to[$#to -1];
3724*0Sstevel@tonic-gate    }
3725*0Sstevel@tonic-gate    my($from, $to);
3726*0Sstevel@tonic-gate    $from = collapse(@from);
3727*0Sstevel@tonic-gate    $to = collapse(@to);
3728*0Sstevel@tonic-gate    $from .= "-" if $delhyphen;
3729*0Sstevel@tonic-gate    return ($from, $to);
3730*0Sstevel@tonic-gate}
3731*0Sstevel@tonic-gate
3732*0Sstevel@tonic-gatesub tr_chr {
3733*0Sstevel@tonic-gate    my $x = shift;
3734*0Sstevel@tonic-gate    if ($x == ord "-") {
3735*0Sstevel@tonic-gate	return "\\-";
3736*0Sstevel@tonic-gate    } elsif ($x == ord "\\") {
3737*0Sstevel@tonic-gate	return "\\\\";
3738*0Sstevel@tonic-gate    } else {
3739*0Sstevel@tonic-gate	return chr $x;
3740*0Sstevel@tonic-gate    }
3741*0Sstevel@tonic-gate}
3742*0Sstevel@tonic-gate
3743*0Sstevel@tonic-gate# XXX This doesn't yet handle all cases correctly either
3744*0Sstevel@tonic-gate
3745*0Sstevel@tonic-gatesub tr_decode_utf8 {
3746*0Sstevel@tonic-gate    my($swash_hv, $flags) = @_;
3747*0Sstevel@tonic-gate    my %swash = $swash_hv->ARRAY;
3748*0Sstevel@tonic-gate    my $final = undef;
3749*0Sstevel@tonic-gate    $final = $swash{'FINAL'}->IV if exists $swash{'FINAL'};
3750*0Sstevel@tonic-gate    my $none = $swash{"NONE"}->IV;
3751*0Sstevel@tonic-gate    my $extra = $none + 1;
3752*0Sstevel@tonic-gate    my(@from, @delfrom, @to);
3753*0Sstevel@tonic-gate    my $line;
3754*0Sstevel@tonic-gate    foreach $line (split /\n/, $swash{'LIST'}->PV) {
3755*0Sstevel@tonic-gate	my($min, $max, $result) = split(/\t/, $line);
3756*0Sstevel@tonic-gate	$min = hex $min;
3757*0Sstevel@tonic-gate	if (length $max) {
3758*0Sstevel@tonic-gate	    $max = hex $max;
3759*0Sstevel@tonic-gate	} else {
3760*0Sstevel@tonic-gate	    $max = $min;
3761*0Sstevel@tonic-gate	}
3762*0Sstevel@tonic-gate	$result = hex $result;
3763*0Sstevel@tonic-gate	if ($result == $extra) {
3764*0Sstevel@tonic-gate	    push @delfrom, [$min, $max];
3765*0Sstevel@tonic-gate	} else {
3766*0Sstevel@tonic-gate	    push @from, [$min, $max];
3767*0Sstevel@tonic-gate	    push @to, [$result, $result + $max - $min];
3768*0Sstevel@tonic-gate	}
3769*0Sstevel@tonic-gate    }
3770*0Sstevel@tonic-gate    for my $i (0 .. $#from) {
3771*0Sstevel@tonic-gate	if ($from[$i][0] == ord '-') {
3772*0Sstevel@tonic-gate	    unshift @from, splice(@from, $i, 1);
3773*0Sstevel@tonic-gate	    unshift @to, splice(@to, $i, 1);
3774*0Sstevel@tonic-gate	    last;
3775*0Sstevel@tonic-gate	} elsif ($from[$i][1] == ord '-') {
3776*0Sstevel@tonic-gate	    $from[$i][1]--;
3777*0Sstevel@tonic-gate	    $to[$i][1]--;
3778*0Sstevel@tonic-gate	    unshift @from, ord '-';
3779*0Sstevel@tonic-gate	    unshift @to, ord '-';
3780*0Sstevel@tonic-gate	    last;
3781*0Sstevel@tonic-gate	}
3782*0Sstevel@tonic-gate    }
3783*0Sstevel@tonic-gate    for my $i (0 .. $#delfrom) {
3784*0Sstevel@tonic-gate	if ($delfrom[$i][0] == ord '-') {
3785*0Sstevel@tonic-gate	    push @delfrom, splice(@delfrom, $i, 1);
3786*0Sstevel@tonic-gate	    last;
3787*0Sstevel@tonic-gate	} elsif ($delfrom[$i][1] == ord '-') {
3788*0Sstevel@tonic-gate	    $delfrom[$i][1]--;
3789*0Sstevel@tonic-gate	    push @delfrom, ord '-';
3790*0Sstevel@tonic-gate	    last;
3791*0Sstevel@tonic-gate	}
3792*0Sstevel@tonic-gate    }
3793*0Sstevel@tonic-gate    if (defined $final and $to[$#to][1] != $final) {
3794*0Sstevel@tonic-gate	push @to, [$final, $final];
3795*0Sstevel@tonic-gate    }
3796*0Sstevel@tonic-gate    push @from, @delfrom;
3797*0Sstevel@tonic-gate    if ($flags & OPpTRANS_COMPLEMENT) {
3798*0Sstevel@tonic-gate	my @newfrom;
3799*0Sstevel@tonic-gate	my $next = 0;
3800*0Sstevel@tonic-gate	for my $i (0 .. $#from) {
3801*0Sstevel@tonic-gate	    push @newfrom, [$next, $from[$i][0] - 1];
3802*0Sstevel@tonic-gate	    $next = $from[$i][1] + 1;
3803*0Sstevel@tonic-gate	}
3804*0Sstevel@tonic-gate	@from = ();
3805*0Sstevel@tonic-gate	for my $range (@newfrom) {
3806*0Sstevel@tonic-gate	    if ($range->[0] <= $range->[1]) {
3807*0Sstevel@tonic-gate		push @from, $range;
3808*0Sstevel@tonic-gate	    }
3809*0Sstevel@tonic-gate	}
3810*0Sstevel@tonic-gate    }
3811*0Sstevel@tonic-gate    my($from, $to, $diff);
3812*0Sstevel@tonic-gate    for my $chunk (@from) {
3813*0Sstevel@tonic-gate	$diff = $chunk->[1] - $chunk->[0];
3814*0Sstevel@tonic-gate	if ($diff > 1) {
3815*0Sstevel@tonic-gate	    $from .= tr_chr($chunk->[0]) . "-" . tr_chr($chunk->[1]);
3816*0Sstevel@tonic-gate	} elsif ($diff == 1) {
3817*0Sstevel@tonic-gate	    $from .= tr_chr($chunk->[0]) . tr_chr($chunk->[1]);
3818*0Sstevel@tonic-gate	} else {
3819*0Sstevel@tonic-gate	    $from .= tr_chr($chunk->[0]);
3820*0Sstevel@tonic-gate	}
3821*0Sstevel@tonic-gate    }
3822*0Sstevel@tonic-gate    for my $chunk (@to) {
3823*0Sstevel@tonic-gate	$diff = $chunk->[1] - $chunk->[0];
3824*0Sstevel@tonic-gate	if ($diff > 1) {
3825*0Sstevel@tonic-gate	    $to .= tr_chr($chunk->[0]) . "-" . tr_chr($chunk->[1]);
3826*0Sstevel@tonic-gate	} elsif ($diff == 1) {
3827*0Sstevel@tonic-gate	    $to .= tr_chr($chunk->[0]) . tr_chr($chunk->[1]);
3828*0Sstevel@tonic-gate	} else {
3829*0Sstevel@tonic-gate	    $to .= tr_chr($chunk->[0]);
3830*0Sstevel@tonic-gate	}
3831*0Sstevel@tonic-gate    }
3832*0Sstevel@tonic-gate    #$final = sprintf("%04x", $final) if defined $final;
3833*0Sstevel@tonic-gate    #$none = sprintf("%04x", $none) if defined $none;
3834*0Sstevel@tonic-gate    #$extra = sprintf("%04x", $extra) if defined $extra;
3835*0Sstevel@tonic-gate    #print STDERR "final: $final\n none: $none\nextra: $extra\n";
3836*0Sstevel@tonic-gate    #print STDERR $swash{'LIST'}->PV;
3837*0Sstevel@tonic-gate    return (escape_str($from), escape_str($to));
3838*0Sstevel@tonic-gate}
3839*0Sstevel@tonic-gate
3840*0Sstevel@tonic-gatesub pp_trans {
3841*0Sstevel@tonic-gate    my $self = shift;
3842*0Sstevel@tonic-gate    my($op, $cx) = @_;
3843*0Sstevel@tonic-gate    my($from, $to);
3844*0Sstevel@tonic-gate    if (class($op) eq "PVOP") {
3845*0Sstevel@tonic-gate	($from, $to) = tr_decode_byte($op->pv, $op->private);
3846*0Sstevel@tonic-gate    } else { # class($op) eq "SVOP"
3847*0Sstevel@tonic-gate	($from, $to) = tr_decode_utf8($op->sv->RV, $op->private);
3848*0Sstevel@tonic-gate    }
3849*0Sstevel@tonic-gate    my $flags = "";
3850*0Sstevel@tonic-gate    $flags .= "c" if $op->private & OPpTRANS_COMPLEMENT;
3851*0Sstevel@tonic-gate    $flags .= "d" if $op->private & OPpTRANS_DELETE;
3852*0Sstevel@tonic-gate    $to = "" if $from eq $to and $flags eq "";
3853*0Sstevel@tonic-gate    $flags .= "s" if $op->private & OPpTRANS_SQUASH;
3854*0Sstevel@tonic-gate    return "tr" . double_delim($from, $to) . $flags;
3855*0Sstevel@tonic-gate}
3856*0Sstevel@tonic-gate
3857*0Sstevel@tonic-gate# Like dq(), but different
3858*0Sstevel@tonic-gatesub re_dq {
3859*0Sstevel@tonic-gate    my $self = shift;
3860*0Sstevel@tonic-gate    my ($op, $extended) = @_;
3861*0Sstevel@tonic-gate
3862*0Sstevel@tonic-gate    my $type = $op->name;
3863*0Sstevel@tonic-gate    if ($type eq "const") {
3864*0Sstevel@tonic-gate	return '$[' if $op->private & OPpCONST_ARYBASE;
3865*0Sstevel@tonic-gate	my $unbacked = re_unback($self->const_sv($op)->as_string);
3866*0Sstevel@tonic-gate	return re_uninterp_extended(escape_extended_re($unbacked))
3867*0Sstevel@tonic-gate	    if $extended;
3868*0Sstevel@tonic-gate	return re_uninterp(escape_str($unbacked));
3869*0Sstevel@tonic-gate    } elsif ($type eq "concat") {
3870*0Sstevel@tonic-gate	my $first = $self->re_dq($op->first, $extended);
3871*0Sstevel@tonic-gate	my $last  = $self->re_dq($op->last,  $extended);
3872*0Sstevel@tonic-gate
3873*0Sstevel@tonic-gate	# Disambiguate "${foo}bar", "${foo}{bar}", "${foo}[1]"
3874*0Sstevel@tonic-gate	($last =~ /^[A-Z\\\^\[\]_?]/ &&
3875*0Sstevel@tonic-gate	    $first =~ s/([\$@])\^$/${1}{^}/)  # "${^}W" etc
3876*0Sstevel@tonic-gate	    || ($last =~ /^[{\[\w_]/ &&
3877*0Sstevel@tonic-gate		$first =~ s/([\$@])([A-Za-z_]\w*)$/${1}{$2}/);
3878*0Sstevel@tonic-gate
3879*0Sstevel@tonic-gate	return $first . $last;
3880*0Sstevel@tonic-gate    } elsif ($type eq "uc") {
3881*0Sstevel@tonic-gate	return '\U' . $self->re_dq($op->first->sibling, $extended) . '\E';
3882*0Sstevel@tonic-gate    } elsif ($type eq "lc") {
3883*0Sstevel@tonic-gate	return '\L' . $self->re_dq($op->first->sibling, $extended) . '\E';
3884*0Sstevel@tonic-gate    } elsif ($type eq "ucfirst") {
3885*0Sstevel@tonic-gate	return '\u' . $self->re_dq($op->first->sibling, $extended);
3886*0Sstevel@tonic-gate    } elsif ($type eq "lcfirst") {
3887*0Sstevel@tonic-gate	return '\l' . $self->re_dq($op->first->sibling, $extended);
3888*0Sstevel@tonic-gate    } elsif ($type eq "quotemeta") {
3889*0Sstevel@tonic-gate	return '\Q' . $self->re_dq($op->first->sibling, $extended) . '\E';
3890*0Sstevel@tonic-gate    } elsif ($type eq "join") {
3891*0Sstevel@tonic-gate	return $self->deparse($op->last, 26); # was join($", @ary)
3892*0Sstevel@tonic-gate    } else {
3893*0Sstevel@tonic-gate	return $self->deparse($op, 26);
3894*0Sstevel@tonic-gate    }
3895*0Sstevel@tonic-gate}
3896*0Sstevel@tonic-gate
3897*0Sstevel@tonic-gatesub pure_string {
3898*0Sstevel@tonic-gate    my ($self, $op) = @_;
3899*0Sstevel@tonic-gate    return 0 if null $op;
3900*0Sstevel@tonic-gate    my $type = $op->name;
3901*0Sstevel@tonic-gate
3902*0Sstevel@tonic-gate    if ($type eq 'const') {
3903*0Sstevel@tonic-gate	return 1;
3904*0Sstevel@tonic-gate    }
3905*0Sstevel@tonic-gate    elsif ($type =~ /^[ul]c(first)?$/ || $type eq 'quotemeta') {
3906*0Sstevel@tonic-gate	return $self->pure_string($op->first->sibling);
3907*0Sstevel@tonic-gate    }
3908*0Sstevel@tonic-gate    elsif ($type eq 'join') {
3909*0Sstevel@tonic-gate	my $join_op = $op->first->sibling;  # Skip pushmark
3910*0Sstevel@tonic-gate	return 0 unless $join_op->name eq 'null' && $join_op->targ eq OP_RV2SV;
3911*0Sstevel@tonic-gate
3912*0Sstevel@tonic-gate	my $gvop = $join_op->first;
3913*0Sstevel@tonic-gate	return 0 unless $gvop->name eq 'gvsv';
3914*0Sstevel@tonic-gate        return 0 unless '"' eq $self->gv_name($self->gv_or_padgv($gvop));
3915*0Sstevel@tonic-gate
3916*0Sstevel@tonic-gate	return 0 unless ${$join_op->sibling} eq ${$op->last};
3917*0Sstevel@tonic-gate	return 0 unless $op->last->name =~ /^(rv2|pad)av$/;
3918*0Sstevel@tonic-gate    }
3919*0Sstevel@tonic-gate    elsif ($type eq 'concat') {
3920*0Sstevel@tonic-gate	return $self->pure_string($op->first)
3921*0Sstevel@tonic-gate            && $self->pure_string($op->last);
3922*0Sstevel@tonic-gate    }
3923*0Sstevel@tonic-gate    elsif (is_scalar($op) || $type =~ /^[ah]elem$/) {
3924*0Sstevel@tonic-gate	return 1;
3925*0Sstevel@tonic-gate    }
3926*0Sstevel@tonic-gate    elsif ($type eq "null" and $op->can('first') and not null $op->first and
3927*0Sstevel@tonic-gate	   $op->first->name eq "null" and $op->first->can('first')
3928*0Sstevel@tonic-gate	   and not null $op->first->first and
3929*0Sstevel@tonic-gate	   $op->first->first->name eq "aelemfast") {
3930*0Sstevel@tonic-gate	return 1;
3931*0Sstevel@tonic-gate    }
3932*0Sstevel@tonic-gate    else {
3933*0Sstevel@tonic-gate	return 0;
3934*0Sstevel@tonic-gate    }
3935*0Sstevel@tonic-gate
3936*0Sstevel@tonic-gate    return 1;
3937*0Sstevel@tonic-gate}
3938*0Sstevel@tonic-gate
3939*0Sstevel@tonic-gatesub regcomp {
3940*0Sstevel@tonic-gate    my $self = shift;
3941*0Sstevel@tonic-gate    my($op, $cx, $extended) = @_;
3942*0Sstevel@tonic-gate    my $kid = $op->first;
3943*0Sstevel@tonic-gate    $kid = $kid->first if $kid->name eq "regcmaybe";
3944*0Sstevel@tonic-gate    $kid = $kid->first if $kid->name eq "regcreset";
3945*0Sstevel@tonic-gate    return ($self->re_dq($kid, $extended), 1) if $self->pure_string($kid);
3946*0Sstevel@tonic-gate    return ($self->deparse($kid, $cx), 0);
3947*0Sstevel@tonic-gate}
3948*0Sstevel@tonic-gate
3949*0Sstevel@tonic-gatesub pp_regcomp {
3950*0Sstevel@tonic-gate    my ($self, $op, $cx) = @_;
3951*0Sstevel@tonic-gate    return (($self->regcomp($op, $cx, 0))[0]);
3952*0Sstevel@tonic-gate}
3953*0Sstevel@tonic-gate
3954*0Sstevel@tonic-gate# osmic acid -- see osmium tetroxide
3955*0Sstevel@tonic-gate
3956*0Sstevel@tonic-gatemy %matchwords;
3957*0Sstevel@tonic-gatemap($matchwords{join "", sort split //, $_} = $_, 'cig', 'cog', 'cos', 'cogs',
3958*0Sstevel@tonic-gate    'cox', 'go', 'is', 'ism', 'iso', 'mig', 'mix', 'osmic', 'ox', 'sic',
3959*0Sstevel@tonic-gate    'sig', 'six', 'smog', 'so', 'soc', 'sog', 'xi');
3960*0Sstevel@tonic-gate
3961*0Sstevel@tonic-gatesub matchop {
3962*0Sstevel@tonic-gate    my $self = shift;
3963*0Sstevel@tonic-gate    my($op, $cx, $name, $delim) = @_;
3964*0Sstevel@tonic-gate    my $kid = $op->first;
3965*0Sstevel@tonic-gate    my ($binop, $var, $re) = ("", "", "");
3966*0Sstevel@tonic-gate    if ($op->flags & OPf_STACKED) {
3967*0Sstevel@tonic-gate	$binop = 1;
3968*0Sstevel@tonic-gate	$var = $self->deparse($kid, 20);
3969*0Sstevel@tonic-gate	$kid = $kid->sibling;
3970*0Sstevel@tonic-gate    }
3971*0Sstevel@tonic-gate    my $quote = 1;
3972*0Sstevel@tonic-gate    my $extended = ($op->pmflags & PMf_EXTENDED);
3973*0Sstevel@tonic-gate    if (null $kid) {
3974*0Sstevel@tonic-gate	my $unbacked = re_unback($op->precomp);
3975*0Sstevel@tonic-gate	if ($extended) {
3976*0Sstevel@tonic-gate	    $re = re_uninterp_extended(escape_extended_re($unbacked));
3977*0Sstevel@tonic-gate	} else {
3978*0Sstevel@tonic-gate	    $re = re_uninterp(escape_str(re_unback($op->precomp)));
3979*0Sstevel@tonic-gate	}
3980*0Sstevel@tonic-gate    } elsif ($kid->name ne 'regcomp') {
3981*0Sstevel@tonic-gate	carp("found ".$kid->name." where regcomp expected");
3982*0Sstevel@tonic-gate    } else {
3983*0Sstevel@tonic-gate	($re, $quote) = $self->regcomp($kid, 21, $extended);
3984*0Sstevel@tonic-gate    }
3985*0Sstevel@tonic-gate    my $flags = "";
3986*0Sstevel@tonic-gate    $flags .= "c" if $op->pmflags & PMf_CONTINUE;
3987*0Sstevel@tonic-gate    $flags .= "g" if $op->pmflags & PMf_GLOBAL;
3988*0Sstevel@tonic-gate    $flags .= "i" if $op->pmflags & PMf_FOLD;
3989*0Sstevel@tonic-gate    $flags .= "m" if $op->pmflags & PMf_MULTILINE;
3990*0Sstevel@tonic-gate    $flags .= "o" if $op->pmflags & PMf_KEEP;
3991*0Sstevel@tonic-gate    $flags .= "s" if $op->pmflags & PMf_SINGLELINE;
3992*0Sstevel@tonic-gate    $flags .= "x" if $op->pmflags & PMf_EXTENDED;
3993*0Sstevel@tonic-gate    $flags = $matchwords{$flags} if $matchwords{$flags};
3994*0Sstevel@tonic-gate    if ($op->pmflags & PMf_ONCE) { # only one kind of delimiter works here
3995*0Sstevel@tonic-gate	$re =~ s/\?/\\?/g;
3996*0Sstevel@tonic-gate	$re = "?$re?";
3997*0Sstevel@tonic-gate    } elsif ($quote) {
3998*0Sstevel@tonic-gate	$re = single_delim($name, $delim, $re);
3999*0Sstevel@tonic-gate    }
4000*0Sstevel@tonic-gate    $re = $re . $flags if $quote;
4001*0Sstevel@tonic-gate    if ($binop) {
4002*0Sstevel@tonic-gate	return $self->maybe_parens("$var =~ $re", $cx, 20);
4003*0Sstevel@tonic-gate    } else {
4004*0Sstevel@tonic-gate	return $re;
4005*0Sstevel@tonic-gate    }
4006*0Sstevel@tonic-gate}
4007*0Sstevel@tonic-gate
4008*0Sstevel@tonic-gatesub pp_match { matchop(@_, "m", "/") }
4009*0Sstevel@tonic-gatesub pp_pushre { matchop(@_, "m", "/") }
4010*0Sstevel@tonic-gatesub pp_qr { matchop(@_, "qr", "") }
4011*0Sstevel@tonic-gate
4012*0Sstevel@tonic-gatesub pp_split {
4013*0Sstevel@tonic-gate    my $self = shift;
4014*0Sstevel@tonic-gate    my($op, $cx) = @_;
4015*0Sstevel@tonic-gate    my($kid, @exprs, $ary, $expr);
4016*0Sstevel@tonic-gate    $kid = $op->first;
4017*0Sstevel@tonic-gate
4018*0Sstevel@tonic-gate    # For our kid (an OP_PUSHRE), pmreplroot is never actually the
4019*0Sstevel@tonic-gate    # root of a replacement; it's either empty, or abused to point to
4020*0Sstevel@tonic-gate    # the GV for an array we split into (an optimization to save
4021*0Sstevel@tonic-gate    # assignment overhead). Depending on whether we're using ithreads,
4022*0Sstevel@tonic-gate    # this OP* holds either a GV* or a PADOFFSET. Luckily, B.xs
4023*0Sstevel@tonic-gate    # figures out for us which it is.
4024*0Sstevel@tonic-gate    my $replroot = $kid->pmreplroot;
4025*0Sstevel@tonic-gate    my $gv = 0;
4026*0Sstevel@tonic-gate    if (ref($replroot) eq "B::GV") {
4027*0Sstevel@tonic-gate	$gv = $replroot;
4028*0Sstevel@tonic-gate    } elsif (!ref($replroot) and $replroot > 0) {
4029*0Sstevel@tonic-gate	$gv = $self->padval($replroot);
4030*0Sstevel@tonic-gate    }
4031*0Sstevel@tonic-gate    $ary = $self->stash_variable('@', $self->gv_name($gv)) if $gv;
4032*0Sstevel@tonic-gate
4033*0Sstevel@tonic-gate    for (; !null($kid); $kid = $kid->sibling) {
4034*0Sstevel@tonic-gate	push @exprs, $self->deparse($kid, 6);
4035*0Sstevel@tonic-gate    }
4036*0Sstevel@tonic-gate
4037*0Sstevel@tonic-gate    # handle special case of split(), and split(" ") that compiles to /\s+/
4038*0Sstevel@tonic-gate    $kid = $op->first;
4039*0Sstevel@tonic-gate    if ($kid->flags & OPf_SPECIAL
4040*0Sstevel@tonic-gate	&& $exprs[0] eq '/\\s+/'
4041*0Sstevel@tonic-gate	&& $kid->pmflags & PMf_SKIPWHITE ) {
4042*0Sstevel@tonic-gate	    $exprs[0] = '" "';
4043*0Sstevel@tonic-gate    }
4044*0Sstevel@tonic-gate
4045*0Sstevel@tonic-gate    $expr = "split(" . join(", ", @exprs) . ")";
4046*0Sstevel@tonic-gate    if ($ary) {
4047*0Sstevel@tonic-gate	return $self->maybe_parens("$ary = $expr", $cx, 7);
4048*0Sstevel@tonic-gate    } else {
4049*0Sstevel@tonic-gate	return $expr;
4050*0Sstevel@tonic-gate    }
4051*0Sstevel@tonic-gate}
4052*0Sstevel@tonic-gate
4053*0Sstevel@tonic-gate# oxime -- any of various compounds obtained chiefly by the action of
4054*0Sstevel@tonic-gate# hydroxylamine on aldehydes and ketones and characterized by the
4055*0Sstevel@tonic-gate# bivalent grouping C=NOH [Webster's Tenth]
4056*0Sstevel@tonic-gate
4057*0Sstevel@tonic-gatemy %substwords;
4058*0Sstevel@tonic-gatemap($substwords{join "", sort split //, $_} = $_, 'ego', 'egoism', 'em',
4059*0Sstevel@tonic-gate    'es', 'ex', 'exes', 'gee', 'go', 'goes', 'ie', 'ism', 'iso', 'me',
4060*0Sstevel@tonic-gate    'meese', 'meso', 'mig', 'mix', 'os', 'ox', 'oxime', 'see', 'seem',
4061*0Sstevel@tonic-gate    'seg', 'sex', 'sig', 'six', 'smog', 'sog', 'some', 'xi');
4062*0Sstevel@tonic-gate
4063*0Sstevel@tonic-gatesub pp_subst {
4064*0Sstevel@tonic-gate    my $self = shift;
4065*0Sstevel@tonic-gate    my($op, $cx) = @_;
4066*0Sstevel@tonic-gate    my $kid = $op->first;
4067*0Sstevel@tonic-gate    my($binop, $var, $re, $repl) = ("", "", "", "");
4068*0Sstevel@tonic-gate    if ($op->flags & OPf_STACKED) {
4069*0Sstevel@tonic-gate	$binop = 1;
4070*0Sstevel@tonic-gate	$var = $self->deparse($kid, 20);
4071*0Sstevel@tonic-gate	$kid = $kid->sibling;
4072*0Sstevel@tonic-gate    }
4073*0Sstevel@tonic-gate    my $flags = "";
4074*0Sstevel@tonic-gate    if (null($op->pmreplroot)) {
4075*0Sstevel@tonic-gate	$repl = $self->dq($kid);
4076*0Sstevel@tonic-gate	$kid = $kid->sibling;
4077*0Sstevel@tonic-gate    } else {
4078*0Sstevel@tonic-gate	$repl = $op->pmreplroot->first; # skip substcont
4079*0Sstevel@tonic-gate	while ($repl->name eq "entereval") {
4080*0Sstevel@tonic-gate	    $repl = $repl->first;
4081*0Sstevel@tonic-gate	    $flags .= "e";
4082*0Sstevel@tonic-gate	}
4083*0Sstevel@tonic-gate	if ($op->pmflags & PMf_EVAL) {
4084*0Sstevel@tonic-gate	    $repl = $self->deparse($repl->first, 0);
4085*0Sstevel@tonic-gate	} else {
4086*0Sstevel@tonic-gate	    $repl = $self->dq($repl);
4087*0Sstevel@tonic-gate	}
4088*0Sstevel@tonic-gate    }
4089*0Sstevel@tonic-gate    my $extended = ($op->pmflags & PMf_EXTENDED);
4090*0Sstevel@tonic-gate    if (null $kid) {
4091*0Sstevel@tonic-gate	my $unbacked = re_unback($op->precomp);
4092*0Sstevel@tonic-gate	if ($extended) {
4093*0Sstevel@tonic-gate	    $re = re_uninterp_extended(escape_extended_re($unbacked));
4094*0Sstevel@tonic-gate	}
4095*0Sstevel@tonic-gate	else {
4096*0Sstevel@tonic-gate	    $re = re_uninterp(escape_str($unbacked));
4097*0Sstevel@tonic-gate	}
4098*0Sstevel@tonic-gate    } else {
4099*0Sstevel@tonic-gate	($re) = $self->regcomp($kid, 1, $extended);
4100*0Sstevel@tonic-gate    }
4101*0Sstevel@tonic-gate    $flags .= "e" if $op->pmflags & PMf_EVAL;
4102*0Sstevel@tonic-gate    $flags .= "g" if $op->pmflags & PMf_GLOBAL;
4103*0Sstevel@tonic-gate    $flags .= "i" if $op->pmflags & PMf_FOLD;
4104*0Sstevel@tonic-gate    $flags .= "m" if $op->pmflags & PMf_MULTILINE;
4105*0Sstevel@tonic-gate    $flags .= "o" if $op->pmflags & PMf_KEEP;
4106*0Sstevel@tonic-gate    $flags .= "s" if $op->pmflags & PMf_SINGLELINE;
4107*0Sstevel@tonic-gate    $flags .= "x" if $extended;
4108*0Sstevel@tonic-gate    $flags = $substwords{$flags} if $substwords{$flags};
4109*0Sstevel@tonic-gate    if ($binop) {
4110*0Sstevel@tonic-gate	return $self->maybe_parens("$var =~ s"
4111*0Sstevel@tonic-gate				   . double_delim($re, $repl) . $flags,
4112*0Sstevel@tonic-gate				   $cx, 20);
4113*0Sstevel@tonic-gate    } else {
4114*0Sstevel@tonic-gate	return "s". double_delim($re, $repl) . $flags;
4115*0Sstevel@tonic-gate    }
4116*0Sstevel@tonic-gate}
4117*0Sstevel@tonic-gate
4118*0Sstevel@tonic-gate1;
4119*0Sstevel@tonic-gate__END__
4120*0Sstevel@tonic-gate
4121*0Sstevel@tonic-gate=head1 NAME
4122*0Sstevel@tonic-gate
4123*0Sstevel@tonic-gateB::Deparse - Perl compiler backend to produce perl code
4124*0Sstevel@tonic-gate
4125*0Sstevel@tonic-gate=head1 SYNOPSIS
4126*0Sstevel@tonic-gate
4127*0Sstevel@tonic-gateB<perl> B<-MO=Deparse>[B<,-d>][B<,-f>I<FILE>][B<,-p>][B<,-q>][B<,-l>]
4128*0Sstevel@tonic-gate        [B<,-s>I<LETTERS>][B<,-x>I<LEVEL>] I<prog.pl>
4129*0Sstevel@tonic-gate
4130*0Sstevel@tonic-gate=head1 DESCRIPTION
4131*0Sstevel@tonic-gate
4132*0Sstevel@tonic-gateB::Deparse is a backend module for the Perl compiler that generates
4133*0Sstevel@tonic-gateperl source code, based on the internal compiled structure that perl
4134*0Sstevel@tonic-gateitself creates after parsing a program. The output of B::Deparse won't
4135*0Sstevel@tonic-gatebe exactly the same as the original source, since perl doesn't keep
4136*0Sstevel@tonic-gatetrack of comments or whitespace, and there isn't a one-to-one
4137*0Sstevel@tonic-gatecorrespondence between perl's syntactical constructions and their
4138*0Sstevel@tonic-gatecompiled form, but it will often be close. When you use the B<-p>
4139*0Sstevel@tonic-gateoption, the output also includes parentheses even when they are not
4140*0Sstevel@tonic-gaterequired by precedence, which can make it easy to see if perl is
4141*0Sstevel@tonic-gateparsing your expressions the way you intended.
4142*0Sstevel@tonic-gate
4143*0Sstevel@tonic-gateWhile B::Deparse goes to some lengths to try to figure out what your
4144*0Sstevel@tonic-gateoriginal program was doing, some parts of the language can still trip
4145*0Sstevel@tonic-gateit up; it still fails even on some parts of Perl's own test suite. If
4146*0Sstevel@tonic-gateyou encounter a failure other than the most common ones described in
4147*0Sstevel@tonic-gatethe BUGS section below, you can help contribute to B::Deparse's
4148*0Sstevel@tonic-gateongoing development by submitting a bug report with a small
4149*0Sstevel@tonic-gateexample.
4150*0Sstevel@tonic-gate
4151*0Sstevel@tonic-gate=head1 OPTIONS
4152*0Sstevel@tonic-gate
4153*0Sstevel@tonic-gateAs with all compiler backend options, these must follow directly after
4154*0Sstevel@tonic-gatethe '-MO=Deparse', separated by a comma but not any white space.
4155*0Sstevel@tonic-gate
4156*0Sstevel@tonic-gate=over 4
4157*0Sstevel@tonic-gate
4158*0Sstevel@tonic-gate=item B<-d>
4159*0Sstevel@tonic-gate
4160*0Sstevel@tonic-gateOutput data values (when they appear as constants) using Data::Dumper.
4161*0Sstevel@tonic-gateWithout this option, B::Deparse will use some simple routines of its
4162*0Sstevel@tonic-gateown for the same purpose. Currently, Data::Dumper is better for some
4163*0Sstevel@tonic-gatekinds of data (such as complex structures with sharing and
4164*0Sstevel@tonic-gateself-reference) while the built-in routines are better for others
4165*0Sstevel@tonic-gate(such as odd floating-point values).
4166*0Sstevel@tonic-gate
4167*0Sstevel@tonic-gate=item B<-f>I<FILE>
4168*0Sstevel@tonic-gate
4169*0Sstevel@tonic-gateNormally, B::Deparse deparses the main code of a program, and all the subs
4170*0Sstevel@tonic-gatedefined in the same file. To include subs defined in other files, pass the
4171*0Sstevel@tonic-gateB<-f> option with the filename. You can pass the B<-f> option several times, to
4172*0Sstevel@tonic-gateinclude more than one secondary file.  (Most of the time you don't want to
4173*0Sstevel@tonic-gateuse it at all.)  You can also use this option to include subs which are
4174*0Sstevel@tonic-gatedefined in the scope of a B<#line> directive with two parameters.
4175*0Sstevel@tonic-gate
4176*0Sstevel@tonic-gate=item B<-l>
4177*0Sstevel@tonic-gate
4178*0Sstevel@tonic-gateAdd '#line' declarations to the output based on the line and file
4179*0Sstevel@tonic-gatelocations of the original code.
4180*0Sstevel@tonic-gate
4181*0Sstevel@tonic-gate=item B<-p>
4182*0Sstevel@tonic-gate
4183*0Sstevel@tonic-gatePrint extra parentheses. Without this option, B::Deparse includes
4184*0Sstevel@tonic-gateparentheses in its output only when they are needed, based on the
4185*0Sstevel@tonic-gatestructure of your program. With B<-p>, it uses parentheses (almost)
4186*0Sstevel@tonic-gatewhenever they would be legal. This can be useful if you are used to
4187*0Sstevel@tonic-gateLISP, or if you want to see how perl parses your input. If you say
4188*0Sstevel@tonic-gate
4189*0Sstevel@tonic-gate    if ($var & 0x7f == 65) {print "Gimme an A!"}
4190*0Sstevel@tonic-gate    print ($which ? $a : $b), "\n";
4191*0Sstevel@tonic-gate    $name = $ENV{USER} or "Bob";
4192*0Sstevel@tonic-gate
4193*0Sstevel@tonic-gateC<B::Deparse,-p> will print
4194*0Sstevel@tonic-gate
4195*0Sstevel@tonic-gate    if (($var & 0)) {
4196*0Sstevel@tonic-gate        print('Gimme an A!')
4197*0Sstevel@tonic-gate    };
4198*0Sstevel@tonic-gate    (print(($which ? $a : $b)), '???');
4199*0Sstevel@tonic-gate    (($name = $ENV{'USER'}) or '???')
4200*0Sstevel@tonic-gate
4201*0Sstevel@tonic-gatewhich probably isn't what you intended (the C<'???'> is a sign that
4202*0Sstevel@tonic-gateperl optimized away a constant value).
4203*0Sstevel@tonic-gate
4204*0Sstevel@tonic-gate=item B<-P>
4205*0Sstevel@tonic-gate
4206*0Sstevel@tonic-gateDisable prototype checking. With this option, all function calls are
4207*0Sstevel@tonic-gatedeparsed as if no prototype was defined for them. In other words,
4208*0Sstevel@tonic-gate
4209*0Sstevel@tonic-gate    perl -MO=Deparse,-P -e 'sub foo (\@) { 1 } foo @x'
4210*0Sstevel@tonic-gate
4211*0Sstevel@tonic-gatewill print
4212*0Sstevel@tonic-gate
4213*0Sstevel@tonic-gate    sub foo (\@) {
4214*0Sstevel@tonic-gate	1;
4215*0Sstevel@tonic-gate    }
4216*0Sstevel@tonic-gate    &foo(\@x);
4217*0Sstevel@tonic-gate
4218*0Sstevel@tonic-gatemaking clear how the parameters are actually passed to C<foo>.
4219*0Sstevel@tonic-gate
4220*0Sstevel@tonic-gate=item B<-q>
4221*0Sstevel@tonic-gate
4222*0Sstevel@tonic-gateExpand double-quoted strings into the corresponding combinations of
4223*0Sstevel@tonic-gateconcatenation, uc, ucfirst, lc, lcfirst, quotemeta, and join. For
4224*0Sstevel@tonic-gateinstance, print
4225*0Sstevel@tonic-gate
4226*0Sstevel@tonic-gate    print "Hello, $world, @ladies, \u$gentlemen\E, \u\L$me!";
4227*0Sstevel@tonic-gate
4228*0Sstevel@tonic-gateas
4229*0Sstevel@tonic-gate
4230*0Sstevel@tonic-gate    print 'Hello, ' . $world . ', ' . join($", @ladies) . ', '
4231*0Sstevel@tonic-gate          . ucfirst($gentlemen) . ', ' . ucfirst(lc $me . '!');
4232*0Sstevel@tonic-gate
4233*0Sstevel@tonic-gateNote that the expanded form represents the way perl handles such
4234*0Sstevel@tonic-gateconstructions internally -- this option actually turns off the reverse
4235*0Sstevel@tonic-gatetranslation that B::Deparse usually does. On the other hand, note that
4236*0Sstevel@tonic-gateC<$x = "$y"> is not the same as C<$x = $y>: the former makes the value
4237*0Sstevel@tonic-gateof $y into a string before doing the assignment.
4238*0Sstevel@tonic-gate
4239*0Sstevel@tonic-gate=item B<-s>I<LETTERS>
4240*0Sstevel@tonic-gate
4241*0Sstevel@tonic-gateTweak the style of B::Deparse's output. The letters should follow
4242*0Sstevel@tonic-gatedirectly after the 's', with no space or punctuation. The following
4243*0Sstevel@tonic-gateoptions are available:
4244*0Sstevel@tonic-gate
4245*0Sstevel@tonic-gate=over 4
4246*0Sstevel@tonic-gate
4247*0Sstevel@tonic-gate=item B<C>
4248*0Sstevel@tonic-gate
4249*0Sstevel@tonic-gateCuddle C<elsif>, C<else>, and C<continue> blocks. For example, print
4250*0Sstevel@tonic-gate
4251*0Sstevel@tonic-gate    if (...) {
4252*0Sstevel@tonic-gate         ...
4253*0Sstevel@tonic-gate    } else {
4254*0Sstevel@tonic-gate         ...
4255*0Sstevel@tonic-gate    }
4256*0Sstevel@tonic-gate
4257*0Sstevel@tonic-gateinstead of
4258*0Sstevel@tonic-gate
4259*0Sstevel@tonic-gate    if (...) {
4260*0Sstevel@tonic-gate         ...
4261*0Sstevel@tonic-gate    }
4262*0Sstevel@tonic-gate    else {
4263*0Sstevel@tonic-gate         ...
4264*0Sstevel@tonic-gate    }
4265*0Sstevel@tonic-gate
4266*0Sstevel@tonic-gateThe default is not to cuddle.
4267*0Sstevel@tonic-gate
4268*0Sstevel@tonic-gate=item B<i>I<NUMBER>
4269*0Sstevel@tonic-gate
4270*0Sstevel@tonic-gateIndent lines by multiples of I<NUMBER> columns. The default is 4 columns.
4271*0Sstevel@tonic-gate
4272*0Sstevel@tonic-gate=item B<T>
4273*0Sstevel@tonic-gate
4274*0Sstevel@tonic-gateUse tabs for each 8 columns of indent. The default is to use only spaces.
4275*0Sstevel@tonic-gateFor instance, if the style options are B<-si4T>, a line that's indented
4276*0Sstevel@tonic-gate3 times will be preceded by one tab and four spaces; if the options were
4277*0Sstevel@tonic-gateB<-si8T>, the same line would be preceded by three tabs.
4278*0Sstevel@tonic-gate
4279*0Sstevel@tonic-gate=item B<v>I<STRING>B<.>
4280*0Sstevel@tonic-gate
4281*0Sstevel@tonic-gatePrint I<STRING> for the value of a constant that can't be determined
4282*0Sstevel@tonic-gatebecause it was optimized away (mnemonic: this happens when a constant
4283*0Sstevel@tonic-gateis used in B<v>oid context). The end of the string is marked by a period.
4284*0Sstevel@tonic-gateThe string should be a valid perl expression, generally a constant.
4285*0Sstevel@tonic-gateNote that unless it's a number, it probably needs to be quoted, and on
4286*0Sstevel@tonic-gatea command line quotes need to be protected from the shell. Some
4287*0Sstevel@tonic-gateconventional values include 0, 1, 42, '', 'foo', and
4288*0Sstevel@tonic-gate'Useless use of constant omitted' (which may need to be
4289*0Sstevel@tonic-gateB<-sv"'Useless use of constant omitted'.">
4290*0Sstevel@tonic-gateor something similar depending on your shell). The default is '???'.
4291*0Sstevel@tonic-gateIf you're using B::Deparse on a module or other file that's require'd,
4292*0Sstevel@tonic-gateyou shouldn't use a value that evaluates to false, since the customary
4293*0Sstevel@tonic-gatetrue constant at the end of a module will be in void context when the
4294*0Sstevel@tonic-gatefile is compiled as a main program.
4295*0Sstevel@tonic-gate
4296*0Sstevel@tonic-gate=back
4297*0Sstevel@tonic-gate
4298*0Sstevel@tonic-gate=item B<-x>I<LEVEL>
4299*0Sstevel@tonic-gate
4300*0Sstevel@tonic-gateExpand conventional syntax constructions into equivalent ones that expose
4301*0Sstevel@tonic-gatetheir internal operation. I<LEVEL> should be a digit, with higher values
4302*0Sstevel@tonic-gatemeaning more expansion. As with B<-q>, this actually involves turning off
4303*0Sstevel@tonic-gatespecial cases in B::Deparse's normal operations.
4304*0Sstevel@tonic-gate
4305*0Sstevel@tonic-gateIf I<LEVEL> is at least 3, C<for> loops will be translated into equivalent
4306*0Sstevel@tonic-gatewhile loops with continue blocks; for instance
4307*0Sstevel@tonic-gate
4308*0Sstevel@tonic-gate    for ($i = 0; $i < 10; ++$i) {
4309*0Sstevel@tonic-gate        print $i;
4310*0Sstevel@tonic-gate    }
4311*0Sstevel@tonic-gate
4312*0Sstevel@tonic-gateturns into
4313*0Sstevel@tonic-gate
4314*0Sstevel@tonic-gate    $i = 0;
4315*0Sstevel@tonic-gate    while ($i < 10) {
4316*0Sstevel@tonic-gate        print $i;
4317*0Sstevel@tonic-gate    } continue {
4318*0Sstevel@tonic-gate        ++$i
4319*0Sstevel@tonic-gate    }
4320*0Sstevel@tonic-gate
4321*0Sstevel@tonic-gateNote that in a few cases this translation can't be perfectly carried back
4322*0Sstevel@tonic-gateinto the source code -- if the loop's initializer declares a my variable,
4323*0Sstevel@tonic-gatefor instance, it won't have the correct scope outside of the loop.
4324*0Sstevel@tonic-gate
4325*0Sstevel@tonic-gateIf I<LEVEL> is at least 5, C<use> declarations will be translated into
4326*0Sstevel@tonic-gateC<BEGIN> blocks containing calls to C<require> and C<import>; for
4327*0Sstevel@tonic-gateinstance,
4328*0Sstevel@tonic-gate
4329*0Sstevel@tonic-gate    use strict 'refs';
4330*0Sstevel@tonic-gate
4331*0Sstevel@tonic-gateturns into
4332*0Sstevel@tonic-gate
4333*0Sstevel@tonic-gate    sub BEGIN {
4334*0Sstevel@tonic-gate        require strict;
4335*0Sstevel@tonic-gate        do {
4336*0Sstevel@tonic-gate            'strict'->import('refs')
4337*0Sstevel@tonic-gate        };
4338*0Sstevel@tonic-gate    }
4339*0Sstevel@tonic-gate
4340*0Sstevel@tonic-gateIf I<LEVEL> is at least 7, C<if> statements will be translated into
4341*0Sstevel@tonic-gateequivalent expressions using C<&&>, C<?:> and C<do {}>; for instance
4342*0Sstevel@tonic-gate
4343*0Sstevel@tonic-gate    print 'hi' if $nice;
4344*0Sstevel@tonic-gate    if ($nice) {
4345*0Sstevel@tonic-gate        print 'hi';
4346*0Sstevel@tonic-gate    }
4347*0Sstevel@tonic-gate    if ($nice) {
4348*0Sstevel@tonic-gate        print 'hi';
4349*0Sstevel@tonic-gate    } else {
4350*0Sstevel@tonic-gate        print 'bye';
4351*0Sstevel@tonic-gate    }
4352*0Sstevel@tonic-gate
4353*0Sstevel@tonic-gateturns into
4354*0Sstevel@tonic-gate
4355*0Sstevel@tonic-gate    $nice and print 'hi';
4356*0Sstevel@tonic-gate    $nice and do { print 'hi' };
4357*0Sstevel@tonic-gate    $nice ? do { print 'hi' } : do { print 'bye' };
4358*0Sstevel@tonic-gate
4359*0Sstevel@tonic-gateLong sequences of elsifs will turn into nested ternary operators, which
4360*0Sstevel@tonic-gateB::Deparse doesn't know how to indent nicely.
4361*0Sstevel@tonic-gate
4362*0Sstevel@tonic-gate=back
4363*0Sstevel@tonic-gate
4364*0Sstevel@tonic-gate=head1 USING B::Deparse AS A MODULE
4365*0Sstevel@tonic-gate
4366*0Sstevel@tonic-gate=head2 Synopsis
4367*0Sstevel@tonic-gate
4368*0Sstevel@tonic-gate    use B::Deparse;
4369*0Sstevel@tonic-gate    $deparse = B::Deparse->new("-p", "-sC");
4370*0Sstevel@tonic-gate    $body = $deparse->coderef2text(\&func);
4371*0Sstevel@tonic-gate    eval "sub func $body"; # the inverse operation
4372*0Sstevel@tonic-gate
4373*0Sstevel@tonic-gate=head2 Description
4374*0Sstevel@tonic-gate
4375*0Sstevel@tonic-gateB::Deparse can also be used on a sub-by-sub basis from other perl
4376*0Sstevel@tonic-gateprograms.
4377*0Sstevel@tonic-gate
4378*0Sstevel@tonic-gate=head2 new
4379*0Sstevel@tonic-gate
4380*0Sstevel@tonic-gate    $deparse = B::Deparse->new(OPTIONS)
4381*0Sstevel@tonic-gate
4382*0Sstevel@tonic-gateCreate an object to store the state of a deparsing operation and any
4383*0Sstevel@tonic-gateoptions. The options are the same as those that can be given on the
4384*0Sstevel@tonic-gatecommand line (see L</OPTIONS>); options that are separated by commas
4385*0Sstevel@tonic-gateafter B<-MO=Deparse> should be given as separate strings. Some
4386*0Sstevel@tonic-gateoptions, like B<-u>, don't make sense for a single subroutine, so
4387*0Sstevel@tonic-gatedon't pass them.
4388*0Sstevel@tonic-gate
4389*0Sstevel@tonic-gate=head2 ambient_pragmas
4390*0Sstevel@tonic-gate
4391*0Sstevel@tonic-gate    $deparse->ambient_pragmas(strict => 'all', '$[' => $[);
4392*0Sstevel@tonic-gate
4393*0Sstevel@tonic-gateThe compilation of a subroutine can be affected by a few compiler
4394*0Sstevel@tonic-gatedirectives, B<pragmas>. These are:
4395*0Sstevel@tonic-gate
4396*0Sstevel@tonic-gate=over 4
4397*0Sstevel@tonic-gate
4398*0Sstevel@tonic-gate=item *
4399*0Sstevel@tonic-gate
4400*0Sstevel@tonic-gateuse strict;
4401*0Sstevel@tonic-gate
4402*0Sstevel@tonic-gate=item *
4403*0Sstevel@tonic-gate
4404*0Sstevel@tonic-gateuse warnings;
4405*0Sstevel@tonic-gate
4406*0Sstevel@tonic-gate=item *
4407*0Sstevel@tonic-gate
4408*0Sstevel@tonic-gateAssigning to the special variable $[
4409*0Sstevel@tonic-gate
4410*0Sstevel@tonic-gate=item *
4411*0Sstevel@tonic-gate
4412*0Sstevel@tonic-gateuse integer;
4413*0Sstevel@tonic-gate
4414*0Sstevel@tonic-gate=item *
4415*0Sstevel@tonic-gate
4416*0Sstevel@tonic-gateuse bytes;
4417*0Sstevel@tonic-gate
4418*0Sstevel@tonic-gate=item *
4419*0Sstevel@tonic-gate
4420*0Sstevel@tonic-gateuse utf8;
4421*0Sstevel@tonic-gate
4422*0Sstevel@tonic-gate=item *
4423*0Sstevel@tonic-gate
4424*0Sstevel@tonic-gateuse re;
4425*0Sstevel@tonic-gate
4426*0Sstevel@tonic-gate=back
4427*0Sstevel@tonic-gate
4428*0Sstevel@tonic-gateOrdinarily, if you use B::Deparse on a subroutine which has
4429*0Sstevel@tonic-gatebeen compiled in the presence of one or more of these pragmas,
4430*0Sstevel@tonic-gatethe output will include statements to turn on the appropriate
4431*0Sstevel@tonic-gatedirectives. So if you then compile the code returned by coderef2text,
4432*0Sstevel@tonic-gateit will behave the same way as the subroutine which you deparsed.
4433*0Sstevel@tonic-gate
4434*0Sstevel@tonic-gateHowever, you may know that you intend to use the results in a
4435*0Sstevel@tonic-gateparticular context, where some pragmas are already in scope. In
4436*0Sstevel@tonic-gatethis case, you use the B<ambient_pragmas> method to describe the
4437*0Sstevel@tonic-gateassumptions you wish to make.
4438*0Sstevel@tonic-gate
4439*0Sstevel@tonic-gateNot all of the options currently have any useful effect. See
4440*0Sstevel@tonic-gateL</BUGS> for more details.
4441*0Sstevel@tonic-gate
4442*0Sstevel@tonic-gateThe parameters it accepts are:
4443*0Sstevel@tonic-gate
4444*0Sstevel@tonic-gate=over 4
4445*0Sstevel@tonic-gate
4446*0Sstevel@tonic-gate=item strict
4447*0Sstevel@tonic-gate
4448*0Sstevel@tonic-gateTakes a string, possibly containing several values separated
4449*0Sstevel@tonic-gateby whitespace. The special values "all" and "none" mean what you'd
4450*0Sstevel@tonic-gateexpect.
4451*0Sstevel@tonic-gate
4452*0Sstevel@tonic-gate    $deparse->ambient_pragmas(strict => 'subs refs');
4453*0Sstevel@tonic-gate
4454*0Sstevel@tonic-gate=item $[
4455*0Sstevel@tonic-gate
4456*0Sstevel@tonic-gateTakes a number, the value of the array base $[.
4457*0Sstevel@tonic-gate
4458*0Sstevel@tonic-gate=item bytes
4459*0Sstevel@tonic-gate
4460*0Sstevel@tonic-gate=item utf8
4461*0Sstevel@tonic-gate
4462*0Sstevel@tonic-gate=item integer
4463*0Sstevel@tonic-gate
4464*0Sstevel@tonic-gateIf the value is true, then the appropriate pragma is assumed to
4465*0Sstevel@tonic-gatebe in the ambient scope, otherwise not.
4466*0Sstevel@tonic-gate
4467*0Sstevel@tonic-gate=item re
4468*0Sstevel@tonic-gate
4469*0Sstevel@tonic-gateTakes a string, possibly containing a whitespace-separated list of
4470*0Sstevel@tonic-gatevalues. The values "all" and "none" are special. It's also permissible
4471*0Sstevel@tonic-gateto pass an array reference here.
4472*0Sstevel@tonic-gate
4473*0Sstevel@tonic-gate    $deparser->ambient_pragmas(re => 'eval');
4474*0Sstevel@tonic-gate
4475*0Sstevel@tonic-gate
4476*0Sstevel@tonic-gate=item warnings
4477*0Sstevel@tonic-gate
4478*0Sstevel@tonic-gateTakes a string, possibly containing a whitespace-separated list of
4479*0Sstevel@tonic-gatevalues. The values "all" and "none" are special, again. It's also
4480*0Sstevel@tonic-gatepermissible to pass an array reference here.
4481*0Sstevel@tonic-gate
4482*0Sstevel@tonic-gate    $deparser->ambient_pragmas(warnings => [qw[void io]]);
4483*0Sstevel@tonic-gate
4484*0Sstevel@tonic-gateIf one of the values is the string "FATAL", then all the warnings
4485*0Sstevel@tonic-gatein that list will be considered fatal, just as with the B<warnings>
4486*0Sstevel@tonic-gatepragma itself. Should you need to specify that some warnings are
4487*0Sstevel@tonic-gatefatal, and others are merely enabled, you can pass the B<warnings>
4488*0Sstevel@tonic-gateparameter twice:
4489*0Sstevel@tonic-gate
4490*0Sstevel@tonic-gate    $deparser->ambient_pragmas(
4491*0Sstevel@tonic-gate	warnings => 'all',
4492*0Sstevel@tonic-gate	warnings => [FATAL => qw/void io/],
4493*0Sstevel@tonic-gate    );
4494*0Sstevel@tonic-gate
4495*0Sstevel@tonic-gateSee L<perllexwarn> for more information about lexical warnings.
4496*0Sstevel@tonic-gate
4497*0Sstevel@tonic-gate=item hint_bits
4498*0Sstevel@tonic-gate
4499*0Sstevel@tonic-gate=item warning_bits
4500*0Sstevel@tonic-gate
4501*0Sstevel@tonic-gateThese two parameters are used to specify the ambient pragmas in
4502*0Sstevel@tonic-gatethe format used by the special variables $^H and ${^WARNING_BITS}.
4503*0Sstevel@tonic-gate
4504*0Sstevel@tonic-gateThey exist principally so that you can write code like:
4505*0Sstevel@tonic-gate
4506*0Sstevel@tonic-gate    { my ($hint_bits, $warning_bits);
4507*0Sstevel@tonic-gate    BEGIN {($hint_bits, $warning_bits) = ($^H, ${^WARNING_BITS})}
4508*0Sstevel@tonic-gate    $deparser->ambient_pragmas (
4509*0Sstevel@tonic-gate	hint_bits    => $hint_bits,
4510*0Sstevel@tonic-gate	warning_bits => $warning_bits,
4511*0Sstevel@tonic-gate	'$['         => 0 + $[
4512*0Sstevel@tonic-gate    ); }
4513*0Sstevel@tonic-gate
4514*0Sstevel@tonic-gatewhich specifies that the ambient pragmas are exactly those which
4515*0Sstevel@tonic-gateare in scope at the point of calling.
4516*0Sstevel@tonic-gate
4517*0Sstevel@tonic-gate=back
4518*0Sstevel@tonic-gate
4519*0Sstevel@tonic-gate=head2 coderef2text
4520*0Sstevel@tonic-gate
4521*0Sstevel@tonic-gate    $body = $deparse->coderef2text(\&func)
4522*0Sstevel@tonic-gate    $body = $deparse->coderef2text(sub ($$) { ... })
4523*0Sstevel@tonic-gate
4524*0Sstevel@tonic-gateReturn source code for the body of a subroutine (a block, optionally
4525*0Sstevel@tonic-gatepreceded by a prototype in parens), given a reference to the
4526*0Sstevel@tonic-gatesub. Because a subroutine can have no names, or more than one name,
4527*0Sstevel@tonic-gatethis method doesn't return a complete subroutine definition -- if you
4528*0Sstevel@tonic-gatewant to eval the result, you should prepend "sub subname ", or "sub "
4529*0Sstevel@tonic-gatefor an anonymous function constructor. Unless the sub was defined in
4530*0Sstevel@tonic-gatethe main:: package, the code will include a package declaration.
4531*0Sstevel@tonic-gate
4532*0Sstevel@tonic-gate=head1 BUGS
4533*0Sstevel@tonic-gate
4534*0Sstevel@tonic-gate=over 4
4535*0Sstevel@tonic-gate
4536*0Sstevel@tonic-gate=item *
4537*0Sstevel@tonic-gate
4538*0Sstevel@tonic-gateThe only pragmas to be completely supported are: C<use warnings>,
4539*0Sstevel@tonic-gateC<use strict 'refs'>, C<use bytes>, and C<use integer>. (C<$[>, which
4540*0Sstevel@tonic-gatebehaves like a pragma, is also supported.)
4541*0Sstevel@tonic-gate
4542*0Sstevel@tonic-gateExcepting those listed above, we're currently unable to guarantee that
4543*0Sstevel@tonic-gateB::Deparse will produce a pragma at the correct point in the program.
4544*0Sstevel@tonic-gate(Specifically, pragmas at the beginning of a block often appear right
4545*0Sstevel@tonic-gatebefore the start of the block instead.)
4546*0Sstevel@tonic-gateSince the effects of pragmas are often lexically scoped, this can mean
4547*0Sstevel@tonic-gatethat the pragma holds sway over a different portion of the program
4548*0Sstevel@tonic-gatethan in the input file.
4549*0Sstevel@tonic-gate
4550*0Sstevel@tonic-gate=item *
4551*0Sstevel@tonic-gate
4552*0Sstevel@tonic-gateIn fact, the above is a specific instance of a more general problem:
4553*0Sstevel@tonic-gatewe can't guarantee to produce BEGIN blocks or C<use> declarations in
4554*0Sstevel@tonic-gateexactly the right place. So if you use a module which affects compilation
4555*0Sstevel@tonic-gate(such as by over-riding keywords, overloading constants or whatever)
4556*0Sstevel@tonic-gatethen the output code might not work as intended.
4557*0Sstevel@tonic-gate
4558*0Sstevel@tonic-gateThis is the most serious outstanding problem, and will require some help
4559*0Sstevel@tonic-gatefrom the Perl core to fix.
4560*0Sstevel@tonic-gate
4561*0Sstevel@tonic-gate=item *
4562*0Sstevel@tonic-gate
4563*0Sstevel@tonic-gateIf a keyword is over-ridden, and your program explicitly calls
4564*0Sstevel@tonic-gatethe built-in version by using CORE::keyword, the output of B::Deparse
4565*0Sstevel@tonic-gatewill not reflect this. If you run the resulting code, it will call
4566*0Sstevel@tonic-gatethe over-ridden version rather than the built-in one. (Maybe there
4567*0Sstevel@tonic-gateshould be an option to B<always> print keyword calls as C<CORE::name>.)
4568*0Sstevel@tonic-gate
4569*0Sstevel@tonic-gate=item *
4570*0Sstevel@tonic-gate
4571*0Sstevel@tonic-gateSome constants don't print correctly either with or without B<-d>.
4572*0Sstevel@tonic-gateFor instance, neither B::Deparse nor Data::Dumper know how to print
4573*0Sstevel@tonic-gatedual-valued scalars correctly, as in:
4574*0Sstevel@tonic-gate
4575*0Sstevel@tonic-gate    use constant E2BIG => ($!=7); $y = E2BIG; print $y, 0+$y;
4576*0Sstevel@tonic-gate
4577*0Sstevel@tonic-gate=item *
4578*0Sstevel@tonic-gate
4579*0Sstevel@tonic-gateAn input file that uses source filtering probably won't be deparsed into
4580*0Sstevel@tonic-gaterunnable code, because it will still include the B<use> declaration
4581*0Sstevel@tonic-gatefor the source filtering module, even though the code that is
4582*0Sstevel@tonic-gateproduced is already ordinary Perl which shouldn't be filtered again.
4583*0Sstevel@tonic-gate
4584*0Sstevel@tonic-gate=item *
4585*0Sstevel@tonic-gate
4586*0Sstevel@tonic-gateThere are probably many more bugs on non-ASCII platforms (EBCDIC).
4587*0Sstevel@tonic-gate
4588*0Sstevel@tonic-gate=back
4589*0Sstevel@tonic-gate
4590*0Sstevel@tonic-gate=head1 AUTHOR
4591*0Sstevel@tonic-gate
4592*0Sstevel@tonic-gateStephen McCamant <smcc@CSUA.Berkeley.EDU>, based on an earlier version
4593*0Sstevel@tonic-gateby Malcolm Beattie <mbeattie@sable.ox.ac.uk>, with contributions from
4594*0Sstevel@tonic-gateGisle Aas, James Duncan, Albert Dvornik, Robin Houston, Dave Mitchell,
4595*0Sstevel@tonic-gateHugo van der Sanden, Gurusamy Sarathy, Nick Ing-Simmons, and Rafael
4596*0Sstevel@tonic-gateGarcia-Suarez.
4597*0Sstevel@tonic-gate
4598*0Sstevel@tonic-gate=cut
4599