diff options
author | Gurusamy Sarathy <gsar@cpan.org> | 1998-08-08 22:18:54 +0000 |
---|---|---|
committer | Gurusamy Sarathy <gsar@cpan.org> | 1998-08-08 22:18:54 +0000 |
commit | 84df6dbaac5dcce30923bafc61c52f3ffa1b669b (patch) | |
tree | cf12e2c57eeb3ade406af6984e8a91a4ea05a830 /lib | |
parent | 527cc686938e627799b4befb57128e2e7c3272c2 (diff) | |
parent | 1eccc87f4ae921520ce1893dd988f4a8a1fa061d (diff) | |
download | perl-84df6dbaac5dcce30923bafc61c52f3ffa1b669b.tar.gz |
integrate maint-5.005 changes into mainline
p4raw-id: //depot/perl@1760
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Benchmark.pm | 22 | ||||
-rw-r--r-- | lib/Class/Struct.pm | 5 | ||||
-rw-r--r-- | lib/ExtUtils/Liblist.pm | 205 | ||||
-rw-r--r-- | lib/ExtUtils/MM_Win32.pm | 45 | ||||
-rw-r--r-- | lib/File/DosGlob.pm | 23 | ||||
-rw-r--r-- | lib/Math/Complex.pm | 156 | ||||
-rw-r--r-- | lib/Pod/Html.pm | 2 | ||||
-rw-r--r-- | lib/Test.pm | 2 | ||||
-rw-r--r-- | lib/Test/Harness.pm | 28 | ||||
-rw-r--r-- | lib/bigint.pl | 2 | ||||
-rw-r--r-- | lib/dumpvar.pl | 13 | ||||
-rw-r--r-- | lib/overload.pm | 543 | ||||
-rw-r--r-- | lib/perl5db.pl | 7 |
13 files changed, 884 insertions, 169 deletions
diff --git a/lib/Benchmark.pm b/lib/Benchmark.pm index f490998039..a28f510d11 100644 --- a/lib/Benchmark.pm +++ b/lib/Benchmark.pm @@ -238,6 +238,13 @@ functionality. =cut +# evaluate something in a clean lexical environment +sub _doeval { eval shift } + +# +# put any lexicals at file scope AFTER here +# + use Carp; use Exporter; @ISA=(Exporter); @@ -280,7 +287,7 @@ sub real { my($r,$pu,$ps,$cu,$cs) = @{$_[0]}; $r ; } sub timediff { my($a, $b) = @_; my @r; - for ($i=0; $i < @$a; ++$i) { + for (my $i=0; $i < @$a; ++$i) { push(@r, $a->[$i] - $b->[$i]); } bless \@r; @@ -329,10 +336,15 @@ sub runloop { last if $pack ne $curpack; } - my $subcode = (ref $c eq 'CODE') - ? "sub { package $pack; my(\$_i)=$n; while (\$_i--){&\$c;} }" - : "sub { package $pack; my(\$_i)=$n; while (\$_i--){$c;} }"; - my $subref = eval $subcode; + my ($subcode, $subref); + if (ref $c eq 'CODE') { + $subcode = "sub { for (1 .. $n) { local \$_; package $pack; &\$c; } }"; + $subref = eval $subcode; + } + else { + $subcode = "sub { for (1 .. $n) { local \$_; package $pack; $c;} }"; + $subref = _doeval($subcode); + } croak "runloop unable to compile '$c': $@\ncode: $subcode\n" if $@; print STDERR "runloop $n '$subcode'\n" if $debug; diff --git a/lib/Class/Struct.pm b/lib/Class/Struct.pm index a39d1ac04a..8fddfbf68e 100644 --- a/lib/Class/Struct.pm +++ b/lib/Class/Struct.pm @@ -40,6 +40,11 @@ sub printem { $self->[$index]; } + sub FETCHSIZE { + my $self = shift; + return scalar(@$self); + } + sub DESTROY { } } diff --git a/lib/ExtUtils/Liblist.pm b/lib/ExtUtils/Liblist.pm index 94f36018e2..b072c1292c 100644 --- a/lib/ExtUtils/Liblist.pm +++ b/lib/ExtUtils/Liblist.pm @@ -191,10 +191,14 @@ sub _win32_ext { # (caller should probably use the list in $Config{libs}) return ("", "", "", "") unless $potential_libs; - my($so) = $Config{'so'}; - my($libs) = $Config{'libs'}; - my($libpth) = $Config{'libpth'}; - my($libext) = $Config{'lib_ext'} || ".lib"; + my $cc = $Config{cc}; + my $VC = 1 if $cc =~ /^cl/i; + my $BC = 1 if $cc =~ /^bcc/i; + my $GC = 1 if $cc =~ /^gcc/i; + my $so = $Config{'so'}; + my $libs = $Config{'libs'}; + my $libpth = $Config{'libpth'}; + my $libext = $Config{'lib_ext'} || ".lib"; if ($libs and $potential_libs !~ /:nodefault/i) { # If Config.pm defines a set of default libs, we always @@ -212,61 +216,100 @@ sub _win32_ext { # compute $extralibs from $potential_libs - my(@searchpath); # from "-L/path" entries in $potential_libs - my(@libpath) = Text::ParseWords::quotewords('\s+', 0, $libpth); - my(@extralibs); + my @searchpath; # from "-L/path" in $potential_libs + my @libpath = Text::ParseWords::quotewords('\s+', 0, $libpth); + my @extralibs; + my $pwd = cwd(); # from Cwd.pm + my $lib = ''; + my $found = 0; + my $search = 1; my($fullname, $thislib, $thispth); - my($pwd) = cwd(); # from Cwd.pm - my($lib) = ''; - my($found) = 0; - foreach $thislib (Text::ParseWords::quotewords('\s+', 0, $potential_libs)){ + foreach (Text::ParseWords::quotewords('\s+', 0, $potential_libs)){ - # Handle possible linker path arguments. - if ($thislib =~ s/^-L// and not -d $thislib) { - warn "-L$thislib ignored, directory does not exist\n" + $thislib = $_; + + # see if entry is a flag + if (/^:\w+$/) { + $search = 0 if lc eq ':nosearch'; + $search = 1 if lc eq ':search'; + warn "Ignoring unknown flag '$thislib'\n" + if $verbose and !/^:(no)?(search|default)$/i; + next; + } + + # if searching is disabled, do compiler-specific translations + unless ($search) { + s/^-L/-libpath:/ if $VC; + s/^-l(.+)$/$1.lib/ unless $GC; + push(@extralibs, $_); + $found++; + next; + } + + # handle possible linker path arguments + if (s/^-L// and not -d) { + warn "$thislib ignored, directory does not exist\n" if $verbose; next; } - elsif (-d $thislib) { - unless ($self->file_name_is_absolute($thislib)) { - warn "Warning: '-L$thislib' changed to '-L$pwd/$thislib'\n"; - $thislib = $self->catdir($pwd,$thislib); + elsif (-d) { + unless ($self->file_name_is_absolute($_)) { + warn "Warning: '$thislib' changed to '-L$pwd/$_'\n"; + $_ = $self->catdir($pwd,$_); } - push(@searchpath, $thislib); + push(@searchpath, $_); next; } - # Handle possible library arguments. - if ($thislib =~ s/^-l// and $thislib !~ /^lib/i) { - $thislib = "lib$thislib"; + # handle possible library arguments + if (s/^-l// and $GC and !/^lib/i) { + $_ = "lib$_"; } - $thislib .= $libext if $thislib !~ /\Q$libext\E$/i; + $_ .= $libext if !/\Q$libext\E$/i; + + my $secondpass = 0; + LOOKAGAIN: # look for the file itself - if (-f $thislib) { - warn "'$thislib' found\n" if $verbose; + if (-f) { + warn "'$thislib' found as '$_'\n" if $verbose; $found++; - push(@extralibs, $thislib); + push(@extralibs, $_); next; } - my($found_lib)=0; + my $found_lib = 0; foreach $thispth (@searchpath, @libpath){ - unless (-f ($fullname="$thispth\\$thislib")) { - warn "$thislib not found in $thispth\n" if $verbose; + unless (-f ($fullname="$thispth\\$_")) { + warn "'$thislib' not found as '$fullname'\n" if $verbose; next; } - warn "'$thislib' found at $fullname\n" if $verbose; + warn "'$thislib' found as '$fullname'\n" if $verbose; $found++; $found_lib++; push(@extralibs, $fullname); last; } + + # do another pass with (or without) leading 'lib' if they used -l + if (!$found_lib and $thislib =~ /^-l/ and !$secondpass++) { + if ($GC) { + goto LOOKAGAIN if s/^lib//i; + } + elsif (!/^lib/i) { + $_ = "lib$_"; + goto LOOKAGAIN; + } + } + + # give up warn "Note (probably harmless): " ."No library found for '$thislib'\n" unless $found_lib>0; + } + return ('','','','') unless $found; # make sure paths with spaces are properly quoted @@ -579,16 +622,38 @@ Unix-OS/2 version in several respects: =item * +If C<$potential_libs> is empty, the return value will be empty. +Otherwise, the libraries specified by C<$Config{libs}> (see Config.pm) +will be appended to the list of C<$potential_libs>. The libraries +will be searched for in the directories specified in C<$potential_libs> +as well as in C<$Config{libpth}>. For each library that is found, a +space-separated list of fully qualified library pathnames is generated. + +=item * + Input library and path specifications are accepted with or without the -C<-l> and C<-L> prefices used by Unix linkers. C<-lfoo> specifies the -library C<libfoo.lib> (unless C<foo> already starts with C<lib>), and -C<-Ls:ome\dir> specifies a directory to look for the libraries that follow. -If neither prefix is present, a token is considered a directory to search -if it is in fact a directory, and a library to search for otherwise. The -C<$Config{lib_ext}> suffix will be appended to any entries that are not -directories and don't already have the suffix. Authors who wish their -extensions to be portable to Unix or OS/2 should use the Unix prefixes, -since the Unix-OS/2 version of ext() requires them. +C<-l> and C<-L> prefices used by Unix linkers. + +An entry of the form C<-La:\foo> specifies the C<a:\foo> directory to look +for the libraries that follow. + +An entry of the form C<-lfoo> specifies the library C<foo>, which may be +spelled differently depending on what kind of compiler you are using. If +you are using GCC, it gets translated to C<libfoo.a>, but for other win32 +compilers, it becomes C<foo.lib>. If no files are found by those translated +names, one more attempt is made to find them using either C<foo.a> or +C<libfoo.lib>, depending on whether GCC or some other win32 compiler is +being used, respectively. + +If neither the C<-L> or C<-l> prefix is present in an entry, the entry is +considered a directory to search if it is in fact a directory, and a +library to search for otherwise. The C<$Config{lib_ext}> suffix will +be appended to any entries that are not directories and don't already have +the suffix. + +Note that the C<-L> and <-l> prefixes are B<not required>, but authors +who wish their extensions to be portable to Unix or OS/2 should use the +prefixes, since the Unix-OS/2 version of ext() requires them. =item * @@ -597,15 +662,21 @@ not handle object files in the place of libraries. =item * -If C<$potential_libs> is empty, the return value will be empty. -Otherwise, the libraries specified by C<$Config{libs}> (see Config.pm) -will be appended to the list of C<$potential_libs>. The libraries -will be searched for in the directories specified in C<$potential_libs> -as well as in C<$Config{libpth}>. For each library that is found, a -space-separated list of fully qualified library pathnames is generated. -You may specify an entry that matches C</:nodefault/i> in -C<$potential_libs> to disable the appending of default libraries -found in C<$Config{libs}> (this should be only needed very rarely). +Entries in C<$potential_libs> beginning with a colon and followed by +alphanumeric characters are treated as flags. Unknown flags will be ignored. + +An entry that matches C</:nodefault/i> disables the appending of default +libraries found in C<$Config{libs}> (this should be only needed very rarely). + +An entry that matches C</:nosearch/i> disables all searching for +the libraries specified after it. Translation of C<-Lfoo> and +C<-lfoo> still happens as appropriate (depending on compiler being used, +as reflected by C<$Config{cc}>), but the entries are not verified to be +valid files or directories. + +An entry that matches C</:search/i> reenables searching for +the libraries specified after it. You can put it at the end to +enable searching for default libraries specified by C<$Config{libs}>. =item * @@ -630,6 +701,44 @@ C<$potential_libs> could be (literally): Note how the first and last entries are protected by quotes in order to protect the spaces. +=item * + +Since this module is most often used only indirectly from extension +C<Makefile.PL> files, here is an example C<Makefile.PL> entry to add +a library to the build process for an extension: + + LIBS => ['-lgl'] + +When using GCC, that entry specifies that MakeMaker should first look +for C<libgl.a> (followed by C<gl.a>) in all the locations specified by +C<$Config{libpth}>. + +When using a compiler other than GCC, the above entry will search for +C<gl.lib> (followed by C<libgl.lib>). + +If the library happens to be in a location not in C<$Config{libpth}>, +you need: + + LIBS => ['-Lc:\gllibs -lgl'] + +Here is a less often used example: + + LIBS => ['-lgl', ':nosearch -Ld:\mesalibs -lmesa -luser32'] + +This specifies a search for library C<gl> as before. If that search +fails to find the library, it looks at the next item in the list. The +C<:nosearch> flag will prevent searching for the libraries that follow, +so it simply returns the value as C<-Ld:\mesalibs -lmesa -luser32>, +since GCC can use that value as is with its linker. + +When using the Visual C compiler, the second item is returned as +C<-libpath:d:\mesalibs mesa.lib user32.lib>. + +When using the Borland compiler, the second item is returned as +C<-Ld:\mesalibs mesa.lib user32.lib>, and MakeMaker takes care of +moving the C<-Ld:\mesalibs> to the correct place in the linker +command line. + =back diff --git a/lib/ExtUtils/MM_Win32.pm b/lib/ExtUtils/MM_Win32.pm index 72c32fb195..a1226b5463 100644 --- a/lib/ExtUtils/MM_Win32.pm +++ b/lib/ExtUtils/MM_Win32.pm @@ -67,7 +67,21 @@ sub replace_manpage_separator { sub maybe_command { my($self,$file) = @_; - return "$file.exe" if -e "$file.exe"; + my @e = exists($ENV{'PATHEXT'}) + ? split(/;/, $ENV{PATHEXT}) + : qw(.com .exe .bat .cmd); + my $e = ''; + for (@e) { $e .= "\Q$_\E|" } + chop $e; + # see if file ends in one of the known extensions + if ($file =~ /($e)$/i) { + return $file if -e $file; + } + else { + for (@e) { + return "$file$_" if -e "$file$_"; + } + } return; } @@ -155,21 +169,19 @@ sub init_others $self->{'TEST_F'} = '$(PERL) -I$(PERL_ARCHLIB) -I$(PERL_LIB) -MExtUtils::Command -e test_f'; $self->{'LD'} = $Config{'ld'} || 'link'; $self->{'AR'} = $Config{'ar'} || 'lib'; - if ($GCC) - { - $self->{'LDLOADLIBS'} ||= ' '; - } - else - { - $self->{'LDLOADLIBS'} - ||= ( $BORLAND - ? 'import32.lib' - : # compiler adds msvcrtd?.lib according to debug switches - 'oldnames.lib kernel32.lib comdlg32.lib winspool.lib gdi32.lib ' - .'advapi32.lib user32.lib shell32.lib netapi32.lib ole32.lib ' - .'oleaut32.lib uuid.lib wsock32.lib mpr.lib winmm.lib version.lib' - ) . ' $(LIBC) odbc32.lib odbccp32.lib'; - } + $self->{'LDLOADLIBS'} ||= $Config{'libs'}; + # -Lfoo must come first for Borland, so we put it in LDDLFLAGS + if ($BORLAND) { + my $libs = $self->{'LDLOADLIBS'}; + my $libpath = ''; + while ($libs =~ s/(?:^|\s)(("?)-L.+?\2)(?:\s|$)/ /) { + $libpath .= ' ' if length $libpath; + $libpath .= $1; + } + $self->{'LDLOADLIBS'} = $libs; + $self->{'LDDLFLAGS'} ||= $Config{'lddlflags'}; + $self->{'LDDLFLAGS'} .= " $libpath"; + } $self->{'DEV_NULL'} = '> NUL'; # $self->{'NOECHO'} = ''; # till we have it working } @@ -718,6 +730,7 @@ We don't want manpage process. XXX add pod2html support later. =cut sub manifypods { + my($self) = shift; return "\nmanifypods :\n\t$self->{NOECHO}\$(NOOP)\n"; } diff --git a/lib/File/DosGlob.pm b/lib/File/DosGlob.pm index 24b28b2dce..594ee2ec84 100644 --- a/lib/File/DosGlob.pm +++ b/lib/File/DosGlob.pm @@ -97,17 +97,27 @@ my %entries; sub glob { my $pat = shift; my $cxix = shift; + my @pat; # glob without args defaults to $_ $pat = $_ unless defined $pat; + # extract patterns + if ($pat =~ /\s/) { + require Text::ParseWords; + @pat = Text::ParseWords::parse_line('\s+',0,$pat); + } + else { + push @pat, $pat; + } + # assume global context if not provided one $cxix = '_G_' unless defined $cxix; $iter{$cxix} = 0 unless exists $iter{$cxix}; # if we're just beginning, do it all first if ($iter{$cxix} == 0) { - $entries{$cxix} = [doglob(1,$pat)]; + $entries{$cxix} = [doglob(1,@pat)]; } # chuck it all out, quick or slow @@ -174,6 +184,15 @@ backslashes and forward slashes are both accepted, and preserved. You may have to double the backslashes if you are putting them in literally, due to double-quotish parsing of the pattern by perl. +Spaces in the argument delimit distinct patterns, so +C<glob('*.exe *.dll')> globs all filenames that end in C<.exe> +or C<.dll>. If you want to put in literal spaces in the glob +pattern, you can escape them with either double quotes, or backslashes. +e.g. C<glob('c:/"Program Files"/*/*.dll')>, or +C<glob('c:/Program\ Files/*/*.dll')>. The argument is tokenized using +C<Text::ParseWords::parse_line()>, so see L<Text::ParseWords> for details +of the quoting rules used. + Extending it to csh patterns is left as an exercise to the reader. =head1 EXPORTS (by request only) @@ -224,5 +243,7 @@ perl perlglob.bat +Text::ParseWords + =cut diff --git a/lib/Math/Complex.pm b/lib/Math/Complex.pm index aca85c6acd..e711c1483d 100644 --- a/lib/Math/Complex.pm +++ b/lib/Math/Complex.pm @@ -179,7 +179,7 @@ sub cplxe { # # The number defined as pi = 180 degrees # -use constant pi => 4 * atan2(1, 1); +use constant pi => 4 * CORE::atan2(1, 1); # # pit2 @@ -208,7 +208,7 @@ use constant deg1 => pi / 180; # # Used in log10(). # -use constant uplog10 => 1 / log(10); +use constant uplog10 => 1 / CORE::log(10); # # i @@ -246,7 +246,7 @@ sub update_cartesian { my $self = shift; my ($r, $t) = @{$self->{'polar'}}; $self->{c_dirty} = 0; - return $self->{'cartesian'} = [$r * cos $t, $r * sin $t]; + return $self->{'cartesian'} = [$r * CORE::cos($t), $r * CORE::sin($t)]; } # @@ -260,7 +260,7 @@ sub update_polar { my ($x, $y) = @{$self->{'cartesian'}}; $self->{p_dirty} = 0; return $self->{'polar'} = [0, 0] if $x == 0 && $y == 0; - return $self->{'polar'} = [sqrt($x*$x + $y*$y), atan2($y, $x)]; + return $self->{'polar'} = [CORE::sqrt($x*$x + $y*$y), CORE::atan2($y, $x)]; } # @@ -432,7 +432,7 @@ sub power { return 0 if ($z1z); return 1 if ($z2z or $z1 == 1); } - my $w = $inverted ? exp($z1 * log $z2) : exp($z2 * log $z1); + my $w = $inverted ? CORE::exp($z1 * CORE::log($z2)) : CORE::exp($z2 * CORE::log($z1)); # If both arguments cartesian, return cartesian, else polar. return $z1->{c_dirty} == 0 && (not ref $z2 or $z2->{c_dirty} == 0) ? @@ -548,9 +548,9 @@ sub arg { sub sqrt { my ($z) = @_; my ($re, $im) = ref $z ? @{$z->cartesian} : ($z, 0); - return $re < 0 ? cplx(0, sqrt(-$re)) : sqrt($re) if $im == 0; + return $re < 0 ? cplx(0, CORE::sqrt(-$re)) : CORE::sqrt($re) if $im == 0; my ($r, $t) = @{$z->polar}; - return (ref $z)->emake(sqrt($r), $t/2); + return (ref $z)->emake(CORE::sqrt($r), $t/2); } # @@ -562,10 +562,10 @@ sub sqrt { # sub cbrt { my ($z) = @_; - return $z < 0 ? -exp(log(-$z)/3) : ($z > 0 ? exp(log($z)/3): 0) + return $z < 0 ? -CORE::exp(CORE::log(-$z)/3) : ($z > 0 ? CORE::exp(CORE::log($z)/3): 0) unless ref $z; my ($r, $t) = @{$z->polar}; - return (ref $z)->emake(exp(log($r)/3), $t/3); + return (ref $z)->emake(CORE::exp(CORE::log($r)/3), $t/3); } # @@ -596,7 +596,7 @@ sub _rootbad { sub root { my ($z, $n) = @_; _rootbad($n) if ($n < 1 or int($n) != $n); - my ($r, $t) = ref $z ? @{$z->polar} : (abs($z), $z >= 0 ? 0 : pi); + my ($r, $t) = ref $z ? @{$z->polar} : (CORE::abs($z), $z >= 0 ? 0 : pi); my @root; my $k; my $theta_inc = pit2 / $n; @@ -671,7 +671,7 @@ sub theta { sub exp { my ($z) = @_; my ($x, $y) = @{$z->cartesian}; - return (ref $z)->emake(exp($x), $y); + return (ref $z)->emake(CORE::exp($x), $y); } # @@ -704,13 +704,13 @@ sub log { my ($z) = @_; unless (ref $z) { _logofzero("log") if $z == 0; - return $z > 0 ? log($z) : cplx(log(-$z), pi); + return $z > 0 ? CORE::log($z) : cplx(CORE::log(-$z), pi); } my ($r, $t) = @{$z->polar}; _logofzero("log") if $r == 0; if ($t > pi()) { $t -= pit2 } elsif ($t <= -pi()) { $t += pit2 } - return (ref $z)->make(log($r), $t); + return (ref $z)->make(CORE::log($r), $t); } # @@ -739,8 +739,8 @@ sub logn { my ($z, $n) = @_; $z = cplx($z, 0) unless ref $z; my $logn = $logn{$n}; - $logn = $logn{$n} = log($n) unless defined $logn; # Cache log(n) - return log($z) / $logn; + $logn = $logn{$n} = CORE::log($n) unless defined $logn; # Cache log(n) + return CORE::log($z) / $logn; } # @@ -751,10 +751,10 @@ sub logn { sub cos { my ($z) = @_; my ($x, $y) = @{$z->cartesian}; - my $ey = exp($y); + my $ey = CORE::exp($y); my $ey_1 = 1 / $ey; - return (ref $z)->make(cos($x) * ($ey + $ey_1)/2, - sin($x) * ($ey_1 - $ey)/2); + return (ref $z)->make(CORE::cos($x) * ($ey + $ey_1)/2, + CORE::sin($x) * ($ey_1 - $ey)/2); } # @@ -765,10 +765,10 @@ sub cos { sub sin { my ($z) = @_; my ($x, $y) = @{$z->cartesian}; - my $ey = exp($y); + my $ey = CORE::exp($y); my $ey_1 = 1 / $ey; - return (ref $z)->make(sin($x) * ($ey + $ey_1)/2, - cos($x) * ($ey - $ey_1)/2); + return (ref $z)->make(CORE::sin($x) * ($ey + $ey_1)/2, + CORE::cos($x) * ($ey - $ey_1)/2); } # @@ -778,9 +778,9 @@ sub sin { # sub tan { my ($z) = @_; - my $cz = cos($z); - _divbyzero "tan($z)", "cos($z)" if (abs($cz) < $eps); - return sin($z) / $cz; + my $cz = CORE::cos($z); + _divbyzero "tan($z)", "cos($z)" if (CORE::abs($cz) < $eps); + return CORE::sin($z) / $cz; } # @@ -790,7 +790,7 @@ sub tan { # sub sec { my ($z) = @_; - my $cz = cos($z); + my $cz = CORE::cos($z); _divbyzero "sec($z)", "cos($z)" if ($cz == 0); return 1 / $cz; } @@ -802,7 +802,7 @@ sub sec { # sub csc { my ($z) = @_; - my $sz = sin($z); + my $sz = CORE::sin($z); _divbyzero "csc($z)", "sin($z)" if ($sz == 0); return 1 / $sz; } @@ -821,9 +821,9 @@ sub cosec { Math::Complex::csc(@_) } # sub cot { my ($z) = @_; - my $sz = sin($z); + my $sz = CORE::sin($z); _divbyzero "cot($z)", "sin($z)" if ($sz == 0); - return cos($z) / $sz; + return CORE::cos($z) / $sz; } # @@ -840,17 +840,17 @@ sub cotan { Math::Complex::cot(@_) } # sub acos { my $z = $_[0]; - return atan2(sqrt(1-$z*$z), $z) if (! ref $z) && abs($z) <= 1; + return CORE::atan2(CORE::sqrt(1-$z*$z), $z) if (! ref $z) && CORE::abs($z) <= 1; my ($x, $y) = ref $z ? @{$z->cartesian} : ($z, 0); - my $t1 = sqrt(($x+1)*($x+1) + $y*$y); - my $t2 = sqrt(($x-1)*($x-1) + $y*$y); + my $t1 = CORE::sqrt(($x+1)*($x+1) + $y*$y); + my $t2 = CORE::sqrt(($x-1)*($x-1) + $y*$y); my $alpha = ($t1 + $t2)/2; my $beta = ($t1 - $t2)/2; $alpha = 1 if $alpha < 1; if ($beta > 1) { $beta = 1 } elsif ($beta < -1) { $beta = -1 } - my $u = atan2(sqrt(1-$beta*$beta), $beta); - my $v = log($alpha + sqrt($alpha*$alpha-1)); + my $u = CORE::atan2(CORE::sqrt(1-$beta*$beta), $beta); + my $v = CORE::log($alpha + CORE::sqrt($alpha*$alpha-1)); $v = -$v if $y > 0 || ($y == 0 && $x < -1); return $package->make($u, $v); } @@ -862,17 +862,17 @@ sub acos { # sub asin { my $z = $_[0]; - return atan2($z, sqrt(1-$z*$z)) if (! ref $z) && abs($z) <= 1; + return CORE::atan2($z, CORE::sqrt(1-$z*$z)) if (! ref $z) && CORE::abs($z) <= 1; my ($x, $y) = ref $z ? @{$z->cartesian} : ($z, 0); - my $t1 = sqrt(($x+1)*($x+1) + $y*$y); - my $t2 = sqrt(($x-1)*($x-1) + $y*$y); + my $t1 = CORE::sqrt(($x+1)*($x+1) + $y*$y); + my $t2 = CORE::sqrt(($x-1)*($x-1) + $y*$y); my $alpha = ($t1 + $t2)/2; my $beta = ($t1 - $t2)/2; $alpha = 1 if $alpha < 1; if ($beta > 1) { $beta = 1 } elsif ($beta < -1) { $beta = -1 } - my $u = atan2($beta, sqrt(1-$beta*$beta)); - my $v = -log($alpha + sqrt($alpha*$alpha-1)); + my $u = CORE::atan2($beta, CORE::sqrt(1-$beta*$beta)); + my $v = -CORE::log($alpha + CORE::sqrt($alpha*$alpha-1)); $v = -$v if $y > 0 || ($y == 0 && $x < -1); return $package->make($u, $v); } @@ -884,10 +884,10 @@ sub asin { # sub atan { my ($z) = @_; - return atan2($z, 1) unless ref $z; + return CORE::atan2($z, 1) unless ref $z; _divbyzero "atan(i)" if ( $z == i); _divbyzero "atan(-i)" if (-$z == i); - my $log = log((i + $z) / (i - $z)); + my $log = CORE::log((i + $z) / (i - $z)); $ip2 = 0.5 * i unless defined $ip2; return $ip2 * $log; } @@ -928,10 +928,10 @@ sub acosec { Math::Complex::acsc(@_) } # sub acot { my ($z) = @_; - _divbyzero "acot(0)" if (abs($z) < $eps); - return ($z >= 0) ? atan2(1, $z) : atan2(-1, -$z) unless ref $z; - _divbyzero "acot(i)" if (abs($z - i) < $eps); - _logofzero "acot(-i)" if (abs($z + i) < $eps); + _divbyzero "acot(0)" if (CORE::abs($z) < $eps); + return ($z >= 0) ? CORE::atan2(1, $z) : CORE::atan2(-1, -$z) unless ref $z; + _divbyzero "acot(i)" if (CORE::abs($z - i) < $eps); + _logofzero "acot(-i)" if (CORE::abs($z + i) < $eps); return atan(1 / $z); } @@ -951,14 +951,14 @@ sub cosh { my ($z) = @_; my $ex; unless (ref $z) { - $ex = exp($z); + $ex = CORE::exp($z); return ($ex + 1/$ex)/2; } my ($x, $y) = @{$z->cartesian}; - $ex = exp($x); + $ex = CORE::exp($x); my $ex_1 = 1 / $ex; - return (ref $z)->make(cos($y) * ($ex + $ex_1)/2, - sin($y) * ($ex - $ex_1)/2); + return (ref $z)->make(CORE::cos($y) * ($ex + $ex_1)/2, + CORE::sin($y) * ($ex - $ex_1)/2); } # @@ -970,14 +970,14 @@ sub sinh { my ($z) = @_; my $ex; unless (ref $z) { - $ex = exp($z); + $ex = CORE::exp($z); return ($ex - 1/$ex)/2; } my ($x, $y) = @{$z->cartesian}; - $ex = exp($x); + $ex = CORE::exp($x); my $ex_1 = 1 / $ex; - return (ref $z)->make(cos($y) * ($ex - $ex_1)/2, - sin($y) * ($ex + $ex_1)/2); + return (ref $z)->make(CORE::cos($y) * ($ex - $ex_1)/2, + CORE::sin($y) * ($ex + $ex_1)/2); } # @@ -1050,15 +1050,15 @@ sub cotanh { Math::Complex::coth(@_) } sub acosh { my ($z) = @_; unless (ref $z) { - return log($z + sqrt($z*$z-1)) if $z >= 1; + return CORE::log($z + CORE::sqrt($z*$z-1)) if $z >= 1; $z = cplx($z, 0); } my ($re, $im) = @{$z->cartesian}; if ($im == 0) { - return cplx(log($re + sqrt($re*$re - 1)), 0) if $re >= 1; - return cplx(0, atan2(sqrt(1-$re*$re), $re)) if abs($re) <= 1; + return cplx(CORE::log($re + CORE::sqrt($re*$re - 1)), 0) if $re >= 1; + return cplx(0, CORE::atan2(CORE::sqrt(1-$re*$re), $re)) if CORE::abs($re) <= 1; } - return log($z + sqrt($z*$z - 1)); + return CORE::log($z + CORE::sqrt($z*$z - 1)); } # @@ -1068,7 +1068,7 @@ sub acosh { # sub asinh { my ($z) = @_; - return log($z + sqrt($z*$z + 1)); + return CORE::log($z + CORE::sqrt($z*$z + 1)); } # @@ -1079,12 +1079,12 @@ sub asinh { sub atanh { my ($z) = @_; unless (ref $z) { - return log((1 + $z)/(1 - $z))/2 if abs($z) < 1; + return CORE::log((1 + $z)/(1 - $z))/2 if CORE::abs($z) < 1; $z = cplx($z, 0); } _divbyzero 'atanh(1)', "1 - $z" if ($z == 1); _logofzero 'atanh(-1)' if ($z == -1); - return 0.5 * log((1 + $z) / (1 - $z)); + return 0.5 * CORE::log((1 + $z) / (1 - $z)); } # @@ -1123,14 +1123,14 @@ sub acosech { Math::Complex::acsch(@_) } # sub acoth { my ($z) = @_; - _divbyzero 'acoth(0)' if (abs($z) < $eps); + _divbyzero 'acoth(0)' if (CORE::abs($z) < $eps); unless (ref $z) { - return log(($z + 1)/($z - 1))/2 if abs($z) > 1; + return CORE::log(($z + 1)/($z - 1))/2 if CORE::abs($z) > 1; $z = cplx($z, 0); } - _divbyzero 'acoth(1)', "$z - 1" if (abs($z - 1) < $eps); - _logofzero 'acoth(-1)', "1 / $z" if (abs($z + 1) < $eps); - return log((1 + $z) / ($z - 1)) / 2; + _divbyzero 'acoth(1)', "$z - 1" if (CORE::abs($z - 1) < $eps); + _logofzero 'acoth(-1)', "1 / $z" if (CORE::abs($z + 1) < $eps); + return CORE::log((1 + $z) / ($z - 1)) / 2; } # @@ -1156,7 +1156,7 @@ sub atan2 { ($re2, $im2) = ref $z2 ? @{$z2->cartesian} : ($z2, 0); } if ($im2 == 0) { - return cplx(atan2($re1, $re2), 0) if $im1 == 0; + return cplx(CORE::atan2($re1, $re2), 0) if $im1 == 0; return cplx(($im1<=>0) * pip2, 0) if $re2 == 0; } my $w = atan($z1/$z2); @@ -1232,14 +1232,14 @@ sub stringify_cartesian { my ($re, $im); $x = int($x + ($x < 0 ? -1 : 1) * $eps) - if int(abs($x)) != int(abs($x) + $eps); + if int(CORE::abs($x)) != int(CORE::abs($x) + $eps); $y = int($y + ($y < 0 ? -1 : 1) * $eps) - if int(abs($y)) != int(abs($y) + $eps); + if int(CORE::abs($y)) != int(CORE::abs($y) + $eps); - $re = "$x" if abs($x) >= $eps; + $re = "$x" if CORE::abs($x) >= $eps; if ($y == 1) { $im = 'i' } elsif ($y == -1) { $im = '-i' } - elsif (abs($y) >= $eps) { $im = $y . "i" } + elsif (CORE::abs($y) >= $eps) { $im = $y . "i" } my $str = ''; $str = $re if defined $re; @@ -1298,15 +1298,15 @@ sub stringify_polar { $nt = ($nt - int($nt)) * pit2; $nt += pit2 if $nt < 0; # Range [0, 2pi] - if (abs($nt) <= $eps) { $theta = 0 } - elsif (abs(pi-$nt) <= $eps) { $theta = 'pi' } + if (CORE::abs($nt) <= $eps) { $theta = 0 } + elsif (CORE::abs(pi-$nt) <= $eps) { $theta = 'pi' } if (defined $theta) { $r = int($r + ($r < 0 ? -1 : 1) * $eps) - if int(abs($r)) != int(abs($r) + $eps); + if int(CORE::abs($r)) != int(CORE::abs($r) + $eps); $theta = int($theta + ($theta < 0 ? -1 : 1) * $eps) if ($theta ne 'pi' and - int(abs($theta)) != int(abs($theta) + $eps)); + int(CORE::abs($theta)) != int(CORE::abs($theta) + $eps)); return "\[$r,$theta\]"; } @@ -1316,13 +1316,13 @@ sub stringify_polar { $nt -= pit2 if $nt > pi; - if (abs($nt) >= deg1) { + if (CORE::abs($nt) >= deg1) { my ($n, $k, $kpi); for ($k = 1, $kpi = pi; $k < 10; $k++, $kpi += pi) { $n = int($kpi / $nt + ($nt > 0 ? 1 : -1) * 0.5); - if (abs($kpi/$n - $nt) <= $eps) { - $n = abs $n; + if (CORE::abs($kpi/$n - $nt) <= $eps) { + $n = CORE::abs($n); my $gcd = gcd($k, $n); if ($gcd > 1) { $k /= $gcd; @@ -1340,10 +1340,10 @@ sub stringify_polar { $theta = $nt unless defined $theta; $r = int($r + ($r < 0 ? -1 : 1) * $eps) - if int(abs($r)) != int(abs($r) + $eps); + if int(CORE::abs($r)) != int(CORE::abs($r) + $eps); $theta = int($theta + ($theta < 0 ? -1 : 1) * $eps) if ($theta !~ m(^-?\d*pi/\d+$) and - int(abs($theta)) != int(abs($theta) + $eps)); + int(CORE::abs($theta)) != int(CORE::abs($theta) + $eps)); return "\[$r,$theta\]"; } diff --git a/lib/Pod/Html.pm b/lib/Pod/Html.pm index a0dc4b3f11..5d2e07b2af 100644 --- a/lib/Pod/Html.pm +++ b/lib/Pod/Html.pm @@ -1293,7 +1293,7 @@ sub process_puretext { } elsif ($word =~ m,^\w+://\w,) { # looks like a URL $word = qq(<A HREF="$word">$word</A>); - } elsif ($word =~ /[\w.-]+\@\w+\.\w/) { + } elsif ($word =~ /[\w.-]+\@[\w-]+\.\w/) { # looks like an e-mail address my ($w1, $w2, $w3) = ("", $word, ""); ($w1, $w2, $w3) = ("(", $1, ")$2") if $word =~ /^\((.*?)\)(,?)/; diff --git a/lib/Test.pm b/lib/Test.pm index 5f198c234c..6f57415efd 100644 --- a/lib/Test.pm +++ b/lib/Test.pm @@ -225,7 +225,7 @@ L<Test::Harness> and various test coverage analysis tools. =head1 AUTHOR -Copyright © 1998 Joshua Nathaniel Pritikin. All rights reserved. +Copyright (C) 1998 Joshua Nathaniel Pritikin. All rights reserved. This package is free software and is provided "as is" without express or implied warranty. It may be used, redistributed and/or modified diff --git a/lib/Test/Harness.pm b/lib/Test/Harness.pm index 5decc756ff..9c61d3a9dd 100644 --- a/lib/Test/Harness.pm +++ b/lib/Test/Harness.pm @@ -16,6 +16,8 @@ $VERSION = "1.1602"; # Some experimental versions of OS/2 build have broken $? my $ignore_exitcode = $ENV{HARNESS_IGNORE_EXITCODE}; +my $files_in_dir = $ENV{HARNESS_FILELEAK_IN_DIR}; + my $tests_skipped = 0; my $subtests_skipped = 0; @@ -46,6 +48,8 @@ format STDOUT = $verbose = 0; $switches = "-w"; +sub globdir { opendir DIRH, shift; my @f = readdir DIRH; closedir DIRH; @f } + sub runtests { my(@tests) = @_; local($|) = 1; @@ -62,6 +66,7 @@ sub runtests { if ($^O eq 'VMS') { $switches =~ s/-(\S*[A-Z]\S*)/"-$1"/g } + my @dir_files = globdir $files_in_dir if defined $files_in_dir; my $t_start = new Benchmark; while ($test = shift(@tests)) { $te = $test; @@ -212,6 +217,17 @@ sub runtests { }; } $subtests_skipped += $skipped; + if (defined $files_in_dir) { + my @new_dir_files = globdir $files_in_dir; + if (@new_dir_files != @dir_files) { + my %f; + @f{@new_dir_files} = (1) x @new_dir_files; + delete @f{@dir_files}; + my @f = sort keys %f; + print "LEAKED FILES: @f\n"; + @dir_files = @new_dir_files; + } + } } my $t_total = timediff(new Benchmark, $t_start); @@ -421,9 +437,19 @@ above messages. =head1 ENVIRONMENT -Setting C<HARNESS_IGNORE_EXITCODE> makes it ignore the exit status +Setting C<HARNESS_IGNORE_EXITCODE> makes harness ignore the exit status of child processes. +If C<HARNESS_FILELEAK_IN_DIR> is set to the name of a directory, harness +will check after each test whether new files appeared in that directory, +and report them as + + LEAKED FILES: scr.tmp 0 my.db + +If relative, directory name is with respect to the current directory at +the moment runtests() was called. Putting absolute path into +C<HARNESS_FILELEAK_IN_DIR> may give more predicatable results. + =head1 SEE ALSO L<Test> for writing test scripts and also L<Benchmark> for the diff --git a/lib/bigint.pl b/lib/bigint.pl index bfd2efa88c..adeb17f28a 100644 --- a/lib/bigint.pl +++ b/lib/bigint.pl @@ -74,7 +74,7 @@ sub external { #(int_num_array) return num_str sub main'bneg { #(num_str) return num_str local($_) = &'bnorm(@_); vec($_,0,8) ^= ord('+') ^ ord('-') unless $_ eq '+0'; - s/^H/N/; + s/^./N/ unless /^[-+]/; # works both in ASCII and EBCDIC $_; } diff --git a/lib/dumpvar.pl b/lib/dumpvar.pl index cc7da89a62..32d4692d13 100644 --- a/lib/dumpvar.pl +++ b/lib/dumpvar.pl @@ -23,6 +23,7 @@ $tick = "auto" unless defined $tick; $unctrl = 'quote' unless defined $unctrl; $subdump = 1; $dumpReused = 0 unless defined $dumpReused; +$bareStringify = 1 unless defined $bareStringify; sub main::dumpValue { local %address; @@ -50,6 +51,10 @@ sub stringify { return 'undef' unless defined $_ or not $printUndef; return $_ . "" if ref \$_ eq 'GLOB'; + $_ = &{'overload::StrVal'}($_) + if $bareStringify and ref $_ + and defined %overload:: and defined &{'overload::StrVal'}; + if ($tick eq 'auto') { if (/[\000-\011\013-\037\177]/) { $tick = '"'; @@ -110,7 +115,7 @@ sub unwrap { return if $DB::signal; local($v) = shift ; local($s) = shift ; # extra no of spaces - local(%v,@v,$sp,$value,$key,$type,@sortKeys,$more,$shortmore,$short) ; + local(%v,@v,$sp,$value,$key,@sortKeys,$more,$shortmore,$short) ; local($tHashDepth,$tArrayDepth) ; $sp = " " x $s ; @@ -118,9 +123,11 @@ sub unwrap { # Check for reused addresses if (ref $v) { - ($address) = $v =~ /(0x[0-9a-f]+)\)$/ ; + my $val = $v; + $val = &{'overload::StrVal'}($v) + if defined %overload:: and defined &{'overload::StrVal'}; + ($address) = $val =~ /(0x[0-9a-f]+)\)$/ ; if (!$dumpReused && defined $address) { - ($type) = $v =~ /=(.*?)\([^=]+$/ ; $address{$address}++ ; if ( $address{$address} > 1 ) { print "${sp}-> REUSED_ADDRESS\n" ; diff --git a/lib/overload.pm b/lib/overload.pm index dfcdb02b1e..43fef8ae5e 100644 --- a/lib/overload.pm +++ b/lib/overload.pm @@ -62,7 +62,10 @@ sub OverloadedStringify { my $package = shift; $package = ref $package if ref $package; #$package->can('(""') - ov_method mycan($package, '(""'), $package; + ov_method mycan($package, '(""'), $package + or ov_method mycan($package, '(0+'), $package + or ov_method mycan($package, '(bool'), $package + or ov_method mycan($package, '(nomethod'), $package; } sub Method { @@ -108,6 +111,18 @@ sub mycan { # Real can would leave stubs. 'qr' => 0x10000, ); +%ops = ( with_assign => "+ - * / % ** << >> x .", + assign => "+= -= *= /= %= **= <<= >>= x= .=", + str_comparison => "< <= > >= == !=", + '3way_comparison'=> "<=> cmp", + num_comparison => "lt le gt ge eq ne", + binary => "& | ^", + unary => "neg ! ~", + mutators => '++ --', + func => "atan2 cos sin exp abs log sqrt", + conversion => 'bool "" 0+', + special => 'nomethod fallback ='); + sub constant { # Arguments: what, sub while (@_) { @@ -220,7 +235,8 @@ the arguments are reversed. the current operation is an assignment variant (as in C<$a+=7>), but the usual function is called instead. This additional -information can be used to generate some optimizations. +information can be used to generate some optimizations. Compare +L<Calling Conventions for Mutators>. =back @@ -230,9 +246,67 @@ Unary operation are considered binary operations with the second argument being C<undef>. Thus the functions that overloads C<{"++"}> is called with arguments C<($a,undef,'')> when $a++ is executed. +=head2 Calling Conventions for Mutators + +Two types of mutators have different calling conventions: + +=over + +=item C<++> and C<--> + +The routines which implement these operators are expected to actually +I<mutate> their arguments. So, assuming that $obj is a reference to a +number, + + sub incr { my $n = $ {$_[0]}; ++$n; $_[0] = bless \$n} + +is an appropriate implementation of overloaded C<++>. Note that + + sub incr { ++$ {$_[0]} ; shift } + +is OK if used with preincrement and with postincrement. (In the case +of postincrement a copying will be performed, see L<Copy Constructor>.) + +=item C<x=> and other assignment versions + +There is nothing special about these methods. They may change the +value of their arguments, and may leave it as is. The result is going +to be assigned to the value in the left-hand-side if different from +this value. + +This allows for the same method to be used as averloaded C<+=> and +C<+>. Note that this is I<allowed>, but not recommended, since by the +semantic of L<"Fallback"> Perl will call the method for C<+> anyway, +if C<+=> is not overloaded. + +=back + +B<Warning.> Due to the presense of assignment versions of operations, +routines which may be called in assignment context may create +self-referencial structures. Currently Perl will not free self-referential +structures until cycles are C<explicitly> broken. You may get problems +when traversing your structures too. + +Say, + + use overload '+' => sub { bless [ \$_[0], \$_[1] ] }; + +is asking for trouble, since for code C<$obj += $foo> the subroutine +is called as C<$obj = add($obj, $foo, undef)>, or C<$obj = [\$obj, +\$foo]>. If using such a subroutine is an important optimization, one +can overload C<+=> explicitly by a non-"optimized" version, or switch +to non-optimized version if C<not defined $_[2]> (see +L<Calling Conventions for Binary Operations>). + +Even if no I<explicit> assignment-variants of operators are present in +the script, they may be generated by the optimizer. Say, C<",$obj,"> or +C<',' . $obj . ','> may be both optimized to + + my $tmp = ',' . $obj; $tmp .= ','; + =head2 Overloadable Operations -The following symbols can be specified in C<use overload>: +The following symbols can be specified in C<use overload> directive: =over 5 @@ -247,6 +321,10 @@ the assignment variant is not available. Methods for operations "C<+>", increment and decrement methods. The operation "C<->" can be used to autogenerate missing methods for unary minus or C<abs>. +See L<"MAGIC AUTOGENERATION">, L<"Calling Conventions for Mutators"> and +L<"Calling Conventions for Binary Operations">) for details of these +substitutions. + =item * I<Comparison operations> "<", "<=", ">", ">=", "==", "!=", "<=>", @@ -298,7 +376,23 @@ see L<SPECIAL SYMBOLS FOR C<use overload>>. =back -See L<"Fallback"> for an explanation of when a missing method can be autogenerated. +See L<"Fallback"> for an explanation of when a missing method can be +autogenerated. + +A computer-readable form of the above table is available in the hash +%overload::ops, with values being space-separated lists of names: + + with_assign => '+ - * / % ** << >> x .', + assign => '+= -= *= /= %= **= <<= >>= x= .=', + str_comparison => '< <= > >= == !=', + '3way_comparison'=> '<=> cmp', + num_comparison => 'lt le gt ge eq ne', + binary => '& | ^', + unary => 'neg ! ~', + mutators => '++ --', + func => 'atan2 cos sin exp abs log sqrt', + conversion => 'bool "" 0+', + special => 'nomethod fallback =' =head2 Inheritance and overloading @@ -401,15 +495,15 @@ to a reference that shares its object with some other reference, such as $a=$b; - $a++; + ++$a; To make this change $a and not change $b, a copy of C<$$a> is made, and $a is assigned a reference to this new object. This operation is -done during execution of the C<$a++>, and not during the assignment, +done during execution of the C<++$a>, and not during the assignment, (so before the increment C<$$a> coincides with C<$$b>). This is only -done if C<++> is expressed via a method for C<'++'> or C<'+='>. Note -that if this operation is expressed via C<'+'> a nonmutator, i.e., as -in +done if C<++> is expressed via a method for C<'++'> or C<'+='> (or +C<nomethod>). Note that if this operation is expressed via C<'+'> +a nonmutator, i.e., as in $a=$b; $a=$a+1; @@ -443,6 +537,9 @@ C<'='> was overloaded with C<\&clone>. =back +Same behaviour is triggered by C<$b = $a++>, which is consider a synonim for +C<$b = $a; ++$a>. + =head1 MAGIC AUTOGENERATION If a method for an operation is not found, and the value for C<"fallback"> is @@ -499,7 +596,7 @@ value is a scalar and not a reference. =back -=head1 WARNING +=head1 Losing overloading The restriction for the comparison operation is that even if, for example, `C<cmp>' should return a blessed reference, the autogenerated `C<lt>' @@ -661,6 +758,416 @@ behavior by defining your own copy constructor (see L<"Copy Constructor">). It is expected that arguments to methods that are not explicitly supposed to be changed are constant (but this is not enforced). +=head1 Metaphor clash + +One may wonder why the semantic of overloaded C<=> is so counterintuive. +If it I<looks> counterintuive to you, you are subject to a metaphor +clash. + +Here is a Perl object metaphor: + +I< object is a reference to blessed data> + +and an arithmetic metaphor: + +I< object is a thing by itself>. + +The I<main> problem of overloading C<=> is the fact that these metaphors +imply different actions on the assignment C<$a = $b> if $a and $b are +objects. Perl-think implies that $a becomes a reference to whatever +$b was referencing. Arithmetic-think implies that the value of "object" +$a is changed to become the value of the object $b, preserving the fact +that $a and $b are separate entities. + +The difference is not relevant in the absence of mutators. After +a Perl-way assignment an operation which mutates the data referenced by $a +would change the data referenced by $b too. Effectively, after +C<$a = $b> values of $a and $b become I<indistinguishable>. + +On the other hand, anyone who has used algebraic notation knows the +expressive power of the arithmetic metaphor. Overloading works hard +to enable this metaphor while preserving the Perlian way as far as +possible. Since it is not not possible to freely mix two contradicting +metaphors, overloading allows the arithmetic way to write things I<as +far as all the mutators are called via overloaded access only>. The +way it is done is described in L<Copy Constructor>. + +If some mutator methods are directly applied to the overloaded values, +one may need to I<explicitly unlink> other values which references the +same value: + + $a = new Data 23; + ... + $b = $a; # $b is "linked" to $a + ... + $a = $a->clone; # Unlink $b from $a + $a->increment_by(4); + +Note that overloaded access makes this transparent: + + $a = new Data 23; + $b = $a; # $b is "linked" to $a + $a += 4; # would unlink $b automagically + +However, it would not make + + $a = new Data 23; + $a = 4; # Now $a is a plain 4, not 'Data' + +preserve "objectness" of $a. But Perl I<has> a way to make assignments +to an object do whatever you want. It is just not the overload, but +tie()ing interface (see L<perlfunc/tie>). Adding a FETCH() method +which returns the object itself, and STORE() method which changes the +value of the object, one can reproduce the arithmetic metaphor in its +completeness, at least for variables which were tie()d from the start. + +(Note that a workaround for a bug may be needed, see L<"BUGS">.) + +=head1 Cookbook + +Please add examples to what follows! + +=head2 Two-face scalars + +Put this in F<two_face.pm> in your Perl library directory: + + package two_face; # Scalars with separate string and + # numeric values. + sub new { my $p = shift; bless [@_], $p } + use overload '""' => \&str, '0+' => \&num, fallback => 1; + sub num {shift->[1]} + sub str {shift->[0]} + +Use it as follows: + + require two_face; + my $seven = new two_face ("vii", 7); + printf "seven=$seven, seven=%d, eight=%d\n", $seven, $seven+1; + print "seven contains `i'\n" if $seven =~ /i/; + +(The second line creates a scalar which has both a string value, and a +numeric value.) This prints: + + seven=vii, seven=7, eight=8 + seven contains `i' + +=head2 Symbolic calculator + +Put this in F<symbolic.pm> in your Perl library directory: + + package symbolic; # Primitive symbolic calculator + use overload nomethod => \&wrap; + + sub new { shift; bless ['n', @_] } + sub wrap { + my ($obj, $other, $inv, $meth) = @_; + ($obj, $other) = ($other, $obj) if $inv; + bless [$meth, $obj, $other]; + } + +This module is very unusual as overloaded modules go: it does not +provide any usual overloaded operators, instead it provides the L<Last +Resort> operator C<nomethod>. In this example the corresponding +subroutine returns an object which encupsulates operations done over +the objects: C<new symbolic 3> contains C<['n', 3]>, C<2 + new +symbolic 3> contains C<['+', 2, ['n', 3]]>. + +Here is an example of the script which "calculates" the side of +circumscribed octagon using the above package: + + require symbolic; + my $iter = 1; # 2**($iter+2) = 8 + my $side = new symbolic 1; + my $cnt = $iter; + + while ($cnt--) { + $side = (sqrt(1 + $side**2) - 1)/$side; + } + print "OK\n"; + +The value of $side is + + ['/', ['-', ['sqrt', ['+', 1, ['**', ['n', 1], 2]], + undef], 1], ['n', 1]] + +Note that while we obtained this value using a nice little script, +there is no simple way to I<use> this value. In fact this value may +be inspected in debugger (see L<perldebug>), but ony if +C<bareStringify> B<O>ption is set, and not via C<p> command. + +If one attempts to print this value, then the overloaded operator +C<""> will be called, which will call C<nomethod> operator. The +result of this operator will be stringified again, but this result is +again of type C<symbolic>, which will lead to an infinite loop. + +Add a pretty-printer method to the module F<symbolic.pm>: + + sub pretty { + my ($meth, $a, $b) = @{+shift}; + $a = 'u' unless defined $a; + $b = 'u' unless defined $b; + $a = $a->pretty if ref $a; + $b = $b->pretty if ref $b; + "[$meth $a $b]"; + } + +Now one can finish the script by + + print "side = ", $side->pretty, "\n"; + +The method C<pretty> is doing object-to-string conversion, so it +is natural to overload the operator C<""> using this method. However, +inside such a method it is not necessary to pretty-print the +I<components> $a and $b of an object. In the above subroutine +C<"[$meth $a $b]"> is a catenation of some strings and components $a +and $b. If these components use overloading, the catenation operator +will look for an overloaded operator C<.>, if not present, it will +look for an overloaded operator C<"">. Thus it is enough to use + + use overload nomethod => \&wrap, '""' => \&str; + sub str { + my ($meth, $a, $b) = @{+shift}; + $a = 'u' unless defined $a; + $b = 'u' unless defined $b; + "[$meth $a $b]"; + } + +Now one can change the last line of the script to + + print "side = $side\n"; + +which outputs + + side = [/ [- [sqrt [+ 1 [** [n 1 u] 2]] u] 1] [n 1 u]] + +and one can inspect the value in debugger using all the possible +methods. + +Something is is still amiss: consider the loop variable $cnt of the +script. It was a number, not an object. We cannot make this value of +type C<symbolic>, since then the loop will not terminate. + +Indeed, to terminate the cycle, the $cnt should become false. +However, the operator C<bool> for checking falsity is overloaded (this +time via overloaded C<"">), and returns a long string, thus any object +of type C<symbolic> is true. To overcome this, we need a way to +compare an object to 0. In fact, it is easier to write a numeric +conversion routine. + +Here is the text of F<symbolic.pm> with such a routine added (and +slightly modifed str()): + + package symbolic; # Primitive symbolic calculator + use overload + nomethod => \&wrap, '""' => \&str, '0+' => \# + + sub new { shift; bless ['n', @_] } + sub wrap { + my ($obj, $other, $inv, $meth) = @_; + ($obj, $other) = ($other, $obj) if $inv; + bless [$meth, $obj, $other]; + } + sub str { + my ($meth, $a, $b) = @{+shift}; + $a = 'u' unless defined $a; + if (defined $b) { + "[$meth $a $b]"; + } else { + "[$meth $a]"; + } + } + my %subr = ( n => sub {$_[0]}, + sqrt => sub {sqrt $_[0]}, + '-' => sub {shift() - shift()}, + '+' => sub {shift() + shift()}, + '/' => sub {shift() / shift()}, + '*' => sub {shift() * shift()}, + '**' => sub {shift() ** shift()}, + ); + sub num { + my ($meth, $a, $b) = @{+shift}; + my $subr = $subr{$meth} + or die "Do not know how to ($meth) in symbolic"; + $a = $a->num if ref $a eq __PACKAGE__; + $b = $b->num if ref $b eq __PACKAGE__; + $subr->($a,$b); + } + +All the work of numeric conversion is done in %subr and num(). Of +course, %subr is not complete, it contains only operators used in teh +example below. Here is the extra-credit question: why do we need an +explicit recursion in num()? (Answer is at the end of this section.) + +Use this module like this: + + require symbolic; + my $iter = new symbolic 2; # 16-gon + my $side = new symbolic 1; + my $cnt = $iter; + + while ($cnt) { + $cnt = $cnt - 1; # Mutator `--' not implemented + $side = (sqrt(1 + $side**2) - 1)/$side; + } + printf "%s=%f\n", $side, $side; + printf "pi=%f\n", $side*(2**($iter+2)); + +It prints (without so many line breaks) + + [/ [- [sqrt [+ 1 [** [/ [- [sqrt [+ 1 [** [n 1] 2]]] 1] + [n 1]] 2]]] 1] + [/ [- [sqrt [+ 1 [** [n 1] 2]]] 1] [n 1]]]=0.198912 + pi=3.182598 + +The above module is very primitive. It does not implement +mutator methods (C<++>, C<-=> and so on), does not do deep copying +(not required without mutators!), and implements only those arithmetic +operations which are used in the example. + +To implement most arithmetic operattions is easy, one should just use +the tables of operations, and change the code which fills %subr to + + my %subr = ( 'n' => sub {$_[0]} ); + foreach my $op (split " ", $overload::ops{with_assign}) { + $subr{$op} = $subr{"$op="} = eval "sub {shift() $op shift()}"; + } + my @bins = qw(binary 3way_comparison num_comparison str_comparison); + foreach my $op (split " ", "@overload::ops{ @bins }") { + $subr{$op} = eval "sub {shift() $op shift()}"; + } + foreach my $op (split " ", "@overload::ops{qw(unary func)}") { + print "defining `$op'\n"; + $subr{$op} = eval "sub {$op shift()}"; + } + +Due to L<Calling Conventions for Mutators>, we do not need anything +special to make C<+=> and friends work, except filling C<+=> entry of +%subr, and defining a copy constructor (needed since Perl has no +way to know that the implementation of C<'+='> does not mutate +the argument, compare L<Copy Constructor>). + +To implement a copy constructor, add C<'=' => \&cpy> to C<use overload> +line, and code (this code assumes that mutators change things one level +deep only, so recursive copying is not needed): + + sub cpy { + my $self = shift; + bless [@$self], ref $self; + } + +To make C<++> and C<--> work, we need to implement actual mutators, +either directly, or in C<nomethod>. We continue to do things inside +C<nomethod>, thus add + + if ($meth eq '++' or $meth eq '--') { + @$obj = ($meth, (bless [@$obj]), 1); # Avoid circular reference + return $obj; + } + +after the first line of wrap(). This is not a most effective +implementation, one may consider + + sub inc { $_[0] = bless ['++', shift, 1]; } + +instead. + +As a final remark, note that one can fill %subr by + + my %subr = ( 'n' => sub {$_[0]} ); + foreach my $op (split " ", $overload::ops{with_assign}) { + $subr{$op} = $subr{"$op="} = eval "sub {shift() $op shift()}"; + } + my @bins = qw(binary 3way_comparison num_comparison str_comparison); + foreach my $op (split " ", "@overload::ops{ @bins }") { + $subr{$op} = eval "sub {shift() $op shift()}"; + } + foreach my $op (split " ", "@overload::ops{qw(unary func)}") { + $subr{$op} = eval "sub {$op shift()}"; + } + $subr{'++'} = $subr{'+'}; + $subr{'--'} = $subr{'-'}; + +This finishes implementation of a primitive symbolic calculator in +50 lines of Perl code. Since the numeric values of subexpressions +are not cached, the calculator is very slow. + +Here is the answer for the exercise: In the case of str(), we need no +explicit recursion since the overloaded C<.>-operator will fall back +to an existing overloaded operator C<"">. Overloaded arithmetic +operators I<do not> fall back to numeric conversion if C<fallback> is +not explicitly requested. Thus without an explicit recursion num() +would convert C<['+', $a, $b]> to C<$a + $b>, which would just rebuild +the argument of num(). + +If you wonder why defaults for conversion are different for str() and +num(), note how easy it was to write the symbolic calculator. This +simplicity is due to an appropriate choice of defaults. One extra +note: due to teh explicit recursion num() is more fragile than sym(): +we need to explicitly check for the type of $a and $b. If componets +$a and $b happen to be of some related type, this may lead to problems. + +=head2 I<Really> symbolic calculator + +One may wonder why we call the above calculator symbolic. The reason +is that the actual calculation of the value of expression is postponed +until the value is I<used>. + +To see it in action, add a method + + sub STORE { + my $obj = shift; + $#$obj = 1; + @$obj->[0,1] = ('=', shift); + } + +to the package C<symbolic>. After this change one can do + + my $a = new symbolic 3; + my $b = new symbolic 4; + my $c = sqrt($a**2 + $b**2); + +and the numeric value of $c becomes 5. However, after calling + + $a->STORE(12); $b->STORE(5); + +the numeric value of $c becomes 13. There is no doubt now that the module +symbolic provides a I<symbolic> calculator indeed. + +To hide the rough edges under the hood, provide a tie()d interface to the +package C<symbolic> (compare with L<Metaphor clash>). Add methods + + sub TIESCALAR { my $pack = shift; $pack->new(@_) } + sub FETCH { shift } + sub nop { } # Around a bug + +(the bug is described in L<"BUGS">). One can use this new interface as + + tie $a, 'symbolic', 3; + tie $b, 'symbolic', 4; + $a->nop; $b->nop; # Around a bug + + my $c = sqrt($a**2 + $b**2); + +Now numeric value of $c is 5. After C<$a = 12; $b = 5> the numeric value +of $c becomes 13. To insulate the user of the module add a method + + sub vars { my $p = shift; tie($_, $p), $_->nop foreach @_; } + +Now + + my ($a, $b); + symbolic->vars($a, $b); + my $c = sqrt($a**2 + $b**2); + + $a = 3; $b = 4; + printf "c5 %s=%f\n", $c, $c; + + $a = 12; $b = 5; + printf "c13 %s=%f\n", $c, $c; + +shows that the numeric value of $c follows changes to the values of $a +and $b. + =head1 AUTHOR Ilya Zakharevich E<lt>F<ilya@math.mps.ohio-state.edu>E<gt>. @@ -676,7 +1183,7 @@ this overloading). Say, if C<eq> is overloaded, then the method C<(eq> is shown by debugger. The method C<()> corresponds to the C<fallback> key (in fact a presence of this method shows that this package has overloading enabled, and it is what is used by the C<Overloaded> -function). +function of module C<overload>). =head1 BUGS @@ -689,9 +1196,21 @@ C<fallback> is present (possibly undefined). This may create interesting effects if some package is not overloaded, but inherits from two overloaded packages. +Relation between overloading and tie()ing is broken. Overloading is +triggered or not basing on the I<previous> class of tie()d value. + +This happens because the presence of overloading is checked too early, +before any tie()d access is attempted. If the FETCH()ed class of the +tie()d value does not change, a simple workaround is to access the value +immediately after tie()ing, so that after this call the I<previous> class +coincides with the current one. + +B<Needed:> a way to fix this without a speed penalty. + Barewords are not covered by overloaded string constants. -This document is confusing. +This document is confusing. There are grammos and misleading language +used in places. It would seem a total rewrite is needed. =cut diff --git a/lib/perl5db.pl b/lib/perl5db.pl index 67a6a6d839..099a49b49f 100644 --- a/lib/perl5db.pl +++ b/lib/perl5db.pl @@ -2,7 +2,7 @@ package DB; # Debugger for Perl 5.00x; perl5db.pl patch level: -$VERSION = 1.03; +$VERSION = 1.0401; $header = "perl5db.pl version $VERSION"; # Enhanced by ilya@math.ohio-state.edu (Ilya Zakharevich) @@ -179,7 +179,7 @@ $inhibit_exit = $option{PrintRet} = 1; TTY noTTY ReadLine NonStop LineInfo maxTraceLen recallCommand ShellBang pager tkRunning ornaments signalLevel warnLevel dieLevel inhibit_exit - ImmediateStop); + ImmediateStop bareStringify); %optionVars = ( hashDepth => \$dumpvar::hashDepth, @@ -191,6 +191,7 @@ $inhibit_exit = $option{PrintRet} = 1; undefPrint => \$dumpvar::printUndef, globPrint => \$dumpvar::globPrint, UsageOnly => \$dumpvar::usageOnly, + bareStringify => \$dumpvar::bareStringify, frame => \$frame, AutoTrace => \$trace, inhibit_exit => \$inhibit_exit, @@ -390,6 +391,7 @@ sub DB { if ($trace & 2) { for (my $n = 0; $n <= $#to_watch; $n++) { $evalarg = $to_watch[$n]; + local $onetimeDump; # Do not output results my ($val) = &eval; # Fix context (&eval is doing array)? $val = ( (defined $val) ? "'$val'" : 'undef' ); if ($val ne $old_watch[$n]) { @@ -1823,6 +1825,7 @@ B<O> [I<opt>[B<=>I<val>]] [I<opt>B<\">I<val>B<\">] [I<opt>B<?>]... I<DumpPackages>: dump symbol tables of packages; I<DumpReused>: dump contents of \"reused\" addresses; I<quote>, I<HighBit>, I<undefPrint>: change style of string dump; + I<bareStringify>: Do not print the overload-stringified value; Option I<PrintRet> affects printing of return value after B<r> command, I<frame> affects printing messages on entry and exit from subroutines. I<AutoTrace> affects printing messages on every possible breaking point. |