PEG грамматика не работает, как ожидалось

Я работаю над грамматикой PEG, которая берет код на языке программирования музыки и создает дерево разбора музыкальных событий (ноты, аккорды, изменения громкости / темпа и т. Д.). Особенностью моего MPL является то, что он поддерживает голоса, то есть разные последовательности событий, происходящих одновременно. Мне трудно заставить мою Instaparse грамматику правильно разобрать это... то, что я хочу, это voices тег, состоящий из одного или нескольких voices, каждый из которых состоит из определения голоса (например, V1:), а затем любое количество событий. voices тег должен заканчиваться либо V0: (что означает конец разделенных голосов, и мы возвращаемся только к одному голосу, или "нулевому голосу"), или конец файла.

Вот выдержка из моей грамматики в процессе (я опускаю определения note, chordи т. д. для наглядности)

part                    = <ows> event+
<event>                 = chord | note | rest | octave-change |
                          attribute-change | voices |
                          marker | at-marker

voices                  = voice+ 
voice                   = !voices voice-number voice-events? 
                          (<voice-zero> | #"\z")
voice-number            = <"V"> #"[1-9]\d*" <":"> <ows>
<voice-zero>            = <"V0:"> <ows>
voice-events            = !voices event+ 

...

ows                     = #"\s*"

Учитывая следующий код:

V1: o2 b1/>b o2 g+/>g+ o2 g/>g 
V0: e8 f+ g+ a b2

Запуск парсера дает следующий вывод:

[:part 
  [:voices 
    [:voice [:voice-number "1"] 
            [:voice-events 
              [:octave-change "2"] [:chord [:note [:pitch "b"] 
              [:duration "1"]] [:octave-change ">"] [:note [:pitch "b"]]] 
              [:octave-change "2"] [:chord [:note [:pitch "g+"]] 
              [:octave-change ">"] [:note [:pitch "g+"]]] 
              [:octave-change "2"] [:chord [:note [:pitch "g"]]
              [:octave-change ">"] [:note [:pitch "g"]]]]]] 
  [:note [:pitch "e"] [:duration "8"]] 
  [:note [:pitch "f+"]] 
  [:note [:pitch "g+"]] 
  [:note [:pitch "a"]] 
  [:note [:pitch "b"] [:duration "2"]]]

Что именно то, что я хочу. V0: сигнализирует конец voices тег, и последние 5 заметок сами по себе в part тег.

Тем не менее, когда я меняю V0 в V2Я получаю это:

[:part 
  [:voices 
    [:voice [:voice-number "1"] 
            [:voice-events 
              [:octave-change "2"] [:chord [:note [:pitch "b"] [:duration "1"]] 
              [:octave-change ">"] [:note [:pitch "b"]]] [:octave-change "2"] 
              [:chord [:note [:pitch "g+"]] [:octave-change ">"] 
              [:note [:pitch "g+"]]] [:octave-change "2"] 
              [:chord [:note [:pitch "g"]] [:octave-change ">"] 
              [:note [:pitch "g"]]] 
              [:voices 
                [:voice [:voice-number "2"] 
                [:voice-events 
                  [:note [:pitch "e"] [:duration "8"]] [:note [:pitch "f+"]] 
                  [:note [:pitch "g+"]] [:note [:pitch "a"]] 
                  [:note [:pitch "b"] [:duration "2"]]]]]]]]]

По какой-то причине либо voice 1 тег или его voice-events тег не заканчивается, как это должно быть, а второй voice проглочен как часть первого voice"s voice-events, Я тоже не хочу там быть вторым voices тег; voice 2 должно быть в пределах основного voices тег.

Что я хочу это:

[:part 
  [:voices 
    [:voice [:voice-number "1"] 
            [:voice-events 
              [:octave-change "2"] [:chord [:note [:pitch "b"] [:duration "1"]] 
              [:octave-change ">"] [:note [:pitch "b"]]] [:octave-change "2"] 
              [:chord [:note [:pitch "g+"]] [:octave-change ">"] 
              [:note [:pitch "g+"]]] [:octave-change "2"] 
              [:chord [:note [:pitch "g"]] [:octave-change ">"] 
              [:note [:pitch "g"]]]]]
    [:voice [:voice-number "2"] 
            [:voice-events 
              [:note [:pitch "e"] [:duration "8"]] [:note [:pitch "f+"]] 
              [:note [:pitch "g+"]] [:note [:pitch "a"]] 
              [:note [:pitch "b"] [:duration "2"]]]]]]

Я не могу понять, что я делаю неправильно, но я думаю, что это как-то связано с тем, как я определяю voice тег и / или voice-events тег. Это может иметь какое-то отношение к тому, как я использую негативные взгляды, что, я думаю, я еще не до конца понимаю. Кто-нибудь может понять, как я могу исправить мою грамматику?

Спасибо!:)

Решено!

Спасибо, @DanielNeal! Я переработал мою грамматику к этому, который работает именно так, как я хочу:

part                    = <ows> (voices | event)+
<event>                 = chord | note | rest | octave-change |
                          attribute-change | marker | at-marker

voices                  = voice+ (<voice-zero> | <#"\z">)
voice                   = voice-number event*
voice-number            = <"V"> #"[1-9]\d*" <":"> <ows>
<voice-zero>            = <"V0:"> <ows>

...

ows                     = #"\s*"

Большое изменение было в том, как я определил part а также event; раньше я определил эти термины так, чтобы voices это событие, поэтому любое последующее voices были поглощены и смешаны в предыдущем voice"s events. Потянув voices из определения event и переопределение part быть переменным числом voices группировки или events, я устранил неоднозначность и заставил грамматику вести себя так, как я хочу.

После этого events в пределах voice были сгруппированы правильно, но у меня все еще была проблема с каждым голосом, находящимся в его отдельном voices тег, когда мне нужно, чтобы они все были в пределах voices группировка. Я исправил это, указав, что voices тег заканчивается либо "V0:" или конец файла (\zдругими словами, более конкретно о том, сколько кода я хочу voices тег потреблять.

Мораль этой истории в том, что если вы пишете грамматику PEG и у вас возникают проблемы, вам, вероятно, нужно сделать свои определения менее двусмысленными! Кроме того, я вообще не использовал отрицательный прогноз, что, как мне кажется, очень помогло упростить / устранить неоднозначность моей грамматики.

1 ответ

Решение

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

voice-events = !voices event+ 

Представляет что-то, что не соответствует voices сопровождаемый одним или несколькими events,

Я предполагаю что voice-events не должен содержать voices внутри него рекурсивно, но в настоящий момент - косвенно. каждыйevent может содержать voices в нем, и в свою очередь, voice-events может содержать events,

В приведенном выше примере первое событие в V1 - это сдвиг октавы (что соответствует условию отсутствия голоса). Это позволяет последующему голосу, который происходит, потребляться в пределах event определение. Если это имеет смысл.

Чтобы это исправить, вы можете (возможно) определить это наоборот:

voice-event = chord | note | rest | octave-change | attribute-change | marker | at-marker
event       = voice-event | voices
Другие вопросы по тегам