rakunqp

How to override the NQPMatch.Str function


... 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.


Solution

  • 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 !