Tôi đang cố gắng tạo một ngữ pháp để phân tích một số công thức giống như Excel mà tôi đã nghĩ ra, trong đó một ký tự đặc biệt ở đầu chuỗi biểu thị một nguồn khác. Ví dụ,$
có thể biểu thị một chuỗi, vì vậy " $This is text
" sẽ được coi là đầu vào chuỗi trong chương trình và &
có thể biểu thị một hàm, do đó &foo()
có thể được coi là một lệnh gọi đến hàm bên trong foo
.
Vấn đề tôi gặp phải là làm thế nào để xây dựng ngữ pháp đúng cách. Ví dụ: Đây là phiên bản đơn giản hóa dưới dạng 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')
Như vậy, với ngữ pháp này, mọi thứ như: $This is a string
, &foo()
, &foo(#arg1)
, &foo($arg1,,#arg2)
và&foo(!w1,w2,w3,,!w4,w5,w6)
tất cả đều được phân tích như mong đợi. Nhưng nếu tôi muốn thêm linh hoạt hơn cho simple
thiết bị đầu cuối của mình , thì tôi cần bắt đầu loay hoay với SINGLESTR
định nghĩa mã thông báo không thuận tiện.
Tôi đã thử những gì
Phần mà tôi không thể vượt qua là nếu tôi muốn có một chuỗi bao gồm dấu ngoặc đơn (nghĩa đen func
), thì tôi không thể xử lý chúng trong tình huống hiện tại của mình.
- Nếu tôi thêm dấu ngoặc đơn vào
SINGLESTR
, thì tôi nhận đượcExpected STARTSYMBOL
, bởi vì nó bị lẫn vớifunc
định nghĩa và nó nghĩ rằng một đối số hàm nên được thông qua, điều này có ý nghĩa. - Nếu tôi xác định lại ngữ pháp để chỉ dành riêng biểu tượng dấu và cho các hàm và thêm dấu ngoặc vào
SINGLESTR
, thì tôi có thể phân tích một chuỗi bằng dấu ngoặc đơn, nhưng mọi hàm tôi đang cố phân tích đều choExpected LPAR
.
Ý định của tôi là bất cứ điều gì bắt đầu bằng một $
sẽ được phân tích cú pháp như là mộtSINGLESTR
mã thông báo mã thông báo và sau đó tôi có thể phân tích những thứ như thế &foo($first arg (has) parentheses,,$second arg)
.
Hiện tại, giải pháp của tôi là tôi đang sử dụng các từ 'thoát' như LEFTPAR và RIGHTPAR trong chuỗi của mình và tôi đã viết các hàm trợ giúp để thay đổi chúng thành dấu ngoặc đơn khi tôi xử lý cây. Vì vậy, $This is a LEFTPARtestRIGHTPAR
tạo ra cây chính xác và khi tôi xử lý nó, thì điều này sẽ được dịch sang This is a (test)
.
Để hình thành một câu hỏi chung: Tôi có thể định nghĩa ngữ pháp của mình theo cách mà một số ký tự đặc biệt đối với ngữ pháp được coi là ký tự bình thường trong một số tình huống và đặc biệt trong mọi trường hợp khác không?
CHỈNH SỬA 1
Dựa trên nhận xét từ jbndlr
tôi đã sửa đổi ngữ pháp của mình để tạo các chế độ riêng dựa trên biểu tượng bắt đầu:
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
'''
Điều này rơi (phần nào) trong trường hợp thử nghiệm thứ hai của tôi. Tôi có thể phân tích tất cả các simple
loại chuỗi (mã thông báo TEXT, MD hoặc DB có thể chứa dấu ngoặc đơn) và các hàm trống; ví dụ, &foo()
hoặc &foo(&bar())
phân tích chính xác. Khoảnh khắc tôi đặt một đối số trong một hàm (bất kể là loại nào), tôi nhận được một UnexpectedEOF Error: Expected ampersand, RPAR or ARGSEP
. Như một bằng chứng về khái niệm, nếu tôi loại bỏ các dấu ngoặc đơn khỏi định nghĩa SINGLESTR trong ngữ pháp mới ở trên, thì mọi thứ sẽ hoạt động như bình thường, nhưng tôi lại quay lại hình vuông.
STARTSYMBOL
) và bạn thêm dấu phân cách và dấu ngoặc đơn ở những nơi cần phải rõ ràng; Tôi không thấy bất kỳ sự mơ hồ nào ở đây. Bạn vẫn phải chiaSTARTSYMBOL
danh sách của mình thành các mục riêng lẻ để có thể phân biệt.