我正在尝试创建一个语法来解析我设计的一些类似 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)
and&foo(!w1,w2,w3,,!w4,w5,w6)
这样的东西都会按预期进行解析。但是,如果我想为我的simple
终端增加更多的灵活性,那么我需要开始摆弄SINGLESTR
不方便的令牌定义。
我试过什么
我无法超越的部分是,如果我想要一个包含括号的字符串(它们是 的文字func
),那么在我目前的情况下我无法处理它们。
- 如果我在 中添加括号
SINGLESTR
,那么我会得到Expected STARTSYMBOL
,因为它与定义混淆了,func
它认为应该传递一个函数参数,这是有道理的。 - 如果我重新定义语法以仅为函数保留 & 符号并在 中添加括号
SINGLESTR
,那么我可以解析带括号的字符串,但我尝试解析的每个函数都会给出Expected LPAR
.
我的意图是任何以 a 开头的东西$
都会被解析为一个SINGLESTR
标记,然后我可以解析像&foo($first arg (has) parentheses,,$second arg)
.
目前,我的解决方案是在我的字符串中使用 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 的定义中删除括号,那么一切正常,但我又回到了原点。