diff options
Diffstat (limited to 'Master/tlpkg/tlperl/lib/B/Deparse.pm')
-rw-r--r-- | Master/tlpkg/tlperl/lib/B/Deparse.pm | 282 |
1 files changed, 187 insertions, 95 deletions
diff --git a/Master/tlpkg/tlperl/lib/B/Deparse.pm b/Master/tlpkg/tlperl/lib/B/Deparse.pm index 7bf1cd51c36..67147f12ddf 100644 --- a/Master/tlpkg/tlperl/lib/B/Deparse.pm +++ b/Master/tlpkg/tlperl/lib/B/Deparse.pm @@ -52,7 +52,7 @@ use B qw(class main_root main_start main_cv svref_2object opnumber perlstring MDEREF_SHIFT ); -$VERSION = '1.54'; +$VERSION = '1.56'; use strict; our $AUTOLOAD; use warnings (); @@ -280,6 +280,7 @@ BEGIN { for (qw[ const stringify rv2sv list glob pushmark null aelem # possibly undoing optimisations along the way. sub DEBUG { 0 } +use if DEBUG, 'Data::Dumper'; sub _pessimise_walk { my ($self, $startop) = @_; @@ -2303,6 +2304,8 @@ my %feature_keywords = ( evalbytes=>'evalbytes', __SUB__ => '__SUB__', fc => 'fc', + try => 'try', + catch => 'try', ); # keywords that are strong and also have a prototype @@ -2471,7 +2474,7 @@ sub unop { return $self->maybe_parens( $self->keyword($name) . " $kid", $cx, 16 ); - } + } return $self->maybe_parens_unop($name, $kid, $cx); } else { return $self->maybe_parens( @@ -2511,7 +2514,7 @@ sub pp_each { unop(@_, "each") } sub pp_values { unop(@_, "values") } sub pp_keys { unop(@_, "keys") } { no strict 'refs'; *{"pp_r$_"} = *{"pp_$_"} for qw< keys each values >; } -sub pp_boolkeys { +sub pp_boolkeys { # no name because its an optimisation op that has no keyword unop(@_,""); } @@ -4054,6 +4057,31 @@ sub pp_leavetry { return "eval {\n\t" . $self->pp_leave(@_) . "\n\b}"; } +sub pp_leavetrycatch { + my $self = shift; + my ($op) = @_; + + # Expect that the first three kids should be (entertrycatch, poptry, catch) + my $entertrycatch = $op->first; + $entertrycatch->name eq "entertrycatch" or die "Expected entertrycatch as first child of leavetrycatch"; + + my $tryblock = $entertrycatch->sibling; + $tryblock->name eq "poptry" or die "Expected poptry as second child of leavetrycatch"; + + my $catch = $tryblock->sibling; + $catch->name eq "catch" or die "Expected catch as third child of leavetrycatch"; + + my $catchblock = $catch->first->sibling; + $catchblock->name eq "scope" or die "Expected scope as second child of catch"; + + my $trycode = scopeop(0, $self, $tryblock); + my $catchvar = $self->padname($catch->targ); + my $catchcode = scopeop(0, $self, $catchblock); + + return "try {\n\t$trycode\n\b}\n" . + "catch($catchvar) {\n\t$catchcode\n\b}\cK"; +} + sub _op_is_or_was { my ($op, $expect_type) = @_; my $type = $op->type; @@ -5658,38 +5686,24 @@ sub double_delim { # Escape a characrter. # Only used by tr///, so backslashes hyphens -sub pchr { # ASCII +sub pchr { my($n) = @_; - if ($n == ord '\\') { - return '\\\\'; - } elsif ($n == ord "-") { - return "\\-"; - } elsif (utf8::native_to_unicode($n) >= utf8::native_to_unicode(ord(' ')) - and utf8::native_to_unicode($n) <= utf8::native_to_unicode(ord('~'))) - { - # I'm presuming a regex is not ok here, otherwise we could have used - # /[[:print:]]/a to get here - return chr($n); - } elsif ($n == ord "\a") { - return '\\a'; - } elsif ($n == ord "\b") { - return '\\b'; - } elsif ($n == ord "\t") { - return '\\t'; - } elsif ($n == ord "\n") { - return '\\n'; - } elsif ($n == ord "\e") { - return '\\e'; - } elsif ($n == ord "\f") { - return '\\f'; - } elsif ($n == ord "\r") { - return '\\r'; - } elsif ($n >= ord("\cA") and $n <= ord("\cZ")) { - return '\\c' . $unctrl{chr $n}; - } else { -# return '\x' . sprintf("%02x", $n); - return '\\' . sprintf("%03o", $n); - } + return sprintf("\\x{%X}", $n) if $n > 255; + return '\\\\' if $n == ord '\\'; + return "\\-" if $n == ord "-"; + # I'm presuming a regex is not ok here, otherwise we could have used + # /[[:print:]]/a to get here + return chr($n) if ( utf8::native_to_unicode($n) + >= utf8::native_to_unicode(ord(' ')) + and utf8::native_to_unicode($n) + <= utf8::native_to_unicode(ord('~'))); + + my $mnemonic_pos = index("\a\b\e\f\n\r\t", chr($n)); + return "\\" . substr("abefnrt", $mnemonic_pos, 1) if $mnemonic_pos >= 0; + + return '\\c' . $unctrl{chr $n} if $n >= ord("\cA") and $n <= ord("\cZ"); +# return '\x' . sprintf("%02x", $n); + return '\\' . sprintf("%03o", $n); } # Convert a list of characters into a string suitable for tr/// search or @@ -5765,92 +5779,170 @@ sub tr_decode_byte { return ($from, $to); } -sub tr_chr { - my $x = shift; - if ($x == ord "-") { - return "\\-"; - } elsif ($x == ord "\\") { - return "\\\\"; - } else { - return chr $x; +my $infinity = ~0 >> 1; # IV_MAX + +sub tr_append_to_invlist { + my ($list_ref, $current, $next) = @_; + + # Appends the range $current..$next-1 to the inversion list $list_ref + + printf STDERR "%d: %d..%d %s", __LINE__, $current, $next, Dumper $list_ref if DEBUG; + + if (@$list_ref && $list_ref->[-1] == $current) { + + # The new range extends the current final one. If it is a finite + # rane, replace the current final by the new ending. + if (defined $next) { + $list_ref->[-1] = $next; + } + else { + # The new range extends to infinity, which means the current end + # of the inversion list is dangling. Removing it causes things to + # work. + pop @$list_ref; + } } + else { # The new range starts after the current final one; add it as a + # new range + push @$list_ref, $current; + push @$list_ref, $next if defined $next; + } + + print STDERR __LINE__, ": ", Dumper $list_ref if DEBUG; } -sub tr_invmap { - my ($invlist_ref, $map_ref) = @_; +sub tr_invlist_to_string { + my ($list_ref, $to_complement) = @_; - my $infinity = ~0 >> 1; # IV_MAX - my $from = ""; - my $to = ""; + # Stringify the inversion list $list_ref, possibly complementing it first. + # CAUTION: this can modify $list_ref. - for my $i (0.. @$invlist_ref - 1) { - my $this_from = $invlist_ref->[$i]; - my $map = $map_ref->[$i]; - my $upper = ($i < @$invlist_ref - 1) - ? $invlist_ref->[$i+1] - : $infinity; - my $range = $upper - $this_from - 1; - if (DEBUG) { - print STDERR "i=$i, from=$this_from, upper=$upper, range=$range\n"; + print STDERR __LINE__, ": ", Dumper $list_ref if DEBUG; + + if ($to_complement) { + + # Complementing an inversion list is done by prepending a 0 if it + # doesn't have one there already; otherwise removing the leading 0. + if ($list_ref->[0] == 0) { + shift @$list_ref; + } + else { + unshift @$list_ref, 0; } - next if $map == ~0; - next if $map == ~0 - 1; - $from .= tr_chr($this_from); - $to .= tr_chr($map); - next if $range == 0; # Single code point - if ($range == 1) { # Adjacent code points - $from .= tr_chr($this_from + 1); - $to .= tr_chr($map + 1); + + print STDERR __LINE__, ": ", Dumper $list_ref if DEBUG; + } + + my $output = ""; + + # Every other element is in the list. + for (my $i = 0; $i < @$list_ref; $i += 2) { + my $base = $list_ref->[$i]; + $output .= pchr($base); + last unless defined $list_ref->[$i+1]; + + # The beginning of the next element starts the range of items not in + # the list. + my $upper = $list_ref->[$i+1] - 1; + my $range = $upper - $base; + $output .= '-' if $range > 1; # Adjacent characters don't have a + # minus, though it would be legal to do + # so + $output .= pchr($upper) if $range > 0; + } + + print STDERR __LINE__, ": tr_invlist_to_string() returning '$output'\n" + if DEBUG; + return $output; +} + +my $unmapped = ~0; +my $special_handling = ~0 - 1; + +sub dump_invmap { + my ($invlist_ref, $map_ref) = @_; + + for my $i (0 .. @$invlist_ref - 1) { + printf STDERR "[%d]\t%x\t", $i, $invlist_ref->[$i]; + my $map = $map_ref->[$i]; + if ($map == $unmapped) { + print STDERR "TR_UNMAPPED\n"; } - elsif ($upper != $infinity) { - $from .= "-" . tr_chr($this_from + $range); - $to .= "-" . tr_chr($map + $range); + elsif ($map == $special_handling) { + print STDERR "TR_SPECIAL\n"; } else { - $from .= "-INFTY"; - $to .= "-INFTY"; + printf STDERR "%x\n", $map; } } - - return ($from, $to); } sub tr_decode_utf8 { my($tr_av, $flags) = @_; - printf STDERR "flags=0x%x\n", $flags if DEBUG; + + printf STDERR "\n%s: %d: flags=0x%x\n", __FILE__, __LINE__, $flags if DEBUG; + my $invlist = $tr_av->ARRAYelt(0); my @invlist = unpack("J*", $invlist->PV); my @map = unpack("J*", $tr_av->ARRAYelt(1)->PV); - if (DEBUG) { - for my $i (0 .. @invlist - 1) { - printf STDERR "[%d]\t%x\t", $i, $invlist[$i]; - my $map = $map[$i]; - if ($map == ~0) { - print STDERR "TR_UNMAPPED\n"; - } - elsif ($map == ~0 - 1) { - print STDERR "TR_SPECIAL\n"; - } - else { - printf STDERR "%x\n", $map; - } + dump_invmap(\@invlist, \@map) if DEBUG; + + my @from; + my @to; + + # Go through the whole map + for (my $i = 0; $i < @invlist; $i++) { + my $map = $map[$i]; + printf STDERR "%d: i=%d, source=%x, map=%x\n", + __LINE__, $i, $invlist[$i], $map if DEBUG; + + # Ignore any lines that are unmapped + next if $map == $unmapped; + + # Calculate this component of the mapping; First the lhs + my $this_from = $invlist[$i]; + my $next_from = $invlist[$i+1] if $i < @invlist - 1; + + # The length of the rhs is the same as the lhs, except when special + my $next_map = $map - $this_from + $next_from + if $map != $special_handling && defined $next_from; + + if (DEBUG) { + printf STDERR "%d: i=%d, from=%x, to=%x", + __LINE__, $i, $this_from, $map; + printf STDERR ", next_from=%x,", $next_from if defined $next_from; + printf STDERR ", next_map=%x", $next_map if defined $next_map; + print STDERR "\n"; } - } - my ($from, $to) = tr_invmap(\@invlist, \@map); + # Add the lhs. + tr_append_to_invlist(\@from, $this_from, $next_from); - if ($flags & OPpTRANS_COMPLEMENT) { - shift @map; - pop @invlist; - my $throw_away; - ($from, $throw_away) = tr_invmap(\@invlist, \@map); + # And, the rhs; special handling doesn't get output as it really is an + # unmatched rhs + tr_append_to_invlist(\@to, $map, $next_map) if $map != $special_handling; } - if (DEBUG) { - print STDERR "Returning ", escape_str($from), "/", - escape_str($to), "\n"; + # Done with the input. + + my $to; + if (join("", @from) eq join("", @to)) { + + # the rhs is suppressed if identical to the left. That's because + # tr/ABC/ABC/ can be written as tr/ABC//. (Do this comparison before + # any complementing) + $to = ""; } + else { + $to = tr_invlist_to_string(\@to, 0); # rhs not complemented + } + + my $from = tr_invlist_to_string(\@from, + ($flags & OPpTRANS_COMPLEMENT) != 0); + + print STDERR "Returning ", escape_str($from), "/", + escape_str($to), "\n" if DEBUG; return (escape_str($from), escape_str($to)); } |