我正在尝试创建一种语法来解析我设计的一些类似于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
定义混在一起,并且认为应该传递函数自变量,这很有意义。 - 如果我重新定义语法以仅为函数保留和号并在其中添加括号
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的定义中删除括号,则一切正常,但是我回到正题。
STARTSYMBOL
),并在需要清楚的地方添加了分隔符和括号;我在这里看不到任何歧义。您仍然必须将STARTSYMBOL
列表分成多个单独的项才能区分。