Saya mencoba membuat tata bahasa untuk mem-parsing beberapa rumus mirip-Excel yang telah saya buat, di mana karakter khusus di awal string menandakan sumber yang berbeda. Sebagai contoh, $
dapat menandakan sebuah string, sehingga " $This is text
" akan diperlakukan sebagai input string dalam program dan &
dapat menandakan suatu fungsi, sehingga &foo()
dapat diperlakukan sebagai panggilan ke fungsi internal foo
.
Masalah yang saya hadapi adalah bagaimana membangun tata bahasa dengan benar. Misalnya, ini adalah versi yang disederhanakan sebagai 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')
Jadi, dengan tata bahasa ini, hal-hal seperti: $This is a string
, &foo()
, &foo(#arg1)
, &foo($arg1,,#arg2)
dan &foo(!w1,w2,w3,,!w4,w5,w6)
semua diurai seperti yang diharapkan. Tetapi jika saya ingin menambahkan lebih banyak fleksibilitas ke simple
terminal saya , maka saya perlu mulai mengutak-atik SINGLESTR
definisi token yang tidak nyaman.
Apa yang sudah saya coba
Bagian yang tidak bisa saya lewati adalah jika saya ingin memiliki string termasuk tanda kurung (yang merupakan literal dari func
), maka saya tidak dapat menangani mereka dalam situasi saya saat ini.
- Jika saya menambahkan tanda kurung di
SINGLESTR
, maka saya mendapatkanExpected STARTSYMBOL
, karena semakin dicampur denganfunc
definisi dan berpikir bahwa argumen fungsi harus dilewati, yang masuk akal. - Jika saya mendefinisikan kembali tata bahasa untuk cadangan simbol ampersand hanya untuk fungsi dan menambahkan tanda kurung
SINGLESTR
, maka saya dapat mengurai string dengan tanda kurung, tetapi setiap fungsi yang saya coba parsing memberiExpected LPAR
.
Maksud saya adalah bahwa apa pun yang dimulai dengan a $
akan diurai sebagai SINGLESTR
token dan kemudian saya bisa menguraikan hal-hal seperti &foo($first arg (has) parentheses,,$second arg)
.
Solusi saya, untuk saat ini, adalah saya menggunakan kata 'escape' seperti LEFTPAR dan RIGHTPAR di string saya dan saya telah menulis fungsi pembantu untuk mengubahnya menjadi tanda kurung ketika saya memproses pohon. Jadi, $This is a LEFTPARtestRIGHTPAR
hasilkan pohon yang benar dan ketika saya memprosesnya, maka ini diterjemahkan This is a (test)
.
Untuk merumuskan pertanyaan umum: Dapatkah saya mendefinisikan tata bahasa saya sedemikian rupa sehingga beberapa karakter yang khusus untuk tata bahasa diperlakukan sebagai karakter normal dalam beberapa situasi dan sebagai istimewa dalam kasus lain?
EDIT 1
Berdasarkan komentar dari jbndlr
saya merevisi tata bahasa saya untuk membuat mode individual berdasarkan simbol awal:
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
'''
Ini jatuh (agak) di bawah test case kedua saya. Saya bisa menguraikan semua simple
jenis string (TEXT, MD atau token DB yang dapat berisi tanda kurung) dan fungsi yang kosong; misalnya, &foo()
atau &foo(&bar())
parse dengan benar. Saat saya menempatkan argumen dalam suatu fungsi (tidak peduli jenis apa), saya mendapatkan UnexpectedEOF Error: Expected ampersand, RPAR or ARGSEP
. Sebagai bukti konsep, jika saya menghapus tanda kurung dari definisi SINGLESTR di tata bahasa baru di atas, maka semuanya berfungsi sebagaimana mestinya, tapi saya kembali ke titik awal.
STARTSYMBOL
) dan Anda menambahkan pemisah dan tanda kurung di mana harus jelas; Saya tidak melihat ambiguitas di sini. Anda masih harus membagiSTARTSYMBOL
daftar menjadi beberapa item agar dapat dibedakan.