Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[PATCH] overridable filetest operators

7 views
Skip to first unread message

Salvador Fandino

unread,
Mar 9, 2006, 11:31:18 AM3/9/06
to perl5-...@perl.org
Hi,

The attached patch makes the filetest operators (-f, -d, etc.)
overridable by subroutines of the same name on the local package or in
CORE::GLOBAL. Overriden built-ins can be called as CORE::-X.

Some sample code:

BEGIN {
*{"Foo::-f"} = sub { print "overrided -f\n"};
*{"CORE::GLOBAL::-d"} = sub { print "overrided -d\n" };
}

package Foo;

-f "/etc/passwd"; # prints "overriden -f"
CORE::-f "/etc/passwd"; # calls built-in
-d "/etc/passwd"; # prints "overriden -d"

package Bar;

-f "/etc/passwd"; # calls built-in
-d "/etc/passwd"; # prints "overriden -d"
CORE::-d "/etc/passwd" # calls built-in


Cheers,

- Salva

perl-override-filetest-1.patch

Nick Ing-Simmons

unread,
Mar 9, 2006, 1:57:14 PM3/9/06
to sfan...@yahoo.com, perl5-...@perl.org
Salvador Fandino <sfan...@yahoo.com> writes:
>Hi,
>
>The attached patch makes the filetest operators (-f, -d, etc.)
>overridable by subroutines of the same name on the local package or in
>CORE::GLOBAL. Overriden built-ins can be called as CORE::-X.

Hmm, overide stat()-derived things as well as recent thread about stat()
override proto. Care to tell us what you are trying to do?
If we understood that we might be more sympathetic to the patches.

stat() is one of the entries "missing" from the PerlIO abstraction.
Getting perl to call stat() via IO abstraction might be cleaner than
all these core overrides.


>
>Some sample code:
>
> BEGIN {
> *{"Foo::-f"} = sub { print "overrided -f\n"};
> *{"CORE::GLOBAL::-d"} = sub { print "overrided -d\n" };
> }
>
> package Foo;
>
> -f "/etc/passwd"; # prints "overriden -f"
> CORE::-f "/etc/passwd"; # calls built-in
> -d "/etc/passwd"; # prints "overriden -d"
>
> package Bar;
>
> -f "/etc/passwd"; # calls built-in
> -d "/etc/passwd"; # prints "overriden -d"
> CORE::-d "/etc/passwd" # calls built-in
>
>
>Cheers,
>
> - Salva

