3

我正在研究一种 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标签内独立。

但是,当我将 更改为V0V2,我得到了这个:

[: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"]]]]]]]]]

出于某种原因,voice1 标签或其voice-events标签没有像预期的那样终止,第二个标签作为第一个标签的voice一部分被吞没。我也不希望有第二个标签;2 应该在主标签内。voicevoice-eventsvoicesvoicevoices

我想要的是这样的:

[: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标签有关。这可能与我如何使用负前瞻有关,我认为我还没有完全理解。谁能弄清楚我如何修复我的语法?

谢谢!:)

解决了!

谢谢,@丹尼尔尼尔!我已经重新编写了我的语法,这正是我想要的方式:

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 都被消耗并归入前一个voicesevent中。通过voices退出 an 的定义event并重新定义part为可变数量的voices分组或events,我消除了歧义并让语法按照我想要的方式运行。

之后,其中的eventsa被正确分组,但是当我需要它们都在同一个分组中时voice,我仍然遇到每个声音都在其自己的单独标签中的问题。我通过指定标签以 a或文件结尾 ( ) 来解决此问题,换句话说,更具体地说明了我希望标签使用多少代码。voicesvoicesvoices"V0:"\zvoices

这个故事的寓意是,如果你正在编写一个 PEG 语法并且你遇到了问题,你可能需要让你的定义不那么模棱两可!我最终也完全没有使用否定前瞻,我认为这对简化/消除我的语法有很大帮助。

4

1 回答 1

2

我认为你是对的 - 这是导致问题的负面预测。如果没有您的完整语法,我将无法正确测试,但是这一行:

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
于 2014-03-31T11:50:55.063 回答