私が考案したExcelのような数式を解析するための文法を作成しようとしています。この場合、文字列の先頭の特殊文字は別のソースを示します。たとえば、$
は文字列を表すことができるため、「$This is text
」はプログラムでは文字列入力として扱われ&
、関数を表すことができるため&foo()
、内部関数の呼び出しとして扱うことができますfoo
。
私が直面している問題は、文法を適切に構築する方法です。たとえば、これはMWEとして簡略化されたバージョンです。
grammar = r'''start: instruction
?instruction: simple
| func
STARTSYMBOL: "!"|"#"|"$"|"&"|"~"
SINGLESTR: (LETTER+|DIGIT+|"_"|" ")*
simple: STARTSYMBOL [SINGLESTR] (WORDSEP SINGLESTR)*
ARGSEP: ",," // argument separator
WORDSEP: "," // word separator
CONDSEP: ";;" // condition separator
STAR: "*"
func: STARTSYMBOL SINGLESTR "(" [simple|func] (ARGSEP simple|func)* ")"
%import common.LETTER
%import common.WORD
%import common.DIGIT
%ignore ARGSEP
%ignore WORDSEP
'''
parser = lark.Lark(grammar, parser='earley')
だから、この文法で、のようなもの:$This is a string
、&foo()
、&foo(#arg1)
、&foo($arg1,,#arg2)
そして&foo(!w1,w2,w3,,!w4,w5,w6)
予想通り、すべて解析されます。しかし、simple
端末に柔軟性を追加したい場合は、SINGLESTR
トークンの定義をいじる必要があり、これは不便です。
私は何を試しましたか
(のリテラルであるfunc
)かっこを含む文字列が必要な場合、現在の状況ではそれらを処理できないということです。
- でかっこを追加すると、と表示され
SINGLESTR
ますExpected STARTSYMBOL
。これは、func
定義と混同されており、関数の引数を渡す必要があると考えられているためです。 - アンパサンド記号を関数のみに予約するように文法を再定義し、括弧をに追加すると、括弧で
SINGLESTR
文字列を解析できますが、解析しようとしているすべての関数はを与えExpected LPAR
ます。
私の意図は、で始まるものはすべてトークン$
として解析され、SINGLESTR
それからのようなものを解析できるようにすることです&foo($first arg (has) parentheses,,$second arg)
。
私の解決策は、今のところ、文字列でLEFTPARやRIGHTPARのような「エスケープ」ワードを使用し、ツリーを処理するときにそれらを括弧に変更するヘルパー関数を記述したことです。したがって、$This is a LEFTPARtestRIGHTPAR
正しいツリーが生成され、それを処理すると、これはに変換されThis is a (test)
ます。
一般的な質問を定式化するには:文法に特有の一部の文字が、状況によっては通常の文字として扱われ、それ以外の場合は特殊文字として扱われるように文法を定義できますか?
編集1
jbndlr
開始記号に基づいて個々のモードを作成するように文法を修正したコメントに基づいて、
grammar = r'''start: instruction
?instruction: simple
| func
SINGLESTR: (LETTER+|DIGIT+|"_"|" ") (LETTER+|DIGIT+|"_"|" "|"("|")")*
FUNCNAME: (LETTER+) (LETTER+|DIGIT+|"_")* // no parentheses allowed in the func name
DB: "!" SINGLESTR (WORDSEP SINGLESTR)*
TEXT: "$" SINGLESTR
MD: "#" SINGLESTR
simple: TEXT|DB|MD
ARGSEP: ",," // argument separator
WORDSEP: "," // word separator
CONDSEP: ";;" // condition separator
STAR: "*"
func: "&" FUNCNAME "(" [simple|func] (ARGSEP simple|func)* ")"
%import common.LETTER
%import common.WORD
%import common.DIGIT
%ignore ARGSEP
%ignore WORDSEP
'''
これは、私の2番目のテストケースに(やや)該当します。すべてのsimple
タイプのストリング(括弧を含めることができるTEXT、MD、またはDBトークン)と空の関数を解析できます。たとえば、&foo()
または&foo(&bar())
正しく解析します。関数内に引数を配置した瞬間(どの型であっても)、を取得しUnexpectedEOF Error: Expected ampersand, RPAR or ARGSEP
ます。概念の証明として、上記の新しい文法のSINGLESTRの定義から括弧を削除すると、すべてが正常に機能しますが、正方形に戻ります。
STARTSYMBOL
)を識別する文字があり、明確にするために必要な場所にセパレータと括弧を追加します。ここにはあいまいさはありません。それでもSTARTSYMBOL
、区別できるようにリストを個々のアイテムに分割する必要があります。