7

Basically, the question is more about grammars but I think that it could be more of a interesting exercise on dynamic variables.

I have a grammar role with a prototyped token (the example is simplified to demonstrate the idea):

proto token foo {*}
token foo:sym<a> {
   :my $*delimiter = q<">;
   \" ~ \" <value>
}
token foo:sym<b> {
   :my $*delimiter = q<'>;
   \' ~ \' <value>
}
token value {
    .+? <?before $($*delimeter) || $($*custom-delimiter)>
}

When the role is consumed by a grammar I want the $*custom-delimiter to be set by the grammar. Of course, I can declare it everywhere where <foo> is needed. But sometimes it is ok to have it pre-initialized with a universal default. Something like:

{ $*custom-delimiter //= $default-delimiter }

in the value token would work. But external pre-declaration would still be needed.

I hoped that:

our $*custom-delimiter is export = $default-delimiter;

in the scope of module where the role is declared would work. But apparently it doesn't. So, the question is: are there any elegant solutions to this?

Actually, I also hope that the solution would allow to move declaration of $*delimiter in foo outside of the token definitions too.

As a side note: my first thought was about adding a parameter to the token. But having absolutely identical signatures for each variant is looking terrible too:

token foo:sym<a> ( $*custom-delimiter = $default-delimiter ) {
}
token foo:sym<b> ( $*custom-delimiter = $default-delimiter ) {
}
token foo:sym<c> ( $*custom-delimiter = $default-delimiter ) {
}

Another approach is to have something like:

token pre-foo ( $*custom-delimiter = $default-delimiter ) {
    <foo>
}

In this case an additional method would be required in actions class to propagate $/<foo>.ast one level up.

jjmerelo
  • 22,578
  • 8
  • 40
  • 86
Vadim Belman
  • 1,210
  • 6
  • 15
  • 1
    I'm too tired to be able to understand your question tonight. But you know roles can be parameterized, right? eg `role foo[$bar = 99] { method baz { say $bar } }; foo[42].baz; # 42`. Is that relevant? – raiph Nov 16 '18 at 02:45
  • Never needed it, so – it's a good reminder! But – no, unfortunately. Token `foo` is used by the same grammar in different contexts where different delimiters are used. – Vadim Belman Nov 16 '18 at 02:49
  • Bumped into this SO again while researching another one. re `our $*custom-delimiter is export = $default-delimiter;` cf "You cannot currently export dynamic variables that way, and maybe we never will." about somewhat similar code [here](https://stackoverflow.com/a/53707507/1077672), and then "still Not Yet Implemented completely: the export works sorta, but winds up being an empty hash. So not very useful." [here](https://stackoverflow.com/a/57018460/1077672). – raiph Jun 07 '20 at 16:47
  • You can't export using `is export` but you can using the EXPORT sub without any problem IME. I've been using that successfully in a way to allow each `use` statement of a module to insert its own scope for certain settings that would otherwise be necessarily global – user0721090601 Aug 16 '20 at 06:59

1 Answers1

1

Based on some test work I've done in one of my modules for allowing scoped settings for a module, you can do this but you will need to use the EXPORT sub.

I imagine the reason is that when doing EXPORT, we can install what is explicitly a brand new dynamic variable, rather than a new symbol linked to an extant dynamic variable — the latter of which to me makes scoping very unclear.

This seems to work okay for me.

# filename: Foo.rakumod
# no 'unit module', etc

sub EXPORT {
  proto token foo {*}
  token foo:a { … }
  token foo:b { … }

  Map.new: 
    '&foo'      => &foo,
    '$*dynamic' => my $ = 'default'
}

user0721090601
  • 5,276
  • 24
  • 41