📄 concise.pm
字号:
package B::Concise;# Copyright (C) 2000-2003 Stephen McCamant. All rights reserved.# This program is free software; you can redistribute and/or modify it# under the same terms as Perl itself.# Note: we need to keep track of how many use declarations/BEGIN# blocks this module uses, so we can avoid printing them when user# asks for the BEGIN blocks in her program. Update the comments and# the count in concise_specials if you add or delete one. The# -MO=Concise counts as use #1.use strict; # use #2use warnings; # uses #3 and #4, since warnings uses Carpuse Exporter (); # use #5our $VERSION = "0.74";our @ISA = qw(Exporter);our @EXPORT_OK = qw( set_style set_style_standard add_callback concise_subref concise_cv concise_main add_style walk_output compile reset_sequence );our %EXPORT_TAGS = ( io => [qw( walk_output compile reset_sequence )], style => [qw( add_style set_style_standard )], cb => [qw( add_callback )], mech => [qw( concise_subref concise_cv concise_main )], );# use #6use 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);my %style = ("terse" => ["(?(#label =>\n)?)(*( )*)#class (#addr) #name (?([#targ])?) " . "#svclass~(?((#svaddr))?)~#svval~(?(label \"#coplabel\")?)\n", "(*( )*)goto #class (#addr)\n", "#class pp_#name"], "concise" => ["#hyphseq2 (*( (x( ;)x))*)<#classsym> #exname#arg(?([#targarglife])?)" . "~#flags(?(/#private)?)(?(:#hints)?)(x(;~->#next)x)\n" , " (*( )*) goto #seq\n", "(?(<#seq>)?)#exname#arg(?([#targarglife])?)"], "linenoise" => ["(x(;(*( )*))x)#noise#arg(?([#targarg])?)(x( ;\n)x)", "gt_#seq ", "(?(#seq)?)#noise#arg(?([#targarg])?)"], "debug" => ["#class (#addr)\n\top_next\t\t#nextaddr\n\top_sibling\t#sibaddr\n\t" . "op_ppaddr\tPL_ppaddr[OP_#NAME]\n\top_type\t\t#typenum\n" . ($] > 5.009 ? '' : "\top_seq\t\t#seqnum\n") . "\top_flags\t#flagval\n\top_private\t#privval\t#hintsval\n" . "(?(\top_first\t#firstaddr\n)?)(?(\top_last\t\t#lastaddr\n)?)" . "(?(\top_sv\t\t#svaddr\n)?)", " GOTO #addr\n", "#addr"], "env" => [$ENV{B_CONCISE_FORMAT}, $ENV{B_CONCISE_GOTO_FORMAT}, $ENV{B_CONCISE_TREE_FORMAT}], );# Renderings, ie how Concise prints, is controlled by these vars# primary:our $stylename; # selects current style from %stylemy $order = "basic"; # how optree is walked & printed: basic, exec, tree# rendering mechanics:# these 'formats' are the line-rendering templates# they're updated from %style when $stylename changesmy ($format, $gotofmt, $treefmt);# lesser players:my $base = 36; # how <sequence#> is displayedmy $big_endian = 1; # more <sequence#> displaymy $tree_style = 0; # tree-order detailsmy $banner = 1; # print banner before optree is traversedmy $do_main = 0; # force printing of main routinemy $show_src; # show source code# another factor: can affect all styles!our @callbacks; # allow external managementset_style_standard("concise");my $curcv;my $cop_seq_base;sub set_style { ($format, $gotofmt, $treefmt) = @_; #warn "set_style: deprecated, use set_style_standard instead\n"; # someday die "expecting 3 style-format args\n" unless @_ == 3;}sub add_style { my ($newstyle,@args) = @_; die "style '$newstyle' already exists, choose a new name\n" if exists $style{$newstyle}; die "expecting 3 style-format args\n" unless @args == 3; $style{$newstyle} = [@args]; $stylename = $newstyle; # update rendering state}sub set_style_standard { ($stylename) = @_; # update rendering state die "err: style '$stylename' unknown\n" unless exists $style{$stylename}; set_style(@{$style{$stylename}});}sub add_callback { push @callbacks, @_;}# output handle, used with all Concise-output printingour $walkHandle; # public for your convenienceBEGIN { $walkHandle = \*STDOUT }sub walk_output { # updates $walkHandle my $handle = shift; return $walkHandle unless $handle; # allow use as accessor if (ref $handle eq 'SCALAR') { require Config; die "no perlio in this build, can't call walk_output (\\\$scalar)\n" unless $Config::Config{useperlio}; # in 5.8+, open(FILEHANDLE,MODE,REFERENCE) writes to string open my $tmp, '>', $handle; # but cant re-set existing STDOUT $walkHandle = $tmp; # so use my $tmp as intermediate var return $walkHandle; } my $iotype = ref $handle; die "expecting argument/object that can print\n" unless $iotype eq 'GLOB' or $iotype and $handle->can('print'); $walkHandle = $handle;}sub concise_subref { my($order, $coderef, $name) = @_; my $codeobj = svref_2object($coderef); return concise_stashref(@_) unless ref $codeobj eq 'B::CV'; concise_cv_obj($order, $codeobj, $name);}sub concise_stashref { my($order, $h) = @_; local *s; foreach my $k (sort keys %$h) { 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, $k) }; warn "err $@ on $codeobj" if $@; }}# This should have been called concise_subref, but it was exported# under this name in versions before 0.56*concise_cv = \&concise_subref;sub concise_cv_obj { my ($order, $cv, $name) = @_; # name is either a string, or a CODE ref (copy of $cv arg??) $curcv = $cv; if (ref($cv->XSUBANY) =~ /B::(\w+)/) { print $walkHandle "$name is a constant sub, optimized to a $1\n"; return; } if ($cv->XSUB) { print $walkHandle "$name is XS code\n"; return; } if (class($cv->START) eq "NULL") { no strict 'refs'; if (ref $name eq 'CODE') { print $walkHandle "coderef $name has no START\n"; } elsif (exists &$name) { print $walkHandle "$name exists in stash, but has no START\n"; } else { print $walkHandle "$name not in symbol table\n"; } return; } sequence($cv->START); if ($order eq "exec") { walk_exec($cv->START); } elsif ($order eq "basic") { # walk_topdown($cv->ROOT, sub { $_[0]->concise($_[1]) }, 0); my $root = $cv->ROOT; unless (ref $root eq 'B::NULL') { walk_topdown($root, sub { $_[0]->concise($_[1]) }, 0); } else { print $walkHandle "B::NULL encountered doing ROOT on $cv. avoiding disaster\n"; } } else { print $walkHandle tree($cv->ROOT, 0); }}sub concise_main { my($order) = @_; sequence(main_start); $curcv = main_cv; if ($order eq "exec") { return if class(main_start) eq "NULL"; walk_exec(main_start); } elsif ($order eq "tree") { return if class(main_root) eq "NULL"; print $walkHandle tree(main_root, 0); } elsif ($order eq "basic") { return if class(main_root) eq "NULL"; walk_topdown(main_root, sub { $_[0]->concise($_[1]) }, 0); }}sub concise_specials { my($name, $order, @cv_s) = @_; my $i = 1; if ($name eq "BEGIN") { splice(@cv_s, 0, 8); # skip 7 BEGIN blocks in this file. NOW 8 ?? } elsif ($name eq "CHECK") { pop @cv_s; # skip the CHECK block that calls us } for my $cv (@cv_s) { print $walkHandle "$name $i:\n"; $i++; concise_cv_obj($order, $cv, $name); }}my $start_sym = "\e(0"; # "\cN" sometimes also worksmy $end_sym = "\e(B"; # "\cO" respectivelymy @tree_decorations = ([" ", "--", "+-", "|-", "| ", "`-", "-", 1], [" ", "-", "+", "+", "|", "`", "", 0], [" ", map("$start_sym$_$end_sym", "qq", "wq", "tq", "x ", "mq", "q"), 1], [" ", 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 my (@options,@args); if (@_) { @options = grep(/^-/, @_); @args = grep(!/^-/, @_); } for my $o (@options) { # mode/order if ($o eq "-basic") { $order = "basic"; } elsif ($o eq "-exec") { $order = "exec"; } elsif ($o eq "-tree") { $order = "tree"; } # tree-specific elsif ($o eq "-compact") { $tree_style |= 1; } elsif ($o eq "-loose") { $tree_style &= ~1; } elsif ($o eq "-vt") { $tree_style |= 2; } elsif ($o eq "-ascii") { $tree_style &= ~2; } # sequence numbering elsif ($o =~ /^-base(\d+)$/) { $base = $1; } elsif ($o eq "-bigendian") { $big_endian = 1; } elsif ($o eq "-littleendian") { $big_endian = 0; } # miscellaneous, presentation elsif ($o eq "-nobanner") { $banner = 0; } elsif ($o eq "-banner") { $banner = 1; } elsif ($o eq "-main") { $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'; eval "require $pkg" unless defined %{$pkg.'::'}; push @render_packs, $pkg; } # line-style options elsif (exists $style{substr($o, 1)}) { $stylename = substr($o, 1); set_style_standard($stylename); } else { warn "Option $o unrecognized"; } } return (@args);}sub compile { my (@args) = compileOpts(@_); return sub { my @newargs = compileOpts(@_); # accept new rendering options warn "disregarding non-options: @newargs\n" if @newargs; for my $objname (@args) { next unless $objname; # skip null args to avoid noisy responses if ($objname eq "BEGIN") { concise_specials("BEGIN", $order, B::begin_av->isa("B::AV") ? B::begin_av->ARRAY : ()); } elsif ($objname eq "INIT") { concise_specials("INIT", $order, B::init_av->isa("B::AV") ? B::init_av->ARRAY : ()); } elsif ($objname eq "CHECK") { concise_specials("CHECK", $order, B::check_av->isa("B::AV") ? B::check_av->ARRAY : ()); } elsif ($objname eq "UNITCHECK") { concise_specials("UNITCHECK", $order, B::unitcheck_av->isa("B::AV") ? B::unitcheck_av->ARRAY : ()); } elsif ($objname eq "END") { concise_specials("END", $order, B::end_av->isa("B::AV") ? B::end_av->ARRAY : ()); } else { # convert function names to subrefs my $objref; if (ref $objname) { print $walkHandle "B::Concise::compile($objname)\n" if $banner; $objref = $objname; } else { $objname = "main::" . $objname unless $objname =~ /::/; print $walkHandle "$objname:\n"; no strict 'refs'; unless (exists &$objname) { print $walkHandle "err: unknown function ($objname)\n"; return; } $objref = \&$objname; } concise_subref($order, $objref, $objname); } } 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); } return @args; # something }}my %labels;my $lastnext; # remembers op-chain, used to insert gotosmy %opclass = ('OP' => "0", 'UNOP' => "1", 'BINOP' => "2", 'LOGOP' => "|", 'LISTOP' => "@", 'PMOP' => "/", 'SVOP' => "\$", 'GVOP' => "*", 'PVOP' => '"', 'LOOP' => "{", 'COP' => ";", 'PADOP' => "#");no warnings 'qw'; # "Possible attempt to put comments..."; use #7my @linenoise = qw'# () sc ( @? 1 $* gv *{ m$ m@ m% m? p/ *$ $ $# & a& pt \\ s\\ rf bl ` *? <> ?? ?/ r/ c/ // qr s/ /c y/ = @= C sC Cp sp df un BM po +1 +I -1 -I 1+ I+ 1- I- ** * i* / i/ %$ i% x + i+ - i- . " << >> < i< > i> <= i, >= i. == i= != i! <? i? s< s> s, s. s= s! s? b& b^ b| -0 -i ! ~ a2 si cs rd sr e^ lg sq in %x %o ab le ss ve ix ri sf FL od ch cy uf lf uc lc qm @ [f [ @[ eh vl ky dl ex % ${ @{ uk pk st jn ) )[ a@ a% sl +] -] [- [+ so rv GS GW MS MW .. f. .f && || ^^ ?: &= |= -> s{ s} v} ca wa di rs ;; ; ;d }{ { } {} f{ it {l l} rt }l }n }r dm }g }e ^o ^c ^| ^# um bm t~ u~ ~d DB db ^s se ^g ^r {w }w pf pr ^O ^K ^R ^W ^d ^v ^e ^t ^k t. fc ic fl .s .p .b .c .l .a .h g1 s1 g2 s2 ?. l? -R -W -X -r -w -x -e -o -O -z -s -M -A -C -S -c -b -f -d -p -l -u -g -k -t -T -B cd co cr u. cm ut r. l@ s@ r@ mD uD oD rD tD sD wD cD f$ w$ p$ sh e$ k$ g3 g4 s4 g5 s5 T@ C@ L@ G@ A@ S@ Hg Hc Hr Hw Mg Mc Ms Mr Sg Sc So rq do {e e} {t t} g6 G6 6e g7 G7 7e g8 G8 8e g9 G9 9e 6s 7s 8s 9s 6E 7E 8E 9E Pn Pu GP SP EP Gn Gg GG SG EG g0 c$ lk t$ ;s n> // /= CO';my $chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";sub op_flags { # common flags (see BASOP.op_flags in op.h) my($x) = @_; my(@v); push @v, "v" if ($x & 3) == 1; push @v, "s" if ($x & 3) == 2; push @v, "l" if ($x & 3) == 3; push @v, "K" if $x & 4; push @v, "P" if $x & 8; push @v, "R" if $x & 16; push @v, "M" if $x & 32; push @v, "S" if $x & 64; push @v, "*" if $x & 128; return join("", @v);}sub base_n { my $x = shift; return "-" . base_n(-$x) if $x < 0; my $str = ""; do { $str .= substr($chars, $x % $base, 1) } while $x = int($x / $base); $str = reverse $str if $big_endian; return $str;}my %sequence_num;my $seq_max = 1;sub reset_sequence { # reset the sequence %sequence_num = (); $seq_max = 1; $lastnext = 0;}sub seq { my($op) = @_; return "-" if not exists $sequence_num{$$op}; return base_n($sequence_num{$$op});}sub walk_topdown { my($op, $sub, $level) = @_; $sub->($op, $level); if ($op->flags & OPf_KIDS) { for (my $kid = $op->first; $$kid; $kid = $kid->sibling) { walk_topdown($kid, $sub, $level + 1); } } elsif (class($op) eq "PMOP") { my $maybe_root = $op->pmreplroot;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -