Perl6: Adding a sigil with Slangs

孤者浪人 提交于 2020-06-27 07:40:25

问题


I am trying to add «€» as an alias for the «$» scalar, and doing it with a Slang is the way to do it I think. But perl6.doc doesn't mention Slangs at all.

I have read the following:

  • https://perlgeek.de/en/article/mutable-grammar-for-perl-6 (from 2008)
  • https://mouq.github.io/slangs.html

And looked at the Slang::Roman and Slang::Tuxic modules.

The result is this file (ScalarEU.pm6):

use nqp;

unit module ScalarEU2;

sub EXPORT(|)
{
  my role Euscalar
  {
    token sigil:sym<$> { '€' | '$' }
  }

  my Mu $MAIN-grammar := nqp::atkey(%*LANG, 'MAIN');
  my $grammar := $MAIN-grammar.HOW.mixin($MAIN-grammar, Euscalar);

  $*LANG.define_slang('MAIN', $grammar, $*LANG.actions);

  {}
}

Then a program (called hello) using it:

use lib "lib";

use ScalarEU;

sub MAIN ($name)
{
  say "Hello, €name!";
}

But it doesn't work, or rather, doesn't do what it should:

$ ./hello Tom
Hello, €name!

(I wrote the program this way so that it doesn't crash.)

I haven't added an action class, but the way the "token sigil" is set up shouldn't require that? But that assumption is based on an 11 year article, and may be wrong.

Also, https://github.com/rakudo/rakudo/issues/2404 says that $*LANG is obsolete, and to use $?LANG instead. REPL agrees:

> $*LANG
Dynamic variable $*LANG not found

> $?LANG
(low-level object `Perl6::Grammar`)

But programs can use both, without errors. (I have tried.)


回答1:


Briefly: You must change $!target string of the ParseShared nqp object, this changes the code at parse time.

Why: The sigil token is not a proto anymore but defined rakudo/src/Perl6/Grammar.nqp as an alternation.

So as a minimal solution: token sigil { <[$@%&€]> } but then comes new problem: the returned value can be and is used in other grammar.

Where: So you must change $<sigil>.Str defined in nqp/src/QRegex/Cursor.nqp as:

method Str()       {
   $!pos >= $!from
        ?? nqp::substr(nqp::getattr_s($!shared, ParseShared, '$!target'),
            $!from, nqp::sub_i(self.to, $!from))
        !! '' }

<- Meaning The string in target between from and to if pos is not so low.

-> So we must change $!target between $!from and $!to in a NQPMatch.

Demo: Here is the code to embed in a slang grammar:

token sigil {
    | <[$@%&]>
    | <nogil> { say "Nogil returned: ", lk($/, 'nogil').Str; }
}

method nogil {
    # The object to return
    my $cursor := self.nogil-proxy;

    # Get from cursor
    my $shared := nqp::getattr($cursor, NQPMatch, '$!shared');
    my $from = nqp::getattr_i($cursor, NQPMatch, '$!from');
    my $target = $cursor.target;

    # Replace in target current character (€) by $
    $target.substr-rw($from, 1) = '$';

    # Set in cursor
    nqp::bindattr_s($shared, $shared.WHAT, '$!target', $target);

    # Return the "created by proxy" and modified NQPMatch
    return $cursor;
}

token nogil-proxy { '€' }

Speaking alone:_ It should work perfect in your case. In mine, (no sigil) I still have problem because the size changes during the $!target modification messes the to and from of other cursors. In which case:

  1. I must overwrite NQPMatch.Str function (hoping it is possible).
  2. I must list cursors (if possible) and change their $!from and $!to attribute wisely to restore peace in the galaxy or at least in the client code.


来源:https://stackoverflow.com/questions/55079169/perl6-adding-a-sigil-with-slangs

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!