PEG грамматика не работает, как ожидалось
Я работаю над грамматикой PEG, которая берет код на языке программирования музыки и создает дерево разбора музыкальных событий (ноты, аккорды, изменения громкости / темпа и т. Д.). Особенностью моего MPL является то, что он поддерживает голоса, то есть разные последовательности событий, происходящих одновременно. Мне трудно заставить мою Instaparse грамматику правильно разобрать это... то, что я хочу, это voices
тег, состоящий из одного или нескольких voice
s, каждый из которых состоит из определения голоса (например, 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
это событие, поэтому любое последующее voice
s были поглощены и смешаны в предыдущем voice
"s event
s. Потянув voices
из определения event
и переопределение part
быть переменным числом voices
группировки или event
s, я устранил неоднозначность и заставил грамматику вести себя так, как я хочу.
После этого 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