>--- ../orig-perl-current/toke.c 2006-02-27 12:06:56.000000000 +0100
>+++ toke.c 2006-03-09 15:29:49.000000000 +0100
>@@ -2216,6 +2216,40 @@
> return "";
> }
>
>+/*
>+ * S_subroutine_call
>+ * Creates the OPs for calling a subroutine and returns the right
>+ * token type depending on its prototype and the context.
>+ */
>+
>+STATIC int
>+S_subroutine_call (pTHX_ GV *gv, CV *cv, char *s) {
>+ yylval.opval = newCVREF(0, newGVOP(OP_GV, 0, gv));
>+
>+ yylval.opval->op_private |= OPpENTERSUB_NOPAREN;
>+ PL_last_lop = PL_oldbufptr;
>+ PL_last_lop_op = OP_ENTERSUB;
>+ /* Is there a prototype? */
>+ if (SvPOK(cv)) {
>+ STRLEN protolen;
>+ const char *proto = SvPV_const((SV*)cv, protolen);
>+ if (!protolen)
>+ TERM(FUNC0SUB);
>+ if (*proto == '$' && proto[1] == '\0')
>+ OPERATOR(UNIOPSUB);
>+ while (*proto == ';')
>+ proto++;
>+ if (*proto == '&' && *s == '{') {
>+ sv_setpv(PL_subname, PL_curstash ?
>+ "__ANON__" : "__ANON__::__ANON__");
>+ PREBLOCK(LSTOPSUB);
>+ }
>+ }
>+ PL_nextval[PL_nexttoke].opval = yylval.opval;
>+ PL_expect = XTERM;
>+ force_next(WORD);
>+ TOKEN(NOAMP);
>+}
>
> /* Encoded script support. filter_add() effectively inserts a
> * 'pre-processing' function into the current source input stream.
>@@ -3083,9 +3117,19 @@
> goto retry;
> case '-':
> if (s[1] && isALPHA(s[1]) && !isALNUM(s[2])) {
>+ bool force_core = 0;
> I32 ftst = 0;
> char tmp;
>
>+ if (0) {
>+ core_filetest:
>+ /* flow jumps here when a filetest operator appears
>+ * after "CORE::". The "if(0)" construction allows us
>+ * to set the force_core flag
>+ */
>+ force_core = 1;
>+ }
>+
> s++;
> PL_bufptr = s;
> tmp = *s++;
>@@ -3093,7 +3137,7 @@
> while (s < PL_bufend && SPACE_OR_TAB(*s))
> s++;
>
>- if (strnEQ(s,"=>",2)) {
>+ if (!force_core && strnEQ(s,"=>",2)) {
> s = force_word(PL_bufptr,WORD,FALSE,FALSE,FALSE);
> DEBUG_T( { S_printbuf(aTHX_
> "### Saw unary minus before =>, forcing word %s\n", s);
>@@ -3139,6 +3183,21 @@
> break;
> }
> if (ftst) {
>+ if (!force_core) {
>+ /* has been overrided? */
>+ GV **gvp;
>+ GV *gv;
>+ CV *cv;
>+ char *ftstname = PL_bufptr - 1;
>+ if (((gvp = (GV**)hv_fetch(PL_curstash, ftstname, 2, FALSE)) &&
>+ (SvTYPE(gv = *gvp) == SVt_PVGV) &&
>+ (cv = GvCVu(gv))) ||
>+ ((gvp = (GV**)hv_fetch(PL_globalstash, ftstname, 2, FALSE)) &&
>+ (SvTYPE(gv = *gvp) == SVt_PVGV) &&
>+ (cv = GvCVu(gv)))) {
>+ return S_subroutine_call(aTHX_ gv, cv, s);
>+ }
>+ }
> PL_last_lop_op = (OPCODE)ftst;
> DEBUG_T( { PerlIO_printf(Perl_debug_log,
> "### Saw file test %c\n", (int)tmp);
>@@ -4458,30 +4517,7 @@
> }
>
> op_free(yylval.opval);
>- yylval.opval = newCVREF(0, newGVOP(OP_GV, 0, gv));
>- yylval.opval->op_private |= OPpENTERSUB_NOPAREN;
>- PL_last_lop = PL_oldbufptr;
>- PL_last_lop_op = OP_ENTERSUB;
>- /* Is there a prototype? */
>- if (SvPOK(cv)) {
>- STRLEN protolen;
>- const char *proto = SvPV_const((SV*)cv, protolen);
>- if (!protolen)
>- TERM(FUNC0SUB);
>- if (*proto == '$' && proto[1] == '\0')
>- OPERATOR(UNIOPSUB);
>- while (*proto == ';')
>- proto++;
>- if (*proto == '&' && *s == '{') {
>- sv_setpv(PL_subname, PL_curstash ?
>- "__ANON__" : "__ANON__::__ANON__");
>- PREBLOCK(LSTOPSUB);
>- }
>- }
>- PL_nextval[PL_nexttoke].opval = yylval.opval;
>- PL_expect = XTERM;
>- force_next(WORD);
>- TOKEN(NOAMP);
>+ return S_subroutine_call(aTHX_ gv, cv, s);
> }
>
> /* Call it a bare word */
>@@ -4631,6 +4667,10 @@
> if (*s == ':' && s[1] == ':') {
> s += 2;
> d = s;
>+ if (*s == '-' && strchr("rwxoRWXOezsfdlpSugkbctTBMAC", s[1]) && !isALNUM(s[2])) {
>+ /* filetest CORE::-X */
>+ goto core_filetest;
>+ }
> s = scan_word(s, PL_tokenbuf, sizeof PL_tokenbuf, FALSE, &len);
> if (!(tmp = keyword(PL_tokenbuf, len)))
> Perl_croak(aTHX_ "CORE::%s is not a keyword", PL_tokenbuf);
>--- ../orig-perl-current/pp.c 2006-02-27 12:06:55.000000000 +0100
>+++ pp.c 2006-03-09 16:47:17.000000000 +0100
>@@ -386,7 +386,15 @@
> if (SvPOK(TOPs) && SvCUR(TOPs) >= 7) {
> const char * const s = SvPVX_const(TOPs);
> if (strnEQ(s, "CORE::", 6)) {
>- const int code = keyword(s + 6, SvCUR(TOPs) - 6);
>+ int code;
>+
>+ if (SvCUR(TOPs) == 8 && s[6] == '-' && /* File test operator */
>+ strchr("rwxoRWXOezsfdlpSugkbctTBMAC", s[7])) {
>+ ret = sv_2mortal(newSVpvs(";*"));
>+ goto set;
>+ }
>+
>+ code = keyword(s + 6, SvCUR(TOPs) - 6);
> if (code < 0) { /* Overridable. */
> #define MAX_ARGS_OP ((sizeof(I32) - 1) * 2)
> int i = 0, n = 0, seen_question = 0;
>--- ../orig-perl-current/t/op/override.t 2005-09-27 10:12:28.000000000 +0200
>+++ t/op/override.t 2006-03-09 17:04:14.000000000 +0100
>@@ -6,7 +6,7 @@
> require './test.pl';
> }
>
>-plan tests => 22;
>+plan tests => 33;
>
> #
> # This file tries to test builtin override using CORE::GLOBAL
>@@ -110,3 +110,36 @@
> };
> is $@, '';
> }
>+
>+BEGIN {
>+ *{"Foo::-d"} = sub ($) { "o=>Foo::-d($_[0])" };
>+ *{"CORE::GLOBAL::-f"} = sub ($) { "o=>CORE::GLOBAL::-f($_[0])" };
>+}
>+
>+my $fn = "non-existent-file";
>+
>+
>+# commented cases do not work because prototype (;$) is not handled
>+# right
>+
>+ok(! -d $fn, "no overriden filetest -d");
>+ok(! -d, "no overriden filetest -d");
>+is(-f $fn, "o=>CORE::GLOBAL::-f($fn)", "overriden filetest -f");
>+# is(-f, "o=>CORE::GLOBAL::-f()", "overriden filetest -f");
>+ok(! CORE::-f $fn, "no overriden filetest CORE::-f");
>+ok(! CORE::-f, "no overriden filetest CORE::-f");
>+
>+{
>+ package Foo;
>+ ::is(-d $fn, "o=>Foo::-d($fn)", "overriden filetest -d");
>+ # ::is(-d, "o=>Foo::-d()", "overriden filetest -d");
>+ ::is(-f $fn, "o=>CORE::GLOBAL::-f($fn)", "overriden filetest -f");
>+ # ::is(-f, "o=>CORE::GLOBAL::-f()", "overriden filetest -f");
>+ ::ok(! CORE::-f $fn, "no overriden filetest CORE::-f");
>+ ::ok(! CORE::-f, "no overriden filetest CORE::-f");
>+ ::ok(! CORE::-d $fn, "no overriden filetest CORE::-d");
>+ ::ok(! CORE::-d, "no overriden filetest CORE::-d");
>+}
>+
>+
>+

Salvador Fandino

unread,
Mar 9, 2006, 3:51:07 PM3/9/06
to perl5-...@perl.org
Hi,

Nick Ing-Simmons wrote:
> Hmm, overide stat()-derived things as well as recent thread about stat()
> override proto. Care to tell us what you are trying to do?
> If we understood that we might be more sympathetic to the patches.

I am writing a module that lets access remote file systems via SFTP
transparently from Perl.

For instance, after importing it, this code should work:

my $fn = "user@host:/foo/bar";

if (-f $fn) {
open F, $fn or die "$!";
while(<F>) { ... }
}


All I have to do is to replace builtins accepting a file name with my
own versions that understand the extended file name syntax.

But, unfortunately not all built-ins can be properly overridden. Until
now I have found several problems:

- '*' proto doesn't convert argument to GV when it is a bareword

- subroutines with (;*) or (;$) prototypes are not handled as unary
operators so I can not write a replacement for stat that works as the
built-in for both these two cases:

print stat, "foo\n";
print stat "foo", "\n";

- filetest operators are not overridable

- tied handles doesn't support directory operations as OPENDIR,
READDIR, etc.

- missing callbacks for tied file handles: STAT, TRUNCATE. filetests


> stat() is one of the entries "missing" from the PerlIO abstraction.
> Getting perl to call stat() via IO abstraction might be cleaner than
> all these core overrides.

I am not currently using PerlIO but tied handles to do IO operations on
remote files.

But anyway, the reason to override the builtins is to do special actions
when they are passed file names and as PerlIO is focused on file handles
I don't think it could be useful for that.

Regards,

- Salva.

Yitzchak Scott-Thoennes

unread,
Mar 10, 2006, 4:55:51 AM3/10/06
to Salvador Fandino, perl5-...@perl.org
No actual help, but a few comments:

On Thu, Mar 09, 2006 at 09:51:07PM +0100, Salvador Fandino wrote:
> But, unfortunately not all built-ins can be properly overridden. Until
> now I have found several problems:
>
> - '*' proto doesn't convert argument to GV when it is a bareword

In most cases, you would use Symbol::qualify_to_ref() as recommended
in perldoc perlsub on *. But as you have noted, that doesn't help
with stat which should distinguish between stat "Foo" and stat Foo.



> - subroutines with (;*) or (;$) prototypes are not handled as unary
> operators so I can not write a replacement for stat that works as the
> built-in for both these two cases:
>
> print stat, "foo\n";
> print stat "foo", "\n";

The problem with ;$ will be resolved when someone implements the _
prototype. But that doesn't help for stat, which would need a
*-variant of _ :(.

>
> - filetest operators are not overridable

How does your patch interact with stacked filetests?



> - tied handles doesn't support directory operations as OPENDIR,
> READDIR, etc.
>
> - missing callbacks for tied file handles: STAT, TRUNCATE. filetests
>
>
> > stat() is one of the entries "missing" from the PerlIO abstraction.
> > Getting perl to call stat() via IO abstraction might be cleaner than
> > all these core overrides.
>
> I am not currently using PerlIO but tied handles to do IO operations on
> remote files.
>
> But anyway, the reason to override the builtins is to do special actions
> when they are passed file names and as PerlIO is focused on file handles
> I don't think it could be useful for that.

I'd recomend you look further into it.

Rafael Garcia-Suarez

unread,
Mar 10, 2006, 5:54:06 AM3/10/06
to perl5-...@perl.org
Salvador Fandino wrote:
> - subroutines with (;*) or (;$) prototypes are not handled as unary
> operators so I can not write a replacement for stat that works as the
> built-in for both these two cases:
>
> print stat, "foo\n";
> print stat "foo", "\n";

There's an item in perltodo.pod about this :

=head2 _ prototype character

Study the possibility of adding a new prototype character, C<_>, meaning
"this argument defaults to $_".

> But anyway, the reason to override the builtins is to do special actions
> when they are passed file names and as PerlIO is focused on file handles
> I don't think it could be useful for that.

I agree with that.

--
Only what happens every three hundred nights is true.
-- Borges

Salvador Fandiño)

unread,
Mar 10, 2006, 6:44:32 AM3/10/06
to Yitzchak Scott-Thoennes, perl5-...@perl.org
Hi,

--- Yitzchak Scott-Thoennes <stho...@efn.org> wrote:

> No actual help, but a few comments:

> > - subroutines with (;*) or (;$) prototypes are not handled as


> unary
> > operators so I can not write a replacement for stat that works as
> the
> > built-in for both these two cases:
> >
> > print stat, "foo\n";
> > print stat "foo", "\n";
>
> The problem with ;$ will be resolved when someone implements the _
> prototype. But that doesn't help for stat, which would need a
> *-variant of _ :(.

the '_' prototype? has this been discussed before?


> > - filetest operators are not overridable
>
> How does your patch interact with stacked filetests?

well, the feature is not broken for built-ins, you can even use

CORE::-f -f CORE::-f CORE::-f -f "$fn"

but when overridden they are handled as subroutines. I have to think
about it, how this could be solved. Any suggestion?

Anyway, if all the filetests are overridden, they could make to stack
properly returning *_ as the true value.


> > - tied handles doesn't support directory operations as OPENDIR,
> > READDIR, etc.
> >
> > - missing callbacks for tied file handles: STAT, TRUNCATE.
> filetests
> >
> >
> > > stat() is one of the entries "missing" from the PerlIO
> abstraction.
> > > Getting perl to call stat() via IO abstraction might be cleaner
> than
> > > all these core overrides.
> >
> > I am not currently using PerlIO but tied handles to do IO
> operations on
> > remote files.
> >

> > But anyway, the reason to override the built-ins is to do special


> actions
> > when they are passed file names and as PerlIO is focused on file
> handles
> > I don't think it could be useful for that.
>

> I'd recommend you look further into it.

I am redefining opendir, glob, stat, filetests, chdir, etc.. Those
(or low level support for those) are not defined on PerlIO. Or I am
missing something?

Cheers,

- Salva

__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com

Yitzchak Scott-Thoennes

unread,
Mar 10, 2006, 4:26:50 PM3/10/06
to Salvador Fandiño, perl5-...@perl.org
On Fri, Mar 10, 2006 at 03:44:32AM -0800, Salvador Fandiño wrote:
> > The problem with ;$ will be resolved when someone implements the _
> > prototype. But that doesn't help for stat, which would need a
> > *-variant of _ :(.
>
> the '_' prototype? has this been discussed before?

Yes (don't have a handy link). It's mentioned in perltodo.
;$ is flawed because it doesn't act like a unary operator, and
relies on the called sub looking up $_, which breaks my $_.

Nick Ing-Simmons

unread,
Mar 10, 2006, 5:57:24 PM3/10/06
to sfan...@yahoo.com, perl5-...@perl.org
Salvador Fandino <sfan...@yahoo.com> writes:
>Hi,
>
>Nick Ing-Simmons wrote:
>> Hmm, overide stat()-derived things as well as recent thread about stat()
>> override proto. Care to tell us what you are trying to do?
>> If we understood that we might be more sympathetic to the patches.
>
>I am writing a module that lets access remote file systems via SFTP
>transparently from Perl.
>
>For instance, after importing it, this code should work:
>
> my $fn = "user@host:/foo/bar";
>
> if (-f $fn) {
> open F, $fn or die "$!";
> while(<F>) { ... }
> }

>


>> stat() is one of the entries "missing" from the PerlIO abstraction.
>> Getting perl to call stat() via IO abstraction might be cleaner than
>> all these core overrides.
>
>I am not currently using PerlIO but tied handles to do IO operations on
>remote files.

Ah, the olde slow way ;-)

>
>But anyway, the reason to override the builtins is to do special actions
>when they are passed file names and as PerlIO is focused on file handles
>I don't think it could be useful for that.

PerlIO is an abstraction for Perl IO.
It extends up to filenames via open/sysopen.
stat() was (possibly) an oversight.

You should be able to do things like:


open(my $fh,"user@host:/foo/bar")
while (<$fh>)
{
}

With a suitably inteligent PerlIO layer with an OPEN method
which did the prefix spotting, opened a socket and then pushed
a layer or two to handle the protocol.

I thought we has proof-concept one that allowed http://... somewhere
by a perl :via layer that used LWP underneath - but perhaps I was dreaming.


sfan...@yahoo.com

unread,
Mar 11, 2006, 6:52:01 AM3/11/06
to perl5-...@perl.org
Yitzchak Scott-Thoennes wrote:

> On Fri, Mar 10, 2006 at 03:44:32AM -0800, Salvador Fandi?o wrote:
>>> The problem with ;$ will be resolved when someone implements the _
>>> prototype. But that doesn't help for stat, which would need a
>>> *-variant of _ :(.
>> the '_' prototype? has this been discussed before?
>
> Yes (don't have a handy link). It's mentioned in perltodo.
> ;$ is flawed because it doesn't act like a unary operator, and
> relies on the called sub looking up $_, which breaks my $_.

instead of a new prototype, how about using a modifier for the existent
ones?

'^' for instance, it would be equivalent to ';' but also forcing the
next prototype to get the adecuate argument from the caller when it is
not explicitelly included.

The prototypes for some builtins would become:

my_split (;$^$$);
my_stat (^*);
my_shift (^\@);

Cheers,

- Salva

Nick Ing-Simmons

unread,
Mar 11, 2006, 11:26:43 AM3/11/06
to sfan...@yahoo.com, perl5-...@perl.org
=?ISO-8859-1?Q?Salvador_Fandi=F1o?= <sfan...@yahoo.com> writes:
>
>instead of a new prototype, how about using a modifier for the existent
>ones?
>
>'^' for instance, it would be equivalent to ';' but also forcing the
>next prototype to get the adecuate argument from the caller when it is
>not explicitelly included.

What does 'adecuate' mean?
And can you explain how to get non explicitly included argument from caller?
Missing args normally default to a well known global, not to fishing
in the caller.

sfan...@yahoo.com

unread,
Mar 11, 2006, 3:15:43 PM3/11/06
to perl5-...@perl.org
Nick Ing-Simmons wrote:
> =?ISO-8859-1?Q?Salvador_Fandi=F1o?= <sfan...@yahoo.com> writes:
>> instead of a new prototype, how about using a modifier for the existent
>> ones?
>>
>> '^' for instance, it would be equivalent to ';' but also forcing the
>> next prototype to get the adecuate argument from the caller when it is
>> not explicitelly included.
>
> What does 'adecuate' mean?
> And can you explain how to get non explicitly included argument from caller?
> Missing args normally default to a well known global, not to fishing
> in the caller.


oops, sorry I mean "appropriate": ^$ would use $_ as the implicit
argument and ^@ (or ^\@) would take @_.

Cheers,

- Salva.

Salvador Fandiño)

unread,
Mar 14, 2006, 5:45:26 AM3/14/06
to perl5-...@perl.org
-- Rafael Garcia-Suarez <rgarci...@gmail.com> wrote:

> On 3/11/06, Salvador Fandi�o <sfan...@yahoo.com> wrote:
> > instead of a new prototype, how about using a modifier for the
> existent
> > ones?
> >
> > '^' for instance, it would be equivalent to ';' but also forcing
> the
> > next prototype to get the adecuate argument from the caller when
> it is
> > not explicitelly included.
> >

> > The prototypes for some builtins would become:
> >
> > my_split (;$^$$);
>

> that woul be used for (;$_$) in the terms of the old proposal,
> although _ somehow implies a ; before. (Also, split isn't
> overridable:)


>
> > my_stat (^*);
> > my_shift (^\@);
>

> You can't declare a lexical @_ or *_, so the need for those forms
> is
> less important. That would be only syntactic sugar, maybe a bit too
> confusing.

@_ can not be declared as lexical but it behaves like a lexical all
the time. AFAIK, there is no way to access @_ on the parent frame.

regarding ^* it should not pass *_ by default but $_, as ^$. The
difference between ^$ and ^* (or ^/) resides in how explicit
arguments are handled. For instance:

sub foo (^$) {...}
sub doz (^*) {...}
sub moc (^/) {...}

foo; # ==> &foo($_)
foo('bar'); # ==> &foo('bar')
foo(bar); # ==> &foo('bar')
# but complaints about bareword
# under 'use strict'

doz; # ==> &doz($_)
doz('bar'); # ==> &doz('bar')
doz(bar); # ==> &doz('bar')

moc; # ==> &moc($_)
moc('bar'); # ==> &moc('bar')
moc(bar); # ==> &moc(*bar)

If you use _, you can only have one of the previous behaviours.

An alternative would be to define prototype _ not as "use $_ by
default", but as "insert $_ on the argument list":

sub foo(_;$);
foo('hello'); # ==> &foo($_, 'hello')
foo; # ==> &foo($_)

though, that solution doesn't solve how to access caller @_.

Using '^' doesn't fully convince my either. I found it confussing,
doing too many things (introducing optional arguments and defaulting
behaviour)... how about still requiring the ';' to indicate optional
args:

sub foo(;^$) {}; # ok
sub bar(^$) {}; # complaints because no ';' was
# found before '^'

> Since ^ in your proposal is a special ; maybe use , instead of ;.
> But I think I still like _ better :)

I like ^ because I can think of it as an arrow pointing to the parent
frame.

Cheers,

- Salvador

Salvador Fandino

unread,
Mar 14, 2006, 5:47:56 PM3/14/06
to perl5-...@perl.org
Salvador Fandino wrote:
> Hi,
>
> Nick Ing-Simmons wrote:
>
>>Hmm, overide stat()-derived things as well as recent thread about stat()
>>override proto. Care to tell us what you are trying to do?
>>If we understood that we might be more sympathetic to the patches.
>
> I am writing a module that lets access remote file systems via SFTP
> transparently from Perl.
> ...

> All I have to do is to replace builtins accepting a file name with my
> own versions that understand the extended file name syntax.
>
> But, unfortunately not all built-ins can be properly overridden. Until
> now I have found several problems:

> ...

> - subroutines with (;*) or (;$) prototypes are not handled as unary
> operators so I can not write a replacement for stat that works as the
> built-in for both these two cases:
>
> print stat, "foo\n";
> print stat "foo", "\n";

I have been looking into this problem. A patch to change (;$) to behave
like an unary operator is attached.

BTW, stat wasn't a good example, 'rand' is better:


sub myrand (;$) { 0.5 * (@_ ? $_[0] : 1.0) }

print rand, "\n"; # ==> print(rand(), "\n");
print rand 2, "\n"; # ==> print(rand(2), "\n");

print myrand, "\n"; # ==> print(myrand(), "\n");
print myrand 2, "\n"; # ==> print(myrand(2, "\n")); croaks!!!

(other builtins with the same prototype are 'caller', 'exit' or 'alarm')


The problem is that this change is going to break things. For instance
Switch.pm defines switch as (;$) and in the test file, after some source
filtering it is used as

switch __ < 5

that with the patch applied gets parsed as

switch(__) < 5

But greeping over CPAN, what I have found is that several authors use
prototypes ($) and (;$) without paying attention to the precedence
issue. For instance, in Class::Contract we find

sub method($) { _member('method' => @_) }
sub ctor(;$) { _member('ctor' => @_) }
sub dtor(;$) { _member('dtor' => @_) }

that which current (;$) handling results very inconsistent, because
things that look similar perform very different operations:

ctor $foo || $bar # ==> ctor($foo || $bar);
method $foo || $bar # ==> method($foo) || $bar;

Also, prototypes (*) and (;*) or the ones requesting references as (\@)
or (\%) do not make the subs behave as unary operators either (but
built-ins with those prototypes do!).

So, in my opinion, I think that the current state is so inconsistent and
can create so many gotchas that it has to be changed even if that breaks
old code.

Opinions? I can dedicate some time to do that if we agree that it is a
convenient change.

Cheers,

- Salva.

perl-subuniop-without-arg-0.patch

Rafael Garcia-Suarez

unread,
Mar 15, 2006, 6:41:42 AM3/15/06
to Salvador Fandino, perl5-...@perl.org
Salvador Fandino wrote:
> > - subroutines with (;*) or (;$) prototypes are not handled as unary
> > operators so I can not write a replacement for stat that works as the
> > built-in for both these two cases:
> >
> > print stat, "foo\n";
> > print stat "foo", "\n";
>
> I have been looking into this problem. A patch to change (;$) to behave
> like an unary operator is attached.
>
> BTW, stat wasn't a good example, 'rand' is better:
>
>
> sub myrand (;$) { 0.5 * (@_ ? $_[0] : 1.0) }
>
> print rand, "\n"; # ==> print(rand(), "\n");
> print rand 2, "\n"; # ==> print(rand(2), "\n");
>
> print myrand, "\n"; # ==> print(myrand(), "\n");
> print myrand 2, "\n"; # ==> print(myrand(2, "\n")); croaks!!!
>
> (other builtins with the same prototype are 'caller', 'exit' or 'alarm')
>
>
> The problem is that this change is going to break things.

I'd hate to break things here, because that's going to be very difficult
to debug and detect afterwards. That's difficult to admit, but Perl 5 is
not an evolving language anymore :) that's why we introduced the
"feature" pragma instead of simply adding new operators and keywords.

I'd rather extend the existing syntax than modifying it.

The _ proposal, that can be renamed to the "^$ and ^* proposal" (^*
being suitable for emulating stat()) can make the situation a bit
better.

That wouldn't solve the case for rand look-alikes (since it does not default
to $_), but that would be a step.

> Also, prototypes (*) and (;*) or the ones requesting references as (\@)
> or (\%) do not make the subs behave as unary operators either (but
> built-ins with those prototypes do!).

Yes.

--
Is it any wonder the world's gone insane, with information come to be
the only real medium of exchange ? -- Thomas Pynchon, Gravity's Rainbow

sfan...@yahoo.com

unread,
Mar 15, 2006, 7:18:06 AM3/15/06
to perl5-...@perl.org
Rafael Garcia-Suarez wrote:
> Salvador Fandino wrote:
>>> - subroutines with (;*) or (;$) prototypes are not handled as unary
>>> operators so I can not write a replacement for stat that works as the
>>> built-in for both these two cases:
>>>
>>> print stat, "foo\n";
>>> print stat "foo", "\n";
>> I have been looking into this problem. A patch to change (;$) to behave
>> like an unary operator is attached.
>>
>> BTW, stat wasn't a good example, 'rand' is better:
>>
>>
>> sub myrand (;$) { 0.5 * (@_ ? $_[0] : 1.0) }
>>
>> print rand, "\n"; # ==> print(rand(), "\n");
>> print rand 2, "\n"; # ==> print(rand(2), "\n");
>>
>> print myrand, "\n"; # ==> print(myrand(), "\n");
>> print myrand 2, "\n"; # ==> print(myrand(2, "\n")); croaks!!!
>>
>> (other builtins with the same prototype are 'caller', 'exit' or 'alarm')
>>
>>
>> The problem is that this change is going to break things.
>
> I'd hate to break things here, because that's going to be very difficult
> to debug and detect afterwards. That's difficult to admit, but Perl 5 is
> not an evolving language anymore :) that's why we introduced the
> "feature" pragma instead of simply adding new operators and keywords.

> I'd rather extend the existing syntax than modifying it.

how about adding another prototype '1' to explicetelly indicate that the
sub has to be parsed as an unary operator?

Using the 1, ^ and / prototypes we could mimic almost any built-in:

myrand (1;$)
mystat (1;^/)
myrequire (1*)
mypop (1;\^@)


Cheers,

- Salva.

David Nicol

unread,
Mar 15, 2006, 1:58:09 PM3/15/06
to perl5-...@perl.org
To reiterate what the _ proposal was; it was that a new prototype character, _,
be added, and only allowed as the last prototype character, and it means
that when the prototype-required slots are not filled, $_ is implied in the
last slot.

_ would not be compatible with ;

_ would not make sense anywhere but at the end of the prototype string

Furthermore it looks to me like the discussion in this thread has
confused "unary" with "high precedence"

I am opposed to mucking with the precedence rules: such a notion
sounds like a sure recipe for AAAD problems.

Salvador Fandino

unread,
Apr 3, 2006, 11:38:31 AM4/3/06
to perl5-...@perl.org

Well, I have finally done it. The patch attached adds support for:

- '/' prototype that is like '$' but that converts barewords to globs so
it can be used on subroutines accepting both a file handler or a file name.

- '^' prototype prefix, that causes the argument of the prototype it
prefixes to default to $_, @_ or %_ from the caller (%_ is probably
useless, but I have added it just for completeness).

- '1', to force the subroutine into being parsed as an unary operator
with unary precedence.


I think that with those extensions it should be possible to mimic almost
any perl built-in.

Cheers,

- Salva

perl-protos-0.patch
0 new messages