如何在pythonlark解析器中平衡规则和终端?

2024-06-25 23:06:32 发布

您现在位置:Python中文网/ 问答频道 /正文

我用的是云雀,一个极好的python parsing library。在

它提供了Earley和LALR(1)解析器,并通过custom ^{} format进行定义。(EBNF代表Extended Backus–Naur form)。在

小写定义是规则,大写定义是终端。Lark还为大写定义提供了权重,以区分匹配的优先级。在

我试图定义一个语法,但我被一个似乎无法平衡的行为所困扰。在

我有一些关于未命名文字(双引号之间的字符串或字符)的规则:

directives: directive+
directive: "@" NAME arguments ?
directive_definition: description? "directive" "@" NAME arguments? "on" directive_locations
directive_locations: "SCALAR" | "OBJECT" | "ENUM"

arguments: "(" argument+ ")"
argument: NAME ":" value

union_type_definition: description? "union" NAME directives? union_member_types?

union_member_types: "=" NAME ("|" NAME)*

description: STRING | LONG_STRING    

STRING: /("(?!"").*?(?<!\\)(\\\\)*?"|'(?!'').*?(?<!\\)(\\\\)*?')/i
LONG_STRING: /(""".*?(?<!\\)(\\\\)*?"""|'''.*?(?<!\\)(\\\\)*?''')/is
NAME.2: /[_A-Za-z][_0-9A-Za-z]*/

它适用于99%的用例。但是,如果在我解析过的语言中,我使用一个directive,它是名为directive,那么一切都会中断:

^{pr2}$

这里,directive字符串与directive_definition规则中的未命名文本匹配,而它应该与NAME.2终端匹配。在

如何平衡/调整这一点,使LALR(1)解析器不存在歧义?在


Tags: 字符串name终端解析器string定义规则description
1条回答
网友
1楼 · 发布于 2024-06-25 23:06:32

《云雀》作者。在

之所以会出现这种误解,是因为“directive”可以是两个不同的标记:“directive”字符串或名称。默认情况下,Lark的LALR lexer总是选择更具体的一个,即字符串。在

那么我们如何让lexer知道@directive是一个名称,而不仅仅是两个常量字符串?在

解决方案1-使用上下文词法器

在这种情况下(如果没有完整的语法,很难确定)使用上下文词法分析器,而不是标准的LALR(1)词法分析器。在

上下文词法分析器可以在某种程度上与解析器通信,以确定哪个终端在每一点上更有意义。这是云雀独有的算法,您可以这样使用它:

parser = Lark(grammar, parser="lalr", lexer="contextual")

(这个lexer可以做标准lexer可以做的任何事情,甚至更多,因此在将来的版本中,它可能成为默认的lexer。)

解决方案2-为终端添加前缀

如果上下文词法器不能解决您的冲突,一个更“经典”的解决方案是定义一个指令标记,类似于:

^{pr2}$

与您的指令规则不同,这不会给lexer留下歧义。指令和“指令”字符串(或名称终端)之间有明显的区别。在

如果所有其他方法都失败了,您可以始终使用Earley解析器,它将以性能为代价来处理您提供的任何语法,而不管有多少冲突。在

希望这有帮助!在

编辑:我只想指出上下文lexer是LALR的默认设置,因此可以调用:

parser = Lark(grammar, parser="lalr")

相关问题 更多 >