Извлечение токенов из грамматики - PullRequest
0 голосов
/ 10 декабря 2018

В этом году я работал над проблемами Advent of Code в Perl6 и пытался использовать грамматику для анализа входных данных третьего дня.

Учитывая ввод в этой форме: #1 @ 1,3: 4x4 и эту грамматику, которую я создал:

grammar Claim {
  token TOP {
    '#' <id> \s* '@' \s* <coordinates> ':' \s* <dimensions>
  }

  token digits {
    <digit>+
  }

  token id {
    <digits>
  }

  token coordinates {
    <digits> ',' <digits>
  }

  token dimensions {
    <digits> 'x' <digits>
  }
}

say Claim.parse('#1 @ 1,3: 4x4');

Я заинтересован в извлечении фактических токенов, которые были сопоставлены, т.е. id, x + y из координати высота + ширина от размеров из полученного анализа.Я понимаю, что могу вытащить их из полученного Match объекта Claim.parse(<input>), но мне нужно копаться в каждом грамматическом произведении, чтобы получить нужное мне значение, например

say $match<id>.hash<digits>.<digit>;

, это выглядит немного грязно, Есть ли способ лучше?

Ответы [ 2 ]

0 голосов
/ 11 декабря 2018

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

Как говорит @Scimon, одно регулярное выражение было бы хорошо.Вы можете сделать его хорошо читаемым, правильно выложив его.Вы можете назвать снимки и сохранить их все на верхнем уровне:

/ ^
  '#' $<id>=(\d+) ' '
  '@ ' $<x>=(\d+) ',' $<y>=(\d+)
  ': ' $<w>=(\d+)  x  $<d>=(\d+)
  $
/;

say ~$<id x y w d>; # 1 1 3 4 4

(Префикс ~ вызывает .Str для значения в правой части. Вызывается для объекта Match.приравнивает к соответствующим строкам.)

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

Копать менее беспорядочно

say $match<id>.hash<digits>.<digit>; # [「1」]

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

Ваш say содержит ненужный код и вложенность вывода.Вы могли бы просто упростить что-то вроде:

say ~$match<id> # 1

Копать немного глубже, менее беспорядочно

Я заинтересован в извлечении реальных токенов, которые были сопоставлены, т.е. id, x + y изкоординаты и высота + ширина из измерений из полученного анализа.

Для совпадений с множественными токенами вы больше не можете позволить себе роскошь полагаться на Perl 6, угадывая, какой из них вы имеете в виду,(Когда есть только один, угадайте, какой из них, по вашему мнению, вы имеете в виду.:))

Один способ написать say, чтобы получить y координату:

say ~$match<coordinates><digits>[1] # 3

Если выЕсли вы хотите удалить <digits>, вы можете отметить, какие части шаблона должны быть сохранены в списке пронумерованных снимков.Один из способов сделать это - заключить скобки в эти части:

token coordinates { (<digits>) ',' (<digits>) }

Теперь вы устранили необходимость упоминания <digits>:

say ~$match<coordinates>[1] # 3

Вы также можете назвать новыйЗахваты в скобках:

token coordinates { $<x>=(<digits>) ',' $<y>=(<digits>) }

say ~$match<coordinates><y> # 3

Предварительное копание

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

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

Еще один шаг - сделать работу по копанию как часть процесса разбора, чтобы say был простым.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * Мог бы мог бы встроить некоторый код прямо в TOP токен, чтобы сохранить только интересные данные, которые Вы сделали.Просто вставьте {...} блок в соответствующее место (для такого рода вещей это означает конец токена, если вы хотите, чтобы шаблон токена уже выполнил соответствующую работу):

my $made;
grammar Claim {
  token TOP {
    '#' <id> \s* '@' \s* <coordinates> ':' \s* <dimensions>
     { $made = ~($<id>, $<coordinatess><x y>, $<dimensions><digits>[0,1]) }
  }
...

Сейчасвы можете написать просто:

say $made # 1 1 3 4 4

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

Предварительное копание менее беспорядочно

Встраивание кода быстрое и грязное.Так же используется переменная.

Для хранения данных обычно нужно использовать функцию make.Это приводит к зависанию данных от строящегося объекта соответствия, соответствующего заданному правилу.Затем его можно получить с помощью метода .made.Таким образом, вместо $make = у вас будет:

{ make ~($<id>, $<coordinatess><x y>, $<dimensions><digits>[0,1]) }

А теперь вы можете написать:

say $match.made # 1 1 3 4 4

Это намного аккуратнее.Но это еще не все.

Разреженное поддерево дерева разбора

.oO (? В первый день предполагаемого 2019 календаря рождественских приключений Perl 6 title заголовок StackOverflowсказал мне ...)

В приведенном выше примере я построил полезную нагрузку .made только для узла TOP.Для больших грамматик обычно образуется разреженное поддерево (термин, который я придумал для этого, потому что не смог найти стандартный существующий термин).

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

Канонический вариант использования для этого - сформировать Абстрактное синтаксическое дерево после анализа некоторого программного кода.

На самом деле есть псевдоним для .made, а именно .ast:

say $match.ast # 1 1 3 4 4

Хотя это тривиально, но оно также носит общий характер.P6 использует грамматику P6 для анализа кода P6, а затем создает AST, используя этот механизм.

Делая все это элегантным

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

В итоге

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

Добавьте круглые скобки, как я объяснил выше, назвав перехват, на котором эти круглые скобки сосредоточены, если это хорошее упрощение для копания в дереве разбора.

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

Oneпоследняя вещьВозможно, вы захотите сократить дерево разбора, чтобы исключить ненужные детали листа (чтобы уменьшить потребление памяти и / или упростить отображение дерева разбора).Для этого напишите <.foo> с точкой перед именем правила, чтобы отключить автоматический захват по умолчанию с для этого правила.

0 голосов
/ 10 декабря 2018

Вы можете обратиться к каждой из названных вами частей напрямую.Таким образом, чтобы получить кординаты, вы можете получить доступ к:

say $match.<coordinates>.<digits>

. Это вернет вам массив из digits совпадений.Если вам просто нужны значения, возможно, самый простой способ:

say $match.<coordinates>.<digits>.map( *.Int) или say $match.<coordinates>.<digits>>>.Int или даже say $match.<coordinates>.<digits>».Int

для приведения их к Int s

Для поля id еще проще, вы можете просто привести соответствие <id> к Int:

say $match.<id>.Int

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...