Bir dize başlangıcında özel bir karakter farklı bir kaynak anlamına gelir, tasarladığım bazı Excel benzeri formülleri ayrıştırmak için bir dilbilgisi oluşturmaya çalışıyorum. Örneğin, $
bir dizeyi ifade edebilir, böylece " $This is text
" programda dize girişi olarak kabul edilir ve&
bir işlevi ifade edebilir, bu nedenle &foo()
dahili işleve çağrı olarak değerlendirilebilir foo
.
Karşılaştığım sorun dilbilgisinin nasıl düzgün bir şekilde oluşturulacağı. Örneğin, Bu bir MWE olarak basitleştirilmiş bir sürümdür:
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')
Yani, bu dilbilgisi ile, gibi şeyler: $This is a string
, &foo()
, &foo(#arg1)
, &foo($arg1,,#arg2)
ve &foo(!w1,w2,w3,,!w4,w5,w6)
beklendiği gibi tüm ayrıştırıldı. Ama simple
terminalime daha fazla esneklik eklemek istersem SINGLESTR
, uygun olmayan jeton tanımıyla uğraşmaya başlamam gerekiyor .
Ne denedim
Geçmişi alamıyorum kısmı (parantezler dahil) parantez içeren bir dize sahip olmak istiyorsanız func
, o zaman geçerli durumumda bunları işleyemezsiniz.
- Parantezleri eklersem
SINGLESTR
, o zaman alıyorumExpected STARTSYMBOL
, çünküfunc
tanımla karışıyor ve bir işlev argümanının geçirilmesi gerektiğini düşünüyor, bu da mantıklı. - Ve işareti sadece işlevler için ayırmak ve parantez eklemek için dilbilgisi yeniden tanımlamak
SINGLESTR
, o zaman parantez ile bir dize ayrıştırabilir, ancak ayrıştırmaya çalıştığım her işlev verirExpected LPAR
.
Amacım bir ile başlayan her şeyin $
bir SINGLESTR
jeton olarak ayrıştırılması ve sonra gibi şeyler ayrıştırmak olabilir &foo($first arg (has) parentheses,,$second arg)
.
Benim çözümüm, şimdilik, dizelerimde LEFTPAR ve RIGHTPAR gibi 'escape' kelimeleri kullanıyorum ve ağacı işlediğimde bunları parantez haline getirmek için yardımcı işlevler yazdım. Yani,$This is a LEFTPARtestRIGHTPAR
doğru ağacı üretir ve işlediğimde, bu tercüme edilir This is a (test)
.
Genel bir soru formüle etmek için: Dilbilgimi, dilbilgisine özel bazı karakterlerin bazı durumlarda normal karakterler ve diğer durumlarda özel olarak ele alınacak şekilde tanımlayabilir miyim?
DÜZENLEME 1
jbndlr
Başlangıç yorumuna dayanarak tek tek modlar oluşturmak için dilbilgimi gözden geçirdim:
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
'''
Bu (biraz) ikinci test durumumun altına düşüyor. Tüm simple
dizeleri (metin, parantez içeren MD veya DB belirteçleri) ve boş işlevleri ayrıştırabilir ; örneğin &foo()
veya &foo(&bar())
doğru ayrıştırın. Bir fonksiyonun içine bir argüman koyduğum an (hangi tür olursa olsun), bir UnexpectedEOF Error: Expected ampersand, RPAR or ARGSEP
. Bir kavram kanıtı olarak, yukarıdaki yeni dilbilgisinde parantezleri SINGLESTR tanımından kaldırırsam, her şey olması gerektiği gibi çalışır, ancak kareye geri dönüyorum.
STARTSYMBOL
) tanımlayan karakterleriniz var ve açık olması gerektiğinde ayırıcılar ve parantezler ekliyorsunuz; Burada herhangi bir belirsizlik görmüyorum. YineSTARTSYMBOL
de ayırt edilebilir olmak için listenizi tek tek öğelere ayırmanız gerekir.