2017-01-20 20 views
6

Вот две грамматики. Один использует proto token, а другой нет. Они оба делают то же самое. Это в основном примеры в S05 under "Variable (non-)interpolation". В этом простом примере они оба могут делать одни и те же вещи.Зачем вам использовать правило proto в грамматике Perl 6?

Какие ситуации оправдывают всю дополнительную напечатку? Токены proto имеют различные методы в классе действий, и, возможно, там небольшое преимущество. Однако вам нужно набрать некоторые дополнительные материалы, чтобы получить эту выгоду.

Есть ли какая-то особенность proto, которая упрощает другие части грамматики?

grammar NoProto { 
    token variable { <sigil> <identifier> } 

    token identifier { <ident>+ } 
    token sigil  { < $ @ % & :: > } 
    } 

grammar YesProto { 
      token variable  { <sigil> <identifier> } 

      token identifier { <ident>+ } 
    proto token sigil   { *  } 
      token sigil:sym<$> { <sym> } 
      token sigil:sym<@> { <sym> } 
      token sigil:sym<%> { <sym> } 
      token sigil:sym<&> { <sym> } 
      token sigil:sym<::> { <sym> } 
    } 

class Proto::Actions { 
    method variable ($/) { 
     say "found variable: " ~ $/; 
     } 
    method identifier ($/) { 
     say "found identifier: " ~ $/; 
     } 
    method sigil ($/) { 
     say "found sigil: " ~ $/; 
     } 
    method sigil:sym<$> ($/) { 
     say "found sym sigil: " ~ $/; 
     } 
    } 

my $variable = '$butterfuly'; 

say "------No proto parsing"; 
my $no_proto_match = NoProto.parse(
    $variable, 
    :rule<variable>, 
    :actions(Proto::Actions), 
    ); 

say "------Yes proto parsing"; 
my $yes_proto_match = YesProto.parse(
    $variable, 
    :rule<variable>, 
    :actions(Proto::Actions), 
    ); 

Выходные данные показывают, что proto вызывает другой метод в классе действий:

------No proto parsing 
found sigil: $ 
found identifier: butterfuly 
found variable: $butterfuly 
------Yes proto parsing 
found sym sigil: $ 
found identifier: butterfuly 
found variable: $butterfuly 

ответ

7

Технически proto будет сделано для вас, если вы не задаете сами. Он в основном создает обработчик отправки нескольких методов для этого конкретного token (точно так же, как и для sub и method). Что вам обычно не нужно заботиться.

Почему бы вам не указать proto? Я могу думать по ряду причины:

  1. потому что вы хотите token s поделиться некоторыми чертами
  2. потому, что вы хотите, чтобы выполнить код до или после отправки

Да, { * } может содержать исполняемый код. Голый Whatever указывает на отправку соответствующему кандидату. Показано это в более простой ситуации с sub:

proto a(|) { say "before"; {*}; say "after" } 
multi a(Int) { say "Int" } 
multi a(Str) { say "Str" } 
a 42; a "42" 

показывает:

before 
Int 
after 
before 
Str 
after 

Надеется, что это помогает :-)

+0

А, ладно. Но почему не вызвал метод 'sigil' в классе действий? Я вижу использование этого вне грамматик, но я пытаюсь придумать для него случай внутри грамматик. Примеры, которые я видел, не были сложными, или они были грузовым культом. –

6

Имея метод, который он вызывает в классе действия может быть полезны для отделяя логику. Это в основном такая же идея, как и многоточие, кроме грамматик.

(I wrote this для оценки ответов на Code Golf)

grammar Mathemania { 
    token TOP    { <cmd-list>   } 

    token cmd-list   { <cmd>+    } 

    token cmd    { <op> <argument>? } 

    proto token op   { * } 
    token op:sym<exp>  { e } # notice that the name doesn't have to match 
    token op:sym<factorial> { f } 
    token op:sym<root>  { r } 
    token op:sym<ceil>  { c } 
    token op:sym<floor>  { l } 

    token argument   { '(' ~ ')' <cmd-list> } 
} 

class Calculate { 
    method TOP  ($/) { make $<cmd-list>.made } 
    method argument ($/) { make $<cmd-list>.made } 

    method cmd-list ($/) { 
    my $result = 2; 

    $result = .made.($result).narrow for @<cmd>; 

    make $result; 
    } 

    method cmd ($/) { 
    if $<argument> { 
     make $<op>.made.assuming(*, $<argument>.made); 
    } else { 
     make $<op>.made; 
    } 
    } 

    method op:sym<exp>  ($/) { make -> \n, \e = 2 { n ** e     } } 
    method op:sym<factorial> ($/) { make -> \n, \k = 2 { [*] n, n - k + 1 ...^ 0 } } 
    method op:sym<root>  ($/) { make -> \n, \r = 2 { n ** (1/r)    } } 
    method op:sym<ceil>  ($/) { make &ceiling } 
    method op:sym<floor>  ($/) { make &floor } 

} 

Это также делает его так, что подклассы грамматика может добавить свои маркеры вдоль стороны тех, которые уже существуют, и класс подклассов действия могут сделать одна и та же. (try it)

grammar Mathmania-Plus is Mathemania { 
    token op:sym<negate> { n } 
    token op:sym<abs> { a } 
} 

class Calculate-Plus is Calculate { 
    method op:sym<negate> ($/) { make &prefix:<-> } 
    method op:sym<abs> ($/) { make &abs } 
} 
+1

Итак, для суб (грамматика | класс) вам не нужно переопределять или расширять методы. Это хороший материал. –

5

Одним из преимуществ разделения ваших альтернатив в прото и MULTIS является то, что вы можете продлить его более надежно. Вы можете добавить multis к существующему прото в грамматике, которая наследует грамматику, объявляющую прото, и вам не нужно перечислять все возможные альтернативы (которые вам нужно было бы сделать в случае одного правила).

Это означает, что вы можете даже иметь несколько независимых расширений для одной и той же грамматики, например, путем смешивания нескольких правил, которые снабжают multis для обозначения разных символов.

Это в основном механизм, который использует сам Perl 6 при определении пользовательского оператора: существуют правила для сопоставления различных типов операторов (например, инфикс, префикс, постфикс, ...) и объявление нового оператора новая грамматика из текущей активной, с добавлением нескольких кандидатов для нового оператора. Сценарий может импортировать операторы из нескольких модулей, которые не знают друг о друге благодаря расширяемости механизма проточных токенов.