develooper Front page | perl.perl5.porters | Postings from August 2013

Re: [perl #119455] [EXPERIMENT] pluggable keyword API

Thread Previous | Thread Next
August 25, 2013 13:10
Re: [perl #119455] [EXPERIMENT] pluggable keyword API
Message ID:
Lukas Mai wrote:
>Devel::CallParser solves a different problem. I can only use it to
>implement custom argument syntax for subroutines, but what I actually
>want is custom keywords.

The breakthrough that led to Devel::CallParser is the understanding that
there's no need for such a distinction, and in fact that we're better
off without one.  Keyword-led syntax and subroutine calls both begin
the same way, with a bareword that identifies the type of construction
that is to follow.  Keywords and subroutine names are semantically
active in the same situations.  So it doesn't make sense to treat
them as two separate namespaces.  Better to have one namespace, with
the metaobject to which the name refers determining all the behaviour.
Of the two namespaces we already have, the one that is more manipulable
is the subroutine namespace.

>For example: if I want to reimplement 'sub', I need to accept a bareword
>(subroutine name) followed by a block (subroutine body). None of the
>parse_args_* functions can be used for this.

That's orthogonal to how the namespace is managed.  You need those
parsing routines regardless of how your plugged-in parser got invoked.
parse_args_*() don't cover your needs, but the core's parse_block()
does the sub body.  parse_block() can be perfectly well called from a
D:CP-managed sub arg parser.  The possibility of calling parsing code
other than the standard argument parsers is the point of D:CP.

>                                 I still have to return an argument
>list. This isn't needed because all the effects I want happen at
>runtime, but the API insists on constructing a sub call.

When using D:CP for this sort of thing, you'll also want to use
Devel::CallChecker to turn the sub calling ops into something
other than a sub call.  See Memoize::Lift, for example, which turns
lift(some_arbitrary_expr()) into a compile-time constant, requiring
modification of both stages.

It's sane to argue that the eventual API should make it easier than
this to generate a completely custom op sequence from a custom parser.
I think there is some use in keeping the two-phase structure, because
this potentially allows use of the op customisation from code that builds
ops directly rather than using the parser.  This is always possible for
things that use only Devel::CallChecker: one can build the entersub op
tree manually, and the check phase replaces it with the custom ops just
as if the sub call had been parsed normally.  But where such a split is
really not convenient (as in Memoize::Lift, which actually does all the
constant lifting in the parse phase), a call checker that just throws
away the entersub wrapper and yields the parameter's op tree (M:L's
myck_entersub_lift minus the const op type check) could be supplied by
the core for convenience.

>Finally, the whole thing won't be a statement by itself, so the user
>will need to add a ';'.

That's what the CALLPARSER_STATEMENT flag in the D:CP API is for.
For example, see Scope::Escape::Sugar, which implements "block foo
{...}" being a complete statement, without semicolon.  The code that
implements that form is a bit tricky to follow, I'm afraid, partly due
to also allowing "block(foo {...})" as an expression (not statement)
and partly due to some hackery to support parsing on Perls that don't
supply the parser callback interfaces.

>In short: If I haven't misunderstood how D:CP works,

You have misunderstood much of D:CP.


Thread Previous | Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About