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 simpleterminalime 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üfunctanı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 SINGLESTRjeton 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
jbndlrBaş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 simpledizeleri (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. YineSTARTSYMBOLde ayırt edilebilir olmak için listenizi tek tek öğelere ayırmanız gerekir.