ฉันกำลังพยายามสร้างไวยากรณ์เพื่อแยกสูตรคล้าย 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
คำจำกัดความและคิดว่าอาร์กิวเมนต์ของฟังก์ชันควรถูกส่งผ่านซึ่งทำให้เข้าใจได้ - ถ้าฉัน redefine ไวยากรณ์เพื่อรองรับสัญลักษณ์เครื่องหมายสำหรับฟังก์ชั่นเท่านั้นและเพิ่มวงเล็บใน
SINGLESTR
แล้วผมสามารถแยกสตริงกับวงเล็บExpected LPAR
แต่ฟังก์ชั่นทุกฉันพยายามที่จะแยกให้
ความตั้งใจของฉันคือสิ่งที่เริ่มต้นด้วย$
จะถูกแยกเป็นโทเค็นแล้วฉันจะแยกสิ่งที่ต้องการSINGLESTR
&foo($first arg (has) parentheses,,$second arg)
ตอนนี้ทางออกของฉันคือฉันใช้คำว่า 'escape' เช่น 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
'''
เรื่องนี้ตก (ค่อนข้าง) ภายใต้กรณีทดสอบที่สองของฉัน ฉันสามารถแยกsimple
ประเภทของสตริงทั้งหมด (โทเค็น TEXT, MD หรือ DB ที่สามารถมีวงเล็บ) และฟังก์ชั่นที่ว่างเปล่า; ตัวอย่างเช่น&foo()
หรือ&foo(&bar())
แยกอย่างถูกต้อง ขณะที่ฉันใส่อาร์กิวเมนต์ภายในฟังก์ชั่น (ไม่ว่าประเภท) UnexpectedEOF Error: Expected ampersand, RPAR or ARGSEP
ผมได้รับ เพื่อเป็นการพิสูจน์แนวคิดถ้าฉันลบวงเล็บออกจากคำจำกัดความของ SINGLESTR ในไวยากรณ์ใหม่ข้างต้นทุกอย่างก็ทำงานได้ตามที่ควรจะเป็น แต่ฉันกลับไปที่จตุรัสคนหนึ่ง
STARTSYMBOL
) และคุณเพิ่มตัวคั่นและวงเล็บที่จำเป็นต้องมีการล้าง; ฉันไม่เห็นความกำกวมใด ๆ ที่นี่ คุณยังคงต้องแยกSTARTSYMBOL
รายการของคุณออกเป็นแต่ละรายการเพื่อให้สามารถแยกแยะได้