Passing variables to token or regex or rule is fairly straightfoward. For example, the output of
grammar Foo {
token TOP { (.) {} <bar($0)> }
token bar($s) { {say ~$s} .+ }
}
Foo.parse("xyz")
is simply x. But things get awry when using proto. For example,1 let's make a simple proto to discriminate the rest of the string being alpha or numeric:
grammar Foo {
token TOP { (.) {} <bar($0)> }
proto token bar { * }
token bar:sym<a> ($s) { {say ~$s} <alpha>+ }
token bar:sym<1> ($s) { {say ~$s} <digit>+ }
}
Foo.parse("xyz")
This bombs, claiming that it expected 1 argument but got 2 for bar. Okay, well in normal methods, we have to specify the args in the proto declaration, so let's just declare that:
grammar Foo {
token TOP { (.) {} <bar($0)> }
proto token bar ($s) { * }
token bar:sym<a> ($s) { {say ~$s} <alpha>+ }
token bar:sym<1> ($s) { {say ~$s} <digit>+ }
}
Foo.parse("xyz")
Now we get the opposite: expected 2 arguments but got 1. Hm, maybe that means the proto declaration is eating the value and not passing anything along. So I tried just slipping it in:
grammar Foo {
token TOP { (.) {} <bar($0)> }
proto token bar (|) { * }
token bar:sym<a> ($s) { {say ~$s} <alpha>+ }
token bar:sym<1> ($s) { {say ~$s} <digit>+ }
}
Foo.parse("xyz")
Same error here. It claims it expected 2 arguments, but got 1.2 Somehow the use of proto is eating up the arguments. Currently, the only solution that I've found uses dynamic variables, which makes me think that there may be some hidden step where the variable isn't being passed from proto to candidate.
grammar Foo {
token TOP { (.) {} <bar($0)> }
proto token bar ($*s) { * }
token bar:sym<a> { {say ~$*s} <alpha>+ }
token bar:sym<1> { {say ~$*s} <digit>+ }
}
Foo.parse("xyz")
But this seems like a not-entirely intuitive step. How would one pass the variable directly in a non-dynamic fashion to a proto such that it is received by the candidate?
[1] Note that all of the above code has been golfed to focus on passing variables. The actual tokens used bear no resemblance to my real code.
[2] I'm starting to wonder too if this is (generally speaking) a LTA error message. While I get that it's based on first arg = invocant, it still feels off. Maybe it should say "Expected invocant and one argument, only received invocant" or somesuch.
TL;DR
It's a bug. See [BUG] Proto regex with params isn't called correctly (possibly NYI) in Rakudo.
I have an alternative approach that works for passing non-dynamic arguments. But see the next point.
Your follow up commentary explaining what you'd golf'd from suggests your dynamic variable alternative might be better. I'll discuss that too.
An alternative approach that works
Switch proto token... to proto method... and token foo:sym<...>s to multi tokens without the :sym<...> affix:
grammar Foo {
token TOP { (.) {} <bar($0)> }
proto method bar ($s) {*}
multi token bar ($s where /<alpha>+/) { {say 'alpha start ', $s} .. }
multi token bar ($s where /<digit>+/) { {say 'digit start ', $s} .. }
}
say Foo.parse("xyz")
displays:
alpha start 「x」
「xyz」
0 => 「x」
bar => 「yz」
Your dynamic variable alternative might be better
In my actual code, the variable is passed along to block certain matches (mainly to avoid certain types of recursion)
It sounds like you could have a single dynamic variable (say $*nope), set to whatever value you wish, and systematically use that. Or perhaps a couple. Dynamic variables are intended for exactly this sort of thing. Beyond an ideological discomfit with dynamic variables (to the degree they're carelessly used as unconstrained globals they are bad news), what's not to like?
The first thing is that I don't really get what you intend to do here. My impression is that you want the second part of the token to be a function of the first part. I don't get why you use a proto here. You can do that straight away this way:
grammar Foo {
token TOP { (.) {} <bar($0)> }
token bar( $s ) { {say ~$s} $s <alpha>+ }
}
say Foo.parse("xxz")
But I'm not sure you can actually make it work combining syms and arguments. syms already have one argument: the one used in the adverb. It's more than simply a symbol, it's what is going to be matched there (if you use the predefined token <sym>; you can simply use it as sub-matches too:
grammar Foo {
token TOP { (.) {} <bar> }
proto token bar {*}
token bar:sym<alpha> { <alpha>+ }
token bar:sym<digit> { <digit>+ }
}
say Foo.parse("xxz");
say Foo.parse("x01")
Or simply use the string as a sym match:
grammar Foo {
token TOP { (.) {} <bar>+ }
proto token bar {*}
token bar:sym<x> { <sym>+ }
token bar:sym<z> { <sym>+ }
token bar:sym<1> { <sym>+ }
token bar:sym<0> { <sym>+ }
}
say Foo.parse("xxz");
say Foo.parse("x01")
So I would say that Raiph's answer is where you want to go; syms do not seem like the right way to achieve that, since they have a built-in variable (the argument to sym), but you have to specify every single case.
Related
On the trail of this question by Codesections, I'm trying to add a phaser to a variable using traits. Something like this:
my &doing-good is Block will enter {
.add_phaser: "ENTER",
{
if Backtrace.new.grep: { .subname ~~ /bad/ } {
fail("Not authorized to call this");
}
}
};
This fails with is trait on &-sigil variable not yet implemented. Sorry.
I arrived to this because there seems no way to declare that as a block; by default is a Callable, and add_method does not work on Callables, apparently. Any other way of doing this?
This question is a near-duplicate of Apply a proxy using traits. However, that question dealt with applying a proxy to an Attribute, and I would like to do the same thing for a Variable. From Jonathan's answer, I understand that I
need to arrange for the Proxy to be bound into the attribute, so that there's a Proxy there rather than a Scalar container that is usually created by class initialization logic.
However, I can't seem to bind successfully to a Variable:D, even at compile time. (Including with nqp::bind). I'd greatly appreciate any pointers in the correct direction.
(Ideally, I'd like to support using the variable/trait with assignment syntax. In a perfect world, I'd have syntax like:
my $thing is custom-proxy = 42;
And the result of that would be that $thing is containerized inside the Proxy, but not in a Scalar. But if that's not possible, I'd settle for getting it working with binding via :=.
[EDIT: building on the accepted answer below, it is possible to mostly do this with the following code:
multi trait_mod:<is>(Variable \v, :$tom) {
v.block.add_phaser(
'ENTER',
v.willdo(<-> $_ {
$_ = Proxy.new:
STORE => -> $, $v { say "store $v" },
FETCH => { say "fetch!"; 42}
}, 1))
}
This works for variables that are not initialized to a different value or for state variables on calls to the function other than the first.
You can always bind.
my $actual-thing = 42;
my $thing := Proxy.new(
FETCH => anon method fetch () {
say 'fetch';
$actual-thing
},
STORE => anon method store ($new) {
say 'store ',$new;
$actual-thing = $new
}
);
say $thing;
$thing = 5;
say $thing;
Which currently results in the following.
fetch
fetch
fetch
fetch
fetch
fetch
fetch
42
store 5
fetch
fetch
fetch
fetch
fetch
fetch
fetch
5
(The repeated FETCH calls are a known limitation.)
If you wanted to have syntax like
my $thing is custom-proxy = 42;
You would need to start with
multi trait_mod:<is> ( Variable:D \var, :$custom-proxy! ){
…
}
The problem is that currently doing it this way requires a lot of deep Rakudo/nqp knowledge that I do not possess.
For example the code behind my $var is default('value') looks a bit like this:
multi sub trait_mod:<is>(Variable:D $v, Mu :$default!) {
my $var := $v.var;
my $what := $var.VAR.WHAT;
my $descriptor;
{
$descriptor := nqp::getattr($var, $what.^mixin_base, '$!descriptor');
CATCH {
my $native = $v.native($what);
…
}
}
…
$descriptor.set_default(nqp::decont($default));
# make sure we start with the default if a scalar
$var = $default if nqp::istype($what, Scalar);
}
Why does that have $what.^mixin_base?
I have no idea.
Why isn't $!descriptor accessible something like $v.var.descriptor?
I have no idea.
How do we change $v.var.VAR from a Scalar to a Proxy?
I have no idea.
Is that last one doable? (From within a trait_mod:<is>)
I am fairly certain that the answer is yes.
My 2d[1]:
I'd settle for getting it working with binding via :=.
sub custom-proxy is rw { Proxy.new: FETCH => { 42 }, STORE => { ... } }
my $variable := custom-proxy;
say $variable; # 42
In a perfect world, I'd have syntax like:
my $thing is custom-proxy = 42;
Aiui, that's #Larry's intent.
But, as you presumably know, if a type (eg role custom-proxy { ... }) is applied using an is trait to a scalar variable (eg my $variable is custom-proxy) then the compiler emits a compile time error message (is trait on $-sigil variable not yet implemented).
I can't seem to bind successfully to a Variable:D, even at compile time
First, let's clarify what a Variable is, and what you would need to successfully bind to:
multi trait_mod:<is>(Variable \var, :$foo!) { say var.var.VAR.WHAT } # (Scalar)
my $variable is foo;
You might think you could bind to var. But the compiler is passing an lvalue, so you're not going to be able to alter it.
You might think you could bind to var.var, which is an attribute of a Variable. (I explain what a Variable is, and its var attribute, and why I had to write "varvarVAR!" in the above code, here.)
The SO you linked shows how to alter the value bound to an attribute in some object:
$a.set_build: -> \SELF, | {
$a.set_value: SELF, Proxy.new:
STORE => -> $, $val { say "store $val" },
FETCH => { say "fetch!"; 42 }
}
So perhaps you could use that approach to alter the .var attribute of a Variable?
Unfortunately, "setting build logic" is used to "bind the attribute ... at each object creation", (hence "you'll be overriding any initial default value").
So I don't think this technique is going to help in this case because the Variable, and hence its .var attribute, has presumably already been built by the time the Variable is passed to the is trait.
In summary, while a trait is called at compile-time, I think it's called too late because the var attribute has already been permanently bound.
My guess is that altering Raku(do) so that the Variable's .var attribute becomes writable, or using metaprogramming to dive underneath Variable's public API to force through a change, would be beyond fraught, unreasonably complicating the compiler's variable handling code and/or swapping out codegen optimization logic for pessimization logic.
This may be behind #Larry's speculation that a more controlled is type on scalar variables will one day be implemented.
Footnotes
[1] My two (pennies | dogecoin).
In theory, and according to the documentation, you can use any argument for methods in grammar actions.
grammar G {
token TOP { \w+ }
}
class Action-Arg {
method TOP ($match) { $match.make: ~$match }
}
class Action {
method TOP ($/) { make ~$/ }
}
class Action-Fails {
method TOP ($match) { make ~$match }
}
say G.parse( "zipi", actions => Action-Arg );
say G.parse( "zape", actions => Action );
say G.parse( "pantuflo", actions => Action-Fails );
However, the two first versions work as expected. But the third one (which would be a direct translation of the second), fails with
Cannot bind attributes in a Nil type object
in method TOP at match-and-match.p6 line 19
in regex TOP at match-and-match.p6 line 7
in block <unit> at match-and-match.p6 line 24
There's probably some special syntax going on (in the sense of make being actually $/.make, probably), but I'd just like to clarify if this is according to spec or is a bug.
That is because the make subroutine is one of those rare cases in Rakudo where it actually tries to access the $/ variable from the scope it is called from. Which is also how it is documented:
The sub form operates on the current $/
(from the documentation)
I was curious about grammars being classes or singletons, so I created this small program to find out:
grammar Mini {
token TOP { \* <word> \* }
token word { \w+ }
}
proto sub is-class( | ) { * };
multi sub is-class( Grammar:D $g ) { return "Object" };
multi sub is-class( Grammar:U $g ) { return "Class" };
say is-class( Mini );
This uses multiple dispatch to find that out, and it turns out that Mini is actually a class. In general, would there be a shorter way of finding this out? Or a way that would not require to know the actual class of which the package might be an instance?
You can disambiguate 'instances' and 'classes' via DEFINITE, ie
Mini.DEFINITE ?? 'Object' !! 'Class'
or rather
Mini.DEFINITE ?? 'concrete object' !! 'type object'
should do the trick.
I'm about to choose what language to use for a new project: Perl5 or Perl6. 6 wins so far except that it is missing Moo's lazy attributes. The two implementations I found in modules are missing the key functionality. Hence, my attempt write my own implementation.
Role vs. Class
First problem I've got into is the content of attribute's .package for one declared in a role. Consider the followin:
role HOW1 {
method compose ( Mu $class ) {
note "HOW1.compose";
nextsame;
}
}
role HOW2 {
method compose ( Mu $class ) {
note "HOW2.compose";
nextsame;
}
}
multi trait_mod:<is> (Attribute:D $attr, :$mooish!) {
note "Attribute's package.HOW: ", $attr.package.HOW;
note '$*PACKAGE.HOW: ', $*PACKAGE.HOW;
$attr.package.HOW does HOW1;
$*PACKAGE.HOW does HOW2;
}
class Foo {
has $.bar is mooish;
}
role FooRole {
has $.baz is mooish;
}
The output of the script follows:
Attribute's package.HOW: Perl6::Metamodel::ClassHOW.new
$*PACKAGE.HOW: Perl6::Metamodel::ClassHOW.new
HOW2.compose
HOW1.compose
Attribute's package.HOW: Perl6::Metamodel::GenericHOW.new
$*PACKAGE.HOW: Perl6::Metamodel::ParametricRoleHOW.new
HOW2.compose
As it is clearly seen from the output, applying a role to a metaclass always works for classes and only works for $*PACKAGE.HOW with roles. Use of $*PACKAGE instead of .package could be considered a solution, but not the one I'd really like to use. (Though, if there is no better way...)
Accessor
I would like to provide lazy functionality for private attributes too. Yes, this will be availabe with self!bar syntax only, but this is a sacrifice I'm willing to make. 😉 The problem is that all the examples of custome-made accessor I found so far are using Attribute.set_value() method which is way too low-level. I'd like to have something like this:
role MooishHOW {
method compose ( Mu $class ) {
my $accessor = $class.^add_private_method( 'bar1',
method () is rw {
note self.WHO, ".bar1";
Proxy.new(
FETCH => -> $o {
$!bar1;
},
STORE => method ( $val ) {
note "Storing";
$!bar1 = $val;
}
);
}
);
callsame;
}
}
multi trait_mod:<is> (Attribute:D $attr, :$mooish!) {
$attr.package.HOW does MooishHOW unless $attr.package.HOW ~~ MooishHOW;
}
class Foo {
has $.bar is mooish;
has $!bar1 is mooish;
method to-bar1 {
note "bar1 val:",self!bar1;
}
}
my $inst = Foo.new;
$inst.to-bar1;
But $!bar1 notation doesn't compile because of the scope (MooishRole). Are there a trick I'm missing which would allow referencing a private attribute on self?
Tricky one
Perhaps it is possible to make an attribute to be a Proxy container? This would greatly simplify the overall logic of laziness implementation.
I have answered all my questions by finally achieving the target and released AttrX::Mooish module.
So far, the answer for the first question is: no. $*PACKAGE is currently the only way.
Second question: have no answer, but the final code has to rely on set_value() anyway.
The tricky one happened to be possible: set_value() does binding of an attribue to a container making it possible to bind to a Proxy object. No need to for sacrifices, private attributes can be accessed directly with lazyness working on them.
Thanks everybody, your answers let me work around some rough edges!