I was writing a replacement subroutine for CORE::GLOBAL::stat when I
noticed that there is no way to know if its argument is a file name or a
file handler. For instance:
use Devel::Peek;
CORE::GLOBAL::stat = sub ($) { Dump($_[0]) }
# these two calls dump similar SVs:
stat(Foo);
stat('Foo');
A simple way to remove that limitation is to use a new sub prototype to
handle that kind of argument.
The patch attached adds support for prototype '/'. It is similar to '$'
but automatically converts barewords to globs. For instance:
sub my_stat (/) { ... }
my_stat "foo"; # is equivalent to &my_stat("foo");
my_stat foo; # is equivalent to &my_stat(*foo);
I like '/' as the name of the prototype because it's easy to relate to
filenames (at least if you are a unix user!).
What do you thing? any possibility this could go into blead?
Cheers,
- Salva.
--- Rafael Garcia-Suarez <rgarci...@gmail.com> wrote:
> On 3/8/06, Salvador Fandino <sfan...@yahoo.com> wrote:
> > I was writing a replacement subroutine for CORE::GLOBAL::stat
> when I
> > noticed that there is no way to know if its argument is a file
> name or a
> > file handler. For instance:
> >
> > use Devel::Peek;
> >
> > CORE::GLOBAL::stat = sub ($) { Dump($_[0]) }
>
> What's the problem with (*) ? which is the prototype returned for
> this
> built-in, see :
> $ perl -le 'print prototype "CORE::stat"'
> *
with '*', it happens as with '$', barewords are interpreted as
barewords and there is no way to know if the caller wants to stat an
open file or a named file.
For instance,
CORE::GLOBAL::stat = sub (*) { CORE::stat($_[0]) };
open Foo, "myfile";
stat(Foo);
tries to stat a file named 'Foo' instead of 'myfile'.
Cheers,
- Salva.
__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com
I think I'd rather fix (*) than introducing a new prototype character.
After all, overriden built-ins are supposed to behave like the real
built-ins.
--
A system is nothing more than the subordination of all aspects of
the universe to any one such aspect.
-- Borges
the problem I see with fixing '*' is that it would introduce backward
incompatibilities and break code expecting a glob name instead of a real
glob. Greping over my minicpan I have found that this is the most common
case. I have also found that it is very usual to employ '*' on
subroutines expecting a bareword ('require' replacements, for instance).
IMO, the best solution, would be to start calling '*' the bareword
prototype, and introduce '/' for file handles. The builtin 'prototype'
should be changed accordingly, so that it uses '/' for builtins
expecting a file handle.
Cheers,
- Salvador.
Any examples ?
> I have also found that it is very usual to employ '*' on
> subroutines expecting a bareword ('require' replacements, for instance).
You don't need a prototype if you override require(), the prototype will
get ignored anyway since it's parsed specially.
(Moreover strictures are supposed to guard you against mixing barewords
and globs/handles.)
> IMO, the best solution, would be to start calling '*' the bareword
> prototype, and introduce '/' for file handles. The builtin 'prototype'
> should be changed accordingly, so that it uses '/' for builtins
> expecting a file handle.
I'm not convinced yet.
--
* What system had proved more effective?
* Indirect suggestion implicating selfinterest.
-- Ulysses
after closer examination, I have found that most modules requiring a
file handle are still going to work when passed a GV because they do
something like that:
sub foo (*) {
my $ref = shift;
my $glob;
if (ref $ref) {
$glob = $ref;
else {
unless ($ref =~ /::/) { # a stringified GV matches /::/,
$ref = caller().'::'.$ref; # it is not modified here.
}
no strict 'refs';
$glob = *{$ref}; # if $ref is a GV this line
# is equivalent to $glob = $ref
}
whatever $glob;
...
}
but the problem with subroutines using '*' for syntactic sugar to get a
bareword still remains, and there are lots of modules doing it as PAR
for instance:
sub opt(*) {
my $opt = shift;
return exists($Options->{$opt}) && ($Options->{$opt} || 0);
}
Other modules doing the same are...
CPANPLUS-0.0499/lib/CPANPLUS/Tools/Load.pm
race-1.070/lib/Log/Trace.pm
Module-Find-0.05/Find.pm
Net_SSLeay.pm-1.30/SSLeay.pm
CPANPLUS-0.0562/lib/CPANPLUS/inc/Module/Load.pm
mod_perl-2.0.2/lib/Apache2/porting.pm
Module-Loaded-0.01/lib/Module/Loaded.pm
etc.
>>I have also found that it is very usual to employ '*' on
>>subroutines expecting a bareword ('require' replacements, for instance).
>
>
> You don't need a prototype if you override require(), the prototype will
> get ignored anyway since it's parsed specially.
>
> (Moreover strictures are supposed to guard you against mixing barewords
> and globs/handles.)
>>IMO, the best solution, would be to start calling '*' the bareword
>>prototype, and introduce '/' for file handles. The builtin 'prototype'
>>should be changed accordingly, so that it uses '/' for builtins
>>expecting a file handle.
>
>
> I'm not convinced yet.
>
well, it is an ugly solution but I can not see any other alternative
that does not break backwards compatibility.
And after all, what's the problem with introducing a new prototype? it's
not likely to cause incompatibilities and there are plenty of symbols
available for future use (!?-+="'`:,{}^~|.#).
Cheers,
- Salvador.
I thought there was already a prototype for that - '*' or is that _too_
strong?
>For instance:
>
> sub my_stat (/) { ... }
>
> my_stat "foo"; # is equivalent to &my_stat("foo");
> my_stat foo; # is equivalent to &my_stat(*foo);
>
>I like '/' as the name of the prototype because it's easy to relate to
>filenames (at least if you are a unix user!).
>
>What do you thing? any possibility this could go into blead?
>
>Cheers,
>
> - Salva.
>diff -ru orig-perl-current/op.c my-perl-current/op.c
>--- orig-perl-current/op.c 2006-03-07 18:11:24.000000000 +0100
>+++ my-perl-current/op.c 2006-03-08 00:12:29.000000000 +0100
>@@ -6621,6 +6621,21 @@
> arg == 1 ? "block or sub {}" : "sub {}",
> gv_ename(namegv), o2);
> break;
>+ case '/':
>+ proto++;
>+ arg++;
>+ if (o2->op_type == OP_CONST && (o2->op_private & OPpCONST_BARE)) {
>+ GV * gv = gv_fetchsv(cSVOPx(o2)->op_sv, GV_ADD, 0);
>+ OP * kid = newGVOP(OP_GV, 0, gv);
>+ OP * const sib = o2->op_sibling;
>+ o2->op_sibling = 0;
>+ op_free(o2);
>+ o2 = newUNOP(OP_RV2GV, OPf_REF, scalar(kid));
>+ o2->op_sibling = sib;
>+ prev->op_sibling = o2;
>+ }
>+ scalar(o2);
>+ break;
> case '*':
> /* '*' allows any scalar type, including bareword */
> proto++;
>diff -ru orig-perl-current/toke.c my-perl-current/toke.c
>--- orig-perl-current/toke.c 2006-02-27 12:06:56.000000000 +0100
>+++ my-perl-current/toke.c 2006-03-08 00:11:56.000000000 +0100
>@@ -2146,7 +2146,7 @@
> if (proto) {
> if (*proto == ';')
> proto++;
>- if (*proto == '*')
>+ if (*proto == '*' || *proto == '/')
> return 0;
> }
> }
>@@ -5506,7 +5506,7 @@
> for (p = d; *p; ++p) {
> if (!isSPACE(*p)) {
> d[tmp++] = *p;
>- if (!strchr("$@%*;[]&\\", *p))
>+ if (!strchr("$@%*;[]&\\/", *p))
> bad_proto = TRUE;
> }
> }
But perl's stat() does what '*' proto does.
So having the overriden stat() do something else would be confusing.
But REAL stat gets passed the string doesn't it.
So the sub could do what stat does - test if the glob exists.
>
>Cheers,
>
> - Salvador.
> Salvador Fandino <sfan...@yahoo.com> writes:
>>the problem I see with fixing '*' is that it would introduce backward
>>incompatibilities and break code expecting a glob name instead of a real
>>glob. Greping over my minicpan I have found that this is the most common
>>case. I have also found that it is very usual to employ '*' on
>>subroutines expecting a bareword ('require' replacements, for instance).
>>
>>IMO, the best solution, would be to start calling '*' the bareword
>>prototype, and introduce '/' for file handles. The builtin 'prototype'
>>should be changed accordingly, so that it uses '/' for builtins
>>expecting a file handle.
>
>
> But REAL stat gets passed the string doesn't it.
no, in Perl_ck_ftst, this code
if (kid->op_type == OP_CONST &&
(kid->op_private & OPpCONST_BARE)) {
OP * const newop = newGVOP(type, OPf_REF,
gv_fetchsv(kid->op_sv, GV_ADD, SVt_PVIO));
op_free(o);
o = newop;
return o;
}
checks if the argument is a bareword and if so, converts it to a glob.
Same is done for file test operators in Perl_ck_ftst. And I believe that
at some point inside Perl_ck_fun, the same change is performed for other
ops like chdir or truncate.
Whit out that conversion, it is impossible to know if the caller wants
to perform an operation over the file named "foo" or over the file
handle *foo.
> So the sub could do what stat does - test if the glob exists.
Checking for the existence of the glob is a very bad heuristic. It
doesn't really mean anything because file names and glob names can
collide easily.
Cheers,
- Salva.
I thought so too, and didn't know about the op hackery.
None the less the _intent_ of the '*' prototype was to make the perl sub
behave like the builtin. So if '*' proto caused the op hackery to be done
so that
stat(Foo)
Passed a glob and stat('Foo') passed a string to the sub it would be
like real stat().
> None the less the _intent_ of the '*' prototype was to make the perl sub
> behave like the builtin. So if '*' proto caused the op hackery to be done
> so that
>
> stat(Foo)
>
> Passed a glob and stat('Foo') passed a string to the sub it would be
> like real stat().
yes, but the problem is that changing '*' now will break lots of code
using it to accept a bareword under "use strict". Code as this:
use strict;
sub option (*);
print "option t:" , option(t) , "\n";
can be found in several CPAN modules.
I see three different ways to handle that limitation:
- left it as is, some builtins can not be properly overridden but it has
been broken for a long time and nobody else has complained so, it
shouldn't be something important!
- change '*' to work as built-ins, and break old code doing funny things
- use a new prototype that works as '*' should.
IMO, the last one is the way to go. It is not going to break anything
and would let do interesting things, like implementing a VFS layer.
Cheers,
- Salva
I agree those are the options and maybe even that third looks
best. I don't like '/' as choice of character.
>
>IMO, the last one is the way to go. It is not going to break anything
>and would let do interesting things,
>like implementing a VFS layer.
I/we already did that ;-)
>
>Cheers,
>
> - Salva
> Salvador Fandino <sfan...@yahoo.com> writes:
>> I see three different ways to handle that limitation:
>>
>> - left it as is, some builtins can not be properly overridden but it has
>> been broken for a long time and nobody else has complained so, it
>> shouldn't be something important!
>>
>> - change '*' to work as built-ins, and break old code doing funny things
>>
>> - use a new prototype that works as '*' should.
>
> I agree those are the options and maybe even that third looks
> best. I don't like '/' as choice of character.
Which one would be your choice?
Symbols still available are: !?-+="'`:,{}^~|.#
>> IMO, the last one is the way to go. It is not going to break anything
>> and would let do interesting things,
>
>
>> like implementing a VFS layer.
>
> I/we already did that ;-)
PerlIO is not a full VFS layer, it is missing all the directory handling
functionality, or are you talking about something else?
Cheers,
- Salva
Not sure, but as it relates to barewords as strings then ' would seem
>
>>> IMO, the last one is the way to go. It is not going to break anything
>>> and would let do interesting things,
>>
>>
>>> like implementing a VFS layer.
>>
>> I/we already did that ;-)
>
>PerlIO is not a full VFS layer,
True - but then neither is tie nor is
override-every-file-touching-operator-to-a-sub
> Nick Ing-Simmons wrote:
>
> > Salvador Fandino <sfan...@yahoo.com> writes:
> >> I see three different ways to handle that limitation:
> >>
> >> - left it as is, some builtins can not be properly overridden but it has
> >> been broken for a long time and nobody else has complained so, it
> >> shouldn't be something important!
> >>
> >> - change '*' to work as built-ins, and break old code doing funny things
> >>
> >> - use a new prototype that works as '*' should.
> >
> > I agree those are the options and maybe even that third looks
> > best. I don't like '/' as choice of character.
>
> Which one would be your choice?
>
> Symbols still available are: !?-+="'`:,{}^~|.#
note that ", ', `, and # will most likely cause much trouble to syntax
highlighters as text editors. Not that this should control our decision
process, but it might be something to consider
> >> IMO, the last one is the way to go. It is not going to break anything
> >> and would let do interesting things,
> >
> >
> >> like implementing a VFS layer.
> >
> > I/we already did that ;-)
>
> PerlIO is not a full VFS layer, it is missing all the directory handling
> functionality, or are you talking about something else?
--
H.Merijn Brand Amsterdam Perl Mongers (http://amsterdam.pm.org/)
using & porting perl 5.6.2, 5.8.x, 5.9.x on HP-UX 10.20, 11.00, 11.11,
& 11.23, SuSE 10.0, AIX 4.3 & 5.2, and Cygwin. http://qa.perl.org
http://mirrors.develooper.com/hpux/ http://www.test-smoke.org
http://www.goldmark.org/jeff/stupid-disclaimers/
And Humans now you mention it.
Unpaired {} are probably dubious as well.
So that leaves us with !?-+=:,^~|.
I am torn between '+' (looks most like *) and '?' as what happens
is conditional.
>> Symbols still available are: !?-+="'`:,{}^~|.#
>
> Not sure, but as it relates to barewords as strings then ' would seem
emm... no, the prototype converting barewords to strings is the current
'*' that we want to maintain for backward compatibility. The new
operator, should convert barewords to file handlers.
Cheers,
- Salva