私は、音楽プログラミング言語のコードを取り、音楽イベント (音符、和音、音量/テンポの変更など) の解析ツリーを作成する PEG 文法に取り組んでいます。私の MPL の特徴は、音声、つまり同時に発生するさまざまなイベント シーケンスをサポートすることです。Instaparse文法でこれを正しく解析するのに苦労しています... 私が欲しいのは、 voices
1 つ以上voice
の で構成されるタグで、それぞれが音声定義 (例: V1:
) と任意の数のイベントで構成されます。voices
タグは、(V0:
分割されたボイスの終わりを意味し、1 つのボイスまたは「ボイス ゼロ」に戻ることを意味します)、またはファイルの末尾で終了する必要があります。
以下は進行中の文法の抜粋です (わかりやすくするために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
タグのいずれかが想定どおりに終了せず、2 番目のタグが最初の のvoice
一部として飲み込まれます。また、2 番目のタグを付けたくありません。2 はプライマリタグ内にある必要があります。voice
voice-events
voices
voice
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
の はすべて消費され、前voice
のにまとめられていましたevent
。anvoices
の定義を取り除き、可変数のグループ化またはs になるようevent
に再定義することで、あいまいさを排除し、文法が希望どおりに動作するようにしました。part
voices
event
その後、events
内のは適切にグループ化されましたが、すべてのボイスを同じグループ内に配置する必要がある場合、voice
各ボイスが個別のタグ内にあることにまだ問題がありました。タグが aまたはファイルの末尾 ( ) で終わるように指定することで、これを修正しました。つまり、タグが消費するコードの量をより具体的に指定します。voices
voices
voices
"V0:"
\z
voices
この話の教訓は、もしあなたが PEG 文法を書いていて問題を抱えているなら、おそらく定義があいまいでないようにする必要があるということです! また、否定的な先読みをまったく使用しないことになりました。これは、文法を単純化/曖昧さを解消するのに大いに役立ったと思います。