use Exporter (); # use #5
-our $VERSION = "0.71";
+our $VERSION = "0.90";
our @ISA = qw(Exporter);
our @EXPORT_OK = qw( set_style set_style_standard add_callback
concise_subref concise_cv concise_main
# use #6
use B qw(class ppname main_start main_root main_cv cstring svref_2object
SVf_IOK SVf_NOK SVf_POK SVf_IVisUV SVf_FAKE OPf_KIDS OPf_SPECIAL
- CVf_ANON PAD_FAKELEX_ANON PAD_FAKELEX_MULTI);
+ CVf_ANON PAD_FAKELEX_ANON PAD_FAKELEX_MULTI SVf_ROK);
my %style =
("terse" =>
my $tree_style = 0; # tree-order details
my $banner = 1; # print banner before optree is traversed
my $do_main = 0; # force printing of main routine
+my $show_src; # show source code
# another factor: can affect all styles!
our @callbacks; # allow external management
sub concise_stashref {
my($order, $h) = @_;
+ local *s;
foreach my $k (sort keys %$h) {
- local *s = $h->{$k};
+ next unless defined $h->{$k};
+ *s = $h->{$k};
my $coderef = *s{CODE} or next;
reset_sequence();
print "FUNC: ", *s, "\n";
my $codeobj = svref_2object($coderef);
next unless ref $codeobj eq 'B::CV';
- eval { concise_cv_obj($order, $codeobj) }
- or warn "err $@ on $codeobj";
+ eval { concise_cv_obj($order, $codeobj, $k) };
+ warn "err $@ on $codeobj" if $@;
}
}
[" ", map("$start_sym$_$end_sym", "q", "w", "t", "x", "m"), "", 0],
);
+my @render_packs; # collect -stash=<packages>
sub compileOpts {
# set rendering state from options and args
} elsif ($o eq "-littleendian") {
$big_endian = 0;
}
+ # miscellaneous, presentation
elsif ($o eq "-nobanner") {
$banner = 0;
} elsif ($o eq "-banner") {
$do_main = 1;
} elsif ($o eq "-nomain") {
$do_main = 0;
+ } elsif ($o eq "-src") {
+ $show_src = 1;
+ }
+ elsif ($o =~ /^-stash=(.*)/) {
+ my $pkg = $1;
+ no strict 'refs';
+ if (! %{$pkg.'::'}) {
+ eval "require $pkg";
+ } else {
+ require Config;
+ if (!$Config::Config{usedl}
+ && keys %{$pkg.'::'} == 1
+ && $pkg->can('bootstrap')) {
+ # It is something that we're statically linked to, but hasn't
+ # yet been used.
+ eval "require $pkg";
+ }
+ }
+ push @render_packs, $pkg;
}
# line-style options
elsif (exists $style{substr($o, 1)}) {
concise_subref($order, $objref, $objname);
}
}
- if (!@args or $do_main) {
+ for my $pkg (@render_packs) {
+ no strict 'refs';
+ concise_stashref($order, \%{$pkg.'::'});
+ }
+
+ if (!@args or $do_main or @render_packs) {
print $walkHandle "main program:\n" if $do_main;
concise_main($order);
}
walk_topdown($kid, $sub, $level + 1);
}
}
- elsif (class($op) eq "PMOP") {
+ if (class($op) eq "PMOP") {
my $maybe_root = $op->pmreplroot;
if (ref($maybe_root) and $maybe_root->isa("B::OP")) {
# It really is the root of the replacement, not something
$text =~ s/\#([a-zA-Z]+)/$hr->{$1}/eg; # populate #var's
$text =~ s/[ \t]*~+[ \t]*/ /g; # squeeze tildes
+
+ $text = "# $hr->{src}\n$text" if $show_src and $hr->{src};
+
chomp $text;
- return "$text\n" if $text ne "";
+ return "$text\n" if $text ne "" and $order ne "tree";
return $text; # suppress empty lines
}
$priv{$_}{128} = "LVINTRO"
for ("pos", "substr", "vec", "threadsv", "gvsv", "rv2sv", "rv2hv", "rv2gv",
"rv2av", "rv2arylen", "aelem", "helem", "aslice", "hslice", "padsv",
- "padav", "padhv", "enteriter");
+ "padav", "padhv", "enteriter", "entersub");
$priv{$_}{64} = "REFC" for ("leave", "leavesub", "leavesublv", "leavewrite");
$priv{"aassign"}{64} = "COMMON";
$priv{"aassign"}{32} = $] < 5.009 ? "PHASH" : "STATE";
$priv{"sassign"}{32} = "STATE";
$priv{"sassign"}{64} = "BKWARD";
+$priv{"sassign"}{128}= "CV2GV";
$priv{$_}{64} = "RTIME" for ("match", "subst", "substcont", "qr");
@{$priv{"trans"}}{1,2,4,8,16,64} = ("<UTF", ">UTF", "IDENT", "SQUASH", "DEL",
"COMPL", "GROWS");
+$priv{transr} = $priv{trans};
$priv{"repeat"}{64} = "DOLIST";
$priv{"leaveloop"}{64} = "CONT";
+$priv{$_}{4} = "DREFed" for (qw(rv2sv rv2av rv2hv));
@{$priv{$_}}{32,64,96} = ("DREFAV", "DREFHV", "DREFSV")
for (qw(rv2gv rv2sv padsv aelem helem));
$priv{$_}{16} = "STATE" for ("padav", "padhv", "padsv");
-@{$priv{"entersub"}}{16,32,64} = ("DBG","TARG","NOMOD");
-@{$priv{$_}}{4,8,128} = ("INARGS","AMPER","NO()") for ("entersub", "rv2cv");
+@{$priv{rv2gv}}{4,16} = qw "NOINIT FAKE";
+@{$priv{"entersub"}}{1,4,16,32,64} = qw( INARGS TARG DBG DEREF );
+@{$priv{rv2cv}}{1,8,128} = ("CONST","AMPER","NO()");
$priv{"gv"}{32} = "EARLYCV";
$priv{"aelem"}{16} = $priv{"helem"}{16} = "LVDEFER";
$priv{$_}{16} = "OURINTR" for ("gvsv", "rv2sv", "rv2av", "rv2hv", "r2gv",
"enteriter");
+$priv{$_}{8} = 'LVSUB' for qw(rv2av rv2gv rv2hv padav padhv aelem helem
+ aslice hslice av2arylen keys rkeys substr pos vec);
+$priv{substr}{16} = 'REPL1ST';
$priv{$_}{16} = "TARGMY"
for (map(($_,"s$_"),"chop", "chomp"),
map(($_,"i_$_"), "postinc", "postdec", "multiply", "divide", "modulo",
"exec", "kill", "getppid", "getpgrp", "setpgrp", "getpriority",
"setpriority", "time", "sleep");
$priv{$_}{4} = "REVERSED" for ("enteriter", "iter");
-@{$priv{"const"}}{4,8,16,32,64,128} = ("SHORT","STRICT","ENTERED",'$[',"BARE","WARN");
+@{$priv{"const"}}{2,4,8,16,64,128} =
+ ("NOVER","SHORT","STRICT","ENTERED","BARE","FOLD");
$priv{"flip"}{64} = $priv{"flop"}{64} = "LINENUM";
$priv{"list"}{64} = "GUESSED";
$priv{"delete"}{64} = "SLICE";
$priv{"exists"}{64} = "SUB";
@{$priv{"sort"}}{1,2,4,8,16,32,64} = ("NUM", "INT", "REV", "INPLACE","DESC","QSORT","STABLE");
+$priv{"reverse"}{8} = "INPLACE";
$priv{"threadsv"}{64} = "SVREFd";
@{$priv{$_}}{16,32,64,128} = ("INBIN","INCR","OUTBIN","OUTCR")
for ("open", "backtick");
$priv{"exit"}{128} = "VMS";
$priv{$_}{2} = "FTACCESS"
for ("ftrread", "ftrwrite", "ftrexec", "fteread", "ftewrite", "fteexec");
-$priv{"entereval"}{2} = "HAS_HH";
+@{$priv{"entereval"}}{2,4,8,16} = qw "HAS_HH UNI BYTES COPHH";
if ($] >= 5.009) {
# Stacked filetests are post 5.8.x
- $priv{$_}{4} = "FTSTACKED"
+ @{$priv{$_}}{4,8,16} = ("FTSTACKED","FTSTACKING","FTAFTERt")
for ("ftrread", "ftrwrite", "ftrexec", "fteread", "ftewrite", "fteexec",
"ftis", "fteowned", "ftrowned", "ftzero", "ftsize", "ftmtime",
"ftatime", "ftctime", "ftsock", "ftchr", "ftblk", "ftfile", "ftdir",
$priv{$_}{2} = "GREPLEX"
for ("mapwhile", "mapstart", "grepwhile", "grepstart");
}
+$priv{$_}{128} = '+1' for qw "caller wantarray runcv";
+@{$priv{coreargs}}{1,2,64,128} = ('DREF1','DREF2','$MOD','MARK');
+$priv{$_}{128} = 'UTF' for qw "last redo next goto dump";
our %hints; # used to display each COP's op_hints values
# strict refs, subs, vars
-@hints{2,512,1024} = ('$', '&', '*');
-# integers, locale, bytes, arybase
-@hints{1,4,8,16,32} = ('i', 'l', 'b', '[');
+@hints{2,512,1024,32,64,128} = ('$', '&', '*', 'x$', 'x&', 'x*');
+# integers, locale, bytes
+@hints{1,4,8,16} = ('i', 'l', 'b');
# block scope, localise %^H, $^OPEN (in), $^OPEN (out)
@hints{256,131072,262144,524288} = ('{','%','<','>');
# overload new integer, float, binary, string, re
@hints{4096,8192,16384,32768,65536} = ('I', 'F', 'B', 'S', 'R');
# taint and eval
@hints{1048576,2097152} = ('T', 'E');
-# filetest access, UTF-8, assertions, assertions seen
-@hints{4194304,8388608,16777216,33554432} = ('X', 'U', 'A', 'a');
+# filetest access, UTF-8
+@hints{4194304,8388608} = ('X', 'U');
sub _flags {
my($hash, $x) = @_;
if $hr->{svclass} eq "IV" and $sv->FLAGS & SVf_IVisUV;
Carp::cluck("bad concise_sv: $sv") unless $sv and $$sv;
$hr->{svaddr} = sprintf("%#x", $$sv);
- if ($hr->{svclass} eq "GV") {
+ if ($hr->{svclass} eq "GV" && $sv->isGV_with_GP()) {
my $gv = $sv;
- my $stash = $gv->STASH->NAME;
- if ($stash eq "main") {
+ my $stash = $gv->STASH->NAME; if ($stash eq "main") {
$stash = "";
} else {
$stash = $stash . "::";
$hr->{svval} = "*$stash" . $gv->SAFENAME;
return "*$stash" . $gv->SAFENAME;
} else {
- while (class($sv) eq "RV") {
- $hr->{svval} .= "\\";
- $sv = $sv->RV;
+ if ($] >= 5.011) {
+ while (class($sv) eq "IV" && $sv->FLAGS & SVf_ROK) {
+ $hr->{svval} .= "\\";
+ $sv = $sv->RV;
+ }
+ } else {
+ while (class($sv) eq "RV") {
+ $hr->{svval} .= "\\";
+ $sv = $sv->RV;
+ }
}
if (class($sv) eq "SPECIAL") {
$hr->{svval} .= ["Null", "sv_undef", "sv_yes", "sv_no"]->[$$sv];
}
}
+my %srclines;
+
+sub fill_srclines {
+ my $fullnm = shift;
+ if ($fullnm eq '-e') {
+ $srclines{$fullnm} = [ $fullnm, "-src not supported for -e" ];
+ return;
+ }
+ open (my $fh, '<', $fullnm)
+ or warn "# $fullnm: $!, (chdirs not supported by this feature yet)\n"
+ and return;
+ my @l = <$fh>;
+ chomp @l;
+ unshift @l, $fullnm; # like @{_<$fullnm} in debug, array starts at 1
+ $srclines{$fullnm} = \@l;
+}
+
sub concise_op {
my ($op, $level, $format) = @_;
my %h;
} else {
$h{arg} = "($precomp)";
}
- } elsif ($h{class} eq "PVOP" and $h{name} ne "trans") {
+ } elsif ($h{class} eq "PVOP" and $h{name} !~ '^transr?\z') {
$h{arg} = '("' . $op->pv . '")';
$h{svval} = '"' . $op->pv . '"';
} elsif ($h{class} eq "COP") {
$h{coplabel} = $label;
$label = $label ? "$label: " : "";
my $loc = $op->file;
+ my $pathnm = $loc;
$loc =~ s[.*/][];
- $loc .= ":" . $op->line;
+ my $ln = $op->line;
+ $loc .= ":$ln";
my($stash, $cseq) = ($op->stash->NAME, $op->cop_seq - $cop_seq_base);
- my $arybase = $op->arybase;
- $arybase = $arybase ? ' $[=' . $arybase : "";
- $h{arg} = "($label$stash $cseq $loc$arybase)";
+ $h{arg} = "($label$stash $cseq $loc)";
+ if ($show_src) {
+ fill_srclines($pathnm) unless exists $srclines{$pathnm};
+ # Would love to retain Jim's use of // but this code needs to be
+ # portable to 5.8.x
+ my $line = $srclines{$pathnm}[$ln];
+ $line = "-src unavailable under -e" unless defined $line;
+ $h{src} = "$ln: $line";
+ }
} elsif ($h{class} eq "LOOP") {
$h{arg} = "(next->" . seq($op->nextop) . " last->" . seq($op->lastop)
. " redo->" . seq($op->redoop) . ")";
$h{seq} = "" if $h{seq} eq "-";
if ($] > 5.009) {
$h{opt} = $op->opt;
- $h{static} = $op->static;
$h{label} = $labels{$$op};
} else {
$h{seqnum} = $op->seq;
=head1 OPTIONS
Arguments that don't start with a hyphen are taken to be the names of
-subroutines to print the OPs of; if no such functions are specified,
-the main body of the program (outside any subroutines, and not
-including use'd or require'd files) is rendered. Passing C<BEGIN>,
-C<UNITCHECK>, C<CHECK>, C<INIT>, or C<END> will cause all of the
-corresponding special blocks to be printed.
+subroutines to render; if no such functions are specified, the main
+body of the program (outside any subroutines, and not including use'd
+or require'd files) is rendered. Passing C<BEGIN>, C<UNITCHECK>,
+C<CHECK>, C<INIT>, or C<END> will cause all of the corresponding
+special blocks to be printed. Arguments must follow options.
Options affect how things are rendered (ie printed). They're presented
here by their visual effect, 1st being strongest. They're grouped
=item B<-littleendian>
-Print seqence numbers with the least significant digit first. This is
+Print sequence numbers with the least significant digit first. This is
obviously mutually exclusive with bigendian.
=back
=head2 Other options
-These are pairwise exclusive.
+=over 4
+
+=item B<-src>
+
+With this option, the rendering of each statement (starting with the
+nextstate OP) will be preceded by the 1st line of source code that
+generates it. For example:
+
+ 1 <0> enter
+ # 1: my $i;
+ 2 <;> nextstate(main 1 junk.pl:1) v:{
+ 3 <0> padsv[$i:1,10] vM/LVINTRO
+ # 3: for $i (0..9) {
+ 4 <;> nextstate(main 3 junk.pl:3) v:{
+ 5 <0> pushmark s
+ 6 <$> const[IV 0] s
+ 7 <$> const[IV 9] s
+ 8 <{> enteriter(next->j last->m redo->9)[$i:1,10] lKS
+ k <0> iter s
+ l <|> and(other->9) vK/1
+ # 4: print "line ";
+ 9 <;> nextstate(main 2 junk.pl:4) v
+ a <0> pushmark s
+ b <$> const[PV "line "] s
+ c <@> print vK
+ # 5: print "$i\n";
+ ...
+
+=item B<-stash="somepackage">
+
+With this, "somepackage" will be required, then the stash is
+inspected, and each function is rendered.
+
+=back
+
+The following options are pairwise exclusive.
=over 4
=item B<#hints>
The COP's hint flags, rendered with abbreviated names if possible. An empty
-string if this is not a COP.
+string if this is not a COP. Here are the symbols used:
+
+ $ strict refs
+ & strict subs
+ * strict vars
+ x$ explicit use/no strict refs
+ x& explicit use/no strict subs
+ x* explicit use/no strict vars
+ i integers
+ l locale
+ b bytes
+ { block scope
+ % localise %^H
+ < open in
+ > open out
+ I overload int
+ F overload float
+ B overload binary
+ S overload string
+ R overload re
+ T taint
+ E eval
+ X filetest access
+ U utf-8
=item B<#hintsval>
Only available in 5.9 and later.
-=item B<#static>
-
-Whether or not the op is statically defined. This flag is used by the
-B::C compiler backend and indicates that the op should not be freed.
-
-Only available in 5.9 and later.
-
=item B<#sibaddr>
The address of the OP's next youngest sibling, in hexadecimal.
subroutine rendering is more representative, insofar as a single main
program will have many subs.
+=item perl -MB::Concise -e 'B::Concise::compile("-exec","-src", \%B::Concise::)->()'
+
+This renders all functions in the B::Concise package with the source
+lines. It eschews the O framework so that the stashref can be passed
+directly to B::Concise::compile(). See -stash option for a more
+convenient way to render a package.
=back
C<linenoise>, C<debug>, C<env>, into effect. It also accepts style
names previously defined with add_style().
-=head2 add_style()
+=head2 add_style ()
This subroutine accepts a new style name and three style arguments as
above, and creates, registers, and selects the newly named style. It is
an error to re-add a style; call set_style_standard() to switch between
several styles.
-=head2 add_callback()
+=head2 add_callback ()
If your newly minted styles refer to any new #variables, you'll need
to define a callback subroutine that will populate (or modify) those