Why can't anything go in the body of a Perl 6 grammar proto?
Asked Answered
K

1

10

When declaring a proto, it's possible to surround the multimethod/sub code with additional code. For a silly example:

proto sub foo(|) { 'Camelia says “' ~ {*} ~ '!”' }
multi sub foo(1) { "hi"  }
multi sub foo($) { "bye" }

say foo(1) # Camelia says “hi!”

I haven't come across any times (yet) where I've felt that's hugely useful, but on the flipside, I have come across some where in regex/Grammar world where I occasionally find myself repeating myself throughout all of the proto tokens — it's not that I can't shift into a separate token, but that adds an extra layer of hierarchy and actions. Compare a hypothetical

grammar Bar { 
   token TOP { <meta>* }
   token metastart { '#' }
   proto token meta { <metastart> {*} }
         token meta:name { ( \w* ) }
         token meta:date { (\d**6) }
}

Where they only action methods necessary besides TOP would be meta:nameand meta:date to

grammar Bar { 
   token TOP { <meta>* }
   token metastart { '#' }
   token meta { <metastart> <metacontent> }

   proto token metacontent      {   *   }
         token metacontent:name {  \w*  }
         token metacontent:date { \d**6 }
}

This now requires three methods: metacontent:name, metacontent:date and then a fairly superfluous meta whose entire body would just be make $<metacontent>.made.

Although I've seen documentation / comments floating around saying you can toss in code in the protos inside of a grammar, if anything is placed in the body of a proto token/regex/rule other than a single *, then the compiler complains: Proto regex body must be {*} (or <*> or <...>, which are deprecated)

Is this an as-yet unimplemented features or is the plan to no longer allow anything in the proto? And if it's the latter, why should the proto designator be required? (I'm guessing it's because there's something special about how proto token/regex/rule dispatches, since removing proto inside of a grammar results in runtime failures unlike in the sub/method world.

Kashmiri answered 30/7, 2019 at 21:44 Comment(2)
Regarding usefulness for multi subs: proto sub foo(|) { say "Do some init task"; my $ret = {*}.IO; #`(Coerce all returns to IO::path); say "Do some cleanup task"; return $ret; }; multi sub foo(Str $a) { return $a }; multi sub foo(IO::Path $a) { return $a };Lacrosse
@Lacrosse ++ Hmm, yeah, actually the more I look at it the more I prefer that over doing a pure multidispatch, capturing the string case and calling a samewith with some coerced values.Kashmiri
D
1

As a matter of fact, proto regexes are little more than a grouping device for several tokens. The documentation says:

the name of a group of values we'll create

They don't really declare a routine multi, and in fact, tokens that follow that proto are not declared as multis, they're simply tokens (which are regexes, which are methods) with funny names.

In your case, you can simply avoid the additional proto token by using alternation, which is actually what protos in grammars stand for:

grammar Bar { 
    token TOP { <meta>* }
    token metastart { '#' }
    token meta { <metastart> [ <name> | <date> ] }
    token name {  \w*  }
    token date { \d**6 }
}

say Bar.parse( "#Hey" );
say Bar.parse( "#3333" );
Deicide answered 26/2, 2020 at 8:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.