How to override the NQPMatch.Str function
Asked Answered
T

1

6

... Or how to change $<sigil>.Str value from token sigil { ... } idependently from the matched text. Yes I'm asking how to cheat grammars above (i.e. calling) me.

I am trying to write a Slang for Raku without sigil.

So I want the nogil token, matching anything <?> to return NqpMatch that stringifies: $<sigil>.Str to '$'.

Currently, my token sigil look like that

token sigil {
    | <[$@%&]>
    | <nogil> { say "Nogil returned: ", lk($/, 'nogil').Str; # Here It should print "$"
              }
}
token nogil-proxy {
    | '€'
    | <?>
    {log "No sigil:", get-stack; }
}

And the method with that should return a NQPMatch with method Str overwritten

method nogil {
    my $cursor := self.nogil-proxy;
    # .. This si where Nqp expertise would be nice
    say "string is:", $cursor.Str;    # here also it should print "$"
    return $cursor;
}

Failed try:

$cursor.^cache_add('Str', sub { return '$'; } );
$cursor.^publish_method_cache;
for $cursor.^attributes { .name.say };
for $cursor.^methods { .name.say };
say $cursor.WHAT.Str;
nqp::setmethcacheauth($cursor, 0);

Currently, most of my tests work but I have problems in declarations without my (with no strict) like my-var = 42; because they are considered as method call.

@Arne-Sommer already made a post and an article. This is closely related. But this questions aims:

How can we customize the return value of a compile-time token and not how to declare it.

Tewell answered 2/4, 2020 at 1:37 Comment(7)
Perhaps try mixing a role that overrides Str into the match object, perhaps in a sigil action method. Though I suspect that'll only get you so far. Ultimately, the Raku grammar is formally ambiguous without sigils (that's why sigilless vars have to be introduced with my \foo = ...), so I suspect you'll run into endless problems trying to do this.Ephemeron
@JonathanWorthington What I think you're saying is that practical raku grammars in general are predominantly predictive; and the existing raku one is written to predictively expect, for example, foo in my foo... to be a type; and this and other such predictive presumptions run deep. However, aiui, an actual raku grammar "SHOULD" never be formally ambiguous unless it involves "specified" scenarios declared to be ambiguous. Is that right afauk?Costanzia
@JonathanWorthington: You solved my problem. I did overlook mixin vs composition. And Compose method do not exists in NQPMatch but mixin is working. Thank You!Tewell
@Tewell I was being a little informal there, yes; a concrete Raku grammar has tie-breakers that decide what will be parsed, and a parse thus doesn't ever fail with some kind of ambiguity error. My point was that the language design of Raku itself leans quite heavily on sigils. There's many examples; for instance, :$foo means :foo($foo), but :foo means :foo(True), and you thus can't have a convenience form of the colonpair construct for passing a variable on without sigils - or you have to drop the one for passing True.Ephemeron
@Tewell More fundamentally, though, you'll also have to arrange for things declared as variables to be recognized as terms rather than listops. Otherwise, foo + bar will parse as foo (listop) calling bar and applying the prefix + operator to it. Not an infix operator on two variables.Ephemeron
@JonathanWorthington: In short: You were right: all my test fail now. I managed to get what I wanted. Yippeee. I had to modify both actions and grammars. So my foo = 37; say MY:: -> {$foo => 37}. So foo and $foo is the same thing in declaration: I can say "Seems cool $foo". But then I cannot foo + $foo because no foo in MY:: scope. So I'll have to cheat in many places.Tewell
Anyway even permitting foo in scope (<?>) in <sigil>. I have to check on routine call for foo = 42 to be considered as a declaration.Tewell
T
6

Intro: The answer, pointed by @JonathanWorthington:

Brief: Use the mixin meta function. (And NOT the but requiring compose method.)

Demo:

  1. Create a NQPMatch object by retrieving another token: here the token sigil-my called by self.sigil-my.
  2. Use ^mixin with a role
method sigil { return self.sigil-my.^mixin(Nogil::StrGil); }

Context: full reproducible code:

So you can see what type are sigil-my and Nogil::StrGil. But I told you: token (more than method) and role (uninstantiable classes).

role Nogil::StrGil {
    method Str() {
        return sigilize(callsame);
    }
}


sub EXPORT(|) {

# Save: main raku grammar
my $main-grammar = $*LANG.slang_grammar('MAIN');
my $main-actions = $*LANG.slang_actions('MAIN');

role Nogil::NogilGrammar {
    method sigil {
        return self.sigil-my.^mixin(Nogil::StrGil);
    }
}

token sigil-my { | <[$@%&]> | <?> }

# Mix
my $grammar = $main-grammar.^mixin(Nogil::NogilGrammar);
my $actions = $main-actions.^mixin(Nogil::NogilActions);
$*LANG.define_slang('MAIN', $grammar, $actions);

# Return empty hash -> specify that we’re not exporting anything extra
return {};

}

Note: This opens the door to mush more problems (also pointed by jnthn question comments) -> -0fun !

Tewell answered 2/4, 2020 at 20:49 Comment(1)
@p6steve, yes it is crazy, so easy to modify many things at onces. Modules are very small in Raku for that reason I guess.Tewell

© 2022 - 2024 — McMap. All rights reserved.