Как мне получить доступ к необязательным частям грамматики в perl6? - PullRequest
6 голосов
/ 11 июля 2019

Как часть моей грамматики, у меня есть:

        rule EX1        { <EX2> ( '/' <EX2>)*  }

В своем классе действий я написал:

    method EX1($/) {
            my @ex2s = map *.made,  $/.<EX2>;
            my $ex1 = @ex2s.join('|');
            #say "EX1 making $ex1";
            $/.make($ex1);
    }

Итак, я просто пытаюсь объединить все EX2 вместе с '|' между ними вместо '/'. Однако что-то не так с моим кодом, так как он принимает только первые EX2, а не последующие. Как мне узнать, какие из них являются дополнительными?

Ответы [ 2 ]

10 голосов
/ 12 июля 2019

TL; DR Ваш метод действия сработает, если ваш rule создаст структуру данных, которую ожидает метод. Поэтому мы исправим rule и оставим метод в покое.

Основная проблема

Давайте предположим, что правило EX1 вставлено в рабочую грамматику; строка была успешно проанализирована; подстрока ex2/ex2/ex2 соответствует правилу EX1; и мы отобразили соответствующую часть дерева разбора (просто say используя результаты .parse с использованием грамматики):

EX1 => 「ex2/ex2/ex2」
 EX2 => 「ex2」
 0 => 「/ex2」
  EX2 => 「ex2」
 0 => 「/ex2」
  EX2 => 「ex2」

Обратите внимание на посторонние 0 => снимки и то, как вторые и третьи EX2 смещены под ними и смещены относительно первого EX2. Это неправильная структура вложений относительно предположений вашего метода.

Решение Брэда главной проблемы

Как отметил Брэд ++ в своем комментарии в ответ на первую версию этого ответа, вы можете просто переключиться с конструкции, которая группирует и захватывает ((...)), на конструкцию, которая только группирует ([...]).

    rule EX1        { <EX2> [ '/' <EX2>]*  }

Теперь соответствующий фрагмент дерева разбора для той же входной строки, как указано выше:

EX1 => 「ex2/ex2/ex2」
 EX2 => 「ex2」
 EX2 => 「ex2」
 EX2 => 「ex2」

Захваты 0 исчезли, а EX2 теперь все братья и сестры. Для дальнейшего обсуждения того, когда и почему P6 вложений фиксирует то, как оно происходит, см. ответ jnthn на Почему / как ... захват групп? .

Ваш метод действия теперь должен работать - для некоторых входов ...

Решение Хокон другой вероятной проблемы

Если решение Брэда работает для некоторых входных данных, для которых вы ожидаете, что оно будет работать, но не для всех, вероятно, часть проблемы заключается в том, как ваш rule соответствует между <EX2> и символом /.

Как отметил Хакон ++ в своем ответе, у вашего rule есть интервалы, которые, вероятно, не соответствуют вашим ожиданиям.

Если вы не хотите, чтобы интервал в вашем шаблоне был значительным, не используйте rule. В token или regex все пробелы в шаблоне (игнорирование внутри строки, например, ' ') просто для того, чтобы сделать ваш шаблон более читабельным и не имеет смысла по отношению к любой входной строке, которая сопоставляется , В случае сомнений используйте token (или regex), а не rule:

token EX1 { <EX2> ( '/' <EX2>)* }
           ?    ? ?   ?      ?  ?

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

Напротив, весь смысл конструкции rule состоит в том, что пробел после каждого атома и каждого квантификатора в шаблоне значим . Такой интервал неявно применяет (переопределенное пользователем) правило сопоставления границ (по умолчанию правило, разрешающее пробелы и / или переход между символами «слово» и не «слово») после соответствующей подстроки во входных данных.

В вашем правиле EX1, которое я повторяю ниже с преувеличенным интервалом для обеспечения ясности, некоторые интервалы не значимы, так же, как это не в token или regex :

     rule EX1        {  <EX2>   (  '/'  <EX2>)*   }
                      ?          ?                 ?

Как и раньше, ? указывает на несущественный интервал - вы можете его опустить или расширить, и это не будет иметь значения. Следует помнить, что пробелы в начале шаблона (или подшаблона) предназначены только для удобства чтения. (Опыт использования показал, что было бы намного лучше, если бы какой-либо интервал не считался значимым.)

Но интервал или отсутствие интервала после атома или квантификатора значим:

This spacing is significant: ⮟      ⮟        ⮟
     rule EX1        { <EX2>   ( '/'  <EX2>)*   }
This LACK of spacing is significant:      ⮝⮝

Путем написания rule, как вы сделали, вы указываете P6, что нужно сопоставлять ввод только с сопоставлением границ (которое по умолчанию допускает пропуски):

  • после первый <EX2> (и, следовательно, до * первый /);

  • между / и последующими <EX2> совпадениями;

  • после последнего последнего <EX2> матча.

Итак, ваше правило говорит P6 разрешить пробелы между / и <EX2> совпадать с , когда они встречаются в таком порядке - /, затем <EX2>.

Ноон также указывает P6 не разрешать пробелы наоборот - между <EX2> соответствием и / совпадением в , что в порядке!За исключением самой первой пары <EX2> '/'!P6 позволит вам объявлять шаблоны соответствия произвольной сложности, включая интервалы, но я сомневаюсь, что это то, что вы имели в виду или хотите.

Для полного перечисления того, что означает «после атома» (то есть, когда пробел в rule s имеет значение) см. Когда пробелы действительно важны в грамматиках Perl6? .

Эта важная особенность интервалов:

  • Classic Perl DWIMery предназначен для облегчения жизни;

  • Идиоматический - используется в большинстве грамматик, потому что действительно действительно облегчает жизнь;

  • Единственная причина, по которой существует декларатор rule (этот существенный аспект - только разница между rule и token);

  • Полностью необязательный, потому что вместо него можно использовать token.

Если кто-то, читающий это, думает, что он предпочел бы не использовать эту важную функцию пространства, онвместо этого можно использовать token s.(Это, в свою очередь, вероятно, заставит их понять, почему rule существует как опция, а затем, или, возможно, позже, понять, почему он работает так, как работает, и заново оценить его DWIMery. :))

Встроенная конструкция для сопоставляемого вами шаблона

Наконец, вот идиоматический способ написать шаблон, который вы пытаетесь сопоставить:

rule EX1        { <EX2> + % '/' }

Это говорит P6 о соответствииодин или несколько <EX2> с, разделенных / символами.См. Модифицированный квантификатор: %, %% для объяснения этой хорошей конструкции.

Это все еще rule, поэтому большая часть расстояния вэто остается значительным. Точные детали того, когда он есть и нет , по-видимому, наиболее вероятны для этой конструкции, потому что он имеет до трех значительных распорок, а одна - нет:

NOT significant:  ⮟                 ⮟
     rule EX1   {   <EX2>    +    %    '/'   }
Significant:              ⮝    ⮝          ⮝

Включая интервалыи до и после + с избыточностью:

     rule EX1   {   <EX2>    +    %    '/'   }
     rule EX1   {   <EX2>    +%        '/'   } # same match result
     rule EX1   {   <EX2>+        %    '/'   } # same match result
4 голосов
/ 11 июля 2019

Пробел значительный в rule с .Поэтому я думаю, что вам не хватает пробела после последнего <EX2>:

rule EX1 { <EX2> ( '/' <EX2>)+  }

Это должно быть:

rule EX1 { <EX2> ( '/' <EX2> )+  }

Это позволяет разделить термины в EX1.

